PDU fixing errors. Create test for PDU
[osm/NBI.git] / osm_nbi / instance_topics.py
1 # -*- coding: utf-8 -*-
2
3 # import logging
4 from uuid import uuid4
5 from http import HTTPStatus
6 from time import time
7 from copy import copy, deepcopy
8 from validation import validate_input, ValidationError, ns_instantiate, ns_action, ns_scale
9 from base_topic import BaseTopic, EngineException, get_iterable
10 from descriptor_topics import DescriptorTopic
11
12 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
13
14
15 class NsrTopic(BaseTopic):
16 topic = "nsrs"
17 topic_msg = "ns"
18
19 def __init__(self, db, fs, msg):
20 BaseTopic.__init__(self, db, fs, msg)
21
22 def _check_descriptor_dependencies(self, session, descriptor):
23 """
24 Check that the dependent descriptors exist on a new descriptor or edition
25 :param session: client session information
26 :param descriptor: descriptor to be inserted or edit
27 :return: None or raises exception
28 """
29 if not descriptor.get("nsdId"):
30 return
31 nsd_id = descriptor["nsdId"]
32 if not self.get_item_list(session, "nsds", {"id": nsd_id}):
33 raise EngineException("Descriptor error at nsdId='{}' references a non exist nsd".format(nsd_id),
34 http_code=HTTPStatus.CONFLICT)
35
36 @staticmethod
37 def format_on_new(content, project_id=None, make_public=False):
38 BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
39 content["_admin"]["nsState"] = "NOT_INSTANTIATED"
40
41 def check_conflict_on_del(self, session, _id, force=False):
42 if force:
43 return
44 nsr = self.db.get_one("nsrs", {"_id": _id})
45 if nsr["_admin"].get("nsState") == "INSTANTIATED":
46 raise EngineException("nsr '{}' cannot be deleted because it is in 'INSTANTIATED' state. "
47 "Launch 'terminate' operation first; or force deletion".format(_id),
48 http_code=HTTPStatus.CONFLICT)
49
50 def delete(self, session, _id, force=False, dry_run=False):
51 """
52 Delete item by its internal _id
53 :param session: contains the used login username, working project, and admin rights
54 :param _id: server internal id
55 :param force: indicates if deletion must be forced in case of conflict
56 :param dry_run: make checking but do not delete
57 :return: dictionary with deleted item _id. It raises EngineException on error: not found, conflict, ...
58 """
59 # TODO add admin to filter, validate rights
60 BaseTopic.delete(self, session, _id, force, dry_run=True)
61 if dry_run:
62 return
63
64 v = self.db.del_one("nsrs", {"_id": _id})
65 self.db.del_list("nslcmops", {"nsInstanceId": _id})
66 self.db.del_list("vnfrs", {"nsr-id-ref": _id})
67 # set all used pdus as free
68 self.db.set_list("pdus", {"_admin.usage.nsr_id": _id},
69 {"_admin.usageState": "NOT_IN_USE", "_admin.usage": None})
70 self._send_msg("deleted", {"_id": _id})
71 return v
72
73 def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
74 """
75 Creates a new nsr into database. It also creates needed vnfrs
76 :param rollback: list to append the created items at database in case a rollback must be done
77 :param session: contains the used login username and working project
78 :param indata: params to be used for the nsr
79 :param kwargs: used to override the indata descriptor
80 :param headers: http request headers
81 :param force: If True avoid some dependence checks
82 :param make_public: Make the created item public to all projects
83 :return: the _id of nsr descriptor created at database
84 """
85
86 try:
87 ns_request = self._remove_envelop(indata)
88 # Override descriptor with query string kwargs
89 self._update_input_with_kwargs(ns_request, kwargs)
90 self._validate_input_new(ns_request, force)
91
92 step = ""
93 # look for nsr
94 step = "getting nsd id='{}' from database".format(ns_request.get("nsdId"))
95 _filter = {"_id": ns_request["nsdId"]}
96 _filter.update(BaseTopic._get_project_filter(session, write=False, show_all=True))
97 nsd = self.db.get_one("nsds", _filter)
98
99 nsr_id = str(uuid4())
100 now = time()
101 step = "filling nsr from input data"
102 nsr_descriptor = {
103 "name": ns_request["nsName"],
104 "name-ref": ns_request["nsName"],
105 "short-name": ns_request["nsName"],
106 "admin-status": "ENABLED",
107 "nsd": nsd,
108 "datacenter": ns_request["vimAccountId"],
109 "resource-orchestrator": "osmopenmano",
110 "description": ns_request.get("nsDescription", ""),
111 "constituent-vnfr-ref": [],
112
113 "operational-status": "init", # typedef ns-operational-
114 "config-status": "init", # typedef config-states
115 "detailed-status": "scheduled",
116
117 "orchestration-progress": {},
118 # {"networks": {"active": 0, "total": 0}, "vms": {"active": 0, "total": 0}},
119
120 "crete-time": now,
121 "nsd-name-ref": nsd["name"],
122 "operational-events": [], # "id", "timestamp", "description", "event",
123 "nsd-ref": nsd["id"],
124 "instantiate_params": ns_request,
125 "ns-instance-config-ref": nsr_id,
126 "id": nsr_id,
127 "_id": nsr_id,
128 # "input-parameter": xpath, value,
129 "ssh-authorized-key": ns_request.get("key-pair-ref"), # TODO remove
130 }
131 ns_request["nsr_id"] = nsr_id
132 # Create vld
133 if nsd.get("vld"):
134 nsr_descriptor["vld"] = []
135 for nsd_vld in nsd.get("vld"):
136 nsr_descriptor["vld"].append(
137 {key: nsd_vld[key] for key in ("id", "vim-network-name") if key in nsd_vld})
138
139 # Create VNFR
140 needed_vnfds = {}
141 for member_vnf in nsd.get("constituent-vnfd", ()):
142 vnfd_id = member_vnf["vnfd-id-ref"]
143 step = "getting vnfd id='{}' constituent-vnfd='{}' from database".format(
144 member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
145 if vnfd_id not in needed_vnfds:
146 # Obtain vnfd
147 vnfd = DescriptorTopic.get_one_by_id(self.db, session, "vnfds", vnfd_id)
148 vnfd.pop("_admin")
149 needed_vnfds[vnfd_id] = vnfd
150 else:
151 vnfd = needed_vnfds[vnfd_id]
152 step = "filling vnfr vnfd-id='{}' constituent-vnfd='{}'".format(
153 member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
154 vnfr_id = str(uuid4())
155 vnfr_descriptor = {
156 "id": vnfr_id,
157 "_id": vnfr_id,
158 "nsr-id-ref": nsr_id,
159 "member-vnf-index-ref": member_vnf["member-vnf-index"],
160 "created-time": now,
161 # "vnfd": vnfd, # at OSM model.but removed to avoid data duplication TODO: revise
162 "vnfd-ref": vnfd_id,
163 "vnfd-id": vnfd["_id"], # not at OSM model, but useful
164 "vim-account-id": None,
165 "vdur": [],
166 "connection-point": [],
167 "ip-address": None, # mgmt-interface filled by LCM
168 }
169
170 # Create vld
171 if vnfd.get("internal-vld"):
172 vnfr_descriptor["vld"] = []
173 for vnfd_vld in vnfd.get("internal-vld"):
174 vnfr_descriptor["vld"].append(
175 {key: vnfd_vld[key] for key in ("id", "vim-network-name") if key in vnfd_vld})
176
177 vnfd_mgmt_cp = vnfd["mgmt-interface"].get("cp")
178 for cp in vnfd.get("connection-point", ()):
179 vnf_cp = {
180 "name": cp["name"],
181 "connection-point-id": cp.get("id"),
182 "id": cp.get("id"),
183 # "ip-address", "mac-address" # filled by LCM
184 # vim-id # TODO it would be nice having a vim port id
185 }
186 vnfr_descriptor["connection-point"].append(vnf_cp)
187 for vdu in vnfd.get("vdu", ()):
188 vdur = {
189 "vdu-id-ref": vdu["id"],
190 # TODO "name": "" Name of the VDU in the VIM
191 "ip-address": None, # mgmt-interface filled by LCM
192 # "vim-id", "flavor-id", "image-id", "management-ip" # filled by LCM
193 "internal-connection-point": [],
194 "interfaces": [],
195 }
196 if vdu.get("pdu-type"):
197 vdur["pdu-type"] = vdu["pdu-type"]
198 # TODO volumes: name, volume-id
199 for icp in vdu.get("internal-connection-point", ()):
200 vdu_icp = {
201 "id": icp["id"],
202 "connection-point-id": icp["id"],
203 "name": icp.get("name"),
204 # "ip-address", "mac-address" # filled by LCM
205 # vim-id # TODO it would be nice having a vim port id
206 }
207 vdur["internal-connection-point"].append(vdu_icp)
208 for iface in vdu.get("interface", ()):
209 vdu_iface = {
210 "name": iface.get("name"),
211 # "ip-address", "mac-address" # filled by LCM
212 # vim-id # TODO it would be nice having a vim port id
213 }
214 if vnfd_mgmt_cp and iface.get("external-connection-point-ref") == vnfd_mgmt_cp:
215 vdu_iface["mgmt-vnf"] = True
216 if iface.get("mgmt-interface"):
217 vdu_iface["mgmt-interface"] = True # TODO change to mgmt-vdu
218
219 # look for network where this interface is connected
220 if iface.get("external-connection-point-ref"):
221 for nsd_vld in get_iterable(nsd.get("vld")):
222 for nsd_vld_cp in get_iterable(nsd_vld.get("vnfd-connection-point-ref")):
223 if nsd_vld_cp.get("vnfd-connection-point-ref") == \
224 iface["external-connection-point-ref"] and \
225 nsd_vld_cp.get("member-vnf-index-ref") == member_vnf["member-vnf-index"]:
226 vdu_iface["ns-vld-id"] = nsd_vld["id"]
227 break
228 else:
229 continue
230 break
231 elif iface.get("internal-connection-point-ref"):
232 for vnfd_ivld in get_iterable(vnfd.get("internal-vld")):
233 for vnfd_ivld_icp in get_iterable(vnfd_ivld.get("internal-connection-point")):
234 if vnfd_ivld_icp.get("id-ref") == iface["internal-connection-point-ref"]:
235 vdu_iface["vnf-vld-id"] = vnfd_ivld["id"]
236 break
237 else:
238 continue
239 break
240
241 vdur["interfaces"].append(vdu_iface)
242 count = vdu.get("count", 1)
243 if count is None:
244 count = 1
245 count = int(count) # TODO remove when descriptor serialized with payngbind
246 for index in range(0, count):
247 if index:
248 vdur = deepcopy(vdur)
249 vdur["_id"] = str(uuid4())
250 vdur["count-index"] = index
251 vnfr_descriptor["vdur"].append(vdur)
252
253 step = "creating vnfr vnfd-id='{}' constituent-vnfd='{}' at database".format(
254 member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
255
256 # add at database
257 BaseTopic.format_on_new(vnfr_descriptor, session["project_id"], make_public=make_public)
258 self.db.create("vnfrs", vnfr_descriptor)
259 rollback.append({"topic": "vnfrs", "_id": vnfr_id})
260 nsr_descriptor["constituent-vnfr-ref"].append(vnfr_id)
261
262 step = "creating nsr at database"
263 self.format_on_new(nsr_descriptor, session["project_id"], make_public=make_public)
264 self.db.create("nsrs", nsr_descriptor)
265 rollback.append({"topic": "nsrs", "_id": nsr_id})
266 return nsr_id
267 except Exception as e:
268 self.logger.exception("Exception {} at NsrTopic.new()".format(e), exc_info=True)
269 raise EngineException("Error {}: {}".format(step, e))
270 except ValidationError as e:
271 raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
272
273 def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
274 raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
275
276
277 class VnfrTopic(BaseTopic):
278 topic = "vnfrs"
279 topic_msg = None
280
281 def __init__(self, db, fs, msg):
282 BaseTopic.__init__(self, db, fs, msg)
283
284 def delete(self, session, _id, force=False, dry_run=False):
285 raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
286
287 def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
288 raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
289
290 def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
291 # Not used because vnfrs are created and deleted by NsrTopic class directly
292 raise EngineException("Method new called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
293
294
295 class NsLcmOpTopic(BaseTopic):
296 topic = "nslcmops"
297 topic_msg = "ns"
298 operation_schema = { # mapping between operation and jsonschema to validate
299 "instantiate": ns_instantiate,
300 "action": ns_action,
301 "scale": ns_scale,
302 "terminate": None,
303 }
304
305 def __init__(self, db, fs, msg):
306 BaseTopic.__init__(self, db, fs, msg)
307
308 def _validate_input_new(self, input, force=False):
309 """
310 Validates input user content for a new entry. It uses jsonschema for each type or operation.
311 :param input: user input content for the new topic
312 :param force: may be used for being more tolerant
313 :return: The same input content, or a changed version of it.
314 """
315 if self.schema_new:
316 validate_input(input, self.schema_new)
317 return input
318
319 def _check_ns_operation(self, session, nsr, operation, indata):
320 """
321 Check that user has enter right parameters for the operation
322 :param session:
323 :param operation: it can be: instantiate, terminate, action, TODO: update, heal
324 :param indata: descriptor with the parameters of the operation
325 :return: None
326 """
327 vnfds = {}
328 vim_accounts = []
329 nsd = nsr["nsd"]
330
331 def check_valid_vnf_member_index(member_vnf_index):
332 # TODO change to vnfR
333 for vnf in nsd["constituent-vnfd"]:
334 if member_vnf_index == vnf["member-vnf-index"]:
335 vnfd_id = vnf["vnfd-id-ref"]
336 if vnfd_id not in vnfds:
337 vnfds[vnfd_id] = self.db.get_one("vnfds", {"id": vnfd_id})
338 return vnfds[vnfd_id]
339 else:
340 raise EngineException("Invalid parameter member_vnf_index='{}' is not one of the "
341 "nsd:constituent-vnfd".format(member_vnf_index))
342
343 def _check_vnf_instantiation_params(in_vnfd, vnfd):
344
345 for in_vdu in get_iterable(in_vnfd.get("vdu")):
346 for vdu in get_iterable(vnfd.get("vdu")):
347 if in_vdu["id"] == vdu["id"]:
348 for volume in get_iterable(in_vdu.get("volume")):
349 for volumed in get_iterable(vdu.get("volumes")):
350 if volumed["name"] == volume["name"]:
351 break
352 else:
353 raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
354 "volume:name='{}' is not present at vnfd:vdu:volumes list".
355 format(in_vnf["member-vnf-index"], in_vdu["id"],
356 volume["name"]))
357 for in_iface in get_iterable(in_vdu["interface"]):
358 for iface in get_iterable(vdu.get("interface")):
359 if in_iface["name"] == iface["name"]:
360 break
361 else:
362 raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}']:"
363 "interface[name='{}'] is not present at vnfd:vdu:interface"
364 .format(in_vnf["member-vnf-index"], in_vdu["id"],
365 in_iface["name"]))
366 break
367 else:
368 raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:vdu[id='{}'] is is not present "
369 "at vnfd:vdu".format(in_vnf["member-vnf-index"], in_vdu["id"]))
370
371 for in_ivld in get_iterable(in_vnfd.get("internal-vld")):
372 for ivld in get_iterable(vnfd.get("internal-vld")):
373 if in_ivld["name"] == ivld["name"] or in_ivld["name"] == ivld["id"]:
374 for in_icp in get_iterable(in_ivld["internal-connection-point"]):
375 for icp in ivld["internal-connection-point"]:
376 if in_icp["id-ref"] == icp["id-ref"]:
377 break
378 else:
379 raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld[name"
380 "='{}']:internal-connection-point[id-ref:'{}'] is not present at "
381 "vnfd:internal-vld:name/id:internal-connection-point"
382 .format(in_vnf["member-vnf-index"], in_ivld["name"],
383 in_icp["id-ref"], vnfd["id"]))
384 break
385 else:
386 raise EngineException("Invalid parameter vnf[member-vnf-index='{}']:internal-vld:name='{}'"
387 " is not present at vnfd '{}'".format(in_vnf["member-vnf-index"],
388 in_ivld["name"], vnfd["id"]))
389
390 def check_valid_vim_account(vim_account):
391 if vim_account in vim_accounts:
392 return
393 try:
394 db_filter = self._get_project_filter(session, write=False, show_all=True)
395 db_filter["_id"] = vim_account
396 self.db.get_one("vim_accounts", db_filter)
397 except Exception:
398 raise EngineException("Invalid vimAccountId='{}' not present for the project".format(vim_account))
399 vim_accounts.append(vim_account)
400
401 if operation == "action":
402 # check vnf_member_index
403 if indata.get("vnf_member_index"):
404 indata["member_vnf_index"] = indata.pop("vnf_member_index") # for backward compatibility
405 if not indata.get("member_vnf_index"):
406 raise EngineException("Missing 'member_vnf_index' parameter")
407 vnfd = check_valid_vnf_member_index(indata["member_vnf_index"])
408 # check primitive
409 for config_primitive in get_iterable(vnfd.get("vnf-configuration", {}).get("config-primitive")):
410 if indata["primitive"] == config_primitive["name"]:
411 # check needed primitive_params are provided
412 if indata.get("primitive_params"):
413 in_primitive_params_copy = copy(indata["primitive_params"])
414 else:
415 in_primitive_params_copy = {}
416 for paramd in get_iterable(config_primitive.get("parameter")):
417 if paramd["name"] in in_primitive_params_copy:
418 del in_primitive_params_copy[paramd["name"]]
419 elif not paramd.get("default-value"):
420 raise EngineException("Needed parameter {} not provided for primitive '{}'".format(
421 paramd["name"], indata["primitive"]))
422 # check no extra primitive params are provided
423 if in_primitive_params_copy:
424 raise EngineException("parameter/s '{}' not present at vnfd for primitive '{}'".format(
425 list(in_primitive_params_copy.keys()), indata["primitive"]))
426 break
427 else:
428 raise EngineException("Invalid primitive '{}' is not present at vnfd".format(indata["primitive"]))
429 if operation == "scale":
430 vnfd = check_valid_vnf_member_index(indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"])
431 for scaling_group in get_iterable(vnfd.get("scaling-group-descriptor")):
432 if indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"] == scaling_group["name"]:
433 break
434 else:
435 raise EngineException("Invalid scaleVnfData:scaleByStepData:scaling-group-descriptor '{}' is not "
436 "present at vnfd:scaling-group-descriptor".format(
437 indata["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]))
438 if operation == "instantiate":
439 # check vim_account
440 check_valid_vim_account(indata["vimAccountId"])
441 for in_vnf in get_iterable(indata.get("vnf")):
442 vnfd = check_valid_vnf_member_index(in_vnf["member-vnf-index"])
443 _check_vnf_instantiation_params(in_vnf, vnfd)
444 if in_vnf.get("vimAccountId"):
445 check_valid_vim_account(in_vnf["vimAccountId"])
446
447 for in_vld in get_iterable(indata.get("vld")):
448 for vldd in get_iterable(nsd.get("vld")):
449 if in_vld["name"] == vldd["name"] or in_vld["name"] == vldd["id"]:
450 break
451 else:
452 raise EngineException("Invalid parameter vld:name='{}' is not present at nsd:vld".format(
453 in_vld["name"]))
454
455 def _look_for_pdu(self, session, rollback, vnfr, vim_account, vnfr_update, vnfr_update_rollback):
456 """
457 Look for a free PDU in the catalog matching vdur type and interfaces. Fills vnfr.vdur with the interface
458 (ip_address, ...) information.
459 Modifies PDU _admin.usageState to 'IN_USE'
460
461 :param session: client session information
462 :param rollback: list with the database modifications to rollback if needed
463 :param vnfr: vnfr to be updated. It is modified with pdu interface info if pdu is found
464 :param vim_account: vim_account where this vnfr should be deployed
465 :param vnfr_update: dictionary filled by this method with changes to be done at database vnfr
466 :param vnfr_update_rollback: dictionary filled by this method with original content of vnfr in case a rollback
467 of the changed vnfr is needed
468
469 :return: List of PDU interfaces that are connected to an existing VIM network. Each item contains:
470 "vim-network-name": used at VIM
471 "name": interface name
472 "vnf-vld-id": internal VNFD vld where this interface is connected, or
473 "ns-vld-id": NSD vld where this interface is connected.
474 NOTE: One, and only one between 'vnf-vld-id' and 'ns-vld-id' contains a value. The other will be None
475 """
476
477 ifaces_forcing_vim_network = []
478 for vdur_index, vdur in enumerate(get_iterable(vnfr.get("vdur"))):
479 if not vdur.get("pdu-type"):
480 continue
481 pdu_type = vdur.get("pdu-type")
482 pdu_filter = self._get_project_filter(session, write=True, show_all=True)
483 pdu_filter["vim_accounts"] = vim_account
484 pdu_filter["type"] = pdu_type
485 pdu_filter["_admin.operationalState"] = "ENABLED"
486 pdu_filter["_admin.usageState"] = "NOT_IN_USE"
487 # TODO feature 1417: "shared": True,
488
489 available_pdus = self.db.get_list("pdus", pdu_filter)
490 for pdu in available_pdus:
491 # step 1 check if this pdu contains needed interfaces:
492 match_interfaces = True
493 for vdur_interface in vdur["interfaces"]:
494 for pdu_interface in pdu["interfaces"]:
495 if pdu_interface["name"] == vdur_interface["name"]:
496 # TODO feature 1417: match per mgmt type
497 break
498 else: # no interface found for name
499 match_interfaces = False
500 break
501 if match_interfaces:
502 break
503 else:
504 raise EngineException(
505 "No PDU of type={} at vim_account={} found for member_vnf_index={}, vdu={} matching interface "
506 "names".format(pdu_type, vim_account, vnfr["member-vnf-index-ref"], vdur["vdu-id-ref"]))
507
508 # step 2. Update pdu
509 rollback_pdu = {
510 "_admin.usageState": pdu["_admin"]["usageState"],
511 "_admin.usage.vnfr_id": None,
512 "_admin.usage.nsr_id": None,
513 "_admin.usage.vdur": None,
514 }
515 self.db.set_one("pdus", {"_id": pdu["_id"]},
516 {"_admin.usageState": "IN_USE",
517 "_admin.usage.vnfr_id": vnfr["_id"],
518 "_admin.usage.nsr_id": vnfr["nsr-id-ref"],
519 "_admin.usage.vdur": vdur["vdu-id-ref"]}
520 )
521 rollback.append({"topic": "pdus", "_id": pdu["_id"], "operation": "set", "content": rollback_pdu})
522
523 # step 3. Fill vnfr info by filling vdur
524 vdu_text = "vdur.{}".format(vdur_index)
525 vnfr_update_rollback[vdu_text + ".pdu-id"] = None
526 vnfr_update[vdu_text + ".pdu-id"] = pdu["_id"]
527 for iface_index, vdur_interface in enumerate(vdur["interfaces"]):
528 for pdu_interface in pdu["interfaces"]:
529 if pdu_interface["name"] == vdur_interface["name"]:
530 iface_text = vdu_text + ".interfaces.{}".format(iface_index)
531 for k, v in pdu_interface.items():
532 if k in ("ip-address", "mac-address"): # TODO: switch-xxxxx must be inserted
533 vnfr_update[iface_text + ".{}".format(k)] = v
534 vnfr_update_rollback[iface_text + ".{}".format(k)] = vdur_interface.get(v)
535 if pdu_interface.get("ip-address"):
536 if vdur_interface.get("mgmt-interface"):
537 vnfr_update_rollback[vdu_text + ".ip-address"] = vdur.get("ip-address")
538 vnfr_update[vdu_text + ".ip-address"] = pdu_interface["ip-address"]
539 if vdur_interface.get("mgmt-vnf"):
540 vnfr_update_rollback["ip-address"] = vnfr.get("ip-address")
541 vnfr_update["ip-address"] = pdu_interface["ip-address"]
542 if pdu_interface.get("vim-network-name"): # or pdu_interface.get("vim-network-id"):
543 ifaces_forcing_vim_network.append({
544 # "vim-network-id": pdu_interface.get("vim-network-id"),
545 "vim-network-name": pdu_interface.get("vim-network-name"),
546 "name": vdur_interface.get("vnf-vld-id") or vdur_interface.get("ns-vld-id"),
547 "vnf-vld-id": vdur_interface.get("vnf-vld-id"),
548 "ns-vld-id": vdur_interface.get("ns-vld-id")})
549 break
550
551 return ifaces_forcing_vim_network
552
553 def _update_vnfrs(self, session, rollback, nsr, indata):
554 vnfrs = None
555 # get vnfr
556 nsr_id = nsr["_id"]
557 vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
558
559 for vnfr in vnfrs:
560 vnfr_update = {}
561 vnfr_update_rollback = {}
562 member_vnf_index = vnfr["member-vnf-index-ref"]
563 # update vim-account-id
564
565 vim_account = indata["vimAccountId"]
566 # check instantiate parameters
567 for vnf_inst_params in get_iterable(indata.get("vnf")):
568 if vnf_inst_params["member-vnf-index"] != member_vnf_index:
569 continue
570 if vnf_inst_params.get("vimAccountId"):
571 vim_account = vnf_inst_params.get("vimAccountId")
572
573 vnfr_update["vim-account-id"] = vim_account
574 vnfr_update_rollback["vim-account-id"] = vnfr.get("vim-account-id")
575
576 # get pdu
577 ifaces_forcing_vim_network = self._look_for_pdu(session, rollback, vnfr, vim_account, vnfr_update,
578 vnfr_update_rollback)
579
580 # updata database vnfr
581 self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, vnfr_update)
582 rollback.append({"topic": "vnfrs", "_id": vnfr["_id"], "operation": "set", "content": vnfr_update_rollback})
583
584 # Update indada in case pdu forces to use a concrete vim-network-name
585 # TODO check if user has already insert a vim-network-name and raises an error
586 if not ifaces_forcing_vim_network:
587 continue
588 for iface_info in ifaces_forcing_vim_network:
589 if iface_info.get("ns-vld-id"):
590 if "vld" not in indata:
591 indata["vld"] = []
592 indata["vld"].append({key: iface_info[key] for key in
593 ("name", "vim-network-name", "vim-network-id") if iface_info.get(key)})
594
595 elif iface_info.get("vnf-vld-id"):
596 if "vnf" not in indata:
597 indata["vnf"] = []
598 indata["vnf"].append({
599 "member-vnf-index": member_vnf_index,
600 "internal-vld": [{key: iface_info[key] for key in
601 ("name", "vim-network-name", "vim-network-id") if iface_info.get(key)}]
602 })
603
604 @staticmethod
605 def _create_nslcmop(nsr_id, operation, params):
606 """
607 Creates a ns-lcm-opp content to be stored at database.
608 :param nsr_id: internal id of the instance
609 :param operation: instantiate, terminate, scale, action, ...
610 :param params: user parameters for the operation
611 :return: dictionary following SOL005 format
612 """
613 now = time()
614 _id = str(uuid4())
615 nslcmop = {
616 "id": _id,
617 "_id": _id,
618 "operationState": "PROCESSING", # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
619 "statusEnteredTime": now,
620 "nsInstanceId": nsr_id,
621 "lcmOperationType": operation,
622 "startTime": now,
623 "isAutomaticInvocation": False,
624 "operationParams": params,
625 "isCancelPending": False,
626 "links": {
627 "self": "/osm/nslcm/v1/ns_lcm_op_occs/" + _id,
628 "nsInstance": "/osm/nslcm/v1/ns_instances/" + nsr_id,
629 }
630 }
631 return nslcmop
632
633 def new(self, rollback, session, indata=None, kwargs=None, headers=None, force=False, make_public=False):
634 """
635 Performs a new operation over a ns
636 :param rollback: list to append created items at database in case a rollback must to be done
637 :param session: contains the used login username and working project
638 :param indata: descriptor with the parameters of the operation. It must contains among others
639 nsInstanceId: _id of the nsr to perform the operation
640 operation: it can be: instantiate, terminate, action, TODO: update, heal
641 :param kwargs: used to override the indata descriptor
642 :param headers: http request headers
643 :param force: If True avoid some dependence checks
644 :param make_public: Make the created item public to all projects
645 :return: id of the nslcmops
646 """
647 try:
648 # Override descriptor with query string kwargs
649 self._update_input_with_kwargs(indata, kwargs)
650 operation = indata["lcmOperationType"]
651 nsInstanceId = indata["nsInstanceId"]
652
653 validate_input(indata, self.operation_schema[operation])
654 # get ns from nsr_id
655 _filter = BaseTopic._get_project_filter(session, write=True, show_all=False)
656 _filter["_id"] = nsInstanceId
657 nsr = self.db.get_one("nsrs", _filter)
658
659 # initial checking
660 if not nsr["_admin"].get("nsState") or nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
661 if operation == "terminate" and indata.get("autoremove"):
662 # NSR must be deleted
663 return self.delete(session, nsInstanceId)
664 if operation != "instantiate":
665 raise EngineException("ns_instance '{}' cannot be '{}' because it is not instantiated".format(
666 nsInstanceId, operation), HTTPStatus.CONFLICT)
667 else:
668 if operation == "instantiate" and not indata.get("force"):
669 raise EngineException("ns_instance '{}' cannot be '{}' because it is already instantiated".format(
670 nsInstanceId, operation), HTTPStatus.CONFLICT)
671 self._check_ns_operation(session, nsr, operation, indata)
672
673 if operation == "instantiate":
674 self._update_vnfrs(session, rollback, nsr, indata)
675
676 nslcmop_desc = self._create_nslcmop(nsInstanceId, operation, indata)
677 self.format_on_new(nslcmop_desc, session["project_id"], make_public=make_public)
678 _id = self.db.create("nslcmops", nslcmop_desc)
679 rollback.append({"topic": "nslcmops", "_id": _id})
680 self.msg.write("ns", operation, nslcmop_desc)
681 return _id
682 except ValidationError as e:
683 raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
684 # except DbException as e:
685 # raise EngineException("Cannot get ns_instance '{}': {}".format(e), HTTPStatus.NOT_FOUND)
686
687 def delete(self, session, _id, force=False, dry_run=False):
688 raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
689
690 def edit(self, session, _id, indata=None, kwargs=None, force=False, content=None):
691 raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)