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
:
193 r
= getattr(self
.s
, method
.lower())(url
, data
=payload
, headers
=headers
, verify
=self
.verify
, stream
=stream
)
194 if expected_payload
in ("zip", "octet-string") or store_file
:
195 logger
.debug("RX {}".format(r
.status_code
))
197 logger
.debug("RX {}: {}".format(r
.status_code
, r
.text
))
201 if isinstance(expected_codes
, int):
202 expected_codes
= (expected_codes
,)
203 if r
.status_code
not in expected_codes
:
205 "Got status {}. Expected {}. {}".format(r
.status_code
, expected_codes
, r
.text
))
208 for header_key
, header_val
in expected_headers
.items():
209 if header_key
.lower() not in r
.headers
:
210 raise TestException("Header {} not present".format(header_key
))
211 if header_val
and header_val
.lower() not in r
.headers
[header_key
]:
212 raise TestException("Header {} does not contain {} but {}".format(header_key
, header_val
,
213 r
.headers
[header_key
]))
215 if expected_payload
is not None:
216 if expected_payload
== 0 and len(r
.content
) > 0:
217 raise TestException("Expected empty payload")
218 elif expected_payload
== "json":
221 except Exception as e
:
222 raise TestException("Expected json response payload, but got Exception {}".format(e
))
223 elif expected_payload
== "yaml":
225 yaml
.safe_load(r
.text
)
226 except Exception as e
:
227 raise TestException("Expected yaml response payload, but got Exception {}".format(e
))
228 elif expected_payload
in ("zip", "octet-string"):
229 if len(r
.content
) == 0:
230 raise TestException("Expected some response payload, but got empty")
232 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
233 # for tarinfo in tar:
234 # tarname = tarinfo.name
236 # except Exception as e:
237 # raise TestException("Expected zip response payload, but got Exception {}".format(e))
238 elif expected_payload
== "text":
239 if len(r
.content
) == 0:
240 raise TestException("Expected some response payload, but got empty")
243 with
open(store_file
, 'wb') as fd
:
244 for chunk
in r
.iter_content(chunk_size
=128):
247 location
= r
.headers
.get("Location")
249 _id
= location
[location
.rfind("/") + 1:]
251 self
.last_id
= str(_id
)
253 self
.passed_tests
+= 1
255 except TestException
as e
:
256 self
.failed_tests
+= 1
260 r_status_code
= r
.status_code
262 logger
.error("{} \nRX code{}: {}".format(e
, r_status_code
, r_text
))
267 logger
.error("Cannot open file {}: {}".format(store_file
, e
))
269 logger
.error("Exception: {}".format(e
), exc_info
=True)
270 self
.failed_tests
+= 1
274 def get_autorization(self
): # user=None, password=None, project=None):
275 if self
.token
: # and self.user == user and self.password == password and self.project == project:
278 # self.password = password
279 # self.project = project
280 r
= self
.test("Obtain token", "POST", "/admin/v1/tokens", headers_json
,
281 {"username": self
.user
, "password": self
.password
, "project_id": self
.project
},
282 (200, 201), r_header_json
, "json")
286 self
.token
= response
["id"]
287 self
.set_header({"Authorization": "Bearer {}".format(self
.token
)})
289 def remove_authorization(self
):
291 self
.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self
.token
), headers_json
,
292 None, (200, 201, 204), None, None)
294 self
.unset_header("Authorization")
296 def get_create_vim(self
, test_osm
):
299 self
.get_autorization()
301 vim_name
= os
.environ
.get("OSMNBITEST_VIM_NAME")
304 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
308 r
= self
.test("Get VIM ID", "GET", "/admin/v1/vim_accounts?name={}".format(vim_name
), headers_json
,
309 None, 200, r_header_json
, "json")
314 return vims
[0]["_id"]
317 # check needed environ parameters:
318 if not os
.environ
.get("OSMNBITEST_VIM_URL") or not os
.environ
.get("OSMNBITEST_VIM_TENANT"):
319 raise TestException("Env OSMNBITEST_VIM_URL and OSMNBITEST_VIM_TENANT are needed for create a real VIM"
320 " to deploy on whit the --test-osm option")
321 vim_data
= "{{schema_version: '1.0', name: '{}', vim_type: {}, vim_url: '{}', vim_tenant_name: '{}', "\
322 "vim_user: {}, vim_password: {}".format(vim_name
,
323 os
.environ
.get("OSMNBITEST_VIM_TYPE", "openstack"),
324 os
.environ
.get("OSMNBITEST_VIM_URL"),
325 os
.environ
.get("OSMNBITEST_VIM_TENANT"),
326 os
.environ
.get("OSMNBITEST_VIM_USER"),
327 os
.environ
.get("OSMNBITEST_VIM_PASSWORD"))
328 if os
.environ
.get("OSMNBITEST_VIM_CONFIG"):
329 vim_data
+= " ,config: {}".format(os
.environ
.get("OSMNBITEST_VIM_CONFIG"))
332 vim_data
= "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
333 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
334 self
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml
, vim_data
,
335 (201), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
338 def print_results(self
):
339 print("\n\n\n--------------------------------------------")
340 print("TEST RESULTS: Total: {}, Passed: {}, Failed: {}".format(self
.passed_tests
+ self
.failed_tests
,
341 self
.passed_tests
, self
.failed_tests
))
342 print("--------------------------------------------")
344 def wait_until_delete(self
, url_op
, timeout_delete
):
346 Make a pooling until topic is not present, because of deleted
348 :param timeout_delete:
351 description
= "Wait to topic being deleted"
352 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
353 logger
.warning(test_description
)
356 wait
= timeout_delete
358 r
= self
.test(description
, "GET", url_op
, headers_yaml
, None, (200, 404), None, r_header_yaml
, "yaml",
362 if r
.status_code
== 404:
363 self
.passed_tests
+= 1
365 elif r
.status_code
== 200:
369 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete
))
370 self
.failed_tests
+= 1
372 def wait_operation_ready(self
, ns_nsi
, opp_id
, timeout
, expected_fail
=False):
374 Wait until nslcmop or nsilcmop finished
375 :param ns_nsi: "ns" o "nsi"
376 :param opp_id: Id o fthe operation
378 :param expected_fail:
379 :return: None. Updates passed/failed_tests
382 url_op
= "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id
)
384 url_op
= "/nsilcm/v1/nsi_lcm_op_occs/{}".format(opp_id
)
385 description
= "Wait to {} lcm operation complete".format(ns_nsi
)
386 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
387 logger
.warning(test_description
)
391 r
= self
.test(description
, "GET", url_op
, headers_json
, None,
392 200, r_header_json
, "json", pooling
=True)
396 if "COMPLETED" in nslcmop
["operationState"]:
398 logger
.error("NS terminate has success, expecting failing: {}".format(
399 nslcmop
["detailed-status"]))
400 self
.failed_tests
+= 1
402 self
.passed_tests
+= 1
404 elif "FAILED" in nslcmop
["operationState"]:
405 if not expected_fail
:
406 logger
.error("NS terminate has failed: {}".format(nslcmop
["detailed-status"]))
408 self
.passed_tests
+= 1
411 print(".", end
="", file=stderr
)
415 self
.failed_tests
+= 1
416 logger
.error("NS instantiate is not terminate after {} seconds".format(timeout
))
418 print("", file=stderr
)
421 class TestNonAuthorized
:
422 description
= "test invalid URLs. methods and no authorization"
425 def run(engine
, test_osm
, manual_check
, test_params
=None):
426 engine
.set_test_name("NonAuth")
427 engine
.remove_authorization()
428 test_not_authorized_list
= (
429 ("Invalid token", "GET", "/admin/v1/users", headers_json
, None, 401, r_header_json
, "json"),
430 ("Invalid URL", "POST", "/admin/v1/nonexist", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
431 ("Invalid version", "DELETE", "/admin/v2/users", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
433 for t
in test_not_authorized_list
:
437 class TestUsersProjects
:
438 description
= "test project and user creation"
441 def run(engine
, test_osm
, manual_check
, test_params
=None):
442 engine
.set_test_name("UserProject")
443 engine
.get_autorization()
444 engine
.test("Create project non admin", "POST", "/admin/v1/projects", headers_json
, {"name": "P1"},
445 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
446 engine
.test("Create project admin", "POST", "/admin/v1/projects", headers_json
,
447 {"name": "Padmin", "admin": True}, (201, 204),
448 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
449 engine
.test("Create project bad format", "POST", "/admin/v1/projects", headers_json
, {"name": 1}, 422,
450 r_header_json
, "json")
451 engine
.test("Create user with bad project", "POST", "/admin/v1/users", headers_json
,
452 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 409,
453 r_header_json
, "json")
454 engine
.test("Create user with bad project and force", "POST", "/admin/v1/users?FORCE=True", headers_json
,
455 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 201,
456 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
457 engine
.test("Create user 2", "POST", "/admin/v1/users", headers_json
,
458 {"username": "U2", "projects": ["P1"], "password": "pw2"}, 201,
459 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
460 engine
.test("Edit user U1, delete P2 project", "PATCH", "/admin/v1/users/U1", headers_json
,
461 {"projects": {"$'P2'": None}}, 204, None, None)
462 res
= engine
.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
463 headers_json
, None, 200, None, json
)
467 expected_projects
= ["P1", "Padmin"]
468 if u1
["projects"] != expected_projects
:
469 logger
.error("User content projects '{}' different than expected '{}'. Edition has not done"
470 " properly".format(u1
["projects"], expected_projects
))
471 engine
.failed_tests
+= 1
473 engine
.test("Edit user U1, set Padmin as default project", "PUT", "/admin/v1/users/U1", headers_json
,
474 {"projects": {"$'Padmin'": None, "$+[0]": "Padmin"}}, 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
= ["Padmin", "P1"]
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, change password", "PATCH", "/admin/v1/users/U1", headers_json
,
487 {"password": "pw1_new"}, 204, None, None)
489 engine
.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json
,
490 {"project_id": "P1"}, 401, r_header_json
, "json")
492 res
= engine
.test("Change to user U1 project P1", "POST", "/admin/v1/tokens", headers_json
,
493 {"username": "U1", "password": "pw1_new", "project_id": "P1"}, (200, 201),
494 r_header_json
, "json")
496 response
= res
.json()
497 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
499 engine
.test("Edit user projects non admin", "PUT", "/admin/v1/users/U1", headers_json
,
500 {"projects": {"$'P1'": None}}, 401, r_header_json
, "json")
501 engine
.test("Add new project non admin", "POST", "/admin/v1/projects", headers_json
,
502 {"name": "P2"}, 401, r_header_json
, "json")
503 engine
.test("Add new user non admin", "POST", "/admin/v1/users", headers_json
,
504 {"username": "U3", "projects": ["P1"], "password": "pw3"}, 401,
505 r_header_json
, "json")
507 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
508 {"project_id": "Padmin"}, (200, 201), r_header_json
, "json")
510 response
= res
.json()
511 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
513 engine
.test("Add new project admin", "POST", "/admin/v1/projects", headers_json
, {"name": "P2"},
514 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
515 engine
.test("Add new user U3 admin", "POST", "/admin/v1/users",
516 headers_json
, {"username": "U3", "projects": ["P2"], "password": "pw3"}, (201, 204),
517 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
518 engine
.test("Edit user projects admin", "PUT", "/admin/v1/users/U3", headers_json
,
519 {"projects": ["P2"]}, 204, None, None)
521 engine
.test("Delete project P2 conflict", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 409,
522 r_header_json
, "json")
523 engine
.test("Delete project P2 forcing", "DELETE", "/admin/v1/projects/P2?FORCE=True", headers_json
,
524 None, 204, None, None)
526 engine
.test("Delete user U1. Conflict deleting own user", "DELETE", "/admin/v1/users/U1", headers_json
,
527 None, 409, r_header_json
, "json")
528 engine
.test("Delete user U2", "DELETE", "/admin/v1/users/U2", headers_json
, None, 204, None, None)
529 engine
.test("Delete user U3", "DELETE", "/admin/v1/users/U3", headers_json
, None, 204, None, None)
531 engine
.remove_authorization() # To force get authorization
532 engine
.get_autorization()
533 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
534 engine
.test("Delete project P1", "DELETE", "/admin/v1/projects/P1", headers_json
, None, 204, None, None)
535 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204,
540 description
= "Creates/edit/delete fake VIMs and SDN controllers"
544 "schema_version": "1.0",
545 "schema_type": "No idea",
547 "description": "Descriptor name",
548 "vim_type": "openstack",
549 "vim_url": "http://localhost:/vim",
550 "vim_tenant_name": "vimTenant",
552 "vim_password": "password",
553 "config": {"config_param": 1}
557 "description": "sdn-description",
558 "dpid": "50:50:52:54:00:94:21:21",
559 "ip": "192.168.15.17",
561 "type": "opendaylight",
566 self
.port_mapping
= [
567 {"compute_node": "compute node 1",
568 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
569 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
571 {"compute_node": "compute node 2",
572 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
573 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
577 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
579 vim_bad
= self
.vim
.copy()
582 engine
.set_test_name("FakeVim")
583 engine
.get_autorization()
584 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 204),
585 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
586 vim_id
= engine
.last_id
587 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
588 vim_bad
, 422, None, headers_json
)
589 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
590 409, None, headers_json
)
591 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
593 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
594 r_header_yaml
, "yaml")
597 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
599 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
600 404, r_header_yaml
, "yaml")
602 # delete and wait until is really deleted
603 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
605 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
608 class TestVIMSDN(TestFakeVim
):
609 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
612 TestFakeVim
.__init
__(self
)
614 "schema_version": "1.0",
615 "schema_type": "No idea",
617 "description": "Descriptor name",
619 "wim_url": "http://localhost:/wim",
621 "password": "password",
622 "config": {"config_param": 1}
625 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
626 engine
.set_test_name("VimSdn")
627 engine
.get_autorization()
629 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 204),
630 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
631 sdnc_id
= engine
.last_id
634 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
638 self
.vim
["config"]["sdn-controller"] = sdnc_id
639 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
640 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 204, 201),
641 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
643 vim_id
= engine
.last_id
644 self
.port_mapping
[0]["compute_node"] = "compute node XX"
645 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
646 {"config": {"sdn-port-mapping": self
.port_mapping
}}, 204, None, None)
647 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
648 {"config": {"sdn-port-mapping": None}}, 204, None, None)
650 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 204, 201),
651 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
652 wim_id
= engine
.last_id
656 engine
.test("Delete VIM remove port-mapping", "DELETE",
657 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
658 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
661 engine
.test("Delete WIM", "DELETE",
662 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
663 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
664 None, 404, r_header_yaml
, "yaml")
665 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
666 404, r_header_yaml
, "yaml")
667 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
668 None, 404, r_header_yaml
, "yaml")
671 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
672 # delete and wait until is really deleted
673 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
674 headers_json
, None, (202, 201, 204), None, 0)
675 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
676 (202, 201, 204), None, 0)
677 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
678 headers_json
, None, (202, 201, 204), None, 0)
679 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
680 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
681 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
685 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
688 self
.test_name
= "DEPLOY"
693 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
694 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
695 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
696 self
.descriptor_edit
= None
697 self
.uses_configuration
= False
705 def create_descriptors(self
, engine
):
706 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
707 if not os
.path
.exists(temp_dir
):
708 os
.makedirs(temp_dir
)
709 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
710 if "/" in vnfd_filename
:
711 vnfd_filename_path
= vnfd_filename
712 if not os
.path
.exists(vnfd_filename_path
):
713 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
715 vnfd_filename_path
= temp_dir
+ vnfd_filename
716 if not os
.path
.exists(vnfd_filename_path
):
717 with
open(vnfd_filename_path
, "wb") as file:
718 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
719 if response
.status_code
>= 300:
720 raise TestException("Error downloading descriptor from '{}': {}".format(
721 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
722 file.write(response
.content
)
723 if vnfd_filename_path
.endswith(".yaml"):
724 headers
= headers_yaml
726 headers
= headers_zip_yaml
727 if randint(0, 1) == 0:
728 # vnfd CREATE AND UPLOAD in one step:
729 engine
.test("Onboard VNFD in one step", "POST",
730 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
731 r_headers_yaml_location_vnfd
,
733 self
.vnfds_id
.append(engine
.last_id
)
735 # vnfd CREATE AND UPLOAD ZIP
736 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
737 headers_json
, None, 201,
738 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
739 self
.vnfds_id
.append(engine
.last_id
)
740 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
741 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
742 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
744 if self
.descriptor_edit
:
745 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
747 engine
.test("Edit VNFD ", "PATCH",
748 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
749 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
751 if "/" in self
.nsd_filename
:
752 nsd_filename_path
= self
.nsd_filename
753 if not os
.path
.exists(nsd_filename_path
):
754 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
756 nsd_filename_path
= temp_dir
+ self
.nsd_filename
757 if not os
.path
.exists(nsd_filename_path
):
758 with
open(nsd_filename_path
, "wb") as file:
759 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
760 if response
.status_code
>= 300:
761 raise TestException("Error downloading descriptor from '{}': {}".format(
762 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
763 file.write(response
.content
)
764 if nsd_filename_path
.endswith(".yaml"):
765 headers
= headers_yaml
767 headers
= headers_zip_yaml
769 if randint(0, 1) == 0:
770 # nsd CREATE AND UPLOAD in one step:
771 engine
.test("Onboard NSD in one step", "POST",
772 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
773 r_headers_yaml_location_nsd
, yaml
)
774 self
.nsd_id
= engine
.last_id
776 # nsd CREATE AND UPLOAD ZIP
777 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
778 headers_json
, None, 201,
779 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
780 self
.nsd_id
= engine
.last_id
781 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
782 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
783 headers
, "@b" + nsd_filename_path
, 204, None, 0)
785 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
787 engine
.test("Edit NSD ", "PATCH",
788 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
789 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
791 def delete_descriptors(self
, engine
):
793 engine
.test("Delete NSSD SOL005", "DELETE",
794 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
795 headers_yaml
, None, 204, None, 0)
796 for vnfd_id
in self
.vnfds_id
:
797 engine
.test("Delete VNFD SOL005", "DELETE",
798 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
800 def instantiate(self
, engine
, ns_data
):
801 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
802 # create NS Two steps
803 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
804 headers_yaml
, ns_data_text
, 201,
805 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
808 self
.ns_id
= engine
.last_id
809 engine
.test("Instantiate NS step 2", "POST",
810 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
811 201, r_headers_yaml_location_nslcmop
, "yaml")
812 nslcmop_id
= engine
.last_id
815 # Wait until status is Ok
816 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
817 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
819 def terminate(self
, engine
):
822 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
823 None, 201, r_headers_yaml_location_nslcmop
, "yaml")
824 nslcmop2_id
= engine
.last_id
825 # Wait until status is Ok
826 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
828 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
831 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
832 headers_yaml
, None, 204, None, 0)
834 # check all it is deleted
835 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
837 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
838 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
843 if not isinstance(nslcmops
, list) or nslcmops
:
844 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
846 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
848 r
= engine
.test("GET VNFR IDs", "GET",
849 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
850 200, r_header_json
, "json")
855 vnfr_list
= ns_data
['constituent-vnfr-ref']
858 for vnfr_id
in vnfr_list
:
859 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
860 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
861 200, r_header_json
, "json")
866 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
867 if not commands
.get(vnf_index
):
869 if vnfr_data
.get("ip-address"):
870 description
= "Exec command='{}' at VNFR={} IP={}".format(commands
.get(vnf_index
)[0], vnf_index
,
871 vnfr_data
['ip-address'])
873 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
874 logger
.warning(test_description
)
875 while timeout
>= time
:
876 result
, message
= self
.do_checks([vnfr_data
["ip-address"]],
877 vnf_index
=vnfr_data
["member-vnf-index-ref"],
878 commands
=commands
.get(vnf_index
), user
=users
.get(vnf_index
),
879 passwd
=passwds
.get(vnf_index
), key
=keys
.get(vnf_index
))
881 engine
.passed_tests
+= 1
882 logger
.debug(message
)
888 engine
.failed_tests
+= 1
889 logger
.error(message
)
893 engine
.failed_tests
+= 1
894 logger
.error(message
)
896 engine
.failed_tests
+= 1
897 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnfr_id
))
899 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
902 from pssh
.clients
import ParallelSSHClient
903 from pssh
.utils
import load_private_key
904 from ssh2
import exceptions
as ssh2Exception
905 except ImportError as e
:
906 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
907 "parallel-ssh urllib3': {}".format(e
))
908 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
909 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
911 p_host
= os
.environ
.get("PROXY_HOST")
912 p_user
= os
.environ
.get("PROXY_USER")
913 p_password
= os
.environ
.get("PROXY_PASSWD")
916 pkey
= load_private_key(key
)
920 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
921 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
923 output
= client
.run_command(cmd
)
925 if output
[ip
[0]].exit_code
:
926 return -1, " VNFR {} command '{}' returns error: {}".format(ip
[0], cmd
, output
[ip
[0]].stderr
)
928 return 1, " VNFR {} command '{}' successful".format(ip
[0], cmd
)
929 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
930 ssh2Exception
.SocketRecvError
) as e
:
931 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
932 except Exception as e
:
933 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
935 def aditional_operations(self
, engine
, test_osm
, manual_check
):
938 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
939 engine
.set_test_name(self
.test_name
)
940 engine
.get_autorization()
941 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
943 if "vnfd-files" in test_params
:
944 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
945 if "nsd-file" in test_params
:
946 self
.nsd_filename
= test_params
["nsd-file"]
947 if test_params
.get("ns-name"):
948 nsname
= test_params
["ns-name"]
949 self
.create_descriptors(engine
)
951 # create real VIM if not exist
952 self
.vim_id
= engine
.get_create_vim(test_osm
)
953 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
954 "vimAccountId": self
.vim_id
}
955 if test_params
and test_params
.get("ns-config"):
956 if isinstance(test_params
["ns-config"], str):
957 ns_data
.update(yaml
.load(test_params
["ns-config"]))
959 ns_data
.update(test_params
["ns-config"])
960 self
.instantiate(engine
, ns_data
)
963 input('NS has been deployed. Perform manual check and press enter to resume')
964 if test_osm
and self
.cmds
:
965 self
.test_ns(engine
, test_osm
, self
.cmds
, self
.uss
, self
.pss
, self
.keys
, self
.timeout
)
966 self
.aditional_operations(engine
, test_osm
, manual_check
)
967 self
.terminate(engine
)
968 self
.delete_descriptors(engine
)
971 class TestDeployHackfestCirros(TestDeploy
):
972 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
976 self
.test_name
= "CIRROS"
977 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
978 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
979 self
.cmds
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
980 self
.uss
= {'1': "cirros", '2': "cirros"}
981 self
.pss
= {'1': "cubswin:)", '2': "cubswin:)"}
984 class TestDeployHackfest1(TestDeploy
):
985 description
= "Load and deploy Hackfest_1_vnfd example"
989 self
.test_name
= "HACKFEST1-"
990 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
991 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
992 # self.cmds = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
993 # self.uss = {'1': "cirros", '2': "cirros"}
994 # self.pss = {'1': "cubswin:)", '2': "cubswin:)"}
997 class TestDeployHackfestCirrosScaling(TestDeploy
):
998 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1002 self
.test_name
= "CIRROS-SCALE"
1003 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1004 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1006 def create_descriptors(self
, engine
):
1007 super().create_descriptors(engine
)
1008 # Modify VNFD to add scaling and count=2
1009 self
.descriptor_edit
= {
1012 "$id: 'cirros_vnfd-VM'": {"count": 2}
1014 "scaling-group-descriptor": [{
1015 "name": "scale_cirros",
1016 "max-instance-count": 2,
1018 "vdu-id-ref": "cirros_vnfd-VM",
1025 def aditional_operations(self
, engine
, test_osm
, manual_check
):
1028 # 2 perform scale out twice
1029 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1030 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1031 for i
in range(0, 2):
1032 engine
.test("Execute scale action over NS", "POST",
1033 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1034 201, r_headers_yaml_location_nslcmop
, "yaml")
1035 nslcmop2_scale_out
= engine
.last_id
1036 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1038 input('NS scale out done. Check that two more vdus are there')
1039 # TODO check automatic
1041 # 2 perform scale in
1042 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1043 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1044 for i
in range(0, 2):
1045 engine
.test("Execute scale IN action over NS", "POST",
1046 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1047 201, r_headers_yaml_location_nslcmop
, "yaml")
1048 nslcmop2_scale_in
= engine
.last_id
1049 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1051 input('NS scale in done. Check that two less vdus are there')
1052 # TODO check automatic
1054 # perform scale in that must fail as reached limit
1055 engine
.test("Execute scale IN out of limit action over NS", "POST",
1056 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1057 201, r_headers_yaml_location_nslcmop
, "yaml")
1058 nslcmop2_scale_in
= engine
.last_id
1059 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1062 class TestDeployIpMac(TestDeploy
):
1063 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1067 self
.test_name
= "SetIpMac"
1068 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1069 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1070 self
.descriptor_url
= \
1071 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1072 self
.cmds
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1073 self
.uss
= {'1': "osm", '2': "osm"}
1074 self
.pss
= {'1': "osm4u", '2': "osm4u"}
1077 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1078 # super().run(engine, test_osm, manual_check, test_params)
1079 # run again setting IPs with instantiate parameters
1080 instantiation_params
= {
1083 "member-vnf-index": "1",
1086 "name": "internal_vld1", # net_internal
1088 "ip-version": "ipv4",
1089 "subnet-address": "10.9.8.0/24",
1090 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1092 "internal-connection-point": [
1095 "ip-address": "10.9.8.2",
1099 "ip-address": "10.9.8.3",
1110 # "name": "iface11",
1111 # "floating-ip-required": True,
1115 "mac-address": "52:33:44:55:66:13"
1124 "ip-address": "10.31.31.22",
1125 "mac-address": "52:33:44:55:66:21"
1134 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1137 class TestDeployHackfest4(TestDeploy
):
1138 description
= "Load and deploy Hackfest 4 example."
1142 self
.test_name
= "HACKFEST4-"
1143 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1144 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1145 self
.uses_configuration
= True
1146 self
.cmds
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1147 self
.uss
= {'1': "ubuntu", '2': "ubuntu"}
1148 self
.pss
= {'1': "osm4u", '2': "osm4u"}
1150 def create_descriptors(self
, engine
):
1151 super().create_descriptors(engine
)
1152 # Modify VNFD to add scaling
1153 self
.descriptor_edit
= {
1155 'vnf-configuration': {
1156 'config-primitive': [{
1160 'data-type': 'STRING',
1161 'default-value': '/home/ubuntu/touched'
1165 'scaling-group-descriptor': [{
1166 'name': 'scale_dataVM',
1167 'scaling-policy': [{
1168 'threshold-time': 0,
1169 'name': 'auto_cpu_util_above_threshold',
1170 'scaling-type': 'automatic',
1171 'scaling-criteria': [{
1172 'name': 'cpu_util_above_threshold',
1173 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1174 'scale-out-relational-operation': 'GE',
1175 'scale-in-threshold': 15,
1176 'scale-out-threshold': 60,
1177 'scale-in-relational-operation': 'LE'
1181 'max-instance-count': 10,
1182 'scaling-config-action': [
1183 {'vnf-config-primitive-name-ref': 'touch',
1184 'trigger': 'post-scale-out'},
1185 {'vnf-config-primitive-name-ref': 'touch',
1186 'trigger': 'pre-scale-in'}
1189 'vdu-id-ref': 'dataVM',
1197 class TestDeployHackfest3Charmed(TestDeploy
):
1198 description
= "Load and deploy Hackfest 3charmed_ns example. Modifies it for adding scaling and performs " \
1199 "primitive actions and scaling"
1203 self
.test_name
= "HACKFEST3-"
1204 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1205 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1206 self
.uses_configuration
= True
1207 self
.cmds
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1208 self
.uss
= {'1': "ubuntu", '2': "ubuntu"}
1209 self
.pss
= {'1': "osm4u", '2': "osm4u"}
1210 # self.descriptor_edit = {
1211 # "vnfd0": yaml.load("""
1212 # scaling-group-descriptor:
1213 # - name: "scale_dataVM"
1214 # max-instance-count: 10
1216 # - name: "auto_cpu_util_above_threshold"
1217 # scaling-type: "automatic"
1221 # - name: "cpu_util_above_threshold"
1222 # scale-in-threshold: 15
1223 # scale-in-relational-operation: "LE"
1224 # scale-out-threshold: 60
1225 # scale-out-relational-operation: "GE"
1226 # vnf-monitoring-param-ref: "all_aaa_cpu_util"
1228 # - vdu-id-ref: dataVM
1230 # scaling-config-action:
1231 # - trigger: post-scale-out
1232 # vnf-config-primitive-name-ref: touch
1233 # - trigger: pre-scale-in
1234 # vnf-config-primitive-name-ref: touch
1235 # vnf-configuration:
1241 # default-value: '/home/ubuntu/touched'
1245 def aditional_operations(self
, engine
, test_osm
, manual_check
):
1249 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1250 engine
.test("Exec service primitive over NS", "POST",
1251 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1252 201, r_headers_yaml_location_nslcmop
, "yaml")
1253 nslcmop2_action
= engine
.last_id
1254 # Wait until status is Ok
1255 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1257 input('NS service primitive has been executed. Check that file /home/ubuntu/OSMTESTNBI is present at '
1260 cmds
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1261 uss
= {'1': "ubuntu", '2': "ubuntu"}
1262 pss
= {'1': "osm4u", '2': "osm4u"}
1263 self
.test_ns(engine
, test_osm
, cmds
, uss
, pss
, self
.keys
, self
.timeout
)
1265 # # 2 perform scale out
1266 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1267 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1268 # engine.test("Execute scale action over NS", "POST",
1269 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1270 # 201, r_headers_yaml_location_nslcmop, "yaml")
1271 # nslcmop2_scale_out = engine.last_id
1272 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1274 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1275 # # TODO check automatic
1277 # # 2 perform scale in
1278 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1279 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1280 # engine.test("Execute scale action over NS", "POST",
1281 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1282 # 201, r_headers_yaml_location_nslcmop, "yaml")
1283 # nslcmop2_scale_in = engine.last_id
1284 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1286 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1287 # # TODO check automatic
1290 class TestDeploySimpleCharm(TestDeploy
):
1291 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1295 self
.test_name
= "HACKFEST-SIMPLE"
1296 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1297 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1298 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1299 self
.uses_configuration
= True
1300 self
.cmds
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1301 self
.uss
= {'1': "ubuntu", '2': "ubuntu"}
1302 self
.pss
= {'1': "osm4u", '2': "osm4u"}
1305 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1306 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1311 self
.test_name
= "HACKFEST-SIMPLE2-"
1312 self
.qforce
= "?FORCE=True"
1313 self
.descriptor_edit
= {
1315 "id": "hackfest.simplecharm.vnf"
1319 "id": "hackfest.simplecharm.ns",
1320 "constituent-vnfd": {
1321 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1322 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1326 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1327 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1328 "$[1]": {"member-vnf-index-ref": "$2",
1329 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1332 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1333 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1334 "$[1]": {"member-vnf-index-ref": "$2",
1335 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1342 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1343 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1344 "ids and member-vnf-index"
1348 self
.test_name
= "HACKFEST3bis"
1349 self
.qforce
= "?FORCE=True"
1350 self
.descriptor_edit
= {
1354 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1358 "vnf-configuration": None,
1359 "connection-point": {
1363 "short-name": "pdu-mgmt"
1367 "mgmt-interface": {"cp": "pdu-mgmt"},
1368 "description": "A vnf single vdu to be used as PDU",
1372 "id": "pdu_internal",
1373 "name": "pdu_internal",
1374 "internal-connection-point": {"$[1]": None},
1375 "short-name": "pdu_internal",
1381 # Modify NSD accordingly
1383 "constituent-vnfd": {
1384 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1387 "description": "A nsd to deploy the vnf to act as as PDU",
1389 "name": "nsd-as-pdu",
1390 "short-name": "nsd-as-pdu",
1395 "short-name": "mgmt_pdu",
1396 "vnfd-connection-point-ref": {
1398 "vnfd-connection-point-ref": "pdu-mgmt",
1399 "vnfd-id-ref": "vdu-as-pdu",
1411 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1412 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1416 self
.test_name
= "SingleVDU"
1417 self
.qforce
= "?FORCE=True"
1418 self
.descriptor_edit
= {
1419 # Modify VNFD to remove one VDU
1423 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1427 "vnf-configuration": None,
1428 "connection-point": {
1432 "short-name": "pdu-mgmt"
1436 "mgmt-interface": {"cp": "pdu-mgmt"},
1437 "description": "A vnf single vdu to be used as PDU",
1441 "id": "pdu_internal",
1442 "name": "pdu_internal",
1443 "internal-connection-point": {"$[1]": None},
1444 "short-name": "pdu_internal",
1450 # Modify NSD accordingly
1452 "constituent-vnfd": {
1453 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1456 "description": "A nsd to deploy the vnf to act as as PDU",
1458 "name": "nsd-as-pdu",
1459 "short-name": "nsd-as-pdu",
1464 "short-name": "mgmt_pdu",
1465 "vnfd-connection-point-ref": {
1467 "vnfd-connection-point-ref": "pdu-mgmt",
1468 "vnfd-id-ref": "vdu-as-pdu",
1480 class TestDeployHnfd(TestDeployHackfest3Charmed
):
1481 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1485 self
.test_name
= "HNFD"
1486 self
.pduDeploy
= TestDeploySingleVdu()
1487 self
.pdu_interface_0
= {}
1488 self
.pdu_interface_1
= {}
1491 # self.vnf_to_pdu = """
1494 # pdu-type: PDU-TYPE-1
1499 # name: pdu-iface-internal
1501 # description: HFND, one PDU + One VDU
1507 self
.pdu_descriptor
= {
1509 "type": "PDU-TYPE-1",
1510 "vim_accounts": "to-override",
1513 "name": "mgmt-iface",
1516 "ip-address": "to override",
1517 "mac-address": "mac_address",
1518 "vim-network-name": "mgmt",
1521 "name": "pdu-iface-internal",
1524 "ip-address": "to override",
1525 "mac-address": "mac_address",
1526 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1530 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1532 self
.descriptor_edit
= {
1536 "short-name": "hfn1",
1539 "pdu-type": "PDU-TYPE-1",
1541 "$[0]": {"name": "mgmt-iface"},
1542 "$[1]": {"name": "pdu-iface-internal"},
1548 "constituent-vnfd": {
1549 "$[1]": {"vnfd-id-ref": "hfnd1"}
1552 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1553 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1558 def create_descriptors(self
, engine
):
1559 super().create_descriptors(engine
)
1562 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
1563 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
1564 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
1565 # TODO get vim-network-name from vnfr.vld.name
1566 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1567 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1568 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
1569 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1570 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
1571 201, r_header_yaml
, "yaml")
1572 self
.pdu_id
= engine
.last_id
1574 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1575 engine
.get_autorization()
1576 engine
.set_test_name(self
.test_name
)
1577 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1579 # create real VIM if not exist
1580 self
.vim_id
= engine
.get_create_vim(test_osm
)
1582 self
.pduDeploy
.create_descriptors(engine
)
1583 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
1584 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
1586 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1588 self
.pduDeploy
.test_ns(engine
, test_osm
, self
.pduDeploy
.cmds
, self
.pduDeploy
.uss
, self
.pduDeploy
.pss
,
1589 self
.pduDeploy
.keys
, self
.pduDeploy
.timeout
)
1592 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
1593 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
1594 200, r_header_json
, "json")
1597 vnfr_data
= r
.json()
1600 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
1601 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
1602 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
1603 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
1604 if not self
.pdu_interface_0
["ip-address"]:
1605 raise TestException("Vnfr has not managment ip address")
1607 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
1608 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
1609 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
1610 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
1612 self
.create_descriptors(engine
)
1614 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1615 "vimAccountId": self
.vim_id
}
1616 if test_params
and test_params
.get("ns-config"):
1617 if isinstance(test_params
["ns-config"], str):
1618 ns_data
.update(yaml
.load(test_params
["ns-config"]))
1620 ns_data
.update(test_params
["ns-config"])
1622 self
.instantiate(engine
, ns_data
)
1624 input('NS has been deployed. Perform manual check and press enter to resume')
1626 self
.test_ns(engine
, test_osm
, self
.cmds
, self
.uss
, self
.pss
, self
.keys
, self
.timeout
)
1627 self
.aditional_operations(engine
, test_osm
, manual_check
)
1628 self
.terminate(engine
)
1629 self
.pduDeploy
.terminate(engine
)
1630 self
.delete_descriptors(engine
)
1631 self
.pduDeploy
.delete_descriptors(engine
)
1633 def delete_descriptors(self
, engine
):
1634 super().delete_descriptors(engine
)
1636 engine
.test("Delete PDU SOL005", "DELETE",
1637 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
1638 headers_yaml
, None, 204, None, 0)
1641 class TestDescriptors
:
1642 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1645 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
1646 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1647 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1650 self
.vnfd_empty
= """vnfd:vnfd-catalog:
1656 self
.vnfd_prova
= """vnfd:vnfd-catalog:
1668 - external-connection-point-ref: cp_0h8m
1676 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1677 engine
.set_test_name("Descriptors")
1678 engine
.get_autorization()
1679 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
1680 if not os
.path
.exists(temp_dir
):
1681 os
.makedirs(temp_dir
)
1684 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
1685 filename_path
= temp_dir
+ filename
1686 if not os
.path
.exists(filename_path
):
1687 with
open(filename_path
, "wb") as file:
1688 response
= requests
.get(self
.descriptor_url
+ filename
)
1689 if response
.status_code
>= 300:
1690 raise TestException("Error downloading descriptor from '{}': {}".format(
1691 self
.descriptor_url
+ filename
, response
.status_code
))
1692 file.write(response
.content
)
1694 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
1695 nsd_filename_path
= temp_dir
+ self
.nsd_filename
1697 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
1698 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
1699 self
.vnfd_id
= engine
.last_id
1702 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1703 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1705 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
1706 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
1707 "@b" + vnfd_filename_path
, 204, None, 0)
1710 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
1711 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
1713 # get vnfd descriptor
1714 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1715 headers_yaml
, None, 200, r_header_yaml
, "yaml")
1717 # get vnfd file descriptor
1718 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
1719 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
1720 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
1722 # get vnfd zip file package
1723 engine
.test("Get VNFD zip package", "GET",
1724 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
1725 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
1726 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
1729 engine
.test("Get VNFD artifact package", "GET",
1730 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
1731 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
1732 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
1734 # nsd CREATE AND UPLOAD in one step:
1735 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
1736 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
1737 self
.nsd_id
= engine
.last_id
1739 # get nsd descriptor
1740 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
1741 None, 200, r_header_yaml
, "yaml")
1743 # get nsd file descriptor
1744 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
1745 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
1746 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
1748 # get nsd zip file package
1749 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
1750 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
1751 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
1754 engine
.test("Get NSD artifact package", "GET",
1755 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
1756 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
1757 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
1760 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
1761 headers_yaml
, None, 409, None, None)
1763 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
1764 headers_yaml
, None, 204, None, 0)
1767 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
1771 class TestNetSliceTemplates
:
1772 description
= "Upload a NST to OSM"
1775 self
.nst_filenames
= ("@./cirros_slice/cirros_slice_vld.yaml")
1777 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1779 engine
.set_test_name("NST")
1780 engine
.get_autorization()
1781 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
1782 201, r_headers_yaml_location_nst
, "yaml")
1783 nst_id
= engine
.last_id
1785 # nstd SHOW OSM format
1786 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1787 200, r_header_json
, "json")
1790 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1794 class TestNetSliceInstances
:
1795 description
= "Upload a NST to OSM"
1799 self
.nst_filenames
= ("@./cirros_slice/cirros_slice.yaml")
1801 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1803 engine
.set_test_name("NSI")
1804 engine
.get_autorization()
1805 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
, 201,
1806 r_headers_yaml_location_nst
, "yaml")
1807 nst_id
= engine
.last_id
1810 self
.vim_id
= engine
.get_create_vim(test_osm
)
1812 ns_data
= {"nsiDescription": "default description", "nsiName": "my_slice", "nstId": nst_id
,
1813 "vimAccountId": self
.vim_id
}
1814 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
1816 engine
.test("Onboard NSI", "POST", "/nsilcm/v1/netslice_instances_content", headers_yaml
, ns_data_text
, 201,
1817 r_headers_yaml_location_nst
, "yaml")
1818 nsi_id
= engine
.last_id
1820 # TODO: Improve the wait with a polling if NSI was deployed
1825 engine
.test("Wait until NSI is deployed", "GET", "/nsilcm/v1/netslice_instances_content/{}".format(nsi_id
),
1826 headers_json
, None, 200, r_header_json
, "json")
1829 engine
.test("Delete NSI", "DELETE", "/nsilcm/v1/netslice_instances_content/{}".format(nsi_id
), headers_json
,
1830 None, 202, r_header_json
, "json")
1835 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
1839 if __name__
== "__main__":
1843 # Disable warnings from self-signed certificates.
1844 requests
.packages
.urllib3
.disable_warnings()
1846 logging
.basicConfig(format
="%(levelname)s %(message)s", level
=logging
.ERROR
)
1847 logger
= logging
.getLogger('NBI')
1848 # load parameters and configuration
1849 opts
, args
= getopt
.getopt(sys
.argv
[1:], "hvu:p:",
1850 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
1851 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
1852 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
1853 url
= "https://localhost:9999/osm"
1854 user
= password
= project
= "admin"
1856 manual_check
= False
1861 "NonAuthorized": TestNonAuthorized
,
1862 "FakeVIM": TestFakeVim
,
1863 "TestUsersProjects": TestUsersProjects
,
1864 "VIM-SDN": TestVIMSDN
,
1865 "Deploy-Custom": TestDeploy
,
1866 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros
,
1867 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling
,
1868 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed
,
1869 "Deploy-Hackfest-4": TestDeployHackfest4
,
1870 "Deploy-CirrosMacIp": TestDeployIpMac
,
1871 "TestDescriptors": TestDescriptors
,
1872 "TestDeployHackfest1": TestDeployHackfest1
,
1873 # "Deploy-MultiVIM": TestDeployMultiVIM,
1874 "DeploySingleVdu": TestDeploySingleVdu
,
1875 "DeployHnfd": TestDeployHnfd
,
1876 # "Upload-Slice-Template": TestNetSliceTemplates,
1877 # "Deploy-Slice-Instance": TestNetSliceInstances,
1878 "TestDeploySimpleCharm": TestDeploySimpleCharm
,
1879 "TestDeploySimpleCharm2": TestDeploySimpleCharm2
,
1885 # print("parameter:", o, a)
1886 if o
== "--version":
1887 print("test version " + __version__
+ ' ' + version_date
)
1890 for test
, test_class
in test_classes
.items():
1891 print("{:20} {}".format(test
+ ":", test_class
.description
))
1893 elif o
in ("-v", "--verbose"):
1895 elif o
== "no-verbose":
1897 elif o
in ("-h", "--help"):
1900 elif o
== "--test-osm":
1902 elif o
== "--manual-check":
1906 elif o
in ("-u", "--user"):
1908 elif o
in ("-p", "--password"):
1910 elif o
== "--project":
1912 elif o
== "--fail-fast":
1915 # print("asdfadf", o, a, a.split(","))
1916 for _test
in a
.split(","):
1917 if _test
not in test_classes
:
1918 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test
),
1921 test_to_do
.append(_test
)
1922 elif o
== "--params":
1923 param_key
, _
, param_value
= a
.partition("=")
1924 text_index
= len(test_to_do
)
1925 if text_index
not in test_params
:
1926 test_params
[text_index
] = {}
1927 test_params
[text_index
][param_key
] = param_value
1928 elif o
== "--insecure":
1930 elif o
== "--timeout":
1932 elif o
== "--timeout-deploy":
1933 timeout_deploy
= int(a
)
1934 elif o
== "--timeout-configure":
1935 timeout_configure
= int(a
)
1937 assert False, "Unhandled option"
1939 logger
.setLevel(logging
.WARNING
)
1941 logger
.setLevel(logging
.DEBUG
)
1943 logger
.setLevel(logging
.ERROR
)
1945 test_rest
= TestRest(url
, user
=user
, password
=password
, project
=project
)
1946 # print("tests to do:", test_to_do)
1949 for test
in test_to_do
:
1950 if fail_fast
and test_rest
.failed_tests
:
1953 test_class
= test_classes
[test
]
1954 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(text_index
))
1956 for test
, test_class
in test_classes
.items():
1957 if fail_fast
and test_rest
.failed_tests
:
1959 test_class().run(test_rest
, test_osm
, manual_check
, test_params
.get(0))
1960 test_rest
.print_results()
1961 exit(1 if test_rest
.failed_tests
else 0)
1963 except TestException
as e
:
1964 logger
.error(test
+ "Test {} Exception: {}".format(test
, str(e
)))
1966 except getopt
.GetoptError
as e
:
1968 print(e
, file=sys
.stderr
)
1970 except Exception as e
:
1971 logger
.critical(test
+ " Exception: " + str(e
), exc_info
=True)