a48ee2656a722625e0f9e57d4727934a627915d6
[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}, 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", "DELETE", "/admin/v1/users/U1", headers_json, None, 204, None, None)
548 engine.test("Delete project P1", "DELETE", "/admin/v1/projects/P1", headers_json, None, 204, None, None)
549 engine.test("Delete project Padmin", "DELETE", "/admin/v1/projects/Padmin", headers_json, None, 204,
550 None, None)
551
552
553 class TestFakeVim:
554 description = "Creates/edit/delete fake VIMs and SDN controllers"
555
556 def __init__(self):
557 self.vim = {
558 "schema_version": "1.0",
559 "schema_type": "No idea",
560 "name": "myVim",
561 "description": "Descriptor name",
562 "vim_type": "openstack",
563 "vim_url": "http://localhost:/vim",
564 "vim_tenant_name": "vimTenant",
565 "vim_user": "user",
566 "vim_password": "password",
567 "config": {"config_param": 1}
568 }
569 self.sdn = {
570 "name": "sdn-name",
571 "description": "sdn-description",
572 "dpid": "50:50:52:54:00:94:21:21",
573 "ip": "192.168.15.17",
574 "port": 8080,
575 "type": "opendaylight",
576 "version": "3.5.6",
577 "user": "user",
578 "password": "passwd"
579 }
580 self.port_mapping = [
581 {"compute_node": "compute node 1",
582 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/1", "switch_mac": "52:54:00:94:21:21"},
583 {"pci": "0000:81:00.1", "switch_port": "port-2/2", "switch_mac": "52:54:00:94:21:22"}
584 ]},
585 {"compute_node": "compute node 2",
586 "ports": [{"pci": "0000:81:00.0", "switch_port": "port-2/3", "switch_mac": "52:54:00:94:21:23"},
587 {"pci": "0000:81:00.1", "switch_port": "port-2/4", "switch_mac": "52:54:00:94:21:24"}
588 ]}
589 ]
590
591 def run(self, engine, test_osm, manual_check, test_params=None):
592
593 vim_bad = self.vim.copy()
594 vim_bad.pop("name")
595
596 engine.set_test_name("FakeVim")
597 engine.get_autorization()
598 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (201, 204),
599 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json")
600 vim_id = engine.last_id
601 engine.test("Create VIM without name, bad schema", "POST", "/admin/v1/vim_accounts", headers_json,
602 vim_bad, 422, None, headers_json)
603 engine.test("Create VIM name repeated", "POST", "/admin/v1/vim_accounts", headers_json, self.vim,
604 409, None, headers_json)
605 engine.test("Show VIMs", "GET", "/admin/v1/vim_accounts", headers_yaml, None, 200, r_header_yaml,
606 "yaml")
607 engine.test("Show VIM", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 200,
608 r_header_yaml, "yaml")
609 if not test_osm:
610 # delete with FORCE
611 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_yaml,
612 None, 202, None, 0)
613 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None,
614 404, r_header_yaml, "yaml")
615 else:
616 # delete and wait until is really deleted
617 engine.test("Delete VIM", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml, None, 202,
618 None, 0)
619 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
620
621
622 class TestVIMSDN(TestFakeVim):
623 description = "Creates VIM with SDN editing SDN controllers and port_mapping"
624
625 def __init__(self):
626 TestFakeVim.__init__(self)
627 self.wim = {
628 "schema_version": "1.0",
629 "schema_type": "No idea",
630 "name": "myWim",
631 "description": "Descriptor name",
632 "wim_type": "odl",
633 "wim_url": "http://localhost:/wim",
634 "user": "user",
635 "password": "password",
636 "config": {"config_param": 1}
637 }
638
639 def run(self, engine, test_osm, manual_check, test_params=None):
640 engine.set_test_name("VimSdn")
641 engine.get_autorization()
642 # Added SDN
643 engine.test("Create SDN", "POST", "/admin/v1/sdns", headers_json, self.sdn, (201, 204),
644 {"Location": "/admin/v1/sdns/", "Content-Type": "application/json"}, "json")
645 sdnc_id = engine.last_id
646 # sleep(5)
647 # Edit SDN
648 engine.test("Edit SDN", "PATCH", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, {"name": "new_sdn_name"},
649 204, None, None)
650 # sleep(5)
651 # VIM with SDN
652 self.vim["config"]["sdn-controller"] = sdnc_id
653 self.vim["config"]["sdn-port-mapping"] = self.port_mapping
654 engine.test("Create VIM", "POST", "/admin/v1/vim_accounts", headers_json, self.vim, (200, 204, 201),
655 {"Location": "/admin/v1/vim_accounts/", "Content-Type": "application/json"}, "json"),
656
657 vim_id = engine.last_id
658 self.port_mapping[0]["compute_node"] = "compute node XX"
659 engine.test("Edit VIM change port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
660 {"config": {"sdn-port-mapping": self.port_mapping}}, 204, None, None)
661 engine.test("Edit VIM remove port-mapping", "PUT", "/admin/v1/vim_accounts/{}".format(vim_id), headers_json,
662 {"config": {"sdn-port-mapping": None}}, 204, None, None)
663
664 engine.test("Create WIM", "POST", "/admin/v1/wim_accounts", headers_json, self.wim, (200, 204, 201),
665 {"Location": "/admin/v1/wim_accounts/", "Content-Type": "application/json"}, "json"),
666 wim_id = engine.last_id
667
668 if not test_osm:
669 # delete with FORCE
670 engine.test("Delete VIM remove port-mapping", "DELETE",
671 "/admin/v1/vim_accounts/{}?FORCE=True".format(vim_id), headers_json, None, 202, None, 0)
672 engine.test("Delete SDNC", "DELETE", "/admin/v1/sdns/{}?FORCE=True".format(sdnc_id), headers_json, None,
673 202, None, 0)
674
675 engine.test("Delete WIM", "DELETE",
676 "/admin/v1/wim_accounts/{}?FORCE=True".format(wim_id), headers_json, None, 202, None, 0)
677 engine.test("Check VIM is deleted", "GET", "/admin/v1/vim_accounts/{}".format(vim_id), headers_yaml,
678 None, 404, r_header_yaml, "yaml")
679 engine.test("Check SDN is deleted", "GET", "/admin/v1/sdns/{}".format(sdnc_id), headers_yaml, None,
680 404, r_header_yaml, "yaml")
681 engine.test("Check WIM is deleted", "GET", "/admin/v1/wim_accounts/{}".format(wim_id), headers_yaml,
682 None, 404, r_header_yaml, "yaml")
683 else:
684 if manual_check:
685 input('VIM, SDN, WIM has been deployed. Perform manual check and press enter to resume')
686 # delete and wait until is really deleted
687 engine.test("Delete VIM remove port-mapping", "DELETE", "/admin/v1/vim_accounts/{}".format(vim_id),
688 headers_json, None, (202, 201, 204), None, 0)
689 engine.test("Delete SDN", "DELETE", "/admin/v1/sdns/{}".format(sdnc_id), headers_json, None,
690 (202, 201, 204), None, 0)
691 engine.test("Delete VIM", "DELETE", "/admin/v1/wim_accounts/{}".format(wim_id),
692 headers_json, None, (202, 201, 204), None, 0)
693 engine.wait_until_delete("/admin/v1/vim_accounts/{}".format(vim_id), timeout)
694 engine.wait_until_delete("/admin/v1/sdns/{}".format(sdnc_id), timeout)
695 engine.wait_until_delete("/admin/v1/wim_accounts/{}".format(wim_id), timeout)
696
697
698 class TestDeploy:
699 description = "Base class for downloading descriptors from ETSI, onboard and deploy in real VIM"
700
701 def __init__(self):
702 self.test_name = "DEPLOY"
703 self.nsd_id = None
704 self.vim_id = None
705 self.ns_id = None
706 self.vnfds_id = []
707 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
708 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
709 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
710 self.descriptor_edit = None
711 self.uses_configuration = False
712 self.users = {}
713 self.passwords = {}
714 self.commands = {}
715 self.keys = {}
716 self.timeout = 120
717 self.qforce = ""
718 self.ns_params = None
719 self.vnfr_ip_list = {}
720
721 def create_descriptors(self, engine):
722 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
723 if not os.path.exists(temp_dir):
724 os.makedirs(temp_dir)
725 for vnfd_index, vnfd_filename in enumerate(self.vnfd_filenames):
726 if "/" in vnfd_filename:
727 vnfd_filename_path = vnfd_filename
728 if not os.path.exists(vnfd_filename_path):
729 raise TestException("File '{}' does not exist".format(vnfd_filename_path))
730 else:
731 vnfd_filename_path = temp_dir + vnfd_filename
732 if not os.path.exists(vnfd_filename_path):
733 with open(vnfd_filename_path, "wb") as file:
734 response = requests.get(self.descriptor_url + vnfd_filename)
735 if response.status_code >= 300:
736 raise TestException("Error downloading descriptor from '{}': {}".format(
737 self.descriptor_url + vnfd_filename, response.status_code))
738 file.write(response.content)
739 if vnfd_filename_path.endswith(".yaml"):
740 headers = headers_yaml
741 else:
742 headers = headers_zip_yaml
743 if randint(0, 1) == 0:
744 # vnfd CREATE AND UPLOAD in one step:
745 engine.test("Onboard VNFD in one step", "POST",
746 "/vnfpkgm/v1/vnf_packages_content" + self.qforce, headers, "@b" + vnfd_filename_path, 201,
747 r_headers_yaml_location_vnfd,
748 "yaml")
749 self.vnfds_id.append(engine.last_id)
750 else:
751 # vnfd CREATE AND UPLOAD ZIP
752 engine.test("Onboard VNFD step 1", "POST", "/vnfpkgm/v1/vnf_packages",
753 headers_json, None, 201,
754 {"Location": "/vnfpkgm/v1/vnf_packages/", "Content-Type": "application/json"}, "json")
755 self.vnfds_id.append(engine.last_id)
756 engine.test("Onboard VNFD step 2 as ZIP", "PUT",
757 "/vnfpkgm/v1/vnf_packages/<>/package_content" + self.qforce,
758 headers, "@b" + vnfd_filename_path, 204, None, 0)
759
760 if self.descriptor_edit:
761 if "vnfd{}".format(vnfd_index) in self.descriptor_edit:
762 # Modify VNFD
763 engine.test("Edit VNFD ", "PATCH",
764 "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfds_id[-1]),
765 headers_yaml, self.descriptor_edit["vnfd{}".format(vnfd_index)], 204, None, None)
766
767 if "/" in self.nsd_filename:
768 nsd_filename_path = self.nsd_filename
769 if not os.path.exists(nsd_filename_path):
770 raise TestException("File '{}' does not exist".format(nsd_filename_path))
771 else:
772 nsd_filename_path = temp_dir + self.nsd_filename
773 if not os.path.exists(nsd_filename_path):
774 with open(nsd_filename_path, "wb") as file:
775 response = requests.get(self.descriptor_url + self.nsd_filename)
776 if response.status_code >= 300:
777 raise TestException("Error downloading descriptor from '{}': {}".format(
778 self.descriptor_url + self.nsd_filename, response.status_code))
779 file.write(response.content)
780 if nsd_filename_path.endswith(".yaml"):
781 headers = headers_yaml
782 else:
783 headers = headers_zip_yaml
784
785 if randint(0, 1) == 0:
786 # nsd CREATE AND UPLOAD in one step:
787 engine.test("Onboard NSD in one step", "POST",
788 "/nsd/v1/ns_descriptors_content" + self.qforce, headers, "@b" + nsd_filename_path, 201,
789 r_headers_yaml_location_nsd, yaml)
790 self.nsd_id = engine.last_id
791 else:
792 # nsd CREATE AND UPLOAD ZIP
793 engine.test("Onboard NSD step 1", "POST", "/nsd/v1/ns_descriptors",
794 headers_json, None, 201,
795 {"Location": "/nsd/v1/ns_descriptors/", "Content-Type": "application/json"}, "json")
796 self.nsd_id = engine.last_id
797 engine.test("Onboard NSD step 2 as ZIP", "PUT",
798 "/nsd/v1/ns_descriptors/<>/nsd_content" + self.qforce,
799 headers, "@b" + nsd_filename_path, 204, None, 0)
800
801 if self.descriptor_edit and "nsd" in self.descriptor_edit:
802 # Modify NSD
803 engine.test("Edit NSD ", "PATCH",
804 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
805 headers_yaml, self.descriptor_edit["nsd"], 204, None, None)
806
807 def delete_descriptors(self, engine):
808 # delete descriptors
809 engine.test("Delete NSSD SOL005", "DELETE",
810 "/nsd/v1/ns_descriptors/{}".format(self.nsd_id),
811 headers_yaml, None, 204, None, 0)
812 for vnfd_id in self.vnfds_id:
813 engine.test("Delete VNFD SOL005", "DELETE",
814 "/vnfpkgm/v1/vnf_packages/{}".format(vnfd_id), headers_yaml, None, 204, None, 0)
815
816 def instantiate(self, engine, ns_data):
817 ns_data_text = yaml.safe_dump(ns_data, default_flow_style=True, width=256)
818 # create NS Two steps
819 r = engine.test("Create NS step 1", "POST", "/nslcm/v1/ns_instances",
820 headers_yaml, ns_data_text, 201,
821 {"Location": "nslcm/v1/ns_instances/", "Content-Type": "application/yaml"}, "yaml")
822 if not r:
823 return
824 self.ns_id = engine.last_id
825 engine.test("Instantiate NS step 2", "POST",
826 "/nslcm/v1/ns_instances/{}/instantiate".format(self.ns_id), headers_yaml, ns_data_text,
827 201, r_headers_yaml_location_nslcmop, "yaml")
828 nslcmop_id = engine.last_id
829
830 if test_osm:
831 # Wait until status is Ok
832 timeout = timeout_configure if self.uses_configuration else timeout_deploy
833 engine.wait_operation_ready("ns", nslcmop_id, timeout)
834
835 def terminate(self, engine):
836 # remove deployment
837 if test_osm:
838 engine.test("Terminate NS", "POST", "/nslcm/v1/ns_instances/{}/terminate".format(self.ns_id), headers_yaml,
839 None, 201, r_headers_yaml_location_nslcmop, "yaml")
840 nslcmop2_id = engine.last_id
841 # Wait until status is Ok
842 engine.wait_operation_ready("ns", nslcmop2_id, timeout_deploy)
843
844 engine.test("Delete NS", "DELETE", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
845 204, None, 0)
846 else:
847 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
848 headers_yaml, None, 204, None, 0)
849
850 # check all it is deleted
851 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
852 404, None, "yaml")
853 r = engine.test("Check NSLCMOPs are deleted", "GET",
854 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
855 200, None, "json")
856 if not r:
857 return
858 nslcmops = r.json()
859 if not isinstance(nslcmops, list) or nslcmops:
860 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
861
862 def test_ns(self, engine, test_osm, commands=None, users=None, passwds=None, keys=None, timeout=0):
863
864 r = engine.test("GET VNFR IDs", "GET",
865 "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_json, None,
866 200, r_header_json, "json")
867 if not r:
868 return
869 ns_data = r.json()
870
871 vnfr_list = ns_data['constituent-vnfr-ref']
872 time = 0
873 _commands = commands if commands is not None else self.commands
874 _users = users if users is not None else self.users
875 _passwds = passwds if passwds is not None else self.passwords
876 _keys = keys if keys is not None else self.keys
877 _timeout = timeout if timeout != 0 else self.timeout
878
879 # vnfr_list=[d8272263-6bd3-4680-84ca-6a4be23b3f2d, 88b22e2f-994a-4b61-94fd-4a3c90de3dc4]
880 for vnfr_id in vnfr_list:
881 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
882 "/nslcm/v1/vnfrs/{}".format(vnfr_id), headers_json, None,
883 200, r_header_json, "json")
884 if not r:
885 continue
886 vnfr_data = r.json()
887
888 vnf_index = str(vnfr_data["member-vnf-index-ref"])
889
890 ip_address = self.get_vnfr_ip(engine, vnf_index)
891 description = "Exec command='{}' at VNFR={} IP={}".format(_commands.get(vnf_index)[0], vnf_index,
892 ip_address)
893 engine.step += 1
894 test_description = "{}{} {}".format(engine.test_name, engine.step, description)
895 logger.warning(test_description)
896 while _timeout >= time:
897 result, message = self.do_checks([ip_address],
898 vnf_index=vnfr_data["member-vnf-index-ref"],
899 commands=_commands.get(vnf_index), user=_users.get(vnf_index),
900 passwd=_passwds.get(vnf_index), key=_keys.get(vnf_index))
901 if result == 1:
902 engine.passed_tests += 1
903 logger.debug(message)
904 break
905 elif result == 0:
906 time += 20
907 sleep(20)
908 elif result == -1:
909 engine.failed_tests += 1
910 logger.error(message)
911 break
912 else:
913 time -= 20
914 engine.failed_tests += 1
915 logger.error(message)
916 else:
917 engine.failed_tests += 1
918 logger.error("VNFR {} has not mgmt address. Check failed".format(vnf_index))
919
920 def do_checks(self, ip, vnf_index, commands=[], user=None, passwd=None, key=None):
921 try:
922 import urllib3
923 from pssh.clients import ParallelSSHClient
924 from pssh.utils import load_private_key
925 from ssh2 import exceptions as ssh2Exception
926 except ImportError as e:
927 logger.critical("Package <pssh> or/and <urllib3> is not installed. Please add them with 'pip3 install "
928 "parallel-ssh urllib3': {}".format(e))
929 return -1, "install needed packages 'pip3 install parallel-ssh urllib3'"
930 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
931 try:
932 p_host = os.environ.get("PROXY_HOST")
933 p_user = os.environ.get("PROXY_USER")
934 p_password = os.environ.get("PROXY_PASSWD")
935
936 if key:
937 pkey = load_private_key(key)
938 else:
939 pkey = None
940
941 client = ParallelSSHClient(ip, user=user, password=passwd, pkey=pkey, proxy_host=p_host,
942 proxy_user=p_user, proxy_password=p_password, timeout=10, num_retries=0)
943 for cmd in commands:
944 output = client.run_command(cmd)
945 client.join(output)
946 if output[ip[0]].exit_code:
947 return -1, "VNFR {} command '{}' returns error: '{}'".format(ip[0], cmd,
948 "\n".join(output[ip[0]].stderr))
949 else:
950 return 1, "VNFR {} command '{}' successful".format(ip[0], cmd)
951 except (ssh2Exception.ChannelFailure, ssh2Exception.SocketDisconnectError, ssh2Exception.SocketTimeout,
952 ssh2Exception.SocketRecvError) as e:
953 return 0, "Timeout accessing the VNFR {}: {}".format(ip[0], str(e))
954 except Exception as e:
955 return -1, "ERROR checking the VNFR {}: {}".format(ip[0], str(e))
956
957 def additional_operations(self, engine, test_osm, manual_check):
958 pass
959
960 def run(self, engine, test_osm, manual_check, test_params=None):
961 engine.set_test_name(self.test_name)
962 engine.get_autorization()
963 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
964 if test_params:
965 if "vnfd-files" in test_params:
966 self.vnfd_filenames = test_params["vnfd-files"].split(",")
967 if "nsd-file" in test_params:
968 self.nsd_filename = test_params["nsd-file"]
969 if test_params.get("ns-name"):
970 nsname = test_params["ns-name"]
971 self.create_descriptors(engine)
972
973 # create real VIM if not exist
974 self.vim_id = engine.get_create_vim(test_osm)
975 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
976 "vimAccountId": self.vim_id}
977 if self.ns_params:
978 ns_data.update(self.ns_params)
979 if test_params and test_params.get("ns-config"):
980 if isinstance(test_params["ns-config"], str):
981 ns_data.update(yaml.load(test_params["ns-config"]))
982 else:
983 ns_data.update(test_params["ns-config"])
984 self.instantiate(engine, ns_data)
985
986 if manual_check:
987 input('NS has been deployed. Perform manual check and press enter to resume')
988 if test_osm and self.commands:
989 self.test_ns(engine, test_osm)
990 self.additional_operations(engine, test_osm, manual_check)
991 self.terminate(engine)
992 self.delete_descriptors(engine)
993
994 def get_first_ip(self, ip_string):
995 # When using a floating IP, the vnfr_data['ip-address'] contains a semicolon-separated list of IP:s.
996 first_ip = ip_string.split(";")[0] if ip_string else ""
997 return first_ip
998
999 def get_vnfr_ip(self, engine, vnfr_index_wanted):
1000 # If the IP address list has been obtained before, it has been stored in 'vnfr_ip_list'
1001 ip = self.vnfr_ip_list.get(vnfr_index_wanted, "")
1002 if (ip):
1003 return self.get_first_ip(ip)
1004 r = engine.test("Get VNFR to get IP_ADDRESS", "GET",
1005 "/nslcm/v1/vnfrs?member-vnf-index-ref={}&nsr-id-ref={}".format(
1006 vnfr_index_wanted, self.ns_id), headers_json, None,
1007 200, r_header_json, "json")
1008 if not r:
1009 return ""
1010 vnfr_data = r.json()
1011 if not (vnfr_data and vnfr_data[0]):
1012 return ""
1013 # Store the IP (or list of IPs) in 'vnfr_ip_list'
1014 ip_list = vnfr_data[0].get("ip-address", "")
1015 if ip_list:
1016 self.vnfr_ip_list[vnfr_index_wanted] = ip_list
1017 ip = self.get_first_ip(ip_list)
1018 return ip
1019
1020
1021 class TestDeployHackfestCirros(TestDeploy):
1022 description = "Load and deploy Hackfest cirros_2vnf_ns example"
1023
1024 def __init__(self):
1025 super().__init__()
1026 self.test_name = "CIRROS"
1027 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1028 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1029 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1030 self.users = {'1': "cirros", '2': "cirros"}
1031 self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1032
1033 def terminate(self, engine):
1034 # Make a delete in one step, overriding the normal two step of TestDeploy that launched terminate and delete
1035 if test_osm:
1036 engine.test("Terminate and delete NS in one step", "DELETE", "/nslcm/v1/ns_instances_content/{}".
1037 format(self.ns_id), headers_yaml, None, 202, None, "yaml")
1038
1039 engine .wait_until_delete("/nslcm/v1/ns_instances/{}".format(self.ns_id), timeout_deploy)
1040 else:
1041 engine.test("Delete NS with FORCE", "DELETE", "/nslcm/v1/ns_instances/{}?FORCE=True".format(self.ns_id),
1042 headers_yaml, None, 204, None, 0)
1043
1044 # check all it is deleted
1045 engine.test("Check NS is deleted", "GET", "/nslcm/v1/ns_instances/{}".format(self.ns_id), headers_yaml, None,
1046 404, None, "yaml")
1047 r = engine.test("Check NSLCMOPs are deleted", "GET",
1048 "/nslcm/v1/ns_lcm_op_occs?nsInstanceId={}".format(self.ns_id), headers_json, None,
1049 200, None, "json")
1050 if not r:
1051 return
1052 nslcmops = r.json()
1053 if not isinstance(nslcmops, list) or nslcmops:
1054 raise TestException("NS {} deleted but with ns_lcm_op_occ active: {}".format(self.ns_id, nslcmops))
1055
1056
1057 class TestDeployHackfest1(TestDeploy):
1058 description = "Load and deploy Hackfest_1_vnfd example"
1059
1060 def __init__(self):
1061 super().__init__()
1062 self.test_name = "HACKFEST1-"
1063 self.vnfd_filenames = ("hackfest_1_vnfd.tar.gz",)
1064 self.nsd_filename = "hackfest_1_nsd.tar.gz"
1065 # self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1066 # self.users = {'1': "cirros", '2': "cirros"}
1067 # self.passwords = {'1': "cubswin:)", '2': "cubswin:)"}
1068
1069
1070 class TestDeployHackfestCirrosScaling(TestDeploy):
1071 description = "Load and deploy Hackfest cirros_2vnf_ns example with scaling modifications"
1072
1073 def __init__(self):
1074 super().__init__()
1075 self.test_name = "CIRROS-SCALE"
1076 self.vnfd_filenames = ("cirros_vnf.tar.gz",)
1077 self.nsd_filename = "cirros_2vnf_ns.tar.gz"
1078
1079 def create_descriptors(self, engine):
1080 super().create_descriptors(engine)
1081 # Modify VNFD to add scaling and count=2
1082 self.descriptor_edit = {
1083 "vnfd0": {
1084 "vdu": {
1085 "$id: 'cirros_vnfd-VM'": {"count": 2}
1086 },
1087 "scaling-group-descriptor": [{
1088 "name": "scale_cirros",
1089 "max-instance-count": 2,
1090 "vdu": [{
1091 "vdu-id-ref": "cirros_vnfd-VM",
1092 "count": 2
1093 }]
1094 }]
1095 }
1096 }
1097
1098 def additional_operations(self, engine, test_osm, manual_check):
1099 if not test_osm:
1100 return
1101 # 2 perform scale out twice
1102 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1103 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1104 for i in range(0, 2):
1105 engine.test("Execute scale action over NS", "POST",
1106 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1107 201, r_headers_yaml_location_nslcmop, "yaml")
1108 nslcmop2_scale_out = engine.last_id
1109 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1110 if manual_check:
1111 input('NS scale out done. Check that two more vdus are there')
1112 # TODO check automatic
1113
1114 # 2 perform scale in
1115 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1116 '{scaling-group-descriptor: scale_cirros, member-vnf-index: "1"}}}'
1117 for i in range(0, 2):
1118 engine.test("Execute scale IN action over NS", "POST",
1119 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1120 201, r_headers_yaml_location_nslcmop, "yaml")
1121 nslcmop2_scale_in = engine.last_id
1122 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1123 if manual_check:
1124 input('NS scale in done. Check that two less vdus are there')
1125 # TODO check automatic
1126
1127 # perform scale in that must fail as reached limit
1128 engine.test("Execute scale IN out of limit action over NS", "POST",
1129 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1130 201, r_headers_yaml_location_nslcmop, "yaml")
1131 nslcmop2_scale_in = engine.last_id
1132 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy, expected_fail=True)
1133
1134
1135 class TestDeployIpMac(TestDeploy):
1136 description = "Load and deploy descriptor examples setting mac, ip address at descriptor and instantiate params"
1137
1138 def __init__(self):
1139 super().__init__()
1140 self.test_name = "SetIpMac"
1141 self.vnfd_filenames = ("vnfd_2vdu_set_ip_mac2.yaml", "vnfd_2vdu_set_ip_mac.yaml")
1142 self.nsd_filename = "scenario_2vdu_set_ip_mac.yaml"
1143 self.descriptor_url = \
1144 "https://osm.etsi.org/gitweb/?p=osm/RO.git;a=blob_plain;f=test/RO_tests/v3_2vdu_set_ip_mac/"
1145 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1146 self.users = {'1': "osm", '2': "osm"}
1147 self.passwords = {'1': "osm4u", '2': "osm4u"}
1148 self.timeout = 360
1149
1150 def run(self, engine, test_osm, manual_check, test_params=None):
1151 # super().run(engine, test_osm, manual_check, test_params)
1152 # run again setting IPs with instantiate parameters
1153 instantiation_params = {
1154 "vnf": [
1155 {
1156 "member-vnf-index": "1",
1157 "internal-vld": [
1158 {
1159 "name": "internal_vld1", # net_internal
1160 "ip-profile": {
1161 "ip-version": "ipv4",
1162 "subnet-address": "10.9.8.0/24",
1163 "dhcp-params": {"count": 100, "start-address": "10.9.8.100"}
1164 },
1165 "internal-connection-point": [
1166 {
1167 "id-ref": "eth2",
1168 "ip-address": "10.9.8.2",
1169 },
1170 {
1171 "id-ref": "eth3",
1172 "ip-address": "10.9.8.3",
1173 }
1174 ]
1175 },
1176 ],
1177
1178 "vdu": [
1179 {
1180 "id": "VM1",
1181 "interface": [
1182 # {
1183 # "name": "iface11",
1184 # "floating-ip-required": True,
1185 # },
1186 {
1187 "name": "iface13",
1188 "mac-address": "52:33:44:55:66:13"
1189 },
1190 ],
1191 },
1192 {
1193 "id": "VM2",
1194 "interface": [
1195 {
1196 "name": "iface21",
1197 "ip-address": "10.31.31.22",
1198 "mac-address": "52:33:44:55:66:21"
1199 },
1200 ],
1201 },
1202 ]
1203 },
1204 ]
1205 }
1206
1207 super().run(engine, test_osm, manual_check, test_params={"ns-config": instantiation_params})
1208
1209
1210 class TestDeployHackfest4(TestDeploy):
1211 description = "Load and deploy Hackfest 4 example."
1212
1213 def __init__(self):
1214 super().__init__()
1215 self.test_name = "HACKFEST4-"
1216 self.vnfd_filenames = ("hackfest_4_vnfd.tar.gz",)
1217 self.nsd_filename = "hackfest_4_nsd.tar.gz"
1218 self.uses_configuration = True
1219 self.commands = {'1': ['ls -lrt', ], '2': ['ls -lrt', ]}
1220 self.users = {'1': "ubuntu", '2': "ubuntu"}
1221 self.passwords = {'1': "osm4u", '2': "osm4u"}
1222 # Modify VNFD to add scaling
1223 # self.descriptor_edit = {
1224 # "vnfd0": {
1225 # 'vnf-configuration': {
1226 # 'config-primitive': [{
1227 # 'name': 'touch',
1228 # 'parameter': [{
1229 # 'name': 'filename',
1230 # 'data-type': 'STRING',
1231 # 'default-value': '/home/ubuntu/touched'
1232 # }]
1233 # }]
1234 # },
1235 # 'scaling-group-descriptor': [{
1236 # 'name': 'scale_dataVM',
1237 # 'scaling-policy': [{
1238 # 'threshold-time': 0,
1239 # 'name': 'auto_cpu_util_above_threshold',
1240 # 'scaling-type': 'automatic',
1241 # 'scaling-criteria': [{
1242 # 'name': 'cpu_util_above_threshold',
1243 # 'vnf-monitoring-param-ref': 'all_aaa_cpu_util',
1244 # 'scale-out-relational-operation': 'GE',
1245 # 'scale-in-threshold': 15,
1246 # 'scale-out-threshold': 60,
1247 # 'scale-in-relational-operation': 'LE'
1248 # }],
1249 # 'cooldown-time': 60
1250 # }],
1251 # 'max-instance-count': 10,
1252 # 'scaling-config-action': [
1253 # {'vnf-config-primitive-name-ref': 'touch',
1254 # 'trigger': 'post-scale-out'},
1255 # {'vnf-config-primitive-name-ref': 'touch',
1256 # 'trigger': 'pre-scale-in'}
1257 # ],
1258 # 'vdu': [{
1259 # 'vdu-id-ref': 'dataVM',
1260 # 'count': 1
1261 # }]
1262 # }]
1263 # }
1264 # }
1265
1266
1267 class TestDeployHackfest3Charmed(TestDeploy):
1268 description = "Load and deploy Hackfest 3charmed_ns example"
1269
1270 def __init__(self):
1271 super().__init__()
1272 self.test_name = "HACKFEST3-"
1273 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz",)
1274 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1275 self.uses_configuration = True
1276 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch'], '2': ['ls -lrt /home/ubuntu/first-touch']}
1277 self.users = {'1': "ubuntu", '2': "ubuntu"}
1278 self.passwords = {'1': "osm4u", '2': "osm4u"}
1279 self.descriptor_edit = {
1280 "vnfd0": yaml.full_load(
1281 """
1282 vnf-configuration:
1283 terminate-config-primitive:
1284 - seq: '1'
1285 name: touch
1286 parameter:
1287 - name: filename
1288 value: '/home/ubuntu/last-touch1'
1289 - seq: '3'
1290 name: touch
1291 parameter:
1292 - name: filename
1293 value: '/home/ubuntu/last-touch3'
1294 - seq: '2'
1295 name: touch
1296 parameter:
1297 - name: filename
1298 value: '/home/ubuntu/last-touch2'
1299 """)
1300 }
1301
1302 def additional_operations(self, engine, test_osm, manual_check):
1303 if not test_osm:
1304 return
1305 # 1 perform action
1306 vnfr_index_selected = "2"
1307 payload = '{member_vnf_index: "2", primitive: touch, primitive_params: { filename: /home/ubuntu/OSMTESTNBI }}'
1308 engine.test("Exec service primitive over NS", "POST",
1309 "/nslcm/v1/ns_instances/{}/action".format(self.ns_id), headers_yaml, payload,
1310 201, r_headers_yaml_location_nslcmop, "yaml")
1311 nslcmop2_action = engine.last_id
1312 # Wait until status is Ok
1313 engine.wait_operation_ready("ns", nslcmop2_action, timeout_deploy)
1314 vnfr_ip = self.get_vnfr_ip(engine, vnfr_index_selected)
1315 if manual_check:
1316 input(
1317 "NS service primitive has been executed."
1318 "Check that file /home/ubuntu/OSMTESTNBI is present at {}".
1319 format(vnfr_ip))
1320 if test_osm:
1321 commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/OSMTESTNBI', ]}
1322 self.test_ns(engine, test_osm, commands=commands)
1323
1324 # # 2 perform scale out
1325 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1326 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1327 # engine.test("Execute scale action over NS", "POST",
1328 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1329 # 201, r_headers_yaml_location_nslcmop, "yaml")
1330 # nslcmop2_scale_out = engine.last_id
1331 # engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1332 # if manual_check:
1333 # input('NS scale out done. Check that file /home/ubuntu/touched is present and new VM is created')
1334 # # TODO check automatic
1335 #
1336 # # 2 perform scale in
1337 # payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1338 # '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1339 # engine.test("Execute scale action over NS", "POST",
1340 # "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1341 # 201, r_headers_yaml_location_nslcmop, "yaml")
1342 # nslcmop2_scale_in = engine.last_id
1343 # engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1344 # if manual_check:
1345 # input('NS scale in done. Check that file /home/ubuntu/touched is updated and new VM is deleted')
1346 # # TODO check automatic
1347
1348
1349 class TestDeployHackfest3Charmed2(TestDeployHackfest3Charmed):
1350 description = "Load and deploy Hackfest 3charmed_ns example modified version of descriptors to have dots in " \
1351 "ids and member-vnf-index."
1352
1353 def __init__(self):
1354 super().__init__()
1355 self.test_name = "HACKFEST3v2-"
1356 self.qforce = "?FORCE=True"
1357 self.descriptor_edit = {
1358 "vnfd0": {
1359 "vdu": {
1360 "$[0]": {
1361 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1362 },
1363 "$[1]": None
1364 },
1365 "vnf-configuration": None,
1366 "connection-point": {
1367 "$[0]": {
1368 "id": "pdu-mgmt",
1369 "name": "pdu-mgmt",
1370 "short-name": "pdu-mgmt"
1371 },
1372 "$[1]": None
1373 },
1374 "mgmt-interface": {"cp": "pdu-mgmt"},
1375 "description": "A vnf single vdu to be used as PDU",
1376 "id": "vdu-as-pdu",
1377 "internal-vld": {
1378 "$[0]": {
1379 "id": "pdu_internal",
1380 "name": "pdu_internal",
1381 "internal-connection-point": {"$[1]": None},
1382 "short-name": "pdu_internal",
1383 "type": "ELAN"
1384 }
1385 }
1386 },
1387
1388 # Modify NSD accordingly
1389 "nsd": {
1390 "constituent-vnfd": {
1391 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1392 "$[1]": None,
1393 },
1394 "description": "A nsd to deploy the vnf to act as as PDU",
1395 "id": "nsd-as-pdu",
1396 "name": "nsd-as-pdu",
1397 "short-name": "nsd-as-pdu",
1398 "vld": {
1399 "$[0]": {
1400 "id": "mgmt_pdu",
1401 "name": "mgmt_pdu",
1402 "short-name": "mgmt_pdu",
1403 "vnfd-connection-point-ref": {
1404 "$[0]": {
1405 "vnfd-connection-point-ref": "pdu-mgmt",
1406 "vnfd-id-ref": "vdu-as-pdu",
1407 },
1408 "$[1]": None
1409 },
1410 "type": "ELAN"
1411 },
1412 "$[1]": None,
1413 }
1414 }
1415 }
1416
1417
1418 class TestDeployHackfest3Charmed3(TestDeployHackfest3Charmed):
1419 description = "Load and deploy Hackfest 3charmed_ns example modified version to test scaling and NS parameters"
1420
1421 def __init__(self):
1422 super().__init__()
1423 self.test_name = "HACKFEST3v3-"
1424 self.commands = {'1': ['ls -lrt /home/ubuntu/first-touch-1'], '2': ['ls -lrt /home/ubuntu/first-touch-2']}
1425 self.descriptor_edit = {
1426 "vnfd0": yaml.load(
1427 """
1428 scaling-group-descriptor:
1429 - name: "scale_dataVM"
1430 max-instance-count: 10
1431 scaling-policy:
1432 - name: "auto_cpu_util_above_threshold"
1433 scaling-type: "automatic"
1434 threshold-time: 0
1435 cooldown-time: 60
1436 scaling-criteria:
1437 - name: "cpu_util_above_threshold"
1438 scale-in-threshold: 15
1439 scale-in-relational-operation: "LE"
1440 scale-out-threshold: 60
1441 scale-out-relational-operation: "GE"
1442 vnf-monitoring-param-ref: "monitor1"
1443 vdu:
1444 - vdu-id-ref: dataVM
1445 count: 1
1446 scaling-config-action:
1447 - trigger: post-scale-out
1448 vnf-config-primitive-name-ref: touch
1449 - trigger: pre-scale-in
1450 vnf-config-primitive-name-ref: touch
1451 vdu:
1452 "$id: dataVM":
1453 monitoring-param:
1454 - id: "dataVM_cpu_util"
1455 nfvi-metric: "cpu_utilization"
1456
1457 monitoring-param:
1458 - id: "monitor1"
1459 name: "monitor1"
1460 aggregation-type: AVERAGE
1461 vdu-monitoring-param:
1462 vdu-ref: "dataVM"
1463 vdu-monitoring-param-ref: "dataVM_cpu_util"
1464 vnf-configuration:
1465 initial-config-primitive:
1466 "$[1]":
1467 parameter:
1468 "$[0]":
1469 value: "<touch-filename>" # default-value: /home/ubuntu/first-touch
1470 config-primitive:
1471 "$[0]":
1472 parameter:
1473 "$[0]":
1474 default-value: "<touch-filename2>"
1475 """)
1476 }
1477 self.ns_params = {
1478 "additionalParamsForVnf": [
1479 {"member-vnf-index": "1", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-1",
1480 "touch-filename2": "/home/ubuntu/second-touch-1"}},
1481 {"member-vnf-index": "2", "additionalParams": {"touch-filename": "/home/ubuntu/first-touch-2",
1482 "touch-filename2": "/home/ubuntu/second-touch-2"}},
1483 ]
1484 }
1485
1486 def additional_operations(self, engine, test_osm, manual_check):
1487 super().additional_operations(engine, test_osm, manual_check)
1488 if not test_osm:
1489 return
1490
1491 # 2 perform scale out
1492 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_OUT, scaleByStepData: ' \
1493 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1494 engine.test("Execute scale action over NS", "POST",
1495 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1496 201, r_headers_yaml_location_nslcmop, "yaml")
1497 nslcmop2_scale_out = engine.last_id
1498 engine.wait_operation_ready("ns", nslcmop2_scale_out, timeout_deploy)
1499 if manual_check:
1500 input('NS scale out done. Check that file /home/ubuntu/second-touch-1 is present and new VM is created')
1501 if test_osm:
1502 commands = {'1': ['ls -lrt /home/ubuntu/second-touch-1', ]}
1503 self.test_ns(engine, test_osm, commands=commands)
1504 # TODO check automatic connection to scaled VM
1505
1506 # 2 perform scale in
1507 payload = '{scaleType: SCALE_VNF, scaleVnfData: {scaleVnfType: SCALE_IN, scaleByStepData: ' \
1508 '{scaling-group-descriptor: scale_dataVM, member-vnf-index: "1"}}}'
1509 engine.test("Execute scale action over NS", "POST",
1510 "/nslcm/v1/ns_instances/{}/scale".format(self.ns_id), headers_yaml, payload,
1511 201, r_headers_yaml_location_nslcmop, "yaml")
1512 nslcmop2_scale_in = engine.last_id
1513 engine.wait_operation_ready("ns", nslcmop2_scale_in, timeout_deploy)
1514 if manual_check:
1515 input('NS scale in done. Check that file /home/ubuntu/second-touch-1 is updated and new VM is deleted')
1516 # TODO check automatic
1517
1518
1519 class TestDeploySimpleCharm(TestDeploy):
1520 description = "Deploy hackfest-4 hackfest_simplecharm example"
1521
1522 def __init__(self):
1523 super().__init__()
1524 self.test_name = "HACKFEST-SIMPLE"
1525 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-4.0-four/4th-hackfest/packages/"
1526 self.vnfd_filenames = ("hackfest_simplecharm_vnf.tar.gz",)
1527 self.nsd_filename = "hackfest_simplecharm_ns.tar.gz"
1528 self.uses_configuration = True
1529 self.commands = {'1': [''], '2': ['ls -lrt /home/ubuntu/first-touch', ]}
1530 self.users = {'1': "ubuntu", '2': "ubuntu"}
1531 self.passwords = {'1': "osm4u", '2': "osm4u"}
1532
1533
1534 class TestDeploySimpleCharm2(TestDeploySimpleCharm):
1535 description = "Deploy hackfest-4 hackfest_simplecharm example changing naming to contain dots on ids and " \
1536 "vnf-member-index"
1537
1538 def __init__(self):
1539 super().__init__()
1540 self.test_name = "HACKFEST-SIMPLE2-"
1541 self.qforce = "?FORCE=True"
1542 self.descriptor_edit = {
1543 "vnfd0": {
1544 "id": "hackfest.simplecharm.vnf"
1545 },
1546
1547 "nsd": {
1548 "id": "hackfest.simplecharm.ns",
1549 "constituent-vnfd": {
1550 "$[0]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$1"},
1551 "$[1]": {"vnfd-id-ref": "hackfest.simplecharm.vnf", "member-vnf-index": "$2"},
1552 },
1553 "vld": {
1554 "$[0]": {
1555 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1556 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1557 "$[1]": {"member-vnf-index-ref": "$2",
1558 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1559 },
1560 "$[1]": {
1561 "vnfd-connection-point-ref": {"$[0]": {"member-vnf-index-ref": "$1",
1562 "vnfd-id-ref": "hackfest.simplecharm.vnf"},
1563 "$[1]": {"member-vnf-index-ref": "$2",
1564 "vnfd-id-ref": "hackfest.simplecharm.vnf"}},
1565 },
1566 }
1567 }
1568 }
1569
1570
1571 class TestDeploySingleVdu(TestDeployHackfest3Charmed):
1572 description = "Generate a single VDU base on editing Hackfest3Charmed descriptors and deploy"
1573
1574 def __init__(self):
1575 super().__init__()
1576 self.test_name = "SingleVDU"
1577 self.qforce = "?FORCE=True"
1578 self.descriptor_edit = {
1579 # Modify VNFD to remove one VDU
1580 "vnfd0": {
1581 "vdu": {
1582 "$[0]": {
1583 "interface": {"$[0]": {"external-connection-point-ref": "pdu-mgmt"}}
1584 },
1585 "$[1]": None
1586 },
1587 "vnf-configuration": None,
1588 "connection-point": {
1589 "$[0]": {
1590 "id": "pdu-mgmt",
1591 "name": "pdu-mgmt",
1592 "short-name": "pdu-mgmt"
1593 },
1594 "$[1]": None
1595 },
1596 "mgmt-interface": {"cp": "pdu-mgmt"},
1597 "description": "A vnf single vdu to be used as PDU",
1598 "id": "vdu-as-pdu",
1599 "internal-vld": {
1600 "$[0]": {
1601 "id": "pdu_internal",
1602 "name": "pdu_internal",
1603 "internal-connection-point": {"$[1]": None},
1604 "short-name": "pdu_internal",
1605 "type": "ELAN"
1606 }
1607 }
1608 },
1609
1610 # Modify NSD accordingly
1611 "nsd": {
1612 "constituent-vnfd": {
1613 "$[0]": {"vnfd-id-ref": "vdu-as-pdu"},
1614 "$[1]": None,
1615 },
1616 "description": "A nsd to deploy the vnf to act as as PDU",
1617 "id": "nsd-as-pdu",
1618 "name": "nsd-as-pdu",
1619 "short-name": "nsd-as-pdu",
1620 "vld": {
1621 "$[0]": {
1622 "id": "mgmt_pdu",
1623 "name": "mgmt_pdu",
1624 "short-name": "mgmt_pdu",
1625 "vnfd-connection-point-ref": {
1626 "$[0]": {
1627 "vnfd-connection-point-ref": "pdu-mgmt",
1628 "vnfd-id-ref": "vdu-as-pdu",
1629 },
1630 "$[1]": None
1631 },
1632 "type": "ELAN"
1633 },
1634 "$[1]": None,
1635 }
1636 }
1637 }
1638
1639
1640 class TestDeployHnfd(TestDeployHackfest3Charmed):
1641 description = "Generate a HNFD base on editing Hackfest3Charmed descriptors and deploy"
1642
1643 def __init__(self):
1644 super().__init__()
1645 self.test_name = "HNFD"
1646 self.pduDeploy = TestDeploySingleVdu()
1647 self.pdu_interface_0 = {}
1648 self.pdu_interface_1 = {}
1649
1650 self.pdu_id = None
1651 # self.vnf_to_pdu = """
1652 # vdu:
1653 # "$[0]":
1654 # pdu-type: PDU-TYPE-1
1655 # interface:
1656 # "$[0]":
1657 # name: mgmt-iface
1658 # "$[1]":
1659 # name: pdu-iface-internal
1660 # id: hfn1
1661 # description: HFND, one PDU + One VDU
1662 # name: hfn1
1663 # short-name: hfn1
1664 #
1665 # """
1666
1667 self.pdu_descriptor = {
1668 "name": "my-PDU",
1669 "type": "PDU-TYPE-1",
1670 "vim_accounts": "to-override",
1671 "interfaces": [
1672 {
1673 "name": "mgmt-iface",
1674 "mgmt": True,
1675 "type": "overlay",
1676 "ip-address": "to override",
1677 "mac-address": "mac_address",
1678 "vim-network-name": "mgmt",
1679 },
1680 {
1681 "name": "pdu-iface-internal",
1682 "mgmt": False,
1683 "type": "overlay",
1684 "ip-address": "to override",
1685 "mac-address": "mac_address",
1686 "vim-network-name": "pdu_internal", # OSMNBITEST-PDU-pdu_internal
1687 },
1688 ]
1689 }
1690 self.vnfd_filenames = ("hackfest_3charmed_vnfd.tar.gz", "hackfest_3charmed_vnfd.tar.gz")
1691
1692 self.descriptor_edit = {
1693 "vnfd0": {
1694 "id": "hfnd1",
1695 "name": "hfn1",
1696 "short-name": "hfn1",
1697 "vdu": {
1698 "$[0]": {
1699 "pdu-type": "PDU-TYPE-1",
1700 "interface": {
1701 "$[0]": {"name": "mgmt-iface"},
1702 "$[1]": {"name": "pdu-iface-internal"},
1703 }
1704 }
1705 }
1706 },
1707 "nsd": {
1708 "constituent-vnfd": {
1709 "$[1]": {"vnfd-id-ref": "hfnd1"}
1710 },
1711 "vld": {
1712 "$[0]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}},
1713 "$[1]": {"vnfd-connection-point-ref": {"$[1]": {"vnfd-id-ref": "hfnd1"}}}
1714 }
1715 }
1716 }
1717
1718 def create_descriptors(self, engine):
1719 super().create_descriptors(engine)
1720
1721 # Create PDU
1722 self.pdu_descriptor["interfaces"][0].update(self.pdu_interface_0)
1723 self.pdu_descriptor["interfaces"][1].update(self.pdu_interface_1)
1724 self.pdu_descriptor["vim_accounts"] = [self.vim_id]
1725 # TODO get vim-network-name from vnfr.vld.name
1726 self.pdu_descriptor["interfaces"][1]["vim-network-name"] = "{}-{}-{}".format(
1727 os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST"),
1728 "PDU", self.pdu_descriptor["interfaces"][1]["vim-network-name"])
1729 engine.test("Onboard PDU descriptor", "POST", "/pdu/v1/pdu_descriptors",
1730 {"Location": "/pdu/v1/pdu_descriptors/", "Content-Type": "application/yaml"}, self.pdu_descriptor,
1731 201, r_header_yaml, "yaml")
1732 self.pdu_id = engine.last_id
1733
1734 def run(self, engine, test_osm, manual_check, test_params=None):
1735 engine.get_autorization()
1736 engine.set_test_name(self.test_name)
1737 nsname = os.environ.get("OSMNBITEST_NS_NAME", "OSMNBITEST")
1738
1739 # create real VIM if not exist
1740 self.vim_id = engine.get_create_vim(test_osm)
1741 # instantiate PDU
1742 self.pduDeploy.create_descriptors(engine)
1743 self.pduDeploy.instantiate(engine, {"nsDescription": "to be used as PDU", "nsName": nsname + "-PDU",
1744 "nsdId": self.pduDeploy.nsd_id, "vimAccountId": self.vim_id})
1745 if manual_check:
1746 input('VNF to be used as PDU has been deployed. Perform manual check and press enter to resume')
1747 if test_osm:
1748 self.pduDeploy.test_ns(engine, test_osm)
1749
1750 if test_osm:
1751 r = engine.test("Get VNFR to obtain IP_ADDRESS", "GET",
1752 "/nslcm/v1/vnfrs?nsr-id-ref={}".format(self.pduDeploy.ns_id), headers_json, None,
1753 200, r_header_json, "json")
1754 if not r:
1755 return
1756 vnfr_data = r.json()
1757 # print(vnfr_data)
1758
1759 self.pdu_interface_0["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("ip-address")
1760 self.pdu_interface_1["ip-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("ip-address")
1761 self.pdu_interface_0["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][0].get("mac-address")
1762 self.pdu_interface_1["mac-address"] = vnfr_data[0]["vdur"][0]["interfaces"][1].get("mac-address")
1763 if not self.pdu_interface_0["ip-address"]:
1764 raise TestException("Vnfr has not managment ip address")
1765 else:
1766 self.pdu_interface_0["ip-address"] = "192.168.10.10"
1767 self.pdu_interface_1["ip-address"] = "192.168.11.10"
1768 self.pdu_interface_0["mac-address"] = "52:33:44:55:66:13"
1769 self.pdu_interface_1["mac-address"] = "52:33:44:55:66:14"
1770
1771 self.create_descriptors(engine)
1772
1773 ns_data = {"nsDescription": "default description", "nsName": nsname, "nsdId": self.nsd_id,
1774 "vimAccountId": self.vim_id}
1775 if test_params and test_params.get("ns-config"):
1776 if isinstance(test_params["ns-config"], str):
1777 ns_data.update(yaml.load(test_params["ns-config"]))
1778 else:
1779 ns_data.update(test_params["ns-config"])
1780
1781 self.instantiate(engine, ns_data)
1782 if manual_check:
1783 input('NS has been deployed. Perform manual check and press enter to resume')
1784 if test_osm:
1785 self.test_ns(engine, test_osm)
1786 self.additional_operations(engine, test_osm, manual_check)
1787 self.terminate(engine)
1788 self.pduDeploy.terminate(engine)
1789 self.delete_descriptors(engine)
1790 self.pduDeploy.delete_descriptors(engine)
1791
1792 def delete_descriptors(self, engine):
1793 super().delete_descriptors(engine)
1794 # delete pdu
1795 engine.test("Delete PDU SOL005", "DELETE",
1796 "/pdu/v1/pdu_descriptors/{}".format(self.pdu_id),
1797 headers_yaml, None, 204, None, 0)
1798
1799
1800 class TestDescriptors:
1801 description = "Test VNFD, NSD, PDU descriptors CRUD and dependencies"
1802
1803 def __init__(self):
1804 self.vnfd_filename = "hackfest_3charmed_vnfd.tar.gz"
1805 self.nsd_filename = "hackfest_3charmed_nsd.tar.gz"
1806 self.descriptor_url = "https://osm-download.etsi.org/ftp/osm-3.0-three/2nd-hackfest/packages/"
1807 self.vnfd_id = None
1808 self.nsd_id = None
1809 self.vnfd_empty = """vnfd:vnfd-catalog:
1810 vnfd:
1811 - name: prova
1812 short-name: prova
1813 id: prova
1814 """
1815 self.vnfd_prova = """vnfd:vnfd-catalog:
1816 vnfd:
1817 - connection-point:
1818 - name: cp_0h8m
1819 type: VPORT
1820 id: prova
1821 name: prova
1822 short-name: prova
1823 vdu:
1824 - id: vdu_z4bm
1825 image: ubuntu
1826 interface:
1827 - external-connection-point-ref: cp_0h8m
1828 name: eth0
1829 virtual-interface:
1830 type: VIRTIO
1831 name: vdu_z4bm
1832 version: '1.0'
1833 """
1834
1835 def run(self, engine, test_osm, manual_check, test_params=None):
1836 engine.set_test_name("Descriptors")
1837 engine.get_autorization()
1838 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
1839 if not os.path.exists(temp_dir):
1840 os.makedirs(temp_dir)
1841
1842 # download files
1843 for filename in (self.vnfd_filename, self.nsd_filename):
1844 filename_path = temp_dir + filename
1845 if not os.path.exists(filename_path):
1846 with open(filename_path, "wb") as file:
1847 response = requests.get(self.descriptor_url + filename)
1848 if response.status_code >= 300:
1849 raise TestException("Error downloading descriptor from '{}': {}".format(
1850 self.descriptor_url + filename, response.status_code))
1851 file.write(response.content)
1852
1853 vnfd_filename_path = temp_dir + self.vnfd_filename
1854 nsd_filename_path = temp_dir + self.nsd_filename
1855
1856 engine.test("Onboard empty VNFD in one step", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
1857 self.vnfd_empty, 201, r_headers_yaml_location_vnfd, "yaml")
1858 self.vnfd_id = engine.last_id
1859
1860 # test bug 605
1861 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
1862 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
1863
1864 engine.test("Upload VNFD {}".format(self.vnfd_filename), "PUT",
1865 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip_yaml,
1866 "@b" + vnfd_filename_path, 204, None, 0)
1867
1868 queries = ["mgmt-interface.cp=mgmt", "vdu.0.interface.0.external-connection-point-ref=mgmt",
1869 "vdu.0.interface.1.internal-connection-point-ref=internal",
1870 "internal-vld.0.internal-connection-point.0.id-ref=internal",
1871 # Detection of duplicated VLD names in VNF Descriptors
1872 # URL: internal-vld=[
1873 # {id: internal1, name: internal, type:ELAN,
1874 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]},
1875 # {id: internal2, name: internal, type:ELAN,
1876 # internal-connection-point: [{id-ref: mgmtVM-internal}, {id-ref: dataVM-internal}]}
1877 # ]
1878 "internal-vld=%5B%7Bid%3A%20internal1%2C%20name%3A%20internal%2C%20type%3A%20ELAN%2C%20"
1879 "internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7Bid-ref%3A%20"
1880 "dataVM-internal%7D%5D%7D%2C%20%7Bid%3A%20internal2%2C%20name%3A%20internal%2C%20type%3A%20"
1881 "ELAN%2C%20internal-connection-point%3A%20%5B%7Bid-ref%3A%20mgmtVM-internal%7D%2C%20%7B"
1882 "id-ref%3A%20dataVM-internal%7D%5D%7D%5D"
1883 ]
1884 for query in queries:
1885 engine.test("Upload invalid VNFD ", "PUT",
1886 "/vnfpkgm/v1/vnf_packages/{}/package_content?{}".format(self.vnfd_id, query),
1887 headers_zip_yaml, "@b" + vnfd_filename_path, 422, r_header_yaml, "yaml")
1888
1889 # test bug 605
1890 engine.test("Upload invalid VNFD ", "PUT", "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id),
1891 headers_yaml, self.vnfd_prova, 422, r_header_yaml, "yaml")
1892
1893 # get vnfd descriptor
1894 engine.test("Get VNFD descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
1895 headers_yaml, None, 200, r_header_yaml, "yaml")
1896
1897 # get vnfd file descriptor
1898 engine.test("Get VNFD file descriptor", "GET", "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(self.vnfd_id),
1899 headers_text, None, 200, r_header_text, "text", temp_dir+"vnfd-yaml")
1900 # TODO compare files: diff vnfd-yaml hackfest_3charmed_vnfd/hackfest_3charmed_vnfd.yaml
1901
1902 # get vnfd zip file package
1903 engine.test("Get VNFD zip package", "GET",
1904 "/vnfpkgm/v1/vnf_packages/{}/package_content".format(self.vnfd_id), headers_zip, None, 200,
1905 r_header_zip, "zip", temp_dir+"vnfd-zip")
1906 # TODO compare files: diff vnfd-zip hackfest_3charmed_vnfd.tar.gz
1907
1908 # get vnfd artifact
1909 engine.test("Get VNFD artifact package", "GET",
1910 "/vnfpkgm/v1/vnf_packages/{}/artifacts/icons/osm.png".format(self.vnfd_id), headers_zip, None, 200,
1911 r_header_octect, "octet-string", temp_dir+"vnfd-icon")
1912 # TODO compare files: diff vnfd-icon hackfest_3charmed_vnfd/icons/osm.png
1913
1914 # nsd CREATE AND UPLOAD in one step:
1915 engine.test("Onboard NSD in one step", "POST", "/nsd/v1/ns_descriptors_content", headers_zip_yaml,
1916 "@b" + nsd_filename_path, 201, r_headers_yaml_location_nsd, "yaml")
1917 self.nsd_id = engine.last_id
1918
1919 queries = ["vld.0.vnfd-connection-point-ref.0.vnfd-id-ref=hf"]
1920 for query in queries:
1921 engine.test("Upload invalid NSD ", "PUT",
1922 "/nsd/v1/ns_descriptors/{}/nsd_content?{}".format(self.nsd_id, query),
1923 headers_zip_yaml, "@b" + nsd_filename_path, 422, r_header_yaml, "yaml")
1924
1925 # get nsd descriptor
1926 engine.test("Get NSD descriptor", "GET", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml,
1927 None, 200, r_header_yaml, "yaml")
1928
1929 # get nsd file descriptor
1930 engine.test("Get NSD file descriptor", "GET", "/nsd/v1/ns_descriptors/{}/nsd".format(self.nsd_id), headers_text,
1931 None, 200, r_header_text, "text", temp_dir+"nsd-yaml")
1932 # TODO compare files: diff nsd-yaml hackfest_3charmed_nsd/hackfest_3charmed_nsd.yaml
1933
1934 # get nsd zip file package
1935 engine.test("Get NSD zip package", "GET", "/nsd/v1/ns_descriptors/{}/nsd_content".format(self.nsd_id),
1936 headers_zip, None, 200, r_header_zip, "zip", temp_dir+"nsd-zip")
1937 # TODO compare files: diff nsd-zip hackfest_3charmed_nsd.tar.gz
1938
1939 # get nsd artifact
1940 engine.test("Get NSD artifact package", "GET",
1941 "/nsd/v1/ns_descriptors/{}/artifacts/icons/osm.png".format(self.nsd_id), headers_zip, None, 200,
1942 r_header_octect, "octet-string", temp_dir+"nsd-icon")
1943 # TODO compare files: diff nsd-icon hackfest_3charmed_nsd/icons/osm.png
1944
1945 # vnfd DELETE
1946 test_rest.test("Delete VNFD conflict", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_id),
1947 headers_yaml, None, 409, None, None)
1948
1949 test_rest.test("Delete VNFD force", "DELETE", "/vnfpkgm/v1/vnf_packages/{}?FORCE=TRUE".format(self.vnfd_id),
1950 headers_yaml, None, 204, None, 0)
1951
1952 # nsd DELETE
1953 test_rest.test("Delete NSD", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_id), headers_yaml, None, 204,
1954 None, 0)
1955
1956
1957 class TestNetSliceTemplates:
1958 description = "Upload a NST to OSM"
1959
1960 def __init__(self):
1961 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
1962 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
1963 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
1964 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
1965 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
1966
1967 def run(self, engine, test_osm, manual_check, test_params=None):
1968 # nst CREATE
1969 engine.set_test_name("NST step ")
1970 engine.get_autorization()
1971 temp_dir = os.path.dirname(os.path.abspath(__file__)) + "/temp/"
1972 if not os.path.exists(temp_dir):
1973 os.makedirs(temp_dir)
1974
1975 # Onboard VNFDs
1976 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
1977 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
1978 self.vnfd_edge_id = engine.last_id
1979
1980 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
1981 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
1982 self.vnfd_middle_id = engine.last_id
1983
1984 # Onboard NSDs
1985 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
1986 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
1987 self.nsd_edge_id = engine.last_id
1988
1989 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
1990 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
1991 self.nsd_middle_id = engine.last_id
1992
1993 # Onboard NST
1994 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
1995 201, r_headers_yaml_location_nst, "yaml")
1996 nst_id = engine.last_id
1997
1998 # nstd SHOW OSM format
1999 engine.test("Show NSTD OSM format", "GET", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2000 200, r_header_json, "json")
2001
2002 # nstd DELETE
2003 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2004 204, None, 0)
2005
2006 # NSDs DELETE
2007 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2008 headers_json, None, 204, None, 0)
2009
2010 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2011 None, 204, None, 0)
2012
2013 # VNFDs DELETE
2014 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2015 headers_yaml, None, 204, None, 0)
2016
2017 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2018 headers_yaml, None, 204, None, 0)
2019
2020
2021 class TestNetSliceInstances:
2022 '''
2023 Test procedure:
2024 1. Populate databases with VNFD, NSD, NST with the following scenario
2025 +-----------------management-----------------+
2026 | | |
2027 +--+---+ +----+----+ +---+--+
2028 | | | | | |
2029 | edge +---data1----+ middle +---data2-----+ edge |
2030 | | | | | |
2031 +------+ +---------+ +------+
2032 shared-nss
2033 2. Create NSI-1
2034 3. Instantiate NSI-1
2035 4. Create NSI-2
2036 5. Instantiate NSI-2
2037 Manual check - Are 2 slices instantiated correctly?
2038 NSI-1 3 nss (2 nss-edges + 1 nss-middle)
2039 NSI-2 2 nss (2 nss-edge sharing nss-middle)
2040 6. Terminate NSI-1
2041 7. Delete NSI-1
2042 Manual check - Is slice NSI-1 deleted correctly?
2043 NSI-2 with 2 nss-edge + 1 nss-middle (The one from NSI-1)
2044 8. Create NSI-3
2045 9. Instantiate NSI-3
2046 Manual check - Is slice NSI-3 instantiated correctly?
2047 NSI-3 reuse nss-middle. NSI-3 only create 2 nss-edge
2048 10. Delete NSI-2
2049 11. Terminate NSI-2
2050 12. Delete NSI-3
2051 13. Terminate NSI-3
2052 Manual check - All cleaned correctly?
2053 NSI-2 and NSI-3 were terminated and deleted
2054 14. Cleanup database
2055 '''
2056
2057 description = "Upload a NST to OSM"
2058
2059 def __init__(self):
2060 self.vim_id = None
2061 self.vnfd_filename = ("@./slice_shared/vnfd/slice_shared_vnfd.yaml")
2062 self.vnfd_filename_middle = ("@./slice_shared/vnfd/slice_shared_middle_vnfd.yaml")
2063 self.nsd_filename = ("@./slice_shared/nsd/slice_shared_nsd.yaml")
2064 self.nsd_filename_middle = ("@./slice_shared/nsd/slice_shared_middle_nsd.yaml")
2065 self.nst_filenames = ("@./slice_shared/slice_shared_nstd.yaml")
2066
2067 def create_slice(self, engine, nsi_data, name):
2068 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2069 r = engine.test(name, "POST", "/nsilcm/v1/netslice_instances",
2070 headers_yaml, ns_data_text, 201,
2071 {"Location": "nsilcm/v1/netslice_instances/", "Content-Type": "application/yaml"}, "yaml")
2072 return r
2073
2074 def instantiate_slice(self, engine, nsi_data, nsi_id, name):
2075 ns_data_text = yaml.safe_dump(nsi_data, default_flow_style=True, width=256)
2076 engine.test(name, "POST",
2077 "/nsilcm/v1/netslice_instances/{}/instantiate".format(nsi_id), headers_yaml, ns_data_text,
2078 201, r_headers_yaml_location_nsilcmop, "yaml")
2079
2080 def terminate_slice(self, engine, nsi_id, name):
2081 engine.test(name, "POST", "/nsilcm/v1/netslice_instances/{}/terminate".format(nsi_id),
2082 headers_yaml, None, 201, r_headers_yaml_location_nsilcmop, "yaml")
2083
2084 def delete_slice(self, engine, nsi_id, name):
2085 engine.test(name, "DELETE", "/nsilcm/v1/netslice_instances/{}".format(nsi_id), headers_yaml, None,
2086 204, None, 0)
2087
2088 def run(self, engine, test_osm, manual_check, test_params=None):
2089 # nst CREATE
2090 engine.set_test_name("NSI")
2091 engine.get_autorization()
2092
2093 # Onboard VNFDs
2094 engine.test("Onboard edge VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2095 self.vnfd_filename, 201, r_headers_yaml_location_vnfd, "yaml")
2096 self.vnfd_edge_id = engine.last_id
2097
2098 engine.test("Onboard middle VNFD", "POST", "/vnfpkgm/v1/vnf_packages_content", headers_yaml,
2099 self.vnfd_filename_middle, 201, r_headers_yaml_location_vnfd, "yaml")
2100 self.vnfd_middle_id = engine.last_id
2101
2102 # Onboard NSDs
2103 engine.test("Onboard NSD edge", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2104 self.nsd_filename, 201, r_headers_yaml_location_nsd, "yaml")
2105 self.nsd_edge_id = engine.last_id
2106
2107 engine.test("Onboard NSD middle", "POST", "/nsd/v1/ns_descriptors_content", headers_yaml,
2108 self.nsd_filename_middle, 201, r_headers_yaml_location_nsd, "yaml")
2109 self.nsd_middle_id = engine.last_id
2110
2111 # Onboard NST
2112 engine.test("Onboard NST", "POST", "/nst/v1/netslice_templates_content", headers_yaml, self.nst_filenames,
2113 201, r_headers_yaml_location_nst, "yaml")
2114 nst_id = engine.last_id
2115
2116 self.vim_id = engine.get_create_vim(test_osm)
2117
2118 # CREATE NSI-1
2119 ns_data = {'nsiName': 'Deploy-NSI-1', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2120 r = self.create_slice(engine, ns_data, "Create NSI-1 step 1")
2121 if not r:
2122 return
2123 self.nsi_id1 = engine.last_id
2124
2125 # INSTANTIATE NSI-1
2126 self.instantiate_slice(engine, ns_data, self.nsi_id1, "Instantiate NSI-1 step 2")
2127 nsilcmop_id1 = engine.last_id
2128
2129 # Waiting for NSI-1
2130 engine.wait_operation_ready("nsi", nsilcmop_id1, timeout_deploy)
2131
2132 # CREATE NSI-2
2133 ns_data = {'nsiName': 'Deploy-NSI-2', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2134 r = self.create_slice(engine, ns_data, "Create NSI-2 step 1")
2135 if not r:
2136 return
2137 self.nsi_id2 = engine.last_id
2138
2139 # INSTANTIATE NSI-2
2140 self.instantiate_slice(engine, ns_data, self.nsi_id2, "Instantiate NSI-2 step 2")
2141 nsilcmop_id2 = engine.last_id
2142
2143 # Waiting for NSI-2
2144 engine.wait_operation_ready("nsi", nsilcmop_id2, timeout_deploy)
2145
2146 if manual_check:
2147 input('NSI-1 AND NSI-2 has been deployed. Perform manual check and press enter to resume')
2148
2149 # TERMINATE NSI-1
2150 self.terminate_slice(engine, self.nsi_id1, "Terminate NSI-1")
2151 nsilcmop1_id = engine.last_id
2152
2153 # Wait terminate NSI-1
2154 engine.wait_operation_ready("nsi", nsilcmop1_id, timeout_deploy)
2155
2156 # DELETE NSI-1
2157 self.delete_slice(engine, self.nsi_id1, "Delete NS")
2158
2159 if manual_check:
2160 input('NSI-1 has been deleted. Perform manual check and press enter to resume')
2161
2162 # CREATE NSI-3
2163 ns_data = {'nsiName': 'Deploy-NSI-3', 'vimAccountId': self.vim_id, 'nstId': nst_id, 'nsiDescription': 'default'}
2164 r = self.create_slice(engine, ns_data, "Create NSI-3 step 1")
2165
2166 if not r:
2167 return
2168 self.nsi_id3 = engine.last_id
2169
2170 # INSTANTIATE NSI-3
2171 self.instantiate_slice(engine, ns_data, self.nsi_id3, "Instantiate NSI-3 step 2")
2172 nsilcmop_id3 = engine.last_id
2173
2174 # Wait Instantiate NSI-3
2175 engine.wait_operation_ready("nsi", nsilcmop_id3, timeout_deploy)
2176
2177 if manual_check:
2178 input('NSI-3 has been deployed. Perform manual check and press enter to resume')
2179
2180 # TERMINATE NSI-2
2181 self.terminate_slice(engine, self.nsi_id2, "Terminate NSI-2")
2182 nsilcmop2_id = engine.last_id
2183
2184 # Wait terminate NSI-2
2185 engine.wait_operation_ready("nsi", nsilcmop2_id, timeout_deploy)
2186
2187 # DELETE NSI-2
2188 self.delete_slice(engine, self.nsi_id2, "DELETE NSI-2")
2189
2190 # TERMINATE NSI-3
2191 self. terminate_slice(engine, self.nsi_id3, "Terminate NSI-3")
2192 nsilcmop3_id = engine.last_id
2193
2194 # Wait terminate NSI-3
2195 engine.wait_operation_ready("nsi", nsilcmop3_id, timeout_deploy)
2196
2197 # DELETE NSI-3
2198 self.delete_slice(engine, self.nsi_id3, "DELETE NSI-3")
2199
2200 if manual_check:
2201 input('NSI-2 and NSI-3 has been deleted. Perform manual check and press enter to resume')
2202
2203 # nstd DELETE
2204 engine.test("Delete NSTD", "DELETE", "/nst/v1/netslice_templates/{}".format(nst_id), headers_json, None,
2205 204, None, 0)
2206
2207 # NSDs DELETE
2208 test_rest.test("Delete NSD middle", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_middle_id),
2209 headers_json, None, 204, None, 0)
2210
2211 test_rest.test("Delete NSD edge", "DELETE", "/nsd/v1/ns_descriptors/{}".format(self.nsd_edge_id), headers_json,
2212 None, 204, None, 0)
2213
2214 # VNFDs DELETE
2215 test_rest.test("Delete VNFD edge", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_edge_id),
2216 headers_yaml, None, 204, None, 0)
2217
2218 test_rest.test("Delete VNFD middle", "DELETE", "/vnfpkgm/v1/vnf_packages/{}".format(self.vnfd_middle_id),
2219 headers_yaml, None, 204, None, 0)
2220
2221
2222 if __name__ == "__main__":
2223 global logger
2224 test = ""
2225
2226 # Disable warnings from self-signed certificates.
2227 requests.packages.urllib3.disable_warnings()
2228 try:
2229 logging.basicConfig(format="%(levelname)s %(message)s", level=logging.ERROR)
2230 logger = logging.getLogger('NBI')
2231 # load parameters and configuration
2232 opts, args = getopt.getopt(sys.argv[1:], "hvu:p:",
2233 ["url=", "user=", "password=", "help", "version", "verbose", "no-verbose",
2234 "project=", "insecure", "timeout", "timeout-deploy", "timeout-configure",
2235 "test=", "list", "test-osm", "manual-check", "params=", 'fail-fast'])
2236 url = "https://localhost:9999/osm"
2237 user = password = project = "admin"
2238 test_osm = False
2239 manual_check = False
2240 verbose = 0
2241 verify = True
2242 fail_fast = False
2243 test_classes = {
2244 "NonAuthorized": TestNonAuthorized,
2245 "FakeVIM": TestFakeVim,
2246 "TestUsersProjects": TestUsersProjects,
2247 "VIM-SDN": TestVIMSDN,
2248 "Deploy-Custom": TestDeploy,
2249 "Deploy-Hackfest-Cirros": TestDeployHackfestCirros,
2250 "Deploy-Hackfest-Cirros-Scaling": TestDeployHackfestCirrosScaling,
2251 "Deploy-Hackfest-3Charmed": TestDeployHackfest3Charmed,
2252 "Deploy-Hackfest-3Charmed2": TestDeployHackfest3Charmed2,
2253 "Deploy-Hackfest-3Charmed3": TestDeployHackfest3Charmed3,
2254 "Deploy-Hackfest-4": TestDeployHackfest4,
2255 "Deploy-CirrosMacIp": TestDeployIpMac,
2256 "TestDescriptors": TestDescriptors,
2257 "TestDeployHackfest1": TestDeployHackfest1,
2258 # "Deploy-MultiVIM": TestDeployMultiVIM,
2259 "DeploySingleVdu": TestDeploySingleVdu,
2260 "DeployHnfd": TestDeployHnfd,
2261 "Upload-Slice-Template": TestNetSliceTemplates,
2262 "Deploy-Slice-Instance": TestNetSliceInstances,
2263 "TestDeploySimpleCharm": TestDeploySimpleCharm,
2264 "TestDeploySimpleCharm2": TestDeploySimpleCharm2,
2265 }
2266 test_to_do = []
2267 test_params = {}
2268
2269 for o, a in opts:
2270 # print("parameter:", o, a)
2271 if o == "--version":
2272 print("test version " + __version__ + ' ' + version_date)
2273 exit()
2274 elif o == "--list":
2275 for test, test_class in sorted(test_classes.items()):
2276 print("{:32} {}".format(test + ":", test_class.description))
2277 exit()
2278 elif o in ("-v", "--verbose"):
2279 verbose += 1
2280 elif o == "no-verbose":
2281 verbose = -1
2282 elif o in ("-h", "--help"):
2283 usage()
2284 sys.exit()
2285 elif o == "--test-osm":
2286 test_osm = True
2287 elif o == "--manual-check":
2288 manual_check = True
2289 elif o == "--url":
2290 url = a
2291 elif o in ("-u", "--user"):
2292 user = a
2293 elif o in ("-p", "--password"):
2294 password = a
2295 elif o == "--project":
2296 project = a
2297 elif o == "--fail-fast":
2298 fail_fast = True
2299 elif o == "--test":
2300 # print("asdfadf", o, a, a.split(","))
2301 for _test in a.split(","):
2302 if _test not in test_classes:
2303 print("Invalid test name '{}'. Use option '--list' to show available tests".format(_test),
2304 file=sys.stderr)
2305 exit(1)
2306 test_to_do.append(_test)
2307 elif o == "--params":
2308 param_key, _, param_value = a.partition("=")
2309 text_index = len(test_to_do)
2310 if text_index not in test_params:
2311 test_params[text_index] = {}
2312 test_params[text_index][param_key] = param_value
2313 elif o == "--insecure":
2314 verify = False
2315 elif o == "--timeout":
2316 timeout = int(a)
2317 elif o == "--timeout-deploy":
2318 timeout_deploy = int(a)
2319 elif o == "--timeout-configure":
2320 timeout_configure = int(a)
2321 else:
2322 assert False, "Unhandled option"
2323 if verbose == 0:
2324 logger.setLevel(logging.WARNING)
2325 elif verbose > 1:
2326 logger.setLevel(logging.DEBUG)
2327 else:
2328 logger.setLevel(logging.ERROR)
2329
2330 test_rest = TestRest(url, user=user, password=password, project=project)
2331 # print("tests to do:", test_to_do)
2332 if test_to_do:
2333 text_index = 0
2334 for test in test_to_do:
2335 if fail_fast and test_rest.failed_tests:
2336 break
2337 text_index += 1
2338 test_class = test_classes[test]
2339 test_class().run(test_rest, test_osm, manual_check, test_params.get(text_index))
2340 else:
2341 for test, test_class in test_classes.items():
2342 if fail_fast and test_rest.failed_tests:
2343 break
2344 test_class().run(test_rest, test_osm, manual_check, test_params.get(0))
2345 test_rest.print_results()
2346 exit(1 if test_rest.failed_tests else 0)
2347
2348 except TestException as e:
2349 logger.error(test + "Test {} Exception: {}".format(test, str(e)))
2350 exit(1)
2351 except getopt.GetoptError as e:
2352 logger.error(e)
2353 print(e, file=sys.stderr)
2354 exit(1)
2355 except Exception as e:
2356 logger.critical(test + " Exception: " + str(e), exc_info=True)