Merge "Integration test for metrics + bug fix"
[osm/N2VC.git] / tests / integration / test_metrics.py
1 """Test the collection of charm metrics.
2 1. Deploy a charm w/metrics to a unit
3 2. Collect metrics or wait for collection to run
4 3. Execute n2vc.GetMetrics()
5 5. Destroy Juju unit
6 """
7 import asyncio
8 import functools
9 import logging
10 import sys
11 import time
12 import unittest
13 from .. import utils
14
15 NSD_YAML = """
16 nsd:nsd-catalog:
17 nsd:
18 - id: singlecharmvdu-ns
19 name: singlecharmvdu-ns
20 short-name: singlecharmvdu-ns
21 description: NS with 1 VNFs singlecharmvdu-vnf connected by datanet and mgmtnet VLs
22 version: '1.0'
23 logo: osm.png
24 constituent-vnfd:
25 - vnfd-id-ref: singlecharmvdu-vnf
26 member-vnf-index: '1'
27 vld:
28 - id: mgmtnet
29 name: mgmtnet
30 short-name: mgmtnet
31 type: ELAN
32 mgmt-network: 'true'
33 vim-network-name: mgmt
34 vnfd-connection-point-ref:
35 - vnfd-id-ref: singlecharmvdu-vnf
36 member-vnf-index-ref: '1'
37 vnfd-connection-point-ref: vnf-mgmt
38 - vnfd-id-ref: singlecharmvdu-vnf
39 member-vnf-index-ref: '2'
40 vnfd-connection-point-ref: vnf-mgmt
41 - id: datanet
42 name: datanet
43 short-name: datanet
44 type: ELAN
45 vnfd-connection-point-ref:
46 - vnfd-id-ref: singlecharmvdu-vnf
47 member-vnf-index-ref: '1'
48 vnfd-connection-point-ref: vnf-data
49 - vnfd-id-ref: singlecharmvdu-vnf
50 member-vnf-index-ref: '2'
51 vnfd-connection-point-ref: vnf-data
52 """
53
54 VNFD_YAML = """
55 vnfd:vnfd-catalog:
56 vnfd:
57 - id: singlecharmvdu-vnf
58 name: singlecharmvdu-vnf
59 short-name: singlecharmvdu-vnf
60 version: '1.0'
61 description: A VNF consisting of 2 VDUs w/charms connected to an internal VL, and one VDU with cloud-init
62 logo: osm.png
63 connection-point:
64 - id: vnf-mgmt
65 name: vnf-mgmt
66 short-name: vnf-mgmt
67 type: VPORT
68 - id: vnf-data
69 name: vnf-data
70 short-name: vnf-data
71 type: VPORT
72 mgmt-interface:
73 cp: vnf-mgmt
74 internal-vld:
75 - id: internal
76 name: internal
77 short-name: internal
78 type: ELAN
79 internal-connection-point:
80 - id-ref: mgmtVM-internal
81 - id-ref: dataVM-internal
82 vdu:
83 - id: mgmtVM
84 name: mgmtVM
85 image: xenial
86 count: '1'
87 vm-flavor:
88 vcpu-count: '1'
89 memory-mb: '1024'
90 storage-gb: '10'
91 interface:
92 - name: mgmtVM-eth0
93 position: '1'
94 type: EXTERNAL
95 virtual-interface:
96 type: VIRTIO
97 external-connection-point-ref: vnf-mgmt
98 - name: mgmtVM-eth1
99 position: '2'
100 type: INTERNAL
101 virtual-interface:
102 type: VIRTIO
103 internal-connection-point-ref: mgmtVM-internal
104 internal-connection-point:
105 - id: mgmtVM-internal
106 name: mgmtVM-internal
107 short-name: mgmtVM-internal
108 type: VPORT
109 cloud-init-file: cloud-config.txt
110 vnf-configuration:
111 juju:
112 charm: metrics-ci
113 config-primitive:
114 - name: touch
115 parameter:
116 - name: filename
117 data-type: STRING
118 default-value: '/home/ubuntu/touched'
119 """
120
121
122 class PythonTest(unittest.TestCase):
123 n2vc = None
124 charm = None
125
126 def setUp(self):
127 self.log = logging.getLogger()
128 self.log.level = logging.DEBUG
129
130 self.stream_handler = logging.StreamHandler(sys.stdout)
131 self.log.addHandler(self.stream_handler)
132
133 self.loop = asyncio.get_event_loop()
134
135 self.n2vc = utils.get_n2vc()
136
137 # Parse the descriptor
138 self.log.debug("Parsing the descriptor")
139 self.nsd = utils.get_descriptor(NSD_YAML)
140 self.vnfd = utils.get_descriptor(VNFD_YAML)
141
142
143 # Build the charm
144
145 vnf_config = self.vnfd.get("vnf-configuration")
146 if vnf_config:
147 juju = vnf_config['juju']
148 charm = juju['charm']
149
150 self.log.debug("Building charm {}".format(charm))
151 self.charm = utils.build_charm(charm)
152
153 def tearDown(self):
154 self.loop.run_until_complete(self.n2vc.logout())
155 self.log.removeHandler(self.stream_handler)
156
157 def n2vc_callback(self, model_name, application_name, workload_status,\
158 workload_message, task=None):
159 """We pass the vnfd when setting up the callback, so expect it to be
160 returned as a tuple."""
161 self.log.debug("status: {}; task: {}".format(workload_status, task))
162
163 # if workload_status in ["stop_test"]:
164 # # Stop the test
165 # self.log.debug("Stopping the test1")
166 # self.loop.call_soon_threadsafe(self.loop.stop)
167 # return
168
169 if workload_status:
170 if workload_status in ["active"] and not task:
171 # Force a run of the metric collector, so we don't have
172 # to wait for it's normal 5 minute interval run.
173 # NOTE: this shouldn't be done outside of CI
174 utils.collect_metrics(application_name)
175
176 # get the current metrics
177 task = asyncio.ensure_future(
178 self.n2vc.GetMetrics(
179 model_name,
180 application_name,
181 )
182 )
183 task.add_done_callback(
184 functools.partial(
185 self.n2vc_callback,
186 model_name,
187 application_name,
188 "collect_metrics",
189 task,
190 )
191 )
192
193 elif workload_status in ["collect_metrics"]:
194
195 if task:
196 # Check if task returned metrics
197 results = task.result()
198
199 foo = utils.parse_metrics(application_name, results)
200 if 'load' in foo:
201 self.log.debug("Removing charm")
202 task = asyncio.ensure_future(
203 self.n2vc.RemoveCharms(model_name, application_name, self.n2vc_callback)
204 )
205 task.add_done_callback(
206 functools.partial(
207 self.n2vc_callback,
208 model_name,
209 application_name,
210 "stop_test",
211 task,
212 )
213 )
214 return
215
216 # No metrics are available yet, so try again in a minute.
217 self.log.debug("Sleeping for 60 seconds")
218 time.sleep(60)
219 task = asyncio.ensure_future(
220 self.n2vc.GetMetrics(
221 model_name,
222 application_name,
223 )
224 )
225 task.add_done_callback(
226 functools.partial(
227 self.n2vc_callback,
228 model_name,
229 application_name,
230 "collect_metrics",
231 task,
232 )
233 )
234 elif workload_status in ["stop_test"]:
235 # Stop the test
236 self.log.debug("Stopping the test2")
237 self.loop.call_soon_threadsafe(self.loop.stop)
238
239 def test_deploy_application(self):
240 """Deploy proxy charm to a unit."""
241 if self.nsd and self.vnfd:
242 params = {}
243 vnf_index = 0
244
245 def deploy():
246 """An inner function to do the deployment of a charm from
247 either a vdu or vnf.
248 """
249 charm_dir = "{}/builds/{}".format(utils.get_charm_path(), charm)
250
251 # Setting this to an IP that will fail the initial config.
252 # This will be detected in the callback, which will execute
253 # the "config" primitive with the right IP address.
254 # mgmtaddr = self.container.state().network['eth0']['addresses']
255 # params['rw_mgmt_ip'] = mgmtaddr[0]['address']
256
257 # Legacy method is to set the ssh-private-key config
258 # with open(utils.get_juju_private_key(), "r") as f:
259 # pkey = f.readline()
260 # params['ssh-private-key'] = pkey
261
262 ns_name = "default"
263
264 vnf_name = self.n2vc.FormatApplicationName(
265 ns_name,
266 self.vnfd['name'],
267 str(vnf_index),
268 )
269
270 self.loop.run_until_complete(
271 self.n2vc.DeployCharms(
272 ns_name,
273 vnf_name,
274 self.vnfd,
275 charm_dir,
276 params,
277 {},
278 self.n2vc_callback
279 )
280 )
281
282 # Check if the VDUs in this VNF have a charm
283 # for vdu in vnfd['vdu']:
284 # vdu_config = vdu.get('vdu-configuration')
285 # if vdu_config:
286 # juju = vdu_config['juju']
287 # self.assertIsNotNone(juju)
288 #
289 # charm = juju['charm']
290 # self.assertIsNotNone(charm)
291 #
292 # params['initial-config-primitive'] = vdu_config['initial-config-primitive']
293 #
294 # deploy()
295 # vnf_index += 1
296 #
297 # # Check if this VNF has a charm
298 vnf_config = self.vnfd.get("vnf-configuration")
299 if vnf_config:
300 juju = vnf_config['juju']
301 self.assertIsNotNone(juju)
302
303 charm = juju['charm']
304 self.assertIsNotNone(charm)
305
306 if 'initial-config-primitive' in vnf_config:
307 params['initial-config-primitive'] = vnf_config['initial-config-primitive']
308
309 deploy()
310 vnf_index += 1
311
312 self.loop.run_forever()
313 # while self.loop.is_running():
314 # # await asyncio.sleep(1)
315 # time.sleep(1)