2d7eff396f7aabf5a2d68ed22ab47f73b11e62d7
[osm/NBI.git] / test.py
1 #! /usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import getopt
18 import sys
19 import requests
20 import json
21 import logging
22 import yaml
23 # import json
24 # import tarfile
25 from time import sleep
26 from random import randint
27 import os
28 from sys import stderr
29
30 __author__ = "Alfonso Tierno, alfonso.tiernosepulveda@telefonica.com"
31 __date__ = "$2018-03-01$"
32 __version__ = "0.3"
33 version_date = "Oct 2018"
34
35
36 def usage():
37 print("Usage: ", sys.argv[0], "[options]")
38 print(" Performs system tests over running NBI. It can be used for real OSM test using option '--test-osm'")
39 print(" If this is the case env variables 'OSMNBITEST_VIM_NAME' must be supplied to create a VIM if not exist "
40 "where deployment is done")
41 print("OPTIONS")
42 print(" -h|--help: shows this help")
43 print(" --insecure: Allows non trusted https NBI server")
44 print(" --list: list available tests")
45 print(" --manual-check: Deployment tests stop after deployed to allow manual inspection. Only make sense with "
46 "'--test-osm'")
47 print(" -p|--password PASSWORD: NBI access password. 'admin' by default")
48 print(" ---project PROJECT: NBI access project. 'admin' by default")
49 print(" --test TEST[,...]: Execute only a test or a comma separated list of tests")
50 print(" --params key=val: params to the previous test. key can be vnfd-files, nsd-file, ns-name, ns-config")
51 print(" --test-osm: If missing this test is intended for NBI only, no other OSM components are expected. Use "
52 "this flag to test the system. LCM and RO components are expected to be up and running")
53 print(" --timeout TIMEOUT: General NBI timeout, by default {}s".format(timeout))
54 print(" --timeout-deploy TIMEOUT: Timeout used for getting NS deployed, by default {}s".format(timeout_deploy))
55 print(" --timeout-configure TIMEOUT: Timeout used for getting NS deployed and configured,"
56 " by default {}s".format(timeout_configure))
57 print(" -u|--user USERNAME: NBI access username. 'admin' by default")
58 print(" --url URL: complete NBI server URL. 'https//localhost:9999/osm' by default")
59 print(" -v|--verbose print debug information, can be used several times")
60 print(" --no-verbose remove verbosity")
61 print(" --version: prints current version")
62 print("ENV variables used for real deployment tests with option osm-test.")
63 print(" export OSMNBITEST_VIM_NAME=vim-name")
64 print(" export OSMNBITEST_VIM_URL=vim-url")
65 print(" export OSMNBITEST_VIM_TYPE=vim-type")
66 print(" export OSMNBITEST_VIM_TENANT=vim-tenant")
67 print(" export OSMNBITEST_VIM_USER=vim-user")
68 print(" export OSMNBITEST_VIM_PASSWORD=vim-password")
69 print(" export OSMNBITEST_VIM_CONFIG=\"vim-config\"")
70 print(" export OSMNBITEST_NS_NAME=\"vim-config\"")
71 return
72
73
74 r_header_json = {"Content-type": "application/json"}
75 headers_json = {"Content-type": "application/json", "Accept": "application/json"}
76 r_header_yaml = {"Content-type": "application/yaml"}
77 headers_yaml = {"Content-type": "application/yaml", "Accept": "application/yaml"}
78 r_header_text = {"Content-type": "text/plain"}
79 r_header_octect = {"Content-type": "application/octet-stream"}
80 headers_text = {"Accept": "text/plain,application/yaml"}
81 r_header_zip = {"Content-type": "application/zip"}
82 headers_zip = {"Accept": "application/zip,application/yaml"}
83 headers_zip_yaml = {"Accept": "application/yaml", "Content-type": "application/zip"}
84 r_headers_yaml_location_vnfd = {"Location": "/vnfpkgm/v1/vnf_packages_content/", "Content-Type": "application/yaml"}
85 r_headers_yaml_location_nsd = {"Location": "/nsd/v1/ns_descriptors_content/", "Content-Type": "application/yaml"}
86 r_headers_yaml_location_nst = {"Location": "/nst/v1/netslice_templates_content", "Content-Type": "application/yaml"}
87 r_headers_yaml_location_nslcmop = {"Location": "nslcm/v1/ns_lcm_op_occs/", "Content-Type": "application/yaml"}
88 r_headers_yaml_location_nsilcmop = {"Location": "/osm/nsilcm/v1/nsi_lcm_op_occs/", "Content-Type": "application/yaml"}
89
90 # test ones authorized
91 test_authorized_list = (
92 ("AU1", "Invalid vnfd id", "GET", "/vnfpkgm/v1/vnf_packages/non-existing-id",
93 headers_json, None, 404, r_header_json, "json"),
94 ("AU2", "Invalid nsd id", "GET", "/nsd/v1/ns_descriptors/non-existing-id",
95 headers_yaml, None, 404, r_header_yaml, "yaml"),
96 ("AU3", "Invalid nsd id", "DELETE", "/nsd/v1/ns_descriptors_content/non-existing-id",
97 headers_yaml, None, 404, r_header_yaml, "yaml"),
98 )
99 timeout = 120 # general timeout
100 timeout_deploy = 60*10 # timeout for NS deploying without charms
101 timeout_configure = 60*20 # timeout for NS deploying and configuring
102
103
104 class TestException(Exception):
105 pass
106
107
108 class TestRest:
109 def __init__(self, url_base, header_base=None, verify=False, user="admin", password="admin", project="admin"):
110 self.url_base = url_base
111 if header_base is None:
112 self.header_base = {}
113 else:
114 self.header_base = header_base.copy()
115 self.s = requests.session()
116 self.s.headers = self.header_base
117 self.verify = verify
118 self.token = False
119 self.user = user
120 self.password = password
121 self.project = project
122 self.vim_id = None
123 # contains ID of tests obtained from Location response header. "" key contains last obtained id
124 self.last_id = ""
125 self.test_name = None
126 self.step = 0 # number of subtest under test
127 self.passed_tests = 0
128 self.failed_tests = 0
129
130 def set_test_name(self, test_name):
131 self.test_name = test_name
132 self.step = 0
133 self.last_id = ""
134
135 def set_header(self, header):
136 self.s.headers.update(header)
137
138 def set_tet_name(self, test_name):
139 self.test_name = test_name
140
141 def unset_header(self, key):
142 if key in self.s.headers:
143 del self.s.headers[key]
144
145 def test(self, description, method, url, headers, payload, expected_codes, expected_headers,
146 expected_payload, store_file=None, pooling=False):
147 """
148 Performs an http request and check http code response. Exit if different than allowed. It get the returned id
149 that can be used by following test in the URL with {name} where name is the name of the test
150 :param description: description of the test
151 :param method: HTTP method: GET,PUT,POST,DELETE,...
152 :param url: complete URL or relative URL
153 :param headers: request headers to add to the base headers
154 :param payload: Can be a dict, transformed to json, a text or a file if starts with '@'
155 :param expected_codes: expected response codes, can be int, int tuple or int range
156 :param expected_headers: expected response headers, dict with key values
157 :param expected_payload: expected payload, 0 if empty, 'yaml', 'json', 'text', 'zip', 'octet-stream'
158 :param store_file: filename to store content
159 :param pooling: if True do not count neither log this test. Because a pooling is done with many equal requests
160 :return: requests response
161 """
162 r = None
163 try:
164 if not self.s:
165 self.s = requests.session()
166 # URL
167 if not url:
168 url = self.url_base
169 elif not url.startswith("http"):
170 url = self.url_base + url
171
172 # replace url <> with the last ID
173 url = url.replace("<>", self.last_id)
174 if payload:
175 if isinstance(payload, str):
176 if payload.startswith("@"):
177 mode = "r"
178 file_name = payload[1:]
179 if payload.startswith("@b"):
180 mode = "rb"
181 file_name = payload[2:]
182 with open(file_name, mode) as f:
183 payload = f.read()
184 elif isinstance(payload, dict):
185 payload = json.dumps(payload)
186
187 if not pooling:
188 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, method, url)
189 logger.warning(test_description)
190 self.step += 1
191 stream = False
192 if expected_payload in ("zip", "octet-string") or store_file:
193 stream = True
194 __retry = 0
195 while True:
196 try:
197 r = getattr(self.s, method.lower())(url, data=payload, headers=headers, verify=self.verify,
198 stream=stream)
199 break
200 except requests.exceptions.ConnectionError as e:
201 if __retry == 2:
202 raise
203 logger.error("Exception {}. Retrying".format(e))
204 __retry += 1
205
206 if expected_payload in ("zip", "octet-string") or store_file:
207 logger.debug("RX {}".format(r.status_code))
208 else:
209 logger.debug("RX {}: {}".format(r.status_code, r.text))
210
211 # check response
212 if expected_codes:
213 if isinstance(expected_codes, int):
214 expected_codes = (expected_codes,)
215 if r.status_code not in expected_codes:
216 raise TestException(
217 "Got status {}. Expected {}. {}".format(r.status_code, expected_codes, r.text))
218
219 if expected_headers:
220 for header_key, header_val in expected_headers.items():
221 if header_key.lower() not in r.headers:
222 raise TestException("Header {} not present".format(header_key))
223 if header_val and header_val.lower() not in r.headers[header_key]:
224 raise TestException("Header {} does not contain {} but {}".format(header_key, header_val,
225 r.headers[header_key]))
226
227 if expected_payload is not None:
228 if expected_payload == 0 and len(r.content) > 0:
229 raise TestException("Expected empty payload")
230 elif expected_payload == "json":
231 try:
232 r.json()
233 except Exception as e:
234 raise TestException("Expected json response payload, but got Exception {}".format(e))
235 elif expected_payload == "yaml":
236 try:
237 yaml.safe_load(r.text)
238 except Exception as e:
239 raise TestException("Expected yaml response payload, but got Exception {}".format(e))
240 elif expected_payload in ("zip", "octet-string"):
241 if len(r.content) == 0:
242 raise TestException("Expected some response payload, but got empty")
243 # try:
244 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
245 # for tarinfo in tar:
246 # tarname = tarinfo.name
247 # print(tarname)
248 # except Exception as e:
249 # raise TestException("Expected zip response payload, but got Exception {}".format(e))
250 elif expected_payload == "text":
251 if len(r.content) == 0:
252 raise TestException("Expected some response payload, but got empty")
253 # r.text
254 if store_file:
255 with open(store_file, 'wb') as fd:
256 for chunk in r.iter_content(chunk_size=128):
257 fd.write(chunk)
258
259 location = r.headers.get("Location")
260 if location:
261 _id = location[location.rfind("/") + 1:]
262 if _id:
263 self.last_id = str(_id)
264 if not pooling:
265 self.passed_tests += 1
266 return r
267 except TestException as e:
268 self.failed_tests += 1
269 r_status_code = None
270 r_text = None
271 if r:
272 r_status_code = r.status_code
273 r_text = r.text
274 logger.error("{} \nRX code{}: {}".format(e, r_status_code, r_text))
275 return None
276 # exit(1)
277 except IOError as e:
278 if store_file:
279 logger.error("Cannot open file {}: {}".format(store_file, e))
280 else:
281 logger.error("Exception: {}".format(e), exc_info=True)
282 self.failed_tests += 1
283 return None
284 # exit(1)
285 except requests.exceptions.RequestException as e:
286 logger.error("Exception: {}".format(e))
287
288 def get_autorization(self): # user=None, password=None, project=None):
289 if self.token: # and self.user == user and self.password == password and self.project == project:
290 return
291 # self.user = user
292 # self.password = password
293 # self.project = project
294 r = self.test("Obtain token", "POST", "/admin/v1/tokens", headers_json,
295 {"username": self.user, "password": self.password, "project_id": self.project},
296 (200, 201), r_header_json, "json")
297 if not r:
298 return
299 response = r.json()
300 self.token = response["id"]
301 self.set_header({"Authorization": "Bearer {}".format(self.token)})
302
303 def remove_authorization(self):
304 if self.token:
305 self.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self.token), headers_json,
306 None, (200, 201, 204), None, None)
307 self.token = None
308 self.unset_header("Authorization")
309
310 def get_create_vim(self, test_osm):
311 if self.vim_id:
312 return self.vim_id
313 self.get_autorization()
314 if test_osm:
315 vim_name = os.environ.get("OSMNBITEST_VIM_NAME")
316 if not vim_name:
317 raise TestException(
318 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
319 else:
320 vim_name = "fakeVim"
321 # Get VIM
322 r = self.test("Get VIM ID", "GET", "/admin/v1/vim_accounts?name={}".format(vim_name), headers_json,
323 None, 200, r_header_json, "json")
324 if not r:
325 return
326 vims = r.json()
327 if vims:
328 return vims[0]["_id"]
329 # Add VIM
330 if test_osm:
331 # check needed environ parameters:
332 if not os.environ.get("OSMNBITEST_VIM_URL") or not os.environ.get("OSMNBITEST_VIM_TENANT"):
333 raise TestException("Env OSMNBITEST_VIM_URL and OSMNBITEST_VIM_TENANT are needed for create a real VIM"
334 " to deploy on whit the --test-osm option")
335 vim_data = "{{schema_version: '1.0', name: '{}', vim_type: {}, vim_url: '{}', vim_tenant_name: '{}', "\
336 "vim_user: {}, vim_password: {}".format(vim_name,
337 os.environ.get("OSMNBITEST_VIM_TYPE", "openstack"),
338 os.environ.get("OSMNBITEST_VIM_URL"),
339 os.environ.get("OSMNBITEST_VIM_TENANT"),
340 os.environ.get("OSMNBITEST_VIM_USER"),
341 os.environ.get("OSMNBITEST_VIM_PASSWORD"))
342 if os.environ.get("OSMNBITEST_VIM_CONFIG"):
343 vim_data += " ,config: {}".format(os.environ.get("OSMNBITEST_VIM_CONFIG"))
344 vim_data += "}"
345 else:
346 vim_data = "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
347 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
348 self.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml, vim_data,
349 (201), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
350 return self.last_id
351
352 def print_results(self):
353 print("\n\n\n--------------------------------------------")
354 print("TEST RESULTS: Total: {}, Passed: {}, Failed: {}".format(self.passed_tests + self.failed_tests,
355 self.passed_tests, self.failed_tests))
356 print("--------------------------------------------")
357
358 def wait_until_delete(self, url_op, timeout_delete):
359 """
360 Make a pooling until topic is not present, because of deleted
361 :param url_op:
362 :param timeout_delete:
363 :return:
364 """
365 description = "Wait to topic being deleted"
366 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, "GET", url_op)
367 logger.warning(test_description)
368 self.step += 1
369
370 wait = timeout_delete
371 while wait >= 0:
372 r = self.test(description, "GET", url_op, headers_yaml, None, (200, 404), None, r_header_yaml, "yaml",
373 pooling=True)
374 if not r:
375 return
376 if r.status_code == 404:
377 self.passed_tests += 1
378 break
379 elif r.status_code == 200:
380 wait -= 5
381 sleep(5)
382 else:
383 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete))
384 self.failed_tests += 1
385
386 def wait_operation_ready(self, ns_nsi, opp_id, timeout, expected_fail=False):
387 """
388 Wait until nslcmop or nsilcmop finished
389 :param ns_nsi: "ns" o "nsi"
390 :param opp_id: Id o fthe operation
391 :param timeout:
392 :param expected_fail:
393 :return: None. Updates passed/failed_tests
394 """
395 if ns_nsi == "ns":
396 url_op = "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id)
397 else:
398 url_op = "/nsilcm/v1/nsi_lcm_op_occs/{}".format(opp_id)
399 description = "Wait to {} lcm operation complete".format(ns_nsi)
400 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, "GET", url_op)
401 logger.warning(test_description)
402 self.step += 1
403 wait = timeout
404 while wait >= 0:
405 r = self.test(description, "GET", url_op, headers_json, None,
406 200, r_header_json, "json", pooling=True)
407 if not r:
408 return
409 nslcmop = r.json()
410 if "COMPLETED" in nslcmop["operationState"]:
411 if expected_fail:
412 logger.error("NS terminate has success, expecting failing: {}".format(nslcmop["detailed-status"]))
413 self.failed_tests += 1
414 else:
415 self.passed_tests += 1
416 break
417 elif "FAILED" in nslcmop["operationState"]:
418 if not expected_fail:
419 logger.error("NS terminate has failed: {}".format(nslcmop["detailed-status"]))
420 self.failed_tests += 1
421 else:
422 self.passed_tests += 1
423 break
424
425 print(".", end="", file=stderr)
426 wait -= 10
427 sleep(10)
428 else:
429 self.failed_tests += 1
430 logger.error("NS instantiate is not terminate after {} seconds".format(timeout))
431 return
432 print("", file=stderr)
433
434
435 class TestNonAuthorized:
436 description = "Test invalid URLs. methods and no authorization"
437
438 @staticmethod
439 def run(engine, test_osm, manual_check, test_params=None):
440 engine.set_test_name("NonAuth")
441 engine.remove_authorization()
442 test_not_authorized_list = (
443 ("Invalid token", "GET", "/admin/v1/users", headers_json, None, 401, r_header_json, "json"),
444 ("Invalid URL", "POST", "/admin/v1/nonexist", headers_yaml, None, 405, r_header_yaml, "yaml"),
445 ("Invalid version", "DELETE", "/admin/v2/users", headers_yaml, None, 405, r_header_yaml, "yaml"),
446 )
447 for t in test_not_authorized_list:
448 engine.test(*t)
449
450
451 class TestUsersProjects:
452 description = "test project and user creation"
453
454 @staticmethod
455 def run(engine, test_osm, manual_check, test_params=None):
456 engine.set_test_name("UserProject")
457 engine.get_autorization()
458 engine.test("Create project non admin", "POST", "/admin/v1/projects", headers_json, {"name": "P1"},
459 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
460 engine.test("Create project admin", "POST", "/admin/v1/projects", headers_json,
461 {"name": "Padmin", "admin": True}, (201, 204),
462 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
463 engine.test("Create project bad format", "POST", "/admin/v1/projects", headers_json, {"name": 1}, (400, 422),
464 r_header_json, "json")
465 engine.test("Create user with bad project", "POST", "/admin/v1/users", headers_json,
466 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 409,
467 r_header_json, "json")
468 engine.test("Create user with bad project and force", "POST", "/admin/v1/users?FORCE=True", headers_json,
469 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 201,
470 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
471 engine.test("Create user 2", "POST", "/admin/v1/users", headers_json,
472 {"username": "U2", "projects": ["P1"], "password": "pw2"}, 201,
473 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
474 engine.test("Edit user U1, delete P2 project", "PATCH", "/admin/v1/users/U1", headers_json,
475 {"projects": {"$'P2'": None}}, 204, None, None)
476 res = engine.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
477 headers_json, None, 200, None, json)
478 if res:
479 u1 = res.json()
480 # print(u1)
481 expected_projects = ["P1", "Padmin"]
482 if u1["projects"] != expected_projects:
483 logger.error("User content projects '{}' different than expected '{}'. Edition has not done"
484 " properly".format(u1["projects"], expected_projects))
485 engine.failed_tests += 1
486
487 engine.test("Edit user U1, set Padmin as default project", "PUT", "/admin/v1/users/U1", headers_json,
488 {"projects": {"$'Padmin'": None, "$+[0]": "Padmin"}}, 204, None, None)
489 res = engine.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
490 headers_json, None, 200, None, json)
491 if res:
492 u1 = res.json()
493 # print(u1)
494 expected_projects = ["Padmin", "P1"]
495 if u1["projects"] != expected_projects:
496 logger.error("User content projects '{}' different than expected '{}'. Edition has not done"
497 " properly".format(u1["projects"], expected_projects))
498 engine.failed_tests += 1
499
500 engine.test("Edit user U1, change password", "PATCH", "/admin/v1/users/U1", headers_json,
501 {"password": "pw1_new"}, 204, None, None)
502
503 engine.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json,
504 {"project_id": "P1"}, 401, r_header_json, "json")
505
506 res = engine.test("Change to user U1 project P1", "POST", "/admin/v1/tokens", headers_json,
507 {"username": "U1", "password": "pw1_new", "project_id": "P1"}, (200, 201),
508 r_header_json, "json")
509 if res:
510 response = res.json()
511 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
512
513 engine.test("Edit user projects non admin", "PUT", "/admin/v1/users/U1", headers_json,
514 {"projects": {"$'P1'": None}}, 401, r_header_json, "json")
515 engine.test("Add new project non admin", "POST", "/admin/v1/projects", headers_json,
516 {"name": "P2"}, 401, r_header_json, "json")
517 engine.test("Add new user non admin", "POST", "/admin/v1/users", headers_json,
518 {"username": "U3", "projects": ["P1"], "password": "pw3"}, 401,
519 r_header_json, "json")
520
521 res = engine.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json,
522 {"project_id": "Padmin"}, (200, 201), r_header_json, "json")
523 if res:
524 response = res.json()
525 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
526
527 engine.test("Add new project admin", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
528 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
529 engine.test("Add new user U3 admin", "POST", "/admin/v1/users",
530 headers_json, {"username": "U3", "projects": ["P2"], "password": "pw3"}, (201, 204),
531 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
532 engine.test("Edit user projects admin", "PUT", "/admin/v1/users/U3", headers_json,
533 {"projects": ["P2"]}, 204, None, None)
534
535 engine.test("Delete project P2 conflict", "DELETE", "/admin/v1/projects/P2", headers_json, None, 409,
536 r_header_json, "json")
537 engine.test("Delete project P2 forcing", "DELETE", "/admin/v1/projects/P2?FORCE=True", headers_json,
538 None, 204, None, None)
539
540 engine.test("Delete user U1. Conflict deleting own user", "DELETE", "/admin/v1/users/U1", headers_json,
541 None, 409, r_header_json, "json")
542 engine.test("Delete user U2", "DELETE", "/admin/v1/users/U2", headers_json, None, 204, None, None)
543 engine.test("Delete user U3", "DELETE", "/admin/v1/users/U3", headers_json, None, 204, None, None)
544 # change to admin
545 engine.remove_authorization() # To force get authorization
546 engine.get_autorization()
547 engine.test("Delete user U1 by Name", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
548 engine.test("Delete project P1 by Name", "DELETE", "/admin/v1/projects/P1", headers_json, None, 204, None, None)
549 engine.test("Delete project Padmin by Name", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204,
550 None, None)
551
552 # BEGIN New Tests - Addressing Projects/Users by Name/ID
553 res = engine.test("Create new project P1", "POST", "/admin/v1/projects", headers_json, {"name": "P1"},
554 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
555 if res:
556 pid1 = res.json()["id"]
557 # print("# pid =", pid1)
558 res = engine.test("Create new project P2", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
559 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
560 if res:
561 pid2 = res.json()["id"]
562 # print("# pid =", pid2)
563 res = engine.test("Create new user U1", "POST", "/admin/v1/users", headers_json,
564 {"username": "U1", "projects": ["P1"], "password": "pw1"}, 201,
565 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
566 if res:
567 uid1 = res.json()["id"]
568 # print("# uid =", uid1)
569 res = engine.test("Create new user U2", "POST", "/admin/v1/users", headers_json,
570 {"username": "U2", "projects": ["P2"], "password": "pw2"}, 201,
571 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
572 if res:
573 uid2 = res.json()["id"]
574 # print("# uid =", uid2)
575 engine.test("Get Project P1 by Name", "GET", "/admin/v1/projects/P1", headers_json, None, 200, None, "json")
576 engine.test("Get Project P1 by ID", "GET", "/admin/v1/projects/"+pid1, headers_json, None, 200, None, "json")
577 engine.test("Get User U1 by Name", "GET", "/admin/v1/users/U1", headers_json, None, 200, None, "json")
578 engine.test("Get User U1 by ID", "GET", "/admin/v1/users/"+uid1, headers_json, None, 200, None, "json")
579 engine.test("Rename Project P1 by Name", "PUT", "/admin/v1/projects/P1", headers_json,
580 {"name": "P3"}, 204, None, None)
581 engine.test("Rename Project P2 by ID", "PUT", "/admin/v1/projects/"+pid2, headers_json,
582 {"name": "P4"}, 204, None, None)
583 engine.test("Rename User U1 by Name", "PUT", "/admin/v1/users/U1", headers_json,
584 {"username": "U3"}, 204, None, None)
585 engine.test("Rename User U2 by ID", "PUT", "/admin/v1/users/"+uid2, headers_json,
586 {"username": "U4"}, 204, None, None)
587 engine.test("Get Project P1 by new Name", "GET", "/admin/v1/projects/P3", headers_json, None, 200, None, "json")
588 engine.test("Get User U1 by new Name", "GET", "/admin/v1/users/U3", headers_json, None, 200, None, "json")
589 engine.test("Delete User U1 by Name", "DELETE", "/admin/v1/users/U3", headers_json, None, 204, None, None)
590 engine.test("Delete User U2 by ID", "DELETE", "/admin/v1/users/"+uid2, headers_json, None, 204, None, None)
591 engine.test("Delete Project P1 by Name", "DELETE", "/admin/v1/projects/P3", headers_json, None, 204, None,
592 None)
593 engine.test("Delete Project P2 by ID", "DELETE", "/admin/v1/projects/"+pid2, headers_json, None, 204, None,
594 None)
595 # END New Tests - Addressing Projects/Users by Name
596 engine.remove_authorization() # To finish
597
598
599 class TestProjectsDescriptors:
600 description = "test descriptors visibility among projects"
601
602 @staticmethod
603 def run(engine, test_osm, manual_check, test_params=None):
604 vnfd_ids = []
605 engine.set_test_name("ProjectDescriptors")
606 engine.get_autorization()
607 engine.test("Create project Padmin", "POST", "/admin/v1/projects", headers_json,
608 {"name": "Padmin", "admin": True}, (201, 204),
609 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
610 engine.test("Create project P2", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
611 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
612 engine.test("Create project P3", "POST", "/admin/v1/projects", headers_json, {"name": "P3"},
613 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
614
615 engine.test("Create user U1", "POST", "/admin/v1/users", headers_json,
616 {"username": "U1", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
617 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
618
619 engine.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml,
620 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
621 vnfd_ids.append(engine.last_id)
622 engine.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
623 headers_yaml, TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
624 vnfd_ids.append(engine.last_id)
625 engine.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml,
626 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
627 vnfd_ids.append(engine.last_id)
628
629 res = engine.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
630 headers_json, None, 200, r_header_json, "json")
631 response = res.json()
632 if len(response) != 3:
633 logger.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response)))
634 engine.failed_tests += 1
635
636 # Change to other project Padmin
637 res = engine.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json,
638 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
639 r_header_json, "json")
640 if res:
641 response = res.json()
642 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
643
644 # list vnfds
645 res = engine.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
646 headers_json, None, 200, r_header_json, "json")
647 response = res.json()
648 if len(response) != 0:
649 logger.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response)))
650 engine.failed_tests += 1
651
652 # list Public vnfds
653 res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
654 headers_json, None, 200, r_header_json, "json")
655 response = res.json()
656 if len(response) != 1:
657 logger.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response)))
658 engine.failed_tests += 1
659
660 # list vnfds belonging to project "admin"
661 res = engine.test("List VNFD of admin project", "GET", "/vnfpkgm/v1/vnf_packages?ADMIN=admin",
662 headers_json, None, 200, r_header_json, "json")
663 response = res.json()
664 if len(response) != 3:
665 logger.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response)))
666 engine.failed_tests += 1
667
668 # Get Public vnfds
669 engine.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
670 headers_json, None, 200, r_header_json, "json")
671 # Edit not owned vnfd
672 engine.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
673 headers_yaml, '{name: pepe}', 404, r_header_yaml, "yaml")
674
675 # Add to my catalog
676 engine.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
677 format(vnfd_ids[1]), headers_json, None, 204, None, 0)
678
679 # Add a new vnfd
680 engine.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml,
681 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
682 vnfd_ids.append(engine.last_id)
683
684 # list vnfds
685 res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
686 headers_json, None, 200, r_header_json, "json")
687 response = res.json()
688 if len(response) != 2:
689 logger.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response)))
690 engine.failed_tests += 1
691
692 if manual_check:
693 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
694
695 test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
696 headers_yaml, None, 204, None, 0)
697
698 # change to admin project
699 engine.remove_authorization() # To force get authorization
700 engine.get_autorization()
701 test_rest.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
702 headers_yaml, None, 204, None, 0)
703 test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
704 headers_yaml, None, 204, None, 0)
705 test_rest.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[2]),
706 headers_yaml, None, 204, None, 0)
707 test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[3]),
708 headers_yaml, None, 404, r_header_yaml, "yaml")
709 test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
710 headers_yaml, None, 204, None, 0)
711 # Get Public vnfds
712 engine.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[0]),
713 headers_json, None, 404, r_header_json, "json")
714 engine.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[1]),
715 headers_json, None, 404, r_header_json, "json")
716 engine.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[2]),
717 headers_json, None, 404, r_header_json, "json")
718 engine.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
719 headers_json, None, 404, r_header_json, "json")
720
721 engine.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
722 engine.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204, None, None)
723 engine.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json, None, 204, None, None)
724 engine.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json, None, 204, None, None)
725
726
727 class TestFakeVim:
728 description = "Creates/edit/delete fake VIMs and SDN controllers"
729
730 def __init__(self):
731 self.vim = {
732 "schema_version": "1.0",
733 "schema_type": "No idea",
734 "name": "myVim",
735 "description": "Descriptor name",
736 "vim_type": "openstack",
737 "vim_url": "http://localhost:/vim",
738 "vim_tenant_name": "vimTenant",
739 "vim_user": "user",
740 "vim_password": "password",
741 "config": {"config_param": 1}
742 }
743 self.sdn = {
744 "name": "sdn-name",
745 "description": "sdn-description",
746 "dpid": "50:50:52:54:00:94:21:21",
747 "ip": "192.168.15.17",
748 "port": 8080,
749 "type": "opendaylight",
750 "version": "3.5.6",
751 "user": "user",
752 "password": "passwd"
753 }
754 self.port_mapping = [
755 {"compute_node": "compute node 1",
756 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
757 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
758 ]},
759 {"compute_node": "compute node 2",
760 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
761 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
762 ]}
763 ]
764
765 def run(self, engine, test_osm, manual_check, test_params=None):
766
767 vim_bad = self.vim.copy()
768 vim_bad.pop("name")
769
770 engine.set_test_name("FakeVim")
771 engine.get_autorization()
772 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (201, 204),
773 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
774 vim_id = engine.last_id
775 engine.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json,
776 vim_bad, 422, None, headers_json)
777 engine.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json, self.vim,
778 409, None, headers_json)
779 engine.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml, None, 200, r_header_yaml,
780 "yaml")
781 engine.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 200,
782 r_header_yaml, "yaml")
783 if not test_osm:
784 # delete with FORCE
785 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_yaml,
786 None, 202, None, 0)
787 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None,
788 404, r_header_yaml, "yaml")
789 else:
790 # delete and wait until is really deleted
791 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 202,
792 None, 0)
793 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
794
795
796 class TestVIMSDN(TestFakeVim):
797 description = "Creates VIM with SDN editing SDN controllers and port_mapping"
798
799 def __init__(self):
800 TestFakeVim.__init__(self)
801 self.wim = {
802 "schema_version": "1.0",
803 "schema_type": "No idea",
804 "name": "myWim",
805 "description": "Descriptor name",
806 "wim_type": "odl",
807 "wim_url": "http://localhost:/wim",
808 "user": "user",
809 "password": "password",
810 "config": {"config_param": 1}
811 }
812
813 def run(self, engine, test_osm, manual_check, test_params=None):
814 engine.set_test_name("VimSdn")
815 engine.get_autorization()
816 # Added SDN
817 engine.test("Create SDN", "POST", "/admin/v1/sdns", headers_json, self.sdn, (201, 204),
818 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
819 sdnc_id = engine.last_id
820 # sleep(5)
821 # Edit SDN
822 engine.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, {"name": "new_sdn_name"},
823 204, None, None)
824 # sleep(5)
825 # VIM with SDN
826 self.vim["config"]["sdn-controller"] = sdnc_id
827 self.vim["config"]["sdn-port-mapping"] = self.port_mapping
828 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (200, 204, 201),
829 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
830
831 vim_id = engine.last_id
832 self.port_mapping[0]["compute_node"] = "compute node XX"
833 engine.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
834 {"config": {"sdn-port-mapping": self.port_mapping}}, 204, None, None)
835 engine.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
836 {"config": {"sdn-port-mapping": None}}, 204, None, None)
837
838 engine.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json, self.wim, (200, 204, 201),
839 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
840 wim_id = engine.last_id
841
842 if not test_osm:
843 # delete with FORCE
844 engine.test("Delete VIM remove port-mapping", "DELETE",
845 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_json, None, 202, None, 0)
846 engine.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id), headers_json, None,
847 202, None, 0)
848
849 engine.test("Delete WIM", "DELETE",
850 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id), headers_json, None, 202, None, 0)
851 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml,
852 None, 404, r_header_yaml, "yaml")
853 engine.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id), headers_yaml, None,
854 404, r_header_yaml, "yaml")
855 engine.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id), headers_yaml,
856 None, 404, r_header_yaml, "yaml")
857 else:
858 if manual_check:
859 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
860 # delete and wait until is really deleted
861 engine.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id),
862 headers_json, None, (202, 201, 204), None, 0)
863 engine.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, None,
864 (202, 201, 204), None, 0)
865 engine.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id),
866 headers_json, None, (202, 201, 204), None, 0)
867 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
868 engine.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id), timeout)
869 engine.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id), timeout)
870
871
872 class TestDeploy:
873 description = "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
874
875 def __init__(self):
876 self.test_name = "DEPLOY"
877 self.nsd_id = None
878 self.vim_id = None
879 self.ns_id = None
880 self.vnfds_id = []
881 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
882 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
883 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
884 self.descriptor_edit = None
885 self.uses_configuration = False
886 self.users = {}
887 self.passwords = {}
888 self.commands = {}
889 self.keys = {}
890 self.timeout = 120
891 self.qforce = ""
892 self.ns_params = None
893 self.vnfr_ip_list = {}
894
895 def create_descriptors(self, engine):
896 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
897 if not os.path.exists(temp_dir):
898 os.makedirs(temp_dir)
899 for vnfd_index, vnfd_filename in enumerate(self.vnfd_filenames):
900 if "/" in vnfd_filename:
901 vnfd_filename_path = vnfd_filename
902 if not os.path.exists(vnfd_filename_path):
903 raise TestException("File '{}' does not exist".format(vnfd_filename_path))
904 else:
905 vnfd_filename_path = temp_dir + vnfd_filename
906 if not os.path.exists(vnfd_filename_path):
907 with open(vnfd_filename_path, "wb") as file:
908 response = requests.get(self.descriptor_url + vnfd_filename)
909 if response.status_code >= 300:
910 raise TestException("Error downloading descriptor from '{}': {}".format(
911 self.descriptor_url + vnfd_filename, response.status_code))
912 file.write(response.content)
913 if vnfd_filename_path.endswith(".yaml"):
914 headers = headers_yaml
915 else:
916 headers = headers_zip_yaml
917 if randint(0, 1) == 0:
918 # vnfd CREATE AND UPLOAD in one step:
919 engine.test("Onboard VNFD in one step", "POST",
920 "/vnfpkgm/v1/vnf_packages_content" + self.qforce, headers, "@b" + vnfd_filename_path, 201,
921 r_headers_yaml_location_vnfd,
922 "yaml")
923 self.vnfds_id.append(engine.last_id)
924 else:
925 # vnfd CREATE AND UPLOAD ZIP
926 engine.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
927 headers_json, None, 201,
928 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
929 self.vnfds_id.append(engine.last_id)
930 engine.test("Onboard VNFD step 2 as ZIP", "PUT",
931 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self.qforce,
932 headers, "@b" + vnfd_filename_path, 204, None, 0)
933
934 if self.descriptor_edit:
935 if "vnfd{}".format(vnfd_index) in self.descriptor_edit:
936 # Modify VNFD
937 engine.test("Edit VNFD ", "PATCH",
938 "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfds_id[-1]),
939 headers_yaml, self.descriptor_edit["vnfd{}".format(vnfd_index)], 204, None, None)
940
941 if "/" in self.nsd_filename:
942 nsd_filename_path = self.nsd_filename
943 if not os.path.exists(nsd_filename_path):
944 raise TestException("File '{}' does not exist".format(nsd_filename_path))
945 else:
946 nsd_filename_path = temp_dir + self.nsd_filename
947 if not os.path.exists(nsd_filename_path):
948 with open(nsd_filename_path, "wb") as file:
949 response = requests.get(self.descriptor_url + self.nsd_filename)
950 if response.status_code >= 300:
951 raise TestException("Error downloading descriptor from '{}': {}".format(
952 self.descriptor_url + self.nsd_filename, response.status_code))
953 file.write(response.content)
954 if nsd_filename_path.endswith(".yaml"):
955 headers = headers_yaml
956 else:
957 headers = headers_zip_yaml
958
959 if randint(0, 1) == 0:
960 # nsd CREATE AND UPLOAD in one step:
961 engine.test("Onboard NSD in one step", "POST",
962 "/nsd/v1/ns_descriptors_content" + self.qforce, headers, "@b" + nsd_filename_path, 201,
963 r_headers_yaml_location_nsd, yaml)
964 self.nsd_id = engine.last_id
965 else:
966 # nsd CREATE AND UPLOAD ZIP
967 engine.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
968 headers_json, None, 201,
969 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
970 self.nsd_id = engine.last_id
971 engine.test("Onboard NSD step 2 as ZIP", "PUT",
972 "/nsd/v1/ns_descriptors/<>/nsd_content" + self.qforce,
973 headers, "@b" + nsd_filename_path, 204, None, 0)
974
975 if self.descriptor_edit and "nsd" in self.descriptor_edit:
976 # Modify NSD
977 engine.test("Edit NSD ", "PATCH",
978 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
979 headers_yaml, self.descriptor_edit["nsd"], 204, None, None)
980
981 def delete_descriptors(self, engine):
982 # delete descriptors
983 engine.test("Delete NSSD SOL005", "DELETE",
984 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
985 headers_yaml, None, 204, None, 0)
986 for vnfd_id in self.vnfds_id:
987 engine.test("Delete VNFD SOL005", "DELETE",
988 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id), headers_yaml, None, 204, None, 0)
989
990 def instantiate(self, engine, ns_data):
991 ns_data_text = yaml.safe_dump(ns_data, default_flow_style=True, width=256)
992 # create NS Two steps
993 r = engine.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
994 headers_yaml, ns_data_text, 201,
995 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
996 if not r:
997 return
998 self.ns_id = engine.last_id
999 engine.test("Instantiate NS step 2", "POST",
1000 "/nslcm/v1/ns_instances/{}/instantiate".format(self.ns_id), headers_yaml, ns_data_text,
1001 201, r_headers_yaml_location_nslcmop, "yaml")
1002 nslcmop_id = engine.last_id
1003
1004 if test_osm:
1005 # Wait until status is Ok
1006 timeout = timeout_configure if self.uses_configuration else timeout_deploy
1007 engine.wait_operation_ready("ns", nslcmop_id, timeout)
1008
1009 def terminate(self, engine):
1010 # remove deployment
1011 if test_osm:
1012 engine.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self.ns_id), headers_yaml,
1013 None, 201, r_headers_yaml_location_nslcmop, "yaml")
1014 nslcmop2_id = engine.last_id
1015 # Wait until status is Ok
1016 engine.wait_operation_ready("ns", nslcmop2_id, timeout_deploy)
1017
1018 engine.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1019 204, None, 0)
1020 else:
1021 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1022 headers_yaml, None, 204, None, 0)
1023
1024 # check all it is deleted
1025 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1026 404, None, "yaml")
1027 r = engine.test("Check NSLCMOPs are deleted", "GET",
1028 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1029 200, None, "json")
1030 if not r:
1031 return
1032 nslcmops = r.json()
1033 if not isinstance(nslcmops, list) or nslcmops:
1034 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1035
1036 def test_ns(self, engine, test_osm, commands=None, users=None, passwds=None, keys=None, timeout=0):
1037
1038 r = engine.test("GET VNFR IDs", "GET",
1039 "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_json, None,
1040 200, r_header_json, "json")
1041 if not r:
1042 return
1043 ns_data = r.json()
1044
1045 vnfr_list = ns_data['constituent-vnfr-ref']
1046 time = 0
1047 _commands = commands if commands is not None else self.commands
1048 _users = users if users is not None else self.users
1049 _passwds = passwds if passwds is not None else self.passwords
1050 _keys = keys if keys is not None else self.keys
1051 _timeout = timeout if timeout != 0 else self.timeout
1052
1053 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1054 for vnfr_id in vnfr_list:
1055 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1056 "/nslcm/v1/vnfrs/{}".format(vnfr_id), headers_json, None,
1057 200, r_header_json, "json")
1058 if not r:
1059 continue
1060 vnfr_data = r.json()
1061
1062 vnf_index = str(vnfr_data["member-vnf-index-ref"])
1063
1064 ip_address = self.get_vnfr_ip(engine, vnf_index)
1065 description = "Exec command='{}' at VNFR={} IP={}".format(_commands.get(vnf_index)[0], vnf_index,
1066 ip_address)
1067 engine.step += 1
1068 test_description = "{}{} {}".format(engine.test_name, engine.step, description)
1069 logger.warning(test_description)
1070 while _timeout >= time:
1071 result, message = self.do_checks([ip_address],
1072 vnf_index=vnfr_data["member-vnf-index-ref"],
1073 commands=_commands.get(vnf_index), user=_users.get(vnf_index),
1074 passwd=_passwds.get(vnf_index), key=_keys.get(vnf_index))
1075 if result == 1:
1076 engine.passed_tests += 1
1077 logger.debug(message)
1078 break
1079 elif result == 0:
1080 time += 20
1081 sleep(20)
1082 elif result == -1:
1083 engine.failed_tests += 1
1084 logger.error(message)
1085 break
1086 else:
1087 time -= 20
1088 engine.failed_tests += 1
1089 logger.error(message)
1090 else:
1091 engine.failed_tests += 1
1092 logger.error("VNFR {} has not mgmt address. Check failed".format(vnf_index))
1093
1094 def do_checks(self, ip, vnf_index, commands=[], user=None, passwd=None, key=None):
1095 try:
1096 import urllib3
1097 from pssh.clients import ParallelSSHClient
1098 from pssh.utils import load_private_key
1099 from ssh2 import exceptions as ssh2Exception
1100 except ImportError as e:
1101 logger.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1102 "parallel-ssh urllib3': {}".format(e))
1103 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1104 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
1105 try:
1106 p_host = os.environ.get("PROXY_HOST")
1107 p_user = os.environ.get("PROXY_USER")
1108 p_password = os.environ.get("PROXY_PASSWD")
1109
1110 if key:
1111 pkey = load_private_key(key)
1112 else:
1113 pkey = None
1114
1115 client = ParallelSSHClient(ip, user=user, password=passwd, pkey=pkey, proxy_host=p_host,
1116 proxy_user=p_user, proxy_password=p_password, timeout=10, num_retries=0)
1117 for cmd in commands:
1118 output = client.run_command(cmd)
1119 client.join(output)
1120 if output[ip[0]].exit_code:
1121 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip[0], cmd,
1122 "\n".join(output[ip[0]].stderr))
1123 else:
1124 return 1, "VNFR {} command '{}' successful".format(ip[0], cmd)
1125 except (ssh2Exception.ChannelFailure, ssh2Exception.SocketDisconnectError, ssh2Exception.SocketTimeout,
1126 ssh2Exception.SocketRecvError) as e:
1127 return 0, "Timeout accessing the VNFR {}: {}".format(ip[0], str(e))
1128 except Exception as e:
1129 return -1, "ERROR checking the VNFR {}: {}".format(ip[0], str(e))
1130
1131 def additional_operations(self, engine, test_osm, manual_check):
1132 pass
1133
1134 def run(self, engine, test_osm, manual_check, test_params=None):
1135 engine.set_test_name(self.test_name)
1136 engine.get_autorization()
1137 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1138 if test_params:
1139 if "vnfd-files" in test_params:
1140 self.vnfd_filenames = test_params["vnfd-files"].split(",")
1141 if "nsd-file" in test_params:
1142 self.nsd_filename = test_params["nsd-file"]
1143 if test_params.get("ns-name"):
1144 nsname = test_params["ns-name"]
1145 self.create_descriptors(engine)
1146
1147 # create real VIM if not exist
1148 self.vim_id = engine.get_create_vim(test_osm)
1149 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1150 "vimAccountId": self.vim_id}
1151 if self.ns_params:
1152 ns_data.update(self.ns_params)
1153 if test_params and test_params.get("ns-config"):
1154 if isinstance(test_params["ns-config"], str):
1155 ns_data.update(yaml.load(test_params["ns-config"]))
1156 else:
1157 ns_data.update(test_params["ns-config"])
1158 self.instantiate(engine, ns_data)
1159
1160 if manual_check:
1161 input('NS has been deployed. Perform manual check and press enter to resume')
1162 if test_osm and self.commands:
1163 self.test_ns(engine, test_osm)
1164 self.additional_operations(engine, test_osm, manual_check)
1165 self.terminate(engine)
1166 self.delete_descriptors(engine)
1167
1168 def get_first_ip(self, ip_string):
1169 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1170 first_ip = ip_string.split(";")[0] if ip_string else ""
1171 return first_ip
1172
1173 def get_vnfr_ip(self, engine, vnfr_index_wanted):
1174 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1175 ip = self.vnfr_ip_list.get(vnfr_index_wanted, "")
1176 if (ip):
1177 return self.get_first_ip(ip)
1178 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1179 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1180 vnfr_index_wanted, self.ns_id), headers_json, None,
1181 200, r_header_json, "json")
1182 if not r:
1183 return ""
1184 vnfr_data = r.json()
1185 if not (vnfr_data and vnfr_data[0]):
1186 return ""
1187 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1188 ip_list = vnfr_data[0].get("ip-address", "")
1189 if ip_list:
1190 self.vnfr_ip_list[vnfr_index_wanted] = ip_list
1191 ip = self.get_first_ip(ip_list)
1192 return ip
1193
1194
1195 class TestDeployHackfestCirros(TestDeploy):
1196 description = "Load and deploy Hackfest cirros_2vnf_ns example"
1197
1198 def __init__(self):
1199 super().__init__()
1200 self.test_name = "CIRROS"
1201 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1202 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1203 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1204 self.users = {'1': "cirros", '2': "cirros"}
1205 self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1206
1207 def terminate(self, engine):
1208 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1209 if test_osm:
1210 engine.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1211 format(self.ns_id), headers_yaml, None, 202, None, "yaml")
1212
1213 engine .wait_until_delete("/nslcm/v1/ns_instances/{}".format(self.ns_id), timeout_deploy)
1214 else:
1215 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1216 headers_yaml, None, 204, None, 0)
1217
1218 # check all it is deleted
1219 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1220 404, None, "yaml")
1221 r = engine.test("Check NSLCMOPs are deleted", "GET",
1222 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1223 200, None, "json")
1224 if not r:
1225 return
1226 nslcmops = r.json()
1227 if not isinstance(nslcmops, list) or nslcmops:
1228 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1229
1230
1231 class TestDeployHackfest1(TestDeploy):
1232 description = "Load and deploy Hackfest_1_vnfd example"
1233
1234 def __init__(self):
1235 super().__init__()
1236 self.test_name = "HACKFEST1-"
1237 self.vnfd_filenames = ("hackfest_1_vnfd.tar.gz",)
1238 self.nsd_filename = "hackfest_1_nsd.tar.gz"
1239 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1240 # self.users = {'1': "cirros", '2': "cirros"}
1241 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1242
1243
1244 class TestDeployHackfestCirrosScaling(TestDeploy):
1245 description = "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1246
1247 def __init__(self):
1248 super().__init__()
1249 self.test_name = "CIRROS-SCALE"
1250 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1251 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1252 # Modify VNFD to add scaling and count=2
1253 self.descriptor_edit = {
1254 "vnfd0": {
1255 "vdu": {
1256 "$id: 'cirros_vnfd-VM'": {"count": 2}
1257 },
1258 "scaling-group-descriptor": [{
1259 "name": "scale_cirros",
1260 "max-instance-count": 2,
1261 "vdu": [{
1262 "vdu-id-ref": "cirros_vnfd-VM",
1263 "count": 2
1264 }]
1265 }]
1266 }
1267 }
1268
1269 def additional_operations(self, engine, test_osm, manual_check):
1270 if not test_osm:
1271 return
1272 # 2 perform scale out twice
1273 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1274 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1275 for i in range(0, 2):
1276 engine.test("Execute scale action over NS", "POST",
1277 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1278 201, r_headers_yaml_location_nslcmop, "yaml")
1279 nslcmop2_scale_out = engine.last_id
1280 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1281 if manual_check:
1282 input('NS scale out done. Check that two more vdus are there')
1283 # TODO check automatic
1284
1285 # 2 perform scale in
1286 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1287 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1288 for i in range(0, 2):
1289 engine.test("Execute scale IN action over NS", "POST",
1290 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1291 201, r_headers_yaml_location_nslcmop, "yaml")
1292 nslcmop2_scale_in = engine.last_id
1293 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1294 if manual_check:
1295 input('NS scale in done. Check that two less vdus are there')
1296 # TODO check automatic
1297
1298 # perform scale in that must fail as reached limit
1299 engine.test("Execute scale IN out of limit action over NS", "POST",
1300 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1301 201, r_headers_yaml_location_nslcmop, "yaml")
1302 nslcmop2_scale_in = engine.last_id
1303 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy, expected_fail=True)
1304
1305
1306 class TestDeployIpMac(TestDeploy):
1307 description = "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1308
1309 def __init__(self):
1310 super().__init__()
1311 self.test_name = "SetIpMac"
1312 self.vnfd_filenames = ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1313 self.nsd_filename = "scenario_2vdu_set_ip_mac.yaml"
1314 self.descriptor_url = \
1315 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1316 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1317 self.users = {'1': "osm", '2': "osm"}
1318 self.passwords = {'1': "osm4u", '2': "osm4u"}
1319 self.timeout = 360
1320
1321 def run(self, engine, test_osm, manual_check, test_params=None):
1322 # super().run(engine, test_osm, manual_check, test_params)
1323 # run again setting IPs with instantiate parameters
1324 instantiation_params = {
1325 "vnf": [
1326 {
1327 "member-vnf-index": "1",
1328 "internal-vld": [
1329 {
1330 "name": "internal_vld1", # net_internal
1331 "ip-profile": {
1332 "ip-version": "ipv4",
1333 "subnet-address": "10.9.8.0/24",
1334 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1335 },
1336 "internal-connection-point": [
1337 {
1338 "id-ref": "eth2",
1339 "ip-address": "10.9.8.2",
1340 },
1341 {
1342 "id-ref": "eth3",
1343 "ip-address": "10.9.8.3",
1344 }
1345 ]
1346 },
1347 ],
1348
1349 "vdu": [
1350 {
1351 "id": "VM1",
1352 "interface": [
1353 # {
1354 # "name": "iface11",
1355 # "floating-ip-required": True,
1356 # },
1357 {
1358 "name": "iface13",
1359 "mac-address": "52:33:44:55:66:13"
1360 },
1361 ],
1362 },
1363 {
1364 "id": "VM2",
1365 "interface": [
1366 {
1367 "name": "iface21",
1368 "ip-address": "10.31.31.22",
1369 "mac-address": "52:33:44:55:66:21"
1370 },
1371 ],
1372 },
1373 ]
1374 },
1375 ]
1376 }
1377
1378 super().run(engine, test_osm, manual_check, test_params={"ns-config": instantiation_params})
1379
1380
1381 class TestDeployHackfest4(TestDeploy):
1382 description = "Load and deploy Hackfest 4 example."
1383
1384 def __init__(self):
1385 super().__init__()
1386 self.test_name = "HACKFEST4-"
1387 self.vnfd_filenames = ("hackfest_4_vnfd.tar.gz",)
1388 self.nsd_filename = "hackfest_4_nsd.tar.gz"
1389 self.uses_configuration = True
1390 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1391 self.users = {'1': "ubuntu", '2': "ubuntu"}
1392 self.passwords = {'1': "osm4u", '2': "osm4u"}
1393 # Modify VNFD to add scaling
1394 # self.descriptor_edit = {
1395 # "vnfd0": {
1396 # 'vnf-configuration': {
1397 # 'config-primitive': [{
1398 # 'name': 'touch',
1399 # 'parameter': [{
1400 # 'name': 'filename',
1401 # 'data-type': 'STRING',
1402 # 'default-value': '/home/ubuntu/touched'
1403 # }]
1404 # }]
1405 # },
1406 # 'scaling-group-descriptor': [{
1407 # 'name': 'scale_dataVM',
1408 # 'scaling-policy': [{
1409 # 'threshold-time': 0,
1410 # 'name': 'auto_cpu_util_above_threshold',
1411 # 'scaling-type': 'automatic',
1412 # 'scaling-criteria': [{
1413 # 'name': 'cpu_util_above_threshold',
1414 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1415 # 'scale-out-relational-operation': 'GE',
1416 # 'scale-in-threshold': 15,
1417 # 'scale-out-threshold': 60,
1418 # 'scale-in-relational-operation': 'LE'
1419 # }],
1420 # 'cooldown-time': 60
1421 # }],
1422 # 'max-instance-count': 10,
1423 # 'scaling-config-action': [
1424 # {'vnf-config-primitive-name-ref': 'touch',
1425 # 'trigger': 'post-scale-out'},
1426 # {'vnf-config-primitive-name-ref': 'touch',
1427 # 'trigger': 'pre-scale-in'}
1428 # ],
1429 # 'vdu': [{
1430 # 'vdu-id-ref': 'dataVM',
1431 # 'count': 1
1432 # }]
1433 # }]
1434 # }
1435 # }
1436
1437
1438 class TestDeployHackfest3Charmed(TestDeploy):
1439 description = "Load and deploy Hackfest 3charmed_ns example"
1440
1441 def __init__(self):
1442 super().__init__()
1443 self.test_name = "HACKFEST3-"
1444 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz",)
1445 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1446 self.uses_configuration = True
1447 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1448 self.users = {'1': "ubuntu", '2': "ubuntu"}
1449 self.passwords = {'1': "osm4u", '2': "osm4u"}
1450 self.descriptor_edit = {
1451 "vnfd0": yaml.safe_load(
1452 """
1453 vnf-configuration:
1454 terminate-config-primitive:
1455 - seq: '1'
1456 name: touch
1457 parameter:
1458 - name: filename
1459 value: '/home/ubuntu/last-touch1'
1460 - seq: '3'
1461 name: touch
1462 parameter:
1463 - name: filename
1464 value: '/home/ubuntu/last-touch3'
1465 - seq: '2'
1466 name: touch
1467 parameter:
1468 - name: filename
1469 value: '/home/ubuntu/last-touch2'
1470 """)
1471 }
1472
1473 def additional_operations(self, engine, test_osm, manual_check):
1474 if not test_osm:
1475 return
1476 # 1 perform action
1477 vnfr_index_selected = "2"
1478 payload = '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1479 engine.test("Exec service primitive over NS", "POST",
1480 "/nslcm/v1/ns_instances/{}/action".format(self.ns_id), headers_yaml, payload,
1481 201, r_headers_yaml_location_nslcmop, "yaml")
1482 nslcmop2_action = engine.last_id
1483 # Wait until status is Ok
1484 engine.wait_operation_ready("ns", nslcmop2_action, timeout_deploy)
1485 vnfr_ip = self.get_vnfr_ip(engine, vnfr_index_selected)
1486 if manual_check:
1487 input(
1488 "NS service primitive has been executed."
1489 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1490 format(vnfr_ip))
1491 if test_osm:
1492 commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1493 self.test_ns(engine, test_osm, commands=commands)
1494
1495 # # 2 perform scale out
1496 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1497 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1498 # engine.test("Execute scale action over NS", "POST",
1499 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1500 # 201, r_headers_yaml_location_nslcmop, "yaml")
1501 # nslcmop2_scale_out = engine.last_id
1502 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1503 # if manual_check:
1504 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1505 # # TODO check automatic
1506 #
1507 # # 2 perform scale in
1508 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1509 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1510 # engine.test("Execute scale action over NS", "POST",
1511 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1512 # 201, r_headers_yaml_location_nslcmop, "yaml")
1513 # nslcmop2_scale_in = engine.last_id
1514 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1515 # if manual_check:
1516 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1517 # # TODO check automatic
1518
1519
1520 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed):
1521 description = "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1522 "ids and member-vnf-index."
1523
1524 def __init__(self):
1525 super().__init__()
1526 self.test_name = "HACKFEST3v2-"
1527 self.qforce = "?FORCE=True"
1528 self.descriptor_edit = {
1529 "vnfd0": {
1530 "vdu": {
1531 "$[0]": {
1532 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1533 },
1534 "$[1]": None
1535 },
1536 "vnf-configuration": None,
1537 "connection-point": {
1538 "$[0]": {
1539 "id": "pdu-mgmt",
1540 "name": "pdu-mgmt",
1541 "short-name": "pdu-mgmt"
1542 },
1543 "$[1]": None
1544 },
1545 "mgmt-interface": {"cp": "pdu-mgmt"},
1546 "description": "A vnf single vdu to be used as PDU",
1547 "id": "vdu-as-pdu",
1548 "internal-vld": {
1549 "$[0]": {
1550 "id": "pdu_internal",
1551 "name": "pdu_internal",
1552 "internal-connection-point": {"$[1]": None},
1553 "short-name": "pdu_internal",
1554 "type": "ELAN"
1555 }
1556 }
1557 },
1558
1559 # Modify NSD accordingly
1560 "nsd": {
1561 "constituent-vnfd": {
1562 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1563 "$[1]": None,
1564 },
1565 "description": "A nsd to deploy the vnf to act as as PDU",
1566 "id": "nsd-as-pdu",
1567 "name": "nsd-as-pdu",
1568 "short-name": "nsd-as-pdu",
1569 "vld": {
1570 "$[0]": {
1571 "id": "mgmt_pdu",
1572 "name": "mgmt_pdu",
1573 "short-name": "mgmt_pdu",
1574 "vnfd-connection-point-ref": {
1575 "$[0]": {
1576 "vnfd-connection-point-ref": "pdu-mgmt",
1577 "vnfd-id-ref": "vdu-as-pdu",
1578 },
1579 "$[1]": None
1580 },
1581 "type": "ELAN"
1582 },
1583 "$[1]": None,
1584 }
1585 }
1586 }
1587
1588
1589 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed):
1590 description = "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1591
1592 def __init__(self):
1593 super().__init__()
1594 self.test_name = "HACKFEST3v3-"
1595 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1596 self.descriptor_edit = {
1597 "vnfd0": yaml.load(
1598 """
1599 scaling-group-descriptor:
1600 - name: "scale_dataVM"
1601 max-instance-count: 10
1602 scaling-policy:
1603 - name: "auto_cpu_util_above_threshold"
1604 scaling-type: "automatic"
1605 threshold-time: 0
1606 cooldown-time: 60
1607 scaling-criteria:
1608 - name: "cpu_util_above_threshold"
1609 scale-in-threshold: 15
1610 scale-in-relational-operation: "LE"
1611 scale-out-threshold: 60
1612 scale-out-relational-operation: "GE"
1613 vnf-monitoring-param-ref: "monitor1"
1614 vdu:
1615 - vdu-id-ref: dataVM
1616 count: 1
1617 scaling-config-action:
1618 - trigger: post-scale-out
1619 vnf-config-primitive-name-ref: touch
1620 - trigger: pre-scale-in
1621 vnf-config-primitive-name-ref: touch
1622 vdu:
1623 "$id: dataVM":
1624 monitoring-param:
1625 - id: "dataVM_cpu_util"
1626 nfvi-metric: "cpu_utilization"
1627
1628 monitoring-param:
1629 - id: "monitor1"
1630 name: "monitor1"
1631 aggregation-type: AVERAGE
1632 vdu-monitoring-param:
1633 vdu-ref: "dataVM"
1634 vdu-monitoring-param-ref: "dataVM_cpu_util"
1635 vnf-configuration:
1636 initial-config-primitive:
1637 "$[1]":
1638 parameter:
1639 "$[0]":
1640 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1641 config-primitive:
1642 "$[0]":
1643 parameter:
1644 "$[0]":
1645 default-value: "<touch_filename2>"
1646 """)
1647 }
1648 self.ns_params = {
1649 "additionalParamsForVnf": [
1650 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1651 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1652 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1653 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1654 ]
1655 }
1656
1657 def additional_operations(self, engine, test_osm, manual_check):
1658 super().additional_operations(engine, test_osm, manual_check)
1659 if not test_osm:
1660 return
1661
1662 # 2 perform scale out
1663 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1664 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1665 engine.test("Execute scale action over NS", "POST",
1666 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1667 201, r_headers_yaml_location_nslcmop, "yaml")
1668 nslcmop2_scale_out = engine.last_id
1669 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1670 if manual_check:
1671 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1672 if test_osm:
1673 commands = {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1674 self.test_ns(engine, test_osm, commands=commands)
1675 # TODO check automatic connection to scaled VM
1676
1677 # 2 perform scale in
1678 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1679 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1680 engine.test("Execute scale action over NS", "POST",
1681 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1682 201, r_headers_yaml_location_nslcmop, "yaml")
1683 nslcmop2_scale_in = engine.last_id
1684 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1685 if manual_check:
1686 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1687 # TODO check automatic
1688
1689
1690 class TestDeploySimpleCharm(TestDeploy):
1691 description = "Deploy hackfest-4 hackfest_simplecharm example"
1692
1693 def __init__(self):
1694 super().__init__()
1695 self.test_name = "HACKFEST-SIMPLE"
1696 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1697 self.vnfd_filenames = ("hackfest_simplecharm_vnf.tar.gz",)
1698 self.nsd_filename = "hackfest_simplecharm_ns.tar.gz"
1699 self.uses_configuration = True
1700 self.commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1701 self.users = {'1': "ubuntu", '2': "ubuntu"}
1702 self.passwords = {'1': "osm4u", '2': "osm4u"}
1703
1704
1705 class TestDeploySimpleCharm2(TestDeploySimpleCharm):
1706 description = "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1707 "vnf-member-index"
1708
1709 def __init__(self):
1710 super().__init__()
1711 self.test_name = "HACKFEST-SIMPLE2-"
1712 self.qforce = "?FORCE=True"
1713 self.descriptor_edit = {
1714 "vnfd0": {
1715 "id": "hackfest.simplecharm.vnf"
1716 },
1717
1718 "nsd": {
1719 "id": "hackfest.simplecharm.ns",
1720 "constituent-vnfd": {
1721 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1722 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1723 },
1724 "vld": {
1725 "$[0]": {
1726 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1727 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1728 "$[1]": {"member-vnf-index-ref": "$2",
1729 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1730 },
1731 "$[1]": {
1732 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1733 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1734 "$[1]": {"member-vnf-index-ref": "$2",
1735 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1736 },
1737 }
1738 }
1739 }
1740
1741
1742 class TestDeploySingleVdu(TestDeployHackfest3Charmed):
1743 description = "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1744
1745 def __init__(self):
1746 super().__init__()
1747 self.test_name = "SingleVDU"
1748 self.qforce = "?FORCE=True"
1749 self.descriptor_edit = {
1750 # Modify VNFD to remove one VDU
1751 "vnfd0": {
1752 "vdu": {
1753 "$[0]": {
1754 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1755 },
1756 "$[1]": None
1757 },
1758 "vnf-configuration": None,
1759 "connection-point": {
1760 "$[0]": {
1761 "id": "pdu-mgmt",
1762 "name": "pdu-mgmt",
1763 "short-name": "pdu-mgmt"
1764 },
1765 "$[1]": None
1766 },
1767 "mgmt-interface": {"cp": "pdu-mgmt"},
1768 "description": "A vnf single vdu to be used as PDU",
1769 "id": "vdu-as-pdu",
1770 "internal-vld": {
1771 "$[0]": {
1772 "id": "pdu_internal",
1773 "name": "pdu_internal",
1774 "internal-connection-point": {"$[1]": None},
1775 "short-name": "pdu_internal",
1776 "type": "ELAN"
1777 }
1778 }
1779 },
1780
1781 # Modify NSD accordingly
1782 "nsd": {
1783 "constituent-vnfd": {
1784 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1785 "$[1]": None,
1786 },
1787 "description": "A nsd to deploy the vnf to act as as PDU",
1788 "id": "nsd-as-pdu",
1789 "name": "nsd-as-pdu",
1790 "short-name": "nsd-as-pdu",
1791 "vld": {
1792 "$[0]": {
1793 "id": "mgmt_pdu",
1794 "name": "mgmt_pdu",
1795 "short-name": "mgmt_pdu",
1796 "vnfd-connection-point-ref": {
1797 "$[0]": {
1798 "vnfd-connection-point-ref": "pdu-mgmt",
1799 "vnfd-id-ref": "vdu-as-pdu",
1800 },
1801 "$[1]": None
1802 },
1803 "type": "ELAN"
1804 },
1805 "$[1]": None,
1806 }
1807 }
1808 }
1809
1810
1811 class TestDeployHnfd(TestDeployHackfest3Charmed):
1812 description = "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1813
1814 def __init__(self):
1815 super().__init__()
1816 self.test_name = "HNFD"
1817 self.pduDeploy = TestDeploySingleVdu()
1818 self.pdu_interface_0 = {}
1819 self.pdu_interface_1 = {}
1820
1821 self.pdu_id = None
1822 # self.vnf_to_pdu = """
1823 # vdu:
1824 # "$[0]":
1825 # pdu-type: PDU-TYPE-1
1826 # interface:
1827 # "$[0]":
1828 # name: mgmt-iface
1829 # "$[1]":
1830 # name: pdu-iface-internal
1831 # id: hfn1
1832 # description: HFND, one PDU + One VDU
1833 # name: hfn1
1834 # short-name: hfn1
1835 #
1836 # """
1837
1838 self.pdu_descriptor = {
1839 "name": "my-PDU",
1840 "type": "PDU-TYPE-1",
1841 "vim_accounts": "to-override",
1842 "interfaces": [
1843 {
1844 "name": "mgmt-iface",
1845 "mgmt": True,
1846 "type": "overlay",
1847 "ip-address": "to override",
1848 "mac-address": "mac_address",
1849 "vim-network-name": "mgmt",
1850 },
1851 {
1852 "name": "pdu-iface-internal",
1853 "mgmt": False,
1854 "type": "overlay",
1855 "ip-address": "to override",
1856 "mac-address": "mac_address",
1857 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1858 },
1859 ]
1860 }
1861 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1862
1863 self.descriptor_edit = {
1864 "vnfd0": {
1865 "id": "hfnd1",
1866 "name": "hfn1",
1867 "short-name": "hfn1",
1868 "vdu": {
1869 "$[0]": {
1870 "pdu-type": "PDU-TYPE-1",
1871 "interface": {
1872 "$[0]": {"name": "mgmt-iface"},
1873 "$[1]": {"name": "pdu-iface-internal"},
1874 }
1875 }
1876 }
1877 },
1878 "nsd": {
1879 "constituent-vnfd": {
1880 "$[1]": {"vnfd-id-ref": "hfnd1"}
1881 },
1882 "vld": {
1883 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1884 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1885 }
1886 }
1887 }
1888
1889 def create_descriptors(self, engine):
1890 super().create_descriptors(engine)
1891
1892 # Create PDU
1893 self.pdu_descriptor["interfaces"][0].update(self.pdu_interface_0)
1894 self.pdu_descriptor["interfaces"][1].update(self.pdu_interface_1)
1895 self.pdu_descriptor["vim_accounts"] = [self.vim_id]
1896 # TODO get vim-network-name from vnfr.vld.name
1897 self.pdu_descriptor["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1898 os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1899 "PDU", self.pdu_descriptor["interfaces"][1]["vim-network-name"])
1900 engine.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1901 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self.pdu_descriptor,
1902 201, r_header_yaml, "yaml")
1903 self.pdu_id = engine.last_id
1904
1905 def run(self, engine, test_osm, manual_check, test_params=None):
1906 engine.get_autorization()
1907 engine.set_test_name(self.test_name)
1908 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1909
1910 # create real VIM if not exist
1911 self.vim_id = engine.get_create_vim(test_osm)
1912 # instantiate PDU
1913 self.pduDeploy.create_descriptors(engine)
1914 self.pduDeploy.instantiate(engine, {"nsDescription": "to be used as PDU", "nsName": nsname + "-PDU",
1915 "nsdId": self.pduDeploy.nsd_id, "vimAccountId": self.vim_id})
1916 if manual_check:
1917 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1918 if test_osm:
1919 self.pduDeploy.test_ns(engine, test_osm)
1920
1921 if test_osm:
1922 r = engine.test("Get VNFR to obtain IP_ADDRESS", "GET",
1923 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self.pduDeploy.ns_id), headers_json, None,
1924 200, r_header_json, "json")
1925 if not r:
1926 return
1927 vnfr_data = r.json()
1928 # print(vnfr_data)
1929
1930 self.pdu_interface_0["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("ip-address")
1931 self.pdu_interface_1["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("ip-address")
1932 self.pdu_interface_0["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("mac-address")
1933 self.pdu_interface_1["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("mac-address")
1934 if not self.pdu_interface_0["ip-address"]:
1935 raise TestException("Vnfr has not managment ip address")
1936 else:
1937 self.pdu_interface_0["ip-address"] = "192.168.10.10"
1938 self.pdu_interface_1["ip-address"] = "192.168.11.10"
1939 self.pdu_interface_0["mac-address"] = "52:33:44:55:66:13"
1940 self.pdu_interface_1["mac-address"] = "52:33:44:55:66:14"
1941
1942 self.create_descriptors(engine)
1943
1944 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1945 "vimAccountId": self.vim_id}
1946 if test_params and test_params.get("ns-config"):
1947 if isinstance(test_params["ns-config"], str):
1948 ns_data.update(yaml.load(test_params["ns-config"]))
1949 else:
1950 ns_data.update(test_params["ns-config"])
1951
1952 self.instantiate(engine, ns_data)
1953 if manual_check:
1954 input('NS has been deployed. Perform manual check and press enter to resume')
1955 if test_osm:
1956 self.test_ns(engine, test_osm)
1957 self.additional_operations(engine, test_osm, manual_check)
1958 self.terminate(engine)
1959 self.pduDeploy.terminate(engine)
1960 self.delete_descriptors(engine)
1961 self.pduDeploy.delete_descriptors(engine)
1962
1963 def delete_descriptors(self, engine):
1964 super().delete_descriptors(engine)
1965 # delete pdu
1966 engine.test("Delete PDU SOL005", "DELETE",
1967 "/pdu/v1/pdu_descriptors/{}".format(self.pdu_id),
1968 headers_yaml, None, 204, None, 0)
1969
1970
1971 class TestDescriptors:
1972 description = "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1973 vnfd_empty = """vnfd:vnfd-catalog:
1974 vnfd:
1975 - name: prova
1976 short-name: prova
1977 id: prova
1978 """
1979 vnfd_prova = """vnfd:vnfd-catalog:
1980 vnfd:
1981 - connection-point:
1982 - name: cp_0h8m
1983 type: VPORT
1984 id: prova
1985 name: prova
1986 short-name: prova
1987 vdu:
1988 - id: vdu_z4bm
1989 image: ubuntu
1990 interface:
1991 - external-connection-point-ref: cp_0h8m
1992 name: eth0
1993 virtual-interface:
1994 type: VIRTIO
1995 name: vdu_z4bm
1996 version: '1.0'
1997 """
1998
1999 def __init__(self):
2000 self.vnfd_filename = "hackfest_3charmed_vnfd.tar.gz"
2001 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
2002 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2003 self.vnfd_id = None
2004 self.nsd_id = None
2005
2006 def run(self, engine, test_osm, manual_check, test_params=None):
2007 engine.set_test_name("Descriptors")
2008 engine.get_autorization()
2009 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
2010 if not os.path.exists(temp_dir):
2011 os.makedirs(temp_dir)
2012
2013 # download files
2014 for filename in (self.vnfd_filename, self.nsd_filename):
2015 filename_path = temp_dir + filename
2016 if not os.path.exists(filename_path):
2017 with open(filename_path, "wb") as file:
2018 response = requests.get(self.descriptor_url + filename)
2019 if response.status_code >= 300:
2020 raise TestException("Error downloading descriptor from '{}': {}".format(
2021 self.descriptor_url + filename, response.status_code))
2022 file.write(response.content)
2023
2024 vnfd_filename_path = temp_dir + self.vnfd_filename
2025 nsd_filename_path = temp_dir + self.nsd_filename
2026
2027 engine.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2028 self.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
2029 self.vnfd_id = engine.last_id
2030
2031 # test bug 605
2032 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
2033 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
2034
2035 engine.test("Upload VNFD {}".format(self.vnfd_filename), "PUT",
2036 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip_yaml,
2037 "@b" + vnfd_filename_path, 204, None, 0)
2038
2039 queries = ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2040 "vdu.0.interface.1.internal-connection-point-ref=internal",
2041 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2042 # Detection of duplicated VLD names in VNF Descriptors
2043 # URL: internal-vld=[
2044 # {id: internal1, name: internal, type:ELAN,
2045 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2046 # {id: internal2, name: internal, type:ELAN,
2047 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2048 # ]
2049 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2050 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2051 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2052 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2053 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2054 ]
2055 for query in queries:
2056 engine.test("Upload invalid VNFD ", "PUT",
2057 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self.vnfd_id, query),
2058 headers_zip_yaml, "@b" + vnfd_filename_path, 422, r_header_yaml, "yaml")
2059
2060 # test bug 605
2061 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
2062 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
2063
2064 # get vnfd descriptor
2065 engine.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
2066 headers_yaml, None, 200, r_header_yaml, "yaml")
2067
2068 # get vnfd file descriptor
2069 engine.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self.vnfd_id),
2070 headers_text, None, 200, r_header_text, "text", temp_dir+"vnfd-yaml")
2071 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2072
2073 # get vnfd zip file package
2074 engine.test("Get VNFD zip package", "GET",
2075 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip, None, 200,
2076 r_header_zip, "zip", temp_dir+"vnfd-zip")
2077 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2078
2079 # get vnfd artifact
2080 engine.test("Get VNFD artifact package", "GET",
2081 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self.vnfd_id), headers_zip, None, 200,
2082 r_header_octect, "octet-string", temp_dir+"vnfd-icon")
2083 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2084
2085 # nsd CREATE AND UPLOAD in one step:
2086 engine.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml,
2087 "@b" + nsd_filename_path, 201, r_headers_yaml_location_nsd, "yaml")
2088 self.nsd_id = engine.last_id
2089
2090 queries = ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2091 for query in queries:
2092 engine.test("Upload invalid NSD ", "PUT",
2093 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self.nsd_id, query),
2094 headers_zip_yaml, "@b" + nsd_filename_path, 422, r_header_yaml, "yaml")
2095
2096 # get nsd descriptor
2097 engine.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml,
2098 None, 200, r_header_yaml, "yaml")
2099
2100 # get nsd file descriptor
2101 engine.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self.nsd_id), headers_text,
2102 None, 200, r_header_text, "text", temp_dir+"nsd-yaml")
2103 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2104
2105 # get nsd zip file package
2106 engine.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self.nsd_id),
2107 headers_zip, None, 200, r_header_zip, "zip", temp_dir+"nsd-zip")
2108 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2109
2110 # get nsd artifact
2111 engine.test("Get NSD artifact package", "GET",
2112 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self.nsd_id), headers_zip, None, 200,
2113 r_header_octect, "octet-string", temp_dir+"nsd-icon")
2114 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2115
2116 # vnfd DELETE
2117 test_rest.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
2118 headers_yaml, None, 409, None, None)
2119
2120 test_rest.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self.vnfd_id),
2121 headers_yaml, None, 204, None, 0)
2122
2123 # nsd DELETE
2124 test_rest.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml, None, 204,
2125 None, 0)
2126
2127
2128 class TestNetSliceTemplates:
2129 description = "Upload a NST to OSM"
2130
2131 def __init__(self):
2132 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2133 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2134 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2135 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2136 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2137
2138 def run(self, engine, test_osm, manual_check, test_params=None):
2139 # nst CREATE
2140 engine.set_test_name("NST step ")
2141 engine.get_autorization()
2142 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
2143 if not os.path.exists(temp_dir):
2144 os.makedirs(temp_dir)
2145
2146 # Onboard VNFDs
2147 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2148 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2149 self.vnfd_edge_id = engine.last_id
2150
2151 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2152 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2153 self.vnfd_middle_id = engine.last_id
2154
2155 # Onboard NSDs
2156 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2157 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2158 self.nsd_edge_id = engine.last_id
2159
2160 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2161 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2162 self.nsd_middle_id = engine.last_id
2163
2164 # Onboard NST
2165 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2166 201, r_headers_yaml_location_nst, "yaml")
2167 nst_id = engine.last_id
2168
2169 # nstd SHOW OSM format
2170 engine.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2171 200, r_header_json, "json")
2172
2173 # nstd DELETE
2174 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2175 204, None, 0)
2176
2177 # NSDs DELETE
2178 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2179 headers_json, None, 204, None, 0)
2180
2181 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2182 None, 204, None, 0)
2183
2184 # VNFDs DELETE
2185 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2186 headers_yaml, None, 204, None, 0)
2187
2188 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2189 headers_yaml, None, 204, None, 0)
2190
2191
2192 class TestNetSliceInstances:
2193 '''
2194 Test procedure:
2195 1. Populate databases with VNFD, NSD, NST with the following scenario
2196 +-----------------management-----------------+
2197 | | |
2198 +--+---+ +----+----+ +---+--+
2199 | | | | | |
2200 | edge +---data1----+ middle +---data2-----+ edge |
2201 | | | | | |
2202 +------+ +---------+ +------+
2203 shared-nss
2204 2. Create NSI-1
2205 3. Instantiate NSI-1
2206 4. Create NSI-2
2207 5. Instantiate NSI-2
2208 Manual check - Are 2 slices instantiated correctly?
2209 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2210 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2211 6. Terminate NSI-1
2212 7. Delete NSI-1
2213 Manual check - Is slice NSI-1 deleted correctly?
2214 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2215 8. Create NSI-3
2216 9. Instantiate NSI-3
2217 Manual check - Is slice NSI-3 instantiated correctly?
2218 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2219 10. Delete NSI-2
2220 11. Terminate NSI-2
2221 12. Delete NSI-3
2222 13. Terminate NSI-3
2223 Manual check - All cleaned correctly?
2224 NSI-2 and NSI-3 were terminated and deleted
2225 14. Cleanup database
2226 '''
2227
2228 description = "Upload a NST to OSM"
2229
2230 def __init__(self):
2231 self.vim_id = None
2232 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2233 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2234 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2235 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2236 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2237
2238 def create_slice(self, engine, nsi_data, name):
2239 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2240 r = engine.test(name, "POST", "/nsilcm/v1/netslice_instances",
2241 headers_yaml, ns_data_text, 201,
2242 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2243 return r
2244
2245 def instantiate_slice(self, engine, nsi_data, nsi_id, name):
2246 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2247 engine.test(name, "POST",
2248 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id), headers_yaml, ns_data_text,
2249 201, r_headers_yaml_location_nsilcmop, "yaml")
2250
2251 def terminate_slice(self, engine, nsi_id, name):
2252 engine.test(name, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id),
2253 headers_yaml, None, 201, r_headers_yaml_location_nsilcmop, "yaml")
2254
2255 def delete_slice(self, engine, nsi_id, name):
2256 engine.test(name, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id), headers_yaml, None,
2257 204, None, 0)
2258
2259 def run(self, engine, test_osm, manual_check, test_params=None):
2260 # nst CREATE
2261 engine.set_test_name("NSI")
2262 engine.get_autorization()
2263
2264 # Onboard VNFDs
2265 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2266 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2267 self.vnfd_edge_id = engine.last_id
2268
2269 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2270 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2271 self.vnfd_middle_id = engine.last_id
2272
2273 # Onboard NSDs
2274 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2275 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2276 self.nsd_edge_id = engine.last_id
2277
2278 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2279 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2280 self.nsd_middle_id = engine.last_id
2281
2282 # Onboard NST
2283 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2284 201, r_headers_yaml_location_nst, "yaml")
2285 nst_id = engine.last_id
2286
2287 self.vim_id = engine.get_create_vim(test_osm)
2288
2289 # CREATE NSI-1
2290 ns_data = {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2291 r = self.create_slice(engine, ns_data, "Create NSI-1 step 1")
2292 if not r:
2293 return
2294 self.nsi_id1 = engine.last_id
2295
2296 # INSTANTIATE NSI-1
2297 self.instantiate_slice(engine, ns_data, self.nsi_id1, "Instantiate NSI-1 step 2")
2298 nsilcmop_id1 = engine.last_id
2299
2300 # Waiting for NSI-1
2301 if test_osm:
2302 engine.wait_operation_ready("nsi", nsilcmop_id1, timeout_deploy)
2303
2304 # CREATE NSI-2
2305 ns_data = {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2306 r = self.create_slice(engine, ns_data, "Create NSI-2 step 1")
2307 if not r:
2308 return
2309 self.nsi_id2 = engine.last_id
2310
2311 # INSTANTIATE NSI-2
2312 self.instantiate_slice(engine, ns_data, self.nsi_id2, "Instantiate NSI-2 step 2")
2313 nsilcmop_id2 = engine.last_id
2314
2315 # Waiting for NSI-2
2316 if test_osm:
2317 engine.wait_operation_ready("nsi", nsilcmop_id2, timeout_deploy)
2318
2319 if manual_check:
2320 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2321
2322 # TERMINATE NSI-1
2323 if test_osm:
2324 self.terminate_slice(engine, self.nsi_id1, "Terminate NSI-1")
2325 nsilcmop1_id = engine.last_id
2326
2327 # Wait terminate NSI-1
2328 engine.wait_operation_ready("nsi", nsilcmop1_id, timeout_deploy)
2329
2330 # DELETE NSI-1
2331 self.delete_slice(engine, self.nsi_id1, "Delete NS")
2332
2333 if manual_check:
2334 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2335
2336 # CREATE NSI-3
2337 ns_data = {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2338 r = self.create_slice(engine, ns_data, "Create NSI-3 step 1")
2339
2340 if not r:
2341 return
2342 self.nsi_id3 = engine.last_id
2343
2344 # INSTANTIATE NSI-3
2345 self.instantiate_slice(engine, ns_data, self.nsi_id3, "Instantiate NSI-3 step 2")
2346 nsilcmop_id3 = engine.last_id
2347
2348 # Wait Instantiate NSI-3
2349 if test_osm:
2350 engine.wait_operation_ready("nsi", nsilcmop_id3, timeout_deploy)
2351
2352 if manual_check:
2353 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2354
2355 # TERMINATE NSI-2
2356 if test_osm:
2357 self.terminate_slice(engine, self.nsi_id2, "Terminate NSI-2")
2358 nsilcmop2_id = engine.last_id
2359
2360 # Wait terminate NSI-2
2361 engine.wait_operation_ready("nsi", nsilcmop2_id, timeout_deploy)
2362
2363 # DELETE NSI-2
2364 self.delete_slice(engine, self.nsi_id2, "DELETE NSI-2")
2365
2366 # TERMINATE NSI-3
2367 if test_osm:
2368 self. terminate_slice(engine, self.nsi_id3, "Terminate NSI-3")
2369 nsilcmop3_id = engine.last_id
2370
2371 # Wait terminate NSI-3
2372 engine.wait_operation_ready("nsi", nsilcmop3_id, timeout_deploy)
2373
2374 # DELETE NSI-3
2375 self.delete_slice(engine, self.nsi_id3, "DELETE NSI-3")
2376
2377 if manual_check:
2378 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2379
2380 # nstd DELETE
2381 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2382 204, None, 0)
2383
2384 # NSDs DELETE
2385 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2386 headers_json, None, 204, None, 0)
2387
2388 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2389 None, 204, None, 0)
2390
2391 # VNFDs DELETE
2392 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2393 headers_yaml, None, 204, None, 0)
2394
2395 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2396 headers_yaml, None, 204, None, 0)
2397
2398
2399 if __name__ == "__main__":
2400 global logger
2401 test = ""
2402
2403 # Disable warnings from self-signed certificates.
2404 requests.packages.urllib3.disable_warnings()
2405 try:
2406 logging.basicConfig(format="%(levelname)s %(message)s", level=logging.ERROR)
2407 logger = logging.getLogger('NBI')
2408 # load parameters and configuration
2409 opts, args = getopt.getopt(sys.argv[1:], "hvu:p:",
2410 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2411 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2412 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2413 url = "https://localhost:9999/osm"
2414 user = password = project = "admin"
2415 test_osm = False
2416 manual_check = False
2417 verbose = 0
2418 verify = True
2419 fail_fast = False
2420 test_classes = {
2421 "NonAuthorized": TestNonAuthorized,
2422 "FakeVIM": TestFakeVim,
2423 "Users-Projects": TestUsersProjects,
2424 "Projects-Descriptors": TestProjectsDescriptors,
2425 "VIM-SDN": TestVIMSDN,
2426 "Deploy-Custom": TestDeploy,
2427 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros,
2428 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling,
2429 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed,
2430 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2,
2431 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3,
2432 "Deploy-Hackfest-4": TestDeployHackfest4,
2433 "Deploy-CirrosMacIp": TestDeployIpMac,
2434 "Descriptors": TestDescriptors,
2435 "Deploy-Hackfest1": TestDeployHackfest1,
2436 # "Deploy-MultiVIM": TestDeployMultiVIM,
2437 "Deploy-SingleVdu": TestDeploySingleVdu,
2438 "Deploy-Hnfd": TestDeployHnfd,
2439 "Upload-Slice-Template": TestNetSliceTemplates,
2440 "Deploy-Slice-Instance": TestNetSliceInstances,
2441 "Deploy-SimpleCharm": TestDeploySimpleCharm,
2442 "Deploy-SimpleCharm2": TestDeploySimpleCharm2,
2443 }
2444 test_to_do = []
2445 test_params = {}
2446
2447 for o, a in opts:
2448 # print("parameter:", o, a)
2449 if o == "--version":
2450 print("test version " + __version__ + ' ' + version_date)
2451 exit()
2452 elif o == "--list":
2453 for test, test_class in sorted(test_classes.items()):
2454 print("{:32} {}".format(test + ":", test_class.description))
2455 exit()
2456 elif o in ("-v", "--verbose"):
2457 verbose += 1
2458 elif o == "no-verbose":
2459 verbose = -1
2460 elif o in ("-h", "--help"):
2461 usage()
2462 sys.exit()
2463 elif o == "--test-osm":
2464 test_osm = True
2465 elif o == "--manual-check":
2466 manual_check = True
2467 elif o == "--url":
2468 url = a
2469 elif o in ("-u", "--user"):
2470 user = a
2471 elif o in ("-p", "--password"):
2472 password = a
2473 elif o == "--project":
2474 project = a
2475 elif o == "--fail-fast":
2476 fail_fast = True
2477 elif o == "--test":
2478 # print("asdfadf", o, a, a.split(","))
2479 for _test in a.split(","):
2480 if _test not in test_classes:
2481 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test),
2482 file=sys.stderr)
2483 exit(1)
2484 test_to_do.append(_test)
2485 elif o == "--params":
2486 param_key, _, param_value = a.partition("=")
2487 text_index = len(test_to_do)
2488 if text_index not in test_params:
2489 test_params[text_index] = {}
2490 test_params[text_index][param_key] = param_value
2491 elif o == "--insecure":
2492 verify = False
2493 elif o == "--timeout":
2494 timeout = int(a)
2495 elif o == "--timeout-deploy":
2496 timeout_deploy = int(a)
2497 elif o == "--timeout-configure":
2498 timeout_configure = int(a)
2499 else:
2500 assert False, "Unhandled option"
2501 if verbose == 0:
2502 logger.setLevel(logging.WARNING)
2503 elif verbose > 1:
2504 logger.setLevel(logging.DEBUG)
2505 else:
2506 logger.setLevel(logging.ERROR)
2507
2508 test_rest = TestRest(url, user=user, password=password, project=project)
2509 # print("tests to do:", test_to_do)
2510 if test_to_do:
2511 text_index = 0
2512 for test in test_to_do:
2513 if fail_fast and test_rest.failed_tests:
2514 break
2515 text_index += 1
2516 test_class = test_classes[test]
2517 test_class().run(test_rest, test_osm, manual_check, test_params.get(text_index))
2518 else:
2519 for test, test_class in sorted(test_classes.items()):
2520 if fail_fast and test_rest.failed_tests:
2521 break
2522 test_class().run(test_rest, test_osm, manual_check, test_params.get(0))
2523 test_rest.print_results()
2524 exit(1 if test_rest.failed_tests else 0)
2525
2526 except TestException as e:
2527 logger.error(test + "Test {} Exception: {}".format(test, str(e)))
2528 exit(1)
2529 except getopt.GetoptError as e:
2530 logger.error(e)
2531 print(e, file=sys.stderr)
2532 exit(1)
2533 except Exception as e:
2534 logger.critical(test + " Exception: " + str(e), exc_info=True)