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
29 from uuid
import uuid4
32 __author__
= "Alfonso Tierno, alfonso.tiernosepulveda@telefonica.com"
33 __date__
= "$2018-03-01$"
35 version_date
= "Oct 2018"
39 print("Usage: ", sys
.argv
[0], "[options]")
40 print(" Performs system tests over running NBI. It can be used for real OSM test using option '--test-osm'")
41 print(" If this is the case env variables 'OSMNBITEST_VIM_NAME' must be supplied to create a VIM if not exist "
42 "where deployment is done")
44 print(" -h|--help: shows this help")
45 print(" --insecure: Allows non trusted https NBI server")
46 print(" --list: list available tests")
47 print(" --manual-check: Deployment tests stop after deployed to allow manual inspection. Only make sense with "
49 print(" -p|--password PASSWORD: NBI access password. 'admin' by default")
50 print(" ---project PROJECT: NBI access project. 'admin' by default")
51 print(" --test TEST[,...]: Execute only a test or a comma separated list of tests")
52 print(" --params key=val: params to the previous test. key can be vnfd-files, nsd-file, ns-name, ns-config")
53 print(" --test-osm: If missing this test is intended for NBI only, no other OSM components are expected. Use "
54 "this flag to test the system. LCM and RO components are expected to be up and running")
55 print(" --timeout TIMEOUT: General NBI timeout, by default {}s".format(timeout
))
56 print(" --timeout-deploy TIMEOUT: Timeout used for getting NS deployed, by default {}s".format(timeout_deploy
))
57 print(" --timeout-configure TIMEOUT: Timeout used for getting NS deployed and configured,"
58 " by default {}s".format(timeout_configure
))
59 print(" -u|--user USERNAME: NBI access username. 'admin' by default")
60 print(" --url URL: complete NBI server URL. 'https//localhost:9999/osm' by default")
61 print(" -v|--verbose print debug information, can be used several times")
62 print(" --no-verbose remove verbosity")
63 print(" --version: prints current version")
64 print("ENV variables used for real deployment tests with option osm-test.")
65 print(" export OSMNBITEST_VIM_NAME=vim-name")
66 print(" export OSMNBITEST_VIM_URL=vim-url")
67 print(" export OSMNBITEST_VIM_TYPE=vim-type")
68 print(" export OSMNBITEST_VIM_TENANT=vim-tenant")
69 print(" export OSMNBITEST_VIM_USER=vim-user")
70 print(" export OSMNBITEST_VIM_PASSWORD=vim-password")
71 print(" export OSMNBITEST_VIM_CONFIG=\"vim-config\"")
72 print(" export OSMNBITEST_NS_NAME=\"vim-config\"")
76 r_header_json
= {"Content-type": "application/json"}
77 headers_json
= {"Content-type": "application/json", "Accept": "application/json"}
78 r_header_yaml
= {"Content-type": "application/yaml"}
79 headers_yaml
= {"Content-type": "application/yaml", "Accept": "application/yaml"}
80 r_header_text
= {"Content-type": "text/plain"}
81 r_header_octect
= {"Content-type": "application/octet-stream"}
82 headers_text
= {"Accept": "text/plain,application/yaml"}
83 r_header_zip
= {"Content-type": "application/zip"}
84 headers_zip
= {"Accept": "application/zip,application/yaml"}
85 headers_zip_yaml
= {"Accept": "application/yaml", "Content-type": "application/zip"}
86 headers_zip_json
= {"Accept": "application/json", "Content-type": "application/zip"}
87 headers_txt_json
= {"Accept": "application/json", "Content-type": "text/plain"}
88 r_headers_yaml_location_vnfd
= {"Location": "/vnfpkgm/v1/vnf_packages_content/", "Content-Type": "application/yaml"}
89 r_headers_yaml_location_nsd
= {"Location": "/nsd/v1/ns_descriptors_content/", "Content-Type": "application/yaml"}
90 r_headers_yaml_location_nst
= {"Location": "/nst/v1/netslice_templates_content", "Content-Type": "application/yaml"}
91 r_headers_yaml_location_nslcmop
= {"Location": "nslcm/v1/ns_lcm_op_occs/", "Content-Type": "application/yaml"}
92 r_headers_yaml_location_nsilcmop
= {"Location": "/osm/nsilcm/v1/nsi_lcm_op_occs/", "Content-Type": "application/yaml"}
94 # test ones authorized
95 test_authorized_list
= (
96 ("AU1", "Invalid vnfd id", "GET", "/vnfpkgm/v1/vnf_packages/non-existing-id",
97 headers_json
, None, 404, r_header_json
, "json"),
98 ("AU2", "Invalid nsd id", "GET", "/nsd/v1/ns_descriptors/non-existing-id",
99 headers_yaml
, None, 404, r_header_yaml
, "yaml"),
100 ("AU3", "Invalid nsd id", "DELETE", "/nsd/v1/ns_descriptors_content/non-existing-id",
101 headers_yaml
, None, 404, r_header_yaml
, "yaml"),
103 timeout
= 120 # general timeout
104 timeout_deploy
= 60*10 # timeout for NS deploying without charms
105 timeout_configure
= 60*20 # timeout for NS deploying and configuring
108 class TestException(Exception):
113 def __init__(self
, url_base
, header_base
=None, verify
=False, user
="admin", password
="admin", project
="admin"):
114 self
.url_base
= url_base
115 if header_base
is None:
116 self
.header_base
= {}
118 self
.header_base
= header_base
.copy()
119 self
.s
= requests
.session()
120 self
.s
.headers
= self
.header_base
124 self
.password
= password
125 self
.project
= project
127 # contains ID of tests obtained from Location response header. "" key contains last obtained id
129 self
.test_name
= None
130 self
.step
= 0 # number of subtest under test
131 self
.passed_tests
= 0
132 self
.failed_tests
= 0
134 def set_test_name(self
, test_name
):
135 self
.test_name
= test_name
139 def set_header(self
, header
):
140 self
.s
.headers
.update(header
)
142 def set_tet_name(self
, test_name
):
143 self
.test_name
= test_name
145 def unset_header(self
, key
):
146 if key
in self
.s
.headers
:
147 del self
.s
.headers
[key
]
149 def test(self
, description
, method
, url
, headers
, payload
, expected_codes
, expected_headers
,
150 expected_payload
, store_file
=None, pooling
=False):
152 Performs an http request and check http code response. Exit if different than allowed. It get the returned id
153 that can be used by following test in the URL with {name} where name is the name of the test
154 :param description: description of the test
155 :param method: HTTP method: GET,PUT,POST,DELETE,...
156 :param url: complete URL or relative URL
157 :param headers: request headers to add to the base headers
158 :param payload: Can be a dict, transformed to json, a text or a file if starts with '@'
159 :param expected_codes: expected response codes, can be int, int tuple or int range
160 :param expected_headers: expected response headers, dict with key values
161 :param expected_payload: expected payload, 0 if empty, 'yaml', 'json', 'text', 'zip', 'octet-stream'
162 :param store_file: filename to store content
163 :param pooling: if True do not count neither log this test. Because a pooling is done with many equal requests
164 :return: requests response
169 self
.s
= requests
.session()
173 elif not url
.startswith("http"):
174 url
= self
.url_base
+ url
176 # replace url <> with the last ID
177 url
= url
.replace("<>", self
.last_id
)
179 if isinstance(payload
, str):
180 if payload
.startswith("@"):
182 file_name
= payload
[1:]
183 if payload
.startswith("@b"):
185 file_name
= payload
[2:]
186 with
open(file_name
, mode
) as f
:
188 elif isinstance(payload
, dict):
189 payload
= json
.dumps(payload
)
192 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, method
, url
)
193 logger
.warning(test_description
)
196 if expected_payload
in ("zip", "octet-string") or store_file
:
201 r
= getattr(self
.s
, method
.lower())(url
, data
=payload
, headers
=headers
, verify
=self
.verify
,
204 except requests
.exceptions
.ConnectionError
as e
:
207 logger
.error("Exception {}. Retrying".format(e
))
210 if expected_payload
in ("zip", "octet-string") or store_file
:
211 logger
.debug("RX {}".format(r
.status_code
))
213 logger
.debug("RX {}: {}".format(r
.status_code
, r
.text
))
217 if isinstance(expected_codes
, int):
218 expected_codes
= (expected_codes
,)
219 if r
.status_code
not in expected_codes
:
221 "Got status {}. Expected {}. {}".format(r
.status_code
, expected_codes
, r
.text
))
224 for header_key
, header_val
in expected_headers
.items():
225 if header_key
.lower() not in r
.headers
:
226 raise TestException("Header {} not present".format(header_key
))
227 if header_val
and header_val
.lower() not in r
.headers
[header_key
]:
228 raise TestException("Header {} does not contain {} but {}".format(header_key
, header_val
,
229 r
.headers
[header_key
]))
231 if expected_payload
is not None:
232 if expected_payload
== 0 and len(r
.content
) > 0:
233 raise TestException("Expected empty payload")
234 elif expected_payload
== "json":
237 except Exception as e
:
238 raise TestException("Expected json response payload, but got Exception {}".format(e
))
239 elif expected_payload
== "yaml":
241 yaml
.safe_load(r
.text
)
242 except Exception as e
:
243 raise TestException("Expected yaml response payload, but got Exception {}".format(e
))
244 elif expected_payload
in ("zip", "octet-string"):
245 if len(r
.content
) == 0:
246 raise TestException("Expected some response payload, but got empty")
248 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
249 # for tarinfo in tar:
250 # tarname = tarinfo.name
252 # except Exception as e:
253 # raise TestException("Expected zip response payload, but got Exception {}".format(e))
254 elif expected_payload
== "text":
255 if len(r
.content
) == 0:
256 raise TestException("Expected some response payload, but got empty")
259 with
open(store_file
, 'wb') as fd
:
260 for chunk
in r
.iter_content(chunk_size
=128):
263 location
= r
.headers
.get("Location")
265 _id
= location
[location
.rfind("/") + 1:]
267 self
.last_id
= str(_id
)
269 self
.passed_tests
+= 1
271 except TestException
as e
:
272 self
.failed_tests
+= 1
276 r_status_code
= r
.status_code
278 logger
.error("{} \nRX code{}: {}".format(e
, r_status_code
, r_text
))
283 logger
.error("Cannot open file {}: {}".format(store_file
, e
))
285 logger
.error("Exception: {}".format(e
), exc_info
=True)
286 self
.failed_tests
+= 1
289 except requests
.exceptions
.RequestException
as e
:
290 logger
.error("Exception: {}".format(e
))
292 def get_autorization(self
): # user=None, password=None, project=None):
293 if self
.token
: # and self.user == user and self.password == password and self.project == project:
296 # self.password = password
297 # self.project = project
298 r
= self
.test("Obtain token", "POST", "/admin/v1/tokens", headers_json
,
299 {"username": self
.user
, "password": self
.password
, "project_id": self
.project
},
300 (200, 201), r_header_json
, "json")
304 self
.token
= response
["id"]
305 self
.set_header({"Authorization": "Bearer {}".format(self
.token
)})
307 def remove_authorization(self
):
309 self
.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self
.token
), headers_json
,
310 None, (200, 201, 204), None, None)
312 self
.unset_header("Authorization")
314 def get_create_vim(self
, test_osm
):
317 self
.get_autorization()
319 vim_name
= os
.environ
.get("OSMNBITEST_VIM_NAME")
322 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
326 r
= self
.test("Get VIM ID", "GET", "/admin/v1/vim_accounts?name={}".format(vim_name
), headers_json
,
327 None, 200, r_header_json
, "json")
332 return vims
[0]["_id"]
335 # check needed environ parameters:
336 if not os
.environ
.get("OSMNBITEST_VIM_URL") or not os
.environ
.get("OSMNBITEST_VIM_TENANT"):
337 raise TestException("Env OSMNBITEST_VIM_URL and OSMNBITEST_VIM_TENANT are needed for create a real VIM"
338 " to deploy on whit the --test-osm option")
339 vim_data
= "{{schema_version: '1.0', name: '{}', vim_type: {}, vim_url: '{}', vim_tenant_name: '{}', "\
340 "vim_user: {}, vim_password: {}".format(vim_name
,
341 os
.environ
.get("OSMNBITEST_VIM_TYPE", "openstack"),
342 os
.environ
.get("OSMNBITEST_VIM_URL"),
343 os
.environ
.get("OSMNBITEST_VIM_TENANT"),
344 os
.environ
.get("OSMNBITEST_VIM_USER"),
345 os
.environ
.get("OSMNBITEST_VIM_PASSWORD"))
346 if os
.environ
.get("OSMNBITEST_VIM_CONFIG"):
347 vim_data
+= " ,config: {}".format(os
.environ
.get("OSMNBITEST_VIM_CONFIG"))
350 vim_data
= "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
351 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
352 self
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml
, vim_data
,
353 (201, 202), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
356 def print_results(self
):
357 print("\n\n\n--------------------------------------------")
358 print("TEST RESULTS: Total: {}, Passed: {}, Failed: {}".format(self
.passed_tests
+ self
.failed_tests
,
359 self
.passed_tests
, self
.failed_tests
))
360 print("--------------------------------------------")
362 def wait_until_delete(self
, url_op
, timeout_delete
):
364 Make a pooling until topic is not present, because of deleted
366 :param timeout_delete:
369 description
= "Wait to topic being deleted"
370 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
371 logger
.warning(test_description
)
374 wait
= timeout_delete
376 r
= self
.test(description
, "GET", url_op
, headers_yaml
, None, (200, 404), None, r_header_yaml
, "yaml",
380 if r
.status_code
== 404:
381 self
.passed_tests
+= 1
383 elif r
.status_code
== 200:
387 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete
))
388 self
.failed_tests
+= 1
390 def wait_operation_ready(self
, ns_nsi
, opp_id
, timeout
, expected_fail
=False):
392 Wait until nslcmop or nsilcmop finished
393 :param ns_nsi: "ns" o "nsi"
394 :param opp_id: Id o fthe operation
396 :param expected_fail:
397 :return: None. Updates passed/failed_tests
400 url_op
= "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id
)
402 url_op
= "/nsilcm/v1/nsi_lcm_op_occs/{}".format(opp_id
)
403 description
= "Wait to {} lcm operation complete".format(ns_nsi
)
404 test_description
= "Test {}{} {} {} {}".format(self
.test_name
, self
.step
, description
, "GET", url_op
)
405 logger
.warning(test_description
)
409 r
= self
.test(description
, "GET", url_op
, headers_json
, None,
410 200, r_header_json
, "json", pooling
=True)
414 if "COMPLETED" in nslcmop
["operationState"]:
416 logger
.error("NS terminate has success, expecting failing: {}".format(nslcmop
["detailed-status"]))
417 self
.failed_tests
+= 1
419 self
.passed_tests
+= 1
421 elif "FAILED" in nslcmop
["operationState"]:
422 if not expected_fail
:
423 logger
.error("NS terminate has failed: {}".format(nslcmop
["detailed-status"]))
424 self
.failed_tests
+= 1
426 self
.passed_tests
+= 1
429 print(".", end
="", file=stderr
)
433 self
.failed_tests
+= 1
434 logger
.error("NS instantiate is not terminate after {} seconds".format(timeout
))
436 print("", file=stderr
)
439 class TestNonAuthorized
:
440 description
= "Test invalid URLs. methods and no authorization"
443 def run(engine
, test_osm
, manual_check
, test_params
=None):
444 engine
.set_test_name("NonAuth")
445 engine
.remove_authorization()
446 test_not_authorized_list
= (
447 ("Invalid token", "GET", "/admin/v1/users", headers_json
, None, 401, r_header_json
, "json"),
448 ("Invalid URL", "POST", "/admin/v1/nonexist", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
449 ("Invalid version", "DELETE", "/admin/v2/users", headers_yaml
, None, 405, r_header_yaml
, "yaml"),
451 for t
in test_not_authorized_list
:
455 class TestUsersProjects
:
456 description
= "test project and user creation"
459 def run(engine
, test_osm
, manual_check
, test_params
=None):
460 engine
.set_test_name("UserProject")
461 # backend = test_params.get("backend") if test_params else None # UNUSED
466 u1
= u2
= u3
= u4
= None
468 engine
.get_autorization()
470 res
= engine
.test("Create project non admin 1", "POST", "/admin/v1/projects", headers_json
, {"name": "P1"},
471 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
472 p1
= engine
.last_id
if res
else None
474 res
= engine
.test("Create project admin", "POST", "/admin/v1/projects", headers_json
,
475 {"name": "Padmin", "admin": True}, (201, 204),
476 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
477 padmin
= engine
.last_id
if res
else None
479 res
= engine
.test("Create project bad format", "POST", "/admin/v1/projects", headers_json
, {"name": 1},
480 (400, 422), r_header_json
, "json")
481 pbad
= engine
.last_id
if res
else None
483 res
= engine
.test("Get project admin role", "GET", "/admin/v1/roles?name=project_admin", headers_json
, {},
484 (200), {"Content-Type": "application/json"}, "json")
485 rpa
= res
.json()[0]["_id"] if res
else None
486 res
= engine
.test("Get project user role", "GET", "/admin/v1/roles?name=project_user", headers_json
, {},
487 (200), {"Content-Type": "application/json"}, "json")
488 rpu
= res
.json()[0]["_id"] if res
else None
489 res
= engine
.test("Get system admin role", "GET", "/admin/v1/roles?name=system_admin", headers_json
, {},
490 (200), {"Content-Type": "application/json"}, "json")
491 rsa
= res
.json()[0]["_id"] if res
else None
493 data
= {"username": "U1", "password": "pw1"}
495 data
["project_role_mappings"] = [
496 {"project": p1
, "role": rpa
},
497 {"project": p2
, "role": rpa
},
498 {"project": padmin
, "role": rpu
}
501 xhd
= {"Location": "/admin/v1/users/", "Content-Type": "application/json"}
502 res
= engine
.test("Create user with bad project and force", "POST", "/admin/v1/users?FORCE=True", headers_json
,
503 data
, rc
, xhd
, "json")
507 # User is created sometimes even though an exception is raised
508 res
= engine
.test("Get user U1", "GET", "/admin/v1/users?username=U1", headers_json
, {},
509 (200), {"Content-Type": "application/json"}, "json")
510 u1
= res
.json()[0]["_id"] if res
else None
512 data
= {"username": "U2", "password": "pw2"}
513 data
["project_role_mappings"] = [{"project": p1
, "role": rpa
}, {"project": padmin
, "role": rsa
}]
514 res
= engine
.test("Create user 2", "POST", "/admin/v1/users", headers_json
,
515 data
, 201, {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
516 u2
= engine
.last_id
if res
else None
519 ftt
= "project_role_mappings"
520 xpr
= [{"project": p1
, "role": rpa
}, {"project": padmin
, "role": rpu
}]
522 engine
.test("Edit user U1, delete P2 project", "PATCH", "/admin/v1/users/"+u1
, headers_json
,
523 data
, 204, None, None)
524 res
= engine
.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/"+u1
,
525 headers_json
, None, 200, None, json
)
528 xpr
[0]["project_name"] = "P1"
529 xpr
[0]["role_name"] = "project_admin"
530 xpr
[1]["project_name"] = "Padmin"
531 xpr
[1]["role_name"] = "project_user"
537 if pr
not in rj
[ftt
]:
540 logger
.error("User {} '{}' are different than expected '{}'. Edition was not done properly"
541 .format(ftt
, rj
[ftt
], xpr
))
542 engine
.failed_tests
+= 1
544 p2
= None # To prevent deletion attempts
546 # Add a test of 'default project' for Keystone?
549 engine
.test("Edit user U2, change password", "PUT", "/admin/v1/users/"+u2
, headers_json
,
550 {"password": "pw2_new"}, 204, None, None)
553 engine
.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json
,
554 {"project_id": p1
}, 401, r_header_json
, "json")
557 res
= engine
.test("Change to user U2 project P1", "POST", "/admin/v1/tokens", headers_json
,
558 {"username": "U2", "password": "pw2_new", "project_id": "P1"}, (200, 201),
559 r_header_json
, "json")
562 engine
.set_header({"Authorization": "Bearer {}".format(rj
["id"])})
564 engine
.test("Edit user projects non admin", "PUT", "/admin/v1/users/U1", headers_json
,
565 {"remove_project_role_mappings": [{"project": "P1", "role": None}]},
566 401, r_header_json
, "json")
568 res
= engine
.test("Add new project non admin", "POST", "/admin/v1/projects", headers_json
,
569 {"name": "P2"}, 401, r_header_json
, "json")
570 if res
is None or res
.status_code
== 201:
571 # The project has been created even though it shouldn't
572 res
= engine
.test("Get project P2", "GET", "/admin/v1/projects/P2", headers_json
, None,
573 200, r_header_json
, "json")
574 p2
= res
.json()["_id"] if res
else None
577 data
= {"username": "U3", "password": "pw3"}
578 data
["project_role_mappings"] = [{"project": p1
, "role": rpu
}]
579 res
= engine
.test("Add new user non admin", "POST", "/admin/v1/users", headers_json
,
580 data
, 401, r_header_json
, "json")
581 if res
is None or res
.status_code
== 201:
582 # The user has been created even though it shouldn't
583 res
= engine
.test("Get user U3", "GET", "/admin/v1/users/U3", headers_json
, None,
584 200, r_header_json
, "json")
585 u3
= res
.json()["_id"] if res
else None
590 res
= engine
.test("Change to user U2 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
591 {"project_id": "Padmin"}, # Caused a Keystone authentication error
592 # {"username": "U2", "password": "pw2_new", "project_id": "Padmin"},
593 (200, 201), r_header_json
, "json")
596 engine
.set_header({"Authorization": "Bearer {}".format(rj
["id"])})
598 res
= engine
.test("Add new project admin", "POST", "/admin/v1/projects", headers_json
,
599 {"name": "P3"}, (201, 204),
600 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"},
602 p3
= engine
.last_id
if res
else None
605 data
= {"username": "U4", "password": "pw4"}
606 data
["project_role_mappings"] = [{"project": p1
, "role": rpa
}]
607 res
= engine
.test("Add new user admin", "POST", "/admin/v1/users", headers_json
,
609 {"Location": "/admin/v1/users/", "Content-Type": "application/json"},
611 u4
= engine
.last_id
if res
else None
616 data
= {"project_role_mappings": [{"project": p3
, "role": rpa
}]}
617 engine
.test("Edit user projects admin", "PUT", "/admin/v1/users/U4", headers_json
,
618 data
, 204, None, None)
619 # Project is deleted even though it shouldn't - PROVISIONAL?
620 res
= engine
.test("Delete project P3 conflict", "DELETE", "/admin/v1/projects/"+p3
,
621 headers_json
, None, 409, None, None)
622 if res
and res
.status_code
in (200, 204):
625 res
= engine
.test("Delete project P3 forcing", "DELETE",
626 "/admin/v1/projects/"+p3
+"?FORCE=True", headers_json
, None, 204,
628 if res
and res
.status_code
in (200, 204):
632 res
= engine
.test("Delete user U2. Conflict deleting own user", "DELETE",
633 "/admin/v1/users/"+u2
, headers_json
, None, 409, r_header_json
, "json")
634 if res
is None or res
.status_code
in (200, 204):
637 res
= engine
.test("Delete user U4", "DELETE", "/admin/v1/users/"+u4
, headers_json
, None,
639 if res
and res
.status_code
in (200, 204):
642 res
= engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/"+p3
, headers_json
,
643 None, 204, None, None)
644 if res
and res
.status_code
in (200, 204):
648 res
= engine
.test("Delete user U3", "DELETE", "/admin/v1/users/"+u3
, headers_json
, None,
654 engine
.remove_authorization() # To force get authorization
655 engine
.get_autorization()
657 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/"+u1
, headers_json
, None, 204, None, None)
659 engine
.test("Delete user U2", "DELETE", "/admin/v1/users/"+u2
, headers_json
, None, 204, None, None)
661 engine
.test("Delete user U3", "DELETE", "/admin/v1/users/"+u3
, headers_json
, None, 204, None, None)
663 engine
.test("Delete user U4", "DELETE", "/admin/v1/users/"+u4
, headers_json
, None, 204, None, None)
665 engine
.test("Delete project P1", "DELETE", "/admin/v1/projects/"+p1
, headers_json
, None, 204, None, None)
667 engine
.test("Delete project P2", "DELETE", "/admin/v1/projects/"+p2
, headers_json
, None, 204, None, None)
669 engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/"+p3
, headers_json
, None, 204, None, None)
671 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/"+padmin
, headers_json
, None, 204,
674 engine
.test("Delete bad project", "DELETE", "/admin/v1/projects/"+pbad
, headers_json
, None, 204,
677 # BEGIN New Tests - Addressing Projects/Users by Name/ID
680 res
= engine
.test("Create new project P1", "POST", "/admin/v1/projects", headers_json
, {"name": "P1"},
681 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
683 pid1
= res
.json()["id"]
684 # print("# pid =", pid1)
685 res
= engine
.test("Create new project P2", "POST", "/admin/v1/projects", headers_json
, {"name": "P2"},
686 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
688 pid2
= res
.json()["id"]
689 # print("# pid =", pid2)
690 data
= {"username": "U1", "password": "pw1"}
691 data
["project_role_mappings"] = [{"project": pid1
, "role": rpu
}]
692 res
= engine
.test("Create new user U1", "POST", "/admin/v1/users", headers_json
, data
, 201,
693 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
695 uid1
= res
.json()["id"]
696 # print("# uid =", uid1)
697 data
= {"username": "U2", "password": "pw2"}
698 data
["project_role_mappings"] = [{"project": pid2
, "role": rpu
}]
699 res
= engine
.test("Create new user U2", "POST", "/admin/v1/users", headers_json
, data
, 201,
700 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
702 uid2
= res
.json()["id"]
703 # print("# uid =", uid2)
705 engine
.test("Get Project P1 by Name", "GET", "/admin/v1/projects/P1", headers_json
, None,
707 engine
.test("Get Project P1 by ID", "GET", "/admin/v1/projects/"+pid1
, headers_json
, None,
710 engine
.test("Get User U1 by Name", "GET", "/admin/v1/users/U1", headers_json
, None, 200, None, "json")
711 engine
.test("Get User U1 by ID", "GET", "/admin/v1/users/"+uid1
, headers_json
, None, 200, None, "json")
713 res
= engine
.test("Rename Project P1 by Name", "PUT", "/admin/v1/projects/P1", headers_json
,
714 {"name": "P3"}, 204, None, None)
716 engine
.test("Get Project P1 by new Name", "GET", "/admin/v1/projects/P3", headers_json
, None,
719 res
= engine
.test("Rename Project P2 by ID", "PUT", "/admin/v1/projects/"+pid2
, headers_json
,
720 {"name": "P4"}, 204, None, None)
722 engine
.test("Get Project P2 by new Name", "GET", "/admin/v1/projects/P4", headers_json
, None,
726 res
= engine
.test("Rename User U1 by Name", "PUT", "/admin/v1/users/U1", headers_json
,
727 {"username": "U3"}, 204, None, None)
729 engine
.test("Get User U1 by new Name", "GET", "/admin/v1/users/U3", headers_json
, None,
733 res
= engine
.test("Rename User U2 by ID", "PUT", "/admin/v1/users/"+uid2
, headers_json
,
734 {"username": "U4"}, 204, None, None)
736 engine
.test("Get User U2 by new Name", "GET", "/admin/v1/users/U4", headers_json
, None,
739 res
= engine
.test("Delete User U1 by Name", "DELETE", "/admin/v1/users/U3", headers_json
, None,
745 res
= engine
.test("Delete User U2 by ID", "DELETE", "/admin/v1/users/"+uid2
, headers_json
, None,
751 res
= engine
.test("Delete Project P1 by Name", "DELETE", "/admin/v1/projects/P3", headers_json
, None,
757 res
= engine
.test("Delete Project P2 by ID", "DELETE", "/admin/v1/projects/"+pid2
, headers_json
, None,
762 # END New Tests - Addressing Projects/Users by Name
766 engine
.test("Delete Project P1", "DELETE", "/admin/v1/projects/"+pid1
, headers_json
, None, 204, None, None)
768 engine
.test("Delete Project P2", "DELETE", "/admin/v1/projects/"+pid2
, headers_json
, None, 204, None, None)
770 engine
.test("Delete User U1", "DELETE", "/admin/v1/users/"+uid1
, headers_json
, None, 204, None, None)
772 engine
.test("Delete User U2", "DELETE", "/admin/v1/users/"+uid2
, headers_json
, None, 204, None, None)
774 engine
.remove_authorization() # To finish
777 class TestProjectsDescriptors
:
778 description
= "test descriptors visibility among projects"
781 def run(engine
, test_osm
, manual_check
, test_params
=None):
783 engine
.set_test_name("ProjectDescriptors")
784 engine
.get_autorization()
786 project_admin_id
= None
787 res
= engine
.test("Get my project Padmin", "GET", "/admin/v1/projects/{}".format(engine
.project
), headers_json
,
788 None, 200, r_header_json
, "json")
790 response
= res
.json()
791 project_admin_id
= response
["_id"]
792 engine
.test("Create project Padmin", "POST", "/admin/v1/projects", headers_json
,
793 {"name": "Padmin", "admin": True}, (201, 204),
794 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
795 engine
.test("Create project P2", "POST", "/admin/v1/projects", headers_json
, {"name": "P2"},
796 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
797 engine
.test("Create project P3", "POST", "/admin/v1/projects", headers_json
, {"name": "P3"},
798 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
800 engine
.test("Create user U1", "POST", "/admin/v1/users", headers_json
,
801 {"username": "U1", "password": "pw1",
802 "project_role_mappings": [{"project": "Padmin", "role": "system_admin"},
803 {"project": "P2", "role": "project_admin"},
804 {"project": "P3", "role": "project_admin"}],
805 }, 201, {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
807 engine
.test("Onboard VNFD id1", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id1", headers_yaml
,
808 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
809 vnfd_ids
.append(engine
.last_id
)
810 engine
.test("Onboard VNFD id2 PUBLIC", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id2&PUBLIC=TRUE",
811 headers_yaml
, TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
812 vnfd_ids
.append(engine
.last_id
)
813 engine
.test("Onboard VNFD id3", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id3&PUBLIC=FALSE", headers_yaml
,
814 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
815 vnfd_ids
.append(engine
.last_id
)
817 res
= engine
.test("Get VNFD descriptors", "GET", "/vnfpkgm/v1/vnf_packages?id=id1,id2,id3",
818 headers_json
, None, 200, r_header_json
, "json")
819 response
= res
.json()
820 if len(response
) != 3:
821 logger
.error("Only 3 vnfds should be present for project admin. {} listed".format(len(response
)))
822 engine
.failed_tests
+= 1
824 # Change to other project Padmin
825 res
= engine
.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json
,
826 {"username": "U1", "password": "pw1", "project_id": "Padmin"}, (200, 201),
827 r_header_json
, "json")
829 response
= res
.json()
830 engine
.set_header({"Authorization": "Bearer {}".format(response
["id"])})
833 res
= engine
.test("List VNFD descriptors for Padmin", "GET", "/vnfpkgm/v1/vnf_packages",
834 headers_json
, None, 200, r_header_json
, "json")
835 response
= res
.json()
836 if len(response
) != 0:
837 logger
.error("Only 0 vnfds should be present for project Padmin. {} listed".format(len(response
)))
838 engine
.failed_tests
+= 1
841 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages?PUBLIC=True",
842 headers_json
, None, 200, r_header_json
, "json")
843 response
= res
.json()
844 if len(response
) != 1:
845 logger
.error("Only 1 vnfds should be present for project Padmin. {} listed".format(len(response
)))
846 engine
.failed_tests
+= 1
848 # list vnfds belonging to project "admin"
849 res
= engine
.test("List VNFD of admin project", "GET",
850 "/vnfpkgm/v1/vnf_packages?ADMIN={}".format(project_admin_id
),
851 headers_json
, None, 200, r_header_json
, "json")
853 response
= res
.json()
854 if len(response
) != 3:
855 logger
.error("Only 3 vnfds should be present for project Padmin. {} listed".format(len(response
)))
856 engine
.failed_tests
+= 1
859 engine
.test("Get VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
860 headers_json
, None, 200, r_header_json
, "json")
861 # Edit not owned vnfd
862 engine
.test("Edit VNFD ", "PATCH", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
863 headers_yaml
, '{name: pepe}', 404, r_header_yaml
, "yaml")
866 engine
.test("Add VNFD id2 to my catalog", "PATCH", "/vnfpkgm/v1/vnf_packages/{}?SET_PROJECT".
867 format(vnfd_ids
[1]), headers_json
, None, 204, None, 0)
870 engine
.test("Onboard VNFD id4", "POST", "/vnfpkgm/v1/vnf_packages_content?id=id4", headers_yaml
,
871 TestDescriptors
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
872 vnfd_ids
.append(engine
.last_id
)
875 res
= engine
.test("List VNFD public descriptors", "GET", "/vnfpkgm/v1/vnf_packages",
876 headers_json
, None, 200, r_header_json
, "json")
877 response
= res
.json()
878 if len(response
) != 2:
879 logger
.error("Only 2 vnfds should be present for project Padmin. {} listed".format(len(response
)))
880 engine
.failed_tests
+= 1
883 input('VNFDs have been omboarded. Perform manual check and press enter to resume')
885 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
886 headers_yaml
, None, 204, None, 0)
888 # change to admin project
889 engine
.remove_authorization() # To force get authorization
890 engine
.get_autorization()
891 test_rest
.test("Delete VNFD id1", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[0]),
892 headers_yaml
, None, 204, None, 0)
893 test_rest
.test("Delete VNFD id2", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[1]),
894 headers_yaml
, None, 204, None, 0)
895 test_rest
.test("Delete VNFD id3", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[2]),
896 headers_yaml
, None, 204, None, 0)
897 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_ids
[3]),
898 headers_yaml
, None, 404, r_header_yaml
, "yaml")
899 test_rest
.test("Delete VNFD id4", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
900 headers_yaml
, None, 204, None, 0)
902 engine
.test("Get VNFD deleted id1", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[0]),
903 headers_json
, None, 404, r_header_json
, "json")
904 engine
.test("Get VNFD deleted id2", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[1]),
905 headers_json
, None, 404, r_header_json
, "json")
906 engine
.test("Get VNFD deleted id3", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[2]),
907 headers_json
, None, 404, r_header_json
, "json")
908 engine
.test("Get VNFD deleted id4", "GET", "/vnfpkgm/v1/vnf_packages/{}?ADMIN".format(vnfd_ids
[3]),
909 headers_json
, None, 404, r_header_json
, "json")
911 engine
.test("Delete user U1", "DELETE", "/admin/v1/users/U1", headers_json
, None, 204, None, None)
912 engine
.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json
, None, 204, None, None)
913 engine
.test("Delete project P2", "DELETE", "/admin/v1/projects/P2", headers_json
, None, 204, None, None)
914 engine
.test("Delete project P3", "DELETE", "/admin/v1/projects/P3", headers_json
, None, 204, None, None)
918 description
= "Creates/edit/delete fake VIMs and SDN controllers"
922 "schema_version": "1.0",
923 "schema_type": "No idea",
925 "description": "Descriptor name",
926 "vim_type": "openstack",
927 "vim_url": "http://localhost:/vim",
928 "vim_tenant_name": "vimTenant",
930 "vim_password": "password",
931 "config": {"config_param": 1}
935 "description": "sdn-description",
936 "dpid": "50:50:52:54:00:94:21:21",
937 "ip": "192.168.15.17",
939 "type": "opendaylight",
944 self
.port_mapping
= [
945 {"compute_node": "compute node 1",
946 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
947 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
949 {"compute_node": "compute node 2",
950 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
951 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
955 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
957 vim_bad
= self
.vim
.copy()
960 engine
.set_test_name("FakeVim")
961 engine
.get_autorization()
962 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (201, 202),
963 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
964 vim_id
= engine
.last_id
965 engine
.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json
,
966 vim_bad
, 422, None, headers_json
)
967 engine
.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
,
968 409, None, headers_json
)
969 engine
.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml
, None, 200, r_header_yaml
,
971 engine
.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 200,
972 r_header_yaml
, "yaml")
975 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_yaml
,
977 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None,
978 404, r_header_yaml
, "yaml")
980 # delete and wait until is really deleted
981 engine
.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
, None, 202,
983 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
986 class TestVIMSDN(TestFakeVim
):
987 description
= "Creates VIM with SDN editing SDN controllers and port_mapping"
990 TestFakeVim
.__init
__(self
)
992 "schema_version": "1.0",
993 "schema_type": "No idea",
995 "description": "Descriptor name",
997 "wim_url": "http://localhost:/wim",
999 "password": "password",
1000 "config": {"config_param": 1}
1003 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1004 engine
.set_test_name("VimSdn")
1005 engine
.get_autorization()
1007 engine
.test("Create SDN", "POST", "/admin/v1/sdns", headers_json
, self
.sdn
, (201, 202),
1008 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
1009 sdnc_id
= engine
.last_id
1012 engine
.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, {"name": "new_sdn_name"},
1013 (202, 204), None, None)
1016 self
.vim
["config"]["sdn-controller"] = sdnc_id
1017 self
.vim
["config"]["sdn-port-mapping"] = self
.port_mapping
1018 engine
.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json
, self
.vim
, (200, 202, 201),
1019 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
1021 vim_id
= engine
.last_id
1022 self
.port_mapping
[0]["compute_node"] = "compute node XX"
1023 engine
.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
1024 {"config": {"sdn-port-mapping": self
.port_mapping
}}, (202, 204), None, None)
1025 engine
.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_json
,
1026 {"config": {"sdn-port-mapping": None}}, (202, 204), None, None)
1028 engine
.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json
, self
.wim
, (200, 202, 201),
1029 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
1030 wim_id
= engine
.last_id
1034 engine
.test("Delete VIM remove port-mapping", "DELETE",
1035 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id
), headers_json
, None, 202, None, 0)
1036 engine
.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id
), headers_json
, None,
1039 engine
.test("Delete WIM", "DELETE",
1040 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id
), headers_json
, None, 202, None, 0)
1041 engine
.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id
), headers_yaml
,
1042 None, 404, r_header_yaml
, "yaml")
1043 engine
.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id
), headers_yaml
, None,
1044 404, r_header_yaml
, "yaml")
1045 engine
.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id
), headers_yaml
,
1046 None, 404, r_header_yaml
, "yaml")
1049 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
1050 # delete and wait until is really deleted
1051 engine
.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id
),
1052 headers_json
, None, (202, 201, 204), None, 0)
1053 engine
.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id
), headers_json
, None,
1054 (202, 201, 204), None, 0)
1055 engine
.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id
),
1056 headers_json
, None, (202, 201, 204), None, 0)
1057 engine
.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id
), timeout
)
1058 engine
.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id
), timeout
)
1059 engine
.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id
), timeout
)
1063 description
= "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
1066 self
.test_name
= "DEPLOY"
1071 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1072 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1073 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1074 self
.descriptor_edit
= None
1075 self
.uses_configuration
= False
1082 self
.ns_params
= None
1083 self
.vnfr_ip_list
= {}
1085 def create_descriptors(self
, engine
):
1086 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
1087 if not os
.path
.exists(temp_dir
):
1088 os
.makedirs(temp_dir
)
1089 for vnfd_index
, vnfd_filename
in enumerate(self
.vnfd_filenames
):
1090 if "/" in vnfd_filename
:
1091 vnfd_filename_path
= vnfd_filename
1092 if not os
.path
.exists(vnfd_filename_path
):
1093 raise TestException("File '{}' does not exist".format(vnfd_filename_path
))
1095 vnfd_filename_path
= temp_dir
+ vnfd_filename
1096 if not os
.path
.exists(vnfd_filename_path
):
1097 with
open(vnfd_filename_path
, "wb") as file:
1098 response
= requests
.get(self
.descriptor_url
+ vnfd_filename
)
1099 if response
.status_code
>= 300:
1100 raise TestException("Error downloading descriptor from '{}': {}".format(
1101 self
.descriptor_url
+ vnfd_filename
, response
.status_code
))
1102 file.write(response
.content
)
1103 if vnfd_filename_path
.endswith(".yaml"):
1104 headers
= headers_yaml
1106 headers
= headers_zip_yaml
1107 if randint(0, 1) == 0:
1108 # vnfd CREATE AND UPLOAD in one step:
1109 engine
.test("Onboard VNFD in one step", "POST",
1110 "/vnfpkgm/v1/vnf_packages_content" + self
.qforce
, headers
, "@b" + vnfd_filename_path
, 201,
1111 r_headers_yaml_location_vnfd
,
1113 self
.vnfds_id
.append(engine
.last_id
)
1115 # vnfd CREATE AND UPLOAD ZIP
1116 engine
.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
1117 headers_json
, None, 201,
1118 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
1119 self
.vnfds_id
.append(engine
.last_id
)
1120 engine
.test("Onboard VNFD step 2 as ZIP", "PUT",
1121 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self
.qforce
,
1122 headers
, "@b" + vnfd_filename_path
, 204, None, 0)
1124 if self
.descriptor_edit
:
1125 if "vnfd{}".format(vnfd_index
) in self
.descriptor_edit
:
1127 engine
.test("Edit VNFD ", "PATCH",
1128 "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfds_id
[-1]),
1129 headers_yaml
, self
.descriptor_edit
["vnfd{}".format(vnfd_index
)], 204, None, None)
1131 if "/" in self
.nsd_filename
:
1132 nsd_filename_path
= self
.nsd_filename
1133 if not os
.path
.exists(nsd_filename_path
):
1134 raise TestException("File '{}' does not exist".format(nsd_filename_path
))
1136 nsd_filename_path
= temp_dir
+ self
.nsd_filename
1137 if not os
.path
.exists(nsd_filename_path
):
1138 with
open(nsd_filename_path
, "wb") as file:
1139 response
= requests
.get(self
.descriptor_url
+ self
.nsd_filename
)
1140 if response
.status_code
>= 300:
1141 raise TestException("Error downloading descriptor from '{}': {}".format(
1142 self
.descriptor_url
+ self
.nsd_filename
, response
.status_code
))
1143 file.write(response
.content
)
1144 if nsd_filename_path
.endswith(".yaml"):
1145 headers
= headers_yaml
1147 headers
= headers_zip_yaml
1149 if randint(0, 1) == 0:
1150 # nsd CREATE AND UPLOAD in one step:
1151 engine
.test("Onboard NSD in one step", "POST",
1152 "/nsd/v1/ns_descriptors_content" + self
.qforce
, headers
, "@b" + nsd_filename_path
, 201,
1153 r_headers_yaml_location_nsd
, yaml
)
1154 self
.nsd_id
= engine
.last_id
1156 # nsd CREATE AND UPLOAD ZIP
1157 engine
.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
1158 headers_json
, None, 201,
1159 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
1160 self
.nsd_id
= engine
.last_id
1161 engine
.test("Onboard NSD step 2 as ZIP", "PUT",
1162 "/nsd/v1/ns_descriptors/<>/nsd_content" + self
.qforce
,
1163 headers
, "@b" + nsd_filename_path
, 204, None, 0)
1165 if self
.descriptor_edit
and "nsd" in self
.descriptor_edit
:
1167 engine
.test("Edit NSD ", "PATCH",
1168 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
1169 headers_yaml
, self
.descriptor_edit
["nsd"], 204, None, None)
1171 def delete_descriptors(self
, engine
):
1172 # delete descriptors
1173 engine
.test("Delete NSSD SOL005", "DELETE",
1174 "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
),
1175 headers_yaml
, None, 204, None, 0)
1176 for vnfd_id
in self
.vnfds_id
:
1177 engine
.test("Delete VNFD SOL005", "DELETE",
1178 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id
), headers_yaml
, None, 204, None, 0)
1180 def instantiate(self
, engine
, ns_data
):
1181 ns_data_text
= yaml
.safe_dump(ns_data
, default_flow_style
=True, width
=256)
1182 # create NS Two steps
1183 r
= engine
.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
1184 headers_yaml
, ns_data_text
, (201, 202),
1185 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
1188 self
.ns_id
= engine
.last_id
1189 engine
.test("Instantiate NS step 2", "POST",
1190 "/nslcm/v1/ns_instances/{}/instantiate".format(self
.ns_id
), headers_yaml
, ns_data_text
,
1191 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1192 nslcmop_id
= engine
.last_id
1195 # Wait until status is Ok
1196 timeout
= timeout_configure
if self
.uses_configuration
else timeout_deploy
1197 engine
.wait_operation_ready("ns", nslcmop_id
, timeout
)
1199 def terminate(self
, engine
):
1202 engine
.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self
.ns_id
), headers_yaml
,
1203 None, (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1204 nslcmop2_id
= engine
.last_id
1205 # Wait until status is Ok
1206 engine
.wait_operation_ready("ns", nslcmop2_id
, timeout_deploy
)
1208 engine
.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1211 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1212 headers_yaml
, None, 204, None, 0)
1214 # check all it is deleted
1215 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1217 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1218 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1223 if not isinstance(nslcmops
, list) or nslcmops
:
1224 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1226 def test_ns(self
, engine
, test_osm
, commands
=None, users
=None, passwds
=None, keys
=None, timeout
=0):
1228 r
= engine
.test("GET VNFR IDs", "GET",
1229 "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_json
, None,
1230 200, r_header_json
, "json")
1235 vnfr_list
= ns_data
['constituent-vnfr-ref']
1237 _commands
= commands
if commands
is not None else self
.commands
1238 _users
= users
if users
is not None else self
.users
1239 _passwds
= passwds
if passwds
is not None else self
.passwords
1240 _keys
= keys
if keys
is not None else self
.keys
1241 _timeout
= timeout
if timeout
!= 0 else self
.timeout
1243 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
1244 for vnfr_id
in vnfr_list
:
1245 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1246 "/nslcm/v1/vnfrs/{}".format(vnfr_id
), headers_json
, None,
1247 200, r_header_json
, "json")
1250 vnfr_data
= r
.json()
1252 vnf_index
= str(vnfr_data
["member-vnf-index-ref"])
1254 ip_address
= self
.get_vnfr_ip(engine
, vnf_index
)
1255 description
= "Exec command='{}' at VNFR={} IP={}".format(_commands
.get(vnf_index
)[0], vnf_index
,
1258 test_description
= "{}{} {}".format(engine
.test_name
, engine
.step
, description
)
1259 logger
.warning(test_description
)
1260 while _timeout
>= time
:
1261 result
, message
= self
.do_checks([ip_address
],
1262 vnf_index
=vnfr_data
["member-vnf-index-ref"],
1263 commands
=_commands
.get(vnf_index
), user
=_users
.get(vnf_index
),
1264 passwd
=_passwds
.get(vnf_index
), key
=_keys
.get(vnf_index
))
1266 engine
.passed_tests
+= 1
1267 logger
.debug(message
)
1273 engine
.failed_tests
+= 1
1274 logger
.error(message
)
1278 engine
.failed_tests
+= 1
1279 logger
.error(message
)
1281 engine
.failed_tests
+= 1
1282 logger
.error("VNFR {} has not mgmt address. Check failed".format(vnf_index
))
1284 def do_checks(self
, ip
, vnf_index
, commands
=[], user
=None, passwd
=None, key
=None):
1287 from pssh
.clients
import ParallelSSHClient
1288 from pssh
.utils
import load_private_key
1289 from ssh2
import exceptions
as ssh2Exception
1290 except ImportError as e
:
1291 logger
.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
1292 "parallel-ssh urllib3': {}".format(e
))
1293 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
1294 urllib3
.disable_warnings(urllib3
.exceptions
.InsecureRequestWarning
)
1296 p_host
= os
.environ
.get("PROXY_HOST")
1297 p_user
= os
.environ
.get("PROXY_USER")
1298 p_password
= os
.environ
.get("PROXY_PASSWD")
1301 pkey
= load_private_key(key
)
1305 client
= ParallelSSHClient(ip
, user
=user
, password
=passwd
, pkey
=pkey
, proxy_host
=p_host
,
1306 proxy_user
=p_user
, proxy_password
=p_password
, timeout
=10, num_retries
=0)
1307 for cmd
in commands
:
1308 output
= client
.run_command(cmd
)
1310 if output
[ip
[0]].exit_code
:
1311 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip
[0], cmd
,
1312 "\n".join(output
[ip
[0]].stderr
))
1314 return 1, "VNFR {} command '{}' successful".format(ip
[0], cmd
)
1315 except (ssh2Exception
.ChannelFailure
, ssh2Exception
.SocketDisconnectError
, ssh2Exception
.SocketTimeout
,
1316 ssh2Exception
.SocketRecvError
) as e
:
1317 return 0, "Timeout accessing the VNFR {}: {}".format(ip
[0], str(e
))
1318 except Exception as e
:
1319 return -1, "ERROR checking the VNFR {}: {}".format(ip
[0], str(e
))
1321 def additional_operations(self
, engine
, test_osm
, manual_check
):
1324 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1325 engine
.set_test_name(self
.test_name
)
1326 engine
.get_autorization()
1327 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1329 if "vnfd-files" in test_params
:
1330 self
.vnfd_filenames
= test_params
["vnfd-files"].split(",")
1331 if "nsd-file" in test_params
:
1332 self
.nsd_filename
= test_params
["nsd-file"]
1333 if test_params
.get("ns-name"):
1334 nsname
= test_params
["ns-name"]
1335 self
.create_descriptors(engine
)
1337 # create real VIM if not exist
1338 self
.vim_id
= engine
.get_create_vim(test_osm
)
1339 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
1340 "vimAccountId": self
.vim_id
}
1342 ns_data
.update(self
.ns_params
)
1343 if test_params
and test_params
.get("ns-config"):
1344 if isinstance(test_params
["ns-config"], str):
1345 ns_data
.update(yaml
.load(test_params
["ns-config"]), Loader
=yaml
.Loader
)
1347 ns_data
.update(test_params
["ns-config"])
1348 self
.instantiate(engine
, ns_data
)
1351 input('NS has been deployed. Perform manual check and press enter to resume')
1352 if test_osm
and self
.commands
:
1353 self
.test_ns(engine
, test_osm
)
1354 self
.additional_operations(engine
, test_osm
, manual_check
)
1355 self
.terminate(engine
)
1356 self
.delete_descriptors(engine
)
1358 def get_first_ip(self
, ip_string
):
1359 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1360 first_ip
= ip_string
.split(";")[0] if ip_string
else ""
1363 def get_vnfr_ip(self
, engine
, vnfr_index_wanted
):
1364 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1365 ip
= self
.vnfr_ip_list
.get(vnfr_index_wanted
, "")
1367 return self
.get_first_ip(ip
)
1368 r
= engine
.test("Get VNFR to get IP_ADDRESS", "GET",
1369 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1370 vnfr_index_wanted
, self
.ns_id
), headers_json
, None,
1371 200, r_header_json
, "json")
1374 vnfr_data
= r
.json()
1375 if not (vnfr_data
and vnfr_data
[0]):
1377 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1378 ip_list
= vnfr_data
[0].get("ip-address", "")
1380 self
.vnfr_ip_list
[vnfr_index_wanted
] = ip_list
1381 ip
= self
.get_first_ip(ip_list
)
1385 class TestDeployHackfestCirros(TestDeploy
):
1386 description
= "Load and deploy Hackfest cirros_2vnf_ns example"
1390 self
.test_name
= "CIRROS"
1391 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1392 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1393 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1394 self
.users
= {'1': "cirros", '2': "cirros"}
1395 self
.passwords
= {'1': "cubswin:)", '2': "cubswin:)"}
1397 def terminate(self
, engine
):
1398 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1400 engine
.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1401 format(self
.ns_id
), headers_yaml
, None, 202, None, "yaml")
1403 engine
.wait_until_delete("/nslcm/v1/ns_instances/{}".format(self
.ns_id
), timeout_deploy
)
1405 engine
.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self
.ns_id
),
1406 headers_yaml
, None, 204, None, 0)
1408 # check all it is deleted
1409 engine
.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self
.ns_id
), headers_yaml
, None,
1411 r
= engine
.test("Check NSLCMOPs are deleted", "GET",
1412 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self
.ns_id
), headers_json
, None,
1417 if not isinstance(nslcmops
, list) or nslcmops
:
1418 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self
.ns_id
, nslcmops
))
1421 class TestDeployHackfest1(TestDeploy
):
1422 description
= "Load and deploy Hackfest_1_vnfd example"
1426 self
.test_name
= "HACKFEST1-"
1427 self
.vnfd_filenames
= ("hackfest_1_vnfd.tar.gz",)
1428 self
.nsd_filename
= "hackfest_1_nsd.tar.gz"
1429 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1430 # self.users = {'1': "cirros", '2': "cirros"}
1431 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1434 class TestDeployHackfestCirrosScaling(TestDeploy
):
1435 description
= "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1439 self
.test_name
= "CIRROS-SCALE"
1440 self
.vnfd_filenames
= ("cirros_vnf.tar.gz",)
1441 self
.nsd_filename
= "cirros_2vnf_ns.tar.gz"
1442 # Modify VNFD to add scaling and count=2
1443 self
.descriptor_edit
= {
1446 "$id: 'cirros_vnfd-VM'": {"count": 2}
1448 "scaling-group-descriptor": [{
1449 "name": "scale_cirros",
1450 "max-instance-count": 2,
1452 "vdu-id-ref": "cirros_vnfd-VM",
1459 def additional_operations(self
, engine
, test_osm
, manual_check
):
1462 # 2 perform scale out twice
1463 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1464 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1465 for i
in range(0, 2):
1466 engine
.test("Execute scale action over NS", "POST",
1467 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1468 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1469 nslcmop2_scale_out
= engine
.last_id
1470 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1472 input('NS scale out done. Check that two more vdus are there')
1473 # TODO check automatic
1475 # 2 perform scale in
1476 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1477 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1478 for i
in range(0, 2):
1479 engine
.test("Execute scale IN action over NS", "POST",
1480 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1481 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1482 nslcmop2_scale_in
= engine
.last_id
1483 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1485 input('NS scale in done. Check that two less vdus are there')
1486 # TODO check automatic
1488 # perform scale in that must fail as reached limit
1489 engine
.test("Execute scale IN out of limit action over NS", "POST",
1490 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1491 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1492 nslcmop2_scale_in
= engine
.last_id
1493 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
, expected_fail
=True)
1496 class TestDeployIpMac(TestDeploy
):
1497 description
= "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1501 self
.test_name
= "SetIpMac"
1502 self
.vnfd_filenames
= ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1503 self
.nsd_filename
= "scenario_2vdu_set_ip_mac.yaml"
1504 self
.descriptor_url
= \
1505 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1506 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1507 self
.users
= {'1': "osm", '2': "osm"}
1508 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1511 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
1512 # super().run(engine, test_osm, manual_check, test_params)
1513 # run again setting IPs with instantiate parameters
1514 instantiation_params
= {
1517 "member-vnf-index": "1",
1520 "name": "internal_vld1", # net_internal
1522 "ip-version": "ipv4",
1523 "subnet-address": "10.9.8.0/24",
1524 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1526 "internal-connection-point": [
1529 "ip-address": "10.9.8.2",
1533 "ip-address": "10.9.8.3",
1544 # "name": "iface11",
1545 # "floating-ip-required": True,
1549 "mac-address": "52:33:44:55:66:13"
1558 "ip-address": "10.31.31.22",
1559 "mac-address": "52:33:44:55:66:21"
1568 super().run(engine
, test_osm
, manual_check
, test_params
={"ns-config": instantiation_params
})
1571 class TestDeployHackfest4(TestDeploy
):
1572 description
= "Load and deploy Hackfest 4 example."
1576 self
.test_name
= "HACKFEST4-"
1577 self
.vnfd_filenames
= ("hackfest_4_vnfd.tar.gz",)
1578 self
.nsd_filename
= "hackfest_4_nsd.tar.gz"
1579 self
.uses_configuration
= True
1580 self
.commands
= {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1581 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1582 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1583 # Modify VNFD to add scaling
1584 # self.descriptor_edit = {
1586 # 'vnf-configuration': {
1587 # 'config-primitive': [{
1590 # 'name': 'filename',
1591 # 'data-type': 'STRING',
1592 # 'default-value': '/home/ubuntu/touched'
1596 # 'scaling-group-descriptor': [{
1597 # 'name': 'scale_dataVM',
1598 # 'scaling-policy': [{
1599 # 'threshold-time': 0,
1600 # 'name': 'auto_cpu_util_above_threshold',
1601 # 'scaling-type': 'automatic',
1602 # 'scaling-criteria': [{
1603 # 'name': 'cpu_util_above_threshold',
1604 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1605 # 'scale-out-relational-operation': 'GE',
1606 # 'scale-in-threshold': 15,
1607 # 'scale-out-threshold': 60,
1608 # 'scale-in-relational-operation': 'LE'
1610 # 'cooldown-time': 60
1612 # 'max-instance-count': 10,
1613 # 'scaling-config-action': [
1614 # {'vnf-config-primitive-name-ref': 'touch',
1615 # 'trigger': 'post-scale-out'},
1616 # {'vnf-config-primitive-name-ref': 'touch',
1617 # 'trigger': 'pre-scale-in'}
1620 # 'vdu-id-ref': 'dataVM',
1628 class TestDeployHackfest3Charmed(TestDeploy
):
1629 description
= "Load and deploy Hackfest 3charmed_ns example"
1633 self
.test_name
= "HACKFEST3-"
1634 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz",)
1635 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
1636 self
.uses_configuration
= True
1637 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1638 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1639 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1640 self
.descriptor_edit
= {
1641 "vnfd0": yaml
.safe_load(
1644 terminate-config-primitive:
1649 value: '/home/ubuntu/last-touch1'
1654 value: '/home/ubuntu/last-touch3'
1659 value: '/home/ubuntu/last-touch2'
1663 def additional_operations(self
, engine
, test_osm
, manual_check
):
1667 vnfr_index_selected
= "2"
1668 payload
= '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1669 engine
.test("Exec service primitive over NS", "POST",
1670 "/nslcm/v1/ns_instances/{}/action".format(self
.ns_id
), headers_yaml
, payload
,
1671 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1672 nslcmop2_action
= engine
.last_id
1673 # Wait until status is Ok
1674 engine
.wait_operation_ready("ns", nslcmop2_action
, timeout_deploy
)
1675 vnfr_ip
= self
.get_vnfr_ip(engine
, vnfr_index_selected
)
1678 "NS service primitive has been executed."
1679 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1682 commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1683 self
.test_ns(engine
, test_osm
, commands
=commands
)
1685 # # 2 perform scale out
1686 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1687 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1688 # engine.test("Execute scale action over NS", "POST",
1689 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1690 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1691 # nslcmop2_scale_out = engine.last_id
1692 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1694 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1695 # # TODO check automatic
1697 # # 2 perform scale in
1698 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1699 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1700 # engine.test("Execute scale action over NS", "POST",
1701 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1702 # (201, 202), r_headers_yaml_location_nslcmop, "yaml")
1703 # nslcmop2_scale_in = engine.last_id
1704 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1706 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1707 # # TODO check automatic
1710 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed
):
1711 description
= "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1712 "ids and member-vnf-index."
1716 self
.test_name
= "HACKFEST3v2-"
1717 self
.qforce
= "?FORCE=True"
1718 self
.descriptor_edit
= {
1722 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1726 "vnf-configuration": None,
1727 "connection-point": {
1731 "short-name": "pdu-mgmt"
1735 "mgmt-interface": {"cp": "pdu-mgmt"},
1736 "description": "A vnf single vdu to be used as PDU",
1740 "id": "pdu_internal",
1741 "name": "pdu_internal",
1742 "internal-connection-point": {"$[1]": None},
1743 "short-name": "pdu_internal",
1749 # Modify NSD accordingly
1751 "constituent-vnfd": {
1752 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1755 "description": "A nsd to deploy the vnf to act as as PDU",
1757 "name": "nsd-as-pdu",
1758 "short-name": "nsd-as-pdu",
1763 "short-name": "mgmt_pdu",
1764 "vnfd-connection-point-ref": {
1766 "vnfd-connection-point-ref": "pdu-mgmt",
1767 "vnfd-id-ref": "vdu-as-pdu",
1779 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed
):
1780 description
= "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1784 self
.test_name
= "HACKFEST3v3-"
1785 self
.commands
= {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1786 self
.descriptor_edit
= {
1789 scaling-group-descriptor:
1790 - name: "scale_dataVM"
1791 max-instance-count: 10
1793 - name: "auto_cpu_util_above_threshold"
1794 scaling-type: "automatic"
1798 - name: "cpu_util_above_threshold"
1799 scale-in-threshold: 15
1800 scale-in-relational-operation: "LE"
1801 scale-out-threshold: 60
1802 scale-out-relational-operation: "GE"
1803 vnf-monitoring-param-ref: "monitor1"
1805 - vdu-id-ref: dataVM
1807 scaling-config-action:
1808 - trigger: post-scale-out
1809 vnf-config-primitive-name-ref: touch
1810 - trigger: pre-scale-in
1811 vnf-config-primitive-name-ref: touch
1815 - id: "dataVM_cpu_util"
1816 nfvi-metric: "cpu_utilization"
1821 aggregation-type: AVERAGE
1822 vdu-monitoring-param:
1824 vdu-monitoring-param-ref: "dataVM_cpu_util"
1826 initial-config-primitive:
1830 value: "<touch_filename>" # default-value: /home/ubuntu/first-touch
1835 default-value: "<touch_filename2>"
1840 "additionalParamsForVnf": [
1841 {"member-vnf-index": "1", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-1",
1842 "touch_filename2": "/home/ubuntu/second-touch-1"}},
1843 {"member-vnf-index": "2", "additionalParams": {"touch_filename": "/home/ubuntu/first-touch-2",
1844 "touch_filename2": "/home/ubuntu/second-touch-2"}},
1848 def additional_operations(self
, engine
, test_osm
, manual_check
):
1849 super().additional_operations(engine
, test_osm
, manual_check
)
1853 # 2 perform scale out
1854 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1855 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1856 engine
.test("Execute scale action over NS", "POST",
1857 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1858 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1859 nslcmop2_scale_out
= engine
.last_id
1860 engine
.wait_operation_ready("ns", nslcmop2_scale_out
, timeout_deploy
)
1862 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1864 commands
= {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1865 self
.test_ns(engine
, test_osm
, commands
=commands
)
1866 # TODO check automatic connection to scaled VM
1868 # 2 perform scale in
1869 payload
= '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1870 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1871 engine
.test("Execute scale action over NS", "POST",
1872 "/nslcm/v1/ns_instances/{}/scale".format(self
.ns_id
), headers_yaml
, payload
,
1873 (201, 202), r_headers_yaml_location_nslcmop
, "yaml")
1874 nslcmop2_scale_in
= engine
.last_id
1875 engine
.wait_operation_ready("ns", nslcmop2_scale_in
, timeout_deploy
)
1877 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1878 # TODO check automatic
1881 class TestDeploySimpleCharm(TestDeploy
):
1882 description
= "Deploy hackfest-4 hackfest_simplecharm example"
1886 self
.test_name
= "HACKFEST-SIMPLE"
1887 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1888 self
.vnfd_filenames
= ("hackfest_simplecharm_vnf.tar.gz",)
1889 self
.nsd_filename
= "hackfest_simplecharm_ns.tar.gz"
1890 self
.uses_configuration
= True
1891 self
.commands
= {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1892 self
.users
= {'1': "ubuntu", '2': "ubuntu"}
1893 self
.passwords
= {'1': "osm4u", '2': "osm4u"}
1896 class TestDeploySimpleCharm2(TestDeploySimpleCharm
):
1897 description
= "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1902 self
.test_name
= "HACKFEST-SIMPLE2-"
1903 self
.qforce
= "?FORCE=True"
1904 self
.descriptor_edit
= {
1906 "id": "hackfest.simplecharm.vnf"
1910 "id": "hackfest.simplecharm.ns",
1911 "constituent-vnfd": {
1912 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1913 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1917 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1918 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1919 "$[1]": {"member-vnf-index-ref": "$2",
1920 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1923 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1924 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1925 "$[1]": {"member-vnf-index-ref": "$2",
1926 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1933 class TestDeploySingleVdu(TestDeployHackfest3Charmed
):
1934 description
= "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1938 self
.test_name
= "SingleVDU"
1939 self
.qforce
= "?FORCE=True"
1940 self
.descriptor_edit
= {
1941 # Modify VNFD to remove one VDU
1945 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1949 "vnf-configuration": None,
1950 "connection-point": {
1954 "short-name": "pdu-mgmt"
1958 "mgmt-interface": {"cp": "pdu-mgmt"},
1959 "description": "A vnf single vdu to be used as PDU",
1963 "id": "pdu_internal",
1964 "name": "pdu_internal",
1965 "internal-connection-point": {"$[1]": None},
1966 "short-name": "pdu_internal",
1972 # Modify NSD accordingly
1974 "constituent-vnfd": {
1975 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1978 "description": "A nsd to deploy the vnf to act as as PDU",
1980 "name": "nsd-as-pdu",
1981 "short-name": "nsd-as-pdu",
1986 "short-name": "mgmt_pdu",
1987 "vnfd-connection-point-ref": {
1989 "vnfd-connection-point-ref": "pdu-mgmt",
1990 "vnfd-id-ref": "vdu-as-pdu",
2002 class TestDeployHnfd(TestDeployHackfest3Charmed
):
2003 description
= "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
2007 self
.test_name
= "HNFD"
2008 self
.pduDeploy
= TestDeploySingleVdu()
2009 self
.pdu_interface_0
= {}
2010 self
.pdu_interface_1
= {}
2013 # self.vnf_to_pdu = """
2016 # pdu-type: PDU-TYPE-1
2021 # name: pdu-iface-internal
2023 # description: HFND, one PDU + One VDU
2029 self
.pdu_descriptor
= {
2031 "type": "PDU-TYPE-1",
2032 "vim_accounts": "to-override",
2035 "name": "mgmt-iface",
2038 "ip-address": "to override",
2039 "mac-address": "mac_address",
2040 "vim-network-name": "mgmt",
2043 "name": "pdu-iface-internal",
2046 "ip-address": "to override",
2047 "mac-address": "mac_address",
2048 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
2052 self
.vnfd_filenames
= ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
2054 self
.descriptor_edit
= {
2058 "short-name": "hfn1",
2061 "pdu-type": "PDU-TYPE-1",
2063 "$[0]": {"name": "mgmt-iface"},
2064 "$[1]": {"name": "pdu-iface-internal"},
2070 "constituent-vnfd": {
2071 "$[1]": {"vnfd-id-ref": "hfnd1"}
2074 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
2075 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
2080 def create_descriptors(self
, engine
):
2081 super().create_descriptors(engine
)
2084 self
.pdu_descriptor
["interfaces"][0].update(self
.pdu_interface_0
)
2085 self
.pdu_descriptor
["interfaces"][1].update(self
.pdu_interface_1
)
2086 self
.pdu_descriptor
["vim_accounts"] = [self
.vim_id
]
2087 # TODO get vim-network-name from vnfr.vld.name
2088 self
.pdu_descriptor
["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
2089 os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
2090 "PDU", self
.pdu_descriptor
["interfaces"][1]["vim-network-name"])
2091 engine
.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
2092 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self
.pdu_descriptor
,
2093 201, r_header_yaml
, "yaml")
2094 self
.pdu_id
= engine
.last_id
2096 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2097 engine
.get_autorization()
2098 engine
.set_test_name(self
.test_name
)
2099 nsname
= os
.environ
.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
2101 # create real VIM if not exist
2102 self
.vim_id
= engine
.get_create_vim(test_osm
)
2104 self
.pduDeploy
.create_descriptors(engine
)
2105 self
.pduDeploy
.instantiate(engine
, {"nsDescription": "to be used as PDU", "nsName": nsname
+ "-PDU",
2106 "nsdId": self
.pduDeploy
.nsd_id
, "vimAccountId": self
.vim_id
})
2108 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
2110 self
.pduDeploy
.test_ns(engine
, test_osm
)
2113 r
= engine
.test("Get VNFR to obtain IP_ADDRESS", "GET",
2114 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self
.pduDeploy
.ns_id
), headers_json
, None,
2115 200, r_header_json
, "json")
2118 vnfr_data
= r
.json()
2121 self
.pdu_interface_0
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("ip-address")
2122 self
.pdu_interface_1
["ip-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("ip-address")
2123 self
.pdu_interface_0
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][0].get("mac-address")
2124 self
.pdu_interface_1
["mac-address"] = vnfr_data
[0]["vdur"][0]["interfaces"][1].get("mac-address")
2125 if not self
.pdu_interface_0
["ip-address"]:
2126 raise TestException("Vnfr has not managment ip address")
2128 self
.pdu_interface_0
["ip-address"] = "192.168.10.10"
2129 self
.pdu_interface_1
["ip-address"] = "192.168.11.10"
2130 self
.pdu_interface_0
["mac-address"] = "52:33:44:55:66:13"
2131 self
.pdu_interface_1
["mac-address"] = "52:33:44:55:66:14"
2133 self
.create_descriptors(engine
)
2135 ns_data
= {"nsDescription": "default description", "nsName": nsname
, "nsdId": self
.nsd_id
,
2136 "vimAccountId": self
.vim_id
}
2137 if test_params
and test_params
.get("ns-config"):
2138 if isinstance(test_params
["ns-config"], str):
2139 ns_data
.update(yaml
.load(test_params
["ns-config"]), Loader
=yaml
.Loader
)
2141 ns_data
.update(test_params
["ns-config"])
2143 self
.instantiate(engine
, ns_data
)
2145 input('NS has been deployed. Perform manual check and press enter to resume')
2147 self
.test_ns(engine
, test_osm
)
2148 self
.additional_operations(engine
, test_osm
, manual_check
)
2149 self
.terminate(engine
)
2150 self
.pduDeploy
.terminate(engine
)
2151 self
.delete_descriptors(engine
)
2152 self
.pduDeploy
.delete_descriptors(engine
)
2154 def delete_descriptors(self
, engine
):
2155 super().delete_descriptors(engine
)
2157 engine
.test("Delete PDU SOL005", "DELETE",
2158 "/pdu/v1/pdu_descriptors/{}".format(self
.pdu_id
),
2159 headers_yaml
, None, 204, None, 0)
2162 class TestDescriptors
:
2163 description
= "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
2164 vnfd_empty
= """vnfd:vnfd-catalog:
2170 vnfd_prova
= """vnfd:vnfd-catalog:
2182 - external-connection-point-ref: cp_0h8m
2191 self
.vnfd_filename
= "hackfest_3charmed_vnfd.tar.gz"
2192 self
.nsd_filename
= "hackfest_3charmed_nsd.tar.gz"
2193 self
.descriptor_url
= "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
2197 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2198 engine
.set_test_name("Descriptors")
2199 engine
.get_autorization()
2200 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2201 if not os
.path
.exists(temp_dir
):
2202 os
.makedirs(temp_dir
)
2205 for filename
in (self
.vnfd_filename
, self
.nsd_filename
):
2206 filename_path
= temp_dir
+ filename
2207 if not os
.path
.exists(filename_path
):
2208 with
open(filename_path
, "wb") as file:
2209 response
= requests
.get(self
.descriptor_url
+ filename
)
2210 if response
.status_code
>= 300:
2211 raise TestException("Error downloading descriptor from '{}': {}".format(
2212 self
.descriptor_url
+ filename
, response
.status_code
))
2213 file.write(response
.content
)
2215 vnfd_filename_path
= temp_dir
+ self
.vnfd_filename
2216 nsd_filename_path
= temp_dir
+ self
.nsd_filename
2218 engine
.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2219 self
.vnfd_empty
, 201, r_headers_yaml_location_vnfd
, "yaml")
2220 self
.vnfd_id
= engine
.last_id
2223 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2224 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2226 engine
.test("Upload VNFD {}".format(self
.vnfd_filename
), "PUT",
2227 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip_yaml
,
2228 "@b" + vnfd_filename_path
, 204, None, 0)
2230 queries
= ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
2231 "vdu.0.interface.1.internal-connection-point-ref=internal",
2232 "internal-vld.0.internal-connection-point.0.id-ref=internal",
2233 # Detection of duplicated VLD names in VNF Descriptors
2234 # URL: internal-vld=[
2235 # {id: internal1, name: internal, type:ELAN,
2236 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
2237 # {id: internal2, name: internal, type:ELAN,
2238 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
2240 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
2241 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
2242 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
2243 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
2244 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
2246 for query
in queries
:
2247 engine
.test("Upload invalid VNFD ", "PUT",
2248 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self
.vnfd_id
, query
),
2249 headers_zip_yaml
, "@b" + vnfd_filename_path
, 422, r_header_yaml
, "yaml")
2252 engine
.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
),
2253 headers_yaml
, self
.vnfd_prova
, 422, r_header_yaml
, "yaml")
2255 # get vnfd descriptor
2256 engine
.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2257 headers_yaml
, None, 200, r_header_yaml
, "yaml")
2259 # get vnfd file descriptor
2260 engine
.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self
.vnfd_id
),
2261 headers_text
, None, 200, r_header_text
, "text", temp_dir
+"vnfd-yaml")
2262 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
2264 # get vnfd zip file package
2265 engine
.test("Get VNFD zip package", "GET",
2266 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self
.vnfd_id
), headers_zip
, None, 200,
2267 r_header_zip
, "zip", temp_dir
+"vnfd-zip")
2268 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
2271 engine
.test("Get VNFD artifact package", "GET",
2272 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self
.vnfd_id
), headers_zip
, None, 200,
2273 r_header_octect
, "octet-string", temp_dir
+"vnfd-icon")
2274 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
2276 # nsd CREATE AND UPLOAD in one step:
2277 engine
.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml
,
2278 "@b" + nsd_filename_path
, 201, r_headers_yaml_location_nsd
, "yaml")
2279 self
.nsd_id
= engine
.last_id
2281 queries
= ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
2282 for query
in queries
:
2283 engine
.test("Upload invalid NSD ", "PUT",
2284 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self
.nsd_id
, query
),
2285 headers_zip_yaml
, "@b" + nsd_filename_path
, 422, r_header_yaml
, "yaml")
2287 # get nsd descriptor
2288 engine
.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
,
2289 None, 200, r_header_yaml
, "yaml")
2291 # get nsd file descriptor
2292 engine
.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self
.nsd_id
), headers_text
,
2293 None, 200, r_header_text
, "text", temp_dir
+"nsd-yaml")
2294 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
2296 # get nsd zip file package
2297 engine
.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self
.nsd_id
),
2298 headers_zip
, None, 200, r_header_zip
, "zip", temp_dir
+"nsd-zip")
2299 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
2302 engine
.test("Get NSD artifact package", "GET",
2303 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self
.nsd_id
), headers_zip
, None, 200,
2304 r_header_octect
, "octet-string", temp_dir
+"nsd-icon")
2305 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
2308 test_rest
.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_id
),
2309 headers_yaml
, None, 409, None, None)
2311 test_rest
.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self
.vnfd_id
),
2312 headers_yaml
, None, 204, None, 0)
2315 test_rest
.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_id
), headers_yaml
, None, 204,
2319 class TestNetSliceTemplates
:
2320 description
= "Upload a NST to OSM"
2323 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2324 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2325 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2326 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2327 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2329 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2331 engine
.set_test_name("NST step ")
2332 engine
.get_autorization()
2333 temp_dir
= os
.path
.dirname(os
.path
.abspath(__file__
)) + "/temp/"
2334 if not os
.path
.exists(temp_dir
):
2335 os
.makedirs(temp_dir
)
2338 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2339 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2340 self
.vnfd_edge_id
= engine
.last_id
2342 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2343 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2344 self
.vnfd_middle_id
= engine
.last_id
2347 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2348 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2349 self
.nsd_edge_id
= engine
.last_id
2351 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2352 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2353 self
.nsd_middle_id
= engine
.last_id
2356 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2357 201, r_headers_yaml_location_nst
, "yaml")
2358 nst_id
= engine
.last_id
2360 # nstd SHOW OSM format
2361 engine
.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2362 200, r_header_json
, "json")
2365 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2369 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2370 headers_json
, None, 204, None, 0)
2372 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2376 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2377 headers_yaml
, None, 204, None, 0)
2379 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2380 headers_yaml
, None, 204, None, 0)
2383 class TestNetSliceInstances
:
2386 1. Populate databases with VNFD, NSD, NST with the following scenario
2387 +-----------------management-----------------+
2389 +--+---+ +----+----+ +---+--+
2391 | edge +---data1----+ middle +---data2-----+ edge |
2393 +------+ +---------+ +------+
2396 3. Instantiate NSI-1
2398 5. Instantiate NSI-2
2399 Manual check - Are 2 slices instantiated correctly?
2400 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2401 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2404 Manual check - Is slice NSI-1 deleted correctly?
2405 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2407 9. Instantiate NSI-3
2408 Manual check - Is slice NSI-3 instantiated correctly?
2409 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2414 Manual check - All cleaned correctly?
2415 NSI-2 and NSI-3 were terminated and deleted
2416 14. Cleanup database
2419 description
= "Upload a NST to OSM"
2423 self
.vnfd_filename
= ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2424 self
.vnfd_filename_middle
= ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2425 self
.nsd_filename
= ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2426 self
.nsd_filename_middle
= ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2427 self
.nst_filenames
= ("@./slice_shared/slice_shared_nstd.yaml")
2429 def create_slice(self
, engine
, nsi_data
, name
):
2430 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2431 r
= engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances",
2432 headers_yaml
, ns_data_text
, (201, 202),
2433 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2436 def instantiate_slice(self
, engine
, nsi_data
, nsi_id
, name
):
2437 ns_data_text
= yaml
.safe_dump(nsi_data
, default_flow_style
=True, width
=256)
2438 engine
.test(name
, "POST",
2439 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id
), headers_yaml
, ns_data_text
,
2440 (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2442 def terminate_slice(self
, engine
, nsi_id
, name
):
2443 engine
.test(name
, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id
),
2444 headers_yaml
, None, (201, 202), r_headers_yaml_location_nsilcmop
, "yaml")
2446 def delete_slice(self
, engine
, nsi_id
, name
):
2447 engine
.test(name
, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id
), headers_yaml
, None,
2450 def run(self
, engine
, test_osm
, manual_check
, test_params
=None):
2452 engine
.set_test_name("NSI")
2453 engine
.get_autorization()
2456 engine
.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2457 self
.vnfd_filename
, 201, r_headers_yaml_location_vnfd
, "yaml")
2458 self
.vnfd_edge_id
= engine
.last_id
2460 engine
.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml
,
2461 self
.vnfd_filename_middle
, 201, r_headers_yaml_location_vnfd
, "yaml")
2462 self
.vnfd_middle_id
= engine
.last_id
2465 engine
.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2466 self
.nsd_filename
, 201, r_headers_yaml_location_nsd
, "yaml")
2467 self
.nsd_edge_id
= engine
.last_id
2469 engine
.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml
,
2470 self
.nsd_filename_middle
, 201, r_headers_yaml_location_nsd
, "yaml")
2471 self
.nsd_middle_id
= engine
.last_id
2474 engine
.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml
, self
.nst_filenames
,
2475 201, r_headers_yaml_location_nst
, "yaml")
2476 nst_id
= engine
.last_id
2478 self
.vim_id
= engine
.get_create_vim(test_osm
)
2481 ns_data
= {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2482 r
= self
.create_slice(engine
, ns_data
, "Create NSI-1 step 1")
2485 self
.nsi_id1
= engine
.last_id
2488 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id1
, "Instantiate NSI-1 step 2")
2489 nsilcmop_id1
= engine
.last_id
2493 engine
.wait_operation_ready("nsi", nsilcmop_id1
, timeout_deploy
)
2496 ns_data
= {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2497 r
= self
.create_slice(engine
, ns_data
, "Create NSI-2 step 1")
2500 self
.nsi_id2
= engine
.last_id
2503 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id2
, "Instantiate NSI-2 step 2")
2504 nsilcmop_id2
= engine
.last_id
2508 engine
.wait_operation_ready("nsi", nsilcmop_id2
, timeout_deploy
)
2511 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2515 self
.terminate_slice(engine
, self
.nsi_id1
, "Terminate NSI-1")
2516 nsilcmop1_id
= engine
.last_id
2518 # Wait terminate NSI-1
2519 engine
.wait_operation_ready("nsi", nsilcmop1_id
, timeout_deploy
)
2522 self
.delete_slice(engine
, self
.nsi_id1
, "Delete NS")
2525 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2528 ns_data
= {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self
.vim_id
, 'nstId': nst_id
, 'nsiDescription': 'default'}
2529 r
= self
.create_slice(engine
, ns_data
, "Create NSI-3 step 1")
2533 self
.nsi_id3
= engine
.last_id
2536 self
.instantiate_slice(engine
, ns_data
, self
.nsi_id3
, "Instantiate NSI-3 step 2")
2537 nsilcmop_id3
= engine
.last_id
2539 # Wait Instantiate NSI-3
2541 engine
.wait_operation_ready("nsi", nsilcmop_id3
, timeout_deploy
)
2544 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2548 self
.terminate_slice(engine
, self
.nsi_id2
, "Terminate NSI-2")
2549 nsilcmop2_id
= engine
.last_id
2551 # Wait terminate NSI-2
2552 engine
.wait_operation_ready("nsi", nsilcmop2_id
, timeout_deploy
)
2555 self
.delete_slice(engine
, self
.nsi_id2
, "DELETE NSI-2")
2559 self
. terminate_slice(engine
, self
.nsi_id3
, "Terminate NSI-3")
2560 nsilcmop3_id
= engine
.last_id
2562 # Wait terminate NSI-3
2563 engine
.wait_operation_ready("nsi", nsilcmop3_id
, timeout_deploy
)
2566 self
.delete_slice(engine
, self
.nsi_id3
, "DELETE NSI-3")
2569 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2572 engine
.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id
), headers_json
, None,
2576 test_rest
.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_middle_id
),
2577 headers_json
, None, 204, None, 0)
2579 test_rest
.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self
.nsd_edge_id
), headers_json
,
2583 test_rest
.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_edge_id
),
2584 headers_yaml
, None, 204, None, 0)
2586 test_rest
.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self
.vnfd_middle_id
),
2587 headers_yaml
, None, 204, None, 0)
2590 class TestAuthentication
:
2591 description
= "Test Authentication"
2594 def run(engine
, test_osm
, manual_check
, test_params
=None):
2595 engine
.set_test_name("Authentication")
2596 # backend = test_params.get("backend") if test_params else None # UNUSED
2598 admin_project_id
= test_project_id
= None
2599 project_admin_role_id
= project_user_role_id
= None
2600 test_user_id
= empty_user_id
= None
2601 default_role_id
= empty_role_id
= token_role_id
= None
2603 engine
.get_autorization()
2606 engine
.test("Get tokens", "GET", "/admin/v1/tokens", headers_json
, {},
2607 (200), {"Content-Type": "application/json"}, "json")
2608 engine
.test("Get projects", "GET", "/admin/v1/projects", headers_json
, {},
2609 (200), {"Content-Type": "application/json"}, "json")
2610 engine
.test("Get users", "GET", "/admin/v1/users", headers_json
, {},
2611 (200), {"Content-Type": "application/json"}, "json")
2612 engine
.test("Get roles", "GET", "/admin/v1/roles", headers_json
, {},
2613 (200), {"Content-Type": "application/json"}, "json")
2614 res
= engine
.test("Get admin project", "GET", "/admin/v1/projects?name=admin", headers_json
, {},
2615 (200), {"Content-Type": "application/json"}, "json")
2616 admin_project_id
= res
.json()[0]["_id"] if res
else None
2617 res
= engine
.test("Get project admin role", "GET", "/admin/v1/roles?name=project_admin", headers_json
, {},
2618 (200), {"Content-Type": "application/json"}, "json")
2619 project_admin_role_id
= res
.json()[0]["_id"] if res
else None
2620 res
= engine
.test("Get project user role", "GET", "/admin/v1/roles?name=project_user", headers_json
, {},
2621 (200), {"Content-Type": "application/json"}, "json")
2622 project_user_role_id
= res
.json()[0]["_id"] if res
else None
2625 res
= engine
.test("Create test project", "POST", "/admin/v1/projects", headers_json
, {"name": "test"},
2626 (201), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
2627 test_project_id
= engine
.last_id
if res
else None
2628 res
= engine
.test("Create role without permissions", "POST", "/admin/v1/roles", headers_json
, {"name": "empty"},
2629 (201), {"Content-Type": "application/json"}, "json")
2630 empty_role_id
= engine
.last_id
if res
else None
2631 res
= engine
.test("Create role with default permissions", "POST", "/admin/v1/roles", headers_json
,
2632 {"name": "default", "permissions": {"default": True}},
2633 (201), {"Location": "/admin/v1/roles/", "Content-Type": "application/json"}, "json")
2634 default_role_id
= engine
.last_id
if res
else None
2635 res
= engine
.test("Create role with token permissions", "POST", "/admin/v1/roles", headers_json
,
2636 {"name": "tokens", "permissions": {"tokens": True}}, # is default required ?
2637 (201), {"Location": "/admin/v1/roles/", "Content-Type": "application/json"}, "json")
2638 token_role_id
= engine
.last_id
if res
else None
2639 pr
= "project-role mappings"
2640 res
= engine
.test("Create user without "+pr
, "POST", "/admin/v1/users", headers_json
,
2641 {"username": "empty", "password": "empty"},
2642 201, {"Content-Type": "application/json"}, "json")
2643 empty_user_id
= engine
.last_id
if res
else None
2644 if admin_project_id
and test_project_id
and project_admin_role_id
and project_user_role_id
:
2645 data
= {"username": "test", "password": "test"}
2646 data
["project_role_mappings"] = [
2647 {"project": test_project_id
, "role": project_admin_role_id
},
2648 {"project": admin_project_id
, "role": project_user_role_id
}
2650 res
= engine
.test("Create user with "+pr
, "POST", "/admin/v1/users", headers_json
, data
,
2651 (201), {"Content-Type": "application/json"}, "json")
2652 test_user_id
= engine
.last_id
if res
else None
2656 engine
.test("Modify test user's password", "PUT", "/admin/v1/users/"+test_user_id
, headers_json
,
2657 {"password": "password"},
2659 if empty_user_id
and admin_project_id
and test_project_id
and project_admin_role_id
and project_user_role_id
:
2660 data
= {"project_role_mappings": [
2661 {"project": test_project_id
, "role": project_admin_role_id
},
2662 {"project": admin_project_id
, "role": project_user_role_id
}
2664 engine
.test("Modify empty user's "+pr
, "PUT", "/admin/v1/users/"+empty_user_id
,