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"}
89 # test ones authorized
90 test_authorized_list
= (
91 ("AU1", "Invalid vnfd id", "GET", "/vnfpkgm/v1/vnf_packages/non-existing-id",
92 headers_json
, None, 404, r_header_json
, "json"),
93 ("AU2", "Invalid nsd id", "GET", "/nsd/v1/ns_descriptors/non-existing-id",
94 headers_yaml
, None, 404, r_header_yaml
, "yaml"),
95 ("AU3", "Invalid nsd id", "DELETE", "/nsd/v1/ns_descriptors_content/non-existing-id",
96 headers_yaml
, None, 404, r_header_yaml
, "yaml"),
98 timeout
= 120 # general timeout
99 timeout_deploy
= 60*10 # timeout for NS deploying without charms
100 timeout_configure
= 60*20 # timeout for NS deploying and configuring
103 class TestException(Exception):
108 def __init__(self
, url_base
, header_base
=None, verify
=False, user
="admin", password
="admin", project
="admin"):
109 self
.url_base
= url_base
110 if header_base
is None:
111 self
.header_base
= {}
113 self
.header_base
= header_base
.copy()
114 self
.s
= requests
.session()
115 self
.s
.headers
= self
.header_base
119 self
.password
= password
120 self
.project
= project
122 # contains ID of tests obtained from Location response header. "" key contains last obtained id
124 self
.test_name
= None
125 self
.step
= 0 # number of subtest under test
126 self
.passed_tests
= 0
127 self
.failed_tests
= 0
129 def set_test_name(self
, test_name
):
130 self
.test_name
= test_name
134 def set_header(self
, header
):
135 self
.s
.headers
.update(header
)
137 def set_tet_name(self
, test_name
):
138 self
.test_name
= test_name
140 def unset_header(self
, key
):
141 if key
in self
.s
.headers
:
142 del self
.s
.headers
[key
]
144 def test(self
, description
, method
, url
, headers
, payload
, expected_codes
, expected_headers
,
145 expected_payload
, store_file
=None, pooling
=False):
147 Performs an http request and check http code response. Exit if different than allowed. It get the returned id
148 that can be used by following test in the URL with {name} where name is the name of the test
149 :param description: description of the test
150 :param method: HTTP method: GET,PUT,POST,DELETE,...
151 :param url: complete URL or relative URL
152 :param headers: request headers to add to the base headers
153 :param payload: Can be a dict, transformed to json, a text or a file if starts with '@'
154 :param expected_codes: expected response codes, can be int, int tuple or int range
155 :param expected_headers: expected response headers, dict with key values
156 :param expected_payload: expected payload, 0 if empty, 'yaml', 'json', 'text', 'zip', 'octet-stream'
157 :param store_file: filename to store content
158 :param pooling: if True do not count neither log this test. Because a pooling is done with many equal requests
159 :return: requests response
164 self
.s
= requests
.session()
168 elif not url
.startswith("http"):
169 url
= self
.url_base
+ url
171 # replace url <> with the last ID
172 url
= url
.replace("<>", self
.last_id
)
174 if isinstance(payload
, str):
175 if payload
.startswith("@"):
177 file_name
= payload
[1:]
178 if payload
.startswith("@b"):
180 file_name
= payload
[2:]
181 with
open(file_name
, mode
) as f
:
183 elif isinstance(payload
, dict):
184 payload
= json
.dumps(payload
)
187 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, method
, url
)
188 logger
.warning(test_description
)
191 if expected_payload
in ("zip", "octet-string") or store_file
:
196 r
= getattr(self
.s
, method
.lower())(url
, data
=payload
, headers
=headers
, verify
=self
.verify
,
199 except requests
.exceptions
.ConnectionError
as e
:
202 logger
.error("Exception {}. Retrying".format(e
))
205 if expected_payload
in ("zip", "octet-string") or store_file
:
206 logger
.debug("RX {}".format(r
.status_code
))
208 logger
.debug("RX {}: {}".format(r
.status_code
, r
.text
))
212 if isinstance(expected_codes
, int):
213 expected_codes
= (expected_codes
,)
214 if r
.status_code
not in expected_codes
:
216 "Got status {}. Expected {}. {}".format(r
.status_code
, expected_codes
, r
.text
))
219 for header_key
, header_val
in expected_headers
.items():
220 if header_key
.lower() not in r
.headers
:
221 raise TestException("Header {} not present".format(header_key
))
222 if header_val
and header_val
.lower() not in r
.headers
[header_key
]:
223 raise TestException("Header {} does not contain {} but {}".format(header_key
, header_val
,
224 r
.headers
[header_key
]))
226 if expected_payload
is not None:
227 if expected_payload
== 0 and len(r
.content
) > 0:
228 raise TestException("Expected empty payload")
229 elif expected_payload
== "json":
232 except Exception as e
:
233 raise TestException("Expected json response payload, but got Exception {}".format(e
))
234 elif expected_payload
== "yaml":
236 yaml
.safe_load(r
.text
)
237 except Exception as e
:
238 raise TestException("Expected yaml response payload, but got Exception {}".format(e
))
239 elif expected_payload
in ("zip", "octet-string"):
240 if len(r
.content
) == 0:
241 raise TestException("Expected some response payload, but got empty")
243 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
244 # for tarinfo in tar:
245 # tarname = tarinfo.name
247 # except Exception as e:
248 # raise TestException("Expected zip response payload, but got Exception {}".format(e))
249 elif expected_payload
== "text":
250 if len(r
.content
) == 0:
251 raise TestException("Expected some response payload, but got empty")
254 with
open(store_file
, 'wb') as fd
:
255 for chunk
in r
.iter_content(chunk_size
=128):
258 location
= r
.headers
.get("Location")
260 _id
= location
[location
.rfind("/") + 1:]
262 self
.last_id
= str(_id
)
264 self
.passed_tests
+= 1
266 except TestException
as e
:
267 self
.failed_tests
+= 1
271 r_status_code
= r
.status_code
273 logger
.error("{} \nRX code{}: {}".format(e
, r_status_code
, r_text
))
278 logger
.error("Cannot open file {}: {}".format(store_file
, e
))
280 logger
.error("Exception: {}".format(e
), exc_info
=True)
281 self
.failed_tests
+= 1
284 except requests
.exceptions
.RequestException
as e
:
285 logger
.error("Exception: {}".format(e
))
287 def get_autorization(self
): # user=None, password=None, project=None):
288 if self
.token
: # and self.user == user and self.password == password and self.project == project:
291 # self.password = password
292 # self.project = project
293 r
= self
.test("Obtain token", "POST", "/admin/v1/tokens", headers_json
,
294 {"username": self
.user
, "password": self
.password
, "project_id": self
.project
},
295 (200, 201), r_header_json
, "json")
299 self
.token
= response
["id"]
300 self
.set_header({"Authorization": "Bearer {}".format(self
.token
)})
302 def remove_authorization(self
):
304 self
.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self
.token
), headers_json
,
305 None, (200, 201, 204), None, None)
307 self
.unset_header("Authorization")
309 def get_create_vim(self
, test_osm
):
312 self
.get_autorization()
314 vim_name
= os
.environ
.get("OSMNBITEST_VIM_NAME")
317 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
321 r
= self
.test("Get VIM ID", "GET", "/admin/v1/vim_accounts?name={}".format(vim_name
), headers_json
,
322 None, 200, r_header_json
, "json")
327 return vims
[0]["_id"]
330 # check needed environ parameters:
331 if not os
.environ
.get("OSMNBITEST_VIM_URL") or not os
.environ
.get("OSMNBITEST_VIM_TENANT"):
332 raise TestException("Env OSMNBITEST_VIM_URL and OSMNBITEST_VIM_TENANT are needed for create a real VIM"
333 " to deploy on whit the --test-osm option")
334 vim_data
= "{{schema_version: '1.0', name: '{}', vim_type: {}, vim_url: '{}', vim_tenant_name: '{}', "\
335 "vim_user: {}, vim_password: {}".format(vim_name
,
336 os
.environ
.get("OSMNBITEST_VIM_TYPE", "openstack"),
337 os
.environ
.get("OSMNBITEST_VIM_URL"),
338 os
.environ
.get("OSMNBITEST_VIM_TENANT"),
339 os
.environ
.get("OSMNBITEST_VIM_USER"),
340 os
.environ
.get("OSMNBITEST_VIM_PASSWORD"))
341 if os
.environ
.get("OSMNBITEST_VIM_CONFIG"):
342 vim_data
+= " ,config: {}".format(os
.environ
.get("OSMNBITEST_VIM_CONFIG"))
345 vim_data
= "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
346 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
347 self
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml
, vim_data
,
348 (201), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
351 def print_results(self
):
352 print("\n\n\n--------------------------------------------")
353 print("TEST RESULTS: Total: {}, Passed: {}, Failed: {}".format(self
.passed_tests
+ self
.failed_tests
,
354 self
.passed_tests
, self
.failed_tests
))
355 print("--------------------------------------------")
357 def wait_until_delete(self
, url_op
, timeout_delete
):
359 Make a pooling until topic is not present, because of deleted
361 :param timeout_delete:
364 description
= "Wait to topic being deleted"
365 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
366 logger
.warning(test_description
)
369 wait
= timeout_delete
371 r
= self
.test(description
, "GET", url_op
, headers_yaml
, None, (200, 404), None, r_header_yaml
, "yaml",
375 if r
.status_code
== 404:
376 self
.passed_tests
+= 1
378 elif r
.status_code
== 200:
382 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete
))
383 self
.failed_tests
+= 1
385 def wait_operation_ready(self
, ns_nsi
, opp_id
, timeout
, expected_fail
=False):
387 Wait until nslcmop or nsilcmop finished
388 :param ns_nsi: "ns" o "nsi"
389 :param opp_id: Id o fthe operation
391 :param expected_fail:
392 :return: None. Updates passed/failed_tests
395 url_op
= "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id
)
397 url_op
= "/nsilcm/v1/nsi_lcm_op_occs/{}".format(opp_id
)
398 description
= "Wait to {} lcm operation complete".format(ns_nsi
)
399 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
400 logger
.warning(test_description
)
404 r
= self
.test(description
, "GET", url_op
, headers_json
, None,
405 200, r_header_json
, "json", pooling
=True)
409 if "COMPLETED" in nslcmop
["operationState"]:
411 logger
.error("NS terminate has success, expecting failing: {}".format(nslcmop
["detailed-status"]))
412 self
.failed_tests
+= 1
414 self
.passed_tests
+= 1
416 elif "FAILED" in nslcmop
["operationState"]:
417 if not expected_fail
:
418 logger
.error("NS terminate has failed: {}".format(nslcmop
["detailed-status"]))
419 self
.failed_tests
+= 1
421 self
.passed_tests
+= 1
424 print(".", end
="", file=stderr
)
428 self
.failed_tests
+= 1
429 logger
.error("NS instantiate is not terminate after {} seconds".format(timeout
))
431 print("", file=stderr
)
434 class TestNonAuthorized
:
435 description
= "Test invalid URLs. methods and no authorization"
438 def run(engine
, test_osm
, manual_check
, test_params
=None):
439 engine
.set_test_name("NonAuth")
440 engine
.remove_authorization()
441 test_not_authorized_list
= (
442 ("Invalid token", "GET", "/admin/v1/users", headers_json
, None, 401, r_header_json
, "json"),
443 ("Invalid URL", "POST", "/admin/v1/nonexist", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
444 ("Invalid version", "DELETE", "/admin/v2/users", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
446 for t
in test_not_authorized_list
:
450 class TestUsersProjects
:
451 description
= "test project and user creation"
454 def run(engine
, test_osm
, manual_check
, test_params
=None):
455 engine
.set_test_name("UserProject")
456 engine
.get_autorization()
457 engine
.test("Create project non admin", "POST", "/admin/v1/projects", headers_json
, {"name": "P1"},
458 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
459 engine
.test("Create project admin", "POST", "/admin/v1/projects", headers_json
,
460 {"name": "Padmin", "admin": True}, (201, 204),
461 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
462 engine
.test("Create project bad format", "POST", "/admin/v1/projects", headers_json
, {"name": 1}, 422,
463 r_header_json
, "json")
464 engine
.test("Create user with bad project", "POST", "/admin/v1/users", headers_json
,
465 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 409,
466 r_header_json
, "json")
467 engine
.test("Create user with bad project and force", "POST", "/admin/v1/users?FORCE=True", headers_json
,
468 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 201,
469 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
470 engine
.test("Create user 2", "POST", "/admin/v1/users", headers_json
,
471 {"username": "U2", "projects": ["P1"], "password": "pw2"}, 201,
472 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
473 engine
.test("Edit user U1, delete P2 project", "PATCH", "/admin/v1/users/U1", headers_json
,
474 {"projects": {"$'P2'": None}}, 204, None, None)
475 res
= engine
.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
476 headers_json
, None, 200, None, json
)
480 expected_projects
= ["P1", "Padmin"]
481 if u1
["projects"] != expected_projects
:
482 logger
.error("User content projects '{}' different than expected '{}'. Edition has not done"
483 " properly".format(u1
["projects"], expected_projects
))
484 engine
.failed_tests
+= 1
486 engine
.test("Edit user U1, set Padmin as default project", "PUT", "/admin/v1/users/U1", headers_json
,
487 {"projects": {"$'Padmin'": None, "$+[0]": "Padmin"}}, 204, None, None)
488 res
= engine
.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
489 headers_json
, None, 200, None, json
)
493 expected_projects
= ["Padmin", "P1"]
494 if u1
["projects"] != expected_projects
:
495 logger
.error("User content projects '{}' different than expected '{}'. Edition has not done"
496 " properly".format(u1
["projects"], expected_projects
))
497 engine
.failed_tests
+= 1
499 engine
.test("Edit user U1, change password", "PATCH", "/admin/v1/users/U1", headers_json
,
500 {"password": "pw1_new"}, 204, None, None)
502 engine
.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json
,
503 {"project_id": "P1"}, 401, r_header_json
, "json")
505 res
= engine
.test("Change to user U1 project P1", "POST", "/admin/v1/tokens", headers_json
,
506 {"username": "U1", "password": "pw1_new", "project_id": "P1"}, (200, 201),
507 r_header_json
, "json")
509 response
= res
.json()
510 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
512 engine
.test("Edit user projects non admin", "PUT", "/admin/v1/users/U1", headers_json
,
513 {"projects": {"$'P1'": None}}, 401, r_header_json
, "json")
514 engine
.test("Add new project non admin", "POST", "/admin/v1/projects", headers_json
,
515 {"name": "P2"}, 401, r_header_json
, "json")
516 engine
.test("Add new user non admin", "POST", "/admin/v1/users", headers_json
,
517 {"username": "U3", "projects": ["P1"], "password": "pw3"}, 401,
518 r_header_json
, "json")
520 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
521 {"project_id": "Padmin"}, (200, 201), r_header_json
, "json")
523 response
= res
.json()
524 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
526 engine
.test("Add new project admin", "POST", "/admin/v1/projects", headers_json
, {"name": "P2"},
527 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
528 engine
.test("Add new user U3 admin", "POST", "/admin/v1/users",
529 headers_json
, {"username": "U3", "projects": ["P2"], "password": "pw3"}, (201, 204),
530 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
531 engine
.test("Edit user projects admin", "PUT", "/admin/v1/users/U3", headers_json
,
532 {"projects": ["P2"]}, 204, None, None)
534 engine
.test("Delete project P2 conflict", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 409,
535 r_header_json
, "json")
536 engine
.test("Delete project P2 forcing", "DELETE", "/admin/v1/projects/P2?FORCE=True", headers_json
,
537 None, 204, None, None)
539 engine
.test("Delete user U1. Conflict deleting own user", "DELETE", "/admin/v1/users/U1", headers_json
,
540 None, 409, r_header_json
, "json")
541 engine
.test("Delete user U2", "DELETE", "/admin/v1/users/U2", headers_json
, None, 204, None, None)
542 engine
.test("Delete user U3", "DELETE", "/admin/v1/users/U3", headers_json
, None, 204, None, None)
544 engine
.remove_authorization() # To force get authorization
545 engine
.get_autorization()
546 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
547 engine
.test("Delete project P1", "DELETE", "/admin/v1/projects/P1", headers_json
, None, 204, None, None)
548 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204,
553 description
= "Creates/edit/delete fake VIMs and SDN controllers"
557 "schema_version": "1.0",
558 "schema_type": "No idea",
560 "description": "Descriptor name",
561 "vim_type": "openstack",
562 "vim_url": "http://localhost:/vim",
563 "vim_tenant_name": "vimTenant",
565 "vim_password": "password",
566 "config": {"config_param": 1}
570 "description": "sdn-description",
571 "dpid": "50:50:52:54:00:94:21:21",
572 "ip": "192.168.15.17",
574 "type": "opendaylight",
579 self
.port_mapping
= [
580 {"compute_node": "compute node 1",
581 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
582 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
584 {"compute_node": "compute node 2",
585 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
586 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
590 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
592 vim_bad
= self
.vim
.copy()
595 engine
.set_test_name("FakeVim")
596 engine
.get_autorization()
597 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 204),
598 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
599 vim_id
= engine
.last_id
600 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
601 vim_bad
, 422, None, headers_json
)
602 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
603 409, None, headers_json
)
604 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
606 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
607 r_header_yaml
, "yaml")
610 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
612 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
613 404, r_header_yaml
, "yaml")
615 # delete and wait until is really deleted
616 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
618 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
621 class TestVIMSDN(TestFakeVim
):
622 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
625 TestFakeVim
.__init
__(self
)
627 "schema_version": "1.0",
628 "schema_type": "No idea",
630 "description": "Descriptor name",
632 "wim_url": "http://localhost:/wim",
634 "password": "password",
635 "config": {"config_param": 1}
638 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
639 engine
.set_test_name("VimSdn")
640 engine
.get_autorization()
642 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 204),
643 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
644 sdnc_id
= engine
.last_id
647 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
651 self
.vim
["config"]["sdn-controller"] = sdnc_id
652 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
653 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 204, 201),
654 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
656 vim_id
= engine
.last_id
657 self
.port_mapping
[0]["compute_node"] = "compute node XX"
658 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
659 {"config": {"sdn-port-mapping": self
.port_mapping
}}, 204, None, None)
660 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
661 {"config": {"sdn-port-mapping": None}}, 204, None, None)
663 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 204, 201),
664 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
665 wim_id
= engine
.last_id
669 engine
.test("Delete VIM remove port-mapping", "DELETE",
670 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
671 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
674 engine
.test("Delete WIM", "DELETE",
675 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
676 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
677 None, 404, r_header_yaml
, "yaml")
678 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
679 404, r_header_yaml
, "yaml")
680 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
681 None, 404, r_header_yaml
, "yaml")
684 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
685 # delete and wait until is really deleted
686 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
687 headers_json
, None, (202, 201, 204), None, 0)
688 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
689 (202, 201, 204), None, 0)
690 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
691 headers_json
, None, (202, 201, 204), None, 0)
692 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
693 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
694 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
698 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
701 self
.test_name
= "DEPLOY"
706 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
707 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
708 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
709 self
.descriptor_edit
= None
710 self
.uses_configuration
= False
717 self
.ns_params
= None
719 def create_descriptors(self
, engine
):
720 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
721 if not os
.path
.exists(temp_dir
):
722 os
.makedirs(temp_dir
)
723 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
724 if "/" in vnfd_filename
:
725 vnfd_filename_path
= vnfd_filename
726 if not os
.path
.exists(vnfd_filename_path
):
727 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
729 vnfd_filename_path
= temp_dir
+ vnfd_filename
730 if not os
.path
.exists(vnfd_filename_path
):
731 with
open(vnfd_filename_path
, "wb") as file:
732 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
733 if response
.status_code
>= 300:
734 raise TestException("Error downloading descriptor from '{}': {}".format(
735 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
736 file.write(response
.content
)
737 if vnfd_filename_path
.endswith(".yaml"):
738 headers
= headers_yaml
740 headers
= headers_zip_yaml
741 if randint(0, 1) == 0:
742 # vnfd CREATE AND UPLOAD in one step:
743 engine
.test("Onboard VNFD in one step", "POST",
744 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
745 r_headers_yaml_location_vnfd
,
747 self
.vnfds_id
.append(engine
.last_id
)
749 # vnfd CREATE AND UPLOAD ZIP
750 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
751 headers_json
, None, 201,
752 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
753 self
.vnfds_id
.append(engine
.last_id
)
754 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
755 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
756 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
758 if self
.descriptor_edit
:
759 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
761 engine
.test("Edit VNFD ", "PATCH",
762 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
763 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
765 if "/" in self
.nsd_filename
:
766 nsd_filename_path
= self
.nsd_filename
767 if not os
.path
.exists(nsd_filename_path
):
768 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
770 nsd_filename_path
= temp_dir
+ self
.nsd_filename
771 if not os
.path
.exists(nsd_filename_path
):
772 with
open(nsd_filename_path
, "wb") as file:
773 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
774 if response
.status_code
>= 300:
775 raise TestException("Error downloading descriptor from '{}': {}".format(
776 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
777 file.write(response
.content
)
778 if nsd_filename_path
.endswith(".yaml"):
779 headers
= headers_yaml
781 headers
= headers_zip_yaml
783 if randint(0, 1) == 0:
784 # nsd CREATE AND UPLOAD in one step:
785 engine
.test("Onboard NSD in one step", "POST",
786 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
787 r_headers_yaml_location_nsd
, yaml
)
788 self
.nsd_id
= engine
.last_id
790 # nsd CREATE AND UPLOAD ZIP
791 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
792 headers_json
, None, 201,
793 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
794 self
.nsd_id
= engine
.last_id
795 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
796 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
797 headers
, "@b" + nsd_filename_path
, 204, None, 0)
799 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
801 engine
.test("Edit NSD ", "PATCH",
802 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
803 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
805 def delete_descriptors(self
, engine
):
807 engine
.test("Delete NSSD SOL005", "DELETE",
808 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
809 headers_yaml
, None, 204, None, 0)
810 for vnfd_id
in self
.vnfds_id
:
811 engine
.test("Delete VNFD SOL005", "DELETE",
812 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
814 def instantiate(self
, engine
, ns_data
):
815 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
816 # create NS Two steps
817 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
818 headers_yaml
, ns_data_text
, 201,
819 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
822 self
.ns_id
= engine
.last_id
823 engine
.test("Instantiate NS step 2", "POST",
824 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
825 201, r_headers_yaml_location_nslcmop
, "yaml")
826 nslcmop_id
= engine
.last_id
829 # Wait until status is Ok
830 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
831 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
833 def terminate(self
, engine
):
836 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
837 None, 201, r_headers_yaml_location_nslcmop
, "yaml")
838 nslcmop2_id
= engine
.last_id
839 # Wait until status is Ok
840 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
842 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
845 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
846 headers_yaml
, None, 204, None, 0)
848 # check all it is deleted
849 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
851 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
852 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
857 if not isinstance(nslcmops
, list) or nslcmops
:
858 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
860 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
862 r
= engine
.test("GET VNFR IDs", "GET",
863 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
864 200, r_header_json
, "json")
869 vnfr_list
= ns_data
['constituent-vnfr-ref']
871 _commands
= commands
if commands
is not None else self
.commands
872 _users
= users
if users
is not None else self
.users
873 _passwds
= passwds
if passwds
is not None else self
.passwords
874 _keys
= keys
if keys
is not None else self
.keys
875 _timeout
= timeout
if timeout
!= 0 else self
.timeout
877 for vnfr_id
in vnfr_list
:
878 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
879 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
880 200, r_header_json
, "json")
885 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
886 if not _commands
.get(vnf_index
):
888 if vnfr_data
.get("ip-address"):
889 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
890 vnfr_data
['ip-address'])
892 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
893 logger
.warning(test_description
)
894 while _timeout
>= time
:
895 result
, message
= self
.do_checks([vnfr_data
["ip-address"]],
896 vnf_index
=vnfr_data
["member-vnf-index-ref"],
897 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
898 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
900 engine
.passed_tests
+= 1
901 logger
.debug(message
)
907 engine
.failed_tests
+= 1
908 logger
.error(message
)
912 engine
.failed_tests
+= 1
913 logger
.error(message
)
915 engine
.failed_tests
+= 1
916 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnfr_id
))
918 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
921 from pssh
.clients
import ParallelSSHClient
922 from pssh
.utils
import load_private_key
923 from ssh2
import exceptions
as ssh2Exception
924 except ImportError as e
:
925 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
926 "parallel-ssh urllib3': {}".format(e
))
927 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
928 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
930 p_host
= os
.environ
.get("PROXY_HOST")
931 p_user
= os
.environ
.get("PROXY_USER")
932 p_password
= os
.environ
.get("PROXY_PASSWD")
935 pkey
= load_private_key(key
)
939 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
940 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
942 output
= client
.run_command(cmd
)
944 if output
[ip
[0]].exit_code
:
945 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
946 "\n".join(output
[ip
[0]].stderr
))
948 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
949 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
950 ssh2Exception
.SocketRecvError
) as e
:
951 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
952 except Exception as e
:
953 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
955 def additional_operations(self
, engine
, test_osm
, manual_check
):
958 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
959 engine
.set_test_name(self
.test_name
)
960 engine
.get_autorization()
961 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
963 if "vnfd-files" in test_params
:
964 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
965 if "nsd-file" in test_params
:
966 self
.nsd_filename
= test_params
["nsd-file"]
967 if test_params
.get("ns-name"):
968 nsname
= test_params
["ns-name"]
969 self
.create_descriptors(engine
)
971 # create real VIM if not exist
972 self
.vim_id
= engine
.get_create_vim(test_osm
)
973 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
974 "vimAccountId": self
.vim_id
}
976 ns_data
.update(self
.ns_params
)
977 if test_params
and test_params
.get("ns-config"):
978 if isinstance(test_params
["ns-config"], str):
979 ns_data
.update(yaml
.load(test_params
["ns-config"]))
981 ns_data
.update(test_params
["ns-config"])
982 self
.instantiate(engine
, ns_data
)
985 input('NS has been deployed. Perform manual check and press enter to resume')
986 if test_osm
and self
.commands
:
987 self
.test_ns(engine
, test_osm
)
988 self
.additional_operations(engine
, test_osm
, manual_check
)
989 self
.terminate(engine
)
990 self
.delete_descriptors(engine
)
993 class TestDeployHackfestCirros(TestDeploy
):
994 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
998 self
.test_name
= "CIRROS"
999 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1000 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1001 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1002 self
.users
= {'1': "cirros", '2': "cirros"}
1003 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1005 def terminate(self
, engine
):
1006 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1008 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1009 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1011 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1013 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1014 headers_yaml
, None, 204, None, 0)
1016 # check all it is deleted
1017 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1019 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1020 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1025 if not isinstance(nslcmops
, list) or nslcmops
:
1026 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1029 class TestDeployHackfest1(TestDeploy
):
1030 description
= "Load and deploy Hackfest_1_vnfd example"
1034 self
.test_name
= "HACKFEST1-"
1035 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1036 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1037 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1038 # self.users = {'1': "cirros", '2': "cirros"}
1039 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1042 class TestDeployHackfestCirrosScaling(TestDeploy
):
1043 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1047 self
.test_name
= "CIRROS-SCALE"
1048 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1049 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1051 def create_descriptors(self
, engine
):
1052 super().create_descriptors(engine
)
1053 # Modify VNFD to add scaling and count=2
1054 self
.descriptor_edit
= {
1057 "$id: 'cirros_vnfd-VM'": {"count": 2}
1059 "scaling-group-descriptor": [{
1060 "name": "scale_cirros",
1061 "max-instance-count": 2,
1063 "vdu-id-ref": "cirros_vnfd-VM",
1070 def additional_operations(self
, engine
, test_osm
, manual_check
):
1073 # 2 perform scale out twice
1074 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1075 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1076 for i
in range(0, 2):
1077 engine
.test("Execute scale action over NS", "POST",
1078 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1079 201, r_headers_yaml_location_nslcmop
, "yaml")
1080 nslcmop2_scale_out
= engine
.last_id
1081 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1083 input('NS scale out done. Check that two more vdus are there')
1084 # TODO check automatic
1086 # 2 perform scale in
1087 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1088 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1089 for i
in range(0, 2):
1090 engine
.test("Execute scale IN action over NS", "POST",
1091 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1092 201, r_headers_yaml_location_nslcmop
, "yaml")
1093 nslcmop2_scale_in
= engine
.last_id
1094 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1096 input('NS scale in done. Check that two less vdus are there')
1097 # TODO check automatic
1099 # perform scale in that must fail as reached limit
1100 engine
.test("Execute scale IN out of limit action over NS", "POST",
1101 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1102 201, r_headers_yaml_location_nslcmop
, "yaml")
1103 nslcmop2_scale_in
= engine
.last_id
1104 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1107 class TestDeployIpMac(TestDeploy
):
1108 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1112 self
.test_name
= "SetIpMac"
1113 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1114 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1115 self
.descriptor_url
= \
1116 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1117 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1118 self
.users
= {'1': "osm", '2': "osm"}
1119 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1122 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1123 # super().run(engine, test_osm, manual_check, test_params)
1124 # run again setting IPs with instantiate parameters
1125 instantiation_params
= {
1128 "member-vnf-index": "1",
1131 "name": "internal_vld1", # net_internal
1133 "ip-version": "ipv4",
1134 "subnet-address": "10.9.8.0/24",
1135 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1137 "internal-connection-point": [
1140 "ip-address": "10.9.8.2",
1144 "ip-address": "10.9.8.3",
1155 # "name": "iface11",
1156 # "floating-ip-required": True,
1160 "mac-address": "52:33:44:55:66:13"
1169 "ip-address": "10.31.31.22",
1170 "mac-address": "52:33:44:55:66:21"
1179 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1182 class TestDeployHackfest4(TestDeploy
):
1183 description
= "Load and deploy Hackfest 4 example."
1187 self
.test_name
= "HACKFEST4-"
1188 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1189 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1190 self
.uses_configuration
= True
1191 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1192 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1193 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1194 # Modify VNFD to add scaling
1195 # self.descriptor_edit = {
1197 # 'vnf-configuration': {
1198 # 'config-primitive': [{
1201 # 'name': 'filename',
1202 # 'data-type': 'STRING',
1203 # 'default-value': '/home/ubuntu/touched'
1207 # 'scaling-group-descriptor': [{
1208 # 'name': 'scale_dataVM',
1209 # 'scaling-policy': [{
1210 # 'threshold-time': 0,
1211 # 'name': 'auto_cpu_util_above_threshold',
1212 # 'scaling-type': 'automatic',
1213 # 'scaling-criteria': [{
1214 # 'name': 'cpu_util_above_threshold',
1215 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1216 # 'scale-out-relational-operation': 'GE',
1217 # 'scale-in-threshold': 15,
1218 # 'scale-out-threshold': 60,
1219 # 'scale-in-relational-operation': 'LE'
1221 # 'cooldown-time': 60
1223 # 'max-instance-count': 10,
1224 # 'scaling-config-action': [
1225 # {'vnf-config-primitive-name-ref': 'touch',
1226 # 'trigger': 'post-scale-out'},
1227 # {'vnf-config-primitive-name-ref': 'touch',
1228 # 'trigger': 'pre-scale-in'}
1231 # 'vdu-id-ref': 'dataVM',
1239 class TestDeployHackfest3Charmed(TestDeploy
):
1240 description
= "Load and deploy Hackfest 3charmed_ns example"
1244 self
.test_name
= "HACKFEST3-"
1245 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1246 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1247 self
.uses_configuration
= True
1248 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1249 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1250 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1252 def additional_operations(self
, engine
, test_osm
, manual_check
):
1256 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1257 engine
.test("Exec service primitive over NS", "POST",
1258 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1259 201, r_headers_yaml_location_nslcmop
, "yaml")
1260 nslcmop2_action
= engine
.last_id
1261 # Wait until status is Ok
1262 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1264 input('NS service primitive has been executed. Check that file /home/ubuntu/OSMTESTNBI is present at '
1267 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1268 self
.test_ns(engine
, test_osm
, commands
=commands
)
1270 # # 2 perform scale out
1271 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1272 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1273 # engine.test("Execute scale action over NS", "POST",
1274 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1275 # 201, r_headers_yaml_location_nslcmop, "yaml")
1276 # nslcmop2_scale_out = engine.last_id
1277 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1279 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1280 # # TODO check automatic
1282 # # 2 perform scale in
1283 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1284 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1285 # engine.test("Execute scale action over NS", "POST",
1286 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1287 # 201, r_headers_yaml_location_nslcmop, "yaml")
1288 # nslcmop2_scale_in = engine.last_id
1289 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1291 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1292 # # TODO check automatic
1295 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1296 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1297 "ids and member-vnf-index."
1301 self
.test_name
= "HACKFEST3v2-"
1302 self
.qforce
= "?FORCE=True"
1303 self
.descriptor_edit
= {
1307 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1311 "vnf-configuration": None,
1312 "connection-point": {
1316 "short-name": "pdu-mgmt"
1320 "mgmt-interface": {"cp": "pdu-mgmt"},
1321 "description": "A vnf single vdu to be used as PDU",
1325 "id": "pdu_internal",
1326 "name": "pdu_internal",
1327 "internal-connection-point": {"$[1]": None},
1328 "short-name": "pdu_internal",
1334 # Modify NSD accordingly
1336 "constituent-vnfd": {
1337 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1340 "description": "A nsd to deploy the vnf to act as as PDU",
1342 "name": "nsd-as-pdu",
1343 "short-name": "nsd-as-pdu",
1348 "short-name": "mgmt_pdu",
1349 "vnfd-connection-point-ref": {
1351 "vnfd-connection-point-ref": "pdu-mgmt",
1352 "vnfd-id-ref": "vdu-as-pdu",
1364 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1365 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1369 self
.test_name
= "HACKFEST3v3-"
1370 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1371 self
.descriptor_edit
= {
1374 scaling-group-descriptor:
1375 - name: "scale_dataVM"
1376 max-instance-count: 10
1378 - name: "auto_cpu_util_above_threshold"
1379 scaling-type: "automatic"
1383 - name: "cpu_util_above_threshold"
1384 scale-in-threshold: 15
1385 scale-in-relational-operation: "LE"
1386 scale-out-threshold: 60
1387 scale-out-relational-operation: "GE"
1388 vnf-monitoring-param-ref: "monitor1"
1390 - vdu-id-ref: dataVM
1392 scaling-config-action:
1393 - trigger: post-scale-out
1394 vnf-config-primitive-name-ref: touch
1395 - trigger: pre-scale-in
1396 vnf-config-primitive-name-ref: touch
1400 - id: "dataVM_cpu_util"
1401 nfvi-metric: "cpu_utilization"
1406 aggregation-type: AVERAGE
1407 vdu-monitoring-param:
1409 vdu-monitoring-param-ref: "dataVM_cpu_util"
1411 initial-config-primitive:
1415 value: "<touch-filename>" # default-value: /home/ubuntu/first-touch
1420 default-value: "<touch-filename2>"
1424 "additionalParamsForVnf": [
1425 {"member-vnf-index": "1", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-1",
1426 "touch-filename2": "/home/ubuntu/second-touch-1"}},
1427 {"member-vnf-index": "2", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-2",
1428 "touch-filename2": "/home/ubuntu/second-touch-2"}},
1432 def additional_operations(self
, engine
, test_osm
, manual_check
):
1433 super().additional_operations(engine
, test_osm
, manual_check
)
1437 # 2 perform scale out
1438 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1439 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1440 engine
.test("Execute scale action over NS", "POST",
1441 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1442 201, r_headers_yaml_location_nslcmop
, "yaml")
1443 nslcmop2_scale_out
= engine
.last_id
1444 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1446 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1448 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1449 self
.test_ns(engine
, test_osm
, commands
=commands
)
1450 # TODO check automatic connection to scaled VM
1452 # 2 perform scale in
1453 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1454 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1455 engine
.test("Execute scale action over NS", "POST",
1456 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1457 201, r_headers_yaml_location_nslcmop
, "yaml")
1458 nslcmop2_scale_in
= engine
.last_id
1459 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1461 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1462 # TODO check automatic
1465 class TestDeploySimpleCharm(TestDeploy
):
1466 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1470 self
.test_name
= "HACKFEST-SIMPLE"
1471 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1472 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1473 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1474 self
.uses_configuration
= True
1475 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1476 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1477 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1480 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1481 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1486 self
.test_name
= "HACKFEST-SIMPLE2-"
1487 self
.qforce
= "?FORCE=True"
1488 self
.descriptor_edit
= {
1490 "id": "hackfest.simplecharm.vnf"
1494 "id": "hackfest.simplecharm.ns",
1495 "constituent-vnfd": {
1496 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1497 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1501 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1502 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1503 "$[1]": {"member-vnf-index-ref": "$2",
1504 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1507 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1508 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1509 "$[1]": {"member-vnf-index-ref": "$2",
1510 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1517 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1518 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1522 self
.test_name
= "SingleVDU"
1523 self
.qforce
= "?FORCE=True"
1524 self
.descriptor_edit
= {
1525 # Modify VNFD to remove one VDU
1529 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1533 "vnf-configuration": None,
1534 "connection-point": {
1538 "short-name": "pdu-mgmt"
1542 "mgmt-interface": {"cp": "pdu-mgmt"},
1543 "description": "A vnf single vdu to be used as PDU",
1547 "id": "pdu_internal",
1548 "name": "pdu_internal",
1549 "internal-connection-point": {"$[1]": None},
1550 "short-name": "pdu_internal",
1556 # Modify NSD accordingly
1558 "constituent-vnfd": {
1559 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1562 "description": "A nsd to deploy the vnf to act as as PDU",
1564 "name": "nsd-as-pdu",
1565 "short-name": "nsd-as-pdu",
1570 "short-name": "mgmt_pdu",
1571 "vnfd-connection-point-ref": {
1573 "vnfd-connection-point-ref": "pdu-mgmt",
1574 "vnfd-id-ref": "vdu-as-pdu",
1586 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1587 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1591 self
.test_name
= "HNFD"
1592 self
.pduDeploy
= TestDeploySingleVdu()
1593 self
.pdu_interface_0
= {}
1594 self
.pdu_interface_1
= {}
1597 # self.vnf_to_pdu = """
1600 # pdu-type: PDU-TYPE-1
1605 # name: pdu-iface-internal
1607 # description: HFND, one PDU + One VDU
1613 self
.pdu_descriptor
= {
1615 "type": "PDU-TYPE-1",
1616 "vim_accounts": "to-override",
1619 "name": "mgmt-iface",
1622 "ip-address": "to override",
1623 "mac-address": "mac_address",
1624 "vim-network-name": "mgmt",
1627 "name": "pdu-iface-internal",
1630 "ip-address": "to override",
1631 "mac-address": "mac_address",
1632 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1636 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1638 self
.descriptor_edit
= {
1642 "short-name": "hfn1",
1645 "pdu-type": "PDU-TYPE-1",
1647 "$[0]": {"name": "mgmt-iface"},
1648 "$[1]": {"name": "pdu-iface-internal"},
1654 "constituent-vnfd": {
1655 "$[1]": {"vnfd-id-ref": "hfnd1"}
1658 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1659 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1664 def create_descriptors(self
, engine
):
1665 super().create_descriptors(engine
)
1668 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1669 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1670 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1671 # TODO get vim-network-name from vnfr.vld.name
1672 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1673 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1674 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1675 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1676 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1677 201, r_header_yaml
, "yaml")
1678 self
.pdu_id
= engine
.last_id
1680 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1681 engine
.get_autorization()
1682 engine
.set_test_name(self
.test_name
)
1683 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1685 # create real VIM if not exist
1686 self
.vim_id
= engine
.get_create_vim(test_osm
)
1688 self
.pduDeploy
.create_descriptors(engine
)
1689 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1690 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1692 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1694 self
.pduDeploy
.test_ns(engine
, test_osm
)
1697 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1698 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1699 200, r_header_json
, "json")
1702 vnfr_data
= r
.json()
1705 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1706 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1707 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1708 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1709 if not self
.pdu_interface_0
["ip-address"]:
1710 raise TestException("Vnfr has not managment ip address")
1712 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1713 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1714 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1715 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1717 self
.create_descriptors(engine
)
1719 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1720 "vimAccountId": self
.vim_id
}
1721 if test_params
and test_params
.get("ns-config"):
1722 if isinstance(test_params
["ns-config"], str):
1723 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1725 ns_data
.update(test_params
["ns-config"])
1727 self
.instantiate(engine
, ns_data
)
1729 input('NS has been deployed. Perform manual check and press enter to resume')
1731 self
.test_ns(engine
, test_osm
)
1732 self
.additional_operations(engine
, test_osm
, manual_check
)
1733 self
.terminate(engine
)
1734 self
.pduDeploy
.terminate(engine
)
1735 self
.delete_descriptors(engine
)
1736 self
.pduDeploy
.delete_descriptors(engine
)
1738 def delete_descriptors(self
, engine
):
1739 super().delete_descriptors(engine
)
1741 engine
.test("Delete PDU SOL005", "DELETE",
1742 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1743 headers_yaml
, None, 204, None, 0)
1746 class TestDescriptors
:
1747 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1750 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
1751 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1752 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1755 self
.vnfd_empty
= """vnfd:vnfd-catalog:
1761 self
.vnfd_prova
= """vnfd:vnfd-catalog:
1773 - external-connection-point-ref: cp_0h8m
1781 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1782 engine
.set_test_name("Descriptors")
1783 engine
.get_autorization()
1784 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
1785 if not os
.path
.exists(temp_dir
):
1786 os
.makedirs(temp_dir
)
1789 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
1790 filename_path
= temp_dir
+ filename
1791 if not os
.path
.exists(filename_path
):
1792 with
open(filename_path
, "wb") as file:
1793 response
= requests
.get(self
.descriptor_url
+ filename
)
1794 if response
.status_code
>= 300:
1795 raise TestException("Error downloading descriptor from '{}': {}".format(
1796 self
.descriptor_url
+ filename
, response
.status_code
))
1797 file.write(response
.content
)
1799 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
1800 nsd_filename_path
= temp_dir
+ self
.nsd_filename
1802 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
1803 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
1804 self
.vnfd_id
= engine
.last_id
1807 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1808 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1810 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
1811 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
1812 "@b" + vnfd_filename_path
, 204, None, 0)
1814 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
1815 "vdu.0.interface.1.internal-connection-point-ref=internal",
1816 "internal-vld.0.internal-connection-point.0.id-ref=internal"]
1817 for query
in queries
:
1818 engine
.test("Upload invalid VNFD ", "PUT",
1819 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
1820 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
1823 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1824 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1826 # get vnfd descriptor
1827 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1828 headers_yaml
, None, 200, r_header_yaml
, "yaml")
1830 # get vnfd file descriptor
1831 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
1832 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
1833 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
1835 # get vnfd zip file package
1836 engine
.test("Get VNFD zip package", "GET",
1837 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
1838 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
1839 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
1842 engine
.test("Get VNFD artifact package", "GET",
1843 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
1844 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
1845 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
1847 # nsd CREATE AND UPLOAD in one step:
1848 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
1849 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
1850 self
.nsd_id
= engine
.last_id
1852 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
1853 for query
in queries
:
1854 engine
.test("Upload invalid NSD ", "PUT",
1855 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
1856 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
1858 # get nsd descriptor
1859 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
1860 None, 200, r_header_yaml
, "yaml")
1862 # get nsd file descriptor
1863 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
1864 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
1865 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
1867 # get nsd zip file package
1868 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
1869 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
1870 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
1873 engine
.test("Get NSD artifact package", "GET",
1874 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
1875 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
1876 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
1879 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1880 headers_yaml
, None, 409, None, None)
1882 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
1883 headers_yaml
, None, 204, None, 0)
1886 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
1890 class TestNetSliceTemplates
:
1891 description
= "Upload a NST to OSM"
1894 self
.nst_filenames
= ("@./cirros_slice/cirros_slice_vld.yaml")
1896 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1898 engine
.set_test_name("NST")
1899 engine
.get_autorization()
1900 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
1901 201, r_headers_yaml_location_nst
, "yaml")
1902 nst_id
= engine
.last_id
1904 # nstd SHOW OSM format
1905 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1906 200, r_header_json
, "json")
1909 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1913 class TestNetSliceInstances
:
1914 description
= "Upload a NST to OSM"
1918 self
.nst_filenames
= ("@./cirros_slice/cirros_slice.yaml")
1920 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1922 engine
.set_test_name("NSI")
1923 engine
.get_autorization()
1924 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
, 201,
1925 r_headers_yaml_location_nst
, "yaml")
1926 nst_id
= engine
.last_id
1929 self
.vim_id
= engine
.get_create_vim(test_osm
)
1931 ns_data
= {"nsiDescription": "default description", "nsiName": "my_slice", "nstId": nst_id
,
1932 "vimAccountId": self
.vim_id
}
1933 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
1935 engine
.test("Onboard NSI", "POST", "/nsilcm/v1/netslice_instances_content", headers_yaml
, ns_data_text
, 201,
1936 r_headers_yaml_location_nst
, "yaml")
1937 nsi_id
= engine
.last_id
1939 # TODO: Improve the wait with a polling if NSI was deployed
1944 engine
.test("Wait until NSI is deployed", "GET", "/nsilcm/v1/netslice_instances_content/{}".format(nsi_id
),
1945 headers_json
, None, 200, r_header_json
, "json")
1948 engine
.test("Delete NSI", "DELETE", "/nsilcm/v1/netslice_instances_content/{}".format(nsi_id
), headers_json
,
1949 None, 202, r_header_json
, "json")
1954 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1958 if __name__
== "__main__":
1962 # Disable warnings from self-signed certificates.
1963 requests
.packages
.urllib3
.disable_warnings()
1965 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
1966 logger
= logging
.getLogger('NBI')
1967 # load parameters and configuration
1968 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
1969 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
1970 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
1971 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
1972 url
= "https://localhost:9999/osm"
1973 user
= password
= project
= "admin"
1975 manual_check
= False
1980 "NonAuthorized": TestNonAuthorized
,
1981 "FakeVIM": TestFakeVim
,
1982 "TestUsersProjects": TestUsersProjects
,
1983 "VIM-SDN": TestVIMSDN
,
1984 "Deploy-Custom": TestDeploy
,
1985 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
1986 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
1987 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
1988 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2
,
1989 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3
,
1990 "Deploy-Hackfest-4": TestDeployHackfest4
,
1991 "Deploy-CirrosMacIp": TestDeployIpMac
,
1992 "TestDescriptors": TestDescriptors
,
1993 "TestDeployHackfest1": TestDeployHackfest1
,
1994 # "Deploy-MultiVIM": TestDeployMultiVIM,
1995 "DeploySingleVdu": TestDeploySingleVdu
,
1996 "DeployHnfd": TestDeployHnfd
,
1997 # "Upload-Slice-Template": TestNetSliceTemplates,
1998 # "Deploy-Slice-Instance": TestNetSliceInstances,
1999 "TestDeploySimpleCharm": TestDeploySimpleCharm
,
2000 "TestDeploySimpleCharm2": TestDeploySimpleCharm2
,
2006 # print("parameter:", o, a)
2007 if o
== "--version":
2008 print("test version " + __version__
+ ' ' + version_date
)
2011 for test
, test_class
in sorted(test_classes
.items()):
2012 print("{:32} {}".format(test
+ ":", test_class
.description
))
2014 elif o
in ("-v", "--verbose"):
2016 elif o
== "no-verbose":
2018 elif o
in ("-h", "--help"):
2021 elif o
== "--test-osm":
2023 elif o
== "--manual-check":
2027 elif o
in ("-u", "--user"):
2029 elif o
in ("-p", "--password"):
2031 elif o
== "--project":
2033 elif o
== "--fail-fast":
2036 # print("asdfadf", o, a, a.split(","))
2037 for _test
in a
.split(","):
2038 if _test
not in test_classes
:
2039 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
2042 test_to_do
.append(_test
)
2043 elif o
== "--params":
2044 param_key
, _
, param_value
= a
.partition("=")
2045 text_index
= len(test_to_do
)
2046 if text_index
not in test_params
:
2047 test_params
[text_index
] = {}
2048 test_params
[text_index
][param_key
] = param_value
2049 elif o
== "--insecure":
2051 elif o
== "--timeout":
2053 elif o
== "--timeout-deploy":
2054 timeout_deploy
= int(a
)
2055 elif o
== "--timeout-configure":
2056 timeout_configure
= int(a
)
2058 assert False, "Unhandled option"
2060 logger
.setLevel(logging
.WARNING
)
2062 logger
.setLevel(logging
.DEBUG
)
2064 logger
.setLevel(logging
.ERROR
)
2066 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
2067 # print("tests to do:", test_to_do)
2070 for test
in test_to_do
:
2071 if fail_fast
and test_rest
.failed_tests
:
2074 test_class
= test_classes
[test
]
2075 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
2077 for test
, test_class
in test_classes
.items():
2078 if fail_fast
and test_rest
.failed_tests
:
2080 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
2081 test_rest
.print_results()
2082 exit(1 if test_rest
.failed_tests
else 0)
2084 except TestException
as e
:
2085 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
2087 except getopt
.GetoptError
as e
:
2089 print(e
, file=sys
.stderr
)
2091 except Exception as e
:
2092 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)