Improved Primitive support and better testing
[osm/N2VC.git] / tests / test_single_vdu_proxy_charm.py
1 """Test the deployment and configuration of a proxy charm.
2 1. Deploy proxy charm to a unit
3 2. Execute 'get-ssh-public-key' primitive and get returned value
4 3. Create LXD container with unit's public ssh key
5 4. Verify SSH works between unit and container
6 5. Destroy Juju unit
7 6. Stop and Destroy LXD container
8 """
9 import asyncio
10 import functools
11 import os
12 import sys
13 import logging
14 import unittest
15 from . import utils
16 import yaml
17 from n2vc.vnf import N2VC
18
19 NSD_YAML = """
20 nsd:nsd-catalog:
21 nsd:
22 - id: singlecharmvdu-ns
23 name: singlecharmvdu-ns
24 short-name: singlecharmvdu-ns
25 description: NS with 1 VNFs singlecharmvdu-vnf connected by datanet and mgmtnet VLs
26 version: '1.0'
27 logo: osm.png
28 constituent-vnfd:
29 - vnfd-id-ref: singlecharmvdu-vnf
30 member-vnf-index: '1'
31 vld:
32 - id: mgmtnet
33 name: mgmtnet
34 short-name: mgmtnet
35 type: ELAN
36 mgmt-network: 'true'
37 vim-network-name: mgmt
38 vnfd-connection-point-ref:
39 - vnfd-id-ref: singlecharmvdu-vnf
40 member-vnf-index-ref: '1'
41 vnfd-connection-point-ref: vnf-mgmt
42 - vnfd-id-ref: singlecharmvdu-vnf
43 member-vnf-index-ref: '2'
44 vnfd-connection-point-ref: vnf-mgmt
45 - id: datanet
46 name: datanet
47 short-name: datanet
48 type: ELAN
49 vnfd-connection-point-ref:
50 - vnfd-id-ref: singlecharmvdu-vnf
51 member-vnf-index-ref: '1'
52 vnfd-connection-point-ref: vnf-data
53 - vnfd-id-ref: singlecharmvdu-vnf
54 member-vnf-index-ref: '2'
55 vnfd-connection-point-ref: vnf-data
56 """
57
58 VNFD_YAML = """
59 vnfd:vnfd-catalog:
60 vnfd:
61 - id: singlecharmvdu-vnf
62 name: singlecharmvdu-vnf
63 short-name: singlecharmvdu-vnf
64 version: '1.0'
65 description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
66 logo: osm.png
67 connection-point:
68 - id: vnf-mgmt
69 name: vnf-mgmt
70 short-name: vnf-mgmt
71 type: VPORT
72 - id: vnf-data
73 name: vnf-data
74 short-name: vnf-data
75 type: VPORT
76 mgmt-interface:
77 cp: vnf-mgmt
78 internal-vld:
79 - id: internal
80 name: internal
81 short-name: internal
82 type: ELAN
83 internal-connection-point:
84 - id-ref: mgmtVM-internal
85 - id-ref: dataVM-internal
86 vdu:
87 - id: mgmtVM
88 name: mgmtVM
89 image: xenial
90 count: '1'
91 vm-flavor:
92 vcpu-count: '1'
93 memory-mb: '1024'
94 storage-gb: '10'
95 interface:
96 - name: mgmtVM-eth0
97 position: '1'
98 type: EXTERNAL
99 virtual-interface:
100 type: VIRTIO
101 external-connection-point-ref: vnf-mgmt
102 - name: mgmtVM-eth1
103 position: '2'
104 type: INTERNAL
105 virtual-interface:
106 type: VIRTIO
107 internal-connection-point-ref: mgmtVM-internal
108 internal-connection-point:
109 - id: mgmtVM-internal
110 name: mgmtVM-internal
111 short-name: mgmtVM-internal
112 type: VPORT
113 cloud-init-file: cloud-config.txt
114 vdu-configuration:
115 juju:
116 charm: simple
117 initial-config-primitive:
118 - seq: '1'
119 name: config
120 parameter:
121 - name: ssh-hostname
122 value: <rw_mgmt_ip>
123 - name: ssh-username
124 value: ubuntu
125 - name: ssh-password
126 value: ubuntu
127 - seq: '2'
128 name: touch
129 parameter:
130 - name: filename
131 value: '/home/ubuntu/first-touch-mgmtVM'
132 config-primitive:
133 - name: touch
134 parameter:
135 - name: filename
136 data-type: STRING
137 default-value: '/home/ubuntu/touched'
138
139 """
140
141
142 class PythonTest(unittest.TestCase):
143 n2vc = None
144 container = None
145
146 def setUp(self):
147 self.log = logging.getLogger()
148 self.log.level = logging.DEBUG
149
150 self.loop = asyncio.get_event_loop()
151
152 # self.container = utils.create_lxd_container()
153 self.n2vc = utils.get_n2vc()
154
155 def tearDown(self):
156 if self.container:
157 self.container.stop()
158 self.container.delete()
159
160 self.loop.run_until_complete(self.n2vc.logout())
161
162 def n2vc_callback(self, model_name, application_name, workload_status, workload_message, task=None):
163 """We pass the vnfd when setting up the callback, so expect it to be
164 returned as a tuple."""
165 self.log.debug("[Callback] Workload status '{}' for application {}".format(workload_status, application_name))
166 self.log.debug("[Callback] Task: \"{}\"".format(task))
167
168 if workload_status == "exec_primitive" and task:
169 self.log.debug("Getting Primitive Status")
170 # get the uuid from the task
171 uuid = task.result()
172
173 # get the status of the action
174 task = asyncio.ensure_future(
175 self.n2vc.GetPrimitiveStatus(
176 model_name,
177 uuid,
178 )
179 )
180 task.add_done_callback(functools.partial(self.n2vc_callback, model_name, application_name, "primitive_status", task))
181
182 if workload_status == "primitive_status" and task and not self.container:
183 self.log.debug("Creating LXD container")
184 # Get the ssh key
185 result = task.result()
186 pubkey = result['pubkey']
187
188 self.container = utils.create_lxd_container(pubkey)
189 mgmtaddr = self.container.state().network['eth0']['addresses']
190
191 self.log.debug("Setting config ssh-hostname={}".format(mgmtaddr[0]['address']))
192 task = asyncio.ensure_future(
193 self.n2vc.ExecutePrimitive(
194 model_name,
195 application_name,
196 "config",
197 None,
198 params={
199 'ssh-hostname': mgmtaddr[0]['address'],
200 }
201 )
202 )
203 task.add_done_callback(functools.partial(self.n2vc_callback, model_name, application_name, None, None))
204
205 if workload_status and not task:
206 self.log.debug("Callback: workload status \"{}\"".format(workload_status))
207
208 if workload_status in ["blocked"] and not self.container:
209 self.log.debug("Getting public SSH key")
210
211 # Execute 'get-ssh-public-key' primitive and get returned value
212 task = asyncio.ensure_future(
213 self.n2vc.ExecutePrimitive(
214 model_name,
215 application_name,
216 "get-ssh-public-key",
217 None,
218 params={
219 'ssh-hostname': '10.195.8.78',
220 'ssh-username': 'ubuntu',
221 'ssh-password': 'ubuntu'
222 }
223 )
224 )
225 task.add_done_callback(functools.partial(self.n2vc_callback, model_name, application_name, "exec_primitive", task))
226
227
228 # task = asyncio.ensure_future(
229 # self.n2vc.ExecutePrimitive(
230 # model_name,
231 # application_name,
232 # "config",
233 # None,
234 # params={
235 # 'ssh-hostname': '10.195.8.78',
236 # 'ssh-username': 'ubuntu',
237 # 'ssh-password': 'ubuntu'
238 # }
239 # )
240 # )
241 # task.add_done_callback(functools.partial(self.n2vc_callback, None, None, None))
242 pass
243 elif workload_status in ["active"]:
244 self.log.debug("Removing charm")
245 task = asyncio.ensure_future(
246 self.n2vc.RemoveCharms(model_name, application_name, self.n2vc_callback)
247 )
248 task.add_done_callback(functools.partial(self.n2vc_callback, None, None, None))
249
250 if self.container:
251 utils.destroy_lxd_container(self.container)
252 self.container = None
253
254 # Stop the test
255 self.loop.call_soon_threadsafe(self.loop.stop)
256
257 def test_deploy_application(self):
258 """Deploy proxy charm to a unit."""
259 stream_handler = logging.StreamHandler(sys.stdout)
260 self.log.addHandler(stream_handler)
261 try:
262 self.log.info("Log handler installed")
263 nsd = utils.get_descriptor(NSD_YAML)
264 vnfd = utils.get_descriptor(VNFD_YAML)
265
266 if nsd and vnfd:
267
268 vca_charms = os.getenv('VCA_CHARMS', None)
269
270 params = {}
271 vnf_index = 0
272
273 def deploy():
274 """An inner function to do the deployment of a charm from
275 either a vdu or vnf.
276 """
277 charm_dir = "{}/{}".format(vca_charms, charm)
278
279 # Setting this to an IP that will fail the initial config.
280 # This will be detected in the callback, which will execute
281 # the "config" primitive with the right IP address.
282 # mgmtaddr = self.container.state().network['eth0']['addresses']
283 # params['rw_mgmt_ip'] = mgmtaddr[0]['address']
284
285 # Legacy method is to set the ssh-private-key config
286 # with open(utils.get_juju_private_key(), "r") as f:
287 # pkey = f.readline()
288 # params['ssh-private-key'] = pkey
289
290 ns_name = "default"
291
292 vnf_name = self.n2vc.FormatApplicationName(
293 ns_name,
294 vnfd['name'],
295 str(vnf_index),
296 )
297
298 self.loop.run_until_complete(
299 self.n2vc.DeployCharms(
300 ns_name,
301 vnf_name,
302 vnfd,
303 charm_dir,
304 params,
305 {},
306 self.n2vc_callback
307 )
308 )
309
310 # Check if the VDUs in this VNF have a charm
311 for vdu in vnfd['vdu']:
312 vdu_config = vdu.get('vdu-configuration')
313 if vdu_config:
314 juju = vdu_config['juju']
315 self.assertIsNotNone(juju)
316
317 charm = juju['charm']
318 self.assertIsNotNone(charm)
319
320 params['initial-config-primitive'] = vdu_config['initial-config-primitive']
321
322 deploy()
323 vnf_index += 1
324
325 # Check if this VNF has a charm
326 vnf_config = vnfd.get("vnf-configuration")
327 if vnf_config:
328 juju = vnf_config['juju']
329 self.assertIsNotNone(juju)
330
331 charm = juju['charm']
332 self.assertIsNotNone(charm)
333
334 params['initial-config-primitive'] = vnf_config['initial-config-primitive']
335
336 deploy()
337 vnf_index += 1
338
339 self.loop.run_forever()
340 # while self.loop.is_running():
341 # # await asyncio.sleep(1)
342 # time.sleep(1)
343
344 # Test actions
345 # ExecutePrimitive(self, nsd, vnfd, vnf_member_index, primitive, callback, *callback_args, **params):
346
347 # self.loop.run_until_complete(n.DestroyNetworkService(nsd))
348
349 # self.loop.run_until_complete(self.n2vc.logout())
350 finally:
351 self.log.removeHandler(stream_handler)