9f1cd0a2c673766d88d07b5eae4b321493564171
[osm/SO.git] / rwlaunchpad / ra / pytest / ns / pingpong / test_records.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import collections
19 import socket
20 import subprocess
21 import time
22
23 import pytest
24
25 import gi
26 import re
27 gi.require_version('RwNsrYang', '1.0')
28 from gi.repository import (
29 NsdYang,
30 RwBaseYang,
31 RwConmanYang,
32 RwNsrYang,
33 RwNsdYang,
34 RwVcsYang,
35 RwVlrYang,
36 RwVnfdYang,
37 RwVnfrYang,
38 VlrYang,
39 VnfrYang,
40 )
41 import rift.auto.session
42 import rift.mano.examples.ping_pong_nsd as ping_pong
43
44
45 @pytest.fixture(scope='module')
46 def proxy(request, mgmt_session):
47 return mgmt_session.proxy
48
49 @pytest.fixture(scope='session')
50 def updated_ping_pong_records(ping_pong_factory):
51 '''Fixture returns a newly created set of ping and pong descriptors
52 for the create_update tests
53 '''
54 return ping_pong_factory.generate_descriptors()
55
56 def yield_vnfd_vnfr_pairs(proxy, nsr=None):
57 """
58 Yields tuples of vnfd & vnfr entries.
59
60 Args:
61 proxy (callable): Launchpad proxy
62 nsr (optional): If specified, only the vnfr & vnfd records of the NSR
63 are returned
64
65 Yields:
66 Tuple: VNFD and its corresponding VNFR entry
67 """
68 def get_vnfd(vnfd_id):
69 xpath = "/vnfd-catalog/vnfd[id='{}']".format(vnfd_id)
70 return proxy(RwVnfdYang).get(xpath)
71
72 vnfr = "/vnfr-catalog/vnfr"
73 vnfrs = proxy(RwVnfrYang).get(vnfr, list_obj=True)
74 for vnfr in vnfrs.vnfr:
75
76 if nsr:
77 const_vnfr_ids = [const_vnfr.vnfr_id for const_vnfr in nsr.constituent_vnfr_ref]
78 if vnfr.id not in const_vnfr_ids:
79 continue
80
81 vnfd = get_vnfd(vnfr.vnfd.id)
82 yield vnfd, vnfr
83
84
85 def yield_nsd_nsr_pairs(proxy):
86 """Yields tuples of NSD & NSR
87
88 Args:
89 proxy (callable): Launchpad proxy
90
91 Yields:
92 Tuple: NSD and its corresponding NSR record
93 """
94
95 for nsr_cfg, nsr in yield_nsrc_nsro_pairs(proxy):
96 nsd_path = "/nsd-catalog/nsd[id='{}']".format(
97 nsr_cfg.nsd.id)
98 nsd = proxy(RwNsdYang).get_config(nsd_path)
99
100 yield nsd, nsr
101
102 def yield_nsrc_nsro_pairs(proxy):
103 """Yields tuples of NSR Config & NSR Opdata pairs
104
105 Args:
106 proxy (callable): Launchpad proxy
107
108 Yields:
109 Tuple: NSR config and its corresponding NSR op record
110 """
111 nsr = "/ns-instance-opdata/nsr"
112 nsrs = proxy(RwNsrYang).get(nsr, list_obj=True)
113 for nsr in nsrs.nsr:
114 nsr_cfg_path = "/ns-instance-config/nsr[id='{}']".format(
115 nsr.ns_instance_config_ref)
116 nsr_cfg = proxy(RwNsrYang).get_config(nsr_cfg_path)
117
118 yield nsr_cfg, nsr
119
120
121 def assert_records(proxy):
122 """Verifies if the NSR & VNFR records are created
123 """
124 ns_tuple = list(yield_nsd_nsr_pairs(proxy))
125 assert len(ns_tuple) == 1
126
127 vnf_tuple = list(yield_vnfd_vnfr_pairs(proxy))
128 assert len(vnf_tuple) == 2
129
130
131 @pytest.mark.depends('nsr')
132 @pytest.mark.setup('records')
133 @pytest.mark.usefixtures('recover_tasklet')
134 @pytest.mark.incremental
135 class TestRecordsData(object):
136 def is_valid_ip(self, address):
137 """Verifies if it is a valid IP and if its accessible
138
139 Args:
140 address (str): IP address
141
142 Returns:
143 boolean
144 """
145 try:
146 socket.inet_aton(address)
147 except socket.error:
148 return False
149 else:
150 return True
151
152
153 @pytest.mark.feature("recovery")
154 def test_tasklets_recovery(self, mgmt_session, proxy, recover_tasklet):
155 """Test the recovery feature of tasklets
156
157 Triggers the vcrash and waits till the system is up
158 """
159 RECOVERY = "RESTART"
160
161 def vcrash(comp):
162 rpc_ip = RwVcsYang.VCrashInput.from_dict({"instance_name": comp})
163 proxy(RwVcsYang).rpc(rpc_ip)
164
165 tasklet_name = r'^{}-.*'.format(recover_tasklet)
166
167 vcs_info = proxy(RwBaseYang).get("/vcs/info/components")
168 for comp in vcs_info.component_info:
169 if comp.recovery_action == RECOVERY and \
170 re.match(tasklet_name, comp.instance_name):
171 vcrash(comp.instance_name)
172
173 time.sleep(60)
174
175 rift.vcs.vcs.wait_until_system_started(mgmt_session)
176 # NSM tasklet takes a couple of seconds to set up the python structure
177 # so sleep and then continue with the tests.
178 time.sleep(60)
179
180 def test_records_present(self, proxy):
181 assert_records(proxy)
182
183 def test_nsd_ref_count(self, proxy):
184 """
185 Asserts
186 1. The ref count data of the NSR with the actual number of NSRs
187 """
188 nsd_ref_xpath = "/ns-instance-opdata/nsd-ref-count"
189 nsd_refs = proxy(RwNsrYang).get(nsd_ref_xpath, list_obj=True)
190
191 expected_ref_count = collections.defaultdict(int)
192 for nsd_ref in nsd_refs.nsd_ref_count:
193 expected_ref_count[nsd_ref.nsd_id_ref] = nsd_ref.instance_ref_count
194
195 actual_ref_count = collections.defaultdict(int)
196 for nsd, nsr in yield_nsd_nsr_pairs(proxy):
197 actual_ref_count[nsd.id] += 1
198
199 assert expected_ref_count == actual_ref_count
200
201 def test_vnfd_ref_count(self, proxy):
202 """
203 Asserts
204 1. The ref count data of the VNFR with the actual number of VNFRs
205 """
206 vnfd_ref_xpath = "/vnfr-catalog/vnfd-ref-count"
207 vnfd_refs = proxy(RwVnfrYang).get(vnfd_ref_xpath, list_obj=True)
208
209 expected_ref_count = collections.defaultdict(int)
210 for vnfd_ref in vnfd_refs.vnfd_ref_count:
211 expected_ref_count[vnfd_ref.vnfd_id_ref] = vnfd_ref.instance_ref_count
212
213 actual_ref_count = collections.defaultdict(int)
214 for vnfd, vnfr in yield_vnfd_vnfr_pairs(proxy):
215 actual_ref_count[vnfd.id] += 1
216
217 assert expected_ref_count == actual_ref_count
218
219 def test_nsr_nsd_records(self, proxy):
220 """
221 Verifies the correctness of the NSR record using its NSD counter-part
222
223 Asserts:
224 1. The count of vnfd and vnfr records
225 2. Count of connection point descriptor and records
226 """
227 for nsd, nsr in yield_nsd_nsr_pairs(proxy):
228 assert nsd.name == nsr.nsd_name_ref
229 assert len(nsd.constituent_vnfd) == len(nsr.constituent_vnfr_ref)
230
231 assert len(nsd.vld) == len(nsr.vlr)
232 for vnfd_conn_pts, vnfr_conn_pts in zip(nsd.vld, nsr.vlr):
233 assert len(vnfd_conn_pts.vnfd_connection_point_ref) == \
234 len(vnfr_conn_pts.vnfr_connection_point_ref)
235
236 def test_vdu_record_params(self, proxy):
237 """
238 Asserts:
239 1. If a valid floating IP has been assigned to the VM
240 2. Count of VDUD and the VDUR
241 3. Check if the VM flavor has been copied over the VDUR
242 """
243 for vnfd, vnfr in yield_vnfd_vnfr_pairs(proxy):
244 assert vnfd.mgmt_interface.port == vnfr.mgmt_interface.port
245 assert len(vnfd.vdu) == len(vnfr.vdur)
246
247 for vdud, vdur in zip(vnfd.vdu, vnfr.vdur):
248 assert vdud.vm_flavor == vdur.vm_flavor
249 assert self.is_valid_ip(vdur.management_ip) is True
250 assert vdud.external_interface[0].vnfd_connection_point_ref == \
251 vdur.external_interface[0].vnfd_connection_point_ref
252
253 def test_external_vl(self, proxy):
254 """
255 Asserts:
256 1. Valid IP for external connection point
257 2. A valid external network fabric
258 3. Connection point names are copied over
259 4. Count of VLD and VLR
260 5. Checks for a valid subnet ?
261 6. Checks for the operational status to be running?
262 """
263 for vnfd, vnfr in yield_vnfd_vnfr_pairs(proxy):
264 cp_des, cp_rec = vnfd.connection_point, vnfr.connection_point
265
266 assert len(cp_des) == len(cp_rec)
267 assert cp_des[0].name == cp_rec[0].name
268 assert self.is_valid_ip(cp_rec[0].ip_address) is True
269
270 xpath = "/vlr-catalog/vlr[id='{}']".format(cp_rec[0].vlr_ref)
271 vlr = proxy(RwVlrYang).get(xpath)
272
273 assert len(vlr.network_id) > 0
274 assert len(vlr.assigned_subnet) > 0
275 ip, _ = vlr.assigned_subnet.split("/")
276 assert self.is_valid_ip(ip) is True
277 assert vlr.operational_status == "running"
278
279
280 def test_nsr_record(self, proxy):
281 """
282 Currently we only test for the components of NSR tests. Ignoring the
283 operational-events records
284
285 Asserts:
286 1. The constituent components.
287 2. Admin status of the corresponding NSD record.
288 """
289 for nsr_cfg, nsr in yield_nsrc_nsro_pairs(proxy):
290 # 1 n/w and 2 connection points
291 assert len(nsr.vlr) == 1
292 assert len(nsr.vlr[0].vnfr_connection_point_ref) == 2
293
294 assert len(nsr.constituent_vnfr_ref) == 2
295 assert nsr_cfg.admin_status == 'ENABLED'
296
297 def test_wait_for_pingpong_configured(self, proxy):
298 nsr_opdata = proxy(RwNsrYang).get('/ns-instance-opdata')
299 nsrs = nsr_opdata.nsr
300
301 assert len(nsrs) == 1
302 current_nsr = nsrs[0]
303
304 xpath = "/ns-instance-opdata/nsr[ns-instance-config-ref='{}']/config-status".format(current_nsr.ns_instance_config_ref)
305 proxy(RwNsrYang).wait_for(xpath, "configured", timeout=400)
306
307 def test_monitoring_params(self, proxy):
308 """
309 Asserts:
310 1. The value counter ticks?
311 2. If the meta fields are copied over
312 """
313 def mon_param_record(vnfr_id, mon_param_id):
314 return '/vnfr-catalog/vnfr[id="{}"]/monitoring-param[id="{}"]'.format(
315 vnfr_id, mon_param_id)
316
317 for vnfd, vnfr in yield_vnfd_vnfr_pairs(proxy):
318 for mon_des in (vnfd.monitoring_param):
319 mon_rec = mon_param_record(vnfr.id, mon_des.id)
320 mon_rec = proxy(VnfrYang).get(mon_rec)
321
322 # Meta data check
323 fields = mon_des.as_dict().keys()
324 for field in fields:
325 assert getattr(mon_des, field) == getattr(mon_rec, field)
326 # Tick check
327 #assert mon_rec.value_integer > 0
328
329 def test_cm_nsr(self, proxy):
330 """
331 Asserts:
332 1. The ID of the NSR in cm-state
333 2. Name of the cm-nsr
334 3. The vnfr component's count
335 4. State of the cm-nsr
336 """
337 for nsd, nsr in yield_nsd_nsr_pairs(proxy):
338 con_nsr_xpath = "/cm-state/cm-nsr[id='{}']".format(nsr.ns_instance_config_ref)
339 con_data = proxy(RwConmanYang).get(con_nsr_xpath)
340
341 assert con_data.name == "ping_pong_nsd"
342 assert len(con_data.cm_vnfr) == 2
343
344 state_path = con_nsr_xpath + "/state"
345 proxy(RwConmanYang).wait_for(state_path, 'ready', timeout=120)
346
347 def test_cm_vnfr(self, proxy):
348 """
349 Asserts:
350 1. The ID of Vnfr in cm-state
351 2. Name of the vnfr
352 3. State of the VNFR
353 4. Checks for a reachable IP in mgmt_interface
354 5. Basic checks for connection point and cfg_location.
355 """
356 def is_reachable(ip, timeout=10):
357 rc = subprocess.call(["ping", "-c1", "-w", str(timeout), ip])
358 if rc == 0:
359 return True
360 return False
361
362 nsr_cfg, _ = list(yield_nsrc_nsro_pairs(proxy))[0]
363 con_nsr_xpath = "/cm-state/cm-nsr[id='{}']".format(nsr_cfg.id)
364
365 for _, vnfr in yield_vnfd_vnfr_pairs(proxy):
366 con_vnfr_path = con_nsr_xpath + "/cm-vnfr[id='{}']".format(vnfr.id)
367 con_data = proxy(RwConmanYang).get(con_vnfr_path)
368
369 assert con_data is not None
370
371 state_path = con_vnfr_path + "/state"
372 proxy(RwConmanYang).wait_for(state_path, 'ready', timeout=120)
373
374 con_data = proxy(RwConmanYang).get(con_vnfr_path)
375 assert is_reachable(con_data.mgmt_interface.ip_address) is True
376
377 assert len(con_data.connection_point) == 1
378 connection_point = con_data.connection_point[0]
379 assert connection_point.name == vnfr.connection_point[0].name
380 assert connection_point.ip_address == vnfr.connection_point[0].ip_address
381
382 assert con_data.cfg_location is not None
383
384 @pytest.mark.depends('nsr')
385 @pytest.mark.setup('nfvi')
386 @pytest.mark.incremental
387 class TestNfviMetrics(object):
388
389 def test_records_present(self, proxy):
390 assert_records(proxy)
391
392 @pytest.mark.skipif(True, reason='NFVI metrics collected from NSR are deprecated, test needs to be updated to collected metrics from VNFRs')
393 def test_nfvi_metrics(self, proxy):
394 """
395 Verify the NFVI metrics
396
397 Asserts:
398 1. Computed metrics, such as memory, cpu, storage and ports, match
399 with the metrics in NSR record. The metrics are computed from the
400 descriptor records.
401 2. Check if the 'utilization' field has a valid value (> 0) and matches
402 with the 'used' field, if available.
403 """
404 for nsd, nsr in yield_nsd_nsr_pairs(proxy):
405 nfvi_metrics = nsr.nfvi_metrics
406 computed_metrics = collections.defaultdict(int)
407
408 # Get the constituent VNF records.
409 for vnfd, vnfr in yield_vnfd_vnfr_pairs(proxy, nsr):
410 vdu = vnfd.vdu[0]
411 vm_spec = vdu.vm_flavor
412 computed_metrics['vm'] += 1
413 computed_metrics['memory'] += vm_spec.memory_mb * (10**6)
414 computed_metrics['storage'] += vm_spec.storage_gb * (10**9)
415 computed_metrics['vcpu'] += vm_spec.vcpu_count
416 computed_metrics['external_ports'] += len(vnfd.connection_point)
417 computed_metrics['internal_ports'] += len(vdu.internal_connection_point)
418
419 assert nfvi_metrics.vm.active_vm == computed_metrics['vm']
420
421 # Availability checks
422 for metric_name in computed_metrics:
423 metric_data = getattr(nfvi_metrics, metric_name)
424 total_available = getattr(metric_data, 'total', None)
425
426 if total_available is not None:
427 assert computed_metrics[metric_name] == total_available
428
429 # Utilization checks
430 for metric_name in ['memory', 'storage', 'vcpu']:
431 metric_data = getattr(nfvi_metrics, metric_name)
432
433 utilization = metric_data.utilization
434 # assert utilization > 0
435
436 # If used field is available, check if it matches with utilization!
437 total = metric_data.total
438 used = getattr(metric_data, 'used', None)
439 if used is not None:
440 assert total > 0
441 computed_utilization = round((used/total) * 100, 2)
442 assert abs(computed_utilization - utilization) <= 0.1
443
444
445
446 @pytest.mark.depends('nfvi')
447 @pytest.mark.incremental
448 class TestRecordsDescriptors:
449 def test_create_update_vnfd(self, proxy, updated_ping_pong_records):
450 """
451 Verify VNFD related operations
452
453 Asserts:
454 If a VNFD record is created
455 """
456 ping_vnfd, pong_vnfd, _ = updated_ping_pong_records
457 vnfdproxy = proxy(RwVnfdYang)
458
459 for vnfd_record in [ping_vnfd, pong_vnfd]:
460 xpath = "/vnfd-catalog/vnfd"
461 vnfdproxy.create_config(xpath, vnfd_record.vnfd)
462
463 xpath = "/vnfd-catalog/vnfd[id='{}']".format(vnfd_record.id)
464 vnfd = vnfdproxy.get(xpath)
465 assert vnfd.id == vnfd_record.id
466
467 vnfdproxy.replace_config(xpath, vnfd_record.vnfd)
468
469 def test_create_update_nsd(self, proxy, updated_ping_pong_records):
470 """
471 Verify NSD related operations
472
473 Asserts:
474 If NSD record was created
475 """
476 _, _, ping_pong_nsd = updated_ping_pong_records
477 nsdproxy = proxy(NsdYang)
478
479 xpath = "/nsd-catalog/nsd"
480 nsdproxy.create_config(xpath, ping_pong_nsd.descriptor)
481
482 xpath = "/nsd-catalog/nsd[id='{}']".format(ping_pong_nsd.id)
483 nsd = nsdproxy.get(xpath)
484 assert nsd.id == ping_pong_nsd.id
485
486 nsdproxy.replace_config(xpath, ping_pong_nsd.descriptor)
487