2 # -*- coding: utf-8 -*-
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
25 from time
import sleep
26 from random
import randint
28 from sys
import stderr
30 __author__
= "Alfonso Tierno, alfonso.tiernosepulveda@telefonica.com"
31 __date__
= "$2018-03-01$"
33 version_date
= "Oct 2018"
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")
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 "
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\"")
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"}
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"),
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
104 class TestException(Exception):
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
= {}
114 self
.header_base
= header_base
.copy()
115 self
.s
= requests
.session()
116 self
.s
.headers
= self
.header_base
120 self
.password
= password
121 self
.project
= project
123 # contains ID of tests obtained from Location response header. "" key contains last obtained 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
130 def set_test_name(self
, test_name
):
131 self
.test_name
= test_name
135 def set_header(self
, header
):
136 self
.s
.headers
.update(header
)
138 def set_tet_name(self
, test_name
):
139 self
.test_name
= test_name
141 def unset_header(self
, key
):
142 if key
in self
.s
.headers
:
143 del self
.s
.headers
[key
]
145 def test(self
, description
, method
, url
, headers
, payload
, expected_codes
, expected_headers
,
146 expected_payload
, store_file
=None, pooling
=False):
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
165 self
.s
= requests
.session()
169 elif not url
.startswith("http"):
170 url
= self
.url_base
+ url
172 # replace url <> with the last ID
173 url
= url
.replace("<>", self
.last_id
)
175 if isinstance(payload
, str):
176 if payload
.startswith("@"):
178 file_name
= payload
[1:]
179 if payload
.startswith("@b"):
181 file_name
= payload
[2:]
182 with
open(file_name
, mode
) as f
:
184 elif isinstance(payload
, dict):
185 payload
= json
.dumps(payload
)
188 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, method
, url
)
189 logger
.warning(test_description
)
192 if expected_payload
in ("zip", "octet-string") or store_file
:
197 r
= getattr(self
.s
, method
.lower())(url
, data
=payload
, headers
=headers
, verify
=self
.verify
,
200 except requests
.exceptions
.ConnectionError
as e
:
203 logger
.error("Exception {}. Retrying".format(e
))
206 if expected_payload
in ("zip", "octet-string") or store_file
:
207 logger
.debug("RX {}".format(r
.status_code
))
209 logger
.debug("RX {}: {}".format(r
.status_code
, r
.text
))
213 if isinstance(expected_codes
, int):
214 expected_codes
= (expected_codes
,)
215 if r
.status_code
not in expected_codes
:
217 "Got status {}. Expected {}. {}".format(r
.status_code
, expected_codes
, r
.text
))
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
]))
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":
233 except Exception as e
:
234 raise TestException("Expected json response payload, but got Exception {}".format(e
))
235 elif expected_payload
== "yaml":
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")
244 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
245 # for tarinfo in tar:
246 # tarname = tarinfo.name
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")
255 with
open(store_file
, 'wb') as fd
:
256 for chunk
in r
.iter_content(chunk_size
=128):
259 location
= r
.headers
.get("Location")
261 _id
= location
[location
.rfind("/") + 1:]
263 self
.last_id
= str(_id
)
265 self
.passed_tests
+= 1
267 except TestException
as e
:
268 self
.failed_tests
+= 1
272 r_status_code
= r
.status_code
274 logger
.error("{} \nRX code{}: {}".format(e
, r_status_code
, r_text
))
279 logger
.error("Cannot open file {}: {}".format(store_file
, e
))
281 logger
.error("Exception: {}".format(e
), exc_info
=True)
282 self
.failed_tests
+= 1
285 except requests
.exceptions
.RequestException
as e
:
286 logger
.error("Exception: {}".format(e
))
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:
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")
300 self
.token
= response
["id"]
301 self
.set_header({"Authorization": "Bearer {}".format(self
.token
)})
303 def remove_authorization(self
):
305 self
.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self
.token
), headers_json
,
306 None, (200, 201, 204), None, None)
308 self
.unset_header("Authorization")
310 def get_create_vim(self
, test_osm
):
313 self
.get_autorization()
315 vim_name
= os
.environ
.get("OSMNBITEST_VIM_NAME")
318 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
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")
328 return vims
[0]["_id"]
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"))
346 vim_data
= "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
347 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
348 self
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml
, vim_data
,
349 (201), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
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("--------------------------------------------")
358 def wait_until_delete(self
, url_op
, timeout_delete
):
360 Make a pooling until topic is not present, because of deleted
362 :param timeout_delete:
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
)
370 wait
= timeout_delete
372 r
= self
.test(description
, "GET", url_op
, headers_yaml
, None, (200, 404), None, r_header_yaml
, "yaml",
376 if r
.status_code
== 404:
377 self
.passed_tests
+= 1
379 elif r
.status_code
== 200:
383 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete
))
384 self
.failed_tests
+= 1
386 def wait_operation_ready(self
, ns_nsi
, opp_id
, timeout
, expected_fail
=False):
388 Wait until nslcmop or nsilcmop finished
389 :param ns_nsi: "ns" o "nsi"
390 :param opp_id: Id o fthe operation
392 :param expected_fail:
393 :return: None. Updates passed/failed_tests
396 url_op
= "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id
)
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
)
405 r
= self
.test(description
, "GET", url_op
, headers_json
, None,
406 200, r_header_json
, "json", pooling
=True)
410 if "COMPLETED" in nslcmop
["operationState"]:
412 logger
.error("NS terminate has success, expecting failing: {}".format(nslcmop
["detailed-status"]))
413 self
.failed_tests
+= 1
415 self
.passed_tests
+= 1
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
422 self
.passed_tests
+= 1
425 print(".", end
="", file=stderr
)
429 self
.failed_tests
+= 1
430 logger
.error("NS instantiate is not terminate after {} seconds".format(timeout
))
432 print("", file=stderr
)
435 class TestNonAuthorized
:
436 description
= "Test invalid URLs. methods and no authorization"
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"),
447 for t
in test_not_authorized_list
:
451 class TestUsersProjects
:
452 description
= "test project and user creation"
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}, 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
)
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
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
)
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
500 engine
.test("Edit user U1, change password", "PATCH", "/admin/v1/users/U1", headers_json
,
501 {"password": "pw1_new"}, 204, None, None)
503 engine
.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json
,
504 {"project_id": "P1"}, 401, r_header_json
, "json")
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")
510 response
= res
.json()
511 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
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")
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")
524 response
= res
.json()
525 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
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)
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)
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)
545 engine
.remove_authorization() # To force get authorization
546 engine
.get_autorization()
547 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
548 engine
.test("Delete project P1", "DELETE", "/admin/v1/projects/P1", headers_json
, None, 204, None, None)
549 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204,
554 description
= "Creates/edit/delete fake VIMs and SDN controllers"
558 "schema_version": "1.0",
559 "schema_type": "No idea",
561 "description": "Descriptor name",
562 "vim_type": "openstack",
563 "vim_url": "http://localhost:/vim",
564 "vim_tenant_name": "vimTenant",
566 "vim_password": "password",
567 "config": {"config_param": 1}
571 "description": "sdn-description",
572 "dpid": "50:50:52:54:00:94:21:21",
573 "ip": "192.168.15.17",
575 "type": "opendaylight",
580 self
.port_mapping
= [
581 {"compute_node": "compute node 1",
582 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
583 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
585 {"compute_node": "compute node 2",
586 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
587 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
591 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
593 vim_bad
= self
.vim
.copy()
596 engine
.set_test_name("FakeVim")
597 engine
.get_autorization()
598 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 204),
599 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
600 vim_id
= engine
.last_id
601 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
602 vim_bad
, 422, None, headers_json
)
603 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
604 409, None, headers_json
)
605 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
607 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
608 r_header_yaml
, "yaml")
611 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
613 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
614 404, r_header_yaml
, "yaml")
616 # delete and wait until is really deleted
617 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
619 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
622 class TestVIMSDN(TestFakeVim
):
623 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
626 TestFakeVim
.__init
__(self
)
628 "schema_version": "1.0",
629 "schema_type": "No idea",
631 "description": "Descriptor name",
633 "wim_url": "http://localhost:/wim",
635 "password": "password",
636 "config": {"config_param": 1}
639 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
640 engine
.set_test_name("VimSdn")
641 engine
.get_autorization()
643 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 204),
644 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
645 sdnc_id
= engine
.last_id
648 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
652 self
.vim
["config"]["sdn-controller"] = sdnc_id
653 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
654 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 204, 201),
655 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
657 vim_id
= engine
.last_id
658 self
.port_mapping
[0]["compute_node"] = "compute node XX"
659 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
660 {"config": {"sdn-port-mapping": self
.port_mapping
}}, 204, None, None)
661 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
662 {"config": {"sdn-port-mapping": None}}, 204, None, None)
664 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 204, 201),
665 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
666 wim_id
= engine
.last_id
670 engine
.test("Delete VIM remove port-mapping", "DELETE",
671 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
672 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
675 engine
.test("Delete WIM", "DELETE",
676 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
677 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
678 None, 404, r_header_yaml
, "yaml")
679 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
680 404, r_header_yaml
, "yaml")
681 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
682 None, 404, r_header_yaml
, "yaml")
685 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
686 # delete and wait until is really deleted
687 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
688 headers_json
, None, (202, 201, 204), None, 0)
689 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
690 (202, 201, 204), None, 0)
691 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
692 headers_json
, None, (202, 201, 204), None, 0)
693 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
694 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
695 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
699 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
702 self
.test_name
= "DEPLOY"
707 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
708 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
709 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
710 self
.descriptor_edit
= None
711 self
.uses_configuration
= False
718 self
.ns_params
= None
719 self
.vnfr_ip_list
= {}
721 def create_descriptors(self
, engine
):
722 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
723 if not os
.path
.exists(temp_dir
):
724 os
.makedirs(temp_dir
)
725 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
726 if "/" in vnfd_filename
:
727 vnfd_filename_path
= vnfd_filename
728 if not os
.path
.exists(vnfd_filename_path
):
729 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
731 vnfd_filename_path
= temp_dir
+ vnfd_filename
732 if not os
.path
.exists(vnfd_filename_path
):
733 with
open(vnfd_filename_path
, "wb") as file:
734 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
735 if response
.status_code
>= 300:
736 raise TestException("Error downloading descriptor from '{}': {}".format(
737 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
738 file.write(response
.content
)
739 if vnfd_filename_path
.endswith(".yaml"):
740 headers
= headers_yaml
742 headers
= headers_zip_yaml
743 if randint(0, 1) == 0:
744 # vnfd CREATE AND UPLOAD in one step:
745 engine
.test("Onboard VNFD in one step", "POST",
746 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
747 r_headers_yaml_location_vnfd
,
749 self
.vnfds_id
.append(engine
.last_id
)
751 # vnfd CREATE AND UPLOAD ZIP
752 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
753 headers_json
, None, 201,
754 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
755 self
.vnfds_id
.append(engine
.last_id
)
756 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
757 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
758 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
760 if self
.descriptor_edit
:
761 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
763 engine
.test("Edit VNFD ", "PATCH",
764 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
765 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
767 if "/" in self
.nsd_filename
:
768 nsd_filename_path
= self
.nsd_filename
769 if not os
.path
.exists(nsd_filename_path
):
770 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
772 nsd_filename_path
= temp_dir
+ self
.nsd_filename
773 if not os
.path
.exists(nsd_filename_path
):
774 with
open(nsd_filename_path
, "wb") as file:
775 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
776 if response
.status_code
>= 300:
777 raise TestException("Error downloading descriptor from '{}': {}".format(
778 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
779 file.write(response
.content
)
780 if nsd_filename_path
.endswith(".yaml"):
781 headers
= headers_yaml
783 headers
= headers_zip_yaml
785 if randint(0, 1) == 0:
786 # nsd CREATE AND UPLOAD in one step:
787 engine
.test("Onboard NSD in one step", "POST",
788 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
789 r_headers_yaml_location_nsd
, yaml
)
790 self
.nsd_id
= engine
.last_id
792 # nsd CREATE AND UPLOAD ZIP
793 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
794 headers_json
, None, 201,
795 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
796 self
.nsd_id
= engine
.last_id
797 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
798 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
799 headers
, "@b" + nsd_filename_path
, 204, None, 0)
801 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
803 engine
.test("Edit NSD ", "PATCH",
804 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
805 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
807 def delete_descriptors(self
, engine
):
809 engine
.test("Delete NSSD SOL005", "DELETE",
810 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
811 headers_yaml
, None, 204, None, 0)
812 for vnfd_id
in self
.vnfds_id
:
813 engine
.test("Delete VNFD SOL005", "DELETE",
814 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
816 def instantiate(self
, engine
, ns_data
):
817 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
818 # create NS Two steps
819 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
820 headers_yaml
, ns_data_text
, 201,
821 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
824 self
.ns_id
= engine
.last_id
825 engine
.test("Instantiate NS step 2", "POST",
826 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
827 201, r_headers_yaml_location_nslcmop
, "yaml")
828 nslcmop_id
= engine
.last_id
831 # Wait until status is Ok
832 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
833 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
835 def terminate(self
, engine
):
838 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
839 None, 201, r_headers_yaml_location_nslcmop
, "yaml")
840 nslcmop2_id
= engine
.last_id
841 # Wait until status is Ok
842 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
844 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
847 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
848 headers_yaml
, None, 204, None, 0)
850 # check all it is deleted
851 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
853 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
854 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
859 if not isinstance(nslcmops
, list) or nslcmops
:
860 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
862 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
864 r
= engine
.test("GET VNFR IDs", "GET",
865 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
866 200, r_header_json
, "json")
871 vnfr_list
= ns_data
['constituent-vnfr-ref']
873 _commands
= commands
if commands
is not None else self
.commands
874 _users
= users
if users
is not None else self
.users
875 _passwds
= passwds
if passwds
is not None else self
.passwords
876 _keys
= keys
if keys
is not None else self
.keys
877 _timeout
= timeout
if timeout
!= 0 else self
.timeout
879 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
880 for vnfr_id
in vnfr_list
:
881 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
882 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
883 200, r_header_json
, "json")
888 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
890 ip_address
= self
.get_vnfr_ip(engine
, vnf_index
)
891 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
894 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
895 logger
.warning(test_description
)
896 while _timeout
>= time
:
897 result
, message
= self
.do_checks([ip_address
],
898 vnf_index
=vnfr_data
["member-vnf-index-ref"],
899 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
900 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
902 engine
.passed_tests
+= 1
903 logger
.debug(message
)
909 engine
.failed_tests
+= 1
910 logger
.error(message
)
914 engine
.failed_tests
+= 1
915 logger
.error(message
)
917 engine
.failed_tests
+= 1
918 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnf_index
))
920 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
923 from pssh
.clients
import ParallelSSHClient
924 from pssh
.utils
import load_private_key
925 from ssh2
import exceptions
as ssh2Exception
926 except ImportError as e
:
927 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
928 "parallel-ssh urllib3': {}".format(e
))
929 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
930 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
932 p_host
= os
.environ
.get("PROXY_HOST")
933 p_user
= os
.environ
.get("PROXY_USER")
934 p_password
= os
.environ
.get("PROXY_PASSWD")
937 pkey
= load_private_key(key
)
941 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
942 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
944 output
= client
.run_command(cmd
)
946 if output
[ip
[0]].exit_code
:
947 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
948 "\n".join(output
[ip
[0]].stderr
))
950 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
951 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
952 ssh2Exception
.SocketRecvError
) as e
:
953 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
954 except Exception as e
:
955 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
957 def additional_operations(self
, engine
, test_osm
, manual_check
):
960 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
961 engine
.set_test_name(self
.test_name
)
962 engine
.get_autorization()
963 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
965 if "vnfd-files" in test_params
:
966 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
967 if "nsd-file" in test_params
:
968 self
.nsd_filename
= test_params
["nsd-file"]
969 if test_params
.get("ns-name"):
970 nsname
= test_params
["ns-name"]
971 self
.create_descriptors(engine
)
973 # create real VIM if not exist
974 self
.vim_id
= engine
.get_create_vim(test_osm
)
975 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
976 "vimAccountId": self
.vim_id
}
978 ns_data
.update(self
.ns_params
)
979 if test_params
and test_params
.get("ns-config"):
980 if isinstance(test_params
["ns-config"], str):
981 ns_data
.update(yaml
.load(test_params
["ns-config"]))
983 ns_data
.update(test_params
["ns-config"])
984 self
.instantiate(engine
, ns_data
)
987 input('NS has been deployed. Perform manual check and press enter to resume')
988 if test_osm
and self
.commands
:
989 self
.test_ns(engine
, test_osm
)
990 self
.additional_operations(engine
, test_osm
, manual_check
)
991 self
.terminate(engine
)
992 self
.delete_descriptors(engine
)
994 def get_first_ip(self
, ip_string
):
995 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
996 first_ip
= ip_string
.split(";")[0] if ip_string
else ""
999 def get_vnfr_ip(self
, engine
, vnfr_index_wanted
):
1000 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1001 ip
= self
.vnfr_ip_list
.get(vnfr_index_wanted
, "")
1003 return self
.get_first_ip(ip
)
1004 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1005 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1006 vnfr_index_wanted
, self
.ns_id
), headers_json
, None,
1007 200, r_header_json
, "json")
1010 vnfr_data
= r
.json()
1011 if not (vnfr_data
and vnfr_data
[0]):
1013 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1014 ip_list
= vnfr_data
[0].get("ip-address", "")
1016 self
.vnfr_ip_list
[vnfr_index_wanted
] = ip_list
1017 ip
= self
.get_first_ip(ip_list
)
1021 class TestDeployHackfestCirros(TestDeploy
):
1022 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
1026 self
.test_name
= "CIRROS"
1027 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1028 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1029 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1030 self
.users
= {'1': "cirros", '2': "cirros"}
1031 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1033 def terminate(self
, engine
):
1034 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1036 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1037 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1039 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1041 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1042 headers_yaml
, None, 204, None, 0)
1044 # check all it is deleted
1045 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1047 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1048 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1053 if not isinstance(nslcmops
, list) or nslcmops
:
1054 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1057 class TestDeployHackfest1(TestDeploy
):
1058 description
= "Load and deploy Hackfest_1_vnfd example"
1062 self
.test_name
= "HACKFEST1-"
1063 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1064 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1065 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1066 # self.users = {'1': "cirros", '2': "cirros"}
1067 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1070 class TestDeployHackfestCirrosScaling(TestDeploy
):
1071 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1075 self
.test_name
= "CIRROS-SCALE"
1076 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1077 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1079 def create_descriptors(self
, engine
):
1080 super().create_descriptors(engine
)
1081 # Modify VNFD to add scaling and count=2
1082 self
.descriptor_edit
= {
1085 "$id: 'cirros_vnfd-VM'": {"count": 2}
1087 "scaling-group-descriptor": [{
1088 "name": "scale_cirros",
1089 "max-instance-count": 2,
1091 "vdu-id-ref": "cirros_vnfd-VM",
1098 def additional_operations(self
, engine
, test_osm
, manual_check
):
1101 # 2 perform scale out twice
1102 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1103 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1104 for i
in range(0, 2):
1105 engine
.test("Execute scale action over NS", "POST",
1106 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1107 201, r_headers_yaml_location_nslcmop
, "yaml")
1108 nslcmop2_scale_out
= engine
.last_id
1109 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1111 input('NS scale out done. Check that two more vdus are there')
1112 # TODO check automatic
1114 # 2 perform scale in
1115 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1116 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1117 for i
in range(0, 2):
1118 engine
.test("Execute scale IN action over NS", "POST",
1119 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1120 201, r_headers_yaml_location_nslcmop
, "yaml")
1121 nslcmop2_scale_in
= engine
.last_id
1122 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1124 input('NS scale in done. Check that two less vdus are there')
1125 # TODO check automatic
1127 # perform scale in that must fail as reached limit
1128 engine
.test("Execute scale IN out of limit action over NS", "POST",
1129 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1130 201, r_headers_yaml_location_nslcmop
, "yaml")
1131 nslcmop2_scale_in
= engine
.last_id
1132 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1135 class TestDeployIpMac(TestDeploy
):
1136 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1140 self
.test_name
= "SetIpMac"
1141 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1142 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1143 self
.descriptor_url
= \
1144 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1145 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1146 self
.users
= {'1': "osm", '2': "osm"}
1147 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1150 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1151 # super().run(engine, test_osm, manual_check, test_params)
1152 # run again setting IPs with instantiate parameters
1153 instantiation_params
= {
1156 "member-vnf-index": "1",
1159 "name": "internal_vld1", # net_internal
1161 "ip-version": "ipv4",
1162 "subnet-address": "10.9.8.0/24",
1163 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1165 "internal-connection-point": [
1168 "ip-address": "10.9.8.2",
1172 "ip-address": "10.9.8.3",
1183 # "name": "iface11",
1184 # "floating-ip-required": True,
1188 "mac-address": "52:33:44:55:66:13"
1197 "ip-address": "10.31.31.22",
1198 "mac-address": "52:33:44:55:66:21"
1207 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1210 class TestDeployHackfest4(TestDeploy
):
1211 description
= "Load and deploy Hackfest 4 example."
1215 self
.test_name
= "HACKFEST4-"
1216 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1217 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1218 self
.uses_configuration
= True
1219 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1220 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1221 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1222 # Modify VNFD to add scaling
1223 # self.descriptor_edit = {
1225 # 'vnf-configuration': {
1226 # 'config-primitive': [{
1229 # 'name': 'filename',
1230 # 'data-type': 'STRING',
1231 # 'default-value': '/home/ubuntu/touched'
1235 # 'scaling-group-descriptor': [{
1236 # 'name': 'scale_dataVM',
1237 # 'scaling-policy': [{
1238 # 'threshold-time': 0,
1239 # 'name': 'auto_cpu_util_above_threshold',
1240 # 'scaling-type': 'automatic',
1241 # 'scaling-criteria': [{
1242 # 'name': 'cpu_util_above_threshold',
1243 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1244 # 'scale-out-relational-operation': 'GE',
1245 # 'scale-in-threshold': 15,
1246 # 'scale-out-threshold': 60,
1247 # 'scale-in-relational-operation': 'LE'
1249 # 'cooldown-time': 60
1251 # 'max-instance-count': 10,
1252 # 'scaling-config-action': [
1253 # {'vnf-config-primitive-name-ref': 'touch',
1254 # 'trigger': 'post-scale-out'},
1255 # {'vnf-config-primitive-name-ref': 'touch',
1256 # 'trigger': 'pre-scale-in'}
1259 # 'vdu-id-ref': 'dataVM',
1267 class TestDeployHackfest3Charmed(TestDeploy
):
1268 description
= "Load and deploy Hackfest 3charmed_ns example"
1272 self
.test_name
= "HACKFEST3-"
1273 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1274 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1275 self
.uses_configuration
= True
1276 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1277 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1278 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1279 self
.descriptor_edit
= {
1280 "vnfd0": yaml
.full_load(
1283 terminate-config-primitive:
1288 value: '/home/ubuntu/last-touch1'
1293 value: '/home/ubuntu/last-touch3'
1298 value: '/home/ubuntu/last-touch2'
1302 def additional_operations(self
, engine
, test_osm
, manual_check
):
1306 vnfr_index_selected
= "2"
1307 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1308 engine
.test("Exec service primitive over NS", "POST",
1309 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1310 201, r_headers_yaml_location_nslcmop
, "yaml")
1311 nslcmop2_action
= engine
.last_id
1312 # Wait until status is Ok
1313 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1314 vnfr_ip
= self
.get_vnfr_ip(engine
, vnfr_index_selected
)
1317 "NS service primitive has been executed."
1318 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1321 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1322 self
.test_ns(engine
, test_osm
, commands
=commands
)
1324 # # 2 perform scale out
1325 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1326 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1327 # engine.test("Execute scale action over NS", "POST",
1328 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1329 # 201, r_headers_yaml_location_nslcmop, "yaml")
1330 # nslcmop2_scale_out = engine.last_id
1331 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1333 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1334 # # TODO check automatic
1336 # # 2 perform scale in
1337 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1338 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1339 # engine.test("Execute scale action over NS", "POST",
1340 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1341 # 201, r_headers_yaml_location_nslcmop, "yaml")
1342 # nslcmop2_scale_in = engine.last_id
1343 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1345 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1346 # # TODO check automatic
1349 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1350 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1351 "ids and member-vnf-index."
1355 self
.test_name
= "HACKFEST3v2-"
1356 self
.qforce
= "?FORCE=True"
1357 self
.descriptor_edit
= {
1361 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1365 "vnf-configuration": None,
1366 "connection-point": {
1370 "short-name": "pdu-mgmt"
1374 "mgmt-interface": {"cp": "pdu-mgmt"},
1375 "description": "A vnf single vdu to be used as PDU",
1379 "id": "pdu_internal",
1380 "name": "pdu_internal",
1381 "internal-connection-point": {"$[1]": None},
1382 "short-name": "pdu_internal",
1388 # Modify NSD accordingly
1390 "constituent-vnfd": {
1391 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1394 "description": "A nsd to deploy the vnf to act as as PDU",
1396 "name": "nsd-as-pdu",
1397 "short-name": "nsd-as-pdu",
1402 "short-name": "mgmt_pdu",
1403 "vnfd-connection-point-ref": {
1405 "vnfd-connection-point-ref": "pdu-mgmt",
1406 "vnfd-id-ref": "vdu-as-pdu",
1418 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1419 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1423 self
.test_name
= "HACKFEST3v3-"
1424 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1425 self
.descriptor_edit
= {
1428 scaling-group-descriptor:
1429 - name: "scale_dataVM"
1430 max-instance-count: 10
1432 - name: "auto_cpu_util_above_threshold"
1433 scaling-type: "automatic"
1437 - name: "cpu_util_above_threshold"
1438 scale-in-threshold: 15
1439 scale-in-relational-operation: "LE"
1440 scale-out-threshold: 60
1441 scale-out-relational-operation: "GE"
1442 vnf-monitoring-param-ref: "monitor1"
1444 - vdu-id-ref: dataVM
1446 scaling-config-action:
1447 - trigger: post-scale-out
1448 vnf-config-primitive-name-ref: touch
1449 - trigger: pre-scale-in
1450 vnf-config-primitive-name-ref: touch
1454 - id: "dataVM_cpu_util"
1455 nfvi-metric: "cpu_utilization"
1460 aggregation-type: AVERAGE
1461 vdu-monitoring-param:
1463 vdu-monitoring-param-ref: "dataVM_cpu_util"
1465 initial-config-primitive:
1469 value: "<touch-filename>" # default-value: /home/ubuntu/first-touch
1474 default-value: "<touch-filename2>"
1478 "additionalParamsForVnf": [
1479 {"member-vnf-index": "1", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-1",
1480 "touch-filename2": "/home/ubuntu/second-touch-1"}},
1481 {"member-vnf-index": "2", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-2",
1482 "touch-filename2": "/home/ubuntu/second-touch-2"}},
1486 def additional_operations(self
, engine
, test_osm
, manual_check
):
1487 super().additional_operations(engine
, test_osm
, manual_check
)
1491 # 2 perform scale out
1492 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1493 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1494 engine
.test("Execute scale action over NS", "POST",
1495 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1496 201, r_headers_yaml_location_nslcmop
, "yaml")
1497 nslcmop2_scale_out
= engine
.last_id
1498 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1500 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1502 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1503 self
.test_ns(engine
, test_osm
, commands
=commands
)
1504 # TODO check automatic connection to scaled VM
1506 # 2 perform scale in
1507 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1508 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1509 engine
.test("Execute scale action over NS", "POST",
1510 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1511 201, r_headers_yaml_location_nslcmop
, "yaml")
1512 nslcmop2_scale_in
= engine
.last_id
1513 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1515 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1516 # TODO check automatic
1519 class TestDeploySimpleCharm(TestDeploy
):
1520 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1524 self
.test_name
= "HACKFEST-SIMPLE"
1525 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1526 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1527 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1528 self
.uses_configuration
= True
1529 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1530 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1531 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1534 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1535 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1540 self
.test_name
= "HACKFEST-SIMPLE2-"
1541 self
.qforce
= "?FORCE=True"
1542 self
.descriptor_edit
= {
1544 "id": "hackfest.simplecharm.vnf"
1548 "id": "hackfest.simplecharm.ns",
1549 "constituent-vnfd": {
1550 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1551 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1555 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1556 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1557 "$[1]": {"member-vnf-index-ref": "$2",
1558 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1561 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1562 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1563 "$[1]": {"member-vnf-index-ref": "$2",
1564 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1571 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1572 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1576 self
.test_name
= "SingleVDU"
1577 self
.qforce
= "?FORCE=True"
1578 self
.descriptor_edit
= {
1579 # Modify VNFD to remove one VDU
1583 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1587 "vnf-configuration": None,
1588 "connection-point": {
1592 "short-name": "pdu-mgmt"
1596 "mgmt-interface": {"cp": "pdu-mgmt"},
1597 "description": "A vnf single vdu to be used as PDU",
1601 "id": "pdu_internal",
1602 "name": "pdu_internal",
1603 "internal-connection-point": {"$[1]": None},
1604 "short-name": "pdu_internal",
1610 # Modify NSD accordingly
1612 "constituent-vnfd": {
1613 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1616 "description": "A nsd to deploy the vnf to act as as PDU",
1618 "name": "nsd-as-pdu",
1619 "short-name": "nsd-as-pdu",
1624 "short-name": "mgmt_pdu",
1625 "vnfd-connection-point-ref": {
1627 "vnfd-connection-point-ref": "pdu-mgmt",
1628 "vnfd-id-ref": "vdu-as-pdu",
1640 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1641 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1645 self
.test_name
= "HNFD"
1646 self
.pduDeploy
= TestDeploySingleVdu()
1647 self
.pdu_interface_0
= {}
1648 self
.pdu_interface_1
= {}
1651 # self.vnf_to_pdu = """
1654 # pdu-type: PDU-TYPE-1
1659 # name: pdu-iface-internal
1661 # description: HFND, one PDU + One VDU
1667 self
.pdu_descriptor
= {
1669 "type": "PDU-TYPE-1",
1670 "vim_accounts": "to-override",
1673 "name": "mgmt-iface",
1676 "ip-address": "to override",
1677 "mac-address": "mac_address",
1678 "vim-network-name": "mgmt",
1681 "name": "pdu-iface-internal",
1684 "ip-address": "to override",
1685 "mac-address": "mac_address",
1686 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1690 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1692 self
.descriptor_edit
= {
1696 "short-name": "hfn1",
1699 "pdu-type": "PDU-TYPE-1",
1701 "$[0]": {"name": "mgmt-iface"},
1702 "$[1]": {"name": "pdu-iface-internal"},
1708 "constituent-vnfd": {
1709 "$[1]": {"vnfd-id-ref": "hfnd1"}
1712 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1713 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1718 def create_descriptors(self
, engine
):
1719 super().create_descriptors(engine
)
1722 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1723 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1724 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1725 # TODO get vim-network-name from vnfr.vld.name
1726 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1727 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1728 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1729 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1730 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1731 201, r_header_yaml
, "yaml")
1732 self
.pdu_id
= engine
.last_id
1734 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1735 engine
.get_autorization()
1736 engine
.set_test_name(self
.test_name
)
1737 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1739 # create real VIM if not exist
1740 self
.vim_id
= engine
.get_create_vim(test_osm
)
1742 self
.pduDeploy
.create_descriptors(engine
)
1743 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1744 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1746 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1748 self
.pduDeploy
.test_ns(engine
, test_osm
)
1751 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1752 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1753 200, r_header_json
, "json")
1756 vnfr_data
= r
.json()
1759 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1760 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1761 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1762 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1763 if not self
.pdu_interface_0
["ip-address"]:
1764 raise TestException("Vnfr has not managment ip address")
1766 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1767 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1768 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1769 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1771 self
.create_descriptors(engine
)
1773 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1774 "vimAccountId": self
.vim_id
}
1775 if test_params
and test_params
.get("ns-config"):
1776 if isinstance(test_params
["ns-config"], str):
1777 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1779 ns_data
.update(test_params
["ns-config"])
1781 self
.instantiate(engine
, ns_data
)
1783 input('NS has been deployed. Perform manual check and press enter to resume')
1785 self
.test_ns(engine
, test_osm
)
1786 self
.additional_operations(engine
, test_osm
, manual_check
)
1787 self
.terminate(engine
)
1788 self
.pduDeploy
.terminate(engine
)
1789 self
.delete_descriptors(engine
)
1790 self
.pduDeploy
.delete_descriptors(engine
)
1792 def delete_descriptors(self
, engine
):
1793 super().delete_descriptors(engine
)
1795 engine
.test("Delete PDU SOL005", "DELETE",
1796 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1797 headers_yaml
, None, 204, None, 0)
1800 class TestDescriptors
:
1801 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1804 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
1805 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1806 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1809 self
.vnfd_empty
= """vnfd:vnfd-catalog:
1815 self
.vnfd_prova
= """vnfd:vnfd-catalog:
1827 - external-connection-point-ref: cp_0h8m
1835 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1836 engine
.set_test_name("Descriptors")
1837 engine
.get_autorization()
1838 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
1839 if not os
.path
.exists(temp_dir
):
1840 os
.makedirs(temp_dir
)
1843 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
1844 filename_path
= temp_dir
+ filename
1845 if not os
.path
.exists(filename_path
):
1846 with
open(filename_path
, "wb") as file:
1847 response
= requests
.get(self
.descriptor_url
+ filename
)
1848 if response
.status_code
>= 300:
1849 raise TestException("Error downloading descriptor from '{}': {}".format(
1850 self
.descriptor_url
+ filename
, response
.status_code
))
1851 file.write(response
.content
)
1853 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
1854 nsd_filename_path
= temp_dir
+ self
.nsd_filename
1856 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
1857 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
1858 self
.vnfd_id
= engine
.last_id
1861 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1862 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1864 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
1865 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
1866 "@b" + vnfd_filename_path
, 204, None, 0)
1868 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
1869 "vdu.0.interface.1.internal-connection-point-ref=internal",
1870 "internal-vld.0.internal-connection-point.0.id-ref=internal",
1871 # Detection of duplicated VLD names in VNF Descriptors
1872 # URL: internal-vld=[
1873 # {id: internal1, name: internal, type:ELAN,
1874 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
1875 # {id: internal2, name: internal, type:ELAN,
1876 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
1878 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
1879 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
1880 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
1881 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
1882 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
1884 for query
in queries
:
1885 engine
.test("Upload invalid VNFD ", "PUT",
1886 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
1887 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
1890 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1891 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1893 # get vnfd descriptor
1894 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1895 headers_yaml
, None, 200, r_header_yaml
, "yaml")
1897 # get vnfd file descriptor
1898 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
1899 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
1900 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
1902 # get vnfd zip file package
1903 engine
.test("Get VNFD zip package", "GET",
1904 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
1905 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
1906 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
1909 engine
.test("Get VNFD artifact package", "GET",
1910 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
1911 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
1912 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
1914 # nsd CREATE AND UPLOAD in one step:
1915 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
1916 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
1917 self
.nsd_id
= engine
.last_id
1919 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
1920 for query
in queries
:
1921 engine
.test("Upload invalid NSD ", "PUT",
1922 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
1923 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
1925 # get nsd descriptor
1926 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
1927 None, 200, r_header_yaml
, "yaml")
1929 # get nsd file descriptor
1930 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
1931 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
1932 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
1934 # get nsd zip file package
1935 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
1936 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
1937 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
1940 engine
.test("Get NSD artifact package", "GET",
1941 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
1942 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
1943 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
1946 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1947 headers_yaml
, None, 409, None, None)
1949 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
1950 headers_yaml
, None, 204, None, 0)
1953 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
1957 class TestNetSliceTemplates
:
1958 description
= "Upload a NST to OSM"
1961 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
1962 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
1963 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
1964 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
1965 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
1967 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1969 engine
.set_test_name("NST step ")
1970 engine
.get_autorization()
1971 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
1972 if not os
.path
.exists(temp_dir
):
1973 os
.makedirs(temp_dir
)
1976 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
1977 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
1978 self
.vnfd_edge_id
= engine
.last_id
1980 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
1981 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
1982 self
.vnfd_middle_id
= engine
.last_id
1985 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
1986 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
1987 self
.nsd_edge_id
= engine
.last_id
1989 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
1990 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
1991 self
.nsd_middle_id
= engine
.last_id
1994 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
1995 201, r_headers_yaml_location_nst
, "yaml")
1996 nst_id
= engine
.last_id
1998 # nstd SHOW OSM format
1999 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2000 200, r_header_json
, "json")
2003 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2007 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2008 headers_json
, None, 204, None, 0)
2010 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2014 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2015 headers_yaml
, None, 204, None, 0)
2017 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2018 headers_yaml
, None, 204, None, 0)
2021 class TestNetSliceInstances
:
2024 1. Populate databases with VNFD, NSD, NST with the following scenario
2025 +-----------------management-----------------+
2027 +--+---+ +----+----+ +---+--+
2029 | edge +---data1----+ middle +---data2-----+ edge |
2031 +------+ +---------+ +------+
2034 3. Instantiate NSI-1
2036 5. Instantiate NSI-2
2037 Manual check - Are 2 slices instantiated correctly?
2038 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2039 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2042 Manual check - Is slice NSI-1 deleted correctly?
2043 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2045 9. Instantiate NSI-3
2046 Manual check - Is slice NSI-3 instantiated correctly?
2047 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2052 Manual check - All cleaned correctly?
2053 NSI-2 and NSI-3 were terminated and deleted
2054 14. Cleanup database
2057 description
= "Upload a NST to OSM"
2061 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2062 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2063 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2064 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2065 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2067 def create_slice(self
, engine
, nsi_data
, name
):
2068 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2069 r
= engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances",
2070 headers_yaml
, ns_data_text
, 201,
2071 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2074 def instantiate_slice(self
, engine
, nsi_data
, nsi_id
, name
):
2075 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2076 engine
.test(name
, "POST",
2077 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id
), headers_yaml
, ns_data_text
,
2078 201, r_headers_yaml_location_nsilcmop
, "yaml")
2080 def terminate_slice(self
, engine
, nsi_id
, name
):
2081 engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id
),
2082 headers_yaml
, None, 201, r_headers_yaml_location_nsilcmop
, "yaml")
2084 def delete_slice(self
, engine
, nsi_id
, name
):
2085 engine
.test(name
, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id
), headers_yaml
, None,
2088 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2090 engine
.set_test_name("NSI")
2091 engine
.get_autorization()
2094 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2095 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2096 self
.vnfd_edge_id
= engine
.last_id
2098 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2099 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2100 self
.vnfd_middle_id
= engine
.last_id
2103 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2104 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2105 self
.nsd_edge_id
= engine
.last_id
2107 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2108 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2109 self
.nsd_middle_id
= engine
.last_id
2112 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2113 201, r_headers_yaml_location_nst
, "yaml")
2114 nst_id
= engine
.last_id
2116 self
.vim_id
= engine
.get_create_vim(test_osm
)
2119 ns_data
= {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2120 r
= self
.create_slice(engine
, ns_data
, "Create NSI-1 step 1")
2123 self
.nsi_id1
= engine
.last_id
2126 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id1
, "Instantiate NSI-1 step 2")
2127 nsilcmop_id1
= engine
.last_id
2130 engine
.wait_operation_ready("nsi", nsilcmop_id1
, timeout_deploy
)
2133 ns_data
= {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2134 r
= self
.create_slice(engine
, ns_data
, "Create NSI-2 step 1")
2137 self
.nsi_id2
= engine
.last_id
2140 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id2
, "Instantiate NSI-2 step 2")
2141 nsilcmop_id2
= engine
.last_id
2144 engine
.wait_operation_ready("nsi", nsilcmop_id2
, timeout_deploy
)
2147 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2150 self
.terminate_slice(engine
, self
.nsi_id1
, "Terminate NSI-1")
2151 nsilcmop1_id
= engine
.last_id
2153 # Wait terminate NSI-1
2154 engine
.wait_operation_ready("nsi", nsilcmop1_id
, timeout_deploy
)
2157 self
.delete_slice(engine
, self
.nsi_id1
, "Delete NS")
2160 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2163 ns_data
= {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2164 r
= self
.create_slice(engine
, ns_data
, "Create NSI-3 step 1")
2168 self
.nsi_id3
= engine
.last_id
2171 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id3
, "Instantiate NSI-3 step 2")
2172 nsilcmop_id3
= engine
.last_id
2174 # Wait Instantiate NSI-3
2175 engine
.wait_operation_ready("nsi", nsilcmop_id3
, timeout_deploy
)
2178 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2181 self
.terminate_slice(engine
, self
.nsi_id2
, "Terminate NSI-2")
2182 nsilcmop2_id
= engine
.last_id
2184 # Wait terminate NSI-2
2185 engine
.wait_operation_ready("nsi", nsilcmop2_id
, timeout_deploy
)
2188 self
.delete_slice(engine
, self
.nsi_id2
, "DELETE NSI-2")
2191 self
. terminate_slice(engine
, self
.nsi_id3
, "Terminate NSI-3")
2192 nsilcmop3_id
= engine
.last_id
2194 # Wait terminate NSI-3
2195 engine
.wait_operation_ready("nsi", nsilcmop3_id
, timeout_deploy
)
2198 self
.delete_slice(engine
, self
.nsi_id3
, "DELETE NSI-3")
2201 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2204 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2208 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2209 headers_json
, None, 204, None, 0)
2211 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2215 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2216 headers_yaml
, None, 204, None, 0)
2218 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2219 headers_yaml
, None, 204, None, 0)
2222 if __name__
== "__main__":
2226 # Disable warnings from self-signed certificates.
2227 requests
.packages
.urllib3
.disable_warnings()
2229 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
2230 logger
= logging
.getLogger('NBI')
2231 # load parameters and configuration
2232 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
2233 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2234 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2235 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2236 url
= "https://localhost:9999/osm"
2237 user
= password
= project
= "admin"
2239 manual_check
= False
2244 "NonAuthorized": TestNonAuthorized
,
2245 "FakeVIM": TestFakeVim
,
2246 "TestUsersProjects": TestUsersProjects
,
2247 "VIM-SDN": TestVIMSDN
,
2248 "Deploy-Custom": TestDeploy
,
2249 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
2250 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
2251 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
2252 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2
,
2253 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3
,
2254 "Deploy-Hackfest-4": TestDeployHackfest4
,
2255 "Deploy-CirrosMacIp": TestDeployIpMac
,
2256 "TestDescriptors": TestDescriptors
,
2257 "TestDeployHackfest1": TestDeployHackfest1
,
2258 # "Deploy-MultiVIM": TestDeployMultiVIM,
2259 "DeploySingleVdu": TestDeploySingleVdu
,
2260 "DeployHnfd": TestDeployHnfd
,
2261 "Upload-Slice-Template": TestNetSliceTemplates
,
2262 "Deploy-Slice-Instance": TestNetSliceInstances
,
2263 "TestDeploySimpleCharm": TestDeploySimpleCharm
,
2264 "TestDeploySimpleCharm2": TestDeploySimpleCharm2
,
2270 # print("parameter:", o, a)
2271 if o
== "--version":
2272 print("test version " + __version__
+ ' ' + version_date
)
2275 for test
, test_class
in sorted(test_classes
.items()):
2276 print("{:32} {}".format(test
+ ":", test_class
.description
))
2278 elif o
in ("-v", "--verbose"):
2280 elif o
== "no-verbose":
2282 elif o
in ("-h", "--help"):
2285 elif o
== "--test-osm":
2287 elif o
== "--manual-check":
2291 elif o
in ("-u", "--user"):
2293 elif o
in ("-p", "--password"):
2295 elif o
== "--project":
2297 elif o
== "--fail-fast":
2300 # print("asdfadf", o, a, a.split(","))
2301 for _test
in a
.split(","):
2302 if _test
not in test_classes
:
2303 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
2306 test_to_do
.append(_test
)
2307 elif o
== "--params":
2308 param_key
, _
, param_value
= a
.partition("=")
2309 text_index
= len(test_to_do
)
2310 if text_index
not in test_params
:
2311 test_params
[text_index
] = {}
2312 test_params
[text_index
][param_key
] = param_value
2313 elif o
== "--insecure":
2315 elif o
== "--timeout":
2317 elif o
== "--timeout-deploy":
2318 timeout_deploy
= int(a
)
2319 elif o
== "--timeout-configure":
2320 timeout_configure
= int(a
)
2322 assert False, "Unhandled option"
2324 logger
.setLevel(logging
.WARNING
)
2326 logger
.setLevel(logging
.DEBUG
)
2328 logger
.setLevel(logging
.ERROR
)
2330 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
2331 # print("tests to do:", test_to_do)
2334 for test
in test_to_do
:
2335 if fail_fast
and test_rest
.failed_tests
:
2338 test_class
= test_classes
[test
]
2339 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
2341 for test
, test_class
in test_classes
.items():
2342 if fail_fast
and test_rest
.failed_tests
:
2344 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
2345 test_rest
.print_results()
2346 exit(1 if test_rest
.failed_tests
else 0)
2348 except TestException
as e
:
2349 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
2351 except getopt
.GetoptError
as e
:
2353 print(e
, file=sys
.stderr
)
2355 except Exception as e
:
2356 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)