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}, (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()
607 engine
.test("Create project Padmin", "POST", "/admin/v1/projects", headers_json
,
608 {"name": "Padmin", "admin": True}, (201, 204),
609 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
610 engine
.test("Create project P2", "POST", "/admin/v1/projects", headers_json
, {"name": "P2"},
611 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
612 engine
.test("Create project P3", "POST", "/admin/v1/projects", headers_json
, {"name": "P3"},
613 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
615 engine
.test("Create user U1", "POST", "/admin/v1/users", headers_json
,
616 {"username": "U1", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
617 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
619 engine
.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml
,
620 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
621 vnfd_ids
.append(engine
.last_id
)
622 engine
.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
623 headers_yaml
, TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
624 vnfd_ids
.append(engine
.last_id
)
625 engine
.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml
,
626 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
627 vnfd_ids
.append(engine
.last_id
)
629 res
= engine
.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
630 headers_json
, None, 200, r_header_json
, "json")
631 response
= res
.json()
632 if len(response
) != 3:
633 logger
.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response
)))
634 engine
.failed_tests
+= 1
636 # Change to other project Padmin
637 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
638 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
639 r_header_json
, "json")
641 response
= res
.json()
642 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
645 res
= engine
.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
646 headers_json
, None, 200, r_header_json
, "json")
647 response
= res
.json()
648 if len(response
) != 0:
649 logger
.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response
)))
650 engine
.failed_tests
+= 1
653 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
654 headers_json
, None, 200, r_header_json
, "json")
655 response
= res
.json()
656 if len(response
) != 1:
657 logger
.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response
)))
658 engine
.failed_tests
+= 1
660 # list vnfds belonging to project "admin"
661 res
= engine
.test("List VNFD of admin project", "GET", "/vnfpkgm/v1/vnf_packages?ADMIN=admin",
662 headers_json
, None, 200, r_header_json
, "json")
663 response
= res
.json()
664 if len(response
) != 3:
665 logger
.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response
)))
666 engine
.failed_tests
+= 1
669 engine
.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
670 headers_json
, None, 200, r_header_json
, "json")
671 # Edit not owned vnfd
672 engine
.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
673 headers_yaml
, '{name: pepe}', 404, r_header_yaml
, "yaml")
676 engine
.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
677 format(vnfd_ids
[1]), headers_json
, None, 204, None, 0)
680 engine
.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml
,
681 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
682 vnfd_ids
.append(engine
.last_id
)
685 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
686 headers_json
, None, 200, r_header_json
, "json")
687 response
= res
.json()
688 if len(response
) != 2:
689 logger
.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response
)))
690 engine
.failed_tests
+= 1
693 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
695 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
696 headers_yaml
, None, 204, None, 0)
698 # change to admin project
699 engine
.remove_authorization() # To force get authorization
700 engine
.get_autorization()
701 test_rest
.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
702 headers_yaml
, None, 204, None, 0)
703 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
704 headers_yaml
, None, 204, None, 0)
705 test_rest
.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[2]),
706 headers_yaml
, None, 204, None, 0)
707 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[3]),
708 headers_yaml
, None, 404, r_header_yaml
, "yaml")
709 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
710 headers_yaml
, None, 204, None, 0)
712 engine
.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[0]),
713 headers_json
, None, 404, r_header_json
, "json")
714 engine
.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[1]),
715 headers_json
, None, 404, r_header_json
, "json")
716 engine
.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[2]),
717 headers_json
, None, 404, r_header_json
, "json")
718 engine
.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
719 headers_json
, None, 404, r_header_json
, "json")
721 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
722 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204, None, None)
723 engine
.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 204, None, None)
724 engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json
, None, 204, None, None)
728 description
= "Creates/edit/delete fake VIMs and SDN controllers"
732 "schema_version": "1.0",
733 "schema_type": "No idea",
735 "description": "Descriptor name",
736 "vim_type": "openstack",
737 "vim_url": "http://localhost:/vim",
738 "vim_tenant_name": "vimTenant",
740 "vim_password": "password",
741 "config": {"config_param": 1}
745 "description": "sdn-description",
746 "dpid": "50:50:52:54:00:94:21:21",
747 "ip": "192.168.15.17",
749 "type": "opendaylight",
754 self
.port_mapping
= [
755 {"compute_node": "compute node 1",
756 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
757 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
759 {"compute_node": "compute node 2",
760 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
761 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
765 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
767 vim_bad
= self
.vim
.copy()
770 engine
.set_test_name("FakeVim")
771 engine
.get_autorization()
772 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 204),
773 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
774 vim_id
= engine
.last_id
775 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
776 vim_bad
, 422, None, headers_json
)
777 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
778 409, None, headers_json
)
779 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
781 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
782 r_header_yaml
, "yaml")
785 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
787 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
788 404, r_header_yaml
, "yaml")
790 # delete and wait until is really deleted
791 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
793 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
796 class TestVIMSDN(TestFakeVim
):
797 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
800 TestFakeVim
.__init
__(self
)
802 "schema_version": "1.0",
803 "schema_type": "No idea",
805 "description": "Descriptor name",
807 "wim_url": "http://localhost:/wim",
809 "password": "password",
810 "config": {"config_param": 1}
813 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
814 engine
.set_test_name("VimSdn")
815 engine
.get_autorization()
817 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 204),
818 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
819 sdnc_id
= engine
.last_id
822 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
826 self
.vim
["config"]["sdn-controller"] = sdnc_id
827 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
828 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 204, 201),
829 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
831 vim_id
= engine
.last_id
832 self
.port_mapping
[0]["compute_node"] = "compute node XX"
833 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
834 {"config": {"sdn-port-mapping": self
.port_mapping
}}, 204, None, None)
835 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
836 {"config": {"sdn-port-mapping": None}}, 204, None, None)
838 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 204, 201),
839 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
840 wim_id
= engine
.last_id
844 engine
.test("Delete VIM remove port-mapping", "DELETE",
845 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
846 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
849 engine
.test("Delete WIM", "DELETE",
850 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
851 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
852 None, 404, r_header_yaml
, "yaml")
853 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
854 404, r_header_yaml
, "yaml")
855 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
856 None, 404, r_header_yaml
, "yaml")
859 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
860 # delete and wait until is really deleted
861 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
862 headers_json
, None, (202, 201, 204), None, 0)
863 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
864 (202, 201, 204), None, 0)
865 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
866 headers_json
, None, (202, 201, 204), None, 0)
867 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
868 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
869 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
873 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
876 self
.test_name
= "DEPLOY"
881 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
882 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
883 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
884 self
.descriptor_edit
= None
885 self
.uses_configuration
= False
892 self
.ns_params
= None
893 self
.vnfr_ip_list
= {}
895 def create_descriptors(self
, engine
):
896 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
897 if not os
.path
.exists(temp_dir
):
898 os
.makedirs(temp_dir
)
899 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
900 if "/" in vnfd_filename
:
901 vnfd_filename_path
= vnfd_filename
902 if not os
.path
.exists(vnfd_filename_path
):
903 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
905 vnfd_filename_path
= temp_dir
+ vnfd_filename
906 if not os
.path
.exists(vnfd_filename_path
):
907 with
open(vnfd_filename_path
, "wb") as file:
908 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
909 if response
.status_code
>= 300:
910 raise TestException("Error downloading descriptor from '{}': {}".format(
911 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
912 file.write(response
.content
)
913 if vnfd_filename_path
.endswith(".yaml"):
914 headers
= headers_yaml
916 headers
= headers_zip_yaml
917 if randint(0, 1) == 0:
918 # vnfd CREATE AND UPLOAD in one step:
919 engine
.test("Onboard VNFD in one step", "POST",
920 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
921 r_headers_yaml_location_vnfd
,
923 self
.vnfds_id
.append(engine
.last_id
)
925 # vnfd CREATE AND UPLOAD ZIP
926 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
927 headers_json
, None, 201,
928 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
929 self
.vnfds_id
.append(engine
.last_id
)
930 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
931 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
932 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
934 if self
.descriptor_edit
:
935 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
937 engine
.test("Edit VNFD ", "PATCH",
938 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
939 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
941 if "/" in self
.nsd_filename
:
942 nsd_filename_path
= self
.nsd_filename
943 if not os
.path
.exists(nsd_filename_path
):
944 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
946 nsd_filename_path
= temp_dir
+ self
.nsd_filename
947 if not os
.path
.exists(nsd_filename_path
):
948 with
open(nsd_filename_path
, "wb") as file:
949 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
950 if response
.status_code
>= 300:
951 raise TestException("Error downloading descriptor from '{}': {}".format(
952 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
953 file.write(response
.content
)
954 if nsd_filename_path
.endswith(".yaml"):
955 headers
= headers_yaml
957 headers
= headers_zip_yaml
959 if randint(0, 1) == 0:
960 # nsd CREATE AND UPLOAD in one step:
961 engine
.test("Onboard NSD in one step", "POST",
962 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
963 r_headers_yaml_location_nsd
, yaml
)
964 self
.nsd_id
= engine
.last_id
966 # nsd CREATE AND UPLOAD ZIP
967 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
968 headers_json
, None, 201,
969 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
970 self
.nsd_id
= engine
.last_id
971 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
972 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
973 headers
, "@b" + nsd_filename_path
, 204, None, 0)
975 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
977 engine
.test("Edit NSD ", "PATCH",
978 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
979 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
981 def delete_descriptors(self
, engine
):
983 engine
.test("Delete NSSD SOL005", "DELETE",
984 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
985 headers_yaml
, None, 204, None, 0)
986 for vnfd_id
in self
.vnfds_id
:
987 engine
.test("Delete VNFD SOL005", "DELETE",
988 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
990 def instantiate(self
, engine
, ns_data
):
991 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
992 # create NS Two steps
993 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
994 headers_yaml
, ns_data_text
, 201,
995 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
998 self
.ns_id
= engine
.last_id
999 engine
.test("Instantiate NS step 2", "POST",
1000 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
1001 201, r_headers_yaml_location_nslcmop
, "yaml")
1002 nslcmop_id
= engine
.last_id
1005 # Wait until status is Ok
1006 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
1007 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
1009 def terminate(self
, engine
):
1012 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
1013 None, 201, r_headers_yaml_location_nslcmop
, "yaml")
1014 nslcmop2_id
= engine
.last_id
1015 # Wait until status is Ok
1016 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
1018 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1021 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1022 headers_yaml
, None, 204, None, 0)
1024 # check all it is deleted
1025 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1027 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1028 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1033 if not isinstance(nslcmops
, list) or nslcmops
:
1034 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1036 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
1038 r
= engine
.test("GET VNFR IDs", "GET",
1039 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
1040 200, r_header_json
, "json")
1045 vnfr_list
= ns_data
['constituent-vnfr-ref']
1047 _commands
= commands
if commands
is not None else self
.commands
1048 _users
= users
if users
is not None else self
.users
1049 _passwds
= passwds
if passwds
is not None else self
.passwords
1050 _keys
= keys
if keys
is not None else self
.keys
1051 _timeout
= timeout
if timeout
!= 0 else self
.timeout
1053 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1054 for vnfr_id
in vnfr_list
:
1055 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1056 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
1057 200, r_header_json
, "json")
1060 vnfr_data
= r
.json()
1062 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
1064 ip_address
= self
.get_vnfr_ip(engine
, vnf_index
)
1065 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
1068 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
1069 logger
.warning(test_description
)
1070 while _timeout
>= time
:
1071 result
, message
= self
.do_checks([ip_address
],
1072 vnf_index
=vnfr_data
["member-vnf-index-ref"],
1073 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
1074 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
1076 engine
.passed_tests
+= 1
1077 logger
.debug(message
)
1083 engine
.failed_tests
+= 1
1084 logger
.error(message
)
1088 engine
.failed_tests
+= 1
1089 logger
.error(message
)
1091 engine
.failed_tests
+= 1
1092 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnf_index
))
1094 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
1097 from pssh
.clients
import ParallelSSHClient
1098 from pssh
.utils
import load_private_key
1099 from ssh2
import exceptions
as ssh2Exception
1100 except ImportError as e
:
1101 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1102 "parallel-ssh urllib3': {}".format(e
))
1103 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1104 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
1106 p_host
= os
.environ
.get("PROXY_HOST")
1107 p_user
= os
.environ
.get("PROXY_USER")
1108 p_password
= os
.environ
.get("PROXY_PASSWD")
1111 pkey
= load_private_key(key
)
1115 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
1116 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
1117 for cmd
in commands
:
1118 output
= client
.run_command(cmd
)
1120 if output
[ip
[0]].exit_code
:
1121 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
1122 "\n".join(output
[ip
[0]].stderr
))
1124 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
1125 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
1126 ssh2Exception
.SocketRecvError
) as e
:
1127 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
1128 except Exception as e
:
1129 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
1131 def additional_operations(self
, engine
, test_osm
, manual_check
):
1134 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1135 engine
.set_test_name(self
.test_name
)
1136 engine
.get_autorization()
1137 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1139 if "vnfd-files" in test_params
:
1140 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
1141 if "nsd-file" in test_params
:
1142 self
.nsd_filename
= test_params
["nsd-file"]
1143 if test_params
.get("ns-name"):
1144 nsname
= test_params
["ns-name"]
1145 self
.create_descriptors(engine
)
1147 # create real VIM if not exist
1148 self
.vim_id
= engine
.get_create_vim(test_osm
)
1149 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1150 "vimAccountId": self
.vim_id
}
1152 ns_data
.update(self
.ns_params
)
1153 if test_params
and test_params
.get("ns-config"):
1154 if isinstance(test_params
["ns-config"], str):
1155 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1157 ns_data
.update(test_params
["ns-config"])
1158 self
.instantiate(engine
, ns_data
)
1161 input('NS has been deployed. Perform manual check and press enter to resume')
1162 if test_osm
and self
.commands
:
1163 self
.test_ns(engine
, test_osm
)
1164 self
.additional_operations(engine
, test_osm
, manual_check
)
1165 self
.terminate(engine
)
1166 self
.delete_descriptors(engine
)
1168 def get_first_ip(self
, ip_string
):
1169 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1170 first_ip
= ip_string
.split(";")[0] if ip_string
else ""
1173 def get_vnfr_ip(self
, engine
, vnfr_index_wanted
):
1174 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1175 ip
= self
.vnfr_ip_list
.get(vnfr_index_wanted
, "")
1177 return self
.get_first_ip(ip
)
1178 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1179 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1180 vnfr_index_wanted
, self
.ns_id
), headers_json
, None,
1181 200, r_header_json
, "json")
1184 vnfr_data
= r
.json()
1185 if not (vnfr_data
and vnfr_data
[0]):
1187 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1188 ip_list
= vnfr_data
[0].get("ip-address", "")
1190 self
.vnfr_ip_list
[vnfr_index_wanted
] = ip_list
1191 ip
= self
.get_first_ip(ip_list
)
1195 class TestDeployHackfestCirros(TestDeploy
):
1196 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
1200 self
.test_name
= "CIRROS"
1201 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1202 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1203 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1204 self
.users
= {'1': "cirros", '2': "cirros"}
1205 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1207 def terminate(self
, engine
):
1208 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1210 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1211 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1213 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1215 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1216 headers_yaml
, None, 204, None, 0)
1218 # check all it is deleted
1219 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1221 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1222 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1227 if not isinstance(nslcmops
, list) or nslcmops
:
1228 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1231 class TestDeployHackfest1(TestDeploy
):
1232 description
= "Load and deploy Hackfest_1_vnfd example"
1236 self
.test_name
= "HACKFEST1-"
1237 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1238 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1239 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1240 # self.users = {'1': "cirros", '2': "cirros"}
1241 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1244 class TestDeployHackfestCirrosScaling(TestDeploy
):
1245 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1249 self
.test_name
= "CIRROS-SCALE"
1250 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1251 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1253 def create_descriptors(self
, engine
):
1254 super().create_descriptors(engine
)
1255 # Modify VNFD to add scaling and count=2
1256 self
.descriptor_edit
= {
1259 "$id: 'cirros_vnfd-VM'": {"count": 2}
1261 "scaling-group-descriptor": [{
1262 "name": "scale_cirros",
1263 "max-instance-count": 2,
1265 "vdu-id-ref": "cirros_vnfd-VM",
1272 def additional_operations(self
, engine
, test_osm
, manual_check
):
1275 # 2 perform scale out twice
1276 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1277 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1278 for i
in range(0, 2):
1279 engine
.test("Execute scale action over NS", "POST",
1280 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1281 201, r_headers_yaml_location_nslcmop
, "yaml")
1282 nslcmop2_scale_out
= engine
.last_id
1283 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1285 input('NS scale out done. Check that two more vdus are there')
1286 # TODO check automatic
1288 # 2 perform scale in
1289 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1290 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1291 for i
in range(0, 2):
1292 engine
.test("Execute scale IN action over NS", "POST",
1293 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1294 201, r_headers_yaml_location_nslcmop
, "yaml")
1295 nslcmop2_scale_in
= engine
.last_id
1296 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1298 input('NS scale in done. Check that two less vdus are there')
1299 # TODO check automatic
1301 # perform scale in that must fail as reached limit
1302 engine
.test("Execute scale IN out of limit action over NS", "POST",
1303 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1304 201, r_headers_yaml_location_nslcmop
, "yaml")
1305 nslcmop2_scale_in
= engine
.last_id
1306 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1309 class TestDeployIpMac(TestDeploy
):
1310 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1314 self
.test_name
= "SetIpMac"
1315 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1316 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1317 self
.descriptor_url
= \
1318 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1319 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1320 self
.users
= {'1': "osm", '2': "osm"}
1321 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1324 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1325 # super().run(engine, test_osm, manual_check, test_params)
1326 # run again setting IPs with instantiate parameters
1327 instantiation_params
= {
1330 "member-vnf-index": "1",
1333 "name": "internal_vld1", # net_internal
1335 "ip-version": "ipv4",
1336 "subnet-address": "10.9.8.0/24",
1337 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1339 "internal-connection-point": [
1342 "ip-address": "10.9.8.2",
1346 "ip-address": "10.9.8.3",
1357 # "name": "iface11",
1358 # "floating-ip-required": True,
1362 "mac-address": "52:33:44:55:66:13"
1371 "ip-address": "10.31.31.22",
1372 "mac-address": "52:33:44:55:66:21"
1381 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1384 class TestDeployHackfest4(TestDeploy
):
1385 description
= "Load and deploy Hackfest 4 example."
1389 self
.test_name
= "HACKFEST4-"
1390 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1391 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1392 self
.uses_configuration
= True
1393 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1394 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1395 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1396 # Modify VNFD to add scaling
1397 # self.descriptor_edit = {
1399 # 'vnf-configuration': {
1400 # 'config-primitive': [{
1403 # 'name': 'filename',
1404 # 'data-type': 'STRING',
1405 # 'default-value': '/home/ubuntu/touched'
1409 # 'scaling-group-descriptor': [{
1410 # 'name': 'scale_dataVM',
1411 # 'scaling-policy': [{
1412 # 'threshold-time': 0,
1413 # 'name': 'auto_cpu_util_above_threshold',
1414 # 'scaling-type': 'automatic',
1415 # 'scaling-criteria': [{
1416 # 'name': 'cpu_util_above_threshold',
1417 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1418 # 'scale-out-relational-operation': 'GE',
1419 # 'scale-in-threshold': 15,
1420 # 'scale-out-threshold': 60,
1421 # 'scale-in-relational-operation': 'LE'
1423 # 'cooldown-time': 60
1425 # 'max-instance-count': 10,
1426 # 'scaling-config-action': [
1427 # {'vnf-config-primitive-name-ref': 'touch',
1428 # 'trigger': 'post-scale-out'},
1429 # {'vnf-config-primitive-name-ref': 'touch',
1430 # 'trigger': 'pre-scale-in'}
1433 # 'vdu-id-ref': 'dataVM',
1441 class TestDeployHackfest3Charmed(TestDeploy
):
1442 description
= "Load and deploy Hackfest 3charmed_ns example"
1446 self
.test_name
= "HACKFEST3-"
1447 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1448 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1449 self
.uses_configuration
= True
1450 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1451 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1452 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1453 self
.descriptor_edit
= {
1454 "vnfd0": yaml
.safe_load(
1457 terminate-config-primitive:
1462 value: '/home/ubuntu/last-touch1'
1467 value: '/home/ubuntu/last-touch3'
1472 value: '/home/ubuntu/last-touch2'
1476 def additional_operations(self
, engine
, test_osm
, manual_check
):
1480 vnfr_index_selected
= "2"
1481 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1482 engine
.test("Exec service primitive over NS", "POST",
1483 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1484 201, r_headers_yaml_location_nslcmop
, "yaml")
1485 nslcmop2_action
= engine
.last_id
1486 # Wait until status is Ok
1487 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1488 vnfr_ip
= self
.get_vnfr_ip(engine
, vnfr_index_selected
)
1491 "NS service primitive has been executed."
1492 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1495 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1496 self
.test_ns(engine
, test_osm
, commands
=commands
)
1498 # # 2 perform scale out
1499 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1500 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1501 # engine.test("Execute scale action over NS", "POST",
1502 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1503 # 201, r_headers_yaml_location_nslcmop, "yaml")
1504 # nslcmop2_scale_out = engine.last_id
1505 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1507 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1508 # # TODO check automatic
1510 # # 2 perform scale in
1511 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1512 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1513 # engine.test("Execute scale action over NS", "POST",
1514 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1515 # 201, r_headers_yaml_location_nslcmop, "yaml")
1516 # nslcmop2_scale_in = engine.last_id
1517 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1519 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1520 # # TODO check automatic
1523 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1524 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1525 "ids and member-vnf-index."
1529 self
.test_name
= "HACKFEST3v2-"
1530 self
.qforce
= "?FORCE=True"
1531 self
.descriptor_edit
= {
1535 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1539 "vnf-configuration": None,
1540 "connection-point": {
1544 "short-name": "pdu-mgmt"
1548 "mgmt-interface": {"cp": "pdu-mgmt"},
1549 "description": "A vnf single vdu to be used as PDU",
1553 "id": "pdu_internal",
1554 "name": "pdu_internal",
1555 "internal-connection-point": {"$[1]": None},
1556 "short-name": "pdu_internal",
1562 # Modify NSD accordingly
1564 "constituent-vnfd": {
1565 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1568 "description": "A nsd to deploy the vnf to act as as PDU",
1570 "name": "nsd-as-pdu",
1571 "short-name": "nsd-as-pdu",
1576 "short-name": "mgmt_pdu",
1577 "vnfd-connection-point-ref": {
1579 "vnfd-connection-point-ref": "pdu-mgmt",
1580 "vnfd-id-ref": "vdu-as-pdu",
1592 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1593 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1597 self
.test_name
= "HACKFEST3v3-"
1598 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1599 self
.descriptor_edit
= {
1602 scaling-group-descriptor:
1603 - name: "scale_dataVM"
1604 max-instance-count: 10
1606 - name: "auto_cpu_util_above_threshold"
1607 scaling-type: "automatic"
1611 - name: "cpu_util_above_threshold"
1612 scale-in-threshold: 15
1613 scale-in-relational-operation: "LE"
1614 scale-out-threshold: 60
1615 scale-out-relational-operation: "GE"
1616 vnf-monitoring-param-ref: "monitor1"
1618 - vdu-id-ref: dataVM
1620 scaling-config-action:
1621 - trigger: post-scale-out
1622 vnf-config-primitive-name-ref: touch
1623 - trigger: pre-scale-in
1624 vnf-config-primitive-name-ref: touch
1628 - id: "dataVM_cpu_util"
1629 nfvi-metric: "cpu_utilization"
1634 aggregation-type: AVERAGE
1635 vdu-monitoring-param:
1637 vdu-monitoring-param-ref: "dataVM_cpu_util"
1639 initial-config-primitive:
1643 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1648 default-value: "<touch_filename2>"
1652 "additionalParamsForVnf": [
1653 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1654 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1655 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1656 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1660 def additional_operations(self
, engine
, test_osm
, manual_check
):
1661 super().additional_operations(engine
, test_osm
, manual_check
)
1665 # 2 perform scale out
1666 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1667 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1668 engine
.test("Execute scale action over NS", "POST",
1669 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1670 201, r_headers_yaml_location_nslcmop
, "yaml")
1671 nslcmop2_scale_out
= engine
.last_id
1672 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1674 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1676 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1677 self
.test_ns(engine
, test_osm
, commands
=commands
)
1678 # TODO check automatic connection to scaled VM
1680 # 2 perform scale in
1681 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1682 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1683 engine
.test("Execute scale action over NS", "POST",
1684 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1685 201, r_headers_yaml_location_nslcmop
, "yaml")
1686 nslcmop2_scale_in
= engine
.last_id
1687 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1689 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1690 # TODO check automatic
1693 class TestDeploySimpleCharm(TestDeploy
):
1694 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1698 self
.test_name
= "HACKFEST-SIMPLE"
1699 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1700 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1701 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1702 self
.uses_configuration
= True
1703 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1704 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1705 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1708 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1709 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1714 self
.test_name
= "HACKFEST-SIMPLE2-"
1715 self
.qforce
= "?FORCE=True"
1716 self
.descriptor_edit
= {
1718 "id": "hackfest.simplecharm.vnf"
1722 "id": "hackfest.simplecharm.ns",
1723 "constituent-vnfd": {
1724 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1725 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1729 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1730 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1731 "$[1]": {"member-vnf-index-ref": "$2",
1732 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1735 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1736 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1737 "$[1]": {"member-vnf-index-ref": "$2",
1738 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1745 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1746 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1750 self
.test_name
= "SingleVDU"
1751 self
.qforce
= "?FORCE=True"
1752 self
.descriptor_edit
= {
1753 # Modify VNFD to remove one VDU
1757 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1761 "vnf-configuration": None,
1762 "connection-point": {
1766 "short-name": "pdu-mgmt"
1770 "mgmt-interface": {"cp": "pdu-mgmt"},
1771 "description": "A vnf single vdu to be used as PDU",
1775 "id": "pdu_internal",
1776 "name": "pdu_internal",
1777 "internal-connection-point": {"$[1]": None},
1778 "short-name": "pdu_internal",
1784 # Modify NSD accordingly
1786 "constituent-vnfd": {
1787 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1790 "description": "A nsd to deploy the vnf to act as as PDU",
1792 "name": "nsd-as-pdu",
1793 "short-name": "nsd-as-pdu",
1798 "short-name": "mgmt_pdu",
1799 "vnfd-connection-point-ref": {
1801 "vnfd-connection-point-ref": "pdu-mgmt",
1802 "vnfd-id-ref": "vdu-as-pdu",
1814 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1815 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1819 self
.test_name
= "HNFD"
1820 self
.pduDeploy
= TestDeploySingleVdu()
1821 self
.pdu_interface_0
= {}
1822 self
.pdu_interface_1
= {}
1825 # self.vnf_to_pdu = """
1828 # pdu-type: PDU-TYPE-1
1833 # name: pdu-iface-internal
1835 # description: HFND, one PDU + One VDU
1841 self
.pdu_descriptor
= {
1843 "type": "PDU-TYPE-1",
1844 "vim_accounts": "to-override",
1847 "name": "mgmt-iface",
1850 "ip-address": "to override",
1851 "mac-address": "mac_address",
1852 "vim-network-name": "mgmt",
1855 "name": "pdu-iface-internal",
1858 "ip-address": "to override",
1859 "mac-address": "mac_address",
1860 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1864 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1866 self
.descriptor_edit
= {
1870 "short-name": "hfn1",
1873 "pdu-type": "PDU-TYPE-1",
1875 "$[0]": {"name": "mgmt-iface"},
1876 "$[1]": {"name": "pdu-iface-internal"},
1882 "constituent-vnfd": {
1883 "$[1]": {"vnfd-id-ref": "hfnd1"}
1886 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1887 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1892 def create_descriptors(self
, engine
):
1893 super().create_descriptors(engine
)
1896 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1897 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1898 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1899 # TODO get vim-network-name from vnfr.vld.name
1900 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1901 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1902 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1903 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1904 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1905 201, r_header_yaml
, "yaml")
1906 self
.pdu_id
= engine
.last_id
1908 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1909 engine
.get_autorization()
1910 engine
.set_test_name(self
.test_name
)
1911 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1913 # create real VIM if not exist
1914 self
.vim_id
= engine
.get_create_vim(test_osm
)
1916 self
.pduDeploy
.create_descriptors(engine
)
1917 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1918 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1920 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1922 self
.pduDeploy
.test_ns(engine
, test_osm
)
1925 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1926 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1927 200, r_header_json
, "json")
1930 vnfr_data
= r
.json()
1933 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1934 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1935 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1936 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1937 if not self
.pdu_interface_0
["ip-address"]:
1938 raise TestException("Vnfr has not managment ip address")
1940 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1941 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1942 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1943 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1945 self
.create_descriptors(engine
)
1947 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1948 "vimAccountId": self
.vim_id
}
1949 if test_params
and test_params
.get("ns-config"):
1950 if isinstance(test_params
["ns-config"], str):
1951 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1953 ns_data
.update(test_params
["ns-config"])
1955 self
.instantiate(engine
, ns_data
)
1957 input('NS has been deployed. Perform manual check and press enter to resume')
1959 self
.test_ns(engine
, test_osm
)
1960 self
.additional_operations(engine
, test_osm
, manual_check
)
1961 self
.terminate(engine
)
1962 self
.pduDeploy
.terminate(engine
)
1963 self
.delete_descriptors(engine
)
1964 self
.pduDeploy
.delete_descriptors(engine
)
1966 def delete_descriptors(self
, engine
):
1967 super().delete_descriptors(engine
)
1969 engine
.test("Delete PDU SOL005", "DELETE",
1970 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1971 headers_yaml
, None, 204, None, 0)
1974 class TestDescriptors
:
1975 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1976 vnfd_empty
= """vnfd:vnfd-catalog:
1982 vnfd_prova
= """vnfd:vnfd-catalog:
1994 - external-connection-point-ref: cp_0h8m
2003 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
2004 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
2005 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2009 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2010 engine
.set_test_name("Descriptors")
2011 engine
.get_autorization()
2012 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2013 if not os
.path
.exists(temp_dir
):
2014 os
.makedirs(temp_dir
)
2017 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
2018 filename_path
= temp_dir
+ filename
2019 if not os
.path
.exists(filename_path
):
2020 with
open(filename_path
, "wb") as file:
2021 response
= requests
.get(self
.descriptor_url
+ filename
)
2022 if response
.status_code
>= 300:
2023 raise TestException("Error downloading descriptor from '{}': {}".format(
2024 self
.descriptor_url
+ filename
, response
.status_code
))
2025 file.write(response
.content
)
2027 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
2028 nsd_filename_path
= temp_dir
+ self
.nsd_filename
2030 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2031 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
2032 self
.vnfd_id
= engine
.last_id
2035 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2036 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2038 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
2039 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
2040 "@b" + vnfd_filename_path
, 204, None, 0)
2042 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2043 "vdu.0.interface.1.internal-connection-point-ref=internal",
2044 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2045 # Detection of duplicated VLD names in VNF Descriptors
2046 # URL: internal-vld=[
2047 # {id: internal1, name: internal, type:ELAN,
2048 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2049 # {id: internal2, name: internal, type:ELAN,
2050 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2052 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2053 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2054 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2055 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2056 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2058 for query
in queries
:
2059 engine
.test("Upload invalid VNFD ", "PUT",
2060 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
2061 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
2064 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2065 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2067 # get vnfd descriptor
2068 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2069 headers_yaml
, None, 200, r_header_yaml
, "yaml")
2071 # get vnfd file descriptor
2072 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
2073 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
2074 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2076 # get vnfd zip file package
2077 engine
.test("Get VNFD zip package", "GET",
2078 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
2079 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
2080 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2083 engine
.test("Get VNFD artifact package", "GET",
2084 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
2085 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
2086 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2088 # nsd CREATE AND UPLOAD in one step:
2089 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
2090 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
2091 self
.nsd_id
= engine
.last_id
2093 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2094 for query
in queries
:
2095 engine
.test("Upload invalid NSD ", "PUT",
2096 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
2097 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
2099 # get nsd descriptor
2100 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
2101 None, 200, r_header_yaml
, "yaml")
2103 # get nsd file descriptor
2104 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
2105 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
2106 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2108 # get nsd zip file package
2109 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
2110 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
2111 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2114 engine
.test("Get NSD artifact package", "GET",
2115 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
2116 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
2117 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2120 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2121 headers_yaml
, None, 409, None, None)
2123 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
2124 headers_yaml
, None, 204, None, 0)
2127 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
2131 class TestNetSliceTemplates
:
2132 description
= "Upload a NST to OSM"
2135 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2136 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2137 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2138 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2139 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2141 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2143 engine
.set_test_name("NST step ")
2144 engine
.get_autorization()
2145 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2146 if not os
.path
.exists(temp_dir
):
2147 os
.makedirs(temp_dir
)
2150 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2151 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2152 self
.vnfd_edge_id
= engine
.last_id
2154 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2155 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2156 self
.vnfd_middle_id
= engine
.last_id
2159 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2160 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2161 self
.nsd_edge_id
= engine
.last_id
2163 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2164 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2165 self
.nsd_middle_id
= engine
.last_id
2168 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2169 201, r_headers_yaml_location_nst
, "yaml")
2170 nst_id
= engine
.last_id
2172 # nstd SHOW OSM format
2173 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2174 200, r_header_json
, "json")
2177 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2181 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2182 headers_json
, None, 204, None, 0)
2184 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2188 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2189 headers_yaml
, None, 204, None, 0)
2191 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2192 headers_yaml
, None, 204, None, 0)
2195 class TestNetSliceInstances
:
2198 1. Populate databases with VNFD, NSD, NST with the following scenario
2199 +-----------------management-----------------+
2201 +--+---+ +----+----+ +---+--+
2203 | edge +---data1----+ middle +---data2-----+ edge |
2205 +------+ +---------+ +------+
2208 3. Instantiate NSI-1
2210 5. Instantiate NSI-2
2211 Manual check - Are 2 slices instantiated correctly?
2212 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2213 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2216 Manual check - Is slice NSI-1 deleted correctly?
2217 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2219 9. Instantiate NSI-3
2220 Manual check - Is slice NSI-3 instantiated correctly?
2221 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2226 Manual check - All cleaned correctly?
2227 NSI-2 and NSI-3 were terminated and deleted
2228 14. Cleanup database
2231 description
= "Upload a NST to OSM"
2235 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2236 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2237 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2238 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2239 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2241 def create_slice(self
, engine
, nsi_data
, name
):
2242 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2243 r
= engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances",
2244 headers_yaml
, ns_data_text
, 201,
2245 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2248 def instantiate_slice(self
, engine
, nsi_data
, nsi_id
, name
):
2249 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2250 engine
.test(name
, "POST",
2251 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id
), headers_yaml
, ns_data_text
,
2252 201, r_headers_yaml_location_nsilcmop
, "yaml")
2254 def terminate_slice(self
, engine
, nsi_id
, name
):
2255 engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id
),
2256 headers_yaml
, None, 201, r_headers_yaml_location_nsilcmop
, "yaml")
2258 def delete_slice(self
, engine
, nsi_id
, name
):
2259 engine
.test(name
, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id
), headers_yaml
, None,
2262 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2264 engine
.set_test_name("NSI")
2265 engine
.get_autorization()
2268 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2269 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2270 self
.vnfd_edge_id
= engine
.last_id
2272 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2273 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2274 self
.vnfd_middle_id
= engine
.last_id
2277 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2278 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2279 self
.nsd_edge_id
= engine
.last_id
2281 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2282 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2283 self
.nsd_middle_id
= engine
.last_id
2286 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2287 201, r_headers_yaml_location_nst
, "yaml")
2288 nst_id
= engine
.last_id
2290 self
.vim_id
= engine
.get_create_vim(test_osm
)
2293 ns_data
= {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2294 r
= self
.create_slice(engine
, ns_data
, "Create NSI-1 step 1")
2297 self
.nsi_id1
= engine
.last_id
2300 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id1
, "Instantiate NSI-1 step 2")
2301 nsilcmop_id1
= engine
.last_id
2305 engine
.wait_operation_ready("nsi", nsilcmop_id1
, timeout_deploy
)
2308 ns_data
= {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2309 r
= self
.create_slice(engine
, ns_data
, "Create NSI-2 step 1")
2312 self
.nsi_id2
= engine
.last_id
2315 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id2
, "Instantiate NSI-2 step 2")
2316 nsilcmop_id2
= engine
.last_id
2320 engine
.wait_operation_ready("nsi", nsilcmop_id2
, timeout_deploy
)
2323 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2327 self
.terminate_slice(engine
, self
.nsi_id1
, "Terminate NSI-1")
2328 nsilcmop1_id
= engine
.last_id
2330 # Wait terminate NSI-1
2331 engine
.wait_operation_ready("nsi", nsilcmop1_id
, timeout_deploy
)
2334 self
.delete_slice(engine
, self
.nsi_id1
, "Delete NS")
2337 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2340 ns_data
= {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2341 r
= self
.create_slice(engine
, ns_data
, "Create NSI-3 step 1")
2345 self
.nsi_id3
= engine
.last_id
2348 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id3
, "Instantiate NSI-3 step 2")
2349 nsilcmop_id3
= engine
.last_id
2351 # Wait Instantiate NSI-3
2353 engine
.wait_operation_ready("nsi", nsilcmop_id3
, timeout_deploy
)
2356 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2360 self
.terminate_slice(engine
, self
.nsi_id2
, "Terminate NSI-2")
2361 nsilcmop2_id
= engine
.last_id
2363 # Wait terminate NSI-2
2364 engine
.wait_operation_ready("nsi", nsilcmop2_id
, timeout_deploy
)
2367 self
.delete_slice(engine
, self
.nsi_id2
, "DELETE NSI-2")
2371 self
. terminate_slice(engine
, self
.nsi_id3
, "Terminate NSI-3")
2372 nsilcmop3_id
= engine
.last_id
2374 # Wait terminate NSI-3
2375 engine
.wait_operation_ready("nsi", nsilcmop3_id
, timeout_deploy
)
2378 self
.delete_slice(engine
, self
.nsi_id3
, "DELETE NSI-3")
2381 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2384 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2388 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2389 headers_json
, None, 204, None, 0)
2391 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2395 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2396 headers_yaml
, None, 204, None, 0)
2398 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2399 headers_yaml
, None, 204, None, 0)
2402 if __name__
== "__main__":
2406 # Disable warnings from self-signed certificates.
2407 requests
.packages
.urllib3
.disable_warnings()
2409 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
2410 logger
= logging
.getLogger('NBI')
2411 # load parameters and configuration
2412 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
2413 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2414 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2415 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2416 url
= "https://localhost:9999/osm"
2417 user
= password
= project
= "admin"
2419 manual_check
= False
2424 "NonAuthorized": TestNonAuthorized
,
2425 "FakeVIM": TestFakeVim
,
2426 "Users-Projects": TestUsersProjects
,
2427 "Projects-Descriptors": TestProjectsDescriptors
,
2428 "VIM-SDN": TestVIMSDN
,
2429 "Deploy-Custom": TestDeploy
,
2430 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
2431 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
2432 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
2433 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2
,
2434 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3
,
2435 "Deploy-Hackfest-4": TestDeployHackfest4
,
2436 "Deploy-CirrosMacIp": TestDeployIpMac
,
2437 "Descriptors": TestDescriptors
,
2438 "Deploy-Hackfest1": TestDeployHackfest1
,
2439 # "Deploy-MultiVIM": TestDeployMultiVIM,
2440 "Deploy-SingleVdu": TestDeploySingleVdu
,
2441 "Deploy-Hnfd": TestDeployHnfd
,
2442 "Upload-Slice-Template": TestNetSliceTemplates
,
2443 "Deploy-Slice-Instance": TestNetSliceInstances
,
2444 "Deploy-SimpleCharm": TestDeploySimpleCharm
,
2445 "Deploy-SimpleCharm2": TestDeploySimpleCharm2
,
2451 # print("parameter:", o, a)
2452 if o
== "--version":
2453 print("test version " + __version__
+ ' ' + version_date
)
2456 for test
, test_class
in sorted(test_classes
.items()):
2457 print("{:32} {}".format(test
+ ":", test_class
.description
))
2459 elif o
in ("-v", "--verbose"):
2461 elif o
== "no-verbose":
2463 elif o
in ("-h", "--help"):
2466 elif o
== "--test-osm":
2468 elif o
== "--manual-check":
2472 elif o
in ("-u", "--user"):
2474 elif o
in ("-p", "--password"):
2476 elif o
== "--project":
2478 elif o
== "--fail-fast":
2481 # print("asdfadf", o, a, a.split(","))
2482 for _test
in a
.split(","):
2483 if _test
not in test_classes
:
2484 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
2487 test_to_do
.append(_test
)
2488 elif o
== "--params":
2489 param_key
, _
, param_value
= a
.partition("=")
2490 text_index
= len(test_to_do
)
2491 if text_index
not in test_params
:
2492 test_params
[text_index
] = {}
2493 test_params
[text_index
][param_key
] = param_value
2494 elif o
== "--insecure":
2496 elif o
== "--timeout":
2498 elif o
== "--timeout-deploy":
2499 timeout_deploy
= int(a
)
2500 elif o
== "--timeout-configure":
2501 timeout_configure
= int(a
)
2503 assert False, "Unhandled option"
2505 logger
.setLevel(logging
.WARNING
)
2507 logger
.setLevel(logging
.DEBUG
)
2509 logger
.setLevel(logging
.ERROR
)
2511 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
2512 # print("tests to do:", test_to_do)
2515 for test
in test_to_do
:
2516 if fail_fast
and test_rest
.failed_tests
:
2519 test_class
= test_classes
[test
]
2520 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
2522 for test
, test_class
in sorted(test_classes
.items()):
2523 if fail_fast
and test_rest
.failed_tests
:
2525 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
2526 test_rest
.print_results()
2527 exit(1 if test_rest
.failed_tests
else 0)
2529 except TestException
as e
:
2530 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
2532 except getopt
.GetoptError
as e
:
2534 print(e
, file=sys
.stderr
)
2536 except Exception as e
:
2537 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)