3 # Copyright 2016 RIFT.IO Inc
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
27 gi
.require_version('RwNsrYang', '1.0')
28 from gi
.repository
import (
41 import rift
.auto
.session
42 import rift
.mano
.examples
.ping_pong_nsd
as ping_pong
45 @pytest.fixture(scope
='module')
46 def proxy(request
, mgmt_session
):
47 return mgmt_session
.proxy
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
54 return ping_pong_factory
.generate_descriptors()
56 def yield_vnfd_vnfr_pairs(proxy
, nsr
=None):
58 Yields tuples of vnfd & vnfr entries.
61 proxy (callable): Launchpad proxy
62 nsr (optional): If specified, only the vnfr & vnfd records of the NSR
66 Tuple: VNFD and its corresponding VNFR entry
68 def get_vnfd(vnfd_id
):
69 xpath
= "/vnfd-catalog/vnfd[id='{}']".format(vnfd_id
)
70 return proxy(RwVnfdYang
).get(xpath
)
72 vnfr
= "/vnfr-catalog/vnfr"
73 vnfrs
= proxy(RwVnfrYang
).get(vnfr
, list_obj
=True)
74 for vnfr
in vnfrs
.vnfr
:
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
:
81 vnfd
= get_vnfd(vnfr
.vnfd_ref
)
85 def yield_nsd_nsr_pairs(proxy
):
86 """Yields tuples of NSD & NSR
89 proxy (callable): Launchpad proxy
92 Tuple: NSD and its corresponding NSR record
95 for nsr_cfg
, nsr
in yield_nsrc_nsro_pairs(proxy
):
96 nsd_path
= "/nsd-catalog/nsd[id='{}']".format(
98 nsd
= proxy(RwNsdYang
).get_config(nsd_path
)
102 def yield_nsrc_nsro_pairs(proxy
):
103 """Yields tuples of NSR Config & NSR Opdata pairs
106 proxy (callable): Launchpad proxy
109 Tuple: NSR config and its corresponding NSR op record
111 nsr
= "/ns-instance-opdata/nsr"
112 nsrs
= proxy(RwNsrYang
).get(nsr
, list_obj
=True)
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
)
121 def assert_records(proxy
):
122 """Verifies if the NSR & VNFR records are created
124 ns_tuple
= list(yield_nsd_nsr_pairs(proxy
))
125 assert len(ns_tuple
) == 1
127 vnf_tuple
= list(yield_vnfd_vnfr_pairs(proxy
))
128 assert len(vnf_tuple
) == 2
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
140 address (str): IP address
146 socket
.inet_aton(address
)
153 @pytest.mark
.feature("recovery")
154 def test_tasklets_recovery(self
, mgmt_session
, proxy
, recover_tasklet
):
155 """Test the recovery feature of tasklets
157 Triggers the vcrash and waits till the system is up
162 rpc_ip
= RwVcsYang
.VCrashInput
.from_dict({"instance_name": comp
})
163 proxy(RwVcsYang
).rpc(rpc_ip
)
165 tasklet_name
= r
'^{}-.*'.format(recover_tasklet
)
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
)
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.
180 def test_records_present(self
, proxy
):
181 assert_records(proxy
)
183 def test_nsd_ref_count(self
, proxy
):
186 1. The ref count data of the NSR with the actual number of NSRs
188 nsd_ref_xpath
= "/ns-instance-opdata/nsd-ref-count"
189 nsd_refs
= proxy(RwNsrYang
).get(nsd_ref_xpath
, list_obj
=True)
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
195 actual_ref_count
= collections
.defaultdict(int)
196 for nsd
, nsr
in yield_nsd_nsr_pairs(proxy
):
197 actual_ref_count
[nsd
.id] += 1
199 assert expected_ref_count
== actual_ref_count
201 def test_vnfd_ref_count(self
, proxy
):
204 1. The ref count data of the VNFR with the actual number of VNFRs
206 vnfd_ref_xpath
= "/vnfr-catalog/vnfd-ref-count"
207 vnfd_refs
= proxy(RwVnfrYang
).get(vnfd_ref_xpath
, list_obj
=True)
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
213 actual_ref_count
= collections
.defaultdict(int)
214 for vnfd
, vnfr
in yield_vnfd_vnfr_pairs(proxy
):
215 actual_ref_count
[vnfd
.id] += 1
217 assert expected_ref_count
== actual_ref_count
219 def test_nsr_nsd_records(self
, proxy
):
221 Verifies the correctness of the NSR record using its NSD counter-part
224 1. The count of vnfd and vnfr records
225 2. Count of connection point descriptor and records
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
)
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
)
236 def test_vdu_record_params(self
, proxy
):
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
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
)
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
253 def test_external_vl(self
, proxy
):
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?
263 for vnfd
, vnfr
in yield_vnfd_vnfr_pairs(proxy
):
264 cp_des
, cp_rec
= vnfd
.connection_point
, vnfr
.connection_point
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
270 xpath
= "/vlr-catalog/vlr[id='{}']".format(cp_rec
[0].vlr_ref
)
271 vlr
= proxy(RwVlrYang
).get(xpath
)
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"
280 def test_nsr_record(self
, proxy
):
282 Currently we only test for the components of NSR tests. Ignoring the
283 operational-events records
286 1. The constituent components.
287 2. Admin status of the corresponding NSD record.
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
294 assert len(nsr
.constituent_vnfr_ref
) == 2
295 assert nsr_cfg
.admin_status
== 'ENABLED'
297 def test_wait_for_pingpong_configured(self
, proxy
):
298 nsr_opdata
= proxy(RwNsrYang
).get('/ns-instance-opdata')
299 nsrs
= nsr_opdata
.nsr
301 assert len(nsrs
) == 1
302 current_nsr
= nsrs
[0]
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)
307 def test_monitoring_params(self
, proxy
):
310 1. The value counter ticks?
311 2. If the meta fields are copied over
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
)
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
)
323 fields
= mon_des
.as_dict().keys()
325 assert getattr(mon_des
, field
) == getattr(mon_rec
, field
)
327 #assert mon_rec.value_integer > 0
329 def test_cm_nsr(self
, proxy
):
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
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
)
341 assert con_data
.name
== "ping_pong_nsd"
342 assert len(con_data
.cm_vnfr
) == 2
344 state_path
= con_nsr_xpath
+ "/state"
345 proxy(RwConmanYang
).wait_for(state_path
, 'ready', timeout
=120)
347 def test_cm_vnfr(self
, proxy
):
350 1. The ID of Vnfr in cm-state
353 4. Checks for a reachable IP in mgmt_interface
354 5. Basic checks for connection point and cfg_location.
356 def is_reachable(ip
, timeout
=10):
357 rc
= subprocess
.call(["ping", "-c1", "-w", str(timeout
), ip
])
362 nsr_cfg
, _
= list(yield_nsrc_nsro_pairs(proxy
))[0]
363 con_nsr_xpath
= "/cm-state/cm-nsr[id='{}']".format(nsr_cfg
.id)
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
)
369 assert con_data
is not None
371 state_path
= con_vnfr_path
+ "/state"
372 proxy(RwConmanYang
).wait_for(state_path
, 'ready', timeout
=120)
374 con_data
= proxy(RwConmanYang
).get(con_vnfr_path
)
375 assert is_reachable(con_data
.mgmt_interface
.ip_address
) is True
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
382 assert con_data
.cfg_location
is not None
384 @pytest.mark
.depends('nsr')
385 @pytest.mark
.setup('nfvi')
386 @pytest.mark
.incremental
387 class TestNfviMetrics(object):
389 def test_records_present(self
, proxy
):
390 assert_records(proxy
)
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
):
395 Verify the NFVI metrics
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
401 2. Check if the 'utilization' field has a valid value (> 0) and matches
402 with the 'used' field, if available.
404 for nsd
, nsr
in yield_nsd_nsr_pairs(proxy
):
405 nfvi_metrics
= nsr
.nfvi_metrics
406 computed_metrics
= collections
.defaultdict(int)
408 # Get the constituent VNF records.
409 for vnfd
, vnfr
in yield_vnfd_vnfr_pairs(proxy
, nsr
):
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
)
419 assert nfvi_metrics
.vm
.active_vm
== computed_metrics
['vm']
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)
426 if total_available
is not None:
427 assert computed_metrics
[metric_name
] == total_available
430 for metric_name
in ['memory', 'storage', 'vcpu']:
431 metric_data
= getattr(nfvi_metrics
, metric_name
)
433 utilization
= metric_data
.utilization
434 # assert utilization > 0
436 # If used field is available, check if it matches with utilization!
437 total
= metric_data
.total
438 used
= getattr(metric_data
, 'used', None)
441 computed_utilization
= round((used
/total
) * 100, 2)
442 assert abs(computed_utilization
- utilization
) <= 0.1
446 @pytest.mark
.depends('nfvi')
447 @pytest.mark
.incremental
448 class TestRecordsDescriptors
:
449 def test_create_update_vnfd(self
, proxy
, updated_ping_pong_records
):
451 Verify VNFD related operations
454 If a VNFD record is created
456 ping_vnfd
, pong_vnfd
, _
= updated_ping_pong_records
457 vnfdproxy
= proxy(RwVnfdYang
)
459 for vnfd_record
in [ping_vnfd
, pong_vnfd
]:
460 xpath
= "/vnfd-catalog/vnfd"
461 vnfdproxy
.create_config(xpath
, vnfd_record
.vnfd
)
463 xpath
= "/vnfd-catalog/vnfd[id='{}']".format(vnfd_record
.id)
464 vnfd
= vnfdproxy
.get(xpath
)
465 assert vnfd
.id == vnfd_record
.id
467 vnfdproxy
.replace_config(xpath
, vnfd_record
.vnfd
)
469 def test_create_update_nsd(self
, proxy
, updated_ping_pong_records
):
471 Verify NSD related operations
474 If NSD record was created
476 _
, _
, ping_pong_nsd
= updated_ping_pong_records
477 nsdproxy
= proxy(NsdYang
)
479 xpath
= "/nsd-catalog/nsd"
480 nsdproxy
.create_config(xpath
, ping_pong_nsd
.descriptor
)
482 xpath
= "/nsd-catalog/nsd[id='{}']".format(ping_pong_nsd
.id)
483 nsd
= nsdproxy
.get(xpath
)
484 assert nsd
.id == ping_pong_nsd
.id
486 nsdproxy
.replace_config(xpath
, ping_pong_nsd
.descriptor
)