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", "projects": ["Padmin", "P2", "P3"], "password": "pw1"}, 201,
624 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
626 engine
.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml
,
627 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
628 vnfd_ids
.append(engine
.last_id
)
629 engine
.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
630 headers_yaml
, TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
631 vnfd_ids
.append(engine
.last_id
)
632 engine
.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml
,
633 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
634 vnfd_ids
.append(engine
.last_id
)
636 res
= engine
.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
637 headers_json
, None, 200, r_header_json
, "json")
638 response
= res
.json()
639 if len(response
) != 3:
640 logger
.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response
)))
641 engine
.failed_tests
+= 1
643 # Change to other project Padmin
644 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
645 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
646 r_header_json
, "json")
648 response
= res
.json()
649 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
652 res
= engine
.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
653 headers_json
, None, 200, r_header_json
, "json")
654 response
= res
.json()
655 if len(response
) != 0:
656 logger
.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response
)))
657 engine
.failed_tests
+= 1
660 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
661 headers_json
, None, 200, r_header_json
, "json")
662 response
= res
.json()
663 if len(response
) != 1:
664 logger
.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response
)))
665 engine
.failed_tests
+= 1
667 # list vnfds belonging to project "admin"
668 res
= engine
.test("List VNFD of admin project", "GET",
669 "/vnfpkgm/v1/vnf_packages?ADMIN={}".format(project_admin_id
),
670 headers_json
, None, 200, r_header_json
, "json")
671 response
= res
.json()
672 if len(response
) != 3:
673 logger
.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response
)))
674 engine
.failed_tests
+= 1
677 engine
.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
678 headers_json
, None, 200, r_header_json
, "json")
679 # Edit not owned vnfd
680 engine
.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
681 headers_yaml
, '{name: pepe}', 404, r_header_yaml
, "yaml")
684 engine
.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
685 format(vnfd_ids
[1]), headers_json
, None, 204, None, 0)
688 engine
.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml
,
689 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
690 vnfd_ids
.append(engine
.last_id
)
693 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
694 headers_json
, None, 200, r_header_json
, "json")
695 response
= res
.json()
696 if len(response
) != 2:
697 logger
.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response
)))
698 engine
.failed_tests
+= 1
701 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
703 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
704 headers_yaml
, None, 204, None, 0)
706 # change to admin project
707 engine
.remove_authorization() # To force get authorization
708 engine
.get_autorization()
709 test_rest
.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
710 headers_yaml
, None, 204, None, 0)
711 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
712 headers_yaml
, None, 204, None, 0)
713 test_rest
.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[2]),
714 headers_yaml
, None, 204, None, 0)
715 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[3]),
716 headers_yaml
, None, 404, r_header_yaml
, "yaml")
717 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
718 headers_yaml
, None, 204, None, 0)
720 engine
.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[0]),
721 headers_json
, None, 404, r_header_json
, "json")
722 engine
.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[1]),
723 headers_json
, None, 404, r_header_json
, "json")
724 engine
.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[2]),
725 headers_json
, None, 404, r_header_json
, "json")
726 engine
.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
727 headers_json
, None, 404, r_header_json
, "json")
729 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
730 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204, None, None)
731 engine
.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 204, None, None)
732 engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json
, None, 204, None, None)
736 description
= "Creates/edit/delete fake VIMs and SDN controllers"
740 "schema_version": "1.0",
741 "schema_type": "No idea",
743 "description": "Descriptor name",
744 "vim_type": "openstack",
745 "vim_url": "http://localhost:/vim",
746 "vim_tenant_name": "vimTenant",
748 "vim_password": "password",
749 "config": {"config_param": 1}
753 "description": "sdn-description",
754 "dpid": "50:50:52:54:00:94:21:21",
755 "ip": "192.168.15.17",
757 "type": "opendaylight",
762 self
.port_mapping
= [
763 {"compute_node": "compute node 1",
764 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
765 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
767 {"compute_node": "compute node 2",
768 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
769 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
773 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
775 vim_bad
= self
.vim
.copy()
778 engine
.set_test_name("FakeVim")
779 engine
.get_autorization()
780 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 202),
781 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
782 vim_id
= engine
.last_id
783 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
784 vim_bad
, 422, None, headers_json
)
785 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
786 409, None, headers_json
)
787 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
789 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
790 r_header_yaml
, "yaml")
793 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
795 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
796 404, r_header_yaml
, "yaml")
798 # delete and wait until is really deleted
799 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
801 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
804 class TestVIMSDN(TestFakeVim
):
805 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
808 TestFakeVim
.__init
__(self
)
810 "schema_version": "1.0",
811 "schema_type": "No idea",
813 "description": "Descriptor name",
815 "wim_url": "http://localhost:/wim",
817 "password": "password",
818 "config": {"config_param": 1}
821 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
822 engine
.set_test_name("VimSdn")
823 engine
.get_autorization()
825 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 202),
826 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
827 sdnc_id
= engine
.last_id
830 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
831 (202, 204), None, None)
834 self
.vim
["config"]["sdn-controller"] = sdnc_id
835 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
836 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 202, 201),
837 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
839 vim_id
= engine
.last_id
840 self
.port_mapping
[0]["compute_node"] = "compute node XX"
841 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
842 {"config": {"sdn-port-mapping": self
.port_mapping
}}, (202, 204), None, None)
843 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
844 {"config": {"sdn-port-mapping": None}}, (202, 204), None, None)
846 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 202, 201),
847 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
848 wim_id
= engine
.last_id
852 engine
.test("Delete VIM remove port-mapping", "DELETE",
853 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
854 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
857 engine
.test("Delete WIM", "DELETE",
858 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
859 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
860 None, 404, r_header_yaml
, "yaml")
861 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
862 404, r_header_yaml
, "yaml")
863 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
864 None, 404, r_header_yaml
, "yaml")
867 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
868 # delete and wait until is really deleted
869 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
870 headers_json
, None, (202, 201, 204), None, 0)
871 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
872 (202, 201, 204), None, 0)
873 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
874 headers_json
, None, (202, 201, 204), None, 0)
875 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
876 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
877 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
881 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
884 self
.test_name
= "DEPLOY"
889 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
890 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
891 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
892 self
.descriptor_edit
= None
893 self
.uses_configuration
= False
900 self
.ns_params
= None
901 self
.vnfr_ip_list
= {}
903 def create_descriptors(self
, engine
):
904 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
905 if not os
.path
.exists(temp_dir
):
906 os
.makedirs(temp_dir
)
907 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
908 if "/" in vnfd_filename
:
909 vnfd_filename_path
= vnfd_filename
910 if not os
.path
.exists(vnfd_filename_path
):
911 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
913 vnfd_filename_path
= temp_dir
+ vnfd_filename
914 if not os
.path
.exists(vnfd_filename_path
):
915 with
open(vnfd_filename_path
, "wb") as file:
916 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
917 if response
.status_code
>= 300:
918 raise TestException("Error downloading descriptor from '{}': {}".format(
919 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
920 file.write(response
.content
)
921 if vnfd_filename_path
.endswith(".yaml"):
922 headers
= headers_yaml
924 headers
= headers_zip_yaml
925 if randint(0, 1) == 0:
926 # vnfd CREATE AND UPLOAD in one step:
927 engine
.test("Onboard VNFD in one step", "POST",
928 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
929 r_headers_yaml_location_vnfd
,
931 self
.vnfds_id
.append(engine
.last_id
)
933 # vnfd CREATE AND UPLOAD ZIP
934 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
935 headers_json
, None, 201,
936 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
937 self
.vnfds_id
.append(engine
.last_id
)
938 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
939 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
940 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
942 if self
.descriptor_edit
:
943 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
945 engine
.test("Edit VNFD ", "PATCH",
946 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
947 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
949 if "/" in self
.nsd_filename
:
950 nsd_filename_path
= self
.nsd_filename
951 if not os
.path
.exists(nsd_filename_path
):
952 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
954 nsd_filename_path
= temp_dir
+ self
.nsd_filename
955 if not os
.path
.exists(nsd_filename_path
):
956 with
open(nsd_filename_path
, "wb") as file:
957 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
958 if response
.status_code
>= 300:
959 raise TestException("Error downloading descriptor from '{}': {}".format(
960 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
961 file.write(response
.content
)
962 if nsd_filename_path
.endswith(".yaml"):
963 headers
= headers_yaml
965 headers
= headers_zip_yaml
967 if randint(0, 1) == 0:
968 # nsd CREATE AND UPLOAD in one step:
969 engine
.test("Onboard NSD in one step", "POST",
970 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
971 r_headers_yaml_location_nsd
, yaml
)
972 self
.nsd_id
= engine
.last_id
974 # nsd CREATE AND UPLOAD ZIP
975 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
976 headers_json
, None, 201,
977 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
978 self
.nsd_id
= engine
.last_id
979 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
980 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
981 headers
, "@b" + nsd_filename_path
, 204, None, 0)
983 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
985 engine
.test("Edit NSD ", "PATCH",
986 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
987 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
989 def delete_descriptors(self
, engine
):
991 engine
.test("Delete NSSD SOL005", "DELETE",
992 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
993 headers_yaml
, None, 204, None, 0)
994 for vnfd_id
in self
.vnfds_id
:
995 engine
.test("Delete VNFD SOL005", "DELETE",
996 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
998 def instantiate(self
, engine
, ns_data
):
999 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
1000 # create NS Two steps
1001 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
1002 headers_yaml
, ns_data_text
, (201, 202),
1003 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
1006 self
.ns_id
= engine
.last_id
1007 engine
.test("Instantiate NS step 2", "POST",
1008 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
1009 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1010 nslcmop_id
= engine
.last_id
1013 # Wait until status is Ok
1014 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
1015 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
1017 def terminate(self
, engine
):
1020 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
1021 None, (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1022 nslcmop2_id
= engine
.last_id
1023 # Wait until status is Ok
1024 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
1026 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1029 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1030 headers_yaml
, None, 204, None, 0)
1032 # check all it is deleted
1033 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1035 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1036 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1041 if not isinstance(nslcmops
, list) or nslcmops
:
1042 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1044 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
1046 r
= engine
.test("GET VNFR IDs", "GET",
1047 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
1048 200, r_header_json
, "json")
1053 vnfr_list
= ns_data
['constituent-vnfr-ref']
1055 _commands
= commands
if commands
is not None else self
.commands
1056 _users
= users
if users
is not None else self
.users
1057 _passwds
= passwds
if passwds
is not None else self
.passwords
1058 _keys
= keys
if keys
is not None else self
.keys
1059 _timeout
= timeout
if timeout
!= 0 else self
.timeout
1061 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1062 for vnfr_id
in vnfr_list
:
1063 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1064 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
1065 200, r_header_json
, "json")
1068 vnfr_data
= r
.json()
1070 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
1072 ip_address
= self
.get_vnfr_ip(engine
, vnf_index
)
1073 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
1076 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
1077 logger
.warning(test_description
)
1078 while _timeout
>= time
:
1079 result
, message
= self
.do_checks([ip_address
],
1080 vnf_index
=vnfr_data
["member-vnf-index-ref"],
1081 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
1082 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
1084 engine
.passed_tests
+= 1
1085 logger
.debug(message
)
1091 engine
.failed_tests
+= 1
1092 logger
.error(message
)
1096 engine
.failed_tests
+= 1
1097 logger
.error(message
)
1099 engine
.failed_tests
+= 1
1100 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnf_index
))
1102 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
1105 from pssh
.clients
import ParallelSSHClient
1106 from pssh
.utils
import load_private_key
1107 from ssh2
import exceptions
as ssh2Exception
1108 except ImportError as e
:
1109 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1110 "parallel-ssh urllib3': {}".format(e
))
1111 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1112 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
1114 p_host
= os
.environ
.get("PROXY_HOST")
1115 p_user
= os
.environ
.get("PROXY_USER")
1116 p_password
= os
.environ
.get("PROXY_PASSWD")
1119 pkey
= load_private_key(key
)
1123 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
1124 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
1125 for cmd
in commands
:
1126 output
= client
.run_command(cmd
)
1128 if output
[ip
[0]].exit_code
:
1129 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
1130 "\n".join(output
[ip
[0]].stderr
))
1132 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
1133 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
1134 ssh2Exception
.SocketRecvError
) as e
:
1135 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
1136 except Exception as e
:
1137 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
1139 def additional_operations(self
, engine
, test_osm
, manual_check
):
1142 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1143 engine
.set_test_name(self
.test_name
)
1144 engine
.get_autorization()
1145 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1147 if "vnfd-files" in test_params
:
1148 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
1149 if "nsd-file" in test_params
:
1150 self
.nsd_filename
= test_params
["nsd-file"]
1151 if test_params
.get("ns-name"):
1152 nsname
= test_params
["ns-name"]
1153 self
.create_descriptors(engine
)
1155 # create real VIM if not exist
1156 self
.vim_id
= engine
.get_create_vim(test_osm
)
1157 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1158 "vimAccountId": self
.vim_id
}
1160 ns_data
.update(self
.ns_params
)
1161 if test_params
and test_params
.get("ns-config"):
1162 if isinstance(test_params
["ns-config"], str):
1163 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1165 ns_data
.update(test_params
["ns-config"])
1166 self
.instantiate(engine
, ns_data
)
1169 input('NS has been deployed. Perform manual check and press enter to resume')
1170 if test_osm
and self
.commands
:
1171 self
.test_ns(engine
, test_osm
)
1172 self
.additional_operations(engine
, test_osm
, manual_check
)
1173 self
.terminate(engine
)
1174 self
.delete_descriptors(engine
)
1176 def get_first_ip(self
, ip_string
):
1177 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1178 first_ip
= ip_string
.split(";")[0] if ip_string
else ""
1181 def get_vnfr_ip(self
, engine
, vnfr_index_wanted
):
1182 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1183 ip
= self
.vnfr_ip_list
.get(vnfr_index_wanted
, "")
1185 return self
.get_first_ip(ip
)
1186 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1187 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1188 vnfr_index_wanted
, self
.ns_id
), headers_json
, None,
1189 200, r_header_json
, "json")
1192 vnfr_data
= r
.json()
1193 if not (vnfr_data
and vnfr_data
[0]):
1195 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1196 ip_list
= vnfr_data
[0].get("ip-address", "")
1198 self
.vnfr_ip_list
[vnfr_index_wanted
] = ip_list
1199 ip
= self
.get_first_ip(ip_list
)
1203 class TestDeployHackfestCirros(TestDeploy
):
1204 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
1208 self
.test_name
= "CIRROS"
1209 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1210 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1211 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1212 self
.users
= {'1': "cirros", '2': "cirros"}
1213 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1215 def terminate(self
, engine
):
1216 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1218 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1219 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1221 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1223 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1224 headers_yaml
, None, 204, None, 0)
1226 # check all it is deleted
1227 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1229 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1230 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1235 if not isinstance(nslcmops
, list) or nslcmops
:
1236 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1239 class TestDeployHackfest1(TestDeploy
):
1240 description
= "Load and deploy Hackfest_1_vnfd example"
1244 self
.test_name
= "HACKFEST1-"
1245 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1246 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1247 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1248 # self.users = {'1': "cirros", '2': "cirros"}
1249 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1252 class TestDeployHackfestCirrosScaling(TestDeploy
):
1253 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1257 self
.test_name
= "CIRROS-SCALE"
1258 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1259 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1260 # Modify VNFD to add scaling and count=2
1261 self
.descriptor_edit
= {
1264 "$id: 'cirros_vnfd-VM'": {"count": 2}
1266 "scaling-group-descriptor": [{
1267 "name": "scale_cirros",
1268 "max-instance-count": 2,
1270 "vdu-id-ref": "cirros_vnfd-VM",
1277 def additional_operations(self
, engine
, test_osm
, manual_check
):
1280 # 2 perform scale out twice
1281 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1282 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1283 for i
in range(0, 2):
1284 engine
.test("Execute scale action over NS", "POST",
1285 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1286 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1287 nslcmop2_scale_out
= engine
.last_id
1288 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1290 input('NS scale out done. Check that two more vdus are there')
1291 # TODO check automatic
1293 # 2 perform scale in
1294 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1295 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1296 for i
in range(0, 2):
1297 engine
.test("Execute scale IN action over NS", "POST",
1298 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1299 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1300 nslcmop2_scale_in
= engine
.last_id
1301 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1303 input('NS scale in done. Check that two less vdus are there')
1304 # TODO check automatic
1306 # perform scale in that must fail as reached limit
1307 engine
.test("Execute scale IN out of limit action over NS", "POST",
1308 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1309 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1310 nslcmop2_scale_in
= engine
.last_id
1311 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1314 class TestDeployIpMac(TestDeploy
):
1315 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1319 self
.test_name
= "SetIpMac"
1320 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1321 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1322 self
.descriptor_url
= \
1323 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1324 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1325 self
.users
= {'1': "osm", '2': "osm"}
1326 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1329 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1330 # super().run(engine, test_osm, manual_check, test_params)
1331 # run again setting IPs with instantiate parameters
1332 instantiation_params
= {
1335 "member-vnf-index": "1",
1338 "name": "internal_vld1", # net_internal
1340 "ip-version": "ipv4",
1341 "subnet-address": "10.9.8.0/24",
1342 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1344 "internal-connection-point": [
1347 "ip-address": "10.9.8.2",
1351 "ip-address": "10.9.8.3",
1362 # "name": "iface11",
1363 # "floating-ip-required": True,
1367 "mac-address": "52:33:44:55:66:13"
1376 "ip-address": "10.31.31.22",
1377 "mac-address": "52:33:44:55:66:21"
1386 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1389 class TestDeployHackfest4(TestDeploy
):
1390 description
= "Load and deploy Hackfest 4 example."
1394 self
.test_name
= "HACKFEST4-"
1395 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1396 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1397 self
.uses_configuration
= True
1398 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1399 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1400 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1401 # Modify VNFD to add scaling
1402 # self.descriptor_edit = {
1404 # 'vnf-configuration': {
1405 # 'config-primitive': [{
1408 # 'name': 'filename',
1409 # 'data-type': 'STRING',
1410 # 'default-value': '/home/ubuntu/touched'
1414 # 'scaling-group-descriptor': [{
1415 # 'name': 'scale_dataVM',
1416 # 'scaling-policy': [{
1417 # 'threshold-time': 0,
1418 # 'name': 'auto_cpu_util_above_threshold',
1419 # 'scaling-type': 'automatic',
1420 # 'scaling-criteria': [{
1421 # 'name': 'cpu_util_above_threshold',
1422 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1423 # 'scale-out-relational-operation': 'GE',
1424 # 'scale-in-threshold': 15,
1425 # 'scale-out-threshold': 60,
1426 # 'scale-in-relational-operation': 'LE'
1428 # 'cooldown-time': 60
1430 # 'max-instance-count': 10,
1431 # 'scaling-config-action': [
1432 # {'vnf-config-primitive-name-ref': 'touch',
1433 # 'trigger': 'post-scale-out'},
1434 # {'vnf-config-primitive-name-ref': 'touch',
1435 # 'trigger': 'pre-scale-in'}
1438 # 'vdu-id-ref': 'dataVM',
1446 class TestDeployHackfest3Charmed(TestDeploy
):
1447 description
= "Load and deploy Hackfest 3charmed_ns example"
1451 self
.test_name
= "HACKFEST3-"
1452 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1453 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1454 self
.uses_configuration
= True
1455 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1456 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1457 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1458 self
.descriptor_edit
= {
1459 "vnfd0": yaml
.safe_load(
1462 terminate-config-primitive:
1467 value: '/home/ubuntu/last-touch1'
1472 value: '/home/ubuntu/last-touch3'
1477 value: '/home/ubuntu/last-touch2'
1481 def additional_operations(self
, engine
, test_osm
, manual_check
):
1485 vnfr_index_selected
= "2"
1486 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1487 engine
.test("Exec service primitive over NS", "POST",
1488 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1489 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1490 nslcmop2_action
= engine
.last_id
1491 # Wait until status is Ok
1492 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1493 vnfr_ip
= self
.get_vnfr_ip(engine
, vnfr_index_selected
)
1496 "NS service primitive has been executed."
1497 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1500 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1501 self
.test_ns(engine
, test_osm
, commands
=commands
)
1503 # # 2 perform scale out
1504 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1505 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1506 # engine.test("Execute scale action over NS", "POST",
1507 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1508 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1509 # nslcmop2_scale_out = engine.last_id
1510 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1512 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1513 # # TODO check automatic
1515 # # 2 perform scale in
1516 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1517 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1518 # engine.test("Execute scale action over NS", "POST",
1519 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1520 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1521 # nslcmop2_scale_in = engine.last_id
1522 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1524 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1525 # # TODO check automatic
1528 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1529 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1530 "ids and member-vnf-index."
1534 self
.test_name
= "HACKFEST3v2-"
1535 self
.qforce
= "?FORCE=True"
1536 self
.descriptor_edit
= {
1540 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1544 "vnf-configuration": None,
1545 "connection-point": {
1549 "short-name": "pdu-mgmt"
1553 "mgmt-interface": {"cp": "pdu-mgmt"},
1554 "description": "A vnf single vdu to be used as PDU",
1558 "id": "pdu_internal",
1559 "name": "pdu_internal",
1560 "internal-connection-point": {"$[1]": None},
1561 "short-name": "pdu_internal",
1567 # Modify NSD accordingly
1569 "constituent-vnfd": {
1570 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1573 "description": "A nsd to deploy the vnf to act as as PDU",
1575 "name": "nsd-as-pdu",
1576 "short-name": "nsd-as-pdu",
1581 "short-name": "mgmt_pdu",
1582 "vnfd-connection-point-ref": {
1584 "vnfd-connection-point-ref": "pdu-mgmt",
1585 "vnfd-id-ref": "vdu-as-pdu",
1597 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1598 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1602 self
.test_name
= "HACKFEST3v3-"
1603 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1604 self
.descriptor_edit
= {
1607 scaling-group-descriptor:
1608 - name: "scale_dataVM"
1609 max-instance-count: 10
1611 - name: "auto_cpu_util_above_threshold"
1612 scaling-type: "automatic"
1616 - name: "cpu_util_above_threshold"
1617 scale-in-threshold: 15
1618 scale-in-relational-operation: "LE"
1619 scale-out-threshold: 60
1620 scale-out-relational-operation: "GE"
1621 vnf-monitoring-param-ref: "monitor1"
1623 - vdu-id-ref: dataVM
1625 scaling-config-action:
1626 - trigger: post-scale-out
1627 vnf-config-primitive-name-ref: touch
1628 - trigger: pre-scale-in
1629 vnf-config-primitive-name-ref: touch
1633 - id: "dataVM_cpu_util"
1634 nfvi-metric: "cpu_utilization"
1639 aggregation-type: AVERAGE
1640 vdu-monitoring-param:
1642 vdu-monitoring-param-ref: "dataVM_cpu_util"
1644 initial-config-primitive:
1648 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1653 default-value: "<touch_filename2>"
1657 "additionalParamsForVnf": [
1658 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1659 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1660 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1661 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1665 def additional_operations(self
, engine
, test_osm
, manual_check
):
1666 super().additional_operations(engine
, test_osm
, manual_check
)
1670 # 2 perform scale out
1671 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1672 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1673 engine
.test("Execute scale action over NS", "POST",
1674 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1675 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1676 nslcmop2_scale_out
= engine
.last_id
1677 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1679 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1681 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1682 self
.test_ns(engine
, test_osm
, commands
=commands
)
1683 # TODO check automatic connection to scaled VM
1685 # 2 perform scale in
1686 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1687 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1688 engine
.test("Execute scale action over NS", "POST",
1689 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1690 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1691 nslcmop2_scale_in
= engine
.last_id
1692 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1694 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1695 # TODO check automatic
1698 class TestDeploySimpleCharm(TestDeploy
):
1699 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1703 self
.test_name
= "HACKFEST-SIMPLE"
1704 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1705 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1706 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1707 self
.uses_configuration
= True
1708 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1709 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1710 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1713 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1714 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1719 self
.test_name
= "HACKFEST-SIMPLE2-"
1720 self
.qforce
= "?FORCE=True"
1721 self
.descriptor_edit
= {
1723 "id": "hackfest.simplecharm.vnf"
1727 "id": "hackfest.simplecharm.ns",
1728 "constituent-vnfd": {
1729 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1730 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1734 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1735 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1736 "$[1]": {"member-vnf-index-ref": "$2",
1737 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1740 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1741 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1742 "$[1]": {"member-vnf-index-ref": "$2",
1743 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1750 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1751 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1755 self
.test_name
= "SingleVDU"
1756 self
.qforce
= "?FORCE=True"
1757 self
.descriptor_edit
= {
1758 # Modify VNFD to remove one VDU
1762 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1766 "vnf-configuration": None,
1767 "connection-point": {
1771 "short-name": "pdu-mgmt"
1775 "mgmt-interface": {"cp": "pdu-mgmt"},
1776 "description": "A vnf single vdu to be used as PDU",
1780 "id": "pdu_internal",
1781 "name": "pdu_internal",
1782 "internal-connection-point": {"$[1]": None},
1783 "short-name": "pdu_internal",
1789 # Modify NSD accordingly
1791 "constituent-vnfd": {
1792 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1795 "description": "A nsd to deploy the vnf to act as as PDU",
1797 "name": "nsd-as-pdu",
1798 "short-name": "nsd-as-pdu",
1803 "short-name": "mgmt_pdu",
1804 "vnfd-connection-point-ref": {
1806 "vnfd-connection-point-ref": "pdu-mgmt",
1807 "vnfd-id-ref": "vdu-as-pdu",
1819 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1820 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1824 self
.test_name
= "HNFD"
1825 self
.pduDeploy
= TestDeploySingleVdu()
1826 self
.pdu_interface_0
= {}
1827 self
.pdu_interface_1
= {}
1830 # self.vnf_to_pdu = """
1833 # pdu-type: PDU-TYPE-1
1838 # name: pdu-iface-internal
1840 # description: HFND, one PDU + One VDU
1846 self
.pdu_descriptor
= {
1848 "type": "PDU-TYPE-1",
1849 "vim_accounts": "to-override",
1852 "name": "mgmt-iface",
1855 "ip-address": "to override",
1856 "mac-address": "mac_address",
1857 "vim-network-name": "mgmt",
1860 "name": "pdu-iface-internal",
1863 "ip-address": "to override",
1864 "mac-address": "mac_address",
1865 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1869 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1871 self
.descriptor_edit
= {
1875 "short-name": "hfn1",
1878 "pdu-type": "PDU-TYPE-1",
1880 "$[0]": {"name": "mgmt-iface"},
1881 "$[1]": {"name": "pdu-iface-internal"},
1887 "constituent-vnfd": {
1888 "$[1]": {"vnfd-id-ref": "hfnd1"}
1891 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1892 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1897 def create_descriptors(self
, engine
):
1898 super().create_descriptors(engine
)
1901 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1902 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1903 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1904 # TODO get vim-network-name from vnfr.vld.name
1905 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1906 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1907 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1908 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1909 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1910 201, r_header_yaml
, "yaml")
1911 self
.pdu_id
= engine
.last_id
1913 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1914 engine
.get_autorization()
1915 engine
.set_test_name(self
.test_name
)
1916 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1918 # create real VIM if not exist
1919 self
.vim_id
= engine
.get_create_vim(test_osm
)
1921 self
.pduDeploy
.create_descriptors(engine
)
1922 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1923 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1925 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1927 self
.pduDeploy
.test_ns(engine
, test_osm
)
1930 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1931 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1932 200, r_header_json
, "json")
1935 vnfr_data
= r
.json()
1938 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1939 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1940 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1941 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1942 if not self
.pdu_interface_0
["ip-address"]:
1943 raise TestException("Vnfr has not managment ip address")
1945 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1946 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1947 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1948 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1950 self
.create_descriptors(engine
)
1952 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1953 "vimAccountId": self
.vim_id
}
1954 if test_params
and test_params
.get("ns-config"):
1955 if isinstance(test_params
["ns-config"], str):
1956 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1958 ns_data
.update(test_params
["ns-config"])
1960 self
.instantiate(engine
, ns_data
)
1962 input('NS has been deployed. Perform manual check and press enter to resume')
1964 self
.test_ns(engine
, test_osm
)
1965 self
.additional_operations(engine
, test_osm
, manual_check
)
1966 self
.terminate(engine
)
1967 self
.pduDeploy
.terminate(engine
)
1968 self
.delete_descriptors(engine
)
1969 self
.pduDeploy
.delete_descriptors(engine
)
1971 def delete_descriptors(self
, engine
):
1972 super().delete_descriptors(engine
)
1974 engine
.test("Delete PDU SOL005", "DELETE",
1975 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1976 headers_yaml
, None, 204, None, 0)
1979 class TestDescriptors
:
1980 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1981 vnfd_empty
= """vnfd:vnfd-catalog:
1987 vnfd_prova
= """vnfd:vnfd-catalog:
1999 - external-connection-point-ref: cp_0h8m
2008 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
2009 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
2010 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2014 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2015 engine
.set_test_name("Descriptors")
2016 engine
.get_autorization()
2017 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2018 if not os
.path
.exists(temp_dir
):
2019 os
.makedirs(temp_dir
)
2022 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
2023 filename_path
= temp_dir
+ filename
2024 if not os
.path
.exists(filename_path
):
2025 with
open(filename_path
, "wb") as file:
2026 response
= requests
.get(self
.descriptor_url
+ filename
)
2027 if response
.status_code
>= 300:
2028 raise TestException("Error downloading descriptor from '{}': {}".format(
2029 self
.descriptor_url
+ filename
, response
.status_code
))
2030 file.write(response
.content
)
2032 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
2033 nsd_filename_path
= temp_dir
+ self
.nsd_filename
2035 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2036 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
2037 self
.vnfd_id
= engine
.last_id
2040 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2041 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2043 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
2044 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
2045 "@b" + vnfd_filename_path
, 204, None, 0)
2047 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2048 "vdu.0.interface.1.internal-connection-point-ref=internal",
2049 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2050 # Detection of duplicated VLD names in VNF Descriptors
2051 # URL: internal-vld=[
2052 # {id: internal1, name: internal, type:ELAN,
2053 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2054 # {id: internal2, name: internal, type:ELAN,
2055 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2057 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2058 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2059 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2060 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2061 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2063 for query
in queries
:
2064 engine
.test("Upload invalid VNFD ", "PUT",
2065 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
2066 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
2069 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2070 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2072 # get vnfd descriptor
2073 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2074 headers_yaml
, None, 200, r_header_yaml
, "yaml")
2076 # get vnfd file descriptor
2077 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
2078 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
2079 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2081 # get vnfd zip file package
2082 engine
.test("Get VNFD zip package", "GET",
2083 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
2084 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
2085 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2088 engine
.test("Get VNFD artifact package", "GET",
2089 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
2090 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
2091 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2093 # nsd CREATE AND UPLOAD in one step:
2094 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
2095 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
2096 self
.nsd_id
= engine
.last_id
2098 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2099 for query
in queries
:
2100 engine
.test("Upload invalid NSD ", "PUT",
2101 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
2102 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
2104 # get nsd descriptor
2105 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
2106 None, 200, r_header_yaml
, "yaml")
2108 # get nsd file descriptor
2109 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
2110 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
2111 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2113 # get nsd zip file package
2114 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
2115 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
2116 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2119 engine
.test("Get NSD artifact package", "GET",
2120 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
2121 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
2122 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2125 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2126 headers_yaml
, None, 409, None, None)
2128 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
2129 headers_yaml
, None, 204, None, 0)
2132 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
2136 class TestNetSliceTemplates
:
2137 description
= "Upload a NST to OSM"
2140 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2141 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2142 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2143 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2144 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2146 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2148 engine
.set_test_name("NST step ")
2149 engine
.get_autorization()
2150 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2151 if not os
.path
.exists(temp_dir
):
2152 os
.makedirs(temp_dir
)
2155 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2156 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2157 self
.vnfd_edge_id
= engine
.last_id
2159 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2160 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2161 self
.vnfd_middle_id
= engine
.last_id
2164 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2165 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2166 self
.nsd_edge_id
= engine
.last_id
2168 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2169 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2170 self
.nsd_middle_id
= engine
.last_id
2173 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2174 201, r_headers_yaml_location_nst
, "yaml")
2175 nst_id
= engine
.last_id
2177 # nstd SHOW OSM format
2178 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2179 200, r_header_json
, "json")
2182 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2186 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2187 headers_json
, None, 204, None, 0)
2189 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2193 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2194 headers_yaml
, None, 204, None, 0)
2196 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2197 headers_yaml
, None, 204, None, 0)
2200 class TestNetSliceInstances
:
2203 1. Populate databases with VNFD, NSD, NST with the following scenario
2204 +-----------------management-----------------+
2206 +--+---+ +----+----+ +---+--+
2208 | edge +---data1----+ middle +---data2-----+ edge |
2210 +------+ +---------+ +------+
2213 3. Instantiate NSI-1
2215 5. Instantiate NSI-2
2216 Manual check - Are 2 slices instantiated correctly?
2217 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2218 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2221 Manual check - Is slice NSI-1 deleted correctly?
2222 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2224 9. Instantiate NSI-3
2225 Manual check - Is slice NSI-3 instantiated correctly?
2226 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2231 Manual check - All cleaned correctly?
2232 NSI-2 and NSI-3 were terminated and deleted
2233 14. Cleanup database
2236 description
= "Upload a NST to OSM"
2240 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2241 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2242 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2243 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2244 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2246 def create_slice(self
, engine
, nsi_data
, name
):
2247 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2248 r
= engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances",
2249 headers_yaml
, ns_data_text
, (201, 202),
2250 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2253 def instantiate_slice(self
, engine
, nsi_data
, nsi_id
, name
):
2254 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2255 engine
.test(name
, "POST",
2256 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id
), headers_yaml
, ns_data_text
,
2257 (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2259 def terminate_slice(self
, engine
, nsi_id
, name
):
2260 engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id
),
2261 headers_yaml
, None, (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2263 def delete_slice(self
, engine
, nsi_id
, name
):
2264 engine
.test(name
, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id
), headers_yaml
, None,
2267 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2269 engine
.set_test_name("NSI")
2270 engine
.get_autorization()
2273 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2274 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2275 self
.vnfd_edge_id
= engine
.last_id
2277 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2278 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2279 self
.vnfd_middle_id
= engine
.last_id
2282 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2283 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2284 self
.nsd_edge_id
= engine
.last_id
2286 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2287 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2288 self
.nsd_middle_id
= engine
.last_id
2291 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2292 201, r_headers_yaml_location_nst
, "yaml")
2293 nst_id
= engine
.last_id
2295 self
.vim_id
= engine
.get_create_vim(test_osm
)
2298 ns_data
= {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2299 r
= self
.create_slice(engine
, ns_data
, "Create NSI-1 step 1")
2302 self
.nsi_id1
= engine
.last_id
2305 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id1
, "Instantiate NSI-1 step 2")
2306 nsilcmop_id1
= engine
.last_id
2310 engine
.wait_operation_ready("nsi", nsilcmop_id1
, timeout_deploy
)
2313 ns_data
= {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2314 r
= self
.create_slice(engine
, ns_data
, "Create NSI-2 step 1")
2317 self
.nsi_id2
= engine
.last_id
2320 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id2
, "Instantiate NSI-2 step 2")
2321 nsilcmop_id2
= engine
.last_id
2325 engine
.wait_operation_ready("nsi", nsilcmop_id2
, timeout_deploy
)
2328 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2332 self
.terminate_slice(engine
, self
.nsi_id1
, "Terminate NSI-1")
2333 nsilcmop1_id
= engine
.last_id
2335 # Wait terminate NSI-1
2336 engine
.wait_operation_ready("nsi", nsilcmop1_id
, timeout_deploy
)
2339 self
.delete_slice(engine
, self
.nsi_id1
, "Delete NS")
2342 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2345 ns_data
= {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2346 r
= self
.create_slice(engine
, ns_data
, "Create NSI-3 step 1")
2350 self
.nsi_id3
= engine
.last_id
2353 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id3
, "Instantiate NSI-3 step 2")
2354 nsilcmop_id3
= engine
.last_id
2356 # Wait Instantiate NSI-3
2358 engine
.wait_operation_ready("nsi", nsilcmop_id3
, timeout_deploy
)
2361 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2365 self
.terminate_slice(engine
, self
.nsi_id2
, "Terminate NSI-2")
2366 nsilcmop2_id
= engine
.last_id
2368 # Wait terminate NSI-2
2369 engine
.wait_operation_ready("nsi", nsilcmop2_id
, timeout_deploy
)
2372 self
.delete_slice(engine
, self
.nsi_id2
, "DELETE NSI-2")
2376 self
. terminate_slice(engine
, self
.nsi_id3
, "Terminate NSI-3")
2377 nsilcmop3_id
= engine
.last_id
2379 # Wait terminate NSI-3
2380 engine
.wait_operation_ready("nsi", nsilcmop3_id
, timeout_deploy
)
2383 self
.delete_slice(engine
, self
.nsi_id3
, "DELETE NSI-3")
2386 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2389 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2393 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2394 headers_json
, None, 204, None, 0)
2396 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2400 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2401 headers_yaml
, None, 204, None, 0)
2403 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2404 headers_yaml
, None, 204, None, 0)
2407 if __name__
== "__main__":
2411 # Disable warnings from self-signed certificates.
2412 requests
.packages
.urllib3
.disable_warnings()
2414 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
2415 logger
= logging
.getLogger('NBI')
2416 # load parameters and configuration
2417 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
2418 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2419 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2420 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2421 url
= "https://localhost:9999/osm"
2422 user
= password
= project
= "admin"
2424 manual_check
= False
2429 "NonAuthorized": TestNonAuthorized
,
2430 "FakeVIM": TestFakeVim
,
2431 "Users-Projects": TestUsersProjects
,
2432 "Projects-Descriptors": TestProjectsDescriptors
,
2433 "VIM-SDN": TestVIMSDN
,
2434 "Deploy-Custom": TestDeploy
,
2435 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
2436 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
2437 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
2438 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2
,
2439 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3
,
2440 "Deploy-Hackfest-4": TestDeployHackfest4
,
2441 "Deploy-CirrosMacIp": TestDeployIpMac
,
2442 "Descriptors": TestDescriptors
,
2443 "Deploy-Hackfest1": TestDeployHackfest1
,
2444 # "Deploy-MultiVIM": TestDeployMultiVIM,
2445 "Deploy-SingleVdu": TestDeploySingleVdu
,
2446 "Deploy-Hnfd": TestDeployHnfd
,
2447 "Upload-Slice-Template": TestNetSliceTemplates
,
2448 "Deploy-Slice-Instance": TestNetSliceInstances
,
2449 "Deploy-SimpleCharm": TestDeploySimpleCharm
,
2450 "Deploy-SimpleCharm2": TestDeploySimpleCharm2
,
2456 # print("parameter:", o, a)
2457 if o
== "--version":
2458 print("test version " + __version__
+ ' ' + version_date
)
2461 for test
, test_class
in sorted(test_classes
.items()):
2462 print("{:32} {}".format(test
+ ":", test_class
.description
))
2464 elif o
in ("-v", "--verbose"):
2466 elif o
== "no-verbose":
2468 elif o
in ("-h", "--help"):
2471 elif o
== "--test-osm":
2473 elif o
== "--manual-check":
2477 elif o
in ("-u", "--user"):
2479 elif o
in ("-p", "--password"):
2481 elif o
== "--project":
2483 elif o
== "--fail-fast":
2486 # print("asdfadf", o, a, a.split(","))
2487 for _test
in a
.split(","):
2488 if _test
not in test_classes
:
2489 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
2492 test_to_do
.append(_test
)
2493 elif o
== "--params":
2494 param_key
, _
, param_value
= a
.partition("=")
2495 text_index
= len(test_to_do
)
2496 if text_index
not in test_params
:
2497 test_params
[text_index
] = {}
2498 test_params
[text_index
][param_key
] = param_value
2499 elif o
== "--insecure":
2501 elif o
== "--timeout":
2503 elif o
== "--timeout-deploy":
2504 timeout_deploy
= int(a
)
2505 elif o
== "--timeout-configure":
2506 timeout_configure
= int(a
)
2508 assert False, "Unhandled option"
2510 logger
.setLevel(logging
.WARNING
)
2512 logger
.setLevel(logging
.DEBUG
)
2514 logger
.setLevel(logging
.ERROR
)
2516 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
2517 # print("tests to do:", test_to_do)
2520 for test
in test_to_do
:
2521 if fail_fast
and test_rest
.failed_tests
:
2524 test_class
= test_classes
[test
]
2525 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
2527 for test
, test_class
in sorted(test_classes
.items()):
2528 if fail_fast
and test_rest
.failed_tests
:
2530 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
2531 test_rest
.print_results()
2532 exit(1 if test_rest
.failed_tests
else 0)
2534 except TestException
as e
:
2535 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
2537 except getopt
.GetoptError
as e
:
2539 print(e
, file=sys
.stderr
)
2541 except Exception as e
:
2542 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)