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, 202), {"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}, (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
)
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 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,
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")
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")
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")
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")
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,
593 engine
.test("Delete Project P2 by ID", "DELETE", "/admin/v1/projects/"+pid2
, headers_json
, None, 204, None,
595 # END New Tests - Addressing Projects/Users by Name
596 engine
.remove_authorization() # To finish
599 class TestProjectsDescriptors
:
600 description
= "test descriptors visibility among projects"
603 def run(engine
, test_osm
, manual_check
, test_params
=None):
605 engine
.set_test_name("ProjectDescriptors")
606 engine
.get_autorization()
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")
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")
622 engine
.test("Create user U1", "POST", "/admin/v1/users", headers_json
,
623 {"username": "U1", "password": "pw1",
624 "project_role_mappings": [{"project": "Padmin", "role": "system_admin"},
625 {"project": "P2", "role": "project_admin"},
626 {"project": "P3", "role": "project_admin"}],
627 }, 201, {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
629 engine
.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml
,
630 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
631 vnfd_ids
.append(engine
.last_id
)
632 engine
.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
633 headers_yaml
, TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
634 vnfd_ids
.append(engine
.last_id
)
635 engine
.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml
,
636 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
637 vnfd_ids
.append(engine
.last_id
)
639 res
= engine
.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
640 headers_json
, None, 200, r_header_json
, "json")
641 response
= res
.json()
642 if len(response
) != 3:
643 logger
.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response
)))
644 engine
.failed_tests
+= 1
646 # Change to other project Padmin
647 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
648 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
649 r_header_json
, "json")
651 response
= res
.json()
652 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
655 res
= engine
.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
656 headers_json
, None, 200, r_header_json
, "json")
657 response
= res
.json()
658 if len(response
) != 0:
659 logger
.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response
)))
660 engine
.failed_tests
+= 1
663 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
664 headers_json
, None, 200, r_header_json
, "json")
665 response
= res
.json()
666 if len(response
) != 1:
667 logger
.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response
)))
668 engine
.failed_tests
+= 1
670 # list vnfds belonging to project "admin"
671 res
= engine
.test("List VNFD of admin project", "GET",
672 "/vnfpkgm/v1/vnf_packages?ADMIN={}".format(project_admin_id
),
673 headers_json
, None, 200, r_header_json
, "json")
674 response
= res
.json()
675 if len(response
) != 3:
676 logger
.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response
)))
677 engine
.failed_tests
+= 1
680 engine
.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
681 headers_json
, None, 200, r_header_json
, "json")
682 # Edit not owned vnfd
683 engine
.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
684 headers_yaml
, '{name: pepe}', 404, r_header_yaml
, "yaml")
687 engine
.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
688 format(vnfd_ids
[1]), headers_json
, None, 204, None, 0)
691 engine
.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml
,
692 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
693 vnfd_ids
.append(engine
.last_id
)
696 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
697 headers_json
, None, 200, r_header_json
, "json")
698 response
= res
.json()
699 if len(response
) != 2:
700 logger
.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response
)))
701 engine
.failed_tests
+= 1
704 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
706 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
707 headers_yaml
, None, 204, None, 0)
709 # change to admin project
710 engine
.remove_authorization() # To force get authorization
711 engine
.get_autorization()
712 test_rest
.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
713 headers_yaml
, None, 204, None, 0)
714 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
715 headers_yaml
, None, 204, None, 0)
716 test_rest
.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[2]),
717 headers_yaml
, None, 204, None, 0)
718 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[3]),
719 headers_yaml
, None, 404, r_header_yaml
, "yaml")
720 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
721 headers_yaml
, None, 204, None, 0)
723 engine
.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[0]),
724 headers_json
, None, 404, r_header_json
, "json")
725 engine
.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[1]),
726 headers_json
, None, 404, r_header_json
, "json")
727 engine
.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[2]),
728 headers_json
, None, 404, r_header_json
, "json")
729 engine
.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
730 headers_json
, None, 404, r_header_json
, "json")
732 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
733 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204, None, None)
734 engine
.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 204, None, None)
735 engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json
, None, 204, None, None)
739 description
= "Creates/edit/delete fake VIMs and SDN controllers"
743 "schema_version": "1.0",
744 "schema_type": "No idea",
746 "description": "Descriptor name",
747 "vim_type": "openstack",
748 "vim_url": "http://localhost:/vim",
749 "vim_tenant_name": "vimTenant",
751 "vim_password": "password",
752 "config": {"config_param": 1}
756 "description": "sdn-description",
757 "dpid": "50:50:52:54:00:94:21:21",
758 "ip": "192.168.15.17",
760 "type": "opendaylight",
765 self
.port_mapping
= [
766 {"compute_node": "compute node 1",
767 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
768 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
770 {"compute_node": "compute node 2",
771 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
772 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
776 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
778 vim_bad
= self
.vim
.copy()
781 engine
.set_test_name("FakeVim")
782 engine
.get_autorization()
783 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 202),
784 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
785 vim_id
= engine
.last_id
786 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
787 vim_bad
, 422, None, headers_json
)
788 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
789 409, None, headers_json
)
790 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
792 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
793 r_header_yaml
, "yaml")
796 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
798 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
799 404, r_header_yaml
, "yaml")
801 # delete and wait until is really deleted
802 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
804 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
807 class TestVIMSDN(TestFakeVim
):
808 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
811 TestFakeVim
.__init
__(self
)
813 "schema_version": "1.0",
814 "schema_type": "No idea",
816 "description": "Descriptor name",
818 "wim_url": "http://localhost:/wim",
820 "password": "password",
821 "config": {"config_param": 1}
824 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
825 engine
.set_test_name("VimSdn")
826 engine
.get_autorization()
828 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 202),
829 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
830 sdnc_id
= engine
.last_id
833 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
834 (202, 204), None, None)
837 self
.vim
["config"]["sdn-controller"] = sdnc_id
838 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
839 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 202, 201),
840 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
842 vim_id
= engine
.last_id
843 self
.port_mapping
[0]["compute_node"] = "compute node XX"
844 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
845 {"config": {"sdn-port-mapping": self
.port_mapping
}}, (202, 204), None, None)
846 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
847 {"config": {"sdn-port-mapping": None}}, (202, 204), None, None)
849 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 202, 201),
850 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
851 wim_id
= engine
.last_id
855 engine
.test("Delete VIM remove port-mapping", "DELETE",
856 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
857 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
860 engine
.test("Delete WIM", "DELETE",
861 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
862 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
863 None, 404, r_header_yaml
, "yaml")
864 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
865 404, r_header_yaml
, "yaml")
866 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
867 None, 404, r_header_yaml
, "yaml")
870 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
871 # delete and wait until is really deleted
872 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
873 headers_json
, None, (202, 201, 204), None, 0)
874 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
875 (202, 201, 204), None, 0)
876 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
877 headers_json
, None, (202, 201, 204), None, 0)
878 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
879 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
880 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
884 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
887 self
.test_name
= "DEPLOY"
892 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
893 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
894 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
895 self
.descriptor_edit
= None
896 self
.uses_configuration
= False
903 self
.ns_params
= None
904 self
.vnfr_ip_list
= {}
906 def create_descriptors(self
, engine
):
907 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
908 if not os
.path
.exists(temp_dir
):
909 os
.makedirs(temp_dir
)
910 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
911 if "/" in vnfd_filename
:
912 vnfd_filename_path
= vnfd_filename
913 if not os
.path
.exists(vnfd_filename_path
):
914 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
916 vnfd_filename_path
= temp_dir
+ vnfd_filename
917 if not os
.path
.exists(vnfd_filename_path
):
918 with
open(vnfd_filename_path
, "wb") as file:
919 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
920 if response
.status_code
>= 300:
921 raise TestException("Error downloading descriptor from '{}': {}".format(
922 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
923 file.write(response
.content
)
924 if vnfd_filename_path
.endswith(".yaml"):
925 headers
= headers_yaml
927 headers
= headers_zip_yaml
928 if randint(0, 1) == 0:
929 # vnfd CREATE AND UPLOAD in one step:
930 engine
.test("Onboard VNFD in one step", "POST",
931 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
932 r_headers_yaml_location_vnfd
,
934 self
.vnfds_id
.append(engine
.last_id
)
936 # vnfd CREATE AND UPLOAD ZIP
937 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
938 headers_json
, None, 201,
939 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
940 self
.vnfds_id
.append(engine
.last_id
)
941 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
942 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
943 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
945 if self
.descriptor_edit
:
946 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
948 engine
.test("Edit VNFD ", "PATCH",
949 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
950 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
952 if "/" in self
.nsd_filename
:
953 nsd_filename_path
= self
.nsd_filename
954 if not os
.path
.exists(nsd_filename_path
):
955 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
957 nsd_filename_path
= temp_dir
+ self
.nsd_filename
958 if not os
.path
.exists(nsd_filename_path
):
959 with
open(nsd_filename_path
, "wb") as file:
960 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
961 if response
.status_code
>= 300:
962 raise TestException("Error downloading descriptor from '{}': {}".format(
963 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
964 file.write(response
.content
)
965 if nsd_filename_path
.endswith(".yaml"):
966 headers
= headers_yaml
968 headers
= headers_zip_yaml
970 if randint(0, 1) == 0:
971 # nsd CREATE AND UPLOAD in one step:
972 engine
.test("Onboard NSD in one step", "POST",
973 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
974 r_headers_yaml_location_nsd
, yaml
)
975 self
.nsd_id
= engine
.last_id
977 # nsd CREATE AND UPLOAD ZIP
978 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
979 headers_json
, None, 201,
980 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
981 self
.nsd_id
= engine
.last_id
982 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
983 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
984 headers
, "@b" + nsd_filename_path
, 204, None, 0)
986 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
988 engine
.test("Edit NSD ", "PATCH",
989 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
990 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
992 def delete_descriptors(self
, engine
):
994 engine
.test("Delete NSSD SOL005", "DELETE",
995 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
996 headers_yaml
, None, 204, None, 0)
997 for vnfd_id
in self
.vnfds_id
:
998 engine
.test("Delete VNFD SOL005", "DELETE",
999 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
1001 def instantiate(self
, engine
, ns_data
):
1002 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
1003 # create NS Two steps
1004 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
1005 headers_yaml
, ns_data_text
, (201, 202),
1006 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
1009 self
.ns_id
= engine
.last_id
1010 engine
.test("Instantiate NS step 2", "POST",
1011 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
1012 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1013 nslcmop_id
= engine
.last_id
1016 # Wait until status is Ok
1017 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
1018 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
1020 def terminate(self
, engine
):
1023 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
1024 None, (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1025 nslcmop2_id
= engine
.last_id
1026 # Wait until status is Ok
1027 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
1029 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1032 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1033 headers_yaml
, None, 204, None, 0)
1035 # check all it is deleted
1036 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1038 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1039 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1044 if not isinstance(nslcmops
, list) or nslcmops
:
1045 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1047 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
1049 r
= engine
.test("GET VNFR IDs", "GET",
1050 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
1051 200, r_header_json
, "json")
1056 vnfr_list
= ns_data
['constituent-vnfr-ref']
1058 _commands
= commands
if commands
is not None else self
.commands
1059 _users
= users
if users
is not None else self
.users
1060 _passwds
= passwds
if passwds
is not None else self
.passwords
1061 _keys
= keys
if keys
is not None else self
.keys
1062 _timeout
= timeout
if timeout
!= 0 else self
.timeout
1064 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1065 for vnfr_id
in vnfr_list
:
1066 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1067 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
1068 200, r_header_json
, "json")
1071 vnfr_data
= r
.json()
1073 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
1075 ip_address
= self
.get_vnfr_ip(engine
, vnf_index
)
1076 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
1079 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
1080 logger
.warning(test_description
)
1081 while _timeout
>= time
:
1082 result
, message
= self
.do_checks([ip_address
],
1083 vnf_index
=vnfr_data
["member-vnf-index-ref"],
1084 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
1085 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
1087 engine
.passed_tests
+= 1
1088 logger
.debug(message
)
1094 engine
.failed_tests
+= 1
1095 logger
.error(message
)
1099 engine
.failed_tests
+= 1
1100 logger
.error(message
)
1102 engine
.failed_tests
+= 1
1103 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnf_index
))
1105 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
1108 from pssh
.clients
import ParallelSSHClient
1109 from pssh
.utils
import load_private_key
1110 from ssh2
import exceptions
as ssh2Exception
1111 except ImportError as e
:
1112 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1113 "parallel-ssh urllib3': {}".format(e
))
1114 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1115 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
1117 p_host
= os
.environ
.get("PROXY_HOST")
1118 p_user
= os
.environ
.get("PROXY_USER")
1119 p_password
= os
.environ
.get("PROXY_PASSWD")
1122 pkey
= load_private_key(key
)
1126 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
1127 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
1128 for cmd
in commands
:
1129 output
= client
.run_command(cmd
)
1131 if output
[ip
[0]].exit_code
:
1132 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
1133 "\n".join(output
[ip
[0]].stderr
))
1135 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
1136 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
1137 ssh2Exception
.SocketRecvError
) as e
:
1138 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
1139 except Exception as e
:
1140 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
1142 def additional_operations(self
, engine
, test_osm
, manual_check
):
1145 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1146 engine
.set_test_name(self
.test_name
)
1147 engine
.get_autorization()
1148 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1150 if "vnfd-files" in test_params
:
1151 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
1152 if "nsd-file" in test_params
:
1153 self
.nsd_filename
= test_params
["nsd-file"]
1154 if test_params
.get("ns-name"):
1155 nsname
= test_params
["ns-name"]
1156 self
.create_descriptors(engine
)
1158 # create real VIM if not exist
1159 self
.vim_id
= engine
.get_create_vim(test_osm
)
1160 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1161 "vimAccountId": self
.vim_id
}
1163 ns_data
.update(self
.ns_params
)
1164 if test_params
and test_params
.get("ns-config"):
1165 if isinstance(test_params
["ns-config"], str):
1166 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1168 ns_data
.update(test_params
["ns-config"])
1169 self
.instantiate(engine
, ns_data
)
1172 input('NS has been deployed. Perform manual check and press enter to resume')
1173 if test_osm
and self
.commands
:
1174 self
.test_ns(engine
, test_osm
)
1175 self
.additional_operations(engine
, test_osm
, manual_check
)
1176 self
.terminate(engine
)
1177 self
.delete_descriptors(engine
)
1179 def get_first_ip(self
, ip_string
):
1180 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1181 first_ip
= ip_string
.split(";")[0] if ip_string
else ""
1184 def get_vnfr_ip(self
, engine
, vnfr_index_wanted
):
1185 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1186 ip
= self
.vnfr_ip_list
.get(vnfr_index_wanted
, "")
1188 return self
.get_first_ip(ip
)
1189 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1190 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1191 vnfr_index_wanted
, self
.ns_id
), headers_json
, None,
1192 200, r_header_json
, "json")
1195 vnfr_data
= r
.json()
1196 if not (vnfr_data
and vnfr_data
[0]):
1198 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1199 ip_list
= vnfr_data
[0].get("ip-address", "")
1201 self
.vnfr_ip_list
[vnfr_index_wanted
] = ip_list
1202 ip
= self
.get_first_ip(ip_list
)
1206 class TestDeployHackfestCirros(TestDeploy
):
1207 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
1211 self
.test_name
= "CIRROS"
1212 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1213 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1214 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1215 self
.users
= {'1': "cirros", '2': "cirros"}
1216 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1218 def terminate(self
, engine
):
1219 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1221 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1222 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1224 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1226 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1227 headers_yaml
, None, 204, None, 0)
1229 # check all it is deleted
1230 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1232 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1233 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1238 if not isinstance(nslcmops
, list) or nslcmops
:
1239 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1242 class TestDeployHackfest1(TestDeploy
):
1243 description
= "Load and deploy Hackfest_1_vnfd example"
1247 self
.test_name
= "HACKFEST1-"
1248 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1249 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1250 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1251 # self.users = {'1': "cirros", '2': "cirros"}
1252 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1255 class TestDeployHackfestCirrosScaling(TestDeploy
):
1256 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1260 self
.test_name
= "CIRROS-SCALE"
1261 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1262 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1263 # Modify VNFD to add scaling and count=2
1264 self
.descriptor_edit
= {
1267 "$id: 'cirros_vnfd-VM'": {"count": 2}
1269 "scaling-group-descriptor": [{
1270 "name": "scale_cirros",
1271 "max-instance-count": 2,
1273 "vdu-id-ref": "cirros_vnfd-VM",
1280 def additional_operations(self
, engine
, test_osm
, manual_check
):
1283 # 2 perform scale out twice
1284 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1285 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1286 for i
in range(0, 2):
1287 engine
.test("Execute scale action over NS", "POST",
1288 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1289 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1290 nslcmop2_scale_out
= engine
.last_id
1291 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1293 input('NS scale out done. Check that two more vdus are there')
1294 # TODO check automatic
1296 # 2 perform scale in
1297 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1298 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1299 for i
in range(0, 2):
1300 engine
.test("Execute scale IN action over NS", "POST",
1301 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1302 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1303 nslcmop2_scale_in
= engine
.last_id
1304 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1306 input('NS scale in done. Check that two less vdus are there')
1307 # TODO check automatic
1309 # perform scale in that must fail as reached limit
1310 engine
.test("Execute scale IN out of limit action over NS", "POST",
1311 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1312 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1313 nslcmop2_scale_in
= engine
.last_id
1314 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1317 class TestDeployIpMac(TestDeploy
):
1318 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1322 self
.test_name
= "SetIpMac"
1323 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1324 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1325 self
.descriptor_url
= \
1326 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1327 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1328 self
.users
= {'1': "osm", '2': "osm"}
1329 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1332 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1333 # super().run(engine, test_osm, manual_check, test_params)
1334 # run again setting IPs with instantiate parameters
1335 instantiation_params
= {
1338 "member-vnf-index": "1",
1341 "name": "internal_vld1", # net_internal
1343 "ip-version": "ipv4",
1344 "subnet-address": "10.9.8.0/24",
1345 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1347 "internal-connection-point": [
1350 "ip-address": "10.9.8.2",
1354 "ip-address": "10.9.8.3",
1365 # "name": "iface11",
1366 # "floating-ip-required": True,
1370 "mac-address": "52:33:44:55:66:13"
1379 "ip-address": "10.31.31.22",
1380 "mac-address": "52:33:44:55:66:21"
1389 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1392 class TestDeployHackfest4(TestDeploy
):
1393 description
= "Load and deploy Hackfest 4 example."
1397 self
.test_name
= "HACKFEST4-"
1398 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1399 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1400 self
.uses_configuration
= True
1401 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1402 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1403 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1404 # Modify VNFD to add scaling
1405 # self.descriptor_edit = {
1407 # 'vnf-configuration': {
1408 # 'config-primitive': [{
1411 # 'name': 'filename',
1412 # 'data-type': 'STRING',
1413 # 'default-value': '/home/ubuntu/touched'
1417 # 'scaling-group-descriptor': [{
1418 # 'name': 'scale_dataVM',
1419 # 'scaling-policy': [{
1420 # 'threshold-time': 0,
1421 # 'name': 'auto_cpu_util_above_threshold',
1422 # 'scaling-type': 'automatic',
1423 # 'scaling-criteria': [{
1424 # 'name': 'cpu_util_above_threshold',
1425 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1426 # 'scale-out-relational-operation': 'GE',
1427 # 'scale-in-threshold': 15,
1428 # 'scale-out-threshold': 60,
1429 # 'scale-in-relational-operation': 'LE'
1431 # 'cooldown-time': 60
1433 # 'max-instance-count': 10,
1434 # 'scaling-config-action': [
1435 # {'vnf-config-primitive-name-ref': 'touch',
1436 # 'trigger': 'post-scale-out'},
1437 # {'vnf-config-primitive-name-ref': 'touch',
1438 # 'trigger': 'pre-scale-in'}
1441 # 'vdu-id-ref': 'dataVM',
1449 class TestDeployHackfest3Charmed(TestDeploy
):
1450 description
= "Load and deploy Hackfest 3charmed_ns example"
1454 self
.test_name
= "HACKFEST3-"
1455 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1456 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1457 self
.uses_configuration
= True
1458 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1459 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1460 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1461 self
.descriptor_edit
= {
1462 "vnfd0": yaml
.safe_load(
1465 terminate-config-primitive:
1470 value: '/home/ubuntu/last-touch1'
1475 value: '/home/ubuntu/last-touch3'
1480 value: '/home/ubuntu/last-touch2'
1484 def additional_operations(self
, engine
, test_osm
, manual_check
):
1488 vnfr_index_selected
= "2"
1489 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1490 engine
.test("Exec service primitive over NS", "POST",
1491 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1492 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1493 nslcmop2_action
= engine
.last_id
1494 # Wait until status is Ok
1495 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1496 vnfr_ip
= self
.get_vnfr_ip(engine
, vnfr_index_selected
)
1499 "NS service primitive has been executed."
1500 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1503 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1504 self
.test_ns(engine
, test_osm
, commands
=commands
)
1506 # # 2 perform scale out
1507 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, 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, 202), r_headers_yaml_location_nslcmop, "yaml")
1512 # nslcmop2_scale_out = engine.last_id
1513 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1515 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1516 # # TODO check automatic
1518 # # 2 perform scale in
1519 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1520 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1521 # engine.test("Execute scale action over NS", "POST",
1522 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1523 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1524 # nslcmop2_scale_in = engine.last_id
1525 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1527 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1528 # # TODO check automatic
1531 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1532 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1533 "ids and member-vnf-index."
1537 self
.test_name
= "HACKFEST3v2-"
1538 self
.qforce
= "?FORCE=True"
1539 self
.descriptor_edit
= {
1543 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1547 "vnf-configuration": None,
1548 "connection-point": {
1552 "short-name": "pdu-mgmt"
1556 "mgmt-interface": {"cp": "pdu-mgmt"},
1557 "description": "A vnf single vdu to be used as PDU",
1561 "id": "pdu_internal",
1562 "name": "pdu_internal",
1563 "internal-connection-point": {"$[1]": None},
1564 "short-name": "pdu_internal",
1570 # Modify NSD accordingly
1572 "constituent-vnfd": {
1573 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1576 "description": "A nsd to deploy the vnf to act as as PDU",
1578 "name": "nsd-as-pdu",
1579 "short-name": "nsd-as-pdu",
1584 "short-name": "mgmt_pdu",
1585 "vnfd-connection-point-ref": {
1587 "vnfd-connection-point-ref": "pdu-mgmt",
1588 "vnfd-id-ref": "vdu-as-pdu",
1600 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1601 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1605 self
.test_name
= "HACKFEST3v3-"
1606 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1607 self
.descriptor_edit
= {
1610 scaling-group-descriptor:
1611 - name: "scale_dataVM"
1612 max-instance-count: 10
1614 - name: "auto_cpu_util_above_threshold"
1615 scaling-type: "automatic"
1619 - name: "cpu_util_above_threshold"
1620 scale-in-threshold: 15
1621 scale-in-relational-operation: "LE"
1622 scale-out-threshold: 60
1623 scale-out-relational-operation: "GE"
1624 vnf-monitoring-param-ref: "monitor1"
1626 - vdu-id-ref: dataVM
1628 scaling-config-action:
1629 - trigger: post-scale-out
1630 vnf-config-primitive-name-ref: touch
1631 - trigger: pre-scale-in
1632 vnf-config-primitive-name-ref: touch
1636 - id: "dataVM_cpu_util"
1637 nfvi-metric: "cpu_utilization"
1642 aggregation-type: AVERAGE
1643 vdu-monitoring-param:
1645 vdu-monitoring-param-ref: "dataVM_cpu_util"
1647 initial-config-primitive:
1651 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1656 default-value: "<touch_filename2>"
1660 "additionalParamsForVnf": [
1661 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1662 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1663 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1664 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1668 def additional_operations(self
, engine
, test_osm
, manual_check
):
1669 super().additional_operations(engine
, test_osm
, manual_check
)
1673 # 2 perform scale out
1674 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1675 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1676 engine
.test("Execute scale action over NS", "POST",
1677 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1678 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1679 nslcmop2_scale_out
= engine
.last_id
1680 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1682 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1684 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1685 self
.test_ns(engine
, test_osm
, commands
=commands
)
1686 # TODO check automatic connection to scaled VM
1688 # 2 perform scale in
1689 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1690 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1691 engine
.test("Execute scale action over NS", "POST",
1692 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1693 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1694 nslcmop2_scale_in
= engine
.last_id
1695 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1697 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1698 # TODO check automatic
1701 class TestDeploySimpleCharm(TestDeploy
):
1702 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1706 self
.test_name
= "HACKFEST-SIMPLE"
1707 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1708 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1709 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1710 self
.uses_configuration
= True
1711 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1712 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1713 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1716 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1717 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1722 self
.test_name
= "HACKFEST-SIMPLE2-"
1723 self
.qforce
= "?FORCE=True"
1724 self
.descriptor_edit
= {
1726 "id": "hackfest.simplecharm.vnf"
1730 "id": "hackfest.simplecharm.ns",
1731 "constituent-vnfd": {
1732 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1733 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1737 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1738 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1739 "$[1]": {"member-vnf-index-ref": "$2",
1740 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1743 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1744 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1745 "$[1]": {"member-vnf-index-ref": "$2",
1746 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1753 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1754 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1758 self
.test_name
= "SingleVDU"
1759 self
.qforce
= "?FORCE=True"
1760 self
.descriptor_edit
= {
1761 # Modify VNFD to remove one VDU
1765 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1769 "vnf-configuration": None,
1770 "connection-point": {
1774 "short-name": "pdu-mgmt"
1778 "mgmt-interface": {"cp": "pdu-mgmt"},
1779 "description": "A vnf single vdu to be used as PDU",
1783 "id": "pdu_internal",
1784 "name": "pdu_internal",
1785 "internal-connection-point": {"$[1]": None},
1786 "short-name": "pdu_internal",
1792 # Modify NSD accordingly
1794 "constituent-vnfd": {
1795 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1798 "description": "A nsd to deploy the vnf to act as as PDU",
1800 "name": "nsd-as-pdu",
1801 "short-name": "nsd-as-pdu",
1806 "short-name": "mgmt_pdu",
1807 "vnfd-connection-point-ref": {
1809 "vnfd-connection-point-ref": "pdu-mgmt",
1810 "vnfd-id-ref": "vdu-as-pdu",
1822 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1823 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1827 self
.test_name
= "HNFD"
1828 self
.pduDeploy
= TestDeploySingleVdu()
1829 self
.pdu_interface_0
= {}
1830 self
.pdu_interface_1
= {}
1833 # self.vnf_to_pdu = """
1836 # pdu-type: PDU-TYPE-1
1841 # name: pdu-iface-internal
1843 # description: HFND, one PDU + One VDU
1849 self
.pdu_descriptor
= {
1851 "type": "PDU-TYPE-1",
1852 "vim_accounts": "to-override",
1855 "name": "mgmt-iface",
1858 "ip-address": "to override",
1859 "mac-address": "mac_address",
1860 "vim-network-name": "mgmt",
1863 "name": "pdu-iface-internal",
1866 "ip-address": "to override",
1867 "mac-address": "mac_address",
1868 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1872 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1874 self
.descriptor_edit
= {
1878 "short-name": "hfn1",
1881 "pdu-type": "PDU-TYPE-1",
1883 "$[0]": {"name": "mgmt-iface"},
1884 "$[1]": {"name": "pdu-iface-internal"},
1890 "constituent-vnfd": {
1891 "$[1]": {"vnfd-id-ref": "hfnd1"}
1894 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1895 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1900 def create_descriptors(self
, engine
):
1901 super().create_descriptors(engine
)
1904 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1905 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1906 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1907 # TODO get vim-network-name from vnfr.vld.name
1908 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1909 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1910 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1911 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1912 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1913 201, r_header_yaml
, "yaml")
1914 self
.pdu_id
= engine
.last_id
1916 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1917 engine
.get_autorization()
1918 engine
.set_test_name(self
.test_name
)
1919 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1921 # create real VIM if not exist
1922 self
.vim_id
= engine
.get_create_vim(test_osm
)
1924 self
.pduDeploy
.create_descriptors(engine
)
1925 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1926 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1928 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1930 self
.pduDeploy
.test_ns(engine
, test_osm
)
1933 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1934 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1935 200, r_header_json
, "json")
1938 vnfr_data
= r
.json()
1941 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1942 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1943 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1944 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1945 if not self
.pdu_interface_0
["ip-address"]:
1946 raise TestException("Vnfr has not managment ip address")
1948 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1949 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1950 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1951 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1953 self
.create_descriptors(engine
)
1955 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1956 "vimAccountId": self
.vim_id
}
1957 if test_params
and test_params
.get("ns-config"):
1958 if isinstance(test_params
["ns-config"], str):
1959 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1961 ns_data
.update(test_params
["ns-config"])
1963 self
.instantiate(engine
, ns_data
)
1965 input('NS has been deployed. Perform manual check and press enter to resume')
1967 self
.test_ns(engine
, test_osm
)
1968 self
.additional_operations(engine
, test_osm
, manual_check
)
1969 self
.terminate(engine
)
1970 self
.pduDeploy
.terminate(engine
)
1971 self
.delete_descriptors(engine
)
1972 self
.pduDeploy
.delete_descriptors(engine
)
1974 def delete_descriptors(self
, engine
):
1975 super().delete_descriptors(engine
)
1977 engine
.test("Delete PDU SOL005", "DELETE",
1978 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1979 headers_yaml
, None, 204, None, 0)
1982 class TestDescriptors
:
1983 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1984 vnfd_empty
= """vnfd:vnfd-catalog:
1990 vnfd_prova
= """vnfd:vnfd-catalog:
2002 - external-connection-point-ref: cp_0h8m
2011 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
2012 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
2013 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2017 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2018 engine
.set_test_name("Descriptors")
2019 engine
.get_autorization()
2020 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2021 if not os
.path
.exists(temp_dir
):
2022 os
.makedirs(temp_dir
)
2025 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
2026 filename_path
= temp_dir
+ filename
2027 if not os
.path
.exists(filename_path
):
2028 with
open(filename_path
, "wb") as file:
2029 response
= requests
.get(self
.descriptor_url
+ filename
)
2030 if response
.status_code
>= 300:
2031 raise TestException("Error downloading descriptor from '{}': {}".format(
2032 self
.descriptor_url
+ filename
, response
.status_code
))
2033 file.write(response
.content
)
2035 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
2036 nsd_filename_path
= temp_dir
+ self
.nsd_filename
2038 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2039 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
2040 self
.vnfd_id
= engine
.last_id
2043 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2044 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2046 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
2047 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
2048 "@b" + vnfd_filename_path
, 204, None, 0)
2050 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2051 "vdu.0.interface.1.internal-connection-point-ref=internal",
2052 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2053 # Detection of duplicated VLD names in VNF Descriptors
2054 # URL: internal-vld=[
2055 # {id: internal1, name: internal, type:ELAN,
2056 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2057 # {id: internal2, name: internal, type:ELAN,
2058 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2060 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2061 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2062 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2063 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2064 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2066 for query
in queries
:
2067 engine
.test("Upload invalid VNFD ", "PUT",
2068 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
2069 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
2072 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2073 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2075 # get vnfd descriptor
2076 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2077 headers_yaml
, None, 200, r_header_yaml
, "yaml")
2079 # get vnfd file descriptor
2080 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
2081 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
2082 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2084 # get vnfd zip file package
2085 engine
.test("Get VNFD zip package", "GET",
2086 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
2087 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
2088 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2091 engine
.test("Get VNFD artifact package", "GET",
2092 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
2093 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
2094 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2096 # nsd CREATE AND UPLOAD in one step:
2097 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
2098 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
2099 self
.nsd_id
= engine
.last_id
2101 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2102 for query
in queries
:
2103 engine
.test("Upload invalid NSD ", "PUT",
2104 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
2105 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
2107 # get nsd descriptor
2108 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
2109 None, 200, r_header_yaml
, "yaml")
2111 # get nsd file descriptor
2112 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
2113 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
2114 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2116 # get nsd zip file package
2117 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
2118 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
2119 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2122 engine
.test("Get NSD artifact package", "GET",
2123 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
2124 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
2125 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2128 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2129 headers_yaml
, None, 409, None, None)
2131 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
2132 headers_yaml
, None, 204, None, 0)
2135 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
2139 class TestNetSliceTemplates
:
2140 description
= "Upload a NST to OSM"
2143 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2144 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2145 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2146 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2147 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2149 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2151 engine
.set_test_name("NST step ")
2152 engine
.get_autorization()
2153 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2154 if not os
.path
.exists(temp_dir
):
2155 os
.makedirs(temp_dir
)
2158 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2159 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2160 self
.vnfd_edge_id
= engine
.last_id
2162 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2163 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2164 self
.vnfd_middle_id
= engine
.last_id
2167 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2168 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2169 self
.nsd_edge_id
= engine
.last_id
2171 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2172 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2173 self
.nsd_middle_id
= engine
.last_id
2176 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2177 201, r_headers_yaml_location_nst
, "yaml")
2178 nst_id
= engine
.last_id
2180 # nstd SHOW OSM format
2181 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2182 200, r_header_json
, "json")
2185 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2189 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2190 headers_json
, None, 204, None, 0)
2192 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2196 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2197 headers_yaml
, None, 204, None, 0)
2199 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2200 headers_yaml
, None, 204, None, 0)
2203 class TestNetSliceInstances
:
2206 1. Populate databases with VNFD, NSD, NST with the following scenario
2207 +-----------------management-----------------+
2209 +--+---+ +----+----+ +---+--+
2211 | edge +---data1----+ middle +---data2-----+ edge |
2213 +------+ +---------+ +------+
2216 3. Instantiate NSI-1
2218 5. Instantiate NSI-2
2219 Manual check - Are 2 slices instantiated correctly?
2220 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2221 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2224 Manual check - Is slice NSI-1 deleted correctly?
2225 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2227 9. Instantiate NSI-3
2228 Manual check - Is slice NSI-3 instantiated correctly?
2229 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2234 Manual check - All cleaned correctly?
2235 NSI-2 and NSI-3 were terminated and deleted
2236 14. Cleanup database
2239 description
= "Upload a NST to OSM"
2243 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2244 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2245 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2246 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2247 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2249 def create_slice(self
, engine
, nsi_data
, name
):
2250 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2251 r
= engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances",
2252 headers_yaml
, ns_data_text
, (201, 202),
2253 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2256 def instantiate_slice(self
, engine
, nsi_data
, nsi_id
, name
):
2257 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2258 engine
.test(name
, "POST",
2259 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id
), headers_yaml
, ns_data_text
,
2260 (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2262 def terminate_slice(self
, engine
, nsi_id
, name
):
2263 engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id
),
2264 headers_yaml
, None, (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2266 def delete_slice(self
, engine
, nsi_id
, name
):
2267 engine
.test(name
, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id
), headers_yaml
, None,
2270 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2272 engine
.set_test_name("NSI")
2273 engine
.get_autorization()
2276 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2277 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2278 self
.vnfd_edge_id
= engine
.last_id
2280 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2281 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2282 self
.vnfd_middle_id
= engine
.last_id
2285 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2286 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2287 self
.nsd_edge_id
= engine
.last_id
2289 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2290 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2291 self
.nsd_middle_id
= engine
.last_id
2294 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2295 201, r_headers_yaml_location_nst
, "yaml")
2296 nst_id
= engine
.last_id
2298 self
.vim_id
= engine
.get_create_vim(test_osm
)
2301 ns_data
= {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2302 r
= self
.create_slice(engine
, ns_data
, "Create NSI-1 step 1")
2305 self
.nsi_id1
= engine
.last_id
2308 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id1
, "Instantiate NSI-1 step 2")
2309 nsilcmop_id1
= engine
.last_id
2313 engine
.wait_operation_ready("nsi", nsilcmop_id1
, timeout_deploy
)
2316 ns_data
= {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2317 r
= self
.create_slice(engine
, ns_data
, "Create NSI-2 step 1")
2320 self
.nsi_id2
= engine
.last_id
2323 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id2
, "Instantiate NSI-2 step 2")
2324 nsilcmop_id2
= engine
.last_id
2328 engine
.wait_operation_ready("nsi", nsilcmop_id2
, timeout_deploy
)
2331 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2335 self
.terminate_slice(engine
, self
.nsi_id1
, "Terminate NSI-1")
2336 nsilcmop1_id
= engine
.last_id
2338 # Wait terminate NSI-1
2339 engine
.wait_operation_ready("nsi", nsilcmop1_id
, timeout_deploy
)
2342 self
.delete_slice(engine
, self
.nsi_id1
, "Delete NS")
2345 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2348 ns_data
= {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2349 r
= self
.create_slice(engine
, ns_data
, "Create NSI-3 step 1")
2353 self
.nsi_id3
= engine
.last_id
2356 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id3
, "Instantiate NSI-3 step 2")
2357 nsilcmop_id3
= engine
.last_id
2359 # Wait Instantiate NSI-3
2361 engine
.wait_operation_ready("nsi", nsilcmop_id3
, timeout_deploy
)
2364 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2368 self
.terminate_slice(engine
, self
.nsi_id2
, "Terminate NSI-2")
2369 nsilcmop2_id
= engine
.last_id
2371 # Wait terminate NSI-2
2372 engine
.wait_operation_ready("nsi", nsilcmop2_id
, timeout_deploy
)
2375 self
.delete_slice(engine
, self
.nsi_id2
, "DELETE NSI-2")
2379 self
. terminate_slice(engine
, self
.nsi_id3
, "Terminate NSI-3")
2380 nsilcmop3_id
= engine
.last_id
2382 # Wait terminate NSI-3
2383 engine
.wait_operation_ready("nsi", nsilcmop3_id
, timeout_deploy
)
2386 self
.delete_slice(engine
, self
.nsi_id3
, "DELETE NSI-3")
2389 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2392 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2396 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2397 headers_json
, None, 204, None, 0)
2399 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2403 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2404 headers_yaml
, None, 204, None, 0)
2406 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2407 headers_yaml
, None, 204, None, 0)
2410 if __name__
== "__main__":
2414 # Disable warnings from self-signed certificates.
2415 requests
.packages
.urllib3
.disable_warnings()
2417 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
2418 logger
= logging
.getLogger('NBI')
2419 # load parameters and configuration
2420 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
2421 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2422 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2423 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2424 url
= "https://localhost:9999/osm"
2425 user
= password
= project
= "admin"
2427 manual_check
= False
2432 "NonAuthorized": TestNonAuthorized
,
2433 "FakeVIM": TestFakeVim
,
2434 "Users-Projects": TestUsersProjects
,
2435 "Projects-Descriptors": TestProjectsDescriptors
,
2436 "VIM-SDN": TestVIMSDN
,
2437 "Deploy-Custom": TestDeploy
,
2438 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
2439 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
2440 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
2441 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2
,
2442 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3
,
2443 "Deploy-Hackfest-4": TestDeployHackfest4
,
2444 "Deploy-CirrosMacIp": TestDeployIpMac
,
2445 "Descriptors": TestDescriptors
,
2446 "Deploy-Hackfest1": TestDeployHackfest1
,
2447 # "Deploy-MultiVIM": TestDeployMultiVIM,
2448 "Deploy-SingleVdu": TestDeploySingleVdu
,
2449 "Deploy-Hnfd": TestDeployHnfd
,
2450 "Upload-Slice-Template": TestNetSliceTemplates
,
2451 "Deploy-Slice-Instance": TestNetSliceInstances
,
2452 "Deploy-SimpleCharm": TestDeploySimpleCharm
,
2453 "Deploy-SimpleCharm2": TestDeploySimpleCharm2
,
2459 # print("parameter:", o, a)
2460 if o
== "--version":
2461 print("test version " + __version__
+ ' ' + version_date
)
2464 for test
, test_class
in sorted(test_classes
.items()):
2465 print("{:32} {}".format(test
+ ":", test_class
.description
))
2467 elif o
in ("-v", "--verbose"):
2469 elif o
== "no-verbose":
2471 elif o
in ("-h", "--help"):
2474 elif o
== "--test-osm":
2476 elif o
== "--manual-check":
2480 elif o
in ("-u", "--user"):
2482 elif o
in ("-p", "--password"):
2484 elif o
== "--project":
2486 elif o
== "--fail-fast":
2489 # print("asdfadf", o, a, a.split(","))
2490 for _test
in a
.split(","):
2491 if _test
not in test_classes
:
2492 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
2495 test_to_do
.append(_test
)
2496 elif o
== "--params":
2497 param_key
, _
, param_value
= a
.partition("=")
2498 text_index
= len(test_to_do
)
2499 if text_index
not in test_params
:
2500 test_params
[text_index
] = {}
2501 test_params
[text_index
][param_key
] = param_value
2502 elif o
== "--insecure":
2504 elif o
== "--timeout":
2506 elif o
== "--timeout-deploy":
2507 timeout_deploy
= int(a
)
2508 elif o
== "--timeout-configure":
2509 timeout_configure
= int(a
)
2511 assert False, "Unhandled option"
2513 logger
.setLevel(logging
.WARNING
)
2515 logger
.setLevel(logging
.DEBUG
)
2517 logger
.setLevel(logging
.ERROR
)
2519 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
2520 # print("tests to do:", test_to_do)
2523 for test
in test_to_do
:
2524 if fail_fast
and test_rest
.failed_tests
:
2527 test_class
= test_classes
[test
]
2528 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
2530 for test
, test_class
in sorted(test_classes
.items()):
2531 if fail_fast
and test_rest
.failed_tests
:
2533 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
2534 test_rest
.print_results()
2535 exit(1 if test_rest
.failed_tests
else 0)
2537 except TestException
as e
:
2538 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
2540 except getopt
.GetoptError
as e
:
2542 print(e
, file=sys
.stderr
)
2544 except Exception as e
:
2545 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)