blob: 83f74bfb7c9a97fd6806b4c93d5d79ce5a5eca2b [file] [log] [blame]
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -04001#!/usr/bin/env python
2"""
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -04003#
4# Copyright 2016-2017 RIFT.io Inc
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -04005#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19@file test_onboard.py
20@author Varun Prasad (varun.prasad@riftio.com)
21@brief Onboard descriptors
22"""
23
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040024import gi
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040025import json
26import logging
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040027import numpy as np
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040028import os
29import pytest
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040030import random
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040031import requests
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040032import requests_toolbelt
33import shlex
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040034import shutil
35import subprocess
36import time
37import uuid
38
39import rift.auto.mano
40import rift.auto.session
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040041import rift.auto.descriptor
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040042
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040043gi.require_version('RwNsrYang', '1.0')
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040044gi.require_version('RwProjectVnfdYang', '1.0')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040045gi.require_version('RwLaunchpadYang', '1.0')
46gi.require_version('RwBaseYang', '1.0')
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040047gi.require_version('RwStagingMgmtYang', '1.0')
48gi.require_version('RwPkgMgmtYang', '1.0')
49gi.require_version('RwVlrYang', '1.0')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040050
51from gi.repository import (
52 RwcalYang,
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040053 RwProjectNsdYang,
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040054 RwNsrYang,
55 RwVnfrYang,
56 NsrYang,
57 VnfrYang,
58 VldYang,
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040059 RwProjectVnfdYang,
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040060 RwLaunchpadYang,
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040061 RwBaseYang,
62 RwStagingMgmtYang,
63 RwPkgMgmtYang,
64 RwImageMgmtYang,
65 RwTypes,
66 RwVlrYang
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040067)
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040068gi.require_version('RwKeyspec', '1.0')
69from gi.repository.RwKeyspec import quoted_key
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040070
71logging.basicConfig(level=logging.DEBUG)
72
73
74@pytest.fixture(scope='module')
75def vnfd_proxy(request, mgmt_session):
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040076 return mgmt_session.proxy(RwProjectVnfdYang)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040077
78@pytest.fixture(scope='module')
79def rwvnfr_proxy(request, mgmt_session):
80 return mgmt_session.proxy(RwVnfrYang)
81
82@pytest.fixture(scope='module')
83def vld_proxy(request, mgmt_session):
84 return mgmt_session.proxy(VldYang)
85
86
87@pytest.fixture(scope='module')
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040088def rwvlr_proxy(request, mgmt_session):
89 return mgmt_session.proxy(RwVlrYang)
90
91
92@pytest.fixture(scope='module')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040093def nsd_proxy(request, mgmt_session):
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -040094 return mgmt_session.proxy(RwProjectNsdYang)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -040095
96
97@pytest.fixture(scope='module')
98def rwnsr_proxy(request, mgmt_session):
99 return mgmt_session.proxy(RwNsrYang)
100
101@pytest.fixture(scope='module')
102def base_proxy(request, mgmt_session):
103 return mgmt_session.proxy(RwBaseYang)
104
105
106@pytest.fixture(scope="module")
107def endpoint():
108 return "upload"
109
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400110
111def upload_descriptor(
112 logger,
113 descriptor_file,
114 scheme,
115 cert,
116 host="127.0.0.1",
117 endpoint="upload"):
118 curl_cmd = ('curl --cert {cert} --key {key} -F "descriptor=@{file}" -k '
119 '{scheme}://{host}:4567/api/{endpoint}'.format(
120 cert=cert[0],
121 key=cert[1],
122 scheme=scheme,
123 endpoint=endpoint,
124 file=descriptor_file,
125 host=host,
126 ))
127
128 logger.debug("Uploading descriptor %s using cmd: %s", descriptor_file, curl_cmd)
129 stdout = subprocess.check_output(shlex.split(curl_cmd), universal_newlines=True)
130
131 json_out = json.loads(stdout)
132 transaction_id = json_out["transaction_id"]
133
134 return transaction_id
135
136
137class DescriptorOnboardError(Exception):
138 pass
139
140
141def wait_onboard_transaction_finished(
142 logger,
143 transaction_id,
144 scheme,
145 cert,
146 timeout=600,
147 host="127.0.0.1",
148 endpoint="upload"):
149
150 logger.info("Waiting for onboard trans_id %s to complete", transaction_id)
151 uri = '%s://%s:4567/api/%s/%s/state' % (scheme, host, endpoint, transaction_id)
152
153 elapsed = 0
154 start = time.time()
155 while elapsed < timeout:
156 reply = requests.get(uri, cert=cert, verify=False)
157 state = reply.json()
158 if state["status"] == "success":
159 break
160 if state["status"] != "pending":
161 raise DescriptorOnboardError(state)
162
163 time.sleep(1)
164 elapsed = time.time() - start
165
166
167 if state["status"] != "success":
168 raise DescriptorOnboardError(state)
169 logger.info("Descriptor onboard was successful")
170
171
172def onboard_descriptor(host, file_name, logger, endpoint, scheme, cert):
173 """On-board/update the descriptor.
174
175 Args:
176 host (str): Launchpad IP
177 file_name (str): Full file path.
178 logger: Logger instance
179 endpoint (str): endpoint to be used for the upload operation.
180
181 """
182 logger.info("Onboarding package: %s", file_name)
183 trans_id = upload_descriptor(
184 logger,
185 file_name,
186 scheme,
187 cert,
188 host=host,
189 endpoint=endpoint)
190 wait_onboard_transaction_finished(
191 logger,
192 trans_id,
193 scheme,
194 cert,
195 host=host,
196 endpoint=endpoint)
197
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400198
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400199def get_ns_cloud_resources(rwvnfr_proxy, rwvlr_proxy):
200 """Returns a collection of ports, networks, VMs used by this NS"""
201 ns_cloud_resources = {'ports':[], 'vms':[], 'networks':[]}
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400202
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400203 # Get ports and VMs associated with each VNF
204 vnfrs = rwvnfr_proxy.get('/rw-project:project[rw-project:name="default"]/vnfr-catalog/vnfr', list_obj=True)
205 for vnfr in vnfrs.vnfr:
206 for cp in vnfr.connection_point:
207 ns_cloud_resources['ports'].append(cp.connection_point_id)
208 for vdur in vnfr.vdur:
209 ns_cloud_resources['vms'].append(vdur.vim_id)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400210
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400211 # Get the network associated with each NS
212 vlrs = rwvlr_proxy.get('/rw-project:project[rw-project:name="default"]/vlr-catalog/vlr', list_obj=True)
213 for vlr in vlrs.vlr:
214 ns_cloud_resources['networks'].append(vlr.network_id)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400215
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400216 return ns_cloud_resources
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400217
218
219@pytest.mark.setup('nsr')
220@pytest.mark.depends('launchpad')
221@pytest.mark.incremental
222class TestNsrStart(object):
223 """A brief overview of the steps performed.
224 1. Generate & on-board new descriptors
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400225 2. Start the NSR
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400226 """
227
228 def test_upload_descriptors(
229 self,
230 logger,
231 vnfd_proxy,
232 nsd_proxy,
233 mgmt_session,
234 scheme,
235 cert,
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400236 descriptors,
237 iteration,
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400238 ):
239 """Generates & On-boards the descriptors.
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400240
241 1. Request a staging area: RPC returns an endpoint and port
242 1. Upload the file to the endpoint, return the endpoint to download
243 2. Reconstruct the URL and trigger an RPC upload for the package.
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400244 """
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400245 # We are instantiating the NS twice in port-sequencing test. Seconds NS instantiation will be using already uploaded
246 # descriptors with updated interface positional values.
247 if iteration==1 and pytest.config.getoption("--port-sequencing"):
248 pytest.skip()
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400249 endpoint = "upload"
250
251 for file_name in descriptors:
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400252
253 ip = RwStagingMgmtYang.YangInput_RwStagingMgmt_CreateStagingArea.from_dict({
254 "package_type": "VNFD"})
255
256 if "nsd" in file_name:
257 ip.package_type = "NSD"
258
259 data = mgmt_session.proxy(RwStagingMgmtYang).rpc(ip)
260 form = requests_toolbelt.MultipartEncoder(fields={
261 'file': (os.path.basename(file_name),
262 open(file_name, 'rb'),
263 'application/octet-stream')
264 })
265
266 response = requests.post(
267 "{}://{}:{}/{}".format(
268 scheme,
269 mgmt_session.host,
270 data.port,
271 data.endpoint),
272 data=form.to_string(),
273 cert=cert, # cert is a tuple
274 verify=False,
275 headers={"Content-Type": "multipart/form-data"})
276
277 resp = json.loads(response.text)
278 url = "https://{}:{}{}".format(mgmt_session.host, data.port, resp['path'])
279
280 ip = RwPkgMgmtYang.YangInput_RwPkgMgmt_PackageCreate.from_dict({
281 "package_type": "VNFD",
282 "external_url": url
283 })
284
285 if "nsd" in file_name:
286 ip.package_type = "NSD"
287
288 # trigger the upload.
289 resp = mgmt_session.proxy(RwPkgMgmtYang).rpc(ip)
290
291 wait_onboard_transaction_finished(
292 logger,
293 resp.transaction_id,
294 scheme,
295 cert,
296 host=mgmt_session.host,
297 endpoint=endpoint)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400298
299 descriptor_vnfds, descriptor_nsd = descriptors[:-1], descriptors[-1]
300
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400301 catalog = vnfd_proxy.get_config('/rw-project:project[rw-project:name="default"]/vnfd-catalog')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400302 actual_vnfds = catalog.vnfd
303 assert len(actual_vnfds) == len(descriptor_vnfds), \
304 "There should {} vnfds".format(len(descriptor_vnfds))
305
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400306 catalog = nsd_proxy.get_config('/rw-project:project[rw-project:name="default"]/nsd-catalog')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400307 actual_nsds = catalog.nsd
308 assert len(actual_nsds) == 1, "There should only be a single nsd"
309
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400310 @pytest.mark.skipif(not pytest.config.getoption('--upload-images-multiple-accounts'),
311 reason="need --upload-images-multiple-accounts option to run")
312 def test_images_uploaded_multiple_accounts(self, logger, mgmt_session, random_image_name, cloud_accounts, cal):
313 image_mgmt_proxy = mgmt_session.proxy(RwImageMgmtYang)
314 upload_jobs = image_mgmt_proxy.get('/rw-project:project[rw-project:name="default"]/upload-jobs')
315 logger.info('Embedded image name(apart from ping pong Fedora images): {}'.format(random_image_name))
316 for job in upload_jobs.job:
317 assert image_mgmt_proxy.wait_for('/rw-project:project[rw-project:name="default"]/upload-jobs/job[id={}]/status'.format(quoted_key(job.id)), 'COMPLETED', timeout=240)
318 assert len(job.upload_tasks) == len(cloud_accounts)
319 for upload_task in job.upload_tasks:
320 assert upload_task.status == 'COMPLETED'
321
322 assert len(upload_jobs.job) == 3
323
324 # Check whether images are present in VIMs
325 for account in cloud_accounts:
326 rc, res = cal.get_image_list(RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList.from_dict(account.as_dict()))
327 assert rc == RwTypes.RwStatus.SUCCESS
328 assert [image for image in res.imageinfo_list if image.name == random_image_name]
329
330 @pytest.mark.skipif(not pytest.config.getoption("--vnf-onboard-delete"), reason="need --vnf-onboard-delete option to run")
331 def test_upload_delete_descriptors(self, logger, mgmt_session, vnfd_proxy, descriptors, vnf_onboard_delete):
332 """Randomly upload and delete VNFs. With each upload/delete, verify if the VNF
333 gets uploaded/deleted successfully.
334 """
335 xpath = "/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd[id={}]"
336 iteration, vnf_count = map(int, vnf_onboard_delete.split(','))
337
338 # Get the VNF paths to be used for onboarding
339 all_vnfs = [pkg_path for pkg_path in descriptors if '_nsd' not in os.path.basename(pkg_path)]
340 if vnf_count > len(all_vnfs):
341 vnf_count = len(all_vnfs)
342 available_vnfs = random.sample(all_vnfs, vnf_count)
343
344 # Get the add, delete iterations
345 add_del_seq = list(np.random.choice(['add', 'del'], iteration))
346 random.shuffle(add_del_seq)
347 logger.info('Vnf add-delete iteration sequence: {}'.format(add_del_seq))
348
349 uploaded_vnfs = {}
350
351 def get_vnfd_list():
352 """Returns list of VNFDs"""
353 vnfd_obj = vnfd_proxy.get("/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd", list_obj=True)
354 return vnfd_obj.vnfd if vnfd_obj else []
355
356 def delete_vnfd():
357 """Deletes a VNFD"""
358 vnf_path, vnfd_id = random.choice(list(uploaded_vnfs.items()))
359 logger.info('Deleting VNF {} having id {}'.format(os.path.basename(vnf_path), vnfd_id))
360 vnfd_proxy.delete_config(xpath.format(quoted_key(vnfd_id)))
361 uploaded_vnfs.pop(vnf_path)
362 available_vnfs.append(vnf_path)
363 assert not [vnfd for vnfd in get_vnfd_list() if vnfd.id == vnfd_id]
364
365 for op_type in add_del_seq:
366 if op_type =='del':
367 if uploaded_vnfs:
368 delete_vnfd()
369 continue
370 op_type = 'add'
371
372 if op_type == 'add':
373 if not available_vnfs:
374 delete_vnfd()
375 continue
376 vnf_path = random.choice(available_vnfs)
377 logger.info('Adding VNF {}'.format(os.path.basename(vnf_path)))
378 rift.auto.descriptor.onboard(mgmt_session, vnf_path)
379 vnfs = get_vnfd_list()
380 assert len(vnfs) == len(uploaded_vnfs) + 1
381 vnfd = [vnfd for vnfd in vnfs if vnfd.id not in list(uploaded_vnfs.values())]
382 assert len(vnfd) == 1
383 vnfd = vnfd[0]
384 assert vnfd.name
385 assert vnfd.connection_point
386 assert vnfd.vdu
387 uploaded_vnfs[vnf_path] = vnfd.id
388 available_vnfs.remove(vnf_path)
389
390 assert len(get_vnfd_list()) == len(uploaded_vnfs)
391 logger.info('Onboarded VNFs : {}'.format(uploaded_vnfs))
392
393 assert len(available_vnfs) + len(uploaded_vnfs) == vnf_count
394 # cleanup - Delete VNFs(if any)
395 for vnfd_id in uploaded_vnfs.values():
396 vnfd_proxy.delete_config(xpath.format(quoted_key(vnfd_id)))
397
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400398 @pytest.mark.feature("upload-image")
399 def test_upload_images(self, descriptor_images, cloud_host, cloud_user, cloud_tenants):
400
401 openstack = rift.auto.mano.OpenstackManoSetup(
402 cloud_host,
403 cloud_user,
404 [(tenant, "private") for tenant in cloud_tenants])
405
406 for image_location in descriptor_images:
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400407 image = RwcalYang.YangData_RwProject_Project_VimResources_ImageinfoList.from_dict({
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400408 'name': os.path.basename(image_location),
409 'location': image_location,
410 'disk_format': 'qcow2',
411 'container_format': 'bare'})
412 openstack.create_image(image)
413
414
415 def test_set_scaling_params(self, nsd_proxy):
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400416 nsds = nsd_proxy.get('/rw-project:project[rw-project:name="default"]/nsd-catalog')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400417 nsd = nsds.nsd[0]
418 for scaling_group in nsd.scaling_group_descriptor:
419 scaling_group.max_instance_count = 2
420
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400421 nsd_proxy.replace_config('/rw-project:project[rw-project:name="default"]/nsd-catalog/nsd[id={}]'.format(
422 quoted_key(nsd.id)), nsd)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400423
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400424 @pytest.mark.skipif(not (pytest.config.getoption("--update-vnfd-instantiate") or pytest.config.getoption("--port-sequencing")),
425 reason="need --update-vnfd-instantiate or --port-sequencing option to run")
426 def test_update_vnfd(self, vnfd_proxy, iteration, port_sequencing_intf_positions):
427 """Updates few fields of ping pong VNFDs and verify those changes
428 """
429 xpath = "/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd[id={}]"
430 vnfd_catalog = "/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd"
431
432 if iteration==0 and pytest.config.getoption("--port-sequencing"):
433 pytest.skip()
434
435 def get_vnfd():
436 vnfds = vnfd_proxy.get(vnfd_catalog, list_obj=True)
437 dict_ = {}
438
439 # Get ping pong VNFDs
440 for vnfd in vnfds.vnfd:
441 if 'ping' in vnfd.name:
442 dict_['ping'] = vnfd
443 if 'pong' in vnfd.name:
444 dict_['pong'] = vnfd
445 return dict_
446
447 vnfds_dict = get_vnfd()
448 update_data = {'ping':{'static_ip_address':'31.31.31.60'}, 'pong':{'static_ip_address':'31.31.31.90'}}
449 port_sequencing_intf_positions_tmp = port_sequencing_intf_positions[:]
450
451 # Modify/add fields in VNFDs
452 for name_, vnfd in vnfds_dict.items():
453 if pytest.config.getoption('--update-vnfd-instantiate'):
454 vnfd.vdu[0].interface[1].static_ip_address = update_data[name_]['static_ip_address']
455 if pytest.config.getoption('--port-sequencing'):
456 vnfd_intf_list = vnfd.vdu[0].interface
457 # for ping vnfd, remove positional values from all interfaces
458 # for pong vnfd, modify the positional values as per fixture port_sequencing_intf_positions
459 if 'ping' in vnfd.name:
460 tmp_intf_list = []
461 for i in range(len(vnfd_intf_list)):
462 tmp_intf_dict = vnfd_intf_list[-1].as_dict()
463 del tmp_intf_dict['position']
464 vnfd_intf_list.pop()
465 tmp_intf_list.append(tmp_intf_dict)
466 for intf_dict_without_positional_values in tmp_intf_list:
467 new_intf = vnfd.vdu[0].interface.add()
468 new_intf.from_dict(intf_dict_without_positional_values)
469
470 if 'pong' in vnfd.name:
471 for intf in vnfd_intf_list:
472 if 'position' in intf:
473 intf.position = port_sequencing_intf_positions_tmp.pop()
474
475 # Update/save the VNFDs
476 for vnfd in vnfds_dict.values():
477 vnfd_proxy.replace_config(xpath.format(quoted_key(vnfd.id)), vnfd)
478
479 # Match whether data is updated
480 vnfds_dict = get_vnfd()
481 assert vnfds_dict
482 for name_, vnfd in vnfds_dict.items():
483 if pytest.config.getoption('--update-vnfd-instantiate'):
484 assert vnfd.vdu[0].interface[1].static_ip_address == update_data[name_]['static_ip_address']
485 if pytest.config.getoption('--port-sequencing'):
486 if 'ping' in vnfd.name:
487 for intf in vnfd.vdu[0].interface:
488 assert 'position' not in intf.as_dict()
489 if 'pong' in vnfd.name:
490 tmp_positional_values_list = []
491 for intf in vnfd.vdu[0].interface:
492 if 'position' in intf.as_dict():
493 tmp_positional_values_list.append(intf.position)
494 assert set(tmp_positional_values_list) == set(port_sequencing_intf_positions)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400495
496 def test_instantiate_nsr(self, logger, nsd_proxy, rwnsr_proxy, base_proxy, cloud_account_name):
497
498 def verify_input_parameters(running_config, config_param):
499 """
500 Verify the configured parameter set against the running configuration
501 """
502 for run_input_param in running_config.input_parameter:
503 if (run_input_param.xpath == config_param.xpath and
504 run_input_param.value == config_param.value):
505 return True
506
507 assert False, ("Verification of configured input parameters: { xpath:%s, value:%s} "
508 "is unsuccessful.\nRunning configuration: %s" % (config_param.xpath,
509 config_param.value,
510 running_config.input_parameter))
511
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400512 catalog = nsd_proxy.get_config('/rw-project:project[rw-project:name="default"]/nsd-catalog')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400513 nsd = catalog.nsd[0]
514
515 input_parameters = []
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400516 descr_xpath = "/nsd:nsd-catalog/nsd:nsd/nsd:vendor"
517 descr_value = "New Vendor"
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400518 in_param_id = str(uuid.uuid4())
519
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400520 input_param_1 = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter(
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400521 xpath=descr_xpath,
522 value=descr_value)
523
524 input_parameters.append(input_param_1)
525
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400526 nsr = rift.auto.descriptor.create_nsr(cloud_account_name, nsd.name, nsd, input_param_list=input_parameters)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400527
528 logger.info("Instantiating the Network Service")
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400529 rwnsr_proxy.create_config('/rw-project:project[rw-project:name="default"]/ns-instance-config/nsr', nsr)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400530
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400531 nsr_opdata = rwnsr_proxy.get('/rw-project:project[rw-project:name="default"]/ns-instance-opdata/nsr[ns-instance-config-ref={}]'.format(quoted_key(nsr.id)))
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400532 assert nsr_opdata is not None
533
534 # Verify the input parameter configuration
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400535 running_config = rwnsr_proxy.get_config("/rw-project:project[rw-project:name='default']/ns-instance-config/nsr[id=%s]" % quoted_key(nsr.id))
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400536 for input_param in input_parameters:
537 verify_input_parameters(running_config, input_param)
538
539 def test_wait_for_nsr_started(self, rwnsr_proxy):
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400540 """Verify NSR instances enter 'running' operational-status
541 """
542 nsr_opdata = rwnsr_proxy.get('/rw-project:project[rw-project:name="default"]/ns-instance-opdata')
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400543 nsrs = nsr_opdata.nsr
544
545 for nsr in nsrs:
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400546 xpath = "/rw-project:project[rw-project:name='default']/ns-instance-opdata/nsr[ns-instance-config-ref={}]/operational-status".format(quoted_key(nsr.ns_instance_config_ref))
547 rwnsr_proxy.wait_for(xpath, "running", fail_on=['failed'], timeout=400)
548
549 def test_wait_for_nsr_configured(self, rwnsr_proxy):
550 """Verify NSR instances enter 'configured' config-status
551 """
552 nsr_opdata = rwnsr_proxy.get('/rw-project:project[rw-project:name="default"]/ns-instance-opdata')
553 nsrs = nsr_opdata.nsr
554
555 for nsr in nsrs:
556 xpath = "/rw-project:project[rw-project:name='default']/ns-instance-opdata/nsr[ns-instance-config-ref={}]/config-status".format(quoted_key(nsr.ns_instance_config_ref))
557 rwnsr_proxy.wait_for(xpath, "configured", fail_on=['failed'], timeout=400)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400558
559
560@pytest.mark.teardown('nsr')
561@pytest.mark.depends('launchpad')
562@pytest.mark.incremental
563class TestNsrTeardown(object):
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400564
565 def test_delete_embedded_images(self, random_image_name, cloud_accounts, cal):
566 """Deletes images embedded in VNF from VIM. It only deletes additional images, not
567 the Fedora ping pong images"""
568 for account in cloud_accounts:
569 rc, rsp = cal.get_image_list(RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList.from_dict(account.as_dict()))
570 assert rc == RwTypes.RwStatus.SUCCESS
571 if rsp is not None:
572 for image in rsp.imageinfo_list:
573 if random_image_name in image.name:
574 cal.delete_image(RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList.from_dict(account.as_dict()), image.id)
575
576 def test_terminate_nsr(self, rwvnfr_proxy, rwnsr_proxy, logger, cloud_type,
577 rwvlr_proxy, vim_clients, cloud_account_name):
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400578 """
579 Terminate the instance and check if the record is deleted.
580
581 Asserts:
582 1. NSR record is deleted from instance-config.
583
584 """
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400585 # Collects the Cloud resources like ports, networks, VMs used by the current NS
586 ns_cloud_resources = get_ns_cloud_resources(rwvnfr_proxy, rwvlr_proxy)
587 logger.info('Cloud resources used by NS: {}'.format(ns_cloud_resources))
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400588
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400589 logger.debug("Terminating NSR")
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400590 wait_after_kill = True
591 if cloud_type == "mock":
592 wait_after_kill = False
593
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400594 rift.auto.descriptor.terminate_nsr(rwvnfr_proxy, rwnsr_proxy,
595 rwvlr_proxy, logger,
596 wait_after_kill=wait_after_kill)
597 # Collect all the ports, networks VMs from openstack and
598 # check if previously collected resources (i.e ns_cloud_resources) are still present in this collection
599 start_time = time.time()
600 while time.time()-start_time < 240:
601 try:
602 vim_client = vim_clients[cloud_account_name]
603 vim_resources = dict()
604 vim_resources['networks'] = vim_client.neutron_network_list()
605 vim_resources['vms'] = vim_client.nova_server_list()
606 vim_resources['ports'] = vim_client.neutron_port_list()
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400607
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400608 for resource_type in ns_cloud_resources.keys():
609 logger.debug("Verifying all %s resources have been removed from vim", resource_type)
610 vim_resource_ids = [
611 vim_resource['id'] for vim_resource in vim_resources[resource_type]
612 if 'shared' not in vim_resource.keys()
613 or not vim_resource['shared']
614 ]
615 for ns_resource_id in ns_cloud_resources[resource_type]:
616 logger.debug('Verifying %s resource %s removed', resource_type, ns_resource_id)
617 assert ns_resource_id not in vim_resource_ids
618 return
619 except AssertionError:
620 time.sleep(10)
621 raise AssertionError("Resources not cleared from openstack")
622
623 def test_delete_records(self, nsd_proxy, vnfd_proxy, iteration):
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400624 """Delete the NSD & VNFD records
625
626 Asserts:
627 The records are deleted.
628 """
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400629 # We are instantiating the NS twice in port-sequencing test. Seconds NS instantiation will be using already uploaded
630 # descriptors with updated interface positional values.
631 if iteration==0 and pytest.config.getoption("--port-sequencing"):
632 pytest.skip()
633 nsds = nsd_proxy.get("/rw-project:project[rw-project:name='default']/nsd-catalog/nsd", list_obj=True)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400634 for nsd in nsds.nsd:
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400635 xpath = "/rw-project:project[rw-project:name='default']/nsd-catalog/nsd[id={}]".format(quoted_key(nsd.id))
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400636 nsd_proxy.delete_config(xpath)
637
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400638 nsds = nsd_proxy.get("/rw-project:project[rw-project:name='default']/nsd-catalog/nsd", list_obj=True)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400639 assert nsds is None or len(nsds.nsd) == 0
640
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400641 vnfds = vnfd_proxy.get("/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd", list_obj=True)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400642 for vnfd_record in vnfds.vnfd:
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400643 xpath = "/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd[id={}]".format(quoted_key(vnfd_record.id))
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400644 vnfd_proxy.delete_config(xpath)
645
Jeremy Mordkoff4870d0e2017-09-30 20:28:33 -0400646 vnfds = vnfd_proxy.get("/rw-project:project[rw-project:name='default']/vnfd-catalog/vnfd", list_obj=True)
Jeremy Mordkoff6f07e6f2016-09-07 18:56:51 -0400647 assert vnfds is None or len(vnfds.vnfd) == 0