6c308aa8fc7d1356b9b1a44e98ab5e2a063b9eae
[osm/NBI.git] / osm_nbi / tests / 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, 202), {"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
608 project_admin_id = None
609 res = engine.test("Get my project Padmin", "GET", "/admin/v1/projects/{}".format(engine.project), headers_json,
610 None, 200, r_header_json, "json")
611 if res:
612 response = res.json()
613 project_admin_id = response["_id"]
614 engine.test("Create project Padmin", "POST", "/admin/v1/projects", headers_json,
615 {"name": "Padmin", "admin": True}, (201, 204),
616 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
617 engine.test("Create project P2", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
618 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
619 engine.test("Create project P3", "POST", "/admin/v1/projects", headers_json, {"name": "P3"},
620 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
621
622 engine.test("Create user U1", "POST", "/admin/v1/users", headers_json,
623 {"username": "U1", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
624 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
625
626 engine.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml,
627 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
628 vnfd_ids.append(engine.last_id)
629 engine.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
630 headers_yaml, TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
631 vnfd_ids.append(engine.last_id)
632 engine.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml,
633 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
634 vnfd_ids.append(engine.last_id)
635
636 res = engine.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
637 headers_json, None, 200, r_header_json, "json")
638 response = res.json()
639 if len(response) != 3:
640 logger.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response)))
641 engine.failed_tests += 1
642
643 # Change to other project Padmin
644 res = engine.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json,
645 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
646 r_header_json, "json")
647 if res:
648 response = res.json()
649 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
650
651 # list vnfds
652 res = engine.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
653 headers_json, None, 200, r_header_json, "json")
654 response = res.json()
655 if len(response) != 0:
656 logger.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response)))
657 engine.failed_tests += 1
658
659 # list Public vnfds
660 res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
661 headers_json, None, 200, r_header_json, "json")
662 response = res.json()
663 if len(response) != 1:
664 logger.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response)))
665 engine.failed_tests += 1
666
667 # list vnfds belonging to project "admin"
668 res = engine.test("List VNFD of admin project", "GET",
669 "/vnfpkgm/v1/vnf_packages?ADMIN={}".format(project_admin_id),
670 headers_json, None, 200, r_header_json, "json")
671 response = res.json()
672 if len(response) != 3:
673 logger.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response)))
674 engine.failed_tests += 1
675
676 # Get Public vnfds
677 engine.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
678 headers_json, None, 200, r_header_json, "json")
679 # Edit not owned vnfd
680 engine.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
681 headers_yaml, '{name: pepe}', 404, r_header_yaml, "yaml")
682
683 # Add to my catalog
684 engine.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
685 format(vnfd_ids[1]), headers_json, None, 204, None, 0)
686
687 # Add a new vnfd
688 engine.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml,
689 TestDescriptors.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
690 vnfd_ids.append(engine.last_id)
691
692 # list vnfds
693 res = engine.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
694 headers_json, None, 200, r_header_json, "json")
695 response = res.json()
696 if len(response) != 2:
697 logger.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response)))
698 engine.failed_tests += 1
699
700 if manual_check:
701 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
702
703 test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
704 headers_yaml, None, 204, None, 0)
705
706 # change to admin project
707 engine.remove_authorization() # To force get authorization
708 engine.get_autorization()
709 test_rest.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[0]),
710 headers_yaml, None, 204, None, 0)
711 test_rest.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[1]),
712 headers_yaml, None, 204, None, 0)
713 test_rest.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[2]),
714 headers_yaml, None, 204, None, 0)
715 test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids[3]),
716 headers_yaml, None, 404, r_header_yaml, "yaml")
717 test_rest.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
718 headers_yaml, None, 204, None, 0)
719 # Get Public vnfds
720 engine.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[0]),
721 headers_json, None, 404, r_header_json, "json")
722 engine.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[1]),
723 headers_json, None, 404, r_header_json, "json")
724 engine.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[2]),
725 headers_json, None, 404, r_header_json, "json")
726 engine.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids[3]),
727 headers_json, None, 404, r_header_json, "json")
728
729 engine.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
730 engine.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204, None, None)
731 engine.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json, None, 204, None, None)
732 engine.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json, None, 204, None, None)
733
734
735 class TestFakeVim:
736 description = "Creates/edit/delete fake VIMs and SDN controllers"
737
738 def __init__(self):
739 self.vim = {
740 "schema_version": "1.0",
741 "schema_type": "No idea",
742 "name": "myVim",
743 "description": "Descriptor name",
744 "vim_type": "openstack",
745 "vim_url": "http://localhost:/vim",
746 "vim_tenant_name": "vimTenant",
747 "vim_user": "user",
748 "vim_password": "password",
749 "config": {"config_param": 1}
750 }
751 self.sdn = {
752 "name": "sdn-name",
753 "description": "sdn-description",
754 "dpid": "50:50:52:54:00:94:21:21",
755 "ip": "192.168.15.17",
756 "port": 8080,
757 "type": "opendaylight",
758 "version": "3.5.6",
759 "user": "user",
760 "password": "passwd"
761 }
762 self.port_mapping = [
763 {"compute_node": "compute node 1",
764 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
765 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
766 ]},
767 {"compute_node": "compute node 2",
768 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
769 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
770 ]}
771 ]
772
773 def run(self, engine, test_osm, manual_check, test_params=None):
774
775 vim_bad = self.vim.copy()
776 vim_bad.pop("name")
777
778 engine.set_test_name("FakeVim")
779 engine.get_autorization()
780 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (201, 202),
781 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
782 vim_id = engine.last_id
783 engine.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json,
784 vim_bad, 422, None, headers_json)
785 engine.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json, self.vim,
786 409, None, headers_json)
787 engine.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml, None, 200, r_header_yaml,
788 "yaml")
789 engine.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 200,
790 r_header_yaml, "yaml")
791 if not test_osm:
792 # delete with FORCE
793 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_yaml,
794 None, 202, None, 0)
795 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None,
796 404, r_header_yaml, "yaml")
797 else:
798 # delete and wait until is really deleted
799 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 202,
800 None, 0)
801 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
802
803
804 class TestVIMSDN(TestFakeVim):
805 description = "Creates VIM with SDN editing SDN controllers and port_mapping"
806
807 def __init__(self):
808 TestFakeVim.__init__(self)
809 self.wim = {
810 "schema_version": "1.0",
811 "schema_type": "No idea",
812 "name": "myWim",
813 "description": "Descriptor name",
814 "wim_type": "odl",
815 "wim_url": "http://localhost:/wim",
816 "user": "user",
817 "password": "password",
818 "config": {"config_param": 1}
819 }
820
821 def run(self, engine, test_osm, manual_check, test_params=None):
822 engine.set_test_name("VimSdn")
823 engine.get_autorization()
824 # Added SDN
825 engine.test("Create SDN", "POST", "/admin/v1/sdns", headers_json, self.sdn, (201, 202),
826 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
827 sdnc_id = engine.last_id
828 # sleep(5)
829 # Edit SDN
830 engine.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, {"name": "new_sdn_name"},
831 (202, 204), None, None)
832 # sleep(5)
833 # VIM with SDN
834 self.vim["config"]["sdn-controller"] = sdnc_id
835 self.vim["config"]["sdn-port-mapping"] = self.port_mapping
836 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (200, 202, 201),
837 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
838
839 vim_id = engine.last_id
840 self.port_mapping[0]["compute_node"] = "compute node XX"
841 engine.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
842 {"config": {"sdn-port-mapping": self.port_mapping}}, (202, 204), None, None)
843 engine.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
844 {"config": {"sdn-port-mapping": None}}, (202, 204), None, None)
845
846 engine.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json, self.wim, (200, 202, 201),
847 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
848 wim_id = engine.last_id
849
850 if not test_osm:
851 # delete with FORCE
852 engine.test("Delete VIM remove port-mapping", "DELETE",
853 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_json, None, 202, None, 0)
854 engine.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id), headers_json, None,
855 202, None, 0)
856
857 engine.test("Delete WIM", "DELETE",
858 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id), headers_json, None, 202, None, 0)
859 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml,
860 None, 404, r_header_yaml, "yaml")
861 engine.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id), headers_yaml, None,
862 404, r_header_yaml, "yaml")
863 engine.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id), headers_yaml,
864 None, 404, r_header_yaml, "yaml")
865 else:
866 if manual_check:
867 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
868 # delete and wait until is really deleted
869 engine.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id),
870 headers_json, None, (202, 201, 204), None, 0)
871 engine.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, None,
872 (202, 201, 204), None, 0)
873 engine.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id),
874 headers_json, None, (202, 201, 204), None, 0)
875 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
876 engine.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id), timeout)
877 engine.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id), timeout)
878
879
880 class TestDeploy:
881 description = "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
882
883 def __init__(self):
884 self.test_name = "DEPLOY"
885 self.nsd_id = None
886 self.vim_id = None
887 self.ns_id = None
888 self.vnfds_id = []
889 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
890 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
891 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
892 self.descriptor_edit = None
893 self.uses_configuration = False
894 self.users = {}
895 self.passwords = {}
896 self.commands = {}
897 self.keys = {}
898 self.timeout = 120
899 self.qforce = ""
900 self.ns_params = None
901 self.vnfr_ip_list = {}
902
903 def create_descriptors(self, engine):
904 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
905 if not os.path.exists(temp_dir):
906 os.makedirs(temp_dir)
907 for vnfd_index, vnfd_filename in enumerate(self.vnfd_filenames):
908 if "/" in vnfd_filename:
909 vnfd_filename_path = vnfd_filename
910 if not os.path.exists(vnfd_filename_path):
911 raise TestException("File '{}' does not exist".format(vnfd_filename_path))
912 else:
913 vnfd_filename_path = temp_dir + vnfd_filename
914 if not os.path.exists(vnfd_filename_path):
915 with open(vnfd_filename_path, "wb") as file:
916 response = requests.get(self.descriptor_url + vnfd_filename)
917 if response.status_code >= 300:
918 raise TestException("Error downloading descriptor from '{}': {}".format(
919 self.descriptor_url + vnfd_filename, response.status_code))
920 file.write(response.content)
921 if vnfd_filename_path.endswith(".yaml"):
922 headers = headers_yaml
923 else:
924 headers = headers_zip_yaml
925 if randint(0, 1) == 0:
926 # vnfd CREATE AND UPLOAD in one step:
927 engine.test("Onboard VNFD in one step", "POST",
928 "/vnfpkgm/v1/vnf_packages_content" + self.qforce, headers, "@b" + vnfd_filename_path, 201,
929 r_headers_yaml_location_vnfd,
930 "yaml")
931 self.vnfds_id.append(engine.last_id)
932 else:
933 # vnfd CREATE AND UPLOAD ZIP
934 engine.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
935 headers_json, None, 201,
936 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
937 self.vnfds_id.append(engine.last_id)
938 engine.test("Onboard VNFD step 2 as ZIP", "PUT",
939 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self.qforce,
940 headers, "@b" + vnfd_filename_path, 204, None, 0)
941
942 if self.descriptor_edit:
943 if "vnfd{}".format(vnfd_index) in self.descriptor_edit:
944 # Modify VNFD
945 engine.test("Edit VNFD ", "PATCH",
946 "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfds_id[-1]),
947 headers_yaml, self.descriptor_edit["vnfd{}".format(vnfd_index)], 204, None, None)
948
949 if "/" in self.nsd_filename:
950 nsd_filename_path = self.nsd_filename
951 if not os.path.exists(nsd_filename_path):
952 raise TestException("File '{}' does not exist".format(nsd_filename_path))
953 else:
954 nsd_filename_path = temp_dir + self.nsd_filename
955 if not os.path.exists(nsd_filename_path):
956 with open(nsd_filename_path, "wb") as file:
957 response = requests.get(self.descriptor_url + self.nsd_filename)
958 if response.status_code >= 300:
959 raise TestException("Error downloading descriptor from '{}': {}".format(
960 self.descriptor_url + self.nsd_filename, response.status_code))
961 file.write(response.content)
962 if nsd_filename_path.endswith(".yaml"):
963 headers = headers_yaml
964 else:
965 headers = headers_zip_yaml
966
967 if randint(0, 1) == 0:
968 # nsd CREATE AND UPLOAD in one step:
969 engine.test("Onboard NSD in one step", "POST",
970 "/nsd/v1/ns_descriptors_content" + self.qforce, headers, "@b" + nsd_filename_path, 201,
971 r_headers_yaml_location_nsd, yaml)
972 self.nsd_id = engine.last_id
973 else:
974 # nsd CREATE AND UPLOAD ZIP
975 engine.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
976 headers_json, None, 201,
977 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
978 self.nsd_id = engine.last_id
979 engine.test("Onboard NSD step 2 as ZIP", "PUT",
980 "/nsd/v1/ns_descriptors/<>/nsd_content" + self.qforce,
981 headers, "@b" + nsd_filename_path, 204, None, 0)
982
983 if self.descriptor_edit and "nsd" in self.descriptor_edit:
984 # Modify NSD
985 engine.test("Edit NSD ", "PATCH",
986 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
987 headers_yaml, self.descriptor_edit["nsd"], 204, None, None)
988
989 def delete_descriptors(self, engine):
990 # delete descriptors
991 engine.test("Delete NSSD SOL005", "DELETE",
992 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
993 headers_yaml, None, 204, None, 0)
994 for vnfd_id in self.vnfds_id:
995 engine.test("Delete VNFD SOL005", "DELETE",
996 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id), headers_yaml, None, 204, None, 0)
997
998 def instantiate(self, engine, ns_data):
999 ns_data_text = yaml.safe_dump(ns_data, default_flow_style=True, width=256)
1000 # create NS Two steps
1001 r = engine.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
1002 headers_yaml, ns_data_text, (201, 202),
1003 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
1004 if not r:
1005 return
1006 self.ns_id = engine.last_id
1007 engine.test("Instantiate NS step 2", "POST",
1008 "/nslcm/v1/ns_instances/{}/instantiate".format(self.ns_id), headers_yaml, ns_data_text,
1009 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1010 nslcmop_id = engine.last_id
1011
1012 if test_osm:
1013 # Wait until status is Ok
1014 timeout = timeout_configure if self.uses_configuration else timeout_deploy
1015 engine.wait_operation_ready("ns", nslcmop_id, timeout)
1016
1017 def terminate(self, engine):
1018 # remove deployment
1019 if test_osm:
1020 engine.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self.ns_id), headers_yaml,
1021 None, (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1022 nslcmop2_id = engine.last_id
1023 # Wait until status is Ok
1024 engine.wait_operation_ready("ns", nslcmop2_id, timeout_deploy)
1025
1026 engine.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1027 204, None, 0)
1028 else:
1029 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1030 headers_yaml, None, 204, None, 0)
1031
1032 # check all it is deleted
1033 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1034 404, None, "yaml")
1035 r = engine.test("Check NSLCMOPs are deleted", "GET",
1036 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1037 200, None, "json")
1038 if not r:
1039 return
1040 nslcmops = r.json()
1041 if not isinstance(nslcmops, list) or nslcmops:
1042 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1043
1044 def test_ns(self, engine, test_osm, commands=None, users=None, passwds=None, keys=None, timeout=0):
1045
1046 r = engine.test("GET VNFR IDs", "GET",
1047 "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_json, None,
1048 200, r_header_json, "json")
1049 if not r:
1050 return
1051 ns_data = r.json()
1052
1053 vnfr_list = ns_data['constituent-vnfr-ref']
1054 time = 0
1055 _commands = commands if commands is not None else self.commands
1056 _users = users if users is not None else self.users
1057 _passwds = passwds if passwds is not None else self.passwords
1058 _keys = keys if keys is not None else self.keys
1059 _timeout = timeout if timeout != 0 else self.timeout
1060
1061 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1062 for vnfr_id in vnfr_list:
1063 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1064 "/nslcm/v1/vnfrs/{}".format(vnfr_id), headers_json, None,
1065 200, r_header_json, "json")
1066 if not r:
1067 continue
1068 vnfr_data = r.json()
1069
1070 vnf_index = str(vnfr_data["member-vnf-index-ref"])
1071
1072 ip_address = self.get_vnfr_ip(engine, vnf_index)
1073 description = "Exec command='{}' at VNFR={} IP={}".format(_commands.get(vnf_index)[0], vnf_index,
1074 ip_address)
1075 engine.step += 1
1076 test_description = "{}{} {}".format(engine.test_name, engine.step, description)
1077 logger.warning(test_description)
1078 while _timeout >= time:
1079 result, message = self.do_checks([ip_address],
1080 vnf_index=vnfr_data["member-vnf-index-ref"],
1081 commands=_commands.get(vnf_index), user=_users.get(vnf_index),
1082 passwd=_passwds.get(vnf_index), key=_keys.get(vnf_index))
1083 if result == 1:
1084 engine.passed_tests += 1
1085 logger.debug(message)
1086 break
1087 elif result == 0:
1088 time += 20
1089 sleep(20)
1090 elif result == -1:
1091 engine.failed_tests += 1
1092 logger.error(message)
1093 break
1094 else:
1095 time -= 20
1096 engine.failed_tests += 1
1097 logger.error(message)
1098 else:
1099 engine.failed_tests += 1
1100 logger.error("VNFR {} has not mgmt address. Check failed".format(vnf_index))
1101
1102 def do_checks(self, ip, vnf_index, commands=[], user=None, passwd=None, key=None):
1103 try:
1104 import urllib3
1105 from pssh.clients import ParallelSSHClient
1106 from pssh.utils import load_private_key
1107 from ssh2 import exceptions as ssh2Exception
1108 except ImportError as e:
1109 logger.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1110 "parallel-ssh urllib3': {}".format(e))
1111 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1112 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
1113 try:
1114 p_host = os.environ.get("PROXY_HOST")
1115 p_user = os.environ.get("PROXY_USER")
1116 p_password = os.environ.get("PROXY_PASSWD")
1117
1118 if key:
1119 pkey = load_private_key(key)
1120 else:
1121 pkey = None
1122
1123 client = ParallelSSHClient(ip, user=user, password=passwd, pkey=pkey, proxy_host=p_host,
1124 proxy_user=p_user, proxy_password=p_password, timeout=10, num_retries=0)
1125 for cmd in commands:
1126 output = client.run_command(cmd)
1127 client.join(output)
1128 if output[ip[0]].exit_code:
1129 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip[0], cmd,
1130 "\n".join(output[ip[0]].stderr))
1131 else:
1132 return 1, "VNFR {} command '{}' successful".format(ip[0], cmd)
1133 except (ssh2Exception.ChannelFailure, ssh2Exception.SocketDisconnectError, ssh2Exception.SocketTimeout,
1134 ssh2Exception.SocketRecvError) as e:
1135 return 0, "Timeout accessing the VNFR {}: {}".format(ip[0], str(e))
1136 except Exception as e:
1137 return -1, "ERROR checking the VNFR {}: {}".format(ip[0], str(e))
1138
1139 def additional_operations(self, engine, test_osm, manual_check):
1140 pass
1141
1142 def run(self, engine, test_osm, manual_check, test_params=None):
1143 engine.set_test_name(self.test_name)
1144 engine.get_autorization()
1145 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1146 if test_params:
1147 if "vnfd-files" in test_params:
1148 self.vnfd_filenames = test_params["vnfd-files"].split(",")
1149 if "nsd-file" in test_params:
1150 self.nsd_filename = test_params["nsd-file"]
1151 if test_params.get("ns-name"):
1152 nsname = test_params["ns-name"]
1153 self.create_descriptors(engine)
1154
1155 # create real VIM if not exist
1156 self.vim_id = engine.get_create_vim(test_osm)
1157 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1158 "vimAccountId": self.vim_id}
1159 if self.ns_params:
1160 ns_data.update(self.ns_params)
1161 if test_params and test_params.get("ns-config"):
1162 if isinstance(test_params["ns-config"], str):
1163 ns_data.update(yaml.load(test_params["ns-config"]))
1164 else:
1165 ns_data.update(test_params["ns-config"])
1166 self.instantiate(engine, ns_data)
1167
1168 if manual_check:
1169 input('NS has been deployed. Perform manual check and press enter to resume')
1170 if test_osm and self.commands:
1171 self.test_ns(engine, test_osm)
1172 self.additional_operations(engine, test_osm, manual_check)
1173 self.terminate(engine)
1174 self.delete_descriptors(engine)
1175
1176 def get_first_ip(self, ip_string):
1177 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1178 first_ip = ip_string.split(";")[0] if ip_string else ""
1179 return first_ip
1180
1181 def get_vnfr_ip(self, engine, vnfr_index_wanted):
1182 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1183 ip = self.vnfr_ip_list.get(vnfr_index_wanted, "")
1184 if (ip):
1185 return self.get_first_ip(ip)
1186 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1187 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1188 vnfr_index_wanted, self.ns_id), headers_json, None,
1189 200, r_header_json, "json")
1190 if not r:
1191 return ""
1192 vnfr_data = r.json()
1193 if not (vnfr_data and vnfr_data[0]):
1194 return ""
1195 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1196 ip_list = vnfr_data[0].get("ip-address", "")
1197 if ip_list:
1198 self.vnfr_ip_list[vnfr_index_wanted] = ip_list
1199 ip = self.get_first_ip(ip_list)
1200 return ip
1201
1202
1203 class TestDeployHackfestCirros(TestDeploy):
1204 description = "Load and deploy Hackfest cirros_2vnf_ns example"
1205
1206 def __init__(self):
1207 super().__init__()
1208 self.test_name = "CIRROS"
1209 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1210 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1211 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1212 self.users = {'1': "cirros", '2': "cirros"}
1213 self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1214
1215 def terminate(self, engine):
1216 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1217 if test_osm:
1218 engine.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1219 format(self.ns_id), headers_yaml, None, 202, None, "yaml")
1220
1221 engine .wait_until_delete("/nslcm/v1/ns_instances/{}".format(self.ns_id), timeout_deploy)
1222 else:
1223 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1224 headers_yaml, None, 204, None, 0)
1225
1226 # check all it is deleted
1227 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1228 404, None, "yaml")
1229 r = engine.test("Check NSLCMOPs are deleted", "GET",
1230 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1231 200, None, "json")
1232 if not r:
1233 return
1234 nslcmops = r.json()
1235 if not isinstance(nslcmops, list) or nslcmops:
1236 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1237
1238
1239 class TestDeployHackfest1(TestDeploy):
1240 description = "Load and deploy Hackfest_1_vnfd example"
1241
1242 def __init__(self):
1243 super().__init__()
1244 self.test_name = "HACKFEST1-"
1245 self.vnfd_filenames = ("hackfest_1_vnfd.tar.gz",)
1246 self.nsd_filename = "hackfest_1_nsd.tar.gz"
1247 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1248 # self.users = {'1': "cirros", '2': "cirros"}
1249 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1250
1251
1252 class TestDeployHackfestCirrosScaling(TestDeploy):
1253 description = "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1254
1255 def __init__(self):
1256 super().__init__()
1257 self.test_name = "CIRROS-SCALE"
1258 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1259 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1260 # Modify VNFD to add scaling and count=2
1261 self.descriptor_edit = {
1262 "vnfd0": {
1263 "vdu": {
1264 "$id: 'cirros_vnfd-VM'": {"count": 2}
1265 },
1266 "scaling-group-descriptor": [{
1267 "name": "scale_cirros",
1268 "max-instance-count": 2,
1269 "vdu": [{
1270 "vdu-id-ref": "cirros_vnfd-VM",
1271 "count": 2
1272 }]
1273 }]
1274 }
1275 }
1276
1277 def additional_operations(self, engine, test_osm, manual_check):
1278 if not test_osm:
1279 return
1280 # 2 perform scale out twice
1281 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1282 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1283 for i in range(0, 2):
1284 engine.test("Execute scale action over NS", "POST",
1285 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1286 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1287 nslcmop2_scale_out = engine.last_id
1288 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1289 if manual_check:
1290 input('NS scale out done. Check that two more vdus are there')
1291 # TODO check automatic
1292
1293 # 2 perform scale in
1294 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1295 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1296 for i in range(0, 2):
1297 engine.test("Execute scale IN action over NS", "POST",
1298 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1299 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1300 nslcmop2_scale_in = engine.last_id
1301 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1302 if manual_check:
1303 input('NS scale in done. Check that two less vdus are there')
1304 # TODO check automatic
1305
1306 # perform scale in that must fail as reached limit
1307 engine.test("Execute scale IN out of limit action over NS", "POST",
1308 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1309 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1310 nslcmop2_scale_in = engine.last_id
1311 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy, expected_fail=True)
1312
1313
1314 class TestDeployIpMac(TestDeploy):
1315 description = "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1316
1317 def __init__(self):
1318 super().__init__()
1319 self.test_name = "SetIpMac"
1320 self.vnfd_filenames = ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1321 self.nsd_filename = "scenario_2vdu_set_ip_mac.yaml"
1322 self.descriptor_url = \
1323 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1324 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1325 self.users = {'1': "osm", '2': "osm"}
1326 self.passwords = {'1': "osm4u", '2': "osm4u"}
1327 self.timeout = 360
1328
1329 def run(self, engine, test_osm, manual_check, test_params=None):
1330 # super().run(engine, test_osm, manual_check, test_params)
1331 # run again setting IPs with instantiate parameters
1332 instantiation_params = {
1333 "vnf": [
1334 {
1335 "member-vnf-index": "1",
1336 "internal-vld": [
1337 {
1338 "name": "internal_vld1", # net_internal
1339 "ip-profile": {
1340 "ip-version": "ipv4",
1341 "subnet-address": "10.9.8.0/24",
1342 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1343 },
1344 "internal-connection-point": [
1345 {
1346 "id-ref": "eth2",
1347 "ip-address": "10.9.8.2",
1348 },
1349 {
1350 "id-ref": "eth3",
1351 "ip-address": "10.9.8.3",
1352 }
1353 ]
1354 },
1355 ],
1356
1357 "vdu": [
1358 {
1359 "id": "VM1",
1360 "interface": [
1361 # {
1362 # "name": "iface11",
1363 # "floating-ip-required": True,
1364 # },
1365 {
1366 "name": "iface13",
1367 "mac-address": "52:33:44:55:66:13"
1368 },
1369 ],
1370 },
1371 {
1372 "id": "VM2",
1373 "interface": [
1374 {
1375 "name": "iface21",
1376 "ip-address": "10.31.31.22",
1377 "mac-address": "52:33:44:55:66:21"
1378 },
1379 ],
1380 },
1381 ]
1382 },
1383 ]
1384 }
1385
1386 super().run(engine, test_osm, manual_check, test_params={"ns-config": instantiation_params})
1387
1388
1389 class TestDeployHackfest4(TestDeploy):
1390 description = "Load and deploy Hackfest 4 example."
1391
1392 def __init__(self):
1393 super().__init__()
1394 self.test_name = "HACKFEST4-"
1395 self.vnfd_filenames = ("hackfest_4_vnfd.tar.gz",)
1396 self.nsd_filename = "hackfest_4_nsd.tar.gz"
1397 self.uses_configuration = True
1398 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1399 self.users = {'1': "ubuntu", '2': "ubuntu"}
1400 self.passwords = {'1': "osm4u", '2': "osm4u"}
1401 # Modify VNFD to add scaling
1402 # self.descriptor_edit = {
1403 # "vnfd0": {
1404 # 'vnf-configuration': {
1405 # 'config-primitive': [{
1406 # 'name': 'touch',
1407 # 'parameter': [{
1408 # 'name': 'filename',
1409 # 'data-type': 'STRING',
1410 # 'default-value': '/home/ubuntu/touched'
1411 # }]
1412 # }]
1413 # },
1414 # 'scaling-group-descriptor': [{
1415 # 'name': 'scale_dataVM',
1416 # 'scaling-policy': [{
1417 # 'threshold-time': 0,
1418 # 'name': 'auto_cpu_util_above_threshold',
1419 # 'scaling-type': 'automatic',
1420 # 'scaling-criteria': [{
1421 # 'name': 'cpu_util_above_threshold',
1422 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1423 # 'scale-out-relational-operation': 'GE',
1424 # 'scale-in-threshold': 15,
1425 # 'scale-out-threshold': 60,
1426 # 'scale-in-relational-operation': 'LE'
1427 # }],
1428 # 'cooldown-time': 60
1429 # }],
1430 # 'max-instance-count': 10,
1431 # 'scaling-config-action': [
1432 # {'vnf-config-primitive-name-ref': 'touch',
1433 # 'trigger': 'post-scale-out'},
1434 # {'vnf-config-primitive-name-ref': 'touch',
1435 # 'trigger': 'pre-scale-in'}
1436 # ],
1437 # 'vdu': [{
1438 # 'vdu-id-ref': 'dataVM',
1439 # 'count': 1
1440 # }]
1441 # }]
1442 # }
1443 # }
1444
1445
1446 class TestDeployHackfest3Charmed(TestDeploy):
1447 description = "Load and deploy Hackfest 3charmed_ns example"
1448
1449 def __init__(self):
1450 super().__init__()
1451 self.test_name = "HACKFEST3-"
1452 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz",)
1453 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1454 self.uses_configuration = True
1455 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1456 self.users = {'1': "ubuntu", '2': "ubuntu"}
1457 self.passwords = {'1': "osm4u", '2': "osm4u"}
1458 self.descriptor_edit = {
1459 "vnfd0": yaml.safe_load(
1460 """
1461 vnf-configuration:
1462 terminate-config-primitive:
1463 - seq: '1'
1464 name: touch
1465 parameter:
1466 - name: filename
1467 value: '/home/ubuntu/last-touch1'
1468 - seq: '3'
1469 name: touch
1470 parameter:
1471 - name: filename
1472 value: '/home/ubuntu/last-touch3'
1473 - seq: '2'
1474 name: touch
1475 parameter:
1476 - name: filename
1477 value: '/home/ubuntu/last-touch2'
1478 """)
1479 }
1480
1481 def additional_operations(self, engine, test_osm, manual_check):
1482 if not test_osm:
1483 return
1484 # 1 perform action
1485 vnfr_index_selected = "2"
1486 payload = '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1487 engine.test("Exec service primitive over NS", "POST",
1488 "/nslcm/v1/ns_instances/{}/action".format(self.ns_id), headers_yaml, payload,
1489 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1490 nslcmop2_action = engine.last_id
1491 # Wait until status is Ok
1492 engine.wait_operation_ready("ns", nslcmop2_action, timeout_deploy)
1493 vnfr_ip = self.get_vnfr_ip(engine, vnfr_index_selected)
1494 if manual_check:
1495 input(
1496 "NS service primitive has been executed."
1497 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1498 format(vnfr_ip))
1499 if test_osm:
1500 commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1501 self.test_ns(engine, test_osm, commands=commands)
1502
1503 # # 2 perform scale out
1504 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1505 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1506 # engine.test("Execute scale action over NS", "POST",
1507 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1508 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1509 # nslcmop2_scale_out = engine.last_id
1510 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1511 # if manual_check:
1512 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1513 # # TODO check automatic
1514 #
1515 # # 2 perform scale in
1516 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1517 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1518 # engine.test("Execute scale action over NS", "POST",
1519 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1520 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1521 # nslcmop2_scale_in = engine.last_id
1522 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1523 # if manual_check:
1524 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1525 # # TODO check automatic
1526
1527
1528 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed):
1529 description = "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1530 "ids and member-vnf-index."
1531
1532 def __init__(self):
1533 super().__init__()
1534 self.test_name = "HACKFEST3v2-"
1535 self.qforce = "?FORCE=True"
1536 self.descriptor_edit = {
1537 "vnfd0": {
1538 "vdu": {
1539 "$[0]": {
1540 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1541 },
1542 "$[1]": None
1543 },
1544 "vnf-configuration": None,
1545 "connection-point": {
1546 "$[0]": {
1547 "id": "pdu-mgmt",
1548 "name": "pdu-mgmt",
1549 "short-name": "pdu-mgmt"
1550 },
1551 "$[1]": None
1552 },
1553 "mgmt-interface": {"cp": "pdu-mgmt"},
1554 "description": "A vnf single vdu to be used as PDU",
1555 "id": "vdu-as-pdu",
1556 "internal-vld": {
1557 "$[0]": {
1558 "id": "pdu_internal",
1559 "name": "pdu_internal",
1560 "internal-connection-point": {"$[1]": None},
1561 "short-name": "pdu_internal",
1562 "type": "ELAN"
1563 }
1564 }
1565 },
1566
1567 # Modify NSD accordingly
1568 "nsd": {
1569 "constituent-vnfd": {
1570 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1571 "$[1]": None,
1572 },
1573 "description": "A nsd to deploy the vnf to act as as PDU",
1574 "id": "nsd-as-pdu",
1575 "name": "nsd-as-pdu",
1576 "short-name": "nsd-as-pdu",
1577 "vld": {
1578 "$[0]": {
1579 "id": "mgmt_pdu",
1580 "name": "mgmt_pdu",
1581 "short-name": "mgmt_pdu",
1582 "vnfd-connection-point-ref": {
1583 "$[0]": {
1584 "vnfd-connection-point-ref": "pdu-mgmt",
1585 "vnfd-id-ref": "vdu-as-pdu",
1586 },
1587 "$[1]": None
1588 },
1589 "type": "ELAN"
1590 },
1591 "$[1]": None,
1592 }
1593 }
1594 }
1595
1596
1597 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed):
1598 description = "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1599
1600 def __init__(self):
1601 super().__init__()
1602 self.test_name = "HACKFEST3v3-"
1603 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1604 self.descriptor_edit = {
1605 "vnfd0": yaml.load(
1606 """
1607 scaling-group-descriptor:
1608 - name: "scale_dataVM"
1609 max-instance-count: 10
1610 scaling-policy:
1611 - name: "auto_cpu_util_above_threshold"
1612 scaling-type: "automatic"
1613 threshold-time: 0
1614 cooldown-time: 60
1615 scaling-criteria:
1616 - name: "cpu_util_above_threshold"
1617 scale-in-threshold: 15
1618 scale-in-relational-operation: "LE"
1619 scale-out-threshold: 60
1620 scale-out-relational-operation: "GE"
1621 vnf-monitoring-param-ref: "monitor1"
1622 vdu:
1623 - vdu-id-ref: dataVM
1624 count: 1
1625 scaling-config-action:
1626 - trigger: post-scale-out
1627 vnf-config-primitive-name-ref: touch
1628 - trigger: pre-scale-in
1629 vnf-config-primitive-name-ref: touch
1630 vdu:
1631 "$id: dataVM":
1632 monitoring-param:
1633 - id: "dataVM_cpu_util"
1634 nfvi-metric: "cpu_utilization"
1635
1636 monitoring-param:
1637 - id: "monitor1"
1638 name: "monitor1"
1639 aggregation-type: AVERAGE
1640 vdu-monitoring-param:
1641 vdu-ref: "dataVM"
1642 vdu-monitoring-param-ref: "dataVM_cpu_util"
1643 vnf-configuration:
1644 initial-config-primitive:
1645 "$[1]":
1646 parameter:
1647 "$[0]":
1648 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1649 config-primitive:
1650 "$[0]":
1651 parameter:
1652 "$[0]":
1653 default-value: "<touch_filename2>"
1654 """)
1655 }
1656 self.ns_params = {
1657 "additionalParamsForVnf": [
1658 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1659 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1660 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1661 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1662 ]
1663 }
1664
1665 def additional_operations(self, engine, test_osm, manual_check):
1666 super().additional_operations(engine, test_osm, manual_check)
1667 if not test_osm:
1668 return
1669
1670 # 2 perform scale out
1671 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1672 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1673 engine.test("Execute scale action over NS", "POST",
1674 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1675 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1676 nslcmop2_scale_out = engine.last_id
1677 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1678 if manual_check:
1679 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1680 if test_osm:
1681 commands = {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1682 self.test_ns(engine, test_osm, commands=commands)
1683 # TODO check automatic connection to scaled VM
1684
1685 # 2 perform scale in
1686 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1687 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1688 engine.test("Execute scale action over NS", "POST",
1689 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1690 (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1691 nslcmop2_scale_in = engine.last_id
1692 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1693 if manual_check:
1694 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1695 # TODO check automatic
1696
1697
1698 class TestDeploySimpleCharm(TestDeploy):
1699 description = "Deploy hackfest-4 hackfest_simplecharm example"
1700
1701 def __init__(self):
1702 super().__init__()
1703 self.test_name = "HACKFEST-SIMPLE"
1704 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1705 self.vnfd_filenames = ("hackfest_simplecharm_vnf.tar.gz",)
1706 self.nsd_filename = "hackfest_simplecharm_ns.tar.gz"
1707 self.uses_configuration = True
1708 self.commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1709 self.users = {'1': "ubuntu", '2': "ubuntu"}
1710 self.passwords = {'1': "osm4u", '2': "osm4u"}
1711
1712
1713 class TestDeploySimpleCharm2(TestDeploySimpleCharm):
1714 description = "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1715 "vnf-member-index"
1716
1717 def __init__(self):
1718 super().__init__()
1719 self.test_name = "HACKFEST-SIMPLE2-"
1720 self.qforce = "?FORCE=True"
1721 self.descriptor_edit = {
1722 "vnfd0": {
1723 "id": "hackfest.simplecharm.vnf"
1724 },
1725
1726 "nsd": {
1727 "id": "hackfest.simplecharm.ns",
1728 "constituent-vnfd": {
1729 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1730 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1731 },
1732 "vld": {
1733 "$[0]": {
1734 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1735 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1736 "$[1]": {"member-vnf-index-ref": "$2",
1737 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1738 },
1739 "$[1]": {
1740 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1741 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1742 "$[1]": {"member-vnf-index-ref": "$2",
1743 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1744 },
1745 }
1746 }
1747 }
1748
1749
1750 class TestDeploySingleVdu(TestDeployHackfest3Charmed):
1751 description = "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1752
1753 def __init__(self):
1754 super().__init__()
1755 self.test_name = "SingleVDU"
1756 self.qforce = "?FORCE=True"
1757 self.descriptor_edit = {
1758 # Modify VNFD to remove one VDU
1759 "vnfd0": {
1760 "vdu": {
1761 "$[0]": {
1762 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1763 },
1764 "$[1]": None
1765 },
1766 "vnf-configuration": None,
1767 "connection-point": {
1768 "$[0]": {
1769 "id": "pdu-mgmt",
1770 "name": "pdu-mgmt",
1771 "short-name": "pdu-mgmt"
1772 },
1773 "$[1]": None
1774 },
1775 "mgmt-interface": {"cp": "pdu-mgmt"},
1776 "description": "A vnf single vdu to be used as PDU",
1777 "id": "vdu-as-pdu",
1778 "internal-vld": {
1779 "$[0]": {
1780 "id": "pdu_internal",
1781 "name": "pdu_internal",
1782 "internal-connection-point": {"$[1]": None},
1783 "short-name": "pdu_internal",
1784 "type": "ELAN"
1785 }
1786 }
1787 },
1788
1789 # Modify NSD accordingly
1790 "nsd": {
1791 "constituent-vnfd": {
1792 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1793 "$[1]": None,
1794 },
1795 "description": "A nsd to deploy the vnf to act as as PDU",
1796 "id": "nsd-as-pdu",
1797 "name": "nsd-as-pdu",
1798 "short-name": "nsd-as-pdu",
1799 "vld": {
1800 "$[0]": {
1801 "id": "mgmt_pdu",
1802 "name": "mgmt_pdu",
1803 "short-name": "mgmt_pdu",
1804 "vnfd-connection-point-ref": {
1805 "$[0]": {
1806 "vnfd-connection-point-ref": "pdu-mgmt",
1807 "vnfd-id-ref": "vdu-as-pdu",
1808 },
1809 "$[1]": None
1810 },
1811 "type": "ELAN"
1812 },
1813 "$[1]": None,
1814 }
1815 }
1816 }
1817
1818
1819 class TestDeployHnfd(TestDeployHackfest3Charmed):
1820 description = "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1821
1822 def __init__(self):
1823 super().__init__()
1824 self.test_name = "HNFD"
1825 self.pduDeploy = TestDeploySingleVdu()
1826 self.pdu_interface_0 = {}
1827 self.pdu_interface_1 = {}
1828
1829 self.pdu_id = None
1830 # self.vnf_to_pdu = """
1831 # vdu:
1832 # "$[0]":
1833 # pdu-type: PDU-TYPE-1
1834 # interface:
1835 # "$[0]":
1836 # name: mgmt-iface
1837 # "$[1]":
1838 # name: pdu-iface-internal
1839 # id: hfn1
1840 # description: HFND, one PDU + One VDU
1841 # name: hfn1
1842 # short-name: hfn1
1843 #
1844 # """
1845
1846 self.pdu_descriptor = {
1847 "name": "my-PDU",
1848 "type": "PDU-TYPE-1",
1849 "vim_accounts": "to-override",
1850 "interfaces": [
1851 {
1852 "name": "mgmt-iface",
1853 "mgmt": True,
1854 "type": "overlay",
1855 "ip-address": "to override",
1856 "mac-address": "mac_address",
1857 "vim-network-name": "mgmt",
1858 },
1859 {
1860 "name": "pdu-iface-internal",
1861 "mgmt": False,
1862 "type": "overlay",
1863 "ip-address": "to override",
1864 "mac-address": "mac_address",
1865 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1866 },
1867 ]
1868 }
1869 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1870
1871 self.descriptor_edit = {
1872 "vnfd0": {
1873 "id": "hfnd1",
1874 "name": "hfn1",
1875 "short-name": "hfn1",
1876 "vdu": {
1877 "$[0]": {
1878 "pdu-type": "PDU-TYPE-1",
1879 "interface": {
1880 "$[0]": {"name": "mgmt-iface"},
1881 "$[1]": {"name": "pdu-iface-internal"},
1882 }
1883 }
1884 }
1885 },
1886 "nsd": {
1887 "constituent-vnfd": {
1888 "$[1]": {"vnfd-id-ref": "hfnd1"}
1889 },
1890 "vld": {
1891 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1892 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1893 }
1894 }
1895 }
1896
1897 def create_descriptors(self, engine):
1898 super().create_descriptors(engine)
1899
1900 # Create PDU
1901 self.pdu_descriptor["interfaces"][0].update(self.pdu_interface_0)
1902 self.pdu_descriptor["interfaces"][1].update(self.pdu_interface_1)
1903 self.pdu_descriptor["vim_accounts"] = [self.vim_id]
1904 # TODO get vim-network-name from vnfr.vld.name
1905 self.pdu_descriptor["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1906 os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1907 "PDU", self.pdu_descriptor["interfaces"][1]["vim-network-name"])
1908 engine.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1909 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self.pdu_descriptor,
1910 201, r_header_yaml, "yaml")
1911 self.pdu_id = engine.last_id
1912
1913 def run(self, engine, test_osm, manual_check, test_params=None):
1914 engine.get_autorization()
1915 engine.set_test_name(self.test_name)
1916 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1917
1918 # create real VIM if not exist
1919 self.vim_id = engine.get_create_vim(test_osm)
1920 # instantiate PDU
1921 self.pduDeploy.create_descriptors(engine)
1922 self.pduDeploy.instantiate(engine, {"nsDescription": "to be used as PDU", "nsName": nsname + "-PDU",
1923 "nsdId": self.pduDeploy.nsd_id, "vimAccountId": self.vim_id})
1924 if manual_check:
1925 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1926 if test_osm:
1927 self.pduDeploy.test_ns(engine, test_osm)
1928
1929 if test_osm:
1930 r = engine.test("Get VNFR to obtain IP_ADDRESS", "GET",
1931 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self.pduDeploy.ns_id), headers_json, None,
1932 200, r_header_json, "json")
1933 if not r:
1934 return
1935 vnfr_data = r.json()
1936 # print(vnfr_data)
1937
1938 self.pdu_interface_0["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("ip-address")
1939 self.pdu_interface_1["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("ip-address")
1940 self.pdu_interface_0["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("mac-address")
1941 self.pdu_interface_1["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("mac-address")
1942 if not self.pdu_interface_0["ip-address"]:
1943 raise TestException("Vnfr has not managment ip address")
1944 else:
1945 self.pdu_interface_0["ip-address"] = "192.168.10.10"
1946 self.pdu_interface_1["ip-address"] = "192.168.11.10"
1947 self.pdu_interface_0["mac-address"] = "52:33:44:55:66:13"
1948 self.pdu_interface_1["mac-address"] = "52:33:44:55:66:14"
1949
1950 self.create_descriptors(engine)
1951
1952 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1953 "vimAccountId": self.vim_id}
1954 if test_params and test_params.get("ns-config"):
1955 if isinstance(test_params["ns-config"], str):
1956 ns_data.update(yaml.load(test_params["ns-config"]))
1957 else:
1958 ns_data.update(test_params["ns-config"])
1959
1960 self.instantiate(engine, ns_data)
1961 if manual_check:
1962 input('NS has been deployed. Perform manual check and press enter to resume')
1963 if test_osm:
1964 self.test_ns(engine, test_osm)
1965 self.additional_operations(engine, test_osm, manual_check)
1966 self.terminate(engine)
1967 self.pduDeploy.terminate(engine)
1968 self.delete_descriptors(engine)
1969 self.pduDeploy.delete_descriptors(engine)
1970
1971 def delete_descriptors(self, engine):
1972 super().delete_descriptors(engine)
1973 # delete pdu
1974 engine.test("Delete PDU SOL005", "DELETE",
1975 "/pdu/v1/pdu_descriptors/{}".format(self.pdu_id),
1976 headers_yaml, None, 204, None, 0)
1977
1978
1979 class TestDescriptors:
1980 description = "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1981 vnfd_empty = """vnfd:vnfd-catalog:
1982 vnfd:
1983 - name: prova
1984 short-name: prova
1985 id: prova
1986 """
1987 vnfd_prova = """vnfd:vnfd-catalog:
1988 vnfd:
1989 - connection-point:
1990 - name: cp_0h8m
1991 type: VPORT
1992 id: prova
1993 name: prova
1994 short-name: prova
1995 vdu:
1996 - id: vdu_z4bm
1997 image: ubuntu
1998 interface:
1999 - external-connection-point-ref: cp_0h8m
2000 name: eth0
2001 virtual-interface:
2002 type: VIRTIO
2003 name: vdu_z4bm
2004 version: '1.0'
2005 """
2006
2007 def __init__(self):
2008 self.vnfd_filename = "hackfest_3charmed_vnfd.tar.gz"
2009 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
2010 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2011 self.vnfd_id = None
2012 self.nsd_id = None
2013
2014 def run(self, engine, test_osm, manual_check, test_params=None):
2015 engine.set_test_name("Descriptors")
2016 engine.get_autorization()
2017 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
2018 if not os.path.exists(temp_dir):
2019 os.makedirs(temp_dir)
2020
2021 # download files
2022 for filename in (self.vnfd_filename, self.nsd_filename):
2023 filename_path = temp_dir + filename
2024 if not os.path.exists(filename_path):
2025 with open(filename_path, "wb") as file:
2026 response = requests.get(self.descriptor_url + filename)
2027 if response.status_code >= 300:
2028 raise TestException("Error downloading descriptor from '{}': {}".format(
2029 self.descriptor_url + filename, response.status_code))
2030 file.write(response.content)
2031
2032 vnfd_filename_path = temp_dir + self.vnfd_filename
2033 nsd_filename_path = temp_dir + self.nsd_filename
2034
2035 engine.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2036 self.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
2037 self.vnfd_id = engine.last_id
2038
2039 # test bug 605
2040 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
2041 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
2042
2043 engine.test("Upload VNFD {}".format(self.vnfd_filename), "PUT",
2044 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip_yaml,
2045 "@b" + vnfd_filename_path, 204, None, 0)
2046
2047 queries = ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2048 "vdu.0.interface.1.internal-connection-point-ref=internal",
2049 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2050 # Detection of duplicated VLD names in VNF Descriptors
2051 # URL: internal-vld=[
2052 # {id: internal1, name: internal, type:ELAN,
2053 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2054 # {id: internal2, name: internal, type:ELAN,
2055 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2056 # ]
2057 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2058 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2059 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2060 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2061 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2062 ]
2063 for query in queries:
2064 engine.test("Upload invalid VNFD ", "PUT",
2065 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self.vnfd_id, query),
2066 headers_zip_yaml, "@b" + vnfd_filename_path, 422, r_header_yaml, "yaml")
2067
2068 # test bug 605
2069 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
2070 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
2071
2072 # get vnfd descriptor
2073 engine.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
2074 headers_yaml, None, 200, r_header_yaml, "yaml")
2075
2076 # get vnfd file descriptor
2077 engine.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self.vnfd_id),
2078 headers_text, None, 200, r_header_text, "text", temp_dir+"vnfd-yaml")
2079 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2080
2081 # get vnfd zip file package
2082 engine.test("Get VNFD zip package", "GET",
2083 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip, None, 200,
2084 r_header_zip, "zip", temp_dir+"vnfd-zip")
2085 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2086
2087 # get vnfd artifact
2088 engine.test("Get VNFD artifact package", "GET",
2089 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self.vnfd_id), headers_zip, None, 200,
2090 r_header_octect, "octet-string", temp_dir+"vnfd-icon")
2091 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2092
2093 # nsd CREATE AND UPLOAD in one step:
2094 engine.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml,
2095 "@b" + nsd_filename_path, 201, r_headers_yaml_location_nsd, "yaml")
2096 self.nsd_id = engine.last_id
2097
2098 queries = ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2099 for query in queries:
2100 engine.test("Upload invalid NSD ", "PUT",
2101 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self.nsd_id, query),
2102 headers_zip_yaml, "@b" + nsd_filename_path, 422, r_header_yaml, "yaml")
2103
2104 # get nsd descriptor
2105 engine.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml,
2106 None, 200, r_header_yaml, "yaml")
2107
2108 # get nsd file descriptor
2109 engine.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self.nsd_id), headers_text,
2110 None, 200, r_header_text, "text", temp_dir+"nsd-yaml")
2111 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2112
2113 # get nsd zip file package
2114 engine.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self.nsd_id),
2115 headers_zip, None, 200, r_header_zip, "zip", temp_dir+"nsd-zip")
2116 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2117
2118 # get nsd artifact
2119 engine.test("Get NSD artifact package", "GET",
2120 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self.nsd_id), headers_zip, None, 200,
2121 r_header_octect, "octet-string", temp_dir+"nsd-icon")
2122 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2123
2124 # vnfd DELETE
2125 test_rest.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
2126 headers_yaml, None, 409, None, None)
2127
2128 test_rest.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self.vnfd_id),
2129 headers_yaml, None, 204, None, 0)
2130
2131 # nsd DELETE
2132 test_rest.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml, None, 204,
2133 None, 0)
2134
2135
2136 class TestNetSliceTemplates:
2137 description = "Upload a NST to OSM"
2138
2139 def __init__(self):
2140 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2141 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2142 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2143 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2144 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2145
2146 def run(self, engine, test_osm, manual_check, test_params=None):
2147 # nst CREATE
2148 engine.set_test_name("NST step ")
2149 engine.get_autorization()
2150 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
2151 if not os.path.exists(temp_dir):
2152 os.makedirs(temp_dir)
2153
2154 # Onboard VNFDs
2155 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2156 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2157 self.vnfd_edge_id = engine.last_id
2158
2159 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2160 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2161 self.vnfd_middle_id = engine.last_id
2162
2163 # Onboard NSDs
2164 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2165 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2166 self.nsd_edge_id = engine.last_id
2167
2168 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2169 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2170 self.nsd_middle_id = engine.last_id
2171
2172 # Onboard NST
2173 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2174 201, r_headers_yaml_location_nst, "yaml")
2175 nst_id = engine.last_id
2176
2177 # nstd SHOW OSM format
2178 engine.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2179 200, r_header_json, "json")
2180
2181 # nstd DELETE
2182 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2183 204, None, 0)
2184
2185 # NSDs DELETE
2186 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2187 headers_json, None, 204, None, 0)
2188
2189 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2190 None, 204, None, 0)
2191
2192 # VNFDs DELETE
2193 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2194 headers_yaml, None, 204, None, 0)
2195
2196 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2197 headers_yaml, None, 204, None, 0)
2198
2199
2200 class TestNetSliceInstances:
2201 '''
2202 Test procedure:
2203 1. Populate databases with VNFD, NSD, NST with the following scenario
2204 +-----------------management-----------------+
2205 | | |
2206 +--+---+ +----+----+ +---+--+
2207 | | | | | |
2208 | edge +---data1----+ middle +---data2-----+ edge |
2209 | | | | | |
2210 +------+ +---------+ +------+
2211 shared-nss
2212 2. Create NSI-1
2213 3. Instantiate NSI-1
2214 4. Create NSI-2
2215 5. Instantiate NSI-2
2216 Manual check - Are 2 slices instantiated correctly?
2217 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2218 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2219 6. Terminate NSI-1
2220 7. Delete NSI-1
2221 Manual check - Is slice NSI-1 deleted correctly?
2222 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2223 8. Create NSI-3
2224 9. Instantiate NSI-3
2225 Manual check - Is slice NSI-3 instantiated correctly?
2226 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2227 10. Delete NSI-2
2228 11. Terminate NSI-2
2229 12. Delete NSI-3
2230 13. Terminate NSI-3
2231 Manual check - All cleaned correctly?
2232 NSI-2 and NSI-3 were terminated and deleted
2233 14. Cleanup database
2234 '''
2235
2236 description = "Upload a NST to OSM"
2237
2238 def __init__(self):
2239 self.vim_id = None
2240 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2241 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2242 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2243 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2244 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2245
2246 def create_slice(self, engine, nsi_data, name):
2247 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2248 r = engine.test(name, "POST", "/nsilcm/v1/netslice_instances",
2249 headers_yaml, ns_data_text, (201, 202),
2250 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2251 return r
2252
2253 def instantiate_slice(self, engine, nsi_data, nsi_id, name):
2254 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2255 engine.test(name, "POST",
2256 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id), headers_yaml, ns_data_text,
2257 (201, 202), r_headers_yaml_location_nsilcmop, "yaml")
2258
2259 def terminate_slice(self, engine, nsi_id, name):
2260 engine.test(name, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id),
2261 headers_yaml, None, (201, 202), r_headers_yaml_location_nsilcmop, "yaml")
2262
2263 def delete_slice(self, engine, nsi_id, name):
2264 engine.test(name, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id), headers_yaml, None,
2265 204, None, 0)
2266
2267 def run(self, engine, test_osm, manual_check, test_params=None):
2268 # nst CREATE
2269 engine.set_test_name("NSI")
2270 engine.get_autorization()
2271
2272 # Onboard VNFDs
2273 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2274 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2275 self.vnfd_edge_id = engine.last_id
2276
2277 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2278 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2279 self.vnfd_middle_id = engine.last_id
2280
2281 # Onboard NSDs
2282 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2283 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2284 self.nsd_edge_id = engine.last_id
2285
2286 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2287 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2288 self.nsd_middle_id = engine.last_id
2289
2290 # Onboard NST
2291 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2292 201, r_headers_yaml_location_nst, "yaml")
2293 nst_id = engine.last_id
2294
2295 self.vim_id = engine.get_create_vim(test_osm)
2296
2297 # CREATE NSI-1
2298 ns_data = {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2299 r = self.create_slice(engine, ns_data, "Create NSI-1 step 1")
2300 if not r:
2301 return
2302 self.nsi_id1 = engine.last_id
2303
2304 # INSTANTIATE NSI-1
2305 self.instantiate_slice(engine, ns_data, self.nsi_id1, "Instantiate NSI-1 step 2")
2306 nsilcmop_id1 = engine.last_id
2307
2308 # Waiting for NSI-1
2309 if test_osm:
2310 engine.wait_operation_ready("nsi", nsilcmop_id1, timeout_deploy)
2311
2312 # CREATE NSI-2
2313 ns_data = {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2314 r = self.create_slice(engine, ns_data, "Create NSI-2 step 1")
2315 if not r:
2316 return
2317 self.nsi_id2 = engine.last_id
2318
2319 # INSTANTIATE NSI-2
2320 self.instantiate_slice(engine, ns_data, self.nsi_id2, "Instantiate NSI-2 step 2")
2321 nsilcmop_id2 = engine.last_id
2322
2323 # Waiting for NSI-2
2324 if test_osm:
2325 engine.wait_operation_ready("nsi", nsilcmop_id2, timeout_deploy)
2326
2327 if manual_check:
2328 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2329
2330 # TERMINATE NSI-1
2331 if test_osm:
2332 self.terminate_slice(engine, self.nsi_id1, "Terminate NSI-1")
2333 nsilcmop1_id = engine.last_id
2334
2335 # Wait terminate NSI-1
2336 engine.wait_operation_ready("nsi", nsilcmop1_id, timeout_deploy)
2337
2338 # DELETE NSI-1
2339 self.delete_slice(engine, self.nsi_id1, "Delete NS")
2340
2341 if manual_check:
2342 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2343
2344 # CREATE NSI-3
2345 ns_data = {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2346 r = self.create_slice(engine, ns_data, "Create NSI-3 step 1")
2347
2348 if not r:
2349 return
2350 self.nsi_id3 = engine.last_id
2351
2352 # INSTANTIATE NSI-3
2353 self.instantiate_slice(engine, ns_data, self.nsi_id3, "Instantiate NSI-3 step 2")
2354 nsilcmop_id3 = engine.last_id
2355
2356 # Wait Instantiate NSI-3
2357 if test_osm:
2358 engine.wait_operation_ready("nsi", nsilcmop_id3, timeout_deploy)
2359
2360 if manual_check:
2361 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2362
2363 # TERMINATE NSI-2
2364 if test_osm:
2365 self.terminate_slice(engine, self.nsi_id2, "Terminate NSI-2")
2366 nsilcmop2_id = engine.last_id
2367
2368 # Wait terminate NSI-2
2369 engine.wait_operation_ready("nsi", nsilcmop2_id, timeout_deploy)
2370
2371 # DELETE NSI-2
2372 self.delete_slice(engine, self.nsi_id2, "DELETE NSI-2")
2373
2374 # TERMINATE NSI-3
2375 if test_osm:
2376 self. terminate_slice(engine, self.nsi_id3, "Terminate NSI-3")
2377 nsilcmop3_id = engine.last_id
2378
2379 # Wait terminate NSI-3
2380 engine.wait_operation_ready("nsi", nsilcmop3_id, timeout_deploy)
2381
2382 # DELETE NSI-3
2383 self.delete_slice(engine, self.nsi_id3, "DELETE NSI-3")
2384
2385 if manual_check:
2386 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2387
2388 # nstd DELETE
2389 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2390 204, None, 0)
2391
2392 # NSDs DELETE
2393 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2394 headers_json, None, 204, None, 0)
2395
2396 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2397 None, 204, None, 0)
2398
2399 # VNFDs DELETE
2400 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2401 headers_yaml, None, 204, None, 0)
2402
2403 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2404 headers_yaml, None, 204, None, 0)
2405
2406
2407 if __name__ == "__main__":
2408 global logger
2409 test = ""
2410
2411 # Disable warnings from self-signed certificates.
2412 requests.packages.urllib3.disable_warnings()
2413 try:
2414 logging.basicConfig(format="%(levelname)s %(message)s", level=logging.ERROR)
2415 logger = logging.getLogger('NBI')
2416 # load parameters and configuration
2417 opts, args = getopt.getopt(sys.argv[1:], "hvu:p:",
2418 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2419 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2420 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2421 url = "https://localhost:9999/osm"
2422 user = password = project = "admin"
2423 test_osm = False
2424 manual_check = False
2425 verbose = 0
2426 verify = True
2427 fail_fast = False
2428 test_classes = {
2429 "NonAuthorized": TestNonAuthorized,
2430 "FakeVIM": TestFakeVim,
2431 "Users-Projects": TestUsersProjects,
2432 "Projects-Descriptors": TestProjectsDescriptors,
2433 "VIM-SDN": TestVIMSDN,
2434 "Deploy-Custom": TestDeploy,
2435 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros,
2436 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling,
2437 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed,
2438 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2,
2439 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3,
2440 "Deploy-Hackfest-4": TestDeployHackfest4,
2441 "Deploy-CirrosMacIp": TestDeployIpMac,
2442 "Descriptors": TestDescriptors,
2443 "Deploy-Hackfest1": TestDeployHackfest1,
2444 # "Deploy-MultiVIM": TestDeployMultiVIM,
2445 "Deploy-SingleVdu": TestDeploySingleVdu,
2446 "Deploy-Hnfd": TestDeployHnfd,
2447 "Upload-Slice-Template": TestNetSliceTemplates,
2448 "Deploy-Slice-Instance": TestNetSliceInstances,
2449 "Deploy-SimpleCharm": TestDeploySimpleCharm,
2450 "Deploy-SimpleCharm2": TestDeploySimpleCharm2,
2451 }
2452 test_to_do = []
2453 test_params = {}
2454
2455 for o, a in opts:
2456 # print("parameter:", o, a)
2457 if o == "--version":
2458 print("test version " + __version__ + ' ' + version_date)
2459 exit()
2460 elif o == "--list":
2461 for test, test_class in sorted(test_classes.items()):
2462 print("{:32} {}".format(test + ":", test_class.description))
2463 exit()
2464 elif o in ("-v", "--verbose"):
2465 verbose += 1
2466 elif o == "no-verbose":
2467 verbose = -1
2468 elif o in ("-h", "--help"):
2469 usage()
2470 sys.exit()
2471 elif o == "--test-osm":
2472 test_osm = True
2473 elif o == "--manual-check":
2474 manual_check = True
2475 elif o == "--url":
2476 url = a
2477 elif o in ("-u", "--user"):
2478 user = a
2479 elif o in ("-p", "--password"):
2480 password = a
2481 elif o == "--project":
2482 project = a
2483 elif o == "--fail-fast":
2484 fail_fast = True
2485 elif o == "--test":
2486 # print("asdfadf", o, a, a.split(","))
2487 for _test in a.split(","):
2488 if _test not in test_classes:
2489 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test),
2490 file=sys.stderr)
2491 exit(1)
2492 test_to_do.append(_test)
2493 elif o == "--params":
2494 param_key, _, param_value = a.partition("=")
2495 text_index = len(test_to_do)
2496 if text_index not in test_params:
2497 test_params[text_index] = {}
2498 test_params[text_index][param_key] = param_value
2499 elif o == "--insecure":
2500 verify = False
2501 elif o == "--timeout":
2502 timeout = int(a)
2503 elif o == "--timeout-deploy":
2504 timeout_deploy = int(a)
2505 elif o == "--timeout-configure":
2506 timeout_configure = int(a)
2507 else:
2508 assert False, "Unhandled option"
2509 if verbose == 0:
2510 logger.setLevel(logging.WARNING)
2511 elif verbose > 1:
2512 logger.setLevel(logging.DEBUG)
2513 else:
2514 logger.setLevel(logging.ERROR)
2515
2516 test_rest = TestRest(url, user=user, password=password, project=project)
2517 # print("tests to do:", test_to_do)
2518 if test_to_do:
2519 text_index = 0
2520 for test in test_to_do:
2521 if fail_fast and test_rest.failed_tests:
2522 break
2523 text_index += 1
2524 test_class = test_classes[test]
2525 test_class().run(test_rest, test_osm, manual_check, test_params.get(text_index))
2526 else:
2527 for test, test_class in sorted(test_classes.items()):
2528 if fail_fast and test_rest.failed_tests:
2529 break
2530 test_class().run(test_rest, test_osm, manual_check, test_params.get(0))
2531 test_rest.print_results()
2532 exit(1 if test_rest.failed_tests else 0)
2533
2534 except TestException as e:
2535 logger.error(test + "Test {} Exception: {}".format(test, str(e)))
2536 exit(1)
2537 except getopt.GetoptError as e:
2538 logger.error(e)
2539 print(e, file=sys.stderr)
2540 exit(1)
2541 except Exception as e:
2542 logger.critical(test + " Exception: " + str(e), exc_info=True)