Bug 643 - Unable to rename a Project in OSM
[osm/NBI.git] / osm_nbi / tests / test.py
1 #! /usr/bin/python3
2 # -*- coding: utf-8 -*-
3
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
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
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
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import getopt
18 import sys
19 import requests
20 import json
21 import logging
22 import yaml
23 # import json
24 # import tarfile
25 from time import sleep
26 from random import randint
27 import os
28 from sys import stderr
29
30 __author__ = "Alfonso Tierno, alfonso.tiernosepulveda@telefonica.com"
31 __date__ = "$2018-03-01$"
32 __version__ = "0.3"
33 version_date = "Oct 2018"
34
35
36 def usage():
37 print("Usage: ", sys.argv[0], "[options]")
38 print(" Performs system tests over running NBI. It can be used for real OSM test using option '--test-osm'")
39 print(" If this is the case env variables 'OSMNBITEST_VIM_NAME' must be supplied to create a VIM if not exist "
40 "where deployment is done")
41 print("OPTIONS")
42 print(" -h|--help: shows this help")
43 print(" --insecure: Allows non trusted https NBI server")
44 print(" --list: list available tests")
45 print(" --manual-check: Deployment tests stop after deployed to allow manual inspection. Only make sense with "
46 "'--test-osm'")
47 print(" -p|--password PASSWORD: NBI access password. 'admin' by default")
48 print(" ---project PROJECT: NBI access project. 'admin' by default")
49 print(" --test TEST[,...]: Execute only a test or a comma separated list of tests")
50 print(" --params key=val: params to the previous test. key can be vnfd-files, nsd-file, ns-name, ns-config")
51 print(" --test-osm: If missing this test is intended for NBI only, no other OSM components are expected. Use "
52 "this flag to test the system. LCM and RO components are expected to be up and running")
53 print(" --timeout TIMEOUT: General NBI timeout, by default {}s".format(timeout))
54 print(" --timeout-deploy TIMEOUT: Timeout used for getting NS deployed, by default {}s".format(timeout_deploy))
55 print(" --timeout-configure TIMEOUT: Timeout used for getting NS deployed and configured,"
56 " by default {}s".format(timeout_configure))
57 print(" -u|--user USERNAME: NBI access username. 'admin' by default")
58 print(" --url URL: complete NBI server URL. 'https//localhost:9999/osm' by default")
59 print(" -v|--verbose print debug information, can be used several times")
60 print(" --no-verbose remove verbosity")
61 print(" --version: prints current version")
62 print("ENV variables used for real deployment tests with option osm-test.")
63 print(" export OSMNBITEST_VIM_NAME=vim-name")
64 print(" export OSMNBITEST_VIM_URL=vim-url")
65 print(" export OSMNBITEST_VIM_TYPE=vim-type")
66 print(" export OSMNBITEST_VIM_TENANT=vim-tenant")
67 print(" export OSMNBITEST_VIM_USER=vim-user")
68 print(" export OSMNBITEST_VIM_PASSWORD=vim-password")
69 print(" export OSMNBITEST_VIM_CONFIG=\"vim-config\"")
70 print(" export OSMNBITEST_NS_NAME=\"vim-config\"")
71 return
72
73
74 r_header_json = {"Content-type": "application/json"}
75 headers_json = {"Content-type": "application/json", "Accept": "application/json"}
76 r_header_yaml = {"Content-type": "application/yaml"}
77 headers_yaml = {"Content-type": "application/yaml", "Accept": "application/yaml"}
78 r_header_text = {"Content-type": "text/plain"}
79 r_header_octect = {"Content-type": "application/octet-stream"}
80 headers_text = {"Accept": "text/plain,application/yaml"}
81 r_header_zip = {"Content-type": "application/zip"}
82 headers_zip = {"Accept": "application/zip,application/yaml"}
83 headers_zip_yaml = {"Accept": "application/yaml", "Content-type": "application/zip"}
84 r_headers_yaml_location_vnfd = {"Location": "/vnfpkgm/v1/vnf_packages_content/", "Content-Type": "application/yaml"}
85 r_headers_yaml_location_nsd = {"Location": "/nsd/v1/ns_descriptors_content/", "Content-Type": "application/yaml"}
86 r_headers_yaml_location_nst = {"Location": "/nst/v1/netslice_templates_content", "Content-Type": "application/yaml"}
87 r_headers_yaml_location_nslcmop = {"Location": "nslcm/v1/ns_lcm_op_occs/", "Content-Type": "application/yaml"}
88 r_headers_yaml_location_nsilcmop = {"Location": "/osm/nsilcm/v1/nsi_lcm_op_occs/", "Content-Type": "application/yaml"}
89
90 # test ones authorized
91 test_authorized_list = (
92 ("AU1", "Invalid vnfd id", "GET", "/vnfpkgm/v1/vnf_packages/non-existing-id",
93 headers_json, None, 404, r_header_json, "json"),
94 ("AU2", "Invalid nsd id", "GET", "/nsd/v1/ns_descriptors/non-existing-id",
95 headers_yaml, None, 404, r_header_yaml, "yaml"),
96 ("AU3", "Invalid nsd id", "DELETE", "/nsd/v1/ns_descriptors_content/non-existing-id",
97 headers_yaml, None, 404, r_header_yaml, "yaml"),
98 )
99 timeout = 120 # general timeout
100 timeout_deploy = 60*10 # timeout for NS deploying without charms
101 timeout_configure = 60*20 # timeout for NS deploying and configuring
102
103
104 class TestException(Exception):
105 pass
106
107
108 class TestRest:
109 def __init__(self, url_base, header_base=None, verify=False, user="admin", password="admin", project="admin"):
110 self.url_base = url_base
111 if header_base is None:
112 self.header_base = {}
113 else:
114 self.header_base = header_base.copy()
115 self.s = requests.session()
116 self.s.headers = self.header_base
117 self.verify = verify
118 self.token = False
119 self.user = user
120 self.password = password
121 self.project = project
122 self.vim_id = None
123 # contains ID of tests obtained from Location response header. "" key contains last obtained id
124 self.last_id = ""
125 self.test_name = None
126 self.step = 0 # number of subtest under test
127 self.passed_tests = 0
128 self.failed_tests = 0
129
130 def set_test_name(self, test_name):
131 self.test_name = test_name
132 self.step = 0
133 self.last_id = ""
134
135 def set_header(self, header):
136 self.s.headers.update(header)
137
138 def set_tet_name(self, test_name):
139 self.test_name = test_name
140
141 def unset_header(self, key):
142 if key in self.s.headers:
143 del self.s.headers[key]
144
145 def test(self, description, method, url, headers, payload, expected_codes, expected_headers,
146 expected_payload, store_file=None, pooling=False):
147 """
148 Performs an http request and check http code response. Exit if different than allowed. It get the returned id
149 that can be used by following test in the URL with {name} where name is the name of the test
150 :param description: description of the test
151 :param method: HTTP method: GET,PUT,POST,DELETE,...
152 :param url: complete URL or relative URL
153 :param headers: request headers to add to the base headers
154 :param payload: Can be a dict, transformed to json, a text or a file if starts with '@'
155 :param expected_codes: expected response codes, can be int, int tuple or int range
156 :param expected_headers: expected response headers, dict with key values
157 :param expected_payload: expected payload, 0 if empty, 'yaml', 'json', 'text', 'zip', 'octet-stream'
158 :param store_file: filename to store content
159 :param pooling: if True do not count neither log this test. Because a pooling is done with many equal requests
160 :return: requests response
161 """
162 r = None
163 try:
164 if not self.s:
165 self.s = requests.session()
166 # URL
167 if not url:
168 url = self.url_base
169 elif not url.startswith("http"):
170 url = self.url_base + url
171
172 # replace url <> with the last ID
173 url = url.replace("<>", self.last_id)
174 if payload:
175 if isinstance(payload, str):
176 if payload.startswith("@"):
177 mode = "r"
178 file_name = payload[1:]
179 if payload.startswith("@b"):
180 mode = "rb"
181 file_name = payload[2:]
182 with open(file_name, mode) as f:
183 payload = f.read()
184 elif isinstance(payload, dict):
185 payload = json.dumps(payload)
186
187 if not pooling:
188 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, method, url)
189 logger.warning(test_description)
190 self.step += 1
191 stream = False
192 if expected_payload in ("zip", "octet-string") or store_file:
193 stream = True
194 __retry = 0
195 while True:
196 try:
197 r = getattr(self.s, method.lower())(url, data=payload, headers=headers, verify=self.verify,
198 stream=stream)
199 break
200 except requests.exceptions.ConnectionError as e:
201 if __retry == 2:
202 raise
203 logger.error("Exception {}. Retrying".format(e))
204 __retry += 1
205
206 if expected_payload in ("zip", "octet-string") or store_file:
207 logger.debug("RX {}".format(r.status_code))
208 else:
209 logger.debug("RX {}: {}".format(r.status_code, r.text))
210
211 # check response
212 if expected_codes:
213 if isinstance(expected_codes, int):
214 expected_codes = (expected_codes,)
215 if r.status_code not in expected_codes:
216 raise TestException(
217 "Got status {}. Expected {}. {}".format(r.status_code, expected_codes, r.text))
218
219 if expected_headers:
220 for header_key, header_val in expected_headers.items():
221 if header_key.lower() not in r.headers:
222 raise TestException("Header {} not present".format(header_key))
223 if header_val and header_val.lower() not in r.headers[header_key]:
224 raise TestException("Header {} does not contain {} but {}".format(header_key, header_val,
225 r.headers[header_key]))
226
227 if expected_payload is not None:
228 if expected_payload == 0 and len(r.content) > 0:
229 raise TestException("Expected empty payload")
230 elif expected_payload == "json":
231 try:
232 r.json()
233 except Exception as e:
234 raise TestException("Expected json response payload, but got Exception {}".format(e))
235 elif expected_payload == "yaml":
236 try:
237 yaml.safe_load(r.text)
238 except Exception as e:
239 raise TestException("Expected yaml response payload, but got Exception {}".format(e))
240 elif expected_payload in ("zip", "octet-string"):
241 if len(r.content) == 0:
242 raise TestException("Expected some response payload, but got empty")
243 # try:
244 # tar = tarfile.open(None, 'r:gz', fileobj=r.raw)
245 # for tarinfo in tar:
246 # tarname = tarinfo.name
247 # print(tarname)
248 # except Exception as e:
249 # raise TestException("Expected zip response payload, but got Exception {}".format(e))
250 elif expected_payload == "text":
251 if len(r.content) == 0:
252 raise TestException("Expected some response payload, but got empty")
253 # r.text
254 if store_file:
255 with open(store_file, 'wb') as fd:
256 for chunk in r.iter_content(chunk_size=128):
257 fd.write(chunk)
258
259 location = r.headers.get("Location")
260 if location:
261 _id = location[location.rfind("/") + 1:]
262 if _id:
263 self.last_id = str(_id)
264 if not pooling:
265 self.passed_tests += 1
266 return r
267 except TestException as e:
268 self.failed_tests += 1
269 r_status_code = None
270 r_text = None
271 if r:
272 r_status_code = r.status_code
273 r_text = r.text
274 logger.error("{} \nRX code{}: {}".format(e, r_status_code, r_text))
275 return None
276 # exit(1)
277 except IOError as e:
278 if store_file:
279 logger.error("Cannot open file {}: {}".format(store_file, e))
280 else:
281 logger.error("Exception: {}".format(e), exc_info=True)
282 self.failed_tests += 1
283 return None
284 # exit(1)
285 except requests.exceptions.RequestException as e:
286 logger.error("Exception: {}".format(e))
287
288 def get_autorization(self): # user=None, password=None, project=None):
289 if self.token: # and self.user == user and self.password == password and self.project == project:
290 return
291 # self.user = user
292 # self.password = password
293 # self.project = project
294 r = self.test("Obtain token", "POST", "/admin/v1/tokens", headers_json,
295 {"username": self.user, "password": self.password, "project_id": self.project},
296 (200, 201), r_header_json, "json")
297 if not r:
298 return
299 response = r.json()
300 self.token = response["id"]
301 self.set_header({"Authorization": "Bearer {}".format(self.token)})
302
303 def remove_authorization(self):
304 if self.token:
305 self.test("Delete token", "DELETE", "/admin/v1/tokens/{}".format(self.token), headers_json,
306 None, (200, 201, 204), None, None)
307 self.token = None
308 self.unset_header("Authorization")
309
310 def get_create_vim(self, test_osm):
311 if self.vim_id:
312 return self.vim_id
313 self.get_autorization()
314 if test_osm:
315 vim_name = os.environ.get("OSMNBITEST_VIM_NAME")
316 if not vim_name:
317 raise TestException(
318 "Needed to define OSMNBITEST_VIM_XXX variables to create a real VIM for deployment")
319 else:
320 vim_name = "fakeVim"
321 # Get VIM
322 r = self.test("Get VIM ID", "GET", "/admin/v1/vim_accounts?name={}".format(vim_name), headers_json,
323 None, 200, r_header_json, "json")
324 if not r:
325 return
326 vims = r.json()
327 if vims:
328 return vims[0]["_id"]
329 # Add VIM
330 if test_osm:
331 # check needed environ parameters:
332 if not os.environ.get("OSMNBITEST_VIM_URL") or not os.environ.get("OSMNBITEST_VIM_TENANT"):
333 raise TestException("Env OSMNBITEST_VIM_URL and OSMNBITEST_VIM_TENANT are needed for create a real VIM"
334 " to deploy on whit the --test-osm option")
335 vim_data = "{{schema_version: '1.0', name: '{}', vim_type: {}, vim_url: '{}', vim_tenant_name: '{}', "\
336 "vim_user: {}, vim_password: {}".format(vim_name,
337 os.environ.get("OSMNBITEST_VIM_TYPE", "openstack"),
338 os.environ.get("OSMNBITEST_VIM_URL"),
339 os.environ.get("OSMNBITEST_VIM_TENANT"),
340 os.environ.get("OSMNBITEST_VIM_USER"),
341 os.environ.get("OSMNBITEST_VIM_PASSWORD"))
342 if os.environ.get("OSMNBITEST_VIM_CONFIG"):
343 vim_data += " ,config: {}".format(os.environ.get("OSMNBITEST_VIM_CONFIG"))
344 vim_data += "}"
345 else:
346 vim_data = "{schema_version: '1.0', name: fakeVim, vim_type: openstack, vim_url: 'http://10.11.12.13/fake'"\
347 ", vim_tenant_name: 'vimtenant', vim_user: vimuser, vim_password: vimpassword}"
348 self.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_yaml, vim_data,
349 (201), {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/yaml"}, "yaml")
350 return self.last_id
351
352 def print_results(self):
353 print("\n\n\n--------------------------------------------")
354 print("TEST RESULTS: Total: {}, Passed: {}, Failed: {}".format(self.passed_tests + self.failed_tests,
355 self.passed_tests, self.failed_tests))
356 print("--------------------------------------------")
357
358 def wait_until_delete(self, url_op, timeout_delete):
359 """
360 Make a pooling until topic is not present, because of deleted
361 :param url_op:
362 :param timeout_delete:
363 :return:
364 """
365 description = "Wait to topic being deleted"
366 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, "GET", url_op)
367 logger.warning(test_description)
368 self.step += 1
369
370 wait = timeout_delete
371 while wait >= 0:
372 r = self.test(description, "GET", url_op, headers_yaml, None, (200, 404), None, r_header_yaml, "yaml",
373 pooling=True)
374 if not r:
375 return
376 if r.status_code == 404:
377 self.passed_tests += 1
378 break
379 elif r.status_code == 200:
380 wait -= 5
381 sleep(5)
382 else:
383 raise TestException("Topic is not deleted after {} seconds".format(timeout_delete))
384 self.failed_tests += 1
385
386 def wait_operation_ready(self, ns_nsi, opp_id, timeout, expected_fail=False):
387 """
388 Wait until nslcmop or nsilcmop finished
389 :param ns_nsi: "ns" o "nsi"
390 :param opp_id: Id o fthe operation
391 :param timeout:
392 :param expected_fail:
393 :return: None. Updates passed/failed_tests
394 """
395 if ns_nsi == "ns":
396 url_op = "/nslcm/v1/ns_lcm_op_occs/{}".format(opp_id)
397 else:
398 url_op = "/nsilcm/v1/nsi_lcm_op_occs/{}".format(opp_id)
399 description = "Wait to {} lcm operation complete".format(ns_nsi)
400 test_description = "Test {}{} {} {} {}".format(self.test_name, self.step, description, "GET", url_op)
401 logger.warning(test_description)
402 self.step += 1
403 wait = timeout
404 while wait >= 0:
405 r = self.test(description, "GET", url_op, headers_json, None,
406 200, r_header_json, "json", pooling=True)
407 if not r:
408 return
409 nslcmop = r.json()
410 if "COMPLETED" in nslcmop["operationState"]:
411 if expected_fail:
412 logger.error("NS terminate has success, expecting failing: {}".format(nslcmop["detailed-status"]))
413 self.failed_tests += 1
414 else:
415 self.passed_tests += 1
416 break
417 elif "FAILED" in nslcmop["operationState"]:
418 if not expected_fail:
419 logger.error("NS terminate has failed: {}".format(nslcmop["detailed-status"]))
420 self.failed_tests += 1
421 else:
422 self.passed_tests += 1
423 break
424
425 print(".", end="", file=stderr)
426 wait -= 10
427 sleep(10)
428 else:
429 self.failed_tests += 1
430 logger.error("NS instantiate is not terminate after {} seconds".format(timeout))
431 return
432 print("", file=stderr)
433
434
435 class TestNonAuthorized:
436 description = "Test invalid URLs. methods and no authorization"
437
438 @staticmethod
439 def run(engine, test_osm, manual_check, test_params=None):
440 engine.set_test_name("NonAuth")
441 engine.remove_authorization()
442 test_not_authorized_list = (
443 ("Invalid token", "GET", "/admin/v1/users", headers_json, None, 401, r_header_json, "json"),
444 ("Invalid URL", "POST", "/admin/v1/nonexist", headers_yaml, None, 405, r_header_yaml, "yaml"),
445 ("Invalid version", "DELETE", "/admin/v2/users", headers_yaml, None, 405, r_header_yaml, "yaml"),
446 )
447 for t in test_not_authorized_list:
448 engine.test(*t)
449
450
451 class TestUsersProjects:
452 description = "test project and user creation"
453
454 @staticmethod
455 def run(engine, test_osm, manual_check, test_params=None):
456 engine.set_test_name("UserProject")
457 engine.get_autorization()
458 engine.test("Create project non admin", "POST", "/admin/v1/projects", headers_json, {"name": "P1"},
459 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
460 engine.test("Create project admin", "POST", "/admin/v1/projects", headers_json,
461 {"name": "Padmin", "admin": True}, (201, 204),
462 {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
463 engine.test("Create project bad format", "POST", "/admin/v1/projects", headers_json, {"name": 1}, (400, 422),
464 r_header_json, "json")
465 engine.test("Create user with bad project", "POST", "/admin/v1/users", headers_json,
466 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 409,
467 r_header_json, "json")
468 engine.test("Create user with bad project and force", "POST", "/admin/v1/users?FORCE=True", headers_json,
469 {"username": "U1", "projects": ["P1", "P2", "Padmin"], "password": "pw1"}, 201,
470 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
471 engine.test("Create user 2", "POST", "/admin/v1/users", headers_json,
472 {"username": "U2", "projects": ["P1"], "password": "pw2"}, 201,
473 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
474 engine.test("Edit user U1, delete P2 project", "PATCH", "/admin/v1/users/U1", headers_json,
475 {"projects": {"$'P2'": None}}, 204, None, None)
476 res = engine.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
477 headers_json, None, 200, None, json)
478 if res:
479 u1 = res.json()
480 # print(u1)
481 expected_projects = ["P1", "Padmin"]
482 if u1["projects"] != expected_projects:
483 logger.error("User content projects '{}' different than expected '{}'. Edition has not done"
484 " properly".format(u1["projects"], expected_projects))
485 engine.failed_tests += 1
486
487 engine.test("Edit user U1, set Padmin as default project", "PUT", "/admin/v1/users/U1", headers_json,
488 {"projects": {"$'Padmin'": None, "$+[0]": "Padmin"}}, 204, None, None)
489 res = engine.test("Check user U1, contains the right projects", "GET", "/admin/v1/users/U1",
490 headers_json, None, 200, None, json)
491 if res:
492 u1 = res.json()
493 # print(u1)
494 expected_projects = ["Padmin", "P1"]
495 if u1["projects"] != expected_projects:
496 logger.error("User content projects '{}' different than expected '{}'. Edition has not done"
497 " properly".format(u1["projects"], expected_projects))
498 engine.failed_tests += 1
499
500 engine.test("Edit user U1, change password", "PATCH", "/admin/v1/users/U1", headers_json,
501 {"password": "pw1_new"}, 204, None, None)
502
503 engine.test("Change to project P1 non existing", "POST", "/admin/v1/tokens/", headers_json,
504 {"project_id": "P1"}, 401, r_header_json, "json")
505
506 res = engine.test("Change to user U1 project P1", "POST", "/admin/v1/tokens", headers_json,
507 {"username": "U1", "password": "pw1_new", "project_id": "P1"}, (200, 201),
508 r_header_json, "json")
509 if res:
510 response = res.json()
511 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
512
513 engine.test("Edit user projects non admin", "PUT", "/admin/v1/users/U1", headers_json,
514 {"projects": {"$'P1'": None}}, 401, r_header_json, "json")
515 engine.test("Add new project non admin", "POST", "/admin/v1/projects", headers_json,
516 {"name": "P2"}, 401, r_header_json, "json")
517 engine.test("Add new user non admin", "POST", "/admin/v1/users", headers_json,
518 {"username": "U3", "projects": ["P1"], "password": "pw3"}, 401,
519 r_header_json, "json")
520
521 res = engine.test("Change to user U1 project Padmin", "POST", "/admin/v1/tokens", headers_json,
522 {"project_id": "Padmin"}, (200, 201), r_header_json, "json")
523 if res:
524 response = res.json()
525 engine.set_header({"Authorization": "Bearer {}".format(response["id"])})
526
527 engine.test("Add new project admin", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
528 (201, 204), {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
529 engine.test("Add new user U3 admin", "POST", "/admin/v1/users",
530 headers_json, {"username": "U3", "projects": ["P2"], "password": "pw3"}, (201, 204),
531 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
532 engine.test("Edit user projects admin", "PUT", "/admin/v1/users/U3", headers_json,
533 {"projects": ["P2"]}, 204, None, None)
534
535 engine.test("Delete project P2 conflict", "DELETE", "/admin/v1/projects/P2", headers_json, None, 409,
536 r_header_json, "json")
537 engine.test("Delete project P2 forcing", "DELETE", "/admin/v1/projects/P2?FORCE=True", headers_json,
538 None, 204, None, None)
539
540 engine.test("Delete user U1. Conflict deleting own user", "DELETE", "/admin/v1/users/U1", headers_json,
541 None, 409, r_header_json, "json")
542 engine.test("Delete user U2", "DELETE", "/admin/v1/users/U2", headers_json, None, 204, None, None)
543 engine.test("Delete user U3", "DELETE", "/admin/v1/users/U3", headers_json, None, 204, None, None)
544 # change to admin
545 engine.remove_authorization() # To force get authorization
546 engine.get_autorization()
547 engine.test("Delete user U1 by Name", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
548 engine.test("Delete project P1 by Name", "DELETE", "/admin/v1/projects/P1", headers_json, None, 204, None, None)
549 engine.test("Delete project Padmin by Name", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204,
550 None, None)
551
552 # BEGIN New Tests - Addressing Projects/Users by Name/ID
553 res = engine.test("Create new project P1", "POST", "/admin/v1/projects", headers_json, {"name": "P1"},
554 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
555 if res:
556 pid1 = res.json()["id"]
557 # print("# pid =", pid1)
558 res = engine.test("Create new project P2", "POST", "/admin/v1/projects", headers_json, {"name": "P2"},
559 201, {"Location": "/admin/v1/projects/", "Content-Type": "application/json"}, "json")
560 if res:
561 pid2 = res.json()["id"]
562 # print("# pid =", pid2)
563 res = engine.test("Create new user U1", "POST", "/admin/v1/users", headers_json,
564 {"username": "U1", "projects": ["P1"], "password": "pw1"}, 201,
565 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
566 if res:
567 uid1 = res.json()["id"]
568 # print("# uid =", uid1)
569 res = engine.test("Create new user U2", "POST", "/admin/v1/users", headers_json,
570 {"username": "U2", "projects": ["P2"], "password": "pw2"}, 201,
571 {"Location": "/admin/v1/users/", "Content-Type": "application/json"}, "json")
572 if res:
573 uid2 = res.json()["id"]
574 # print("# uid =", uid2)
575 engine.test("Get Project P1 by Name", "GET", "/admin/v1/projects/P1", headers_json, None, 200, None, "json")
576 engine.test("Get Project P1 by ID", "GET", "/admin/v1/projects/"+pid1, headers_json, None, 200, None, "json")
577 engine.test("Get User U1 by Name", "GET", "/admin/v1/users/U1", headers_json, None, 200, None, "json")
578 engine.test("Get User U1 by ID", "GET", "/admin/v1/users/"+uid1, headers_json, None, 200, None, "json")
579 engine.test("Rename Project P1 by Name", "PUT", "/admin/v1/projects/P1", headers_json,
580 {"name": "P3"}, 204, None, None)
581 engine.test("Rename Project P2 by ID", "PUT", "/admin/v1/projects/"+pid2, headers_json,
582 {"name": "P4"}, 204, None, None)
583 engine.test("Rename User U1 by Name", "PUT", "/admin/v1/users/U1", headers_json,
584 {"username": "U3"}, 204, None, None)
585 engine.test("Rename User U2 by ID", "PUT", "/admin/v1/users/"+uid2, headers_json,
586 {"username": "U4"}, 204, None, None)
587 engine.test("Get Project P1 by new Name", "GET", "/admin/v1/projects/P3", headers_json, None, 200, None, "json")
588 engine.test("Get User U1 by new Name", "GET", "/admin/v1/users/U3", headers_json, None, 200, None, "json")
589 engine.test("Delete User U1 by Name", "DELETE", "/admin/v1/users/U3", headers_json, None, 204, None, None)
590 engine.test("Delete User U2 by ID", "DELETE", "/admin/v1/users/"+uid2, headers_json, None, 204, None, None)
591 engine.test("Delete Project P1 by Name", "DELETE", "/admin/v1/projects/P3", headers_json, None, 204, None,
592 None)
593 engine.test("Delete Project P2 by ID", "DELETE", "/admin/v1/projects/"+pid2, headers_json, None, 204, None,
594 None)
595 # END New Tests - Addressing Projects/Users by Name
596 engine.remove_authorization() # To finish
597
598
599 class TestFakeVim:
600 description = "Creates/edit/delete fake VIMs and SDN controllers"
601
602 def __init__(self):
603 self.vim = {
604 "schema_version": "1.0",
605 "schema_type": "No idea",
606 "name": "myVim",
607 "description": "Descriptor name",
608 "vim_type": "openstack",
609 "vim_url": "http://localhost:/vim",
610 "vim_tenant_name": "vimTenant",
611 "vim_user": "user",
612 "vim_password": "password",
613 "config": {"config_param": 1}
614 }
615 self.sdn = {
616 "name": "sdn-name",
617 "description": "sdn-description",
618 "dpid": "50:50:52:54:00:94:21:21",
619 "ip": "192.168.15.17",
620 "port": 8080,
621 "type": "opendaylight",
622 "version": "3.5.6",
623 "user": "user",
624 "password": "passwd"
625 }
626 self.port_mapping = [
627 {"compute_node": "compute node 1",
628 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
629 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
630 ]},
631 {"compute_node": "compute node 2",
632 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
633 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
634 ]}
635 ]
636
637 def run(self, engine, test_osm, manual_check, test_params=None):
638
639 vim_bad = self.vim.copy()
640 vim_bad.pop("name")
641
642 engine.set_test_name("FakeVim")
643 engine.get_autorization()
644 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (201, 204),
645 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
646 vim_id = engine.last_id
647 engine.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json,
648 vim_bad, 422, None, headers_json)
649 engine.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json, self.vim,
650 409, None, headers_json)
651 engine.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml, None, 200, r_header_yaml,
652 "yaml")
653 engine.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 200,
654 r_header_yaml, "yaml")
655 if not test_osm:
656 # delete with FORCE
657 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_yaml,
658 None, 202, None, 0)
659 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None,
660 404, r_header_yaml, "yaml")
661 else:
662 # delete and wait until is really deleted
663 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 202,
664 None, 0)
665 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
666
667
668 class TestVIMSDN(TestFakeVim):
669 description = "Creates VIM with SDN editing SDN controllers and port_mapping"
670
671 def __init__(self):
672 TestFakeVim.__init__(self)
673 self.wim = {
674 "schema_version": "1.0",
675 "schema_type": "No idea",
676 "name": "myWim",
677 "description": "Descriptor name",
678 "wim_type": "odl",
679 "wim_url": "http://localhost:/wim",
680 "user": "user",
681 "password": "password",
682 "config": {"config_param": 1}
683 }
684
685 def run(self, engine, test_osm, manual_check, test_params=None):
686 engine.set_test_name("VimSdn")
687 engine.get_autorization()
688 # Added SDN
689 engine.test("Create SDN", "POST", "/admin/v1/sdns", headers_json, self.sdn, (201, 204),
690 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
691 sdnc_id = engine.last_id
692 # sleep(5)
693 # Edit SDN
694 engine.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, {"name": "new_sdn_name"},
695 204, None, None)
696 # sleep(5)
697 # VIM with SDN
698 self.vim["config"]["sdn-controller"] = sdnc_id
699 self.vim["config"]["sdn-port-mapping"] = self.port_mapping
700 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (200, 204, 201),
701 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
702
703 vim_id = engine.last_id
704 self.port_mapping[0]["compute_node"] = "compute node XX"
705 engine.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
706 {"config": {"sdn-port-mapping": self.port_mapping}}, 204, None, None)
707 engine.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
708 {"config": {"sdn-port-mapping": None}}, 204, None, None)
709
710 engine.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json, self.wim, (200, 204, 201),
711 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
712 wim_id = engine.last_id
713
714 if not test_osm:
715 # delete with FORCE
716 engine.test("Delete VIM remove port-mapping", "DELETE",
717 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_json, None, 202, None, 0)
718 engine.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id), headers_json, None,
719 202, None, 0)
720
721 engine.test("Delete WIM", "DELETE",
722 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id), headers_json, None, 202, None, 0)
723 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml,
724 None, 404, r_header_yaml, "yaml")
725 engine.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id), headers_yaml, None,
726 404, r_header_yaml, "yaml")
727 engine.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id), headers_yaml,
728 None, 404, r_header_yaml, "yaml")
729 else:
730 if manual_check:
731 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
732 # delete and wait until is really deleted
733 engine.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id),
734 headers_json, None, (202, 201, 204), None, 0)
735 engine.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, None,
736 (202, 201, 204), None, 0)
737 engine.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id),
738 headers_json, None, (202, 201, 204), None, 0)
739 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
740 engine.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id), timeout)
741 engine.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id), timeout)
742
743
744 class TestDeploy:
745 description = "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
746
747 def __init__(self):
748 self.test_name = "DEPLOY"
749 self.nsd_id = None
750 self.vim_id = None
751 self.ns_id = None
752 self.vnfds_id = []
753 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
754 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
755 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
756 self.descriptor_edit = None
757 self.uses_configuration = False
758 self.users = {}
759 self.passwords = {}
760 self.commands = {}
761 self.keys = {}
762 self.timeout = 120
763 self.qforce = ""
764 self.ns_params = None
765 self.vnfr_ip_list = {}
766
767 def create_descriptors(self, engine):
768 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
769 if not os.path.exists(temp_dir):
770 os.makedirs(temp_dir)
771 for vnfd_index, vnfd_filename in enumerate(self.vnfd_filenames):
772 if "/" in vnfd_filename:
773 vnfd_filename_path = vnfd_filename
774 if not os.path.exists(vnfd_filename_path):
775 raise TestException("File '{}' does not exist".format(vnfd_filename_path))
776 else:
777 vnfd_filename_path = temp_dir + vnfd_filename
778 if not os.path.exists(vnfd_filename_path):
779 with open(vnfd_filename_path, "wb") as file:
780 response = requests.get(self.descriptor_url + vnfd_filename)
781 if response.status_code >= 300:
782 raise TestException("Error downloading descriptor from '{}': {}".format(
783 self.descriptor_url + vnfd_filename, response.status_code))
784 file.write(response.content)
785 if vnfd_filename_path.endswith(".yaml"):
786 headers = headers_yaml
787 else:
788 headers = headers_zip_yaml
789 if randint(0, 1) == 0:
790 # vnfd CREATE AND UPLOAD in one step:
791 engine.test("Onboard VNFD in one step", "POST",
792 "/vnfpkgm/v1/vnf_packages_content" + self.qforce, headers, "@b" + vnfd_filename_path, 201,
793 r_headers_yaml_location_vnfd,
794 "yaml")
795 self.vnfds_id.append(engine.last_id)
796 else:
797 # vnfd CREATE AND UPLOAD ZIP
798 engine.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
799 headers_json, None, 201,
800 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
801 self.vnfds_id.append(engine.last_id)
802 engine.test("Onboard VNFD step 2 as ZIP", "PUT",
803 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self.qforce,
804 headers, "@b" + vnfd_filename_path, 204, None, 0)
805
806 if self.descriptor_edit:
807 if "vnfd{}".format(vnfd_index) in self.descriptor_edit:
808 # Modify VNFD
809 engine.test("Edit VNFD ", "PATCH",
810 "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfds_id[-1]),
811 headers_yaml, self.descriptor_edit["vnfd{}".format(vnfd_index)], 204, None, None)
812
813 if "/" in self.nsd_filename:
814 nsd_filename_path = self.nsd_filename
815 if not os.path.exists(nsd_filename_path):
816 raise TestException("File '{}' does not exist".format(nsd_filename_path))
817 else:
818 nsd_filename_path = temp_dir + self.nsd_filename
819 if not os.path.exists(nsd_filename_path):
820 with open(nsd_filename_path, "wb") as file:
821 response = requests.get(self.descriptor_url + self.nsd_filename)
822 if response.status_code >= 300:
823 raise TestException("Error downloading descriptor from '{}': {}".format(
824 self.descriptor_url + self.nsd_filename, response.status_code))
825 file.write(response.content)
826 if nsd_filename_path.endswith(".yaml"):
827 headers = headers_yaml
828 else:
829 headers = headers_zip_yaml
830
831 if randint(0, 1) == 0:
832 # nsd CREATE AND UPLOAD in one step:
833 engine.test("Onboard NSD in one step", "POST",
834 "/nsd/v1/ns_descriptors_content" + self.qforce, headers, "@b" + nsd_filename_path, 201,
835 r_headers_yaml_location_nsd, yaml)
836 self.nsd_id = engine.last_id
837 else:
838 # nsd CREATE AND UPLOAD ZIP
839 engine.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
840 headers_json, None, 201,
841 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
842 self.nsd_id = engine.last_id
843 engine.test("Onboard NSD step 2 as ZIP", "PUT",
844 "/nsd/v1/ns_descriptors/<>/nsd_content" + self.qforce,
845 headers, "@b" + nsd_filename_path, 204, None, 0)
846
847 if self.descriptor_edit and "nsd" in self.descriptor_edit:
848 # Modify NSD
849 engine.test("Edit NSD ", "PATCH",
850 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
851 headers_yaml, self.descriptor_edit["nsd"], 204, None, None)
852
853 def delete_descriptors(self, engine):
854 # delete descriptors
855 engine.test("Delete NSSD SOL005", "DELETE",
856 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
857 headers_yaml, None, 204, None, 0)
858 for vnfd_id in self.vnfds_id:
859 engine.test("Delete VNFD SOL005", "DELETE",
860 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id), headers_yaml, None, 204, None, 0)
861
862 def instantiate(self, engine, ns_data):
863 ns_data_text = yaml.safe_dump(ns_data, default_flow_style=True, width=256)
864 # create NS Two steps
865 r = engine.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
866 headers_yaml, ns_data_text, 201,
867 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
868 if not r:
869 return
870 self.ns_id = engine.last_id
871 engine.test("Instantiate NS step 2", "POST",
872 "/nslcm/v1/ns_instances/{}/instantiate".format(self.ns_id), headers_yaml, ns_data_text,
873 201, r_headers_yaml_location_nslcmop, "yaml")
874 nslcmop_id = engine.last_id
875
876 if test_osm:
877 # Wait until status is Ok
878 timeout = timeout_configure if self.uses_configuration else timeout_deploy
879 engine.wait_operation_ready("ns", nslcmop_id, timeout)
880
881 def terminate(self, engine):
882 # remove deployment
883 if test_osm:
884 engine.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self.ns_id), headers_yaml,
885 None, 201, r_headers_yaml_location_nslcmop, "yaml")
886 nslcmop2_id = engine.last_id
887 # Wait until status is Ok
888 engine.wait_operation_ready("ns", nslcmop2_id, timeout_deploy)
889
890 engine.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
891 204, None, 0)
892 else:
893 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
894 headers_yaml, None, 204, None, 0)
895
896 # check all it is deleted
897 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
898 404, None, "yaml")
899 r = engine.test("Check NSLCMOPs are deleted", "GET",
900 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
901 200, None, "json")
902 if not r:
903 return
904 nslcmops = r.json()
905 if not isinstance(nslcmops, list) or nslcmops:
906 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
907
908 def test_ns(self, engine, test_osm, commands=None, users=None, passwds=None, keys=None, timeout=0):
909
910 r = engine.test("GET VNFR IDs", "GET",
911 "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_json, None,
912 200, r_header_json, "json")
913 if not r:
914 return
915 ns_data = r.json()
916
917 vnfr_list = ns_data['constituent-vnfr-ref']
918 time = 0
919 _commands = commands if commands is not None else self.commands
920 _users = users if users is not None else self.users
921 _passwds = passwds if passwds is not None else self.passwords
922 _keys = keys if keys is not None else self.keys
923 _timeout = timeout if timeout != 0 else self.timeout
924
925 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
926 for vnfr_id in vnfr_list:
927 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
928 "/nslcm/v1/vnfrs/{}".format(vnfr_id), headers_json, None,
929 200, r_header_json, "json")
930 if not r:
931 continue
932 vnfr_data = r.json()
933
934 vnf_index = str(vnfr_data["member-vnf-index-ref"])
935
936 ip_address = self.get_vnfr_ip(engine, vnf_index)
937 description = "Exec command='{}' at VNFR={} IP={}".format(_commands.get(vnf_index)[0], vnf_index,
938 ip_address)
939 engine.step += 1
940 test_description = "{}{} {}".format(engine.test_name, engine.step, description)
941 logger.warning(test_description)
942 while _timeout >= time:
943 result, message = self.do_checks([ip_address],
944 vnf_index=vnfr_data["member-vnf-index-ref"],
945 commands=_commands.get(vnf_index), user=_users.get(vnf_index),
946 passwd=_passwds.get(vnf_index), key=_keys.get(vnf_index))
947 if result == 1:
948 engine.passed_tests += 1
949 logger.debug(message)
950 break
951 elif result == 0:
952 time += 20
953 sleep(20)
954 elif result == -1:
955 engine.failed_tests += 1
956 logger.error(message)
957 break
958 else:
959 time -= 20
960 engine.failed_tests += 1
961 logger.error(message)
962 else:
963 engine.failed_tests += 1
964 logger.error("VNFR {} has not mgmt address. Check failed".format(vnf_index))
965
966 def do_checks(self, ip, vnf_index, commands=[], user=None, passwd=None, key=None):
967 try:
968 import urllib3
969 from pssh.clients import ParallelSSHClient
970 from pssh.utils import load_private_key
971 from ssh2 import exceptions as ssh2Exception
972 except ImportError as e:
973 logger.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
974 "parallel-ssh urllib3': {}".format(e))
975 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
976 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
977 try:
978 p_host = os.environ.get("PROXY_HOST")
979 p_user = os.environ.get("PROXY_USER")
980 p_password = os.environ.get("PROXY_PASSWD")
981
982 if key:
983 pkey = load_private_key(key)
984 else:
985 pkey = None
986
987 client = ParallelSSHClient(ip, user=user, password=passwd, pkey=pkey, proxy_host=p_host,
988 proxy_user=p_user, proxy_password=p_password, timeout=10, num_retries=0)
989 for cmd in commands:
990 output = client.run_command(cmd)
991 client.join(output)
992 if output[ip[0]].exit_code:
993 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip[0], cmd,
994 "\n".join(output[ip[0]].stderr))
995 else:
996 return 1, "VNFR {} command '{}' successful".format(ip[0], cmd)
997 except (ssh2Exception.ChannelFailure, ssh2Exception.SocketDisconnectError, ssh2Exception.SocketTimeout,
998 ssh2Exception.SocketRecvError) as e:
999 return 0, "Timeout accessing the VNFR {}: {}".format(ip[0], str(e))
1000 except Exception as e:
1001 return -1, "ERROR checking the VNFR {}: {}".format(ip[0], str(e))
1002
1003 def additional_operations(self, engine, test_osm, manual_check):
1004 pass
1005
1006 def run(self, engine, test_osm, manual_check, test_params=None):
1007 engine.set_test_name(self.test_name)
1008 engine.get_autorization()
1009 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1010 if test_params:
1011 if "vnfd-files" in test_params:
1012 self.vnfd_filenames = test_params["vnfd-files"].split(",")
1013 if "nsd-file" in test_params:
1014 self.nsd_filename = test_params["nsd-file"]
1015 if test_params.get("ns-name"):
1016 nsname = test_params["ns-name"]
1017 self.create_descriptors(engine)
1018
1019 # create real VIM if not exist
1020 self.vim_id = engine.get_create_vim(test_osm)
1021 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1022 "vimAccountId": self.vim_id}
1023 if self.ns_params:
1024 ns_data.update(self.ns_params)
1025 if test_params and test_params.get("ns-config"):
1026 if isinstance(test_params["ns-config"], str):
1027 ns_data.update(yaml.load(test_params["ns-config"]))
1028 else:
1029 ns_data.update(test_params["ns-config"])
1030 self.instantiate(engine, ns_data)
1031
1032 if manual_check:
1033 input('NS has been deployed. Perform manual check and press enter to resume')
1034 if test_osm and self.commands:
1035 self.test_ns(engine, test_osm)
1036 self.additional_operations(engine, test_osm, manual_check)
1037 self.terminate(engine)
1038 self.delete_descriptors(engine)
1039
1040 def get_first_ip(self, ip_string):
1041 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
1042 first_ip = ip_string.split(";")[0] if ip_string else ""
1043 return first_ip
1044
1045 def get_vnfr_ip(self, engine, vnfr_index_wanted):
1046 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1047 ip = self.vnfr_ip_list.get(vnfr_index_wanted, "")
1048 if (ip):
1049 return self.get_first_ip(ip)
1050 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1051 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1052 vnfr_index_wanted, self.ns_id), headers_json, None,
1053 200, r_header_json, "json")
1054 if not r:
1055 return ""
1056 vnfr_data = r.json()
1057 if not (vnfr_data and vnfr_data[0]):
1058 return ""
1059 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1060 ip_list = vnfr_data[0].get("ip-address", "")
1061 if ip_list:
1062 self.vnfr_ip_list[vnfr_index_wanted] = ip_list
1063 ip = self.get_first_ip(ip_list)
1064 return ip
1065
1066
1067 class TestDeployHackfestCirros(TestDeploy):
1068 description = "Load and deploy Hackfest cirros_2vnf_ns example"
1069
1070 def __init__(self):
1071 super().__init__()
1072 self.test_name = "CIRROS"
1073 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1074 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1075 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1076 self.users = {'1': "cirros", '2': "cirros"}
1077 self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1078
1079 def terminate(self, engine):
1080 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1081 if test_osm:
1082 engine.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1083 format(self.ns_id), headers_yaml, None, 202, None, "yaml")
1084
1085 engine .wait_until_delete("/nslcm/v1/ns_instances/{}".format(self.ns_id), timeout_deploy)
1086 else:
1087 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1088 headers_yaml, None, 204, None, 0)
1089
1090 # check all it is deleted
1091 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1092 404, None, "yaml")
1093 r = engine.test("Check NSLCMOPs are deleted", "GET",
1094 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1095 200, None, "json")
1096 if not r:
1097 return
1098 nslcmops = r.json()
1099 if not isinstance(nslcmops, list) or nslcmops:
1100 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1101
1102
1103 class TestDeployHackfest1(TestDeploy):
1104 description = "Load and deploy Hackfest_1_vnfd example"
1105
1106 def __init__(self):
1107 super().__init__()
1108 self.test_name = "HACKFEST1-"
1109 self.vnfd_filenames = ("hackfest_1_vnfd.tar.gz",)
1110 self.nsd_filename = "hackfest_1_nsd.tar.gz"
1111 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1112 # self.users = {'1': "cirros", '2': "cirros"}
1113 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1114
1115
1116 class TestDeployHackfestCirrosScaling(TestDeploy):
1117 description = "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1118
1119 def __init__(self):
1120 super().__init__()
1121 self.test_name = "CIRROS-SCALE"
1122 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1123 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1124
1125 def create_descriptors(self, engine):
1126 super().create_descriptors(engine)
1127 # Modify VNFD to add scaling and count=2
1128 self.descriptor_edit = {
1129 "vnfd0": {
1130 "vdu": {
1131 "$id: 'cirros_vnfd-VM'": {"count": 2}
1132 },
1133 "scaling-group-descriptor": [{
1134 "name": "scale_cirros",
1135 "max-instance-count": 2,
1136 "vdu": [{
1137 "vdu-id-ref": "cirros_vnfd-VM",
1138 "count": 2
1139 }]
1140 }]
1141 }
1142 }
1143
1144 def additional_operations(self, engine, test_osm, manual_check):
1145 if not test_osm:
1146 return
1147 # 2 perform scale out twice
1148 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1149 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1150 for i in range(0, 2):
1151 engine.test("Execute scale action over NS", "POST",
1152 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1153 201, r_headers_yaml_location_nslcmop, "yaml")
1154 nslcmop2_scale_out = engine.last_id
1155 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1156 if manual_check:
1157 input('NS scale out done. Check that two more vdus are there')
1158 # TODO check automatic
1159
1160 # 2 perform scale in
1161 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1162 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1163 for i in range(0, 2):
1164 engine.test("Execute scale IN action over NS", "POST",
1165 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1166 201, r_headers_yaml_location_nslcmop, "yaml")
1167 nslcmop2_scale_in = engine.last_id
1168 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1169 if manual_check:
1170 input('NS scale in done. Check that two less vdus are there')
1171 # TODO check automatic
1172
1173 # perform scale in that must fail as reached limit
1174 engine.test("Execute scale IN out of limit action over NS", "POST",
1175 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1176 201, r_headers_yaml_location_nslcmop, "yaml")
1177 nslcmop2_scale_in = engine.last_id
1178 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy, expected_fail=True)
1179
1180
1181 class TestDeployIpMac(TestDeploy):
1182 description = "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1183
1184 def __init__(self):
1185 super().__init__()
1186 self.test_name = "SetIpMac"
1187 self.vnfd_filenames = ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1188 self.nsd_filename = "scenario_2vdu_set_ip_mac.yaml"
1189 self.descriptor_url = \
1190 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1191 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1192 self.users = {'1': "osm", '2': "osm"}
1193 self.passwords = {'1': "osm4u", '2': "osm4u"}
1194 self.timeout = 360
1195
1196 def run(self, engine, test_osm, manual_check, test_params=None):
1197 # super().run(engine, test_osm, manual_check, test_params)
1198 # run again setting IPs with instantiate parameters
1199 instantiation_params = {
1200 "vnf": [
1201 {
1202 "member-vnf-index": "1",
1203 "internal-vld": [
1204 {
1205 "name": "internal_vld1", # net_internal
1206 "ip-profile": {
1207 "ip-version": "ipv4",
1208 "subnet-address": "10.9.8.0/24",
1209 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1210 },
1211 "internal-connection-point": [
1212 {
1213 "id-ref": "eth2",
1214 "ip-address": "10.9.8.2",
1215 },
1216 {
1217 "id-ref": "eth3",
1218 "ip-address": "10.9.8.3",
1219 }
1220 ]
1221 },
1222 ],
1223
1224 "vdu": [
1225 {
1226 "id": "VM1",
1227 "interface": [
1228 # {
1229 # "name": "iface11",
1230 # "floating-ip-required": True,
1231 # },
1232 {
1233 "name": "iface13",
1234 "mac-address": "52:33:44:55:66:13"
1235 },
1236 ],
1237 },
1238 {
1239 "id": "VM2",
1240 "interface": [
1241 {
1242 "name": "iface21",
1243 "ip-address": "10.31.31.22",
1244 "mac-address": "52:33:44:55:66:21"
1245 },
1246 ],
1247 },
1248 ]
1249 },
1250 ]
1251 }
1252
1253 super().run(engine, test_osm, manual_check, test_params={"ns-config": instantiation_params})
1254
1255
1256 class TestDeployHackfest4(TestDeploy):
1257 description = "Load and deploy Hackfest 4 example."
1258
1259 def __init__(self):
1260 super().__init__()
1261 self.test_name = "HACKFEST4-"
1262 self.vnfd_filenames = ("hackfest_4_vnfd.tar.gz",)
1263 self.nsd_filename = "hackfest_4_nsd.tar.gz"
1264 self.uses_configuration = True
1265 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1266 self.users = {'1': "ubuntu", '2': "ubuntu"}
1267 self.passwords = {'1': "osm4u", '2': "osm4u"}
1268 # Modify VNFD to add scaling
1269 # self.descriptor_edit = {
1270 # "vnfd0": {
1271 # 'vnf-configuration': {
1272 # 'config-primitive': [{
1273 # 'name': 'touch',
1274 # 'parameter': [{
1275 # 'name': 'filename',
1276 # 'data-type': 'STRING',
1277 # 'default-value': '/home/ubuntu/touched'
1278 # }]
1279 # }]
1280 # },
1281 # 'scaling-group-descriptor': [{
1282 # 'name': 'scale_dataVM',
1283 # 'scaling-policy': [{
1284 # 'threshold-time': 0,
1285 # 'name': 'auto_cpu_util_above_threshold',
1286 # 'scaling-type': 'automatic',
1287 # 'scaling-criteria': [{
1288 # 'name': 'cpu_util_above_threshold',
1289 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1290 # 'scale-out-relational-operation': 'GE',
1291 # 'scale-in-threshold': 15,
1292 # 'scale-out-threshold': 60,
1293 # 'scale-in-relational-operation': 'LE'
1294 # }],
1295 # 'cooldown-time': 60
1296 # }],
1297 # 'max-instance-count': 10,
1298 # 'scaling-config-action': [
1299 # {'vnf-config-primitive-name-ref': 'touch',
1300 # 'trigger': 'post-scale-out'},
1301 # {'vnf-config-primitive-name-ref': 'touch',
1302 # 'trigger': 'pre-scale-in'}
1303 # ],
1304 # 'vdu': [{
1305 # 'vdu-id-ref': 'dataVM',
1306 # 'count': 1
1307 # }]
1308 # }]
1309 # }
1310 # }
1311
1312
1313 class TestDeployHackfest3Charmed(TestDeploy):
1314 description = "Load and deploy Hackfest 3charmed_ns example"
1315
1316 def __init__(self):
1317 super().__init__()
1318 self.test_name = "HACKFEST3-"
1319 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz",)
1320 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1321 self.uses_configuration = True
1322 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1323 self.users = {'1': "ubuntu", '2': "ubuntu"}
1324 self.passwords = {'1': "osm4u", '2': "osm4u"}
1325 self.descriptor_edit = {
1326 "vnfd0": yaml.full_load(
1327 """
1328 vnf-configuration:
1329 terminate-config-primitive:
1330 - seq: '1'
1331 name: touch
1332 parameter:
1333 - name: filename
1334 value: '/home/ubuntu/last-touch1'
1335 - seq: '3'
1336 name: touch
1337 parameter:
1338 - name: filename
1339 value: '/home/ubuntu/last-touch3'
1340 - seq: '2'
1341 name: touch
1342 parameter:
1343 - name: filename
1344 value: '/home/ubuntu/last-touch2'
1345 """)
1346 }
1347
1348 def additional_operations(self, engine, test_osm, manual_check):
1349 if not test_osm:
1350 return
1351 # 1 perform action
1352 vnfr_index_selected = "2"
1353 payload = '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1354 engine.test("Exec service primitive over NS", "POST",
1355 "/nslcm/v1/ns_instances/{}/action".format(self.ns_id), headers_yaml, payload,
1356 201, r_headers_yaml_location_nslcmop, "yaml")
1357 nslcmop2_action = engine.last_id
1358 # Wait until status is Ok
1359 engine.wait_operation_ready("ns", nslcmop2_action, timeout_deploy)
1360 vnfr_ip = self.get_vnfr_ip(engine, vnfr_index_selected)
1361 if manual_check:
1362 input(
1363 "NS service primitive has been executed."
1364 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1365 format(vnfr_ip))
1366 if test_osm:
1367 commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1368 self.test_ns(engine, test_osm, commands=commands)
1369
1370 # # 2 perform scale out
1371 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1372 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1373 # engine.test("Execute scale action over NS", "POST",
1374 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1375 # 201, r_headers_yaml_location_nslcmop, "yaml")
1376 # nslcmop2_scale_out = engine.last_id
1377 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1378 # if manual_check:
1379 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1380 # # TODO check automatic
1381 #
1382 # # 2 perform scale in
1383 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1384 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1385 # engine.test("Execute scale action over NS", "POST",
1386 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1387 # 201, r_headers_yaml_location_nslcmop, "yaml")
1388 # nslcmop2_scale_in = engine.last_id
1389 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1390 # if manual_check:
1391 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1392 # # TODO check automatic
1393
1394
1395 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed):
1396 description = "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1397 "ids and member-vnf-index."
1398
1399 def __init__(self):
1400 super().__init__()
1401 self.test_name = "HACKFEST3v2-"
1402 self.qforce = "?FORCE=True"
1403 self.descriptor_edit = {
1404 "vnfd0": {
1405 "vdu": {
1406 "$[0]": {
1407 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1408 },
1409 "$[1]": None
1410 },
1411 "vnf-configuration": None,
1412 "connection-point": {
1413 "$[0]": {
1414 "id": "pdu-mgmt",
1415 "name": "pdu-mgmt",
1416 "short-name": "pdu-mgmt"
1417 },
1418 "$[1]": None
1419 },
1420 "mgmt-interface": {"cp": "pdu-mgmt"},
1421 "description": "A vnf single vdu to be used as PDU",
1422 "id": "vdu-as-pdu",
1423 "internal-vld": {
1424 "$[0]": {
1425 "id": "pdu_internal",
1426 "name": "pdu_internal",
1427 "internal-connection-point": {"$[1]": None},
1428 "short-name": "pdu_internal",
1429 "type": "ELAN"
1430 }
1431 }
1432 },
1433
1434 # Modify NSD accordingly
1435 "nsd": {
1436 "constituent-vnfd": {
1437 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1438 "$[1]": None,
1439 },
1440 "description": "A nsd to deploy the vnf to act as as PDU",
1441 "id": "nsd-as-pdu",
1442 "name": "nsd-as-pdu",
1443 "short-name": "nsd-as-pdu",
1444 "vld": {
1445 "$[0]": {
1446 "id": "mgmt_pdu",
1447 "name": "mgmt_pdu",
1448 "short-name": "mgmt_pdu",
1449 "vnfd-connection-point-ref": {
1450 "$[0]": {
1451 "vnfd-connection-point-ref": "pdu-mgmt",
1452 "vnfd-id-ref": "vdu-as-pdu",
1453 },
1454 "$[1]": None
1455 },
1456 "type": "ELAN"
1457 },
1458 "$[1]": None,
1459 }
1460 }
1461 }
1462
1463
1464 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed):
1465 description = "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1466
1467 def __init__(self):
1468 super().__init__()
1469 self.test_name = "HACKFEST3v3-"
1470 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1471 self.descriptor_edit = {
1472 "vnfd0": yaml.load(
1473 """
1474 scaling-group-descriptor:
1475 - name: "scale_dataVM"
1476 max-instance-count: 10
1477 scaling-policy:
1478 - name: "auto_cpu_util_above_threshold"
1479 scaling-type: "automatic"
1480 threshold-time: 0
1481 cooldown-time: 60
1482 scaling-criteria:
1483 - name: "cpu_util_above_threshold"
1484 scale-in-threshold: 15
1485 scale-in-relational-operation: "LE"
1486 scale-out-threshold: 60
1487 scale-out-relational-operation: "GE"
1488 vnf-monitoring-param-ref: "monitor1"
1489 vdu:
1490 - vdu-id-ref: dataVM
1491 count: 1
1492 scaling-config-action:
1493 - trigger: post-scale-out
1494 vnf-config-primitive-name-ref: touch
1495 - trigger: pre-scale-in
1496 vnf-config-primitive-name-ref: touch
1497 vdu:
1498 "$id: dataVM":
1499 monitoring-param:
1500 - id: "dataVM_cpu_util"
1501 nfvi-metric: "cpu_utilization"
1502
1503 monitoring-param:
1504 - id: "monitor1"
1505 name: "monitor1"
1506 aggregation-type: AVERAGE
1507 vdu-monitoring-param:
1508 vdu-ref: "dataVM"
1509 vdu-monitoring-param-ref: "dataVM_cpu_util"
1510 vnf-configuration:
1511 initial-config-primitive:
1512 "$[1]":
1513 parameter:
1514 "$[0]":
1515 value: "<touch-filename>" # default-value: /home/ubuntu/first-touch
1516 config-primitive:
1517 "$[0]":
1518 parameter:
1519 "$[0]":
1520 default-value: "<touch-filename2>"
1521 """)
1522 }
1523 self.ns_params = {
1524 "additionalParamsForVnf": [
1525 {"member-vnf-index": "1", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-1",
1526 "touch-filename2": "/home/ubuntu/second-touch-1"}},
1527 {"member-vnf-index": "2", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-2",
1528 "touch-filename2": "/home/ubuntu/second-touch-2"}},
1529 ]
1530 }
1531
1532 def additional_operations(self, engine, test_osm, manual_check):
1533 super().additional_operations(engine, test_osm, manual_check)
1534 if not test_osm:
1535 return
1536
1537 # 2 perform scale out
1538 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1539 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1540 engine.test("Execute scale action over NS", "POST",
1541 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1542 201, r_headers_yaml_location_nslcmop, "yaml")
1543 nslcmop2_scale_out = engine.last_id
1544 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1545 if manual_check:
1546 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1547 if test_osm:
1548 commands = {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1549 self.test_ns(engine, test_osm, commands=commands)
1550 # TODO check automatic connection to scaled VM
1551
1552 # 2 perform scale in
1553 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1554 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1555 engine.test("Execute scale action over NS", "POST",
1556 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1557 201, r_headers_yaml_location_nslcmop, "yaml")
1558 nslcmop2_scale_in = engine.last_id
1559 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1560 if manual_check:
1561 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1562 # TODO check automatic
1563
1564
1565 class TestDeploySimpleCharm(TestDeploy):
1566 description = "Deploy hackfest-4 hackfest_simplecharm example"
1567
1568 def __init__(self):
1569 super().__init__()
1570 self.test_name = "HACKFEST-SIMPLE"
1571 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1572 self.vnfd_filenames = ("hackfest_simplecharm_vnf.tar.gz",)
1573 self.nsd_filename = "hackfest_simplecharm_ns.tar.gz"
1574 self.uses_configuration = True
1575 self.commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1576 self.users = {'1': "ubuntu", '2': "ubuntu"}
1577 self.passwords = {'1': "osm4u", '2': "osm4u"}
1578
1579
1580 class TestDeploySimpleCharm2(TestDeploySimpleCharm):
1581 description = "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1582 "vnf-member-index"
1583
1584 def __init__(self):
1585 super().__init__()
1586 self.test_name = "HACKFEST-SIMPLE2-"
1587 self.qforce = "?FORCE=True"
1588 self.descriptor_edit = {
1589 "vnfd0": {
1590 "id": "hackfest.simplecharm.vnf"
1591 },
1592
1593 "nsd": {
1594 "id": "hackfest.simplecharm.ns",
1595 "constituent-vnfd": {
1596 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1597 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1598 },
1599 "vld": {
1600 "$[0]": {
1601 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1602 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1603 "$[1]": {"member-vnf-index-ref": "$2",
1604 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1605 },
1606 "$[1]": {
1607 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1608 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1609 "$[1]": {"member-vnf-index-ref": "$2",
1610 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1611 },
1612 }
1613 }
1614 }
1615
1616
1617 class TestDeploySingleVdu(TestDeployHackfest3Charmed):
1618 description = "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1619
1620 def __init__(self):
1621 super().__init__()
1622 self.test_name = "SingleVDU"
1623 self.qforce = "?FORCE=True"
1624 self.descriptor_edit = {
1625 # Modify VNFD to remove one VDU
1626 "vnfd0": {
1627 "vdu": {
1628 "$[0]": {
1629 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1630 },
1631 "$[1]": None
1632 },
1633 "vnf-configuration": None,
1634 "connection-point": {
1635 "$[0]": {
1636 "id": "pdu-mgmt",
1637 "name": "pdu-mgmt",
1638 "short-name": "pdu-mgmt"
1639 },
1640 "$[1]": None
1641 },
1642 "mgmt-interface": {"cp": "pdu-mgmt"},
1643 "description": "A vnf single vdu to be used as PDU",
1644 "id": "vdu-as-pdu",
1645 "internal-vld": {
1646 "$[0]": {
1647 "id": "pdu_internal",
1648 "name": "pdu_internal",
1649 "internal-connection-point": {"$[1]": None},
1650 "short-name": "pdu_internal",
1651 "type": "ELAN"
1652 }
1653 }
1654 },
1655
1656 # Modify NSD accordingly
1657 "nsd": {
1658 "constituent-vnfd": {
1659 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1660 "$[1]": None,
1661 },
1662 "description": "A nsd to deploy the vnf to act as as PDU",
1663 "id": "nsd-as-pdu",
1664 "name": "nsd-as-pdu",
1665 "short-name": "nsd-as-pdu",
1666 "vld": {
1667 "$[0]": {
1668 "id": "mgmt_pdu",
1669 "name": "mgmt_pdu",
1670 "short-name": "mgmt_pdu",
1671 "vnfd-connection-point-ref": {
1672 "$[0]": {
1673 "vnfd-connection-point-ref": "pdu-mgmt",
1674 "vnfd-id-ref": "vdu-as-pdu",
1675 },
1676 "$[1]": None
1677 },
1678 "type": "ELAN"
1679 },
1680 "$[1]": None,
1681 }
1682 }
1683 }
1684
1685
1686 class TestDeployHnfd(TestDeployHackfest3Charmed):
1687 description = "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1688
1689 def __init__(self):
1690 super().__init__()
1691 self.test_name = "HNFD"
1692 self.pduDeploy = TestDeploySingleVdu()
1693 self.pdu_interface_0 = {}
1694 self.pdu_interface_1 = {}
1695
1696 self.pdu_id = None
1697 # self.vnf_to_pdu = """
1698 # vdu:
1699 # "$[0]":
1700 # pdu-type: PDU-TYPE-1
1701 # interface:
1702 # "$[0]":
1703 # name: mgmt-iface
1704 # "$[1]":
1705 # name: pdu-iface-internal
1706 # id: hfn1
1707 # description: HFND, one PDU + One VDU
1708 # name: hfn1
1709 # short-name: hfn1
1710 #
1711 # """
1712
1713 self.pdu_descriptor = {
1714 "name": "my-PDU",
1715 "type": "PDU-TYPE-1",
1716 "vim_accounts": "to-override",
1717 "interfaces": [
1718 {
1719 "name": "mgmt-iface",
1720 "mgmt": True,
1721 "type": "overlay",
1722 "ip-address": "to override",
1723 "mac-address": "mac_address",
1724 "vim-network-name": "mgmt",
1725 },
1726 {
1727 "name": "pdu-iface-internal",
1728 "mgmt": False,
1729 "type": "overlay",
1730 "ip-address": "to override",
1731 "mac-address": "mac_address",
1732 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1733 },
1734 ]
1735 }
1736 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1737
1738 self.descriptor_edit = {
1739 "vnfd0": {
1740 "id": "hfnd1",
1741 "name": "hfn1",
1742 "short-name": "hfn1",
1743 "vdu": {
1744 "$[0]": {
1745 "pdu-type": "PDU-TYPE-1",
1746 "interface": {
1747 "$[0]": {"name": "mgmt-iface"},
1748 "$[1]": {"name": "pdu-iface-internal"},
1749 }
1750 }
1751 }
1752 },
1753 "nsd": {
1754 "constituent-vnfd": {
1755 "$[1]": {"vnfd-id-ref": "hfnd1"}
1756 },
1757 "vld": {
1758 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1759 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1760 }
1761 }
1762 }
1763
1764 def create_descriptors(self, engine):
1765 super().create_descriptors(engine)
1766
1767 # Create PDU
1768 self.pdu_descriptor["interfaces"][0].update(self.pdu_interface_0)
1769 self.pdu_descriptor["interfaces"][1].update(self.pdu_interface_1)
1770 self.pdu_descriptor["vim_accounts"] = [self.vim_id]
1771 # TODO get vim-network-name from vnfr.vld.name
1772 self.pdu_descriptor["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1773 os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1774 "PDU", self.pdu_descriptor["interfaces"][1]["vim-network-name"])
1775 engine.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1776 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self.pdu_descriptor,
1777 201, r_header_yaml, "yaml")
1778 self.pdu_id = engine.last_id
1779
1780 def run(self, engine, test_osm, manual_check, test_params=None):
1781 engine.get_autorization()
1782 engine.set_test_name(self.test_name)
1783 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1784
1785 # create real VIM if not exist
1786 self.vim_id = engine.get_create_vim(test_osm)
1787 # instantiate PDU
1788 self.pduDeploy.create_descriptors(engine)
1789 self.pduDeploy.instantiate(engine, {"nsDescription": "to be used as PDU", "nsName": nsname + "-PDU",
1790 "nsdId": self.pduDeploy.nsd_id, "vimAccountId": self.vim_id})
1791 if manual_check:
1792 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1793 if test_osm:
1794 self.pduDeploy.test_ns(engine, test_osm)
1795
1796 if test_osm:
1797 r = engine.test("Get VNFR to obtain IP_ADDRESS", "GET",
1798 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self.pduDeploy.ns_id), headers_json, None,
1799 200, r_header_json, "json")
1800 if not r:
1801 return
1802 vnfr_data = r.json()
1803 # print(vnfr_data)
1804
1805 self.pdu_interface_0["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("ip-address")
1806 self.pdu_interface_1["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("ip-address")
1807 self.pdu_interface_0["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("mac-address")
1808 self.pdu_interface_1["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("mac-address")
1809 if not self.pdu_interface_0["ip-address"]:
1810 raise TestException("Vnfr has not managment ip address")
1811 else:
1812 self.pdu_interface_0["ip-address"] = "192.168.10.10"
1813 self.pdu_interface_1["ip-address"] = "192.168.11.10"
1814 self.pdu_interface_0["mac-address"] = "52:33:44:55:66:13"
1815 self.pdu_interface_1["mac-address"] = "52:33:44:55:66:14"
1816
1817 self.create_descriptors(engine)
1818
1819 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1820 "vimAccountId": self.vim_id}
1821 if test_params and test_params.get("ns-config"):
1822 if isinstance(test_params["ns-config"], str):
1823 ns_data.update(yaml.load(test_params["ns-config"]))
1824 else:
1825 ns_data.update(test_params["ns-config"])
1826
1827 self.instantiate(engine, ns_data)
1828 if manual_check:
1829 input('NS has been deployed. Perform manual check and press enter to resume')
1830 if test_osm:
1831 self.test_ns(engine, test_osm)
1832 self.additional_operations(engine, test_osm, manual_check)
1833 self.terminate(engine)
1834 self.pduDeploy.terminate(engine)
1835 self.delete_descriptors(engine)
1836 self.pduDeploy.delete_descriptors(engine)
1837
1838 def delete_descriptors(self, engine):
1839 super().delete_descriptors(engine)
1840 # delete pdu
1841 engine.test("Delete PDU SOL005", "DELETE",
1842 "/pdu/v1/pdu_descriptors/{}".format(self.pdu_id),
1843 headers_yaml, None, 204, None, 0)
1844
1845
1846 class TestDescriptors:
1847 description = "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1848
1849 def __init__(self):
1850 self.vnfd_filename = "hackfest_3charmed_vnfd.tar.gz"
1851 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1852 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1853 self.vnfd_id = None
1854 self.nsd_id = None
1855 self.vnfd_empty = """vnfd:vnfd-catalog:
1856 vnfd:
1857 - name: prova
1858 short-name: prova
1859 id: prova
1860 """
1861 self.vnfd_prova = """vnfd:vnfd-catalog:
1862 vnfd:
1863 - connection-point:
1864 - name: cp_0h8m
1865 type: VPORT
1866 id: prova
1867 name: prova
1868 short-name: prova
1869 vdu:
1870 - id: vdu_z4bm
1871 image: ubuntu
1872 interface:
1873 - external-connection-point-ref: cp_0h8m
1874 name: eth0
1875 virtual-interface:
1876 type: VIRTIO
1877 name: vdu_z4bm
1878 version: '1.0'
1879 """
1880
1881 def run(self, engine, test_osm, manual_check, test_params=None):
1882 engine.set_test_name("Descriptors")
1883 engine.get_autorization()
1884 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
1885 if not os.path.exists(temp_dir):
1886 os.makedirs(temp_dir)
1887
1888 # download files
1889 for filename in (self.vnfd_filename, self.nsd_filename):
1890 filename_path = temp_dir + filename
1891 if not os.path.exists(filename_path):
1892 with open(filename_path, "wb") as file:
1893 response = requests.get(self.descriptor_url + filename)
1894 if response.status_code >= 300:
1895 raise TestException("Error downloading descriptor from '{}': {}".format(
1896 self.descriptor_url + filename, response.status_code))
1897 file.write(response.content)
1898
1899 vnfd_filename_path = temp_dir + self.vnfd_filename
1900 nsd_filename_path = temp_dir + self.nsd_filename
1901
1902 engine.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
1903 self.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
1904 self.vnfd_id = engine.last_id
1905
1906 # test bug 605
1907 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
1908 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
1909
1910 engine.test("Upload VNFD {}".format(self.vnfd_filename), "PUT",
1911 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip_yaml,
1912 "@b" + vnfd_filename_path, 204, None, 0)
1913
1914 queries = ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
1915 "vdu.0.interface.1.internal-connection-point-ref=internal",
1916 "internal-vld.0.internal-connection-point.0.id-ref=internal",
1917 # Detection of duplicated VLD names in VNF Descriptors
1918 # URL: internal-vld=[
1919 # {id: internal1, name: internal, type:ELAN,
1920 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
1921 # {id: internal2, name: internal, type:ELAN,
1922 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
1923 # ]
1924 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
1925 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
1926 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
1927 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
1928 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
1929 ]
1930 for query in queries:
1931 engine.test("Upload invalid VNFD ", "PUT",
1932 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self.vnfd_id, query),
1933 headers_zip_yaml, "@b" + vnfd_filename_path, 422, r_header_yaml, "yaml")
1934
1935 # test bug 605
1936 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
1937 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
1938
1939 # get vnfd descriptor
1940 engine.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
1941 headers_yaml, None, 200, r_header_yaml, "yaml")
1942
1943 # get vnfd file descriptor
1944 engine.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self.vnfd_id),
1945 headers_text, None, 200, r_header_text, "text", temp_dir+"vnfd-yaml")
1946 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
1947
1948 # get vnfd zip file package
1949 engine.test("Get VNFD zip package", "GET",
1950 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip, None, 200,
1951 r_header_zip, "zip", temp_dir+"vnfd-zip")
1952 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
1953
1954 # get vnfd artifact
1955 engine.test("Get VNFD artifact package", "GET",
1956 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self.vnfd_id), headers_zip, None, 200,
1957 r_header_octect, "octet-string", temp_dir+"vnfd-icon")
1958 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
1959
1960 # nsd CREATE AND UPLOAD in one step:
1961 engine.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml,
1962 "@b" + nsd_filename_path, 201, r_headers_yaml_location_nsd, "yaml")
1963 self.nsd_id = engine.last_id
1964
1965 queries = ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
1966 for query in queries:
1967 engine.test("Upload invalid NSD ", "PUT",
1968 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self.nsd_id, query),
1969 headers_zip_yaml, "@b" + nsd_filename_path, 422, r_header_yaml, "yaml")
1970
1971 # get nsd descriptor
1972 engine.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml,
1973 None, 200, r_header_yaml, "yaml")
1974
1975 # get nsd file descriptor
1976 engine.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self.nsd_id), headers_text,
1977 None, 200, r_header_text, "text", temp_dir+"nsd-yaml")
1978 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
1979
1980 # get nsd zip file package
1981 engine.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self.nsd_id),
1982 headers_zip, None, 200, r_header_zip, "zip", temp_dir+"nsd-zip")
1983 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
1984
1985 # get nsd artifact
1986 engine.test("Get NSD artifact package", "GET",
1987 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self.nsd_id), headers_zip, None, 200,
1988 r_header_octect, "octet-string", temp_dir+"nsd-icon")
1989 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
1990
1991 # vnfd DELETE
1992 test_rest.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
1993 headers_yaml, None, 409, None, None)
1994
1995 test_rest.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self.vnfd_id),
1996 headers_yaml, None, 204, None, 0)
1997
1998 # nsd DELETE
1999 test_rest.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml, None, 204,
2000 None, 0)
2001
2002
2003 class TestNetSliceTemplates:
2004 description = "Upload a NST to OSM"
2005
2006 def __init__(self):
2007 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2008 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2009 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2010 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2011 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2012
2013 def run(self, engine, test_osm, manual_check, test_params=None):
2014 # nst CREATE
2015 engine.set_test_name("NST step ")
2016 engine.get_autorization()
2017 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
2018 if not os.path.exists(temp_dir):
2019 os.makedirs(temp_dir)
2020
2021 # Onboard VNFDs
2022 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2023 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2024 self.vnfd_edge_id = engine.last_id
2025
2026 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2027 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2028 self.vnfd_middle_id = engine.last_id
2029
2030 # Onboard NSDs
2031 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2032 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2033 self.nsd_edge_id = engine.last_id
2034
2035 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2036 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2037 self.nsd_middle_id = engine.last_id
2038
2039 # Onboard NST
2040 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2041 201, r_headers_yaml_location_nst, "yaml")
2042 nst_id = engine.last_id
2043
2044 # nstd SHOW OSM format
2045 engine.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2046 200, r_header_json, "json")
2047
2048 # nstd DELETE
2049 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2050 204, None, 0)
2051
2052 # NSDs DELETE
2053 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2054 headers_json, None, 204, None, 0)
2055
2056 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2057 None, 204, None, 0)
2058
2059 # VNFDs DELETE
2060 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2061 headers_yaml, None, 204, None, 0)
2062
2063 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2064 headers_yaml, None, 204, None, 0)
2065
2066
2067 class TestNetSliceInstances:
2068 '''
2069 Test procedure:
2070 1. Populate databases with VNFD, NSD, NST with the following scenario
2071 +-----------------management-----------------+
2072 | | |
2073 +--+---+ +----+----+ +---+--+
2074 | | | | | |
2075 | edge +---data1----+ middle +---data2-----+ edge |
2076 | | | | | |
2077 +------+ +---------+ +------+
2078 shared-nss
2079 2. Create NSI-1
2080 3. Instantiate NSI-1
2081 4. Create NSI-2
2082 5. Instantiate NSI-2
2083 Manual check - Are 2 slices instantiated correctly?
2084 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2085 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2086 6. Terminate NSI-1
2087 7. Delete NSI-1
2088 Manual check - Is slice NSI-1 deleted correctly?
2089 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2090 8. Create NSI-3
2091 9. Instantiate NSI-3
2092 Manual check - Is slice NSI-3 instantiated correctly?
2093 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2094 10. Delete NSI-2
2095 11. Terminate NSI-2
2096 12. Delete NSI-3
2097 13. Terminate NSI-3
2098 Manual check - All cleaned correctly?
2099 NSI-2 and NSI-3 were terminated and deleted
2100 14. Cleanup database
2101 '''
2102
2103 description = "Upload a NST to OSM"
2104
2105 def __init__(self):
2106 self.vim_id = None
2107 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2108 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2109 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2110 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2111 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2112
2113 def create_slice(self, engine, nsi_data, name):
2114 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2115 r = engine.test(name, "POST", "/nsilcm/v1/netslice_instances",
2116 headers_yaml, ns_data_text, 201,
2117 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2118 return r
2119
2120 def instantiate_slice(self, engine, nsi_data, nsi_id, name):
2121 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2122 engine.test(name, "POST",
2123 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id), headers_yaml, ns_data_text,
2124 201, r_headers_yaml_location_nsilcmop, "yaml")
2125
2126 def terminate_slice(self, engine, nsi_id, name):
2127 engine.test(name, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id),
2128 headers_yaml, None, 201, r_headers_yaml_location_nsilcmop, "yaml")
2129
2130 def delete_slice(self, engine, nsi_id, name):
2131 engine.test(name, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id), headers_yaml, None,
2132 204, None, 0)
2133
2134 def run(self, engine, test_osm, manual_check, test_params=None):
2135 # nst CREATE
2136 engine.set_test_name("NSI")
2137 engine.get_autorization()
2138
2139 # Onboard VNFDs
2140 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2141 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2142 self.vnfd_edge_id = engine.last_id
2143
2144 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2145 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2146 self.vnfd_middle_id = engine.last_id
2147
2148 # Onboard NSDs
2149 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2150 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2151 self.nsd_edge_id = engine.last_id
2152
2153 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2154 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2155 self.nsd_middle_id = engine.last_id
2156
2157 # Onboard NST
2158 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2159 201, r_headers_yaml_location_nst, "yaml")
2160 nst_id = engine.last_id
2161
2162 self.vim_id = engine.get_create_vim(test_osm)
2163
2164 # CREATE NSI-1
2165 ns_data = {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2166 r = self.create_slice(engine, ns_data, "Create NSI-1 step 1")
2167 if not r:
2168 return
2169 self.nsi_id1 = engine.last_id
2170
2171 # INSTANTIATE NSI-1
2172 self.instantiate_slice(engine, ns_data, self.nsi_id1, "Instantiate NSI-1 step 2")
2173 nsilcmop_id1 = engine.last_id
2174
2175 # Waiting for NSI-1
2176 engine.wait_operation_ready("nsi", nsilcmop_id1, timeout_deploy)
2177
2178 # CREATE NSI-2
2179 ns_data = {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2180 r = self.create_slice(engine, ns_data, "Create NSI-2 step 1")
2181 if not r:
2182 return
2183 self.nsi_id2 = engine.last_id
2184
2185 # INSTANTIATE NSI-2
2186 self.instantiate_slice(engine, ns_data, self.nsi_id2, "Instantiate NSI-2 step 2")
2187 nsilcmop_id2 = engine.last_id
2188
2189 # Waiting for NSI-2
2190 engine.wait_operation_ready("nsi", nsilcmop_id2, timeout_deploy)
2191
2192 if manual_check:
2193 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2194
2195 # TERMINATE NSI-1
2196 self.terminate_slice(engine, self.nsi_id1, "Terminate NSI-1")
2197 nsilcmop1_id = engine.last_id
2198
2199 # Wait terminate NSI-1
2200 engine.wait_operation_ready("nsi", nsilcmop1_id, timeout_deploy)
2201
2202 # DELETE NSI-1
2203 self.delete_slice(engine, self.nsi_id1, "Delete NS")
2204
2205 if manual_check:
2206 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2207
2208 # CREATE NSI-3
2209 ns_data = {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2210 r = self.create_slice(engine, ns_data, "Create NSI-3 step 1")
2211
2212 if not r:
2213 return
2214 self.nsi_id3 = engine.last_id
2215
2216 # INSTANTIATE NSI-3
2217 self.instantiate_slice(engine, ns_data, self.nsi_id3, "Instantiate NSI-3 step 2")
2218 nsilcmop_id3 = engine.last_id
2219
2220 # Wait Instantiate NSI-3
2221 engine.wait_operation_ready("nsi", nsilcmop_id3, timeout_deploy)
2222
2223 if manual_check:
2224 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2225
2226 # TERMINATE NSI-2
2227 self.terminate_slice(engine, self.nsi_id2, "Terminate NSI-2")
2228 nsilcmop2_id = engine.last_id
2229
2230 # Wait terminate NSI-2
2231 engine.wait_operation_ready("nsi", nsilcmop2_id, timeout_deploy)
2232
2233 # DELETE NSI-2
2234 self.delete_slice(engine, self.nsi_id2, "DELETE NSI-2")
2235
2236 # TERMINATE NSI-3
2237 self. terminate_slice(engine, self.nsi_id3, "Terminate NSI-3")
2238 nsilcmop3_id = engine.last_id
2239
2240 # Wait terminate NSI-3
2241 engine.wait_operation_ready("nsi", nsilcmop3_id, timeout_deploy)
2242
2243 # DELETE NSI-3
2244 self.delete_slice(engine, self.nsi_id3, "DELETE NSI-3")
2245
2246 if manual_check:
2247 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2248
2249 # nstd DELETE
2250 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2251 204, None, 0)
2252
2253 # NSDs DELETE
2254 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2255 headers_json, None, 204, None, 0)
2256
2257 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2258 None, 204, None, 0)
2259
2260 # VNFDs DELETE
2261 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2262 headers_yaml, None, 204, None, 0)
2263
2264 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2265 headers_yaml, None, 204, None, 0)
2266
2267
2268 if __name__ == "__main__":
2269 global logger
2270 test = ""
2271
2272 # Disable warnings from self-signed certificates.
2273 requests.packages.urllib3.disable_warnings()
2274 try:
2275 logging.basicConfig(format="%(levelname)s %(message)s", level=logging.ERROR)
2276 logger = logging.getLogger('NBI')
2277 # load parameters and configuration
2278 opts, args = getopt.getopt(sys.argv[1:], "hvu:p:",
2279 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2280 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2281 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2282 url = "https://localhost:9999/osm"
2283 user = password = project = "admin"
2284 test_osm = False
2285 manual_check = False
2286 verbose = 0
2287 verify = True
2288 fail_fast = False
2289 test_classes = {
2290 "NonAuthorized": TestNonAuthorized,
2291 "FakeVIM": TestFakeVim,
2292 "TestUsersProjects": TestUsersProjects,
2293 "VIM-SDN": TestVIMSDN,
2294 "Deploy-Custom": TestDeploy,
2295 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros,
2296 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling,
2297 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed,
2298 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2,
2299 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3,
2300 "Deploy-Hackfest-4": TestDeployHackfest4,
2301 "Deploy-CirrosMacIp": TestDeployIpMac,
2302 "TestDescriptors": TestDescriptors,
2303 "TestDeployHackfest1": TestDeployHackfest1,
2304 # "Deploy-MultiVIM": TestDeployMultiVIM,
2305 "DeploySingleVdu": TestDeploySingleVdu,
2306 "DeployHnfd": TestDeployHnfd,
2307 "Upload-Slice-Template": TestNetSliceTemplates,
2308 "Deploy-Slice-Instance": TestNetSliceInstances,
2309 "TestDeploySimpleCharm": TestDeploySimpleCharm,
2310 "TestDeploySimpleCharm2": TestDeploySimpleCharm2,
2311 }
2312 test_to_do = []
2313 test_params = {}
2314
2315 for o, a in opts:
2316 # print("parameter:", o, a)
2317 if o == "--version":
2318 print("test version " + __version__ + ' ' + version_date)
2319 exit()
2320 elif o == "--list":
2321 for test, test_class in sorted(test_classes.items()):
2322 print("{:32} {}".format(test + ":", test_class.description))
2323 exit()
2324 elif o in ("-v", "--verbose"):
2325 verbose += 1
2326 elif o == "no-verbose":
2327 verbose = -1
2328 elif o in ("-h", "--help"):
2329 usage()
2330 sys.exit()
2331 elif o == "--test-osm":
2332 test_osm = True
2333 elif o == "--manual-check":
2334 manual_check = True
2335 elif o == "--url":
2336 url = a
2337 elif o in ("-u", "--user"):
2338 user = a
2339 elif o in ("-p", "--password"):
2340 password = a
2341 elif o == "--project":
2342 project = a
2343 elif o == "--fail-fast":
2344 fail_fast = True
2345 elif o == "--test":
2346 # print("asdfadf", o, a, a.split(","))
2347 for _test in a.split(","):
2348 if _test not in test_classes:
2349 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test),
2350 file=sys.stderr)
2351 exit(1)
2352 test_to_do.append(_test)
2353 elif o == "--params":
2354 param_key, _, param_value = a.partition("=")
2355 text_index = len(test_to_do)
2356 if text_index not in test_params:
2357 test_params[text_index] = {}
2358 test_params[text_index][param_key] = param_value
2359 elif o == "--insecure":
2360 verify = False
2361 elif o == "--timeout":
2362 timeout = int(a)
2363 elif o == "--timeout-deploy":
2364 timeout_deploy = int(a)
2365 elif o == "--timeout-configure":
2366 timeout_configure = int(a)
2367 else:
2368 assert False, "Unhandled option"
2369 if verbose == 0:
2370 logger.setLevel(logging.WARNING)
2371 elif verbose > 1:
2372 logger.setLevel(logging.DEBUG)
2373 else:
2374 logger.setLevel(logging.ERROR)
2375
2376 test_rest = TestRest(url, user=user, password=password, project=project)
2377 # print("tests to do:", test_to_do)
2378 if test_to_do:
2379 text_index = 0
2380 for test in test_to_do:
2381 if fail_fast and test_rest.failed_tests:
2382 break
2383 text_index += 1
2384 test_class = test_classes[test]
2385 test_class().run(test_rest, test_osm, manual_check, test_params.get(text_index))
2386 else:
2387 for test, test_class in sorted(test_classes.items()):
2388 if fail_fast and test_rest.failed_tests:
2389 break
2390 test_class().run(test_rest, test_osm, manual_check, test_params.get(0))
2391 test_rest.print_results()
2392 exit(1 if test_rest.failed_tests else 0)
2393
2394 except TestException as e:
2395 logger.error(test + "Test {} Exception: {}".format(test, str(e)))
2396 exit(1)
2397 except getopt.GetoptError as e:
2398 logger.error(e)
2399 print(e, file=sys.stderr)
2400 exit(1)
2401 except Exception as e:
2402 logger.critical(test + " Exception: " + str(e), exc_info=True)