bug 585 change charm model to nsr_id. Create/Delete model name
[osm/LCM.git] / osm_lcm / ns.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2018 Telefonica S.A.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 ##
18
19 import asyncio
20 import yaml
21 import logging
22 import logging.handlers
23 import functools
24 import traceback
25 from jinja2 import Environment, Template, meta, TemplateError, TemplateNotFound, TemplateSyntaxError
26
27 import ROclient
28 from lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase
29
30 from osm_common.dbbase import DbException
31 from osm_common.fsbase import FsException
32 from n2vc.vnf import N2VC, N2VCPrimitiveExecutionFailed, NetworkServiceDoesNotExist
33
34 from copy import copy, deepcopy
35 from http import HTTPStatus
36 from time import time
37 from uuid import uuid4
38
39 __author__ = "Alfonso Tierno"
40
41
42 def get_iterable(in_dict, in_key):
43 """
44 Similar to <dict>.get(), but if value is None, False, ..., An empty tuple is returned instead
45 :param in_dict: a dictionary
46 :param in_key: the key to look for at in_dict
47 :return: in_dict[in_var] or () if it is None or not present
48 """
49 if not in_dict.get(in_key):
50 return ()
51 return in_dict[in_key]
52
53
54 def populate_dict(target_dict, key_list, value):
55 """
56 Upate target_dict creating nested dictionaries with the key_list. Last key_list item is asigned the value.
57 Example target_dict={K: J}; key_list=[a,b,c]; target_dict will be {K: J, a: {b: {c: value}}}
58 :param target_dict: dictionary to be changed
59 :param key_list: list of keys to insert at target_dict
60 :param value:
61 :return: None
62 """
63 for key in key_list[0:-1]:
64 if key not in target_dict:
65 target_dict[key] = {}
66 target_dict = target_dict[key]
67 target_dict[key_list[-1]] = value
68
69
70 class NsLcm(LcmBase):
71 timeout_vca_on_error = 5 * 60 # Time for charm from first time at blocked,error status to mark as failed
72 total_deploy_timeout = 2 * 3600 # global timeout for deployment
73 timeout_charm_delete = 10 * 60
74 timeout_primitive = 10 * 60 # timeout for primitive execution
75
76 def __init__(self, db, msg, fs, lcm_tasks, ro_config, vca_config, loop):
77 """
78 Init, Connect to database, filesystem storage, and messaging
79 :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
80 :return: None
81 """
82 # logging
83 self.logger = logging.getLogger('lcm.ns')
84 self.loop = loop
85 self.lcm_tasks = lcm_tasks
86
87 super().__init__(db, msg, fs, self.logger)
88
89 self.ro_config = ro_config
90
91 self.n2vc = N2VC(
92 log=self.logger,
93 server=vca_config['host'],
94 port=vca_config['port'],
95 user=vca_config['user'],
96 secret=vca_config['secret'],
97 # TODO: This should point to the base folder where charms are stored,
98 # if there is a common one (like object storage). Otherwise, leave
99 # it unset and pass it via DeployCharms
100 # artifacts=vca_config[''],
101 artifacts=None,
102 juju_public_key=vca_config.get('pubkey'),
103 ca_cert=vca_config.get('cacert'),
104 )
105
106 def vnfd2RO(self, vnfd, new_id=None, additionalParams=None, nsrId=None):
107 """
108 Converts creates a new vnfd descriptor for RO base on input OSM IM vnfd
109 :param vnfd: input vnfd
110 :param new_id: overrides vnf id if provided
111 :param additionalParams: Instantiation params for VNFs provided
112 :param nsrId: Id of the NSR
113 :return: copy of vnfd
114 """
115 try:
116 vnfd_RO = deepcopy(vnfd)
117 # remove unused by RO configuration, monitoring, scaling and internal keys
118 vnfd_RO.pop("_id", None)
119 vnfd_RO.pop("_admin", None)
120 vnfd_RO.pop("vnf-configuration", None)
121 vnfd_RO.pop("monitoring-param", None)
122 vnfd_RO.pop("scaling-group-descriptor", None)
123 if new_id:
124 vnfd_RO["id"] = new_id
125
126 # parse cloud-init or cloud-init-file with the provided variables using Jinja2
127 for vdu in get_iterable(vnfd_RO, "vdu"):
128 cloud_init_file = None
129 if vdu.get("cloud-init-file"):
130 base_folder = vnfd["_admin"]["storage"]
131 cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
132 vdu["cloud-init-file"])
133 with self.fs.file_open(cloud_init_file, "r") as ci_file:
134 cloud_init_content = ci_file.read()
135 vdu.pop("cloud-init-file", None)
136 elif vdu.get("cloud-init"):
137 cloud_init_content = vdu["cloud-init"]
138 else:
139 continue
140
141 env = Environment()
142 ast = env.parse(cloud_init_content)
143 mandatory_vars = meta.find_undeclared_variables(ast)
144 if mandatory_vars:
145 for var in mandatory_vars:
146 if not additionalParams or var not in additionalParams.keys():
147 raise LcmException("Variable '{}' defined at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
148 "file, must be provided in the instantiation parameters inside the "
149 "'additionalParamsForVnf' block".format(var, vnfd["id"], vdu["id"]))
150 template = Template(cloud_init_content)
151 cloud_init_content = template.render(additionalParams or {})
152 vdu["cloud-init"] = cloud_init_content
153
154 return vnfd_RO
155 except FsException as e:
156 raise LcmException("Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".
157 format(vnfd["id"], vdu["id"], cloud_init_file, e))
158 except (TemplateError, TemplateNotFound, TemplateSyntaxError) as e:
159 raise LcmException("Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".
160 format(vnfd["id"], vdu["id"], e))
161
162 def n2vc_callback(self, model_name, application_name, status, message, n2vc_info, task=None):
163 """
164 Callback both for charm status change and task completion
165 :param model_name: Charm model name
166 :param application_name: Charm application name
167 :param status: Can be
168 - blocked: The unit needs manual intervention
169 - maintenance: The unit is actively deploying/configuring
170 - waiting: The unit is waiting for another charm to be ready
171 - active: The unit is deployed, configured, and ready
172 - error: The charm has failed and needs attention.
173 - terminated: The charm has been destroyed
174 - removing,
175 - removed
176 :param message: detailed message error
177 :param n2vc_info: dictionary with information shared with instantiate task. It contains:
178 nsr_id:
179 nslcmop_id:
180 lcmOperationType: currently "instantiate"
181 deployed: dictionary with {<application>: {operational-status: <status>, detailed-status: <text>}}
182 db_update: dictionary to be filled with the changes to be wrote to database with format key.key.key: value
183 n2vc_event: event used to notify instantiation task that some change has been produced
184 :param task: None for charm status change, or task for completion task callback
185 :return:
186 """
187 try:
188 nsr_id = n2vc_info["nsr_id"]
189 deployed = n2vc_info["deployed"]
190 db_nsr_update = n2vc_info["db_update"]
191 nslcmop_id = n2vc_info["nslcmop_id"]
192 ns_operation = n2vc_info["lcmOperationType"]
193 n2vc_event = n2vc_info["n2vc_event"]
194 logging_text = "Task ns={} {}={} [n2vc_callback] application={}".format(nsr_id, ns_operation, nslcmop_id,
195 application_name)
196 for vca_index, vca_deployed in enumerate(deployed):
197 if not vca_deployed:
198 continue
199 if model_name == vca_deployed["model"] and application_name == vca_deployed["application"]:
200 break
201 else:
202 self.logger.error(logging_text + " Not present at nsr._admin.deployed.VCA. Received model_name={}".
203 format(model_name))
204 return
205 if task:
206 if task.cancelled():
207 self.logger.debug(logging_text + " task Cancelled")
208 vca_deployed['operational-status'] = "error"
209 db_nsr_update["_admin.deployed.VCA.{}.operational-status".format(vca_index)] = "error"
210 vca_deployed['detailed-status'] = "Task Cancelled"
211 db_nsr_update["_admin.deployed.VCA.{}.detailed-status".format(vca_index)] = "Task Cancelled"
212
213 elif task.done():
214 exc = task.exception()
215 if exc:
216 self.logger.error(logging_text + " task Exception={}".format(exc))
217 vca_deployed['operational-status'] = "error"
218 db_nsr_update["_admin.deployed.VCA.{}.operational-status".format(vca_index)] = "error"
219 vca_deployed['detailed-status'] = str(exc)
220 db_nsr_update["_admin.deployed.VCA.{}.detailed-status".format(vca_index)] = str(exc)
221 else:
222 self.logger.debug(logging_text + " task Done")
223 # task is Done, but callback is still ongoing. So ignore
224 return
225 elif status:
226 self.logger.debug(logging_text + " Enter status={} message={}".format(status, message))
227 if vca_deployed['operational-status'] == status:
228 return # same status, ignore
229 vca_deployed['operational-status'] = status
230 db_nsr_update["_admin.deployed.VCA.{}.operational-status".format(vca_index)] = status
231 vca_deployed['detailed-status'] = str(message)
232 db_nsr_update["_admin.deployed.VCA.{}.detailed-status".format(vca_index)] = str(message)
233 else:
234 self.logger.critical(logging_text + " Enter with bad parameters", exc_info=True)
235 return
236 # wake up instantiate task
237 n2vc_event.set()
238 except Exception as e:
239 self.logger.critical(logging_text + " Exception {}".format(e), exc_info=True)
240
241 def ns_params_2_RO(self, ns_params, nsd, vnfd_dict, n2vc_key_list):
242 """
243 Creates a RO ns descriptor from OSM ns_instantiate params
244 :param ns_params: OSM instantiate params
245 :return: The RO ns descriptor
246 """
247 vim_2_RO = {}
248 wim_2_RO = {}
249 # TODO feature 1417: Check that no instantiation is set over PDU
250 # check if PDU forces a concrete vim-network-id and add it
251 # check if PDU contains a SDN-assist info (dpid, switch, port) and pass it to RO
252
253 def vim_account_2_RO(vim_account):
254 if vim_account in vim_2_RO:
255 return vim_2_RO[vim_account]
256
257 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account})
258 if db_vim["_admin"]["operationalState"] != "ENABLED":
259 raise LcmException("VIM={} is not available. operationalState={}".format(
260 vim_account, db_vim["_admin"]["operationalState"]))
261 RO_vim_id = db_vim["_admin"]["deployed"]["RO"]
262 vim_2_RO[vim_account] = RO_vim_id
263 return RO_vim_id
264
265 def wim_account_2_RO(wim_account):
266 if isinstance(wim_account, str):
267 if wim_account in wim_2_RO:
268 return wim_2_RO[wim_account]
269
270 db_wim = self.db.get_one("wim_accounts", {"_id": wim_account})
271 if db_wim["_admin"]["operationalState"] != "ENABLED":
272 raise LcmException("WIM={} is not available. operationalState={}".format(
273 wim_account, db_wim["_admin"]["operationalState"]))
274 RO_wim_id = db_wim["_admin"]["deployed"]["RO-account"]
275 wim_2_RO[wim_account] = RO_wim_id
276 return RO_wim_id
277 else:
278 return wim_account
279
280 def ip_profile_2_RO(ip_profile):
281 RO_ip_profile = deepcopy((ip_profile))
282 if "dns-server" in RO_ip_profile:
283 if isinstance(RO_ip_profile["dns-server"], list):
284 RO_ip_profile["dns-address"] = []
285 for ds in RO_ip_profile.pop("dns-server"):
286 RO_ip_profile["dns-address"].append(ds['address'])
287 else:
288 RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
289 if RO_ip_profile.get("ip-version") == "ipv4":
290 RO_ip_profile["ip-version"] = "IPv4"
291 if RO_ip_profile.get("ip-version") == "ipv6":
292 RO_ip_profile["ip-version"] = "IPv6"
293 if "dhcp-params" in RO_ip_profile:
294 RO_ip_profile["dhcp"] = RO_ip_profile.pop("dhcp-params")
295 return RO_ip_profile
296
297 if not ns_params:
298 return None
299 RO_ns_params = {
300 # "name": ns_params["nsName"],
301 # "description": ns_params.get("nsDescription"),
302 "datacenter": vim_account_2_RO(ns_params["vimAccountId"]),
303 "wim_account": wim_account_2_RO(ns_params.get("wimAccountId")),
304 # "scenario": ns_params["nsdId"],
305 }
306 if n2vc_key_list:
307 for vnfd_ref, vnfd in vnfd_dict.items():
308 vdu_needed_access = []
309 mgmt_cp = None
310 if vnfd.get("vnf-configuration"):
311 if vnfd.get("mgmt-interface"):
312 if vnfd["mgmt-interface"].get("vdu-id"):
313 vdu_needed_access.append(vnfd["mgmt-interface"]["vdu-id"])
314 elif vnfd["mgmt-interface"].get("cp"):
315 mgmt_cp = vnfd["mgmt-interface"]["cp"]
316
317 for vdu in vnfd.get("vdu", ()):
318 if vdu.get("vdu-configuration"):
319 vdu_needed_access.append(vdu["id"])
320 elif mgmt_cp:
321 for vdu_interface in vdu.get("interface"):
322 if vdu_interface.get("external-connection-point-ref") and \
323 vdu_interface["external-connection-point-ref"] == mgmt_cp:
324 vdu_needed_access.append(vdu["id"])
325 mgmt_cp = None
326 break
327
328 if vdu_needed_access:
329 for vnf_member in nsd.get("constituent-vnfd"):
330 if vnf_member["vnfd-id-ref"] != vnfd_ref:
331 continue
332 for vdu in vdu_needed_access:
333 populate_dict(RO_ns_params,
334 ("vnfs", vnf_member["member-vnf-index"], "vdus", vdu, "mgmt_keys"),
335 n2vc_key_list)
336
337 if ns_params.get("vduImage"):
338 RO_ns_params["vduImage"] = ns_params["vduImage"]
339
340 if ns_params.get("ssh_keys"):
341 RO_ns_params["cloud-config"] = {"key-pairs": ns_params["ssh_keys"]}
342 for vnf_params in get_iterable(ns_params, "vnf"):
343 for constituent_vnfd in nsd["constituent-vnfd"]:
344 if constituent_vnfd["member-vnf-index"] == vnf_params["member-vnf-index"]:
345 vnf_descriptor = vnfd_dict[constituent_vnfd["vnfd-id-ref"]]
346 break
347 else:
348 raise LcmException("Invalid instantiate parameter vnf:member-vnf-index={} is not present at nsd:"
349 "constituent-vnfd".format(vnf_params["member-vnf-index"]))
350 if vnf_params.get("vimAccountId"):
351 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "datacenter"),
352 vim_account_2_RO(vnf_params["vimAccountId"]))
353
354 for vdu_params in get_iterable(vnf_params, "vdu"):
355 # TODO feature 1417: check that this VDU exist and it is not a PDU
356 if vdu_params.get("volume"):
357 for volume_params in vdu_params["volume"]:
358 if volume_params.get("vim-volume-id"):
359 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
360 vdu_params["id"], "devices", volume_params["name"], "vim_id"),
361 volume_params["vim-volume-id"])
362 if vdu_params.get("interface"):
363 for interface_params in vdu_params["interface"]:
364 if interface_params.get("ip-address"):
365 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
366 vdu_params["id"], "interfaces", interface_params["name"],
367 "ip_address"),
368 interface_params["ip-address"])
369 if interface_params.get("mac-address"):
370 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
371 vdu_params["id"], "interfaces", interface_params["name"],
372 "mac_address"),
373 interface_params["mac-address"])
374 if interface_params.get("floating-ip-required"):
375 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
376 vdu_params["id"], "interfaces", interface_params["name"],
377 "floating-ip"),
378 interface_params["floating-ip-required"])
379
380 for internal_vld_params in get_iterable(vnf_params, "internal-vld"):
381 if internal_vld_params.get("vim-network-name"):
382 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "networks",
383 internal_vld_params["name"], "vim-network-name"),
384 internal_vld_params["vim-network-name"])
385 if internal_vld_params.get("vim-network-id"):
386 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "networks",
387 internal_vld_params["name"], "vim-network-id"),
388 internal_vld_params["vim-network-id"])
389 if internal_vld_params.get("ip-profile"):
390 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "networks",
391 internal_vld_params["name"], "ip-profile"),
392 ip_profile_2_RO(internal_vld_params["ip-profile"]))
393
394 for icp_params in get_iterable(internal_vld_params, "internal-connection-point"):
395 # look for interface
396 iface_found = False
397 for vdu_descriptor in vnf_descriptor["vdu"]:
398 for vdu_interface in vdu_descriptor["interface"]:
399 if vdu_interface.get("internal-connection-point-ref") == icp_params["id-ref"]:
400 if icp_params.get("ip-address"):
401 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
402 vdu_descriptor["id"], "interfaces",
403 vdu_interface["name"], "ip_address"),
404 icp_params["ip-address"])
405
406 if icp_params.get("mac-address"):
407 populate_dict(RO_ns_params, ("vnfs", vnf_params["member-vnf-index"], "vdus",
408 vdu_descriptor["id"], "interfaces",
409 vdu_interface["name"], "mac_address"),
410 icp_params["mac-address"])
411 iface_found = True
412 break
413 if iface_found:
414 break
415 else:
416 raise LcmException("Invalid instantiate parameter vnf:member-vnf-index[{}]:"
417 "internal-vld:id-ref={} is not present at vnfd:internal-"
418 "connection-point".format(vnf_params["member-vnf-index"],
419 icp_params["id-ref"]))
420
421 for vld_params in get_iterable(ns_params, "vld"):
422 if "ip-profile" in vld_params:
423 populate_dict(RO_ns_params, ("networks", vld_params["name"], "ip-profile"),
424 ip_profile_2_RO(vld_params["ip-profile"]))
425
426 if "wimAccountId" in vld_params and vld_params["wimAccountId"] is not None:
427 populate_dict(RO_ns_params, ("networks", vld_params["name"], "wim_account"),
428 wim_account_2_RO(vld_params["wimAccountId"])),
429 if vld_params.get("vim-network-name"):
430 RO_vld_sites = []
431 if isinstance(vld_params["vim-network-name"], dict):
432 for vim_account, vim_net in vld_params["vim-network-name"].items():
433 RO_vld_sites.append({
434 "netmap-use": vim_net,
435 "datacenter": vim_account_2_RO(vim_account)
436 })
437 else: # isinstance str
438 RO_vld_sites.append({"netmap-use": vld_params["vim-network-name"]})
439 if RO_vld_sites:
440 populate_dict(RO_ns_params, ("networks", vld_params["name"], "sites"), RO_vld_sites)
441 if vld_params.get("vim-network-id"):
442 RO_vld_sites = []
443 if isinstance(vld_params["vim-network-id"], dict):
444 for vim_account, vim_net in vld_params["vim-network-id"].items():
445 RO_vld_sites.append({
446 "netmap-use": vim_net,
447 "datacenter": vim_account_2_RO(vim_account)
448 })
449 else: # isinstance str
450 RO_vld_sites.append({"netmap-use": vld_params["vim-network-id"]})
451 if RO_vld_sites:
452 populate_dict(RO_ns_params, ("networks", vld_params["name"], "sites"), RO_vld_sites)
453 if vld_params.get("ns-net"):
454 if isinstance(vld_params["ns-net"], dict):
455 for vld_id, instance_scenario_id in vld_params["ns-net"].items():
456 RO_vld_ns_net = {"instance_scenario_id": instance_scenario_id, "osm_id": vld_id}
457 if RO_vld_ns_net:
458 populate_dict(RO_ns_params, ("networks", vld_params["name"], "use-network"), RO_vld_ns_net)
459 if "vnfd-connection-point-ref" in vld_params:
460 for cp_params in vld_params["vnfd-connection-point-ref"]:
461 # look for interface
462 for constituent_vnfd in nsd["constituent-vnfd"]:
463 if constituent_vnfd["member-vnf-index"] == cp_params["member-vnf-index-ref"]:
464 vnf_descriptor = vnfd_dict[constituent_vnfd["vnfd-id-ref"]]
465 break
466 else:
467 raise LcmException(
468 "Invalid instantiate parameter vld:vnfd-connection-point-ref:member-vnf-index-ref={} "
469 "is not present at nsd:constituent-vnfd".format(cp_params["member-vnf-index-ref"]))
470 match_cp = False
471 for vdu_descriptor in vnf_descriptor["vdu"]:
472 for interface_descriptor in vdu_descriptor["interface"]:
473 if interface_descriptor.get("external-connection-point-ref") == \
474 cp_params["vnfd-connection-point-ref"]:
475 match_cp = True
476 break
477 if match_cp:
478 break
479 else:
480 raise LcmException(
481 "Invalid instantiate parameter vld:vnfd-connection-point-ref:member-vnf-index-ref={}:"
482 "vnfd-connection-point-ref={} is not present at vnfd={}".format(
483 cp_params["member-vnf-index-ref"],
484 cp_params["vnfd-connection-point-ref"],
485 vnf_descriptor["id"]))
486 if cp_params.get("ip-address"):
487 populate_dict(RO_ns_params, ("vnfs", cp_params["member-vnf-index-ref"], "vdus",
488 vdu_descriptor["id"], "interfaces",
489 interface_descriptor["name"], "ip_address"),
490 cp_params["ip-address"])
491 if cp_params.get("mac-address"):
492 populate_dict(RO_ns_params, ("vnfs", cp_params["member-vnf-index-ref"], "vdus",
493 vdu_descriptor["id"], "interfaces",
494 interface_descriptor["name"], "mac_address"),
495 cp_params["mac-address"])
496 return RO_ns_params
497
498 def scale_vnfr(self, db_vnfr, vdu_create=None, vdu_delete=None):
499 # make a copy to do not change
500 vdu_create = copy(vdu_create)
501 vdu_delete = copy(vdu_delete)
502
503 vdurs = db_vnfr.get("vdur")
504 if vdurs is None:
505 vdurs = []
506 vdu_index = len(vdurs)
507 while vdu_index:
508 vdu_index -= 1
509 vdur = vdurs[vdu_index]
510 if vdur.get("pdu-type"):
511 continue
512 vdu_id_ref = vdur["vdu-id-ref"]
513 if vdu_create and vdu_create.get(vdu_id_ref):
514 for index in range(0, vdu_create[vdu_id_ref]):
515 vdur = deepcopy(vdur)
516 vdur["_id"] = str(uuid4())
517 vdur["count-index"] += 1
518 vdurs.insert(vdu_index+1+index, vdur)
519 del vdu_create[vdu_id_ref]
520 if vdu_delete and vdu_delete.get(vdu_id_ref):
521 del vdurs[vdu_index]
522 vdu_delete[vdu_id_ref] -= 1
523 if not vdu_delete[vdu_id_ref]:
524 del vdu_delete[vdu_id_ref]
525 # check all operations are done
526 if vdu_create or vdu_delete:
527 raise LcmException("Error scaling OUT VNFR for {}. There is not any existing vnfr. Scaled to 0?".format(
528 vdu_create))
529 if vdu_delete:
530 raise LcmException("Error scaling IN VNFR for {}. There is not any existing vnfr. Scaled to 0?".format(
531 vdu_delete))
532
533 vnfr_update = {"vdur": vdurs}
534 db_vnfr["vdur"] = vdurs
535 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
536
537 def ns_update_nsr(self, ns_update_nsr, db_nsr, nsr_desc_RO):
538 """
539 Updates database nsr with the RO info for the created vld
540 :param ns_update_nsr: dictionary to be filled with the updated info
541 :param db_nsr: content of db_nsr. This is also modified
542 :param nsr_desc_RO: nsr descriptor from RO
543 :return: Nothing, LcmException is raised on errors
544 """
545
546 for vld_index, vld in enumerate(get_iterable(db_nsr, "vld")):
547 for net_RO in get_iterable(nsr_desc_RO, "nets"):
548 if vld["id"] != net_RO.get("ns_net_osm_id"):
549 continue
550 vld["vim-id"] = net_RO.get("vim_net_id")
551 vld["name"] = net_RO.get("vim_name")
552 vld["status"] = net_RO.get("status")
553 vld["status-detailed"] = net_RO.get("error_msg")
554 ns_update_nsr["vld.{}".format(vld_index)] = vld
555 break
556 else:
557 raise LcmException("ns_update_nsr: Not found vld={} at RO info".format(vld["id"]))
558
559 def ns_update_vnfr(self, db_vnfrs, nsr_desc_RO):
560 """
561 Updates database vnfr with the RO info, e.g. ip_address, vim_id... Descriptor db_vnfrs is also updated
562 :param db_vnfrs: dictionary with member-vnf-index: vnfr-content
563 :param nsr_desc_RO: nsr descriptor from RO
564 :return: Nothing, LcmException is raised on errors
565 """
566 for vnf_index, db_vnfr in db_vnfrs.items():
567 for vnf_RO in nsr_desc_RO["vnfs"]:
568 if vnf_RO["member_vnf_index"] != vnf_index:
569 continue
570 vnfr_update = {}
571 if vnf_RO.get("ip_address"):
572 db_vnfr["ip-address"] = vnfr_update["ip-address"] = vnf_RO["ip_address"].split(";")[0]
573 elif not db_vnfr.get("ip-address"):
574 raise LcmExceptionNoMgmtIP("ns member_vnf_index '{}' has no IP address".format(vnf_index))
575
576 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
577 vdur_RO_count_index = 0
578 if vdur.get("pdu-type"):
579 continue
580 for vdur_RO in get_iterable(vnf_RO, "vms"):
581 if vdur["vdu-id-ref"] != vdur_RO["vdu_osm_id"]:
582 continue
583 if vdur["count-index"] != vdur_RO_count_index:
584 vdur_RO_count_index += 1
585 continue
586 vdur["vim-id"] = vdur_RO.get("vim_vm_id")
587 if vdur_RO.get("ip_address"):
588 vdur["ip-address"] = vdur_RO["ip_address"].split(";")[0]
589 else:
590 vdur["ip-address"] = None
591 vdur["vdu-id-ref"] = vdur_RO.get("vdu_osm_id")
592 vdur["name"] = vdur_RO.get("vim_name")
593 vdur["status"] = vdur_RO.get("status")
594 vdur["status-detailed"] = vdur_RO.get("error_msg")
595 for ifacer in get_iterable(vdur, "interfaces"):
596 for interface_RO in get_iterable(vdur_RO, "interfaces"):
597 if ifacer["name"] == interface_RO.get("internal_name"):
598 ifacer["ip-address"] = interface_RO.get("ip_address")
599 ifacer["mac-address"] = interface_RO.get("mac_address")
600 break
601 else:
602 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} interface={} "
603 "at RO info".format(vnf_index, vdur["vdu-id-ref"], ifacer["name"]))
604 vnfr_update["vdur.{}".format(vdu_index)] = vdur
605 break
606 else:
607 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} count_index={} at "
608 "RO info".format(vnf_index, vdur["vdu-id-ref"], vdur["count-index"]))
609
610 for vld_index, vld in enumerate(get_iterable(db_vnfr, "vld")):
611 for net_RO in get_iterable(nsr_desc_RO, "nets"):
612 if vld["id"] != net_RO.get("vnf_net_osm_id"):
613 continue
614 vld["vim-id"] = net_RO.get("vim_net_id")
615 vld["name"] = net_RO.get("vim_name")
616 vld["status"] = net_RO.get("status")
617 vld["status-detailed"] = net_RO.get("error_msg")
618 vnfr_update["vld.{}".format(vld_index)] = vld
619 break
620 else:
621 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vld={} at RO info".format(
622 vnf_index, vld["id"]))
623
624 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
625 break
626
627 else:
628 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} at RO info".format(vnf_index))
629
630 async def instantiate(self, nsr_id, nslcmop_id):
631 logging_text = "Task ns={} instantiate={} ".format(nsr_id, nslcmop_id)
632 self.logger.debug(logging_text + "Enter")
633 # get all needed from database
634 start_deploy = time()
635 db_nsr = None
636 db_nslcmop = None
637 db_nsr_update = {"_admin.nslcmop": nslcmop_id}
638 db_nslcmop_update = {}
639 nslcmop_operation_state = None
640 db_vnfrs = {}
641 RO_descriptor_number = 0 # number of descriptors created at RO
642 vnf_index_2_RO_id = {} # map between vnfd/nsd id to the id used at RO
643 n2vc_info = {}
644 exc = None
645 try:
646 step = "Getting nslcmop={} from db".format(nslcmop_id)
647 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
648 step = "Getting nsr={} from db".format(nsr_id)
649 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
650 ns_params = db_nslcmop.get("operationParams")
651 nsd = db_nsr["nsd"]
652 nsr_name = db_nsr["name"] # TODO short-name??
653
654 # look if previous tasks in process
655 task_name, task_dependency = self.lcm_tasks.lookfor_related("ns", nsr_id, nslcmop_id)
656 if task_dependency:
657 step = db_nslcmop_update["detailed-status"] = \
658 "Waiting for related tasks to be completed: {}".format(task_name)
659 self.logger.debug(logging_text + step)
660 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
661 _, pending = await asyncio.wait(task_dependency, timeout=3600)
662 if pending:
663 raise LcmException("Timeout waiting related tasks to be completed")
664
665 step = "Getting vnfrs from db"
666 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
667 db_vnfds_ref = {}
668 db_vnfds = {}
669 for vnfr in db_vnfrs_list:
670 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
671 vnfd_id = vnfr["vnfd-id"]
672 vnfd_ref = vnfr["vnfd-ref"]
673 if vnfd_id not in db_vnfds:
674 step = "Getting vnfd={} id='{}' from db".format(vnfd_id, vnfd_ref)
675 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
676 db_vnfds_ref[vnfd_ref] = vnfd
677 db_vnfds[vnfd_id] = vnfd
678
679 # Get or generates the _admin.deployed,VCA list
680 vca_deployed_list = None
681 vca_model_name = None
682 if db_nsr["_admin"].get("deployed"):
683 vca_deployed_list = db_nsr["_admin"]["deployed"].get("VCA")
684 vca_model_name = db_nsr["_admin"]["deployed"].get("VCA-model-name")
685 if vca_deployed_list is None:
686 vca_deployed_list = []
687 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
688 elif isinstance(vca_deployed_list, dict):
689 # maintain backward compatibility. Change a dict to list at database
690 vca_deployed_list = list(vca_deployed_list.values())
691 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
692
693 db_nsr_update["detailed-status"] = "creating"
694 db_nsr_update["operational-status"] = "init"
695 if not db_nsr["_admin"].get("deployed") or not db_nsr["_admin"]["deployed"].get("RO") or \
696 not db_nsr["_admin"]["deployed"]["RO"].get("vnfd"):
697 populate_dict(db_nsr, ("_admin", "deployed", "RO", "vnfd"), [])
698 db_nsr_update["_admin.deployed.RO.vnfd"] = []
699
700 RO = ROclient.ROClient(self.loop, **self.ro_config)
701
702 # set state to INSTANTIATED. When instantiated NBI will not delete directly
703 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
704 self.update_db_2("nsrs", nsr_id, db_nsr_update)
705
706 # get vnfds, instantiate at RO
707 for c_vnf in nsd.get("constituent-vnfd", ()):
708 member_vnf_index = c_vnf["member-vnf-index"]
709 vnfd = db_vnfds_ref[c_vnf['vnfd-id-ref']]
710 vnfd_ref = vnfd["id"]
711 step = db_nsr_update["detailed-status"] = "Creating vnfd='{}' member-vnf-index='{}' at RO".format(
712 vnfd_ref, member_vnf_index)
713 # self.logger.debug(logging_text + step)
714 vnfd_id_RO = "{}.{}.{}".format(nsr_id, RO_descriptor_number, member_vnf_index[:23])
715 vnf_index_2_RO_id[member_vnf_index] = vnfd_id_RO
716 RO_descriptor_number += 1
717
718 # look position at deployed.RO.vnfd if not present it will be appended at the end
719 for index, vnf_deployed in enumerate(db_nsr["_admin"]["deployed"]["RO"]["vnfd"]):
720 if vnf_deployed["member-vnf-index"] == member_vnf_index:
721 break
722 else:
723 index = len(db_nsr["_admin"]["deployed"]["RO"]["vnfd"])
724 db_nsr["_admin"]["deployed"]["RO"]["vnfd"].append(None)
725
726 # look if present
727 RO_update = {"member-vnf-index": member_vnf_index}
728 vnfd_list = await RO.get_list("vnfd", filter_by={"osm_id": vnfd_id_RO})
729 if vnfd_list:
730 RO_update["id"] = vnfd_list[0]["uuid"]
731 self.logger.debug(logging_text + "vnfd='{}' member-vnf-index='{}' exists at RO. Using RO_id={}".
732 format(vnfd_ref, member_vnf_index, vnfd_list[0]["uuid"]))
733 else:
734 vnfd_RO = self.vnfd2RO(vnfd, vnfd_id_RO, db_vnfrs[c_vnf["member-vnf-index"]].
735 get("additionalParamsForVnf"), nsr_id)
736 desc = await RO.create("vnfd", descriptor=vnfd_RO)
737 RO_update["id"] = desc["uuid"]
738 self.logger.debug(logging_text + "vnfd='{}' member-vnf-index='{}' created at RO. RO_id={}".format(
739 vnfd_ref, member_vnf_index, desc["uuid"]))
740 db_nsr_update["_admin.deployed.RO.vnfd.{}".format(index)] = RO_update
741 db_nsr["_admin"]["deployed"]["RO"]["vnfd"][index] = RO_update
742 self.update_db_2("nsrs", nsr_id, db_nsr_update)
743
744 # create nsd at RO
745 nsd_ref = nsd["id"]
746 step = db_nsr_update["detailed-status"] = "Creating nsd={} at RO".format(nsd_ref)
747 # self.logger.debug(logging_text + step)
748
749 RO_osm_nsd_id = "{}.{}.{}".format(nsr_id, RO_descriptor_number, nsd_ref[:23])
750 RO_descriptor_number += 1
751 nsd_list = await RO.get_list("nsd", filter_by={"osm_id": RO_osm_nsd_id})
752 if nsd_list:
753 db_nsr_update["_admin.deployed.RO.nsd_id"] = RO_nsd_uuid = nsd_list[0]["uuid"]
754 self.logger.debug(logging_text + "nsd={} exists at RO. Using RO_id={}".format(
755 nsd_ref, RO_nsd_uuid))
756 else:
757 nsd_RO = deepcopy(nsd)
758 nsd_RO["id"] = RO_osm_nsd_id
759 nsd_RO.pop("_id", None)
760 nsd_RO.pop("_admin", None)
761 for c_vnf in nsd_RO.get("constituent-vnfd", ()):
762 member_vnf_index = c_vnf["member-vnf-index"]
763 c_vnf["vnfd-id-ref"] = vnf_index_2_RO_id[member_vnf_index]
764 for c_vld in nsd_RO.get("vld", ()):
765 for cp in c_vld.get("vnfd-connection-point-ref", ()):
766 member_vnf_index = cp["member-vnf-index-ref"]
767 cp["vnfd-id-ref"] = vnf_index_2_RO_id[member_vnf_index]
768
769 desc = await RO.create("nsd", descriptor=nsd_RO)
770 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
771 db_nsr_update["_admin.deployed.RO.nsd_id"] = RO_nsd_uuid = desc["uuid"]
772 self.logger.debug(logging_text + "nsd={} created at RO. RO_id={}".format(nsd_ref, RO_nsd_uuid))
773 self.update_db_2("nsrs", nsr_id, db_nsr_update)
774
775 # Crate ns at RO
776 # if present use it unless in error status
777 RO_nsr_id = db_nsr["_admin"].get("deployed", {}).get("RO", {}).get("nsr_id")
778 if RO_nsr_id:
779 try:
780 step = db_nsr_update["detailed-status"] = "Looking for existing ns at RO"
781 # self.logger.debug(logging_text + step + " RO_ns_id={}".format(RO_nsr_id))
782 desc = await RO.show("ns", RO_nsr_id)
783 except ROclient.ROClientException as e:
784 if e.http_code != HTTPStatus.NOT_FOUND:
785 raise
786 RO_nsr_id = db_nsr_update["_admin.deployed.RO.nsr_id"] = None
787 if RO_nsr_id:
788 ns_status, ns_status_info = RO.check_ns_status(desc)
789 db_nsr_update["_admin.deployed.RO.nsr_status"] = ns_status
790 if ns_status == "ERROR":
791 step = db_nsr_update["detailed-status"] = "Deleting ns at RO. RO_ns_id={}".format(RO_nsr_id)
792 self.logger.debug(logging_text + step)
793 await RO.delete("ns", RO_nsr_id)
794 RO_nsr_id = db_nsr_update["_admin.deployed.RO.nsr_id"] = None
795 if not RO_nsr_id:
796 step = db_nsr_update["detailed-status"] = "Checking dependencies"
797 # self.logger.debug(logging_text + step)
798
799 # check if VIM is creating and wait look if previous tasks in process
800 task_name, task_dependency = self.lcm_tasks.lookfor_related("vim_account", ns_params["vimAccountId"])
801 if task_dependency:
802 step = "Waiting for related tasks to be completed: {}".format(task_name)
803 self.logger.debug(logging_text + step)
804 await asyncio.wait(task_dependency, timeout=3600)
805 if ns_params.get("vnf"):
806 for vnf in ns_params["vnf"]:
807 if "vimAccountId" in vnf:
808 task_name, task_dependency = self.lcm_tasks.lookfor_related("vim_account",
809 vnf["vimAccountId"])
810 if task_dependency:
811 step = "Waiting for related tasks to be completed: {}".format(task_name)
812 self.logger.debug(logging_text + step)
813 await asyncio.wait(task_dependency, timeout=3600)
814
815 step = db_nsr_update["detailed-status"] = "Checking instantiation parameters"
816
817 # feature 1429. Add n2vc public key to needed VMs
818 n2vc_key = await self.n2vc.GetPublicKey()
819 RO_ns_params = self.ns_params_2_RO(ns_params, nsd, db_vnfds_ref, [n2vc_key])
820
821 step = db_nsr_update["detailed-status"] = "Creating ns at RO"
822 desc = await RO.create("ns", descriptor=RO_ns_params,
823 name=db_nsr["name"],
824 scenario=RO_nsd_uuid)
825 RO_nsr_id = db_nsr_update["_admin.deployed.RO.nsr_id"] = desc["uuid"]
826 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
827 db_nsr_update["_admin.deployed.RO.nsr_status"] = "BUILD"
828 self.logger.debug(logging_text + "ns created at RO. RO_id={}".format(desc["uuid"]))
829 self.update_db_2("nsrs", nsr_id, db_nsr_update)
830
831 # wait until NS is ready
832 step = ns_status_detailed = detailed_status = "Waiting ns ready at RO. RO_id={}".format(RO_nsr_id)
833 detailed_status_old = None
834 self.logger.debug(logging_text + step)
835
836 while time() <= start_deploy + self.total_deploy_timeout:
837 desc = await RO.show("ns", RO_nsr_id)
838 ns_status, ns_status_info = RO.check_ns_status(desc)
839 db_nsr_update["_admin.deployed.RO.nsr_status"] = ns_status
840 if ns_status == "ERROR":
841 raise ROclient.ROClientException(ns_status_info)
842 elif ns_status == "BUILD":
843 detailed_status = ns_status_detailed + "; {}".format(ns_status_info)
844 elif ns_status == "ACTIVE":
845 step = detailed_status = "Waiting for management IP address reported by the VIM. Updating VNFRs"
846 try:
847 self.ns_update_vnfr(db_vnfrs, desc)
848 break
849 except LcmExceptionNoMgmtIP:
850 pass
851 else:
852 assert False, "ROclient.check_ns_status returns unknown {}".format(ns_status)
853 if detailed_status != detailed_status_old:
854 detailed_status_old = db_nsr_update["detailed-status"] = detailed_status
855 self.update_db_2("nsrs", nsr_id, db_nsr_update)
856 await asyncio.sleep(5, loop=self.loop)
857 else: # total_deploy_timeout
858 raise ROclient.ROClientException("Timeout waiting ns to be ready")
859
860 step = "Updating NSR"
861 self.ns_update_nsr(db_nsr_update, db_nsr, desc)
862
863 db_nsr["detailed-status"] = "Configuring vnfr"
864 self.update_db_2("nsrs", nsr_id, db_nsr_update)
865
866 # The parameters we'll need to deploy a charm
867 number_to_configure = 0
868
869 def deploy_charm(vnf_index, vdu_id, vdu_name, vdu_count_index, charm_params, n2vc_info):
870 """An inner function to deploy the charm from either vnf or vdu
871 vnf_index is mandatory. vdu_id can be None for a vnf configuration or the id for vdu configuration
872 """
873 if not charm_params["rw_mgmt_ip"]:
874 raise LcmException("vnfd/vdu has not management ip address to configure it")
875 # Login to the VCA.
876 # if number_to_configure == 0:
877 # self.logger.debug("Logging into N2VC...")
878 # task = asyncio.ensure_future(self.n2vc.login())
879 # yield from asyncio.wait_for(task, 30.0)
880 # self.logger.debug("Logged into N2VC!")
881
882 # # await self.n2vc.login()
883
884 # Note: The charm needs to exist on disk at the location
885 # specified by charm_path.
886 base_folder = vnfd["_admin"]["storage"]
887 storage_params = self.fs.get_params()
888 charm_path = "{}{}/{}/charms/{}".format(
889 storage_params["path"],
890 base_folder["folder"],
891 base_folder["pkg-dir"],
892 proxy_charm
893 )
894
895 # ns_name will be ignored in the current version of N2VC
896 # but will be implemented for the next point release.
897 model_name = nsr_id
898 if vdu_id:
899 vdu_id_text = vdu_id + "-"
900 else:
901 vdu_id_text = "-"
902 application_name = self.n2vc.FormatApplicationName(nsr_name, vnf_index, vdu_id_text)
903
904 vca_index = len(vca_deployed_list)
905 # trunk name and add two char index at the end to ensure that it is unique. It is assumed no more than
906 # 26*26 charm in the same NS
907 application_name = application_name[0:48]
908 application_name += chr(97 + vca_index // 26) + chr(97 + vca_index % 26)
909 vca_deployed_ = {
910 "member-vnf-index": vnf_index,
911 "vdu_id": vdu_id,
912 "model": model_name,
913 "application": application_name,
914 "operational-status": "init",
915 "detailed-status": "",
916 "vnfd_id": vnfd_id,
917 "vdu_name": vdu_name,
918 "vdu_count_index": vdu_count_index,
919 }
920 vca_deployed_list.append(vca_deployed_)
921 db_nsr_update["_admin.deployed.VCA.{}".format(vca_index)] = vca_deployed_
922 self.update_db_2("nsrs", nsr_id, db_nsr_update)
923
924 self.logger.debug("Task create_ns={} Passing artifacts path '{}' for {}".format(nsr_id, charm_path,
925 proxy_charm))
926 if not n2vc_info:
927 n2vc_info["nsr_id"] = nsr_id
928 n2vc_info["nslcmop_id"] = nslcmop_id
929 n2vc_info["n2vc_event"] = asyncio.Event(loop=self.loop)
930 n2vc_info["lcmOperationType"] = "instantiate"
931 n2vc_info["deployed"] = vca_deployed_list
932 n2vc_info["db_update"] = db_nsr_update
933 task = asyncio.ensure_future(
934 self.n2vc.DeployCharms(
935 model_name, # The network service name
936 application_name, # The application name
937 vnfd, # The vnf descriptor
938 charm_path, # Path to charm
939 charm_params, # Runtime params, like mgmt ip
940 {}, # for native charms only
941 self.n2vc_callback, # Callback for status changes
942 n2vc_info, # Callback parameter
943 None, # Callback parameter (task)
944 )
945 )
946 task.add_done_callback(functools.partial(self.n2vc_callback, model_name, application_name, None, None,
947 n2vc_info))
948 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "create_charm:" + application_name, task)
949
950 step = "Looking for needed vnfd to configure"
951 self.logger.debug(logging_text + step)
952
953 for c_vnf in get_iterable(nsd, "constituent-vnfd"):
954 vnfd_id = c_vnf["vnfd-id-ref"]
955 vnf_index = str(c_vnf["member-vnf-index"])
956 vnfd = db_vnfds_ref[vnfd_id]
957
958 # Get additional parameters
959 vnfr_params = {}
960 if db_vnfrs[vnf_index].get("additionalParamsForVnf"):
961 vnfr_params = db_vnfrs[vnf_index]["additionalParamsForVnf"].copy()
962 for k, v in vnfr_params.items():
963 if isinstance(v, str) and v.startswith("!!yaml "):
964 vnfr_params[k] = yaml.safe_load(v[7:])
965
966 # Check if this VNF has a charm configuration
967 vnf_config = vnfd.get("vnf-configuration")
968 if vnf_config and vnf_config.get("juju"):
969 proxy_charm = vnf_config["juju"]["charm"]
970
971 if proxy_charm:
972 if not vca_model_name:
973 step = "creating VCA model name '{}'".format(nsr_id)
974 self.logger.debug(logging_text + step)
975 await self.n2vc.CreateNetworkService(nsr_id)
976 vca_model_name = nsr_id
977 db_nsr_update["_admin.deployed.VCA-model-name"] = nsr_id
978 self.update_db_2("nsrs", nsr_id, db_nsr_update)
979 step = "connecting to N2VC to configure vnf {}".format(vnf_index)
980 vnfr_params["rw_mgmt_ip"] = db_vnfrs[vnf_index]["ip-address"]
981 charm_params = {
982 "user_values": vnfr_params,
983 "rw_mgmt_ip": db_vnfrs[vnf_index]["ip-address"],
984 "initial-config-primitive": vnf_config.get('initial-config-primitive') or {}
985 }
986
987 # Login to the VCA. If there are multiple calls to login(),
988 # subsequent calls will be a nop and return immediately.
989 await self.n2vc.login()
990
991 deploy_charm(vnf_index, None, None, None, charm_params, n2vc_info)
992 number_to_configure += 1
993
994 # Deploy charms for each VDU that supports one.
995 for vdu_index, vdu in enumerate(get_iterable(vnfd, 'vdu')):
996 vdu_config = vdu.get('vdu-configuration')
997 proxy_charm = None
998
999 if vdu_config and vdu_config.get("juju"):
1000 proxy_charm = vdu_config["juju"]["charm"]
1001
1002 if proxy_charm:
1003 if not vca_model_name:
1004 step = "creating VCA model name"
1005 await self.n2vc.CreateNetworkService(nsr_id)
1006 vca_model_name = nsr_id
1007 db_nsr_update["_admin.deployed.VCA-model-name"] = nsr_id
1008 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1009 step = "connecting to N2VC to configure vdu {} from vnf {}".format(vdu["id"], vnf_index)
1010 await self.n2vc.login()
1011 vdur = db_vnfrs[vnf_index]["vdur"][vdu_index]
1012 # TODO for the moment only first vdu_id contains a charm deployed
1013 if vdur["vdu-id-ref"] != vdu["id"]:
1014 raise LcmException("Mismatch vdur {}, vdu {} at index {} for vnf {}"
1015 .format(vdur["vdu-id-ref"], vdu["id"], vdu_index, vnf_index))
1016 vnfr_params["rw_mgmt_ip"] = vdur["ip-address"]
1017 charm_params = {
1018 "user_values": vnfr_params,
1019 "rw_mgmt_ip": vdur["ip-address"],
1020 "initial-config-primitive": vdu_config.get('initial-config-primitive') or {}
1021 }
1022 deploy_charm(vnf_index, vdu["id"], vdur.get("name"), vdur["count-index"],
1023 charm_params, n2vc_info)
1024 number_to_configure += 1
1025
1026 db_nsr_update["operational-status"] = "running"
1027 configuration_failed = False
1028 if number_to_configure:
1029 old_status = "configuring: init: {}".format(number_to_configure)
1030 db_nsr_update["config-status"] = old_status
1031 db_nsr_update["detailed-status"] = old_status
1032 db_nslcmop_update["detailed-status"] = old_status
1033
1034 # wait until all are configured.
1035 while time() <= start_deploy + self.total_deploy_timeout:
1036 if db_nsr_update:
1037 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1038 if db_nslcmop_update:
1039 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1040 # TODO add a fake task that set n2vc_event after some time
1041 await n2vc_info["n2vc_event"].wait()
1042 n2vc_info["n2vc_event"].clear()
1043 all_active = True
1044 status_map = {}
1045 n2vc_error_text = [] # contain text error list. If empty no one is in error status
1046 now = time()
1047 for vca_deployed in vca_deployed_list:
1048 vca_status = vca_deployed["operational-status"]
1049 if vca_status not in status_map:
1050 # Initialize it
1051 status_map[vca_status] = 0
1052 status_map[vca_status] += 1
1053
1054 if vca_status == "active":
1055 vca_deployed.pop("time_first_error", None)
1056 vca_deployed.pop("status_first_error", None)
1057 continue
1058
1059 all_active = False
1060 if vca_status in ("error", "blocked"):
1061 vca_deployed["detailed-status-error"] = vca_deployed["detailed-status"]
1062 # if not first time in this status error
1063 if not vca_deployed.get("time_first_error"):
1064 vca_deployed["time_first_error"] = now
1065 continue
1066 if vca_deployed.get("time_first_error") and \
1067 now <= vca_deployed["time_first_error"] + self.timeout_vca_on_error:
1068 n2vc_error_text.append("member_vnf_index={} vdu_id={} {}: {}"
1069 .format(vca_deployed["member-vnf-index"],
1070 vca_deployed["vdu_id"], vca_status,
1071 vca_deployed["detailed-status-error"]))
1072
1073 if all_active:
1074 break
1075 elif n2vc_error_text:
1076 db_nsr_update["config-status"] = "failed"
1077 error_text = "fail configuring " + ";".join(n2vc_error_text)
1078 db_nsr_update["detailed-status"] = error_text
1079 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED_TEMP"
1080 db_nslcmop_update["detailed-status"] = error_text
1081 db_nslcmop_update["statusEnteredTime"] = time()
1082 configuration_failed = True
1083 break
1084 else:
1085 cs = "configuring: "
1086 separator = ""
1087 for status, num in status_map.items():
1088 cs += separator + "{}: {}".format(status, num)
1089 separator = ", "
1090 if old_status != cs:
1091 db_nsr_update["config-status"] = cs
1092 db_nsr_update["detailed-status"] = cs
1093 db_nslcmop_update["detailed-status"] = cs
1094 old_status = cs
1095 else: # total_deploy_timeout
1096 raise LcmException("Timeout waiting ns to be configured")
1097
1098 if not configuration_failed:
1099 # all is done
1100 db_nslcmop_update["operationState"] = nslcmop_operation_state = "COMPLETED"
1101 db_nslcmop_update["statusEnteredTime"] = time()
1102 db_nslcmop_update["detailed-status"] = "done"
1103 db_nsr_update["config-status"] = "configured"
1104 db_nsr_update["detailed-status"] = "done"
1105
1106 return
1107
1108 except (ROclient.ROClientException, DbException, LcmException) as e:
1109 self.logger.error(logging_text + "Exit Exception while '{}': {}".format(step, e))
1110 exc = e
1111 except asyncio.CancelledError:
1112 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
1113 exc = "Operation was cancelled"
1114 except Exception as e:
1115 exc = traceback.format_exc()
1116 self.logger.critical(logging_text + "Exit Exception {} while '{}': {}".format(type(e).__name__, step, e),
1117 exc_info=True)
1118 finally:
1119 if exc:
1120 if db_nsr:
1121 db_nsr_update["detailed-status"] = "ERROR {}: {}".format(step, exc)
1122 db_nsr_update["operational-status"] = "failed"
1123 if db_nslcmop:
1124 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
1125 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
1126 db_nslcmop_update["statusEnteredTime"] = time()
1127 try:
1128 if db_nsr:
1129 db_nsr_update["_admin.nslcmop"] = None
1130 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1131 if db_nslcmop_update:
1132 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1133 except DbException as e:
1134 self.logger.error(logging_text + "Cannot update database: {}".format(e))
1135 if nslcmop_operation_state:
1136 try:
1137 await self.msg.aiowrite("ns", "instantiated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
1138 "operationState": nslcmop_operation_state},
1139 loop=self.loop)
1140 except Exception as e:
1141 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
1142
1143 self.logger.debug(logging_text + "Exit")
1144 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
1145
1146 async def _destroy_charm(self, model, application):
1147 """
1148 Order N2VC destroy a charm
1149 :param model:
1150 :param application:
1151 :return: True if charm does not exist. False if it exist
1152 """
1153 if not await self.n2vc.HasApplication(model, application):
1154 return True # Already removed
1155 await self.n2vc.RemoveCharms(model, application)
1156 return False
1157
1158 async def _wait_charm_destroyed(self, model, application, timeout):
1159 """
1160 Wait until charm does not exist
1161 :param model:
1162 :param application:
1163 :param timeout:
1164 :return: True if not exist, False if timeout
1165 """
1166 while True:
1167 if not await self.n2vc.HasApplication(model, application):
1168 return True
1169 if timeout < 0:
1170 return False
1171 await asyncio.sleep(10)
1172 timeout -= 10
1173
1174 async def terminate(self, nsr_id, nslcmop_id):
1175 logging_text = "Task ns={} terminate={} ".format(nsr_id, nslcmop_id)
1176 self.logger.debug(logging_text + "Enter")
1177 db_nsr = None
1178 db_nslcmop = None
1179 exc = None
1180 failed_detail = [] # annotates all failed error messages
1181 vca_time_destroy = None # time of where destroy charm order
1182 db_nsr_update = {"_admin.nslcmop": nslcmop_id}
1183 db_nslcmop_update = {}
1184 nslcmop_operation_state = None
1185 autoremove = False # autoremove after terminated
1186 try:
1187 step = "Getting nslcmop={} from db".format(nslcmop_id)
1188 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
1189 step = "Getting nsr={} from db".format(nsr_id)
1190 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1191 # nsd = db_nsr["nsd"]
1192 nsr_deployed = deepcopy(db_nsr["_admin"].get("deployed"))
1193 if db_nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
1194 return
1195 # #TODO check if VIM is creating and wait
1196 # RO_vim_id = db_vim["_admin"]["deployed"]["RO"]
1197
1198 db_nsr_update["operational-status"] = "terminating"
1199 db_nsr_update["config-status"] = "terminating"
1200
1201 if nsr_deployed and nsr_deployed.get("VCA-model-name"):
1202 vca_model_name = nsr_deployed["VCA-model-name"]
1203 step = "deleting VCA model name '{}' and all charms".format(vca_model_name)
1204 self.logger.debug(logging_text + step)
1205 try:
1206 await self.n2vc.DestroyNetworkService(vca_model_name)
1207 except NetworkServiceDoesNotExist:
1208 pass
1209 db_nsr_update["_admin.deployed.VCA-model-name"] = None
1210 if nsr_deployed.get("VCA"):
1211 for vca_index in range(0, len(nsr_deployed["VCA"])):
1212 db_nsr_update["_admin.deployed.VCA.{}".format(vca_index)] = None
1213 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1214 # for backward compatibility if charm have been created with "default" model name delete one by one
1215 elif nsr_deployed and nsr_deployed.get("VCA"):
1216 try:
1217 step = "Scheduling configuration charms removing"
1218 db_nsr_update["detailed-status"] = "Deleting charms"
1219 self.logger.debug(logging_text + step)
1220 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1221 # for backward compatibility
1222 if isinstance(nsr_deployed["VCA"], dict):
1223 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
1224 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
1225 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1226
1227 for vca_index, vca_deployed in enumerate(nsr_deployed["VCA"]):
1228 if vca_deployed:
1229 if await self._destroy_charm(vca_deployed['model'], vca_deployed["application"]):
1230 vca_deployed.clear()
1231 db_nsr["_admin.deployed.VCA.{}".format(vca_index)] = None
1232 else:
1233 vca_time_destroy = time()
1234 except Exception as e:
1235 self.logger.debug(logging_text + "Failed while deleting charms: {}".format(e))
1236
1237 # remove from RO
1238 RO_fail = False
1239 RO = ROclient.ROClient(self.loop, **self.ro_config)
1240
1241 # Delete ns
1242 RO_nsr_id = RO_delete_action = None
1243 if nsr_deployed and nsr_deployed.get("RO"):
1244 RO_nsr_id = nsr_deployed["RO"].get("nsr_id")
1245 RO_delete_action = nsr_deployed["RO"].get("nsr_delete_action_id")
1246 try:
1247 if RO_nsr_id:
1248 step = db_nsr_update["detailed-status"] = db_nslcmop_update["detailed-status"] = "Deleting ns at RO"
1249 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1250 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1251 self.logger.debug(logging_text + step)
1252 desc = await RO.delete("ns", RO_nsr_id)
1253 RO_delete_action = desc["action_id"]
1254 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = RO_delete_action
1255 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
1256 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
1257 if RO_delete_action:
1258 # wait until NS is deleted from VIM
1259 step = detailed_status = "Waiting ns deleted from VIM. RO_id={} RO_delete_action={}".\
1260 format(RO_nsr_id, RO_delete_action)
1261 detailed_status_old = None
1262 self.logger.debug(logging_text + step)
1263
1264 delete_timeout = 20 * 60 # 20 minutes
1265 while delete_timeout > 0:
1266 desc = await RO.show("ns", item_id_name=RO_nsr_id, extra_item="action",
1267 extra_item_id=RO_delete_action)
1268 ns_status, ns_status_info = RO.check_action_status(desc)
1269 if ns_status == "ERROR":
1270 raise ROclient.ROClientException(ns_status_info)
1271 elif ns_status == "BUILD":
1272 detailed_status = step + "; {}".format(ns_status_info)
1273 elif ns_status == "ACTIVE":
1274 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
1275 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
1276 break
1277 else:
1278 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
1279 if detailed_status != detailed_status_old:
1280 detailed_status_old = db_nslcmop_update["detailed-status"] = \
1281 db_nsr_update["detailed-status"] = detailed_status
1282 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1283 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1284 await asyncio.sleep(5, loop=self.loop)
1285 delete_timeout -= 5
1286 else: # delete_timeout <= 0:
1287 raise ROclient.ROClientException("Timeout waiting ns deleted from VIM")
1288
1289 except ROclient.ROClientException as e:
1290 if e.http_code == 404: # not found
1291 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
1292 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
1293 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
1294 self.logger.debug(logging_text + "RO_ns_id={} already deleted".format(RO_nsr_id))
1295 elif e.http_code == 409: # conflict
1296 failed_detail.append("RO_ns_id={} delete conflict: {}".format(RO_nsr_id, e))
1297 self.logger.debug(logging_text + failed_detail[-1])
1298 RO_fail = True
1299 else:
1300 failed_detail.append("RO_ns_id={} delete error: {}".format(RO_nsr_id, e))
1301 self.logger.error(logging_text + failed_detail[-1])
1302 RO_fail = True
1303
1304 # Delete nsd
1305 if not RO_fail and nsr_deployed and nsr_deployed.get("RO") and nsr_deployed["RO"].get("nsd_id"):
1306 RO_nsd_id = nsr_deployed["RO"]["nsd_id"]
1307 try:
1308 step = db_nsr_update["detailed-status"] = db_nslcmop_update["detailed-status"] =\
1309 "Deleting nsd at RO"
1310 await RO.delete("nsd", RO_nsd_id)
1311 self.logger.debug(logging_text + "RO_nsd_id={} deleted".format(RO_nsd_id))
1312 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
1313 except ROclient.ROClientException as e:
1314 if e.http_code == 404: # not found
1315 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
1316 self.logger.debug(logging_text + "RO_nsd_id={} already deleted".format(RO_nsd_id))
1317 elif e.http_code == 409: # conflict
1318 failed_detail.append("RO_nsd_id={} delete conflict: {}".format(RO_nsd_id, e))
1319 self.logger.debug(logging_text + failed_detail[-1])
1320 RO_fail = True
1321 else:
1322 failed_detail.append("RO_nsd_id={} delete error: {}".format(RO_nsd_id, e))
1323 self.logger.error(logging_text + failed_detail[-1])
1324 RO_fail = True
1325
1326 if not RO_fail and nsr_deployed and nsr_deployed.get("RO") and nsr_deployed["RO"].get("vnfd"):
1327 for index, vnf_deployed in enumerate(nsr_deployed["RO"]["vnfd"]):
1328 if not vnf_deployed or not vnf_deployed["id"]:
1329 continue
1330 try:
1331 RO_vnfd_id = vnf_deployed["id"]
1332 step = db_nsr_update["detailed-status"] = db_nslcmop_update["detailed-status"] =\
1333 "Deleting member-vnf-index={} RO_vnfd_id={} from RO".format(
1334 vnf_deployed["member-vnf-index"], RO_vnfd_id)
1335 await RO.delete("vnfd", RO_vnfd_id)
1336 self.logger.debug(logging_text + "RO_vnfd_id={} deleted".format(RO_vnfd_id))
1337 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
1338 except ROclient.ROClientException as e:
1339 if e.http_code == 404: # not found
1340 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
1341 self.logger.debug(logging_text + "RO_vnfd_id={} already deleted ".format(RO_vnfd_id))
1342 elif e.http_code == 409: # conflict
1343 failed_detail.append("RO_vnfd_id={} delete conflict: {}".format(RO_vnfd_id, e))
1344 self.logger.debug(logging_text + failed_detail[-1])
1345 else:
1346 failed_detail.append("RO_vnfd_id={} delete error: {}".format(RO_vnfd_id, e))
1347 self.logger.error(logging_text + failed_detail[-1])
1348
1349 # wait until charm deleted
1350 if vca_time_destroy:
1351 db_nsr_update["detailed-status"] = db_nslcmop_update["detailed-status"] = step = \
1352 "Waiting for deletion of configuration charms"
1353 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1354 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1355 for vca_index, vca_deployed in enumerate(nsr_deployed["VCA"]):
1356 if not vca_deployed:
1357 continue
1358 step = "Waiting for deletion of charm application_name={}".format(vca_deployed["application"])
1359 timeout = self.timeout_charm_delete - int(time() - vca_time_destroy)
1360 if not await self._wait_charm_destroyed(vca_deployed['model'], vca_deployed["application"],
1361 timeout):
1362 failed_detail.append("VCA[application_name={}] Deletion timeout".format(
1363 vca_deployed["application"]))
1364 else:
1365 db_nsr["_admin.deployed.VCA.{}".format(vca_index)] = None
1366
1367 if failed_detail:
1368 self.logger.error(logging_text + " ;".join(failed_detail))
1369 db_nsr_update["operational-status"] = "failed"
1370 db_nsr_update["detailed-status"] = "Deletion errors " + "; ".join(failed_detail)
1371 db_nslcmop_update["detailed-status"] = "; ".join(failed_detail)
1372 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
1373 db_nslcmop_update["statusEnteredTime"] = time()
1374 else:
1375 db_nsr_update["operational-status"] = "terminated"
1376 db_nsr_update["detailed-status"] = "Done"
1377 db_nsr_update["_admin.nsState"] = "NOT_INSTANTIATED"
1378 db_nslcmop_update["detailed-status"] = "Done"
1379 db_nslcmop_update["operationState"] = nslcmop_operation_state = "COMPLETED"
1380 db_nslcmop_update["statusEnteredTime"] = time()
1381 if db_nslcmop["operationParams"].get("autoremove"):
1382 autoremove = True
1383
1384 except (ROclient.ROClientException, DbException) as e:
1385 self.logger.error(logging_text + "Exit Exception {}".format(e))
1386 exc = e
1387 except asyncio.CancelledError:
1388 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
1389 exc = "Operation was cancelled"
1390 except Exception as e:
1391 exc = traceback.format_exc()
1392 self.logger.critical(logging_text + "Exit Exception {}".format(e), exc_info=True)
1393 finally:
1394 if exc and db_nslcmop:
1395 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
1396 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
1397 db_nslcmop_update["statusEnteredTime"] = time()
1398 try:
1399 if db_nslcmop and db_nslcmop_update:
1400 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1401 if db_nsr:
1402 db_nsr_update["_admin.nslcmop"] = None
1403 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1404 except DbException as e:
1405 self.logger.error(logging_text + "Cannot update database: {}".format(e))
1406 if nslcmop_operation_state:
1407 try:
1408 await self.msg.aiowrite("ns", "terminated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
1409 "operationState": nslcmop_operation_state,
1410 "autoremove": autoremove},
1411 loop=self.loop)
1412 except Exception as e:
1413 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
1414 self.logger.debug(logging_text + "Exit")
1415 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_terminate")
1416
1417 @staticmethod
1418 def _map_primitive_params(primitive_desc, params, instantiation_params):
1419 """
1420 Generates the params to be provided to charm before executing primitive. If user does not provide a parameter,
1421 The default-value is used. If it is between < > it look for a value at instantiation_params
1422 :param primitive_desc: portion of VNFD/NSD that describes primitive
1423 :param params: Params provided by user
1424 :param instantiation_params: Instantiation params provided by user
1425 :return: a dictionary with the calculated params
1426 """
1427 calculated_params = {}
1428 for parameter in primitive_desc.get("parameter", ()):
1429 param_name = parameter["name"]
1430 if param_name in params:
1431 calculated_params[param_name] = params[param_name]
1432 elif "default-value" in parameter:
1433 calculated_params[param_name] = parameter["default-value"]
1434 if isinstance(parameter["default-value"], str) and parameter["default-value"].startswith("<") and \
1435 parameter["default-value"].endswith(">"):
1436 if parameter["default-value"][1:-1] in instantiation_params:
1437 calculated_params[param_name] = instantiation_params[parameter["default-value"][1:-1]]
1438 else:
1439 raise LcmException("Parameter {} needed to execute primitive {} not provided".
1440 format(parameter["default-value"], primitive_desc["name"]))
1441 else:
1442 raise LcmException("Parameter {} needed to execute primitive {} not provided".
1443 format(param_name, primitive_desc["name"]))
1444
1445 if isinstance(calculated_params[param_name], (dict, list, tuple)):
1446 calculated_params[param_name] = yaml.safe_dump(calculated_params[param_name], default_flow_style=True,
1447 width=256)
1448 elif isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("!!yaml "):
1449 calculated_params[param_name] = calculated_params[param_name][7:]
1450 return calculated_params
1451
1452 async def _ns_execute_primitive(self, db_deployed, member_vnf_index, vdu_id, vdu_name, vdu_count_index,
1453 primitive, primitive_params):
1454 start_primitive_time = time()
1455 try:
1456 for vca_deployed in db_deployed["VCA"]:
1457 if not vca_deployed:
1458 continue
1459 if member_vnf_index != vca_deployed["member-vnf-index"] or vdu_id != vca_deployed["vdu_id"]:
1460 continue
1461 if vdu_name and vdu_name != vca_deployed["vdu_name"]:
1462 continue
1463 if vdu_count_index and vdu_count_index != vca_deployed["vdu_count_index"]:
1464 continue
1465 break
1466 else:
1467 raise LcmException("charm for member_vnf_index={} vdu_id={} vdu_name={} vdu_count_index={} is not "
1468 "deployed".format(member_vnf_index, vdu_id, vdu_name, vdu_count_index))
1469 model_name = vca_deployed.get("model")
1470 application_name = vca_deployed.get("application")
1471 if not model_name or not application_name:
1472 raise LcmException("charm for member_vnf_index={} vdu_id={} vdu_name={} vdu_count_index={} has not "
1473 "model or application name" .format(member_vnf_index, vdu_id, vdu_name,
1474 vdu_count_index))
1475 if vca_deployed["operational-status"] != "active":
1476 raise LcmException("charm for member_vnf_index={} vdu_id={} operational_status={} not 'active'".format(
1477 member_vnf_index, vdu_id, vca_deployed["operational-status"]))
1478 callback = None # self.n2vc_callback
1479 callback_args = () # [db_nsr, db_nslcmop, member_vnf_index, None]
1480 await self.n2vc.login()
1481 primitive_id = await self.n2vc.ExecutePrimitive(
1482 model_name,
1483 application_name,
1484 primitive,
1485 callback,
1486 *callback_args,
1487 **primitive_params
1488 )
1489 while time() - start_primitive_time < self.timeout_primitive:
1490 primitive_result_ = await self.n2vc.GetPrimitiveStatus(model_name, primitive_id)
1491 if primitive_result_ in ("running", "pending"):
1492 pass
1493 elif primitive_result_ in ("completed", "failed"):
1494 primitive_result = "COMPLETED" if primitive_result_ == "completed" else "FAILED"
1495 detailed_result = await self.n2vc.GetPrimitiveOutput(model_name, primitive_id)
1496 break
1497 else:
1498 detailed_result = "Invalid N2VC.GetPrimitiveStatus = {} obtained".format(primitive_result_)
1499 primitive_result = "FAILED"
1500 break
1501 await asyncio.sleep(5)
1502 else:
1503 raise LcmException("timeout after {} seconds".format(self.timeout_primitive))
1504 return primitive_result, detailed_result
1505 except (N2VCPrimitiveExecutionFailed, LcmException) as e:
1506 return "FAILED", str(e)
1507
1508 async def action(self, nsr_id, nslcmop_id):
1509 logging_text = "Task ns={} action={} ".format(nsr_id, nslcmop_id)
1510 self.logger.debug(logging_text + "Enter")
1511 # get all needed from database
1512 db_nsr = None
1513 db_nslcmop = None
1514 db_nsr_update = {"_admin.nslcmop": nslcmop_id}
1515 db_nslcmop_update = {}
1516 nslcmop_operation_state = None
1517 exc = None
1518 try:
1519 step = "Getting information from database"
1520 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
1521 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1522
1523 nsr_deployed = db_nsr["_admin"].get("deployed")
1524 vnf_index = db_nslcmop["operationParams"]["member_vnf_index"]
1525 vdu_id = db_nslcmop["operationParams"].get("vdu_id")
1526 vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
1527 vdu_name = db_nslcmop["operationParams"].get("vdu_name")
1528
1529 step = "Getting vnfr from database"
1530 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
1531 step = "Getting vnfd from database"
1532 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
1533
1534 # look if previous tasks in process
1535 task_name, task_dependency = self.lcm_tasks.lookfor_related("ns", nsr_id, nslcmop_id)
1536 if task_dependency:
1537 step = db_nslcmop_update["detailed-status"] = \
1538 "Waiting for related tasks to be completed: {}".format(task_name)
1539 self.logger.debug(logging_text + step)
1540 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1541 _, pending = await asyncio.wait(task_dependency, timeout=3600)
1542 if pending:
1543 raise LcmException("Timeout waiting related tasks to be completed")
1544
1545 # for backward compatibility
1546 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
1547 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
1548 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
1549 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1550
1551 primitive = db_nslcmop["operationParams"]["primitive"]
1552 primitive_params = db_nslcmop["operationParams"]["primitive_params"]
1553
1554 # look for primitive
1555 config_primitive_desc = None
1556 if vdu_id:
1557 for vdu in get_iterable(db_vnfd, "vdu"):
1558 if vdu_id == vdu["id"]:
1559 for config_primitive in vdu.get("vdu-configuration", {}).get("config-primitive", ()):
1560 if config_primitive["name"] == primitive:
1561 config_primitive_desc = config_primitive
1562 break
1563 for config_primitive in db_vnfd.get("vnf-configuration", {}).get("config-primitive", ()):
1564 if config_primitive["name"] == primitive:
1565 config_primitive_desc = config_primitive
1566 break
1567 if not config_primitive_desc:
1568 raise LcmException("Primitive {} not found at vnf-configuration:config-primitive or vdu:"
1569 "vdu-configuration:config-primitive".format(primitive))
1570
1571 vnfr_params = {}
1572 if db_vnfr.get("additionalParamsForVnf"):
1573 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
1574
1575 # TODO check if ns is in a proper status
1576 result, result_detail = await self._ns_execute_primitive(
1577 nsr_deployed, vnf_index, vdu_id, vdu_name, vdu_count_index, primitive,
1578 self._map_primitive_params(config_primitive_desc, primitive_params, vnfr_params))
1579 db_nslcmop_update["detailed-status"] = result_detail
1580 db_nslcmop_update["operationState"] = nslcmop_operation_state = result
1581 db_nslcmop_update["statusEnteredTime"] = time()
1582 self.logger.debug(logging_text + " task Done with result {} {}".format(result, result_detail))
1583 return # database update is called inside finally
1584
1585 except (DbException, LcmException) as e:
1586 self.logger.error(logging_text + "Exit Exception {}".format(e))
1587 exc = e
1588 except asyncio.CancelledError:
1589 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
1590 exc = "Operation was cancelled"
1591 except Exception as e:
1592 exc = traceback.format_exc()
1593 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
1594 finally:
1595 if exc and db_nslcmop:
1596 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
1597 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
1598 db_nslcmop_update["statusEnteredTime"] = time()
1599 try:
1600 if db_nslcmop_update:
1601 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1602 if db_nsr:
1603 db_nsr_update["_admin.nslcmop"] = None
1604 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1605 except DbException as e:
1606 self.logger.error(logging_text + "Cannot update database: {}".format(e))
1607 self.logger.debug(logging_text + "Exit")
1608 if nslcmop_operation_state:
1609 try:
1610 await self.msg.aiowrite("ns", "actioned", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
1611 "operationState": nslcmop_operation_state},
1612 loop=self.loop)
1613 except Exception as e:
1614 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
1615 self.logger.debug(logging_text + "Exit")
1616 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_action")
1617
1618 async def scale(self, nsr_id, nslcmop_id):
1619 logging_text = "Task ns={} scale={} ".format(nsr_id, nslcmop_id)
1620 self.logger.debug(logging_text + "Enter")
1621 # get all needed from database
1622 db_nsr = None
1623 db_nslcmop = None
1624 db_nslcmop_update = {}
1625 nslcmop_operation_state = None
1626 db_nsr_update = {"_admin.nslcmop": nslcmop_id}
1627 exc = None
1628 # in case of error, indicates what part of scale was failed to put nsr at error status
1629 scale_process = None
1630 old_operational_status = ""
1631 old_config_status = ""
1632 vnfr_scaled = False
1633 try:
1634 step = "Getting nslcmop from database"
1635 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
1636 step = "Getting nsr from database"
1637 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1638
1639 old_operational_status = db_nsr["operational-status"]
1640 old_config_status = db_nsr["config-status"]
1641
1642 # look if previous tasks in process
1643 task_name, task_dependency = self.lcm_tasks.lookfor_related("ns", nsr_id, nslcmop_id)
1644 if task_dependency:
1645 step = db_nslcmop_update["detailed-status"] = \
1646 "Waiting for related tasks to be completed: {}".format(task_name)
1647 self.logger.debug(logging_text + step)
1648 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1649 _, pending = await asyncio.wait(task_dependency, timeout=3600)
1650 if pending:
1651 raise LcmException("Timeout waiting related tasks to be completed")
1652
1653 step = "Parsing scaling parameters"
1654 db_nsr_update["operational-status"] = "scaling"
1655 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1656 nsr_deployed = db_nsr["_admin"].get("deployed")
1657 RO_nsr_id = nsr_deployed["RO"]["nsr_id"]
1658 vnf_index = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["member-vnf-index"]
1659 scaling_group = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]
1660 scaling_type = db_nslcmop["operationParams"]["scaleVnfData"]["scaleVnfType"]
1661 # scaling_policy = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"].get("scaling-policy")
1662
1663 # for backward compatibility
1664 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
1665 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
1666 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
1667 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1668
1669 step = "Getting vnfr from database"
1670 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
1671 step = "Getting vnfd from database"
1672 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
1673 step = "Getting scaling-group-descriptor"
1674 for scaling_descriptor in db_vnfd["scaling-group-descriptor"]:
1675 if scaling_descriptor["name"] == scaling_group:
1676 break
1677 else:
1678 raise LcmException("input parameter 'scaleByStepData':'scaling-group-descriptor':'{}' is not present "
1679 "at vnfd:scaling-group-descriptor".format(scaling_group))
1680 # cooldown_time = 0
1681 # for scaling_policy_descriptor in scaling_descriptor.get("scaling-policy", ()):
1682 # cooldown_time = scaling_policy_descriptor.get("cooldown-time", 0)
1683 # if scaling_policy and scaling_policy == scaling_policy_descriptor.get("name"):
1684 # break
1685
1686 # TODO check if ns is in a proper status
1687 step = "Sending scale order to RO"
1688 nb_scale_op = 0
1689 if not db_nsr["_admin"].get("scaling-group"):
1690 self.update_db_2("nsrs", nsr_id, {"_admin.scaling-group": [{"name": scaling_group, "nb-scale-op": 0}]})
1691 admin_scale_index = 0
1692 else:
1693 for admin_scale_index, admin_scale_info in enumerate(db_nsr["_admin"]["scaling-group"]):
1694 if admin_scale_info["name"] == scaling_group:
1695 nb_scale_op = admin_scale_info.get("nb-scale-op", 0)
1696 break
1697 else: # not found, set index one plus last element and add new entry with the name
1698 admin_scale_index += 1
1699 db_nsr_update["_admin.scaling-group.{}.name".format(admin_scale_index)] = scaling_group
1700 RO_scaling_info = []
1701 vdu_scaling_info = {"scaling_group_name": scaling_group, "vdu": []}
1702 if scaling_type == "SCALE_OUT":
1703 # count if max-instance-count is reached
1704 if "max-instance-count" in scaling_descriptor and scaling_descriptor["max-instance-count"] is not None:
1705 max_instance_count = int(scaling_descriptor["max-instance-count"])
1706 if nb_scale_op >= max_instance_count:
1707 raise LcmException("reached the limit of {} (max-instance-count) scaling-out operations for the"
1708 " scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group))
1709 nb_scale_op = nb_scale_op + 1
1710 vdu_scaling_info["scaling_direction"] = "OUT"
1711 vdu_scaling_info["vdu-create"] = {}
1712 for vdu_scale_info in scaling_descriptor["vdu"]:
1713 RO_scaling_info.append({"osm_vdu_id": vdu_scale_info["vdu-id-ref"], "member-vnf-index": vnf_index,
1714 "type": "create", "count": vdu_scale_info.get("count", 1)})
1715 vdu_scaling_info["vdu-create"][vdu_scale_info["vdu-id-ref"]] = vdu_scale_info.get("count", 1)
1716 elif scaling_type == "SCALE_IN":
1717 # count if min-instance-count is reached
1718 min_instance_count = 0
1719 if "min-instance-count" in scaling_descriptor and scaling_descriptor["min-instance-count"] is not None:
1720 min_instance_count = int(scaling_descriptor["min-instance-count"])
1721 if nb_scale_op <= min_instance_count:
1722 raise LcmException("reached the limit of {} (min-instance-count) scaling-in operations for the "
1723 "scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group))
1724 nb_scale_op = nb_scale_op - 1
1725 vdu_scaling_info["scaling_direction"] = "IN"
1726 vdu_scaling_info["vdu-delete"] = {}
1727 for vdu_scale_info in scaling_descriptor["vdu"]:
1728 RO_scaling_info.append({"osm_vdu_id": vdu_scale_info["vdu-id-ref"], "member-vnf-index": vnf_index,
1729 "type": "delete", "count": vdu_scale_info.get("count", 1)})
1730 vdu_scaling_info["vdu-delete"][vdu_scale_info["vdu-id-ref"]] = vdu_scale_info.get("count", 1)
1731
1732 # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
1733 vdu_create = vdu_scaling_info.get("vdu-create")
1734 vdu_delete = copy(vdu_scaling_info.get("vdu-delete"))
1735 if vdu_scaling_info["scaling_direction"] == "IN":
1736 for vdur in reversed(db_vnfr["vdur"]):
1737 if vdu_delete.get(vdur["vdu-id-ref"]):
1738 vdu_delete[vdur["vdu-id-ref"]] -= 1
1739 vdu_scaling_info["vdu"].append({
1740 "name": vdur["name"],
1741 "vdu_id": vdur["vdu-id-ref"],
1742 "interface": []
1743 })
1744 for interface in vdur["interfaces"]:
1745 vdu_scaling_info["vdu"][-1]["interface"].append({
1746 "name": interface["name"],
1747 "ip_address": interface["ip-address"],
1748 "mac_address": interface.get("mac-address"),
1749 })
1750 vdu_delete = vdu_scaling_info.pop("vdu-delete")
1751
1752 # execute primitive service PRE-SCALING
1753 step = "Executing pre-scale vnf-config-primitive"
1754 if scaling_descriptor.get("scaling-config-action"):
1755 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
1756 if scaling_config_action.get("trigger") and scaling_config_action["trigger"] == "pre-scale-in" \
1757 and scaling_type == "SCALE_IN":
1758 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
1759 step = db_nslcmop_update["detailed-status"] = \
1760 "executing pre-scale scaling-config-action '{}'".format(vnf_config_primitive)
1761
1762 # look for primitive
1763 for config_primitive in db_vnfd.get("vnf-configuration", {}).get("config-primitive", ()):
1764 if config_primitive["name"] == vnf_config_primitive:
1765 break
1766 else:
1767 raise LcmException(
1768 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
1769 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
1770 "primitive".format(scaling_group, config_primitive))
1771
1772 vnfr_params = {"<VDU_SCALE_INFO>": vdu_scaling_info}
1773 if db_vnfr.get("additionalParamsForVnf"):
1774 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
1775
1776 scale_process = "VCA"
1777 db_nsr_update["config-status"] = "configuring pre-scaling"
1778 result, result_detail = await self._ns_execute_primitive(
1779 nsr_deployed, vnf_index, None, None, None, vnf_config_primitive,
1780 self._map_primitive_params(config_primitive, {}, vnfr_params))
1781 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
1782 vnf_config_primitive, result, result_detail))
1783 if result == "FAILED":
1784 raise LcmException(result_detail)
1785 db_nsr_update["config-status"] = old_config_status
1786 scale_process = None
1787
1788 if RO_scaling_info:
1789 scale_process = "RO"
1790 RO = ROclient.ROClient(self.loop, **self.ro_config)
1791 RO_desc = await RO.create_action("ns", RO_nsr_id, {"vdu-scaling": RO_scaling_info})
1792 db_nsr_update["_admin.scaling-group.{}.nb-scale-op".format(admin_scale_index)] = nb_scale_op
1793 db_nsr_update["_admin.scaling-group.{}.time".format(admin_scale_index)] = time()
1794 # wait until ready
1795 RO_nslcmop_id = RO_desc["instance_action_id"]
1796 db_nslcmop_update["_admin.deploy.RO"] = RO_nslcmop_id
1797
1798 RO_task_done = False
1799 step = detailed_status = "Waiting RO_task_id={} to complete the scale action.".format(RO_nslcmop_id)
1800 detailed_status_old = None
1801 self.logger.debug(logging_text + step)
1802
1803 deployment_timeout = 1 * 3600 # One hour
1804 while deployment_timeout > 0:
1805 if not RO_task_done:
1806 desc = await RO.show("ns", item_id_name=RO_nsr_id, extra_item="action",
1807 extra_item_id=RO_nslcmop_id)
1808 ns_status, ns_status_info = RO.check_action_status(desc)
1809 if ns_status == "ERROR":
1810 raise ROclient.ROClientException(ns_status_info)
1811 elif ns_status == "BUILD":
1812 detailed_status = step + "; {}".format(ns_status_info)
1813 elif ns_status == "ACTIVE":
1814 RO_task_done = True
1815 step = detailed_status = "Waiting ns ready at RO. RO_id={}".format(RO_nsr_id)
1816 self.logger.debug(logging_text + step)
1817 else:
1818 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
1819 else:
1820 desc = await RO.show("ns", RO_nsr_id)
1821 ns_status, ns_status_info = RO.check_ns_status(desc)
1822 if ns_status == "ERROR":
1823 raise ROclient.ROClientException(ns_status_info)
1824 elif ns_status == "BUILD":
1825 detailed_status = step + "; {}".format(ns_status_info)
1826 elif ns_status == "ACTIVE":
1827 step = detailed_status = \
1828 "Waiting for management IP address reported by the VIM. Updating VNFRs"
1829 if not vnfr_scaled:
1830 self.scale_vnfr(db_vnfr, vdu_create=vdu_create, vdu_delete=vdu_delete)
1831 vnfr_scaled = True
1832 try:
1833 desc = await RO.show("ns", RO_nsr_id)
1834 # nsr_deployed["nsr_ip"] = RO.get_ns_vnf_info(desc)
1835 self.ns_update_vnfr({db_vnfr["member-vnf-index-ref"]: db_vnfr}, desc)
1836 break
1837 except LcmExceptionNoMgmtIP:
1838 pass
1839 else:
1840 assert False, "ROclient.check_ns_status returns unknown {}".format(ns_status)
1841 if detailed_status != detailed_status_old:
1842 detailed_status_old = db_nslcmop_update["detailed-status"] = detailed_status
1843 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1844
1845 await asyncio.sleep(5, loop=self.loop)
1846 deployment_timeout -= 5
1847 if deployment_timeout <= 0:
1848 raise ROclient.ROClientException("Timeout waiting ns to be ready")
1849
1850 # update VDU_SCALING_INFO with the obtained ip_addresses
1851 if vdu_scaling_info["scaling_direction"] == "OUT":
1852 for vdur in reversed(db_vnfr["vdur"]):
1853 if vdu_scaling_info["vdu-create"].get(vdur["vdu-id-ref"]):
1854 vdu_scaling_info["vdu-create"][vdur["vdu-id-ref"]] -= 1
1855 vdu_scaling_info["vdu"].append({
1856 "name": vdur["name"],
1857 "vdu_id": vdur["vdu-id-ref"],
1858 "interface": []
1859 })
1860 for interface in vdur["interfaces"]:
1861 vdu_scaling_info["vdu"][-1]["interface"].append({
1862 "name": interface["name"],
1863 "ip_address": interface["ip-address"],
1864 "mac_address": interface.get("mac-address"),
1865 })
1866 del vdu_scaling_info["vdu-create"]
1867
1868 scale_process = None
1869 if db_nsr_update:
1870 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1871
1872 # execute primitive service POST-SCALING
1873 step = "Executing post-scale vnf-config-primitive"
1874 if scaling_descriptor.get("scaling-config-action"):
1875 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
1876 if scaling_config_action.get("trigger") and scaling_config_action["trigger"] == "post-scale-out" \
1877 and scaling_type == "SCALE_OUT":
1878 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
1879 step = db_nslcmop_update["detailed-status"] = \
1880 "executing post-scale scaling-config-action '{}'".format(vnf_config_primitive)
1881
1882 vnfr_params = {"<VDU_SCALE_INFO>": vdu_scaling_info}
1883 if db_vnfr.get("additionalParamsForVnf"):
1884 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
1885
1886 # look for primitive
1887 for config_primitive in db_vnfd.get("vnf-configuration", {}).get("config-primitive", ()):
1888 if config_primitive["name"] == vnf_config_primitive:
1889 break
1890 else:
1891 raise LcmException("Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:"
1892 "scaling-config-action[vnf-config-primitive-name-ref='{}'] does not "
1893 "match any vnf-configuration:config-primitive".format(scaling_group,
1894 config_primitive))
1895 scale_process = "VCA"
1896 db_nsr_update["config-status"] = "configuring post-scaling"
1897
1898 result, result_detail = await self._ns_execute_primitive(
1899 nsr_deployed, vnf_index, None, None, None, vnf_config_primitive,
1900 self._map_primitive_params(config_primitive, {}, vnfr_params))
1901 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
1902 vnf_config_primitive, result, result_detail))
1903 if result == "FAILED":
1904 raise LcmException(result_detail)
1905 db_nsr_update["config-status"] = old_config_status
1906 scale_process = None
1907
1908 db_nslcmop_update["operationState"] = nslcmop_operation_state = "COMPLETED"
1909 db_nslcmop_update["statusEnteredTime"] = time()
1910 db_nslcmop_update["detailed-status"] = "done"
1911 db_nsr_update["detailed-status"] = "" # "scaled {} {}".format(scaling_group, scaling_type)
1912 db_nsr_update["operational-status"] = old_operational_status
1913 db_nsr_update["config-status"] = old_config_status
1914 return
1915 except (ROclient.ROClientException, DbException, LcmException) as e:
1916 self.logger.error(logging_text + "Exit Exception {}".format(e))
1917 exc = e
1918 except asyncio.CancelledError:
1919 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
1920 exc = "Operation was cancelled"
1921 except Exception as e:
1922 exc = traceback.format_exc()
1923 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
1924 finally:
1925 if exc:
1926 if db_nslcmop:
1927 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
1928 db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
1929 db_nslcmop_update["statusEnteredTime"] = time()
1930 if db_nsr:
1931 db_nsr_update["operational-status"] = old_operational_status
1932 db_nsr_update["config-status"] = old_config_status
1933 db_nsr_update["detailed-status"] = ""
1934 db_nsr_update["_admin.nslcmop"] = None
1935 if scale_process:
1936 if "VCA" in scale_process:
1937 db_nsr_update["config-status"] = "failed"
1938 if "RO" in scale_process:
1939 db_nsr_update["operational-status"] = "failed"
1940 db_nsr_update["detailed-status"] = "FAILED scaling nslcmop={} {}: {}".format(nslcmop_id, step,
1941 exc)
1942 try:
1943 if db_nslcmop and db_nslcmop_update:
1944 self.update_db_2("nslcmops", nslcmop_id, db_nslcmop_update)
1945 if db_nsr:
1946 db_nsr_update["_admin.nslcmop"] = None
1947 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1948 except DbException as e:
1949 self.logger.error(logging_text + "Cannot update database: {}".format(e))
1950 if nslcmop_operation_state:
1951 try:
1952 await self.msg.aiowrite("ns", "scaled", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
1953 "operationState": nslcmop_operation_state},
1954 loop=self.loop)
1955 # if cooldown_time:
1956 # await asyncio.sleep(cooldown_time)
1957 # await self.msg.aiowrite("ns","scaled-cooldown-time", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id})
1958 except Exception as e:
1959 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
1960 self.logger.debug(logging_text + "Exit")
1961 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")