fda67221b268f0a7c8b3037305ebccbe3d17174a
[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 shutil
21 from typing import Any, Dict, List
22 import yaml
23 import logging
24 import logging.handlers
25 import traceback
26 import json
27 from jinja2 import (
28 Environment,
29 TemplateError,
30 TemplateNotFound,
31 StrictUndefined,
32 UndefinedError,
33 select_autoescape,
34 )
35
36 from osm_lcm import ROclient
37 from osm_lcm.data_utils.lcm_config import LcmCfg
38 from osm_lcm.data_utils.nsr import (
39 get_deployed_kdu,
40 get_deployed_vca,
41 get_deployed_vca_list,
42 get_nsd,
43 )
44 from osm_lcm.data_utils.vca import (
45 DeployedComponent,
46 DeployedK8sResource,
47 DeployedVCA,
48 EELevel,
49 Relation,
50 EERelation,
51 safe_get_ee_relation,
52 )
53 from osm_lcm.ng_ro import NgRoClient, NgRoException
54 from osm_lcm.lcm_utils import (
55 LcmException,
56 LcmExceptionNoMgmtIP,
57 LcmBase,
58 deep_get,
59 get_iterable,
60 populate_dict,
61 check_juju_bundle_existence,
62 get_charm_artifact_path,
63 get_ee_id_parts,
64 vld_to_ro_ip_profile,
65 )
66 from osm_lcm.data_utils.nsd import (
67 get_ns_configuration_relation_list,
68 get_vnf_profile,
69 get_vnf_profiles,
70 )
71 from osm_lcm.data_utils.vnfd import (
72 get_kdu,
73 get_kdu_services,
74 get_relation_list,
75 get_vdu_list,
76 get_vdu_profile,
77 get_ee_sorted_initial_config_primitive_list,
78 get_ee_sorted_terminate_config_primitive_list,
79 get_kdu_list,
80 get_virtual_link_profiles,
81 get_vdu,
82 get_configuration,
83 get_vdu_index,
84 get_scaling_aspect,
85 get_number_of_instances,
86 get_juju_ee_ref,
87 get_kdu_resource_profile,
88 find_software_version,
89 check_helm_ee_in_ns,
90 )
91 from osm_lcm.data_utils.list_utils import find_in_list
92 from osm_lcm.data_utils.vnfr import (
93 get_osm_params,
94 get_vdur_index,
95 get_kdur,
96 get_volumes_from_instantiation_params,
97 )
98 from osm_lcm.data_utils.dict_utils import parse_yaml_strings
99 from osm_lcm.data_utils.database.vim_account import VimAccountDB
100 from n2vc.definitions import RelationEndpoint
101 from n2vc.k8s_helm_conn import K8sHelmConnector
102 from n2vc.k8s_helm3_conn import K8sHelm3Connector
103 from n2vc.k8s_juju_conn import K8sJujuConnector
104
105 from osm_common.dbbase import DbException
106 from osm_common.fsbase import FsException
107
108 from osm_lcm.data_utils.database.database import Database
109 from osm_lcm.data_utils.filesystem.filesystem import Filesystem
110 from osm_lcm.data_utils.wim import (
111 get_sdn_ports,
112 get_target_wim_attrs,
113 select_feasible_wim_account,
114 )
115
116 from n2vc.n2vc_juju_conn import N2VCJujuConnector
117 from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException
118
119 from osm_lcm.lcm_helm_conn import LCMHelmConn
120 from osm_lcm.osm_config import OsmConfigBuilder
121 from osm_lcm.prometheus import parse_job
122
123 from copy import copy, deepcopy
124 from time import time
125 from uuid import uuid4
126
127 from random import SystemRandom
128
129 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
130
131
132 class NsLcm(LcmBase):
133 SUBOPERATION_STATUS_NOT_FOUND = -1
134 SUBOPERATION_STATUS_NEW = -2
135 SUBOPERATION_STATUS_SKIP = -3
136 EE_TLS_NAME = "ee-tls"
137 task_name_deploy_vca = "Deploying VCA"
138 rel_operation_types = {
139 "GE": ">=",
140 "LE": "<=",
141 "GT": ">",
142 "LT": "<",
143 "EQ": "==",
144 "NE": "!=",
145 }
146
147 def __init__(self, msg, lcm_tasks, config: LcmCfg):
148 """
149 Init, Connect to database, filesystem storage, and messaging
150 :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
151 :return: None
152 """
153 super().__init__(msg=msg, logger=logging.getLogger("lcm.ns"))
154
155 self.db = Database().instance.db
156 self.fs = Filesystem().instance.fs
157 self.lcm_tasks = lcm_tasks
158 self.timeout = config.timeout
159 self.ro_config = config.RO
160 self.vca_config = config.VCA
161
162 # create N2VC connector
163 self.n2vc = N2VCJujuConnector(
164 log=self.logger,
165 on_update_db=self._on_update_n2vc_db,
166 fs=self.fs,
167 db=self.db,
168 )
169
170 self.conn_helm_ee = LCMHelmConn(
171 log=self.logger,
172 vca_config=self.vca_config,
173 on_update_db=self._on_update_n2vc_db,
174 )
175
176 self.k8sclusterhelm2 = K8sHelmConnector(
177 kubectl_command=self.vca_config.kubectlpath,
178 helm_command=self.vca_config.helmpath,
179 log=self.logger,
180 on_update_db=None,
181 fs=self.fs,
182 db=self.db,
183 )
184
185 self.k8sclusterhelm3 = K8sHelm3Connector(
186 kubectl_command=self.vca_config.kubectlpath,
187 helm_command=self.vca_config.helm3path,
188 fs=self.fs,
189 log=self.logger,
190 db=self.db,
191 on_update_db=None,
192 )
193
194 self.k8sclusterjuju = K8sJujuConnector(
195 kubectl_command=self.vca_config.kubectlpath,
196 juju_command=self.vca_config.jujupath,
197 log=self.logger,
198 on_update_db=self._on_update_k8s_db,
199 fs=self.fs,
200 db=self.db,
201 )
202
203 self.k8scluster_map = {
204 "helm-chart": self.k8sclusterhelm2,
205 "helm-chart-v3": self.k8sclusterhelm3,
206 "chart": self.k8sclusterhelm3,
207 "juju-bundle": self.k8sclusterjuju,
208 "juju": self.k8sclusterjuju,
209 }
210
211 self.vca_map = {
212 "lxc_proxy_charm": self.n2vc,
213 "native_charm": self.n2vc,
214 "k8s_proxy_charm": self.n2vc,
215 "helm": self.conn_helm_ee,
216 "helm-v3": self.conn_helm_ee,
217 }
218
219 # create RO client
220 self.RO = NgRoClient(**self.ro_config.to_dict())
221
222 self.op_status_map = {
223 "instantiation": self.RO.status,
224 "termination": self.RO.status,
225 "migrate": self.RO.status,
226 "healing": self.RO.recreate_status,
227 "verticalscale": self.RO.status,
228 "start_stop_rebuild": self.RO.status,
229 }
230
231 @staticmethod
232 def increment_ip_mac(ip_mac, vm_index=1):
233 if not isinstance(ip_mac, str):
234 return ip_mac
235 try:
236 # try with ipv4 look for last dot
237 i = ip_mac.rfind(".")
238 if i > 0:
239 i += 1
240 return "{}{}".format(ip_mac[:i], int(ip_mac[i:]) + vm_index)
241 # try with ipv6 or mac look for last colon. Operate in hex
242 i = ip_mac.rfind(":")
243 if i > 0:
244 i += 1
245 # format in hex, len can be 2 for mac or 4 for ipv6
246 return ("{}{:0" + str(len(ip_mac) - i) + "x}").format(
247 ip_mac[:i], int(ip_mac[i:], 16) + vm_index
248 )
249 except Exception:
250 pass
251 return None
252
253 def _on_update_ro_db(self, nsrs_id, ro_descriptor):
254 # self.logger.debug('_on_update_ro_db(nsrs_id={}'.format(nsrs_id))
255
256 try:
257 # TODO filter RO descriptor fields...
258
259 # write to database
260 db_dict = dict()
261 # db_dict['deploymentStatus'] = yaml.dump(ro_descriptor, default_flow_style=False, indent=2)
262 db_dict["deploymentStatus"] = ro_descriptor
263 self.update_db_2("nsrs", nsrs_id, db_dict)
264
265 except Exception as e:
266 self.logger.warn(
267 "Cannot write database RO deployment for ns={} -> {}".format(nsrs_id, e)
268 )
269
270 async def _on_update_n2vc_db(self, table, filter, path, updated_data, vca_id=None):
271 # remove last dot from path (if exists)
272 if path.endswith("."):
273 path = path[:-1]
274
275 # self.logger.debug('_on_update_n2vc_db(table={}, filter={}, path={}, updated_data={}'
276 # .format(table, filter, path, updated_data))
277 try:
278 nsr_id = filter.get("_id")
279
280 # read ns record from database
281 nsr = self.db.get_one(table="nsrs", q_filter=filter)
282 current_ns_status = nsr.get("nsState")
283
284 # get vca status for NS
285 status_dict = await self.n2vc.get_status(
286 namespace="." + nsr_id, yaml_format=False, vca_id=vca_id
287 )
288
289 # vcaStatus
290 db_dict = dict()
291 db_dict["vcaStatus"] = status_dict
292
293 # update configurationStatus for this VCA
294 try:
295 vca_index = int(path[path.rfind(".") + 1 :])
296
297 vca_list = deep_get(
298 target_dict=nsr, key_list=("_admin", "deployed", "VCA")
299 )
300 vca_status = vca_list[vca_index].get("status")
301
302 configuration_status_list = nsr.get("configurationStatus")
303 config_status = configuration_status_list[vca_index].get("status")
304
305 if config_status == "BROKEN" and vca_status != "failed":
306 db_dict["configurationStatus"][vca_index] = "READY"
307 elif config_status != "BROKEN" and vca_status == "failed":
308 db_dict["configurationStatus"][vca_index] = "BROKEN"
309 except Exception as e:
310 # not update configurationStatus
311 self.logger.debug("Error updating vca_index (ignore): {}".format(e))
312
313 # if nsState = 'READY' check if juju is reporting some error => nsState = 'DEGRADED'
314 # if nsState = 'DEGRADED' check if all is OK
315 is_degraded = False
316 if current_ns_status in ("READY", "DEGRADED"):
317 error_description = ""
318 # check machines
319 if status_dict.get("machines"):
320 for machine_id in status_dict.get("machines"):
321 machine = status_dict.get("machines").get(machine_id)
322 # check machine agent-status
323 if machine.get("agent-status"):
324 s = machine.get("agent-status").get("status")
325 if s != "started":
326 is_degraded = True
327 error_description += (
328 "machine {} agent-status={} ; ".format(
329 machine_id, s
330 )
331 )
332 # check machine instance status
333 if machine.get("instance-status"):
334 s = machine.get("instance-status").get("status")
335 if s != "running":
336 is_degraded = True
337 error_description += (
338 "machine {} instance-status={} ; ".format(
339 machine_id, s
340 )
341 )
342 # check applications
343 if status_dict.get("applications"):
344 for app_id in status_dict.get("applications"):
345 app = status_dict.get("applications").get(app_id)
346 # check application status
347 if app.get("status"):
348 s = app.get("status").get("status")
349 if s != "active":
350 is_degraded = True
351 error_description += (
352 "application {} status={} ; ".format(app_id, s)
353 )
354
355 if error_description:
356 db_dict["errorDescription"] = error_description
357 if current_ns_status == "READY" and is_degraded:
358 db_dict["nsState"] = "DEGRADED"
359 if current_ns_status == "DEGRADED" and not is_degraded:
360 db_dict["nsState"] = "READY"
361
362 # write to database
363 self.update_db_2("nsrs", nsr_id, db_dict)
364
365 except (asyncio.CancelledError, asyncio.TimeoutError):
366 raise
367 except Exception as e:
368 self.logger.warn("Error updating NS state for ns={}: {}".format(nsr_id, e))
369
370 async def _on_update_k8s_db(
371 self, cluster_uuid, kdu_instance, filter=None, vca_id=None, cluster_type="juju"
372 ):
373 """
374 Updating vca status in NSR record
375 :param cluster_uuid: UUID of a k8s cluster
376 :param kdu_instance: The unique name of the KDU instance
377 :param filter: To get nsr_id
378 :cluster_type: The cluster type (juju, k8s)
379 :return: none
380 """
381
382 # self.logger.debug("_on_update_k8s_db(cluster_uuid={}, kdu_instance={}, filter={}"
383 # .format(cluster_uuid, kdu_instance, filter))
384
385 nsr_id = filter.get("_id")
386 try:
387 vca_status = await self.k8scluster_map[cluster_type].status_kdu(
388 cluster_uuid=cluster_uuid,
389 kdu_instance=kdu_instance,
390 yaml_format=False,
391 complete_status=True,
392 vca_id=vca_id,
393 )
394
395 # vcaStatus
396 db_dict = dict()
397 db_dict["vcaStatus"] = {nsr_id: vca_status}
398
399 self.logger.debug(
400 f"Obtained VCA status for cluster type '{cluster_type}': {vca_status}"
401 )
402
403 # write to database
404 self.update_db_2("nsrs", nsr_id, db_dict)
405 except (asyncio.CancelledError, asyncio.TimeoutError):
406 raise
407 except Exception as e:
408 self.logger.warn("Error updating NS state for ns={}: {}".format(nsr_id, e))
409
410 @staticmethod
411 def _parse_cloud_init(cloud_init_text, additional_params, vnfd_id, vdu_id):
412 try:
413 env = Environment(
414 undefined=StrictUndefined,
415 autoescape=select_autoescape(default_for_string=True, default=True),
416 )
417 template = env.from_string(cloud_init_text)
418 return template.render(additional_params or {})
419 except UndefinedError as e:
420 raise LcmException(
421 "Variable {} at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
422 "file, must be provided in the instantiation parameters inside the "
423 "'additionalParamsForVnf/Vdu' block".format(e, vnfd_id, vdu_id)
424 )
425 except (TemplateError, TemplateNotFound) as e:
426 raise LcmException(
427 "Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".format(
428 vnfd_id, vdu_id, e
429 )
430 )
431
432 def _get_vdu_cloud_init_content(self, vdu, vnfd):
433 cloud_init_content = cloud_init_file = None
434 try:
435 if vdu.get("cloud-init-file"):
436 base_folder = vnfd["_admin"]["storage"]
437 if base_folder["pkg-dir"]:
438 cloud_init_file = "{}/{}/cloud_init/{}".format(
439 base_folder["folder"],
440 base_folder["pkg-dir"],
441 vdu["cloud-init-file"],
442 )
443 else:
444 cloud_init_file = "{}/Scripts/cloud_init/{}".format(
445 base_folder["folder"],
446 vdu["cloud-init-file"],
447 )
448 with self.fs.file_open(cloud_init_file, "r") as ci_file:
449 cloud_init_content = ci_file.read()
450 elif vdu.get("cloud-init"):
451 cloud_init_content = vdu["cloud-init"]
452
453 return cloud_init_content
454 except FsException as e:
455 raise LcmException(
456 "Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".format(
457 vnfd["id"], vdu["id"], cloud_init_file, e
458 )
459 )
460
461 def _get_vdu_additional_params(self, db_vnfr, vdu_id):
462 vdur = next(
463 (vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"]), {}
464 )
465 additional_params = vdur.get("additionalParams")
466 return parse_yaml_strings(additional_params)
467
468 def vnfd2RO(self, vnfd, new_id=None, additionalParams=None, nsrId=None):
469 """
470 Converts creates a new vnfd descriptor for RO base on input OSM IM vnfd
471 :param vnfd: input vnfd
472 :param new_id: overrides vnf id if provided
473 :param additionalParams: Instantiation params for VNFs provided
474 :param nsrId: Id of the NSR
475 :return: copy of vnfd
476 """
477 vnfd_RO = deepcopy(vnfd)
478 # remove unused by RO configuration, monitoring, scaling and internal keys
479 vnfd_RO.pop("_id", None)
480 vnfd_RO.pop("_admin", None)
481 vnfd_RO.pop("monitoring-param", None)
482 vnfd_RO.pop("scaling-group-descriptor", None)
483 vnfd_RO.pop("kdu", None)
484 vnfd_RO.pop("k8s-cluster", None)
485 if new_id:
486 vnfd_RO["id"] = new_id
487
488 # parse cloud-init or cloud-init-file with the provided variables using Jinja2
489 for vdu in get_iterable(vnfd_RO, "vdu"):
490 vdu.pop("cloud-init-file", None)
491 vdu.pop("cloud-init", None)
492 return vnfd_RO
493
494 @staticmethod
495 def ip_profile_2_RO(ip_profile):
496 RO_ip_profile = deepcopy(ip_profile)
497 if "dns-server" in RO_ip_profile:
498 if isinstance(RO_ip_profile["dns-server"], list):
499 RO_ip_profile["dns-address"] = []
500 for ds in RO_ip_profile.pop("dns-server"):
501 RO_ip_profile["dns-address"].append(ds["address"])
502 else:
503 RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
504 if RO_ip_profile.get("ip-version") == "ipv4":
505 RO_ip_profile["ip-version"] = "IPv4"
506 if RO_ip_profile.get("ip-version") == "ipv6":
507 RO_ip_profile["ip-version"] = "IPv6"
508 if "dhcp-params" in RO_ip_profile:
509 RO_ip_profile["dhcp"] = RO_ip_profile.pop("dhcp-params")
510 return RO_ip_profile
511
512 def _get_ro_vim_id_for_vim_account(self, vim_account):
513 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account})
514 if db_vim["_admin"]["operationalState"] != "ENABLED":
515 raise LcmException(
516 "VIM={} is not available. operationalState={}".format(
517 vim_account, db_vim["_admin"]["operationalState"]
518 )
519 )
520 RO_vim_id = db_vim["_admin"]["deployed"]["RO"]
521 return RO_vim_id
522
523 def get_ro_wim_id_for_wim_account(self, wim_account):
524 if isinstance(wim_account, str):
525 db_wim = self.db.get_one("wim_accounts", {"_id": wim_account})
526 if db_wim["_admin"]["operationalState"] != "ENABLED":
527 raise LcmException(
528 "WIM={} is not available. operationalState={}".format(
529 wim_account, db_wim["_admin"]["operationalState"]
530 )
531 )
532 RO_wim_id = db_wim["_admin"]["deployed"]["RO-account"]
533 return RO_wim_id
534 else:
535 return wim_account
536
537 def scale_vnfr(self, db_vnfr, vdu_create=None, vdu_delete=None, mark_delete=False):
538 db_vdu_push_list = []
539 template_vdur = []
540 db_update = {"_admin.modified": time()}
541 if vdu_create:
542 for vdu_id, vdu_count in vdu_create.items():
543 vdur = next(
544 (
545 vdur
546 for vdur in reversed(db_vnfr["vdur"])
547 if vdur["vdu-id-ref"] == vdu_id
548 ),
549 None,
550 )
551 if not vdur:
552 # Read the template saved in the db:
553 self.logger.debug(
554 "No vdur in the database. Using the vdur-template to scale"
555 )
556 vdur_template = db_vnfr.get("vdur-template")
557 if not vdur_template:
558 raise LcmException(
559 "Error scaling OUT VNFR for {}. No vnfr or template exists".format(
560 vdu_id
561 )
562 )
563 vdur = vdur_template[0]
564 # Delete a template from the database after using it
565 self.db.set_one(
566 "vnfrs",
567 {"_id": db_vnfr["_id"]},
568 None,
569 pull={"vdur-template": {"_id": vdur["_id"]}},
570 )
571 for count in range(vdu_count):
572 vdur_copy = deepcopy(vdur)
573 vdur_copy["status"] = "BUILD"
574 vdur_copy["status-detailed"] = None
575 vdur_copy["ip-address"] = None
576 vdur_copy["_id"] = str(uuid4())
577 vdur_copy["count-index"] += count + 1
578 vdur_copy["id"] = "{}-{}".format(
579 vdur_copy["vdu-id-ref"], vdur_copy["count-index"]
580 )
581 vdur_copy.pop("vim_info", None)
582 for iface in vdur_copy["interfaces"]:
583 if iface.get("fixed-ip"):
584 iface["ip-address"] = self.increment_ip_mac(
585 iface["ip-address"], count + 1
586 )
587 else:
588 iface.pop("ip-address", None)
589 if iface.get("fixed-mac"):
590 iface["mac-address"] = self.increment_ip_mac(
591 iface["mac-address"], count + 1
592 )
593 else:
594 iface.pop("mac-address", None)
595 if db_vnfr["vdur"]:
596 iface.pop(
597 "mgmt_vnf", None
598 ) # only first vdu can be managment of vnf
599 db_vdu_push_list.append(vdur_copy)
600 # self.logger.debug("scale out, adding vdu={}".format(vdur_copy))
601 if vdu_delete:
602 if len(db_vnfr["vdur"]) == 1:
603 # The scale will move to 0 instances
604 self.logger.debug(
605 "Scaling to 0 !, creating the template with the last vdur"
606 )
607 template_vdur = [db_vnfr["vdur"][0]]
608 for vdu_id, vdu_count in vdu_delete.items():
609 if mark_delete:
610 indexes_to_delete = [
611 iv[0]
612 for iv in enumerate(db_vnfr["vdur"])
613 if iv[1]["vdu-id-ref"] == vdu_id
614 ]
615 db_update.update(
616 {
617 "vdur.{}.status".format(i): "DELETING"
618 for i in indexes_to_delete[-vdu_count:]
619 }
620 )
621 else:
622 # it must be deleted one by one because common.db does not allow otherwise
623 vdus_to_delete = [
624 v
625 for v in reversed(db_vnfr["vdur"])
626 if v["vdu-id-ref"] == vdu_id
627 ]
628 for vdu in vdus_to_delete[:vdu_count]:
629 self.db.set_one(
630 "vnfrs",
631 {"_id": db_vnfr["_id"]},
632 None,
633 pull={"vdur": {"_id": vdu["_id"]}},
634 )
635 db_push = {}
636 if db_vdu_push_list:
637 db_push["vdur"] = db_vdu_push_list
638 if template_vdur:
639 db_push["vdur-template"] = template_vdur
640 if not db_push:
641 db_push = None
642 db_vnfr["vdur-template"] = template_vdur
643 self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, db_update, push_list=db_push)
644 # modify passed dictionary db_vnfr
645 db_vnfr_ = self.db.get_one("vnfrs", {"_id": db_vnfr["_id"]})
646 db_vnfr["vdur"] = db_vnfr_["vdur"]
647
648 def ns_update_nsr(self, ns_update_nsr, db_nsr, nsr_desc_RO):
649 """
650 Updates database nsr with the RO info for the created vld
651 :param ns_update_nsr: dictionary to be filled with the updated info
652 :param db_nsr: content of db_nsr. This is also modified
653 :param nsr_desc_RO: nsr descriptor from RO
654 :return: Nothing, LcmException is raised on errors
655 """
656
657 for vld_index, vld in enumerate(get_iterable(db_nsr, "vld")):
658 for net_RO in get_iterable(nsr_desc_RO, "nets"):
659 if vld["id"] != net_RO.get("ns_net_osm_id"):
660 continue
661 vld["vim-id"] = net_RO.get("vim_net_id")
662 vld["name"] = net_RO.get("vim_name")
663 vld["status"] = net_RO.get("status")
664 vld["status-detailed"] = net_RO.get("error_msg")
665 ns_update_nsr["vld.{}".format(vld_index)] = vld
666 break
667 else:
668 raise LcmException(
669 "ns_update_nsr: Not found vld={} at RO info".format(vld["id"])
670 )
671
672 def set_vnfr_at_error(self, db_vnfrs, error_text):
673 try:
674 for db_vnfr in db_vnfrs.values():
675 vnfr_update = {"status": "ERROR"}
676 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
677 if "status" not in vdur:
678 vdur["status"] = "ERROR"
679 vnfr_update["vdur.{}.status".format(vdu_index)] = "ERROR"
680 if error_text:
681 vdur["status-detailed"] = str(error_text)
682 vnfr_update[
683 "vdur.{}.status-detailed".format(vdu_index)
684 ] = "ERROR"
685 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
686 except DbException as e:
687 self.logger.error("Cannot update vnf. {}".format(e))
688
689 def ns_update_vnfr(self, db_vnfrs, nsr_desc_RO):
690 """
691 Updates database vnfr with the RO info, e.g. ip_address, vim_id... Descriptor db_vnfrs is also updated
692 :param db_vnfrs: dictionary with member-vnf-index: vnfr-content
693 :param nsr_desc_RO: nsr descriptor from RO
694 :return: Nothing, LcmException is raised on errors
695 """
696 for vnf_index, db_vnfr in db_vnfrs.items():
697 for vnf_RO in nsr_desc_RO["vnfs"]:
698 if vnf_RO["member_vnf_index"] != vnf_index:
699 continue
700 vnfr_update = {}
701 if vnf_RO.get("ip_address"):
702 db_vnfr["ip-address"] = vnfr_update["ip-address"] = vnf_RO[
703 "ip_address"
704 ].split(";")[0]
705 elif not db_vnfr.get("ip-address"):
706 if db_vnfr.get("vdur"): # if not VDUs, there is not ip_address
707 raise LcmExceptionNoMgmtIP(
708 "ns member_vnf_index '{}' has no IP address".format(
709 vnf_index
710 )
711 )
712
713 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
714 vdur_RO_count_index = 0
715 if vdur.get("pdu-type"):
716 continue
717 for vdur_RO in get_iterable(vnf_RO, "vms"):
718 if vdur["vdu-id-ref"] != vdur_RO["vdu_osm_id"]:
719 continue
720 if vdur["count-index"] != vdur_RO_count_index:
721 vdur_RO_count_index += 1
722 continue
723 vdur["vim-id"] = vdur_RO.get("vim_vm_id")
724 if vdur_RO.get("ip_address"):
725 vdur["ip-address"] = vdur_RO["ip_address"].split(";")[0]
726 else:
727 vdur["ip-address"] = None
728 vdur["vdu-id-ref"] = vdur_RO.get("vdu_osm_id")
729 vdur["name"] = vdur_RO.get("vim_name")
730 vdur["status"] = vdur_RO.get("status")
731 vdur["status-detailed"] = vdur_RO.get("error_msg")
732 for ifacer in get_iterable(vdur, "interfaces"):
733 for interface_RO in get_iterable(vdur_RO, "interfaces"):
734 if ifacer["name"] == interface_RO.get("internal_name"):
735 ifacer["ip-address"] = interface_RO.get(
736 "ip_address"
737 )
738 ifacer["mac-address"] = interface_RO.get(
739 "mac_address"
740 )
741 break
742 else:
743 raise LcmException(
744 "ns_update_vnfr: Not found member_vnf_index={} vdur={} interface={} "
745 "from VIM info".format(
746 vnf_index, vdur["vdu-id-ref"], ifacer["name"]
747 )
748 )
749 vnfr_update["vdur.{}".format(vdu_index)] = vdur
750 break
751 else:
752 raise LcmException(
753 "ns_update_vnfr: Not found member_vnf_index={} vdur={} count_index={} from "
754 "VIM info".format(
755 vnf_index, vdur["vdu-id-ref"], vdur["count-index"]
756 )
757 )
758
759 for vld_index, vld in enumerate(get_iterable(db_vnfr, "vld")):
760 for net_RO in get_iterable(nsr_desc_RO, "nets"):
761 if vld["id"] != net_RO.get("vnf_net_osm_id"):
762 continue
763 vld["vim-id"] = net_RO.get("vim_net_id")
764 vld["name"] = net_RO.get("vim_name")
765 vld["status"] = net_RO.get("status")
766 vld["status-detailed"] = net_RO.get("error_msg")
767 vnfr_update["vld.{}".format(vld_index)] = vld
768 break
769 else:
770 raise LcmException(
771 "ns_update_vnfr: Not found member_vnf_index={} vld={} from VIM info".format(
772 vnf_index, vld["id"]
773 )
774 )
775
776 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
777 break
778
779 else:
780 raise LcmException(
781 "ns_update_vnfr: Not found member_vnf_index={} from VIM info".format(
782 vnf_index
783 )
784 )
785
786 def _get_ns_config_info(self, nsr_id):
787 """
788 Generates a mapping between vnf,vdu elements and the N2VC id
789 :param nsr_id: id of nsr to get last database _admin.deployed.VCA that contains this list
790 :return: a dictionary with {osm-config-mapping: {}} where its element contains:
791 "<member-vnf-index>": <N2VC-id> for a vnf configuration, or
792 "<member-vnf-index>.<vdu.id>.<vdu replica(0, 1,..)>": <N2VC-id> for a vdu configuration
793 """
794 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
795 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
796 mapping = {}
797 ns_config_info = {"osm-config-mapping": mapping}
798 for vca in vca_deployed_list:
799 if not vca["member-vnf-index"]:
800 continue
801 if not vca["vdu_id"]:
802 mapping[vca["member-vnf-index"]] = vca["application"]
803 else:
804 mapping[
805 "{}.{}.{}".format(
806 vca["member-vnf-index"], vca["vdu_id"], vca["vdu_count_index"]
807 )
808 ] = vca["application"]
809 return ns_config_info
810
811 async def _instantiate_ng_ro(
812 self,
813 logging_text,
814 nsr_id,
815 nsd,
816 db_nsr,
817 db_nslcmop,
818 db_vnfrs,
819 db_vnfds,
820 n2vc_key_list,
821 stage,
822 start_deploy,
823 timeout_ns_deploy,
824 ):
825 db_vims = {}
826
827 def get_vim_account(vim_account_id):
828 nonlocal db_vims
829 if vim_account_id in db_vims:
830 return db_vims[vim_account_id]
831 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account_id})
832 db_vims[vim_account_id] = db_vim
833 return db_vim
834
835 # modify target_vld info with instantiation parameters
836 def parse_vld_instantiation_params(
837 target_vim, target_vld, vld_params, target_sdn
838 ):
839 if vld_params.get("ip-profile"):
840 target_vld["vim_info"][target_vim]["ip_profile"] = vld_to_ro_ip_profile(
841 vld_params["ip-profile"]
842 )
843 if vld_params.get("provider-network"):
844 target_vld["vim_info"][target_vim]["provider_network"] = vld_params[
845 "provider-network"
846 ]
847 if "sdn-ports" in vld_params["provider-network"] and target_sdn:
848 target_vld["vim_info"][target_sdn]["sdn-ports"] = vld_params[
849 "provider-network"
850 ]["sdn-ports"]
851
852 # check if WIM is needed; if needed, choose a feasible WIM able to connect VIMs
853 # if wim_account_id is specified in vld_params, validate if it is feasible.
854 wim_account_id, db_wim = select_feasible_wim_account(
855 db_nsr, db_vnfrs, target_vld, vld_params, self.logger
856 )
857
858 if wim_account_id:
859 # WIM is needed and a feasible one was found, populate WIM target and SDN ports
860 self.logger.info("WIM selected: {:s}".format(str(wim_account_id)))
861 # update vld_params with correct WIM account Id
862 vld_params["wimAccountId"] = wim_account_id
863
864 target_wim = "wim:{}".format(wim_account_id)
865 target_wim_attrs = get_target_wim_attrs(nsr_id, target_vld, vld_params)
866 sdn_ports = get_sdn_ports(vld_params, db_wim)
867 if len(sdn_ports) > 0:
868 target_vld["vim_info"][target_wim] = target_wim_attrs
869 target_vld["vim_info"][target_wim]["sdn-ports"] = sdn_ports
870
871 self.logger.debug(
872 "Target VLD with WIM data: {:s}".format(str(target_vld))
873 )
874
875 for param in ("vim-network-name", "vim-network-id"):
876 if vld_params.get(param):
877 if isinstance(vld_params[param], dict):
878 for vim, vim_net in vld_params[param].items():
879 other_target_vim = "vim:" + vim
880 populate_dict(
881 target_vld["vim_info"],
882 (other_target_vim, param.replace("-", "_")),
883 vim_net,
884 )
885 else: # isinstance str
886 target_vld["vim_info"][target_vim][
887 param.replace("-", "_")
888 ] = vld_params[param]
889 if vld_params.get("common_id"):
890 target_vld["common_id"] = vld_params.get("common_id")
891
892 # modify target["ns"]["vld"] with instantiation parameters to override vnf vim-account
893 def update_ns_vld_target(target, ns_params):
894 for vnf_params in ns_params.get("vnf", ()):
895 if vnf_params.get("vimAccountId"):
896 target_vnf = next(
897 (
898 vnfr
899 for vnfr in db_vnfrs.values()
900 if vnf_params["member-vnf-index"]
901 == vnfr["member-vnf-index-ref"]
902 ),
903 None,
904 )
905 vdur = next((vdur for vdur in target_vnf.get("vdur", ())), None)
906 if not vdur:
907 continue
908 for a_index, a_vld in enumerate(target["ns"]["vld"]):
909 target_vld = find_in_list(
910 get_iterable(vdur, "interfaces"),
911 lambda iface: iface.get("ns-vld-id") == a_vld["name"],
912 )
913
914 vld_params = find_in_list(
915 get_iterable(ns_params, "vld"),
916 lambda v_vld: v_vld["name"] in (a_vld["name"], a_vld["id"]),
917 )
918 if target_vld:
919 if vnf_params.get("vimAccountId") not in a_vld.get(
920 "vim_info", {}
921 ):
922 target_vim_network_list = [
923 v for _, v in a_vld.get("vim_info").items()
924 ]
925 target_vim_network_name = next(
926 (
927 item.get("vim_network_name", "")
928 for item in target_vim_network_list
929 ),
930 "",
931 )
932
933 target["ns"]["vld"][a_index].get("vim_info").update(
934 {
935 "vim:{}".format(vnf_params["vimAccountId"]): {
936 "vim_network_name": target_vim_network_name,
937 }
938 }
939 )
940
941 if vld_params:
942 for param in ("vim-network-name", "vim-network-id"):
943 if vld_params.get(param) and isinstance(
944 vld_params[param], dict
945 ):
946 for vim, vim_net in vld_params[
947 param
948 ].items():
949 other_target_vim = "vim:" + vim
950 populate_dict(
951 target["ns"]["vld"][a_index].get(
952 "vim_info"
953 ),
954 (
955 other_target_vim,
956 param.replace("-", "_"),
957 ),
958 vim_net,
959 )
960
961 nslcmop_id = db_nslcmop["_id"]
962 target = {
963 "name": db_nsr["name"],
964 "ns": {"vld": []},
965 "vnf": [],
966 "image": deepcopy(db_nsr["image"]),
967 "flavor": deepcopy(db_nsr["flavor"]),
968 "action_id": nslcmop_id,
969 "cloud_init_content": {},
970 }
971 for image in target["image"]:
972 image["vim_info"] = {}
973 for flavor in target["flavor"]:
974 flavor["vim_info"] = {}
975 if db_nsr.get("shared-volumes"):
976 target["shared-volumes"] = deepcopy(db_nsr["shared-volumes"])
977 for shared_volumes in target["shared-volumes"]:
978 shared_volumes["vim_info"] = {}
979 if db_nsr.get("affinity-or-anti-affinity-group"):
980 target["affinity-or-anti-affinity-group"] = deepcopy(
981 db_nsr["affinity-or-anti-affinity-group"]
982 )
983 for affinity_or_anti_affinity_group in target[
984 "affinity-or-anti-affinity-group"
985 ]:
986 affinity_or_anti_affinity_group["vim_info"] = {}
987
988 if db_nslcmop.get("lcmOperationType") != "instantiate":
989 # get parameters of instantiation:
990 db_nslcmop_instantiate = self.db.get_list(
991 "nslcmops",
992 {
993 "nsInstanceId": db_nslcmop["nsInstanceId"],
994 "lcmOperationType": "instantiate",
995 },
996 )[-1]
997 ns_params = db_nslcmop_instantiate.get("operationParams")
998 else:
999 ns_params = db_nslcmop.get("operationParams")
1000 ssh_keys_instantiation = ns_params.get("ssh_keys") or []
1001 ssh_keys_all = ssh_keys_instantiation + (n2vc_key_list or [])
1002
1003 cp2target = {}
1004 for vld_index, vld in enumerate(db_nsr.get("vld")):
1005 target_vim = "vim:{}".format(ns_params["vimAccountId"])
1006 target_vld = {
1007 "id": vld["id"],
1008 "name": vld["name"],
1009 "mgmt-network": vld.get("mgmt-network", False),
1010 "type": vld.get("type"),
1011 "vim_info": {
1012 target_vim: {
1013 "vim_network_name": vld.get("vim-network-name"),
1014 "vim_account_id": ns_params["vimAccountId"],
1015 }
1016 },
1017 }
1018 # check if this network needs SDN assist
1019 if vld.get("pci-interfaces"):
1020 db_vim = get_vim_account(ns_params["vimAccountId"])
1021 if vim_config := db_vim.get("config"):
1022 if sdnc_id := vim_config.get("sdn-controller"):
1023 sdn_vld = "nsrs:{}:vld.{}".format(nsr_id, vld["id"])
1024 target_sdn = "sdn:{}".format(sdnc_id)
1025 target_vld["vim_info"][target_sdn] = {
1026 "sdn": True,
1027 "target_vim": target_vim,
1028 "vlds": [sdn_vld],
1029 "type": vld.get("type"),
1030 }
1031
1032 nsd_vnf_profiles = get_vnf_profiles(nsd)
1033 for nsd_vnf_profile in nsd_vnf_profiles:
1034 for cp in nsd_vnf_profile["virtual-link-connectivity"]:
1035 if cp["virtual-link-profile-id"] == vld["id"]:
1036 cp2target[
1037 "member_vnf:{}.{}".format(
1038 cp["constituent-cpd-id"][0][
1039 "constituent-base-element-id"
1040 ],
1041 cp["constituent-cpd-id"][0]["constituent-cpd-id"],
1042 )
1043 ] = "nsrs:{}:vld.{}".format(nsr_id, vld_index)
1044
1045 # check at nsd descriptor, if there is an ip-profile
1046 vld_params = {}
1047 nsd_vlp = find_in_list(
1048 get_virtual_link_profiles(nsd),
1049 lambda a_link_profile: a_link_profile["virtual-link-desc-id"]
1050 == vld["id"],
1051 )
1052 if (
1053 nsd_vlp
1054 and nsd_vlp.get("virtual-link-protocol-data")
1055 and nsd_vlp["virtual-link-protocol-data"].get("l3-protocol-data")
1056 ):
1057 vld_params["ip-profile"] = nsd_vlp["virtual-link-protocol-data"][
1058 "l3-protocol-data"
1059 ]
1060
1061 # update vld_params with instantiation params
1062 vld_instantiation_params = find_in_list(
1063 get_iterable(ns_params, "vld"),
1064 lambda a_vld: a_vld["name"] in (vld["name"], vld["id"]),
1065 )
1066 if vld_instantiation_params:
1067 vld_params.update(vld_instantiation_params)
1068 parse_vld_instantiation_params(target_vim, target_vld, vld_params, None)
1069 target["ns"]["vld"].append(target_vld)
1070 # Update the target ns_vld if vnf vim_account is overriden by instantiation params
1071 update_ns_vld_target(target, ns_params)
1072
1073 for vnfr in db_vnfrs.values():
1074 vnfd = find_in_list(
1075 db_vnfds, lambda db_vnf: db_vnf["id"] == vnfr["vnfd-ref"]
1076 )
1077 vnf_params = find_in_list(
1078 get_iterable(ns_params, "vnf"),
1079 lambda a_vnf: a_vnf["member-vnf-index"] == vnfr["member-vnf-index-ref"],
1080 )
1081 target_vnf = deepcopy(vnfr)
1082 target_vim = "vim:{}".format(vnfr["vim-account-id"])
1083 for vld in target_vnf.get("vld", ()):
1084 # check if connected to a ns.vld, to fill target'
1085 vnf_cp = find_in_list(
1086 vnfd.get("int-virtual-link-desc", ()),
1087 lambda cpd: cpd.get("id") == vld["id"],
1088 )
1089 if vnf_cp:
1090 ns_cp = "member_vnf:{}.{}".format(
1091 vnfr["member-vnf-index-ref"], vnf_cp["id"]
1092 )
1093 if cp2target.get(ns_cp):
1094 vld["target"] = cp2target[ns_cp]
1095
1096 vld["vim_info"] = {
1097 target_vim: {"vim_network_name": vld.get("vim-network-name")}
1098 }
1099 # check if this network needs SDN assist
1100 target_sdn = None
1101 if vld.get("pci-interfaces"):
1102 db_vim = get_vim_account(vnfr["vim-account-id"])
1103 sdnc_id = db_vim["config"].get("sdn-controller")
1104 if sdnc_id:
1105 sdn_vld = "vnfrs:{}:vld.{}".format(target_vnf["_id"], vld["id"])
1106 target_sdn = "sdn:{}".format(sdnc_id)
1107 vld["vim_info"][target_sdn] = {
1108 "sdn": True,
1109 "target_vim": target_vim,
1110 "vlds": [sdn_vld],
1111 "type": vld.get("type"),
1112 }
1113
1114 # check at vnfd descriptor, if there is an ip-profile
1115 vld_params = {}
1116 vnfd_vlp = find_in_list(
1117 get_virtual_link_profiles(vnfd),
1118 lambda a_link_profile: a_link_profile["id"] == vld["id"],
1119 )
1120 if (
1121 vnfd_vlp
1122 and vnfd_vlp.get("virtual-link-protocol-data")
1123 and vnfd_vlp["virtual-link-protocol-data"].get("l3-protocol-data")
1124 ):
1125 vld_params["ip-profile"] = vnfd_vlp["virtual-link-protocol-data"][
1126 "l3-protocol-data"
1127 ]
1128 # update vld_params with instantiation params
1129 if vnf_params:
1130 vld_instantiation_params = find_in_list(
1131 get_iterable(vnf_params, "internal-vld"),
1132 lambda i_vld: i_vld["name"] == vld["id"],
1133 )
1134 if vld_instantiation_params:
1135 vld_params.update(vld_instantiation_params)
1136 parse_vld_instantiation_params(target_vim, vld, vld_params, target_sdn)
1137
1138 vdur_list = []
1139 for vdur in target_vnf.get("vdur", ()):
1140 if vdur.get("status") == "DELETING" or vdur.get("pdu-type"):
1141 continue # This vdu must not be created
1142 vdur["vim_info"] = {"vim_account_id": vnfr["vim-account-id"]}
1143
1144 self.logger.debug("NS > ssh_keys > {}".format(ssh_keys_all))
1145
1146 if ssh_keys_all:
1147 vdu_configuration = get_configuration(vnfd, vdur["vdu-id-ref"])
1148 vnf_configuration = get_configuration(vnfd, vnfd["id"])
1149 if (
1150 vdu_configuration
1151 and vdu_configuration.get("config-access")
1152 and vdu_configuration.get("config-access").get("ssh-access")
1153 ):
1154 vdur["ssh-keys"] = ssh_keys_all
1155 vdur["ssh-access-required"] = vdu_configuration[
1156 "config-access"
1157 ]["ssh-access"]["required"]
1158 elif (
1159 vnf_configuration
1160 and vnf_configuration.get("config-access")
1161 and vnf_configuration.get("config-access").get("ssh-access")
1162 and any(iface.get("mgmt-vnf") for iface in vdur["interfaces"])
1163 ):
1164 vdur["ssh-keys"] = ssh_keys_all
1165 vdur["ssh-access-required"] = vnf_configuration[
1166 "config-access"
1167 ]["ssh-access"]["required"]
1168 elif ssh_keys_instantiation and find_in_list(
1169 vdur["interfaces"], lambda iface: iface.get("mgmt-vnf")
1170 ):
1171 vdur["ssh-keys"] = ssh_keys_instantiation
1172
1173 self.logger.debug("NS > vdur > {}".format(vdur))
1174
1175 vdud = get_vdu(vnfd, vdur["vdu-id-ref"])
1176 # cloud-init
1177 if vdud.get("cloud-init-file"):
1178 vdur["cloud-init"] = "{}:file:{}".format(
1179 vnfd["_id"], vdud.get("cloud-init-file")
1180 )
1181 # read file and put content at target.cloul_init_content. Avoid ng_ro to use shared package system
1182 if vdur["cloud-init"] not in target["cloud_init_content"]:
1183 base_folder = vnfd["_admin"]["storage"]
1184 if base_folder["pkg-dir"]:
1185 cloud_init_file = "{}/{}/cloud_init/{}".format(
1186 base_folder["folder"],
1187 base_folder["pkg-dir"],
1188 vdud.get("cloud-init-file"),
1189 )
1190 else:
1191 cloud_init_file = "{}/Scripts/cloud_init/{}".format(
1192 base_folder["folder"],
1193 vdud.get("cloud-init-file"),
1194 )
1195 with self.fs.file_open(cloud_init_file, "r") as ci_file:
1196 target["cloud_init_content"][
1197 vdur["cloud-init"]
1198 ] = ci_file.read()
1199 elif vdud.get("cloud-init"):
1200 vdur["cloud-init"] = "{}:vdu:{}".format(
1201 vnfd["_id"], get_vdu_index(vnfd, vdur["vdu-id-ref"])
1202 )
1203 # put content at target.cloul_init_content. Avoid ng_ro read vnfd descriptor
1204 target["cloud_init_content"][vdur["cloud-init"]] = vdud[
1205 "cloud-init"
1206 ]
1207 vdur["additionalParams"] = vdur.get("additionalParams") or {}
1208 deploy_params_vdu = self._format_additional_params(
1209 vdur.get("additionalParams") or {}
1210 )
1211 deploy_params_vdu["OSM"] = get_osm_params(
1212 vnfr, vdur["vdu-id-ref"], vdur["count-index"]
1213 )
1214 vdur["additionalParams"] = deploy_params_vdu
1215
1216 # flavor
1217 ns_flavor = target["flavor"][int(vdur["ns-flavor-id"])]
1218 if target_vim not in ns_flavor["vim_info"]:
1219 ns_flavor["vim_info"][target_vim] = {}
1220
1221 # deal with images
1222 # in case alternative images are provided we must check if they should be applied
1223 # for the vim_type, modify the vim_type taking into account
1224 ns_image_id = int(vdur["ns-image-id"])
1225 if vdur.get("alt-image-ids"):
1226 db_vim = get_vim_account(vnfr["vim-account-id"])
1227 vim_type = db_vim["vim_type"]
1228 for alt_image_id in vdur.get("alt-image-ids"):
1229 ns_alt_image = target["image"][int(alt_image_id)]
1230 if vim_type == ns_alt_image.get("vim-type"):
1231 # must use alternative image
1232 self.logger.debug(
1233 "use alternative image id: {}".format(alt_image_id)
1234 )
1235 ns_image_id = alt_image_id
1236 vdur["ns-image-id"] = ns_image_id
1237 break
1238 ns_image = target["image"][int(ns_image_id)]
1239 if target_vim not in ns_image["vim_info"]:
1240 ns_image["vim_info"][target_vim] = {}
1241
1242 # Affinity groups
1243 if vdur.get("affinity-or-anti-affinity-group-id"):
1244 for ags_id in vdur["affinity-or-anti-affinity-group-id"]:
1245 ns_ags = target["affinity-or-anti-affinity-group"][int(ags_id)]
1246 if target_vim not in ns_ags["vim_info"]:
1247 ns_ags["vim_info"][target_vim] = {}
1248
1249 # shared-volumes
1250 if vdur.get("shared-volumes-id"):
1251 for sv_id in vdur["shared-volumes-id"]:
1252 ns_sv = find_in_list(
1253 target["shared-volumes"], lambda sv: sv_id in sv["id"]
1254 )
1255 if ns_sv:
1256 ns_sv["vim_info"][target_vim] = {}
1257
1258 vdur["vim_info"] = {target_vim: {}}
1259 # instantiation parameters
1260 if vnf_params:
1261 vdu_instantiation_params = find_in_list(
1262 get_iterable(vnf_params, "vdu"),
1263 lambda i_vdu: i_vdu["id"] == vdud["id"],
1264 )
1265 if vdu_instantiation_params:
1266 # Parse the vdu_volumes from the instantiation params
1267 vdu_volumes = get_volumes_from_instantiation_params(
1268 vdu_instantiation_params, vdud
1269 )
1270 vdur["additionalParams"]["OSM"]["vdu_volumes"] = vdu_volumes
1271 vdur["additionalParams"]["OSM"][
1272 "vim_flavor_id"
1273 ] = vdu_instantiation_params.get("vim-flavor-id")
1274 vdur_list.append(vdur)
1275 target_vnf["vdur"] = vdur_list
1276 target["vnf"].append(target_vnf)
1277
1278 self.logger.debug("Send to RO > nsr_id={} target={}".format(nsr_id, target))
1279 desc = await self.RO.deploy(nsr_id, target)
1280 self.logger.debug("RO return > {}".format(desc))
1281 action_id = desc["action_id"]
1282 await self._wait_ng_ro(
1283 nsr_id,
1284 action_id,
1285 nslcmop_id,
1286 start_deploy,
1287 timeout_ns_deploy,
1288 stage,
1289 operation="instantiation",
1290 )
1291
1292 # Updating NSR
1293 db_nsr_update = {
1294 "_admin.deployed.RO.operational-status": "running",
1295 "detailed-status": " ".join(stage),
1296 }
1297 # db_nsr["_admin.deployed.RO.detailed-status"] = "Deployed at VIM"
1298 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1299 self._write_op_status(nslcmop_id, stage)
1300 self.logger.debug(
1301 logging_text + "ns deployed at RO. RO_id={}".format(action_id)
1302 )
1303 return
1304
1305 async def _wait_ng_ro(
1306 self,
1307 nsr_id,
1308 action_id,
1309 nslcmop_id=None,
1310 start_time=None,
1311 timeout=600,
1312 stage=None,
1313 operation=None,
1314 ):
1315 detailed_status_old = None
1316 db_nsr_update = {}
1317 start_time = start_time or time()
1318 while time() <= start_time + timeout:
1319 desc_status = await self.op_status_map[operation](nsr_id, action_id)
1320 self.logger.debug("Wait NG RO > {}".format(desc_status))
1321 if desc_status["status"] == "FAILED":
1322 raise NgRoException(desc_status["details"])
1323 elif desc_status["status"] == "BUILD":
1324 if stage:
1325 stage[2] = "VIM: ({})".format(desc_status["details"])
1326 elif desc_status["status"] == "DONE":
1327 if stage:
1328 stage[2] = "Deployed at VIM"
1329 break
1330 else:
1331 assert False, "ROclient.check_ns_status returns unknown {}".format(
1332 desc_status["status"]
1333 )
1334 if stage and nslcmop_id and stage[2] != detailed_status_old:
1335 detailed_status_old = stage[2]
1336 db_nsr_update["detailed-status"] = " ".join(stage)
1337 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1338 self._write_op_status(nslcmop_id, stage)
1339 await asyncio.sleep(15)
1340 else: # timeout_ns_deploy
1341 raise NgRoException("Timeout waiting ns to deploy")
1342
1343 async def _terminate_ng_ro(
1344 self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage
1345 ):
1346 db_nsr_update = {}
1347 failed_detail = []
1348 action_id = None
1349 start_deploy = time()
1350 try:
1351 target = {
1352 "ns": {"vld": []},
1353 "vnf": [],
1354 "image": [],
1355 "flavor": [],
1356 "action_id": nslcmop_id,
1357 }
1358 desc = await self.RO.deploy(nsr_id, target)
1359 action_id = desc["action_id"]
1360 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETING"
1361 self.logger.debug(
1362 logging_text
1363 + "ns terminate action at RO. action_id={}".format(action_id)
1364 )
1365
1366 # wait until done
1367 delete_timeout = 20 * 60 # 20 minutes
1368 await self._wait_ng_ro(
1369 nsr_id,
1370 action_id,
1371 nslcmop_id,
1372 start_deploy,
1373 delete_timeout,
1374 stage,
1375 operation="termination",
1376 )
1377 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
1378 # delete all nsr
1379 await self.RO.delete(nsr_id)
1380 except NgRoException as e:
1381 if e.http_code == 404: # not found
1382 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
1383 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
1384 self.logger.debug(
1385 logging_text + "RO_action_id={} already deleted".format(action_id)
1386 )
1387 elif e.http_code == 409: # conflict
1388 failed_detail.append("delete conflict: {}".format(e))
1389 self.logger.debug(
1390 logging_text
1391 + "RO_action_id={} delete conflict: {}".format(action_id, e)
1392 )
1393 else:
1394 failed_detail.append("delete error: {}".format(e))
1395 self.logger.error(
1396 logging_text
1397 + "RO_action_id={} delete error: {}".format(action_id, e)
1398 )
1399 except Exception as e:
1400 failed_detail.append("delete error: {}".format(e))
1401 self.logger.error(
1402 logging_text + "RO_action_id={} delete error: {}".format(action_id, e)
1403 )
1404
1405 if failed_detail:
1406 stage[2] = "Error deleting from VIM"
1407 else:
1408 stage[2] = "Deleted from VIM"
1409 db_nsr_update["detailed-status"] = " ".join(stage)
1410 self.update_db_2("nsrs", nsr_id, db_nsr_update)
1411 self._write_op_status(nslcmop_id, stage)
1412
1413 if failed_detail:
1414 raise LcmException("; ".join(failed_detail))
1415 return
1416
1417 async def instantiate_RO(
1418 self,
1419 logging_text,
1420 nsr_id,
1421 nsd,
1422 db_nsr,
1423 db_nslcmop,
1424 db_vnfrs,
1425 db_vnfds,
1426 n2vc_key_list,
1427 stage,
1428 ):
1429 """
1430 Instantiate at RO
1431 :param logging_text: preffix text to use at logging
1432 :param nsr_id: nsr identity
1433 :param nsd: database content of ns descriptor
1434 :param db_nsr: database content of ns record
1435 :param db_nslcmop: database content of ns operation, in this case, 'instantiate'
1436 :param db_vnfrs:
1437 :param db_vnfds: database content of vnfds, indexed by id (not _id). {id: {vnfd_object}, ...}
1438 :param n2vc_key_list: ssh-public-key list to be inserted to management vdus via cloud-init
1439 :param stage: list with 3 items: [general stage, tasks, vim_specific]. This task will write over vim_specific
1440 :return: None or exception
1441 """
1442 try:
1443 start_deploy = time()
1444 ns_params = db_nslcmop.get("operationParams")
1445 if ns_params and ns_params.get("timeout_ns_deploy"):
1446 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
1447 else:
1448 timeout_ns_deploy = self.timeout.ns_deploy
1449
1450 # Check for and optionally request placement optimization. Database will be updated if placement activated
1451 stage[2] = "Waiting for Placement."
1452 if await self._do_placement(logging_text, db_nslcmop, db_vnfrs):
1453 # in case of placement change ns_params[vimAcountId) if not present at any vnfrs
1454 for vnfr in db_vnfrs.values():
1455 if ns_params["vimAccountId"] == vnfr["vim-account-id"]:
1456 break
1457 else:
1458 ns_params["vimAccountId"] == vnfr["vim-account-id"]
1459
1460 return await self._instantiate_ng_ro(
1461 logging_text,
1462 nsr_id,
1463 nsd,
1464 db_nsr,
1465 db_nslcmop,
1466 db_vnfrs,
1467 db_vnfds,
1468 n2vc_key_list,
1469 stage,
1470 start_deploy,
1471 timeout_ns_deploy,
1472 )
1473 except Exception as e:
1474 stage[2] = "ERROR deploying at VIM"
1475 self.set_vnfr_at_error(db_vnfrs, str(e))
1476 self.logger.error(
1477 "Error deploying at VIM {}".format(e),
1478 exc_info=not isinstance(
1479 e,
1480 (
1481 ROclient.ROClientException,
1482 LcmException,
1483 DbException,
1484 NgRoException,
1485 ),
1486 ),
1487 )
1488 raise
1489
1490 async def wait_kdu_up(self, logging_text, nsr_id, vnfr_id, kdu_name):
1491 """
1492 Wait for kdu to be up, get ip address
1493 :param logging_text: prefix use for logging
1494 :param nsr_id:
1495 :param vnfr_id:
1496 :param kdu_name:
1497 :return: IP address, K8s services
1498 """
1499
1500 # self.logger.debug(logging_text + "Starting wait_kdu_up")
1501 nb_tries = 0
1502
1503 while nb_tries < 360:
1504 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
1505 kdur = next(
1506 (
1507 x
1508 for x in get_iterable(db_vnfr, "kdur")
1509 if x.get("kdu-name") == kdu_name
1510 ),
1511 None,
1512 )
1513 if not kdur:
1514 raise LcmException(
1515 "Not found vnfr_id={}, kdu_name={}".format(vnfr_id, kdu_name)
1516 )
1517 if kdur.get("status"):
1518 if kdur["status"] in ("READY", "ENABLED"):
1519 return kdur.get("ip-address"), kdur.get("services")
1520 else:
1521 raise LcmException(
1522 "target KDU={} is in error state".format(kdu_name)
1523 )
1524
1525 await asyncio.sleep(10)
1526 nb_tries += 1
1527 raise LcmException("Timeout waiting KDU={} instantiated".format(kdu_name))
1528
1529 async def wait_vm_up_insert_key_ro(
1530 self, logging_text, nsr_id, vnfr_id, vdu_id, vdu_index, pub_key=None, user=None
1531 ):
1532 """
1533 Wait for ip addres at RO, and optionally, insert public key in virtual machine
1534 :param logging_text: prefix use for logging
1535 :param nsr_id:
1536 :param vnfr_id:
1537 :param vdu_id:
1538 :param vdu_index:
1539 :param pub_key: public ssh key to inject, None to skip
1540 :param user: user to apply the public ssh key
1541 :return: IP address
1542 """
1543
1544 self.logger.debug(logging_text + "Starting wait_vm_up_insert_key_ro")
1545 ip_address = None
1546 target_vdu_id = None
1547 ro_retries = 0
1548
1549 while True:
1550 ro_retries += 1
1551 if ro_retries >= 360: # 1 hour
1552 raise LcmException(
1553 "Not found _admin.deployed.RO.nsr_id for nsr_id: {}".format(nsr_id)
1554 )
1555
1556 await asyncio.sleep(10)
1557
1558 # get ip address
1559 if not target_vdu_id:
1560 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
1561
1562 if not vdu_id: # for the VNF case
1563 if db_vnfr.get("status") == "ERROR":
1564 raise LcmException(
1565 "Cannot inject ssh-key because target VNF is in error state"
1566 )
1567 ip_address = db_vnfr.get("ip-address")
1568 if not ip_address:
1569 continue
1570 vdur = next(
1571 (
1572 x
1573 for x in get_iterable(db_vnfr, "vdur")
1574 if x.get("ip-address") == ip_address
1575 ),
1576 None,
1577 )
1578 else: # VDU case
1579 vdur = next(
1580 (
1581 x
1582 for x in get_iterable(db_vnfr, "vdur")
1583 if x.get("vdu-id-ref") == vdu_id
1584 and x.get("count-index") == vdu_index
1585 ),
1586 None,
1587 )
1588
1589 if (
1590 not vdur and len(db_vnfr.get("vdur", ())) == 1
1591 ): # If only one, this should be the target vdu
1592 vdur = db_vnfr["vdur"][0]
1593 if not vdur:
1594 raise LcmException(
1595 "Not found vnfr_id={}, vdu_id={}, vdu_index={}".format(
1596 vnfr_id, vdu_id, vdu_index
1597 )
1598 )
1599 # New generation RO stores information at "vim_info"
1600 ng_ro_status = None
1601 target_vim = None
1602 if vdur.get("vim_info"):
1603 target_vim = next(
1604 t for t in vdur["vim_info"]
1605 ) # there should be only one key
1606 ng_ro_status = vdur["vim_info"][target_vim].get("vim_status")
1607 if (
1608 vdur.get("pdu-type")
1609 or vdur.get("status") == "ACTIVE"
1610 or ng_ro_status == "ACTIVE"
1611 ):
1612 ip_address = vdur.get("ip-address")
1613 if not ip_address:
1614 continue
1615 target_vdu_id = vdur["vdu-id-ref"]
1616 elif vdur.get("status") == "ERROR" or ng_ro_status == "ERROR":
1617 raise LcmException(
1618 "Cannot inject ssh-key because target VM is in error state"
1619 )
1620
1621 if not target_vdu_id:
1622 continue
1623
1624 # inject public key into machine
1625 if pub_key and user:
1626 self.logger.debug(logging_text + "Inserting RO key")
1627 self.logger.debug("SSH > PubKey > {}".format(pub_key))
1628 if vdur.get("pdu-type"):
1629 self.logger.error(logging_text + "Cannot inject ssh-ky to a PDU")
1630 return ip_address
1631 try:
1632 target = {
1633 "action": {
1634 "action": "inject_ssh_key",
1635 "key": pub_key,
1636 "user": user,
1637 },
1638 "vnf": [{"_id": vnfr_id, "vdur": [{"id": vdur["id"]}]}],
1639 }
1640 desc = await self.RO.deploy(nsr_id, target)
1641 action_id = desc["action_id"]
1642 await self._wait_ng_ro(
1643 nsr_id, action_id, timeout=600, operation="instantiation"
1644 )
1645 break
1646 except NgRoException as e:
1647 raise LcmException(
1648 "Reaching max tries injecting key. Error: {}".format(e)
1649 )
1650 else:
1651 break
1652
1653 return ip_address
1654
1655 async def _wait_dependent_n2vc(self, nsr_id, vca_deployed_list, vca_index):
1656 """
1657 Wait until dependent VCA deployments have been finished. NS wait for VNFs and VDUs. VNFs for VDUs
1658 """
1659 my_vca = vca_deployed_list[vca_index]
1660 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
1661 # vdu or kdu: no dependencies
1662 return
1663 timeout = 300
1664 while timeout >= 0:
1665 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1666 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
1667 configuration_status_list = db_nsr["configurationStatus"]
1668 for index, vca_deployed in enumerate(configuration_status_list):
1669 if index == vca_index:
1670 # myself
1671 continue
1672 if not my_vca.get("member-vnf-index") or (
1673 vca_deployed.get("member-vnf-index")
1674 == my_vca.get("member-vnf-index")
1675 ):
1676 internal_status = configuration_status_list[index].get("status")
1677 if internal_status == "READY":
1678 continue
1679 elif internal_status == "BROKEN":
1680 raise LcmException(
1681 "Configuration aborted because dependent charm/s has failed"
1682 )
1683 else:
1684 break
1685 else:
1686 # no dependencies, return
1687 return
1688 await asyncio.sleep(10)
1689 timeout -= 1
1690
1691 raise LcmException("Configuration aborted because dependent charm/s timeout")
1692
1693 def get_vca_id(self, db_vnfr: dict, db_nsr: dict):
1694 vca_id = None
1695 if db_vnfr:
1696 vca_id = deep_get(db_vnfr, ("vca-id",))
1697 elif db_nsr:
1698 vim_account_id = deep_get(db_nsr, ("instantiate_params", "vimAccountId"))
1699 vca_id = VimAccountDB.get_vim_account_with_id(vim_account_id).get("vca")
1700 return vca_id
1701
1702 async def instantiate_N2VC(
1703 self,
1704 logging_text,
1705 vca_index,
1706 nsi_id,
1707 db_nsr,
1708 db_vnfr,
1709 vdu_id,
1710 kdu_name,
1711 vdu_index,
1712 kdu_index,
1713 config_descriptor,
1714 deploy_params,
1715 base_folder,
1716 nslcmop_id,
1717 stage,
1718 vca_type,
1719 vca_name,
1720 ee_config_descriptor,
1721 ):
1722 nsr_id = db_nsr["_id"]
1723 db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
1724 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
1725 vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
1726 osm_config = {"osm": {"ns_id": db_nsr["_id"]}}
1727 db_dict = {
1728 "collection": "nsrs",
1729 "filter": {"_id": nsr_id},
1730 "path": db_update_entry,
1731 }
1732 step = ""
1733 try:
1734 element_type = "NS"
1735 element_under_configuration = nsr_id
1736
1737 vnfr_id = None
1738 if db_vnfr:
1739 vnfr_id = db_vnfr["_id"]
1740 osm_config["osm"]["vnf_id"] = vnfr_id
1741
1742 namespace = "{nsi}.{ns}".format(nsi=nsi_id if nsi_id else "", ns=nsr_id)
1743
1744 if vca_type == "native_charm":
1745 index_number = 0
1746 else:
1747 index_number = vdu_index or 0
1748
1749 if vnfr_id:
1750 element_type = "VNF"
1751 element_under_configuration = vnfr_id
1752 namespace += ".{}-{}".format(vnfr_id, index_number)
1753 if vdu_id:
1754 namespace += ".{}-{}".format(vdu_id, index_number)
1755 element_type = "VDU"
1756 element_under_configuration = "{}-{}".format(vdu_id, index_number)
1757 osm_config["osm"]["vdu_id"] = vdu_id
1758 elif kdu_name:
1759 namespace += ".{}".format(kdu_name)
1760 element_type = "KDU"
1761 element_under_configuration = kdu_name
1762 osm_config["osm"]["kdu_name"] = kdu_name
1763
1764 # Get artifact path
1765 if base_folder["pkg-dir"]:
1766 artifact_path = "{}/{}/{}/{}".format(
1767 base_folder["folder"],
1768 base_folder["pkg-dir"],
1769 "charms"
1770 if vca_type
1771 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
1772 else "helm-charts",
1773 vca_name,
1774 )
1775 else:
1776 artifact_path = "{}/Scripts/{}/{}/".format(
1777 base_folder["folder"],
1778 "charms"
1779 if vca_type
1780 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
1781 else "helm-charts",
1782 vca_name,
1783 )
1784
1785 self.logger.debug("Artifact path > {}".format(artifact_path))
1786
1787 # get initial_config_primitive_list that applies to this element
1788 initial_config_primitive_list = config_descriptor.get(
1789 "initial-config-primitive"
1790 )
1791
1792 self.logger.debug(
1793 "Initial config primitive list > {}".format(
1794 initial_config_primitive_list
1795 )
1796 )
1797
1798 # add config if not present for NS charm
1799 ee_descriptor_id = ee_config_descriptor.get("id")
1800 self.logger.debug("EE Descriptor > {}".format(ee_descriptor_id))
1801 initial_config_primitive_list = get_ee_sorted_initial_config_primitive_list(
1802 initial_config_primitive_list, vca_deployed, ee_descriptor_id
1803 )
1804
1805 self.logger.debug(
1806 "Initial config primitive list #2 > {}".format(
1807 initial_config_primitive_list
1808 )
1809 )
1810 # n2vc_redesign STEP 3.1
1811 # find old ee_id if exists
1812 ee_id = vca_deployed.get("ee_id")
1813
1814 vca_id = self.get_vca_id(db_vnfr, db_nsr)
1815 # create or register execution environment in VCA
1816 if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm", "helm-v3"):
1817 self._write_configuration_status(
1818 nsr_id=nsr_id,
1819 vca_index=vca_index,
1820 status="CREATING",
1821 element_under_configuration=element_under_configuration,
1822 element_type=element_type,
1823 )
1824
1825 step = "create execution environment"
1826 self.logger.debug(logging_text + step)
1827
1828 ee_id = None
1829 credentials = None
1830 if vca_type == "k8s_proxy_charm":
1831 ee_id = await self.vca_map[vca_type].install_k8s_proxy_charm(
1832 charm_name=artifact_path[artifact_path.rfind("/") + 1 :],
1833 namespace=namespace,
1834 artifact_path=artifact_path,
1835 db_dict=db_dict,
1836 vca_id=vca_id,
1837 )
1838 elif vca_type == "helm" or vca_type == "helm-v3":
1839 ee_id, credentials = await self.vca_map[
1840 vca_type
1841 ].create_execution_environment(
1842 namespace=nsr_id,
1843 reuse_ee_id=ee_id,
1844 db_dict=db_dict,
1845 config=osm_config,
1846 artifact_path=artifact_path,
1847 chart_model=vca_name,
1848 vca_type=vca_type,
1849 )
1850 else:
1851 ee_id, credentials = await self.vca_map[
1852 vca_type
1853 ].create_execution_environment(
1854 namespace=namespace,
1855 reuse_ee_id=ee_id,
1856 db_dict=db_dict,
1857 vca_id=vca_id,
1858 )
1859
1860 elif vca_type == "native_charm":
1861 step = "Waiting to VM being up and getting IP address"
1862 self.logger.debug(logging_text + step)
1863 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
1864 logging_text,
1865 nsr_id,
1866 vnfr_id,
1867 vdu_id,
1868 vdu_index,
1869 user=None,
1870 pub_key=None,
1871 )
1872 credentials = {"hostname": rw_mgmt_ip}
1873 # get username
1874 username = deep_get(
1875 config_descriptor, ("config-access", "ssh-access", "default-user")
1876 )
1877 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
1878 # merged. Meanwhile let's get username from initial-config-primitive
1879 if not username and initial_config_primitive_list:
1880 for config_primitive in initial_config_primitive_list:
1881 for param in config_primitive.get("parameter", ()):
1882 if param["name"] == "ssh-username":
1883 username = param["value"]
1884 break
1885 if not username:
1886 raise LcmException(
1887 "Cannot determine the username neither with 'initial-config-primitive' nor with "
1888 "'config-access.ssh-access.default-user'"
1889 )
1890 credentials["username"] = username
1891 # n2vc_redesign STEP 3.2
1892
1893 self._write_configuration_status(
1894 nsr_id=nsr_id,
1895 vca_index=vca_index,
1896 status="REGISTERING",
1897 element_under_configuration=element_under_configuration,
1898 element_type=element_type,
1899 )
1900
1901 step = "register execution environment {}".format(credentials)
1902 self.logger.debug(logging_text + step)
1903 ee_id = await self.vca_map[vca_type].register_execution_environment(
1904 credentials=credentials,
1905 namespace=namespace,
1906 db_dict=db_dict,
1907 vca_id=vca_id,
1908 )
1909
1910 # for compatibility with MON/POL modules, the need model and application name at database
1911 # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
1912 ee_id_parts = ee_id.split(".")
1913 db_nsr_update = {db_update_entry + "ee_id": ee_id}
1914 if len(ee_id_parts) >= 2:
1915 model_name = ee_id_parts[0]
1916 application_name = ee_id_parts[1]
1917 db_nsr_update[db_update_entry + "model"] = model_name
1918 db_nsr_update[db_update_entry + "application"] = application_name
1919
1920 # n2vc_redesign STEP 3.3
1921 step = "Install configuration Software"
1922
1923 self._write_configuration_status(
1924 nsr_id=nsr_id,
1925 vca_index=vca_index,
1926 status="INSTALLING SW",
1927 element_under_configuration=element_under_configuration,
1928 element_type=element_type,
1929 other_update=db_nsr_update,
1930 )
1931
1932 # TODO check if already done
1933 self.logger.debug(logging_text + step)
1934 config = None
1935 if vca_type == "native_charm":
1936 config_primitive = next(
1937 (p for p in initial_config_primitive_list if p["name"] == "config"),
1938 None,
1939 )
1940 if config_primitive:
1941 config = self._map_primitive_params(
1942 config_primitive, {}, deploy_params
1943 )
1944 num_units = 1
1945 if vca_type == "lxc_proxy_charm":
1946 if element_type == "NS":
1947 num_units = db_nsr.get("config-units") or 1
1948 elif element_type == "VNF":
1949 num_units = db_vnfr.get("config-units") or 1
1950 elif element_type == "VDU":
1951 for v in db_vnfr["vdur"]:
1952 if vdu_id == v["vdu-id-ref"]:
1953 num_units = v.get("config-units") or 1
1954 break
1955 if vca_type != "k8s_proxy_charm":
1956 await self.vca_map[vca_type].install_configuration_sw(
1957 ee_id=ee_id,
1958 artifact_path=artifact_path,
1959 db_dict=db_dict,
1960 config=config,
1961 num_units=num_units,
1962 vca_id=vca_id,
1963 vca_type=vca_type,
1964 )
1965
1966 # write in db flag of configuration_sw already installed
1967 self.update_db_2(
1968 "nsrs", nsr_id, {db_update_entry + "config_sw_installed": True}
1969 )
1970
1971 # add relations for this VCA (wait for other peers related with this VCA)
1972 is_relation_added = await self._add_vca_relations(
1973 logging_text=logging_text,
1974 nsr_id=nsr_id,
1975 vca_type=vca_type,
1976 vca_index=vca_index,
1977 )
1978
1979 if not is_relation_added:
1980 raise LcmException("Relations could not be added to VCA.")
1981
1982 # if SSH access is required, then get execution environment SSH public
1983 # if native charm we have waited already to VM be UP
1984 if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
1985 pub_key = None
1986 user = None
1987 # self.logger.debug("get ssh key block")
1988 if deep_get(
1989 config_descriptor, ("config-access", "ssh-access", "required")
1990 ):
1991 # self.logger.debug("ssh key needed")
1992 # Needed to inject a ssh key
1993 user = deep_get(
1994 config_descriptor,
1995 ("config-access", "ssh-access", "default-user"),
1996 )
1997 step = "Install configuration Software, getting public ssh key"
1998 pub_key = await self.vca_map[vca_type].get_ee_ssh_public__key(
1999 ee_id=ee_id, db_dict=db_dict, vca_id=vca_id
2000 )
2001
2002 step = "Insert public key into VM user={} ssh_key={}".format(
2003 user, pub_key
2004 )
2005 else:
2006 # self.logger.debug("no need to get ssh key")
2007 step = "Waiting to VM being up and getting IP address"
2008 self.logger.debug(logging_text + step)
2009
2010 # default rw_mgmt_ip to None, avoiding the non definition of the variable
2011 rw_mgmt_ip = None
2012
2013 # n2vc_redesign STEP 5.1
2014 # wait for RO (ip-address) Insert pub_key into VM
2015 if vnfr_id:
2016 if kdu_name:
2017 rw_mgmt_ip, services = await self.wait_kdu_up(
2018 logging_text, nsr_id, vnfr_id, kdu_name
2019 )
2020 vnfd = self.db.get_one(
2021 "vnfds_revisions",
2022 {"_id": f'{db_vnfr["vnfd-id"]}:{db_vnfr["revision"]}'},
2023 )
2024 kdu = get_kdu(vnfd, kdu_name)
2025 kdu_services = [
2026 service["name"] for service in get_kdu_services(kdu)
2027 ]
2028 exposed_services = []
2029 for service in services:
2030 if any(s in service["name"] for s in kdu_services):
2031 exposed_services.append(service)
2032 await self.vca_map[vca_type].exec_primitive(
2033 ee_id=ee_id,
2034 primitive_name="config",
2035 params_dict={
2036 "osm-config": json.dumps(
2037 OsmConfigBuilder(
2038 k8s={"services": exposed_services}
2039 ).build()
2040 )
2041 },
2042 vca_id=vca_id,
2043 )
2044
2045 # This verification is needed in order to avoid trying to add a public key
2046 # to a VM, when the VNF is a KNF (in the edge case where the user creates a VCA
2047 # for a KNF and not for its KDUs, the previous verification gives False, and the code
2048 # jumps to this block, meaning that there is the need to verify if the VNF is actually a VNF
2049 # or it is a KNF)
2050 elif db_vnfr.get("vdur"):
2051 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
2052 logging_text,
2053 nsr_id,
2054 vnfr_id,
2055 vdu_id,
2056 vdu_index,
2057 user=user,
2058 pub_key=pub_key,
2059 )
2060
2061 self.logger.debug(logging_text + " VM_ip_address={}".format(rw_mgmt_ip))
2062
2063 # store rw_mgmt_ip in deploy params for later replacement
2064 deploy_params["rw_mgmt_ip"] = rw_mgmt_ip
2065
2066 # n2vc_redesign STEP 6 Execute initial config primitive
2067 step = "execute initial config primitive"
2068
2069 # wait for dependent primitives execution (NS -> VNF -> VDU)
2070 if initial_config_primitive_list:
2071 await self._wait_dependent_n2vc(nsr_id, vca_deployed_list, vca_index)
2072
2073 # stage, in function of element type: vdu, kdu, vnf or ns
2074 my_vca = vca_deployed_list[vca_index]
2075 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
2076 # VDU or KDU
2077 stage[0] = "Stage 3/5: running Day-1 primitives for VDU."
2078 elif my_vca.get("member-vnf-index"):
2079 # VNF
2080 stage[0] = "Stage 4/5: running Day-1 primitives for VNF."
2081 else:
2082 # NS
2083 stage[0] = "Stage 5/5: running Day-1 primitives for NS."
2084
2085 self._write_configuration_status(
2086 nsr_id=nsr_id, vca_index=vca_index, status="EXECUTING PRIMITIVE"
2087 )
2088
2089 self._write_op_status(op_id=nslcmop_id, stage=stage)
2090
2091 check_if_terminated_needed = True
2092 for initial_config_primitive in initial_config_primitive_list:
2093 # adding information on the vca_deployed if it is a NS execution environment
2094 if not vca_deployed["member-vnf-index"]:
2095 deploy_params["ns_config_info"] = json.dumps(
2096 self._get_ns_config_info(nsr_id)
2097 )
2098 # TODO check if already done
2099 primitive_params_ = self._map_primitive_params(
2100 initial_config_primitive, {}, deploy_params
2101 )
2102
2103 step = "execute primitive '{}' params '{}'".format(
2104 initial_config_primitive["name"], primitive_params_
2105 )
2106 self.logger.debug(logging_text + step)
2107 await self.vca_map[vca_type].exec_primitive(
2108 ee_id=ee_id,
2109 primitive_name=initial_config_primitive["name"],
2110 params_dict=primitive_params_,
2111 db_dict=db_dict,
2112 vca_id=vca_id,
2113 vca_type=vca_type,
2114 )
2115 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
2116 if check_if_terminated_needed:
2117 if config_descriptor.get("terminate-config-primitive"):
2118 self.update_db_2(
2119 "nsrs", nsr_id, {db_update_entry + "needed_terminate": True}
2120 )
2121 check_if_terminated_needed = False
2122
2123 # TODO register in database that primitive is done
2124
2125 # STEP 7 Configure metrics
2126 if vca_type == "helm" or vca_type == "helm-v3":
2127 # TODO: review for those cases where the helm chart is a reference and
2128 # is not part of the NF package
2129 prometheus_jobs = await self.extract_prometheus_scrape_jobs(
2130 ee_id=ee_id,
2131 artifact_path=artifact_path,
2132 ee_config_descriptor=ee_config_descriptor,
2133 vnfr_id=vnfr_id,
2134 nsr_id=nsr_id,
2135 target_ip=rw_mgmt_ip,
2136 element_type=element_type,
2137 vnf_member_index=db_vnfr.get("member-vnf-index-ref", ""),
2138 vdu_id=vdu_id,
2139 vdu_index=vdu_index,
2140 kdu_name=kdu_name,
2141 kdu_index=kdu_index,
2142 )
2143 if prometheus_jobs:
2144 self.update_db_2(
2145 "nsrs",
2146 nsr_id,
2147 {db_update_entry + "prometheus_jobs": prometheus_jobs},
2148 )
2149
2150 for job in prometheus_jobs:
2151 self.db.set_one(
2152 "prometheus_jobs",
2153 {"job_name": job["job_name"]},
2154 job,
2155 upsert=True,
2156 fail_on_empty=False,
2157 )
2158
2159 step = "instantiated at VCA"
2160 self.logger.debug(logging_text + step)
2161
2162 self._write_configuration_status(
2163 nsr_id=nsr_id, vca_index=vca_index, status="READY"
2164 )
2165
2166 except Exception as e: # TODO not use Exception but N2VC exception
2167 # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
2168 if not isinstance(
2169 e, (DbException, N2VCException, LcmException, asyncio.CancelledError)
2170 ):
2171 self.logger.error(
2172 "Exception while {} : {}".format(step, e), exc_info=True
2173 )
2174 self._write_configuration_status(
2175 nsr_id=nsr_id, vca_index=vca_index, status="BROKEN"
2176 )
2177 raise LcmException("{}. {}".format(step, e)) from e
2178
2179 def _write_ns_status(
2180 self,
2181 nsr_id: str,
2182 ns_state: str,
2183 current_operation: str,
2184 current_operation_id: str,
2185 error_description: str = None,
2186 error_detail: str = None,
2187 other_update: dict = None,
2188 ):
2189 """
2190 Update db_nsr fields.
2191 :param nsr_id:
2192 :param ns_state:
2193 :param current_operation:
2194 :param current_operation_id:
2195 :param error_description:
2196 :param error_detail:
2197 :param other_update: Other required changes at database if provided, will be cleared
2198 :return:
2199 """
2200 try:
2201 db_dict = other_update or {}
2202 db_dict[
2203 "_admin.nslcmop"
2204 ] = current_operation_id # for backward compatibility
2205 db_dict["_admin.current-operation"] = current_operation_id
2206 db_dict["_admin.operation-type"] = (
2207 current_operation if current_operation != "IDLE" else None
2208 )
2209 db_dict["currentOperation"] = current_operation
2210 db_dict["currentOperationID"] = current_operation_id
2211 db_dict["errorDescription"] = error_description
2212 db_dict["errorDetail"] = error_detail
2213
2214 if ns_state:
2215 db_dict["nsState"] = ns_state
2216 self.update_db_2("nsrs", nsr_id, db_dict)
2217 except DbException as e:
2218 self.logger.warn("Error writing NS status, ns={}: {}".format(nsr_id, e))
2219
2220 def _write_op_status(
2221 self,
2222 op_id: str,
2223 stage: list = None,
2224 error_message: str = None,
2225 queuePosition: int = 0,
2226 operation_state: str = None,
2227 other_update: dict = None,
2228 ):
2229 try:
2230 db_dict = other_update or {}
2231 db_dict["queuePosition"] = queuePosition
2232 if isinstance(stage, list):
2233 db_dict["stage"] = stage[0]
2234 db_dict["detailed-status"] = " ".join(stage)
2235 elif stage is not None:
2236 db_dict["stage"] = str(stage)
2237
2238 if error_message is not None:
2239 db_dict["errorMessage"] = error_message
2240 if operation_state is not None:
2241 db_dict["operationState"] = operation_state
2242 db_dict["statusEnteredTime"] = time()
2243 self.update_db_2("nslcmops", op_id, db_dict)
2244 except DbException as e:
2245 self.logger.warn(
2246 "Error writing OPERATION status for op_id: {} -> {}".format(op_id, e)
2247 )
2248
2249 def _write_all_config_status(self, db_nsr: dict, status: str):
2250 try:
2251 nsr_id = db_nsr["_id"]
2252 # configurationStatus
2253 config_status = db_nsr.get("configurationStatus")
2254 if config_status:
2255 db_nsr_update = {
2256 "configurationStatus.{}.status".format(index): status
2257 for index, v in enumerate(config_status)
2258 if v
2259 }
2260 # update status
2261 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2262
2263 except DbException as e:
2264 self.logger.warn(
2265 "Error writing all configuration status, ns={}: {}".format(nsr_id, e)
2266 )
2267
2268 def _write_configuration_status(
2269 self,
2270 nsr_id: str,
2271 vca_index: int,
2272 status: str = None,
2273 element_under_configuration: str = None,
2274 element_type: str = None,
2275 other_update: dict = None,
2276 ):
2277 # self.logger.debug('_write_configuration_status(): vca_index={}, status={}'
2278 # .format(vca_index, status))
2279
2280 try:
2281 db_path = "configurationStatus.{}.".format(vca_index)
2282 db_dict = other_update or {}
2283 if status:
2284 db_dict[db_path + "status"] = status
2285 if element_under_configuration:
2286 db_dict[
2287 db_path + "elementUnderConfiguration"
2288 ] = element_under_configuration
2289 if element_type:
2290 db_dict[db_path + "elementType"] = element_type
2291 self.update_db_2("nsrs", nsr_id, db_dict)
2292 except DbException as e:
2293 self.logger.warn(
2294 "Error writing configuration status={}, ns={}, vca_index={}: {}".format(
2295 status, nsr_id, vca_index, e
2296 )
2297 )
2298
2299 async def _do_placement(self, logging_text, db_nslcmop, db_vnfrs):
2300 """
2301 Check and computes the placement, (vim account where to deploy). If it is decided by an external tool, it
2302 sends the request via kafka and wait until the result is wrote at database (nslcmops _admin.plca).
2303 Database is used because the result can be obtained from a different LCM worker in case of HA.
2304 :param logging_text: contains the prefix for logging, with the ns and nslcmop identifiers
2305 :param db_nslcmop: database content of nslcmop
2306 :param db_vnfrs: database content of vnfrs, indexed by member-vnf-index.
2307 :return: True if some modification is done. Modifies database vnfrs and parameter db_vnfr with the
2308 computed 'vim-account-id'
2309 """
2310 modified = False
2311 nslcmop_id = db_nslcmop["_id"]
2312 placement_engine = deep_get(db_nslcmop, ("operationParams", "placement-engine"))
2313 if placement_engine == "PLA":
2314 self.logger.debug(
2315 logging_text + "Invoke and wait for placement optimization"
2316 )
2317 await self.msg.aiowrite("pla", "get_placement", {"nslcmopId": nslcmop_id})
2318 db_poll_interval = 5
2319 wait = db_poll_interval * 10
2320 pla_result = None
2321 while not pla_result and wait >= 0:
2322 await asyncio.sleep(db_poll_interval)
2323 wait -= db_poll_interval
2324 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
2325 pla_result = deep_get(db_nslcmop, ("_admin", "pla"))
2326
2327 if not pla_result:
2328 raise LcmException(
2329 "Placement timeout for nslcmopId={}".format(nslcmop_id)
2330 )
2331
2332 for pla_vnf in pla_result["vnf"]:
2333 vnfr = db_vnfrs.get(pla_vnf["member-vnf-index"])
2334 if not pla_vnf.get("vimAccountId") or not vnfr:
2335 continue
2336 modified = True
2337 self.db.set_one(
2338 "vnfrs",
2339 {"_id": vnfr["_id"]},
2340 {"vim-account-id": pla_vnf["vimAccountId"]},
2341 )
2342 # Modifies db_vnfrs
2343 vnfr["vim-account-id"] = pla_vnf["vimAccountId"]
2344 return modified
2345
2346 def _gather_vnfr_healing_alerts(self, vnfr, vnfd):
2347 alerts = []
2348 nsr_id = vnfr["nsr-id-ref"]
2349 df = vnfd.get("df", [{}])[0]
2350 # Checking for auto-healing configuration
2351 if "healing-aspect" in df:
2352 healing_aspects = df["healing-aspect"]
2353 for healing in healing_aspects:
2354 for healing_policy in healing.get("healing-policy", ()):
2355 vdu_id = healing_policy["vdu-id"]
2356 vdur = next(
2357 (vdur for vdur in vnfr["vdur"] if vdu_id == vdur["vdu-id-ref"]),
2358 {},
2359 )
2360 if not vdur:
2361 continue
2362 metric_name = "vm_status"
2363 vdu_name = vdur.get("name")
2364 vnf_member_index = vnfr["member-vnf-index-ref"]
2365 uuid = str(uuid4())
2366 name = f"healing_{uuid}"
2367 action = healing_policy
2368 # action_on_recovery = healing.get("action-on-recovery")
2369 # cooldown_time = healing.get("cooldown-time")
2370 # day1 = healing.get("day1")
2371 alert = {
2372 "uuid": uuid,
2373 "name": name,
2374 "metric": metric_name,
2375 "tags": {
2376 "ns_id": nsr_id,
2377 "vnf_member_index": vnf_member_index,
2378 "vdu_name": vdu_name,
2379 },
2380 "alarm_status": "ok",
2381 "action_type": "healing",
2382 "action": action,
2383 }
2384 alerts.append(alert)
2385 return alerts
2386
2387 def _gather_vnfr_scaling_alerts(self, vnfr, vnfd):
2388 alerts = []
2389 nsr_id = vnfr["nsr-id-ref"]
2390 df = vnfd.get("df", [{}])[0]
2391 # Checking for auto-scaling configuration
2392 if "scaling-aspect" in df:
2393 scaling_aspects = df["scaling-aspect"]
2394 all_vnfd_monitoring_params = {}
2395 for ivld in vnfd.get("int-virtual-link-desc", ()):
2396 for mp in ivld.get("monitoring-parameters", ()):
2397 all_vnfd_monitoring_params[mp.get("id")] = mp
2398 for vdu in vnfd.get("vdu", ()):
2399 for mp in vdu.get("monitoring-parameter", ()):
2400 all_vnfd_monitoring_params[mp.get("id")] = mp
2401 for df in vnfd.get("df", ()):
2402 for mp in df.get("monitoring-parameter", ()):
2403 all_vnfd_monitoring_params[mp.get("id")] = mp
2404 for scaling_aspect in scaling_aspects:
2405 scaling_group_name = scaling_aspect.get("name", "")
2406 # Get monitored VDUs
2407 all_monitored_vdus = set()
2408 for delta in scaling_aspect.get("aspect-delta-details", {}).get(
2409 "deltas", ()
2410 ):
2411 for vdu_delta in delta.get("vdu-delta", ()):
2412 all_monitored_vdus.add(vdu_delta.get("id"))
2413 monitored_vdurs = list(
2414 filter(
2415 lambda vdur: vdur["vdu-id-ref"] in all_monitored_vdus,
2416 vnfr["vdur"],
2417 )
2418 )
2419 if not monitored_vdurs:
2420 self.logger.error(
2421 "Scaling criteria is referring to a vnf-monitoring-param that does not contain a reference to a vdu or vnf metric"
2422 )
2423 continue
2424 for scaling_policy in scaling_aspect.get("scaling-policy", ()):
2425 if scaling_policy["scaling-type"] != "automatic":
2426 continue
2427 threshold_time = scaling_policy.get("threshold-time", "1")
2428 cooldown_time = scaling_policy.get("cooldown-time", "0")
2429 for scaling_criteria in scaling_policy["scaling-criteria"]:
2430 monitoring_param_ref = scaling_criteria.get(
2431 "vnf-monitoring-param-ref"
2432 )
2433 vnf_monitoring_param = all_vnfd_monitoring_params[
2434 monitoring_param_ref
2435 ]
2436 for vdur in monitored_vdurs:
2437 vdu_id = vdur["vdu-id-ref"]
2438 metric_name = vnf_monitoring_param.get("performance-metric")
2439 metric_name = f"osm_{metric_name}"
2440 vnf_member_index = vnfr["member-vnf-index-ref"]
2441 scalein_threshold = scaling_criteria.get(
2442 "scale-in-threshold"
2443 )
2444 scaleout_threshold = scaling_criteria.get(
2445 "scale-out-threshold"
2446 )
2447 # Looking for min/max-number-of-instances
2448 instances_min_number = 1
2449 instances_max_number = 1
2450 vdu_profile = df["vdu-profile"]
2451 if vdu_profile:
2452 profile = next(
2453 item for item in vdu_profile if item["id"] == vdu_id
2454 )
2455 instances_min_number = profile.get(
2456 "min-number-of-instances", 1
2457 )
2458 instances_max_number = profile.get(
2459 "max-number-of-instances", 1
2460 )
2461
2462 if scalein_threshold:
2463 uuid = str(uuid4())
2464 name = f"scalein_{uuid}"
2465 operation = scaling_criteria[
2466 "scale-in-relational-operation"
2467 ]
2468 rel_operator = self.rel_operation_types.get(
2469 operation, "<="
2470 )
2471 metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
2472 expression = f"(count ({metric_selector}) > {instances_min_number}) and (avg({metric_selector}) {rel_operator} {scalein_threshold})"
2473 labels = {
2474 "ns_id": nsr_id,
2475 "vnf_member_index": vnf_member_index,
2476 "vdu_id": vdu_id,
2477 }
2478 prom_cfg = {
2479 "alert": name,
2480 "expr": expression,
2481 "for": str(threshold_time) + "m",
2482 "labels": labels,
2483 }
2484 action = scaling_policy
2485 action = {
2486 "scaling-group": scaling_group_name,
2487 "cooldown-time": cooldown_time,
2488 }
2489 alert = {
2490 "uuid": uuid,
2491 "name": name,
2492 "metric": metric_name,
2493 "tags": {
2494 "ns_id": nsr_id,
2495 "vnf_member_index": vnf_member_index,
2496 "vdu_id": vdu_id,
2497 },
2498 "alarm_status": "ok",
2499 "action_type": "scale_in",
2500 "action": action,
2501 "prometheus_config": prom_cfg,
2502 }
2503 alerts.append(alert)
2504
2505 if scaleout_threshold:
2506 uuid = str(uuid4())
2507 name = f"scaleout_{uuid}"
2508 operation = scaling_criteria[
2509 "scale-out-relational-operation"
2510 ]
2511 rel_operator = self.rel_operation_types.get(
2512 operation, "<="
2513 )
2514 metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
2515 expression = f"(count ({metric_selector}) < {instances_max_number}) and (avg({metric_selector}) {rel_operator} {scaleout_threshold})"
2516 labels = {
2517 "ns_id": nsr_id,
2518 "vnf_member_index": vnf_member_index,
2519 "vdu_id": vdu_id,
2520 }
2521 prom_cfg = {
2522 "alert": name,
2523 "expr": expression,
2524 "for": str(threshold_time) + "m",
2525 "labels": labels,
2526 }
2527 action = scaling_policy
2528 action = {
2529 "scaling-group": scaling_group_name,
2530 "cooldown-time": cooldown_time,
2531 }
2532 alert = {
2533 "uuid": uuid,
2534 "name": name,
2535 "metric": metric_name,
2536 "tags": {
2537 "ns_id": nsr_id,
2538 "vnf_member_index": vnf_member_index,
2539 "vdu_id": vdu_id,
2540 },
2541 "alarm_status": "ok",
2542 "action_type": "scale_out",
2543 "action": action,
2544 "prometheus_config": prom_cfg,
2545 }
2546 alerts.append(alert)
2547 return alerts
2548
2549 def _gather_vnfr_alarm_alerts(self, vnfr, vnfd):
2550 alerts = []
2551 nsr_id = vnfr["nsr-id-ref"]
2552 vnf_member_index = vnfr["member-vnf-index-ref"]
2553
2554 # Checking for VNF alarm configuration
2555 for vdur in vnfr["vdur"]:
2556 vdu_id = vdur["vdu-id-ref"]
2557 vdu = next(filter(lambda vdu: vdu["id"] == vdu_id, vnfd["vdu"]))
2558 if "alarm" in vdu:
2559 # Get VDU monitoring params, since alerts are based on them
2560 vdu_monitoring_params = {}
2561 for mp in vdu.get("monitoring-parameter", []):
2562 vdu_monitoring_params[mp.get("id")] = mp
2563 if not vdu_monitoring_params:
2564 self.logger.error(
2565 "VDU alarm refers to a VDU monitoring param, but there are no VDU monitoring params in the VDU"
2566 )
2567 continue
2568 # Get alarms in the VDU
2569 alarm_descriptors = vdu["alarm"]
2570 # Create VDU alarms for each alarm in the VDU
2571 for alarm_descriptor in alarm_descriptors:
2572 # Check that the VDU alarm refers to a proper monitoring param
2573 alarm_monitoring_param = alarm_descriptor.get(
2574 "vnf-monitoring-param-ref", ""
2575 )
2576 vdu_specific_monitoring_param = vdu_monitoring_params.get(
2577 alarm_monitoring_param, {}
2578 )
2579 if not vdu_specific_monitoring_param:
2580 self.logger.error(
2581 "VDU alarm refers to a VDU monitoring param not present in the VDU"
2582 )
2583 continue
2584 metric_name = vdu_specific_monitoring_param.get(
2585 "performance-metric"
2586 )
2587 if not metric_name:
2588 self.logger.error(
2589 "VDU alarm refers to a VDU monitoring param that has no associated performance-metric"
2590 )
2591 continue
2592 # Set params of the alarm to be created in Prometheus
2593 metric_name = f"osm_{metric_name}"
2594 metric_threshold = alarm_descriptor.get("value")
2595 uuid = str(uuid4())
2596 alert_name = f"vdu_alarm_{uuid}"
2597 operation = alarm_descriptor["operation"]
2598 rel_operator = self.rel_operation_types.get(operation, "<=")
2599 metric_selector = f'{metric_name}{{ns_id="{nsr_id}", vnf_member_index="{vnf_member_index}", vdu_id="{vdu_id}"}}'
2600 expression = f"{metric_selector} {rel_operator} {metric_threshold}"
2601 labels = {
2602 "ns_id": nsr_id,
2603 "vnf_member_index": vnf_member_index,
2604 "vdu_id": vdu_id,
2605 "vdu_name": "{{ $labels.vdu_name }}",
2606 }
2607 prom_cfg = {
2608 "alert": alert_name,
2609 "expr": expression,
2610 "for": "1m", # default value. Ideally, this should be related to an IM param, but there is not such param
2611 "labels": labels,
2612 }
2613 alarm_action = dict()
2614 for action_type in ["ok", "insufficient-data", "alarm"]:
2615 if (
2616 "actions" in alarm_descriptor
2617 and action_type in alarm_descriptor["actions"]
2618 ):
2619 alarm_action[action_type] = alarm_descriptor["actions"][
2620 action_type
2621 ]
2622 alert = {
2623 "uuid": uuid,
2624 "name": alert_name,
2625 "metric": metric_name,
2626 "tags": {
2627 "ns_id": nsr_id,
2628 "vnf_member_index": vnf_member_index,
2629 "vdu_id": vdu_id,
2630 },
2631 "alarm_status": "ok",
2632 "action_type": "vdu_alarm",
2633 "action": alarm_action,
2634 "prometheus_config": prom_cfg,
2635 }
2636 alerts.append(alert)
2637 return alerts
2638
2639 def update_nsrs_with_pla_result(self, params):
2640 try:
2641 nslcmop_id = deep_get(params, ("placement", "nslcmopId"))
2642 self.update_db_2(
2643 "nslcmops", nslcmop_id, {"_admin.pla": params.get("placement")}
2644 )
2645 except Exception as e:
2646 self.logger.warn("Update failed for nslcmop_id={}:{}".format(nslcmop_id, e))
2647
2648 async def instantiate(self, nsr_id, nslcmop_id):
2649 """
2650
2651 :param nsr_id: ns instance to deploy
2652 :param nslcmop_id: operation to run
2653 :return:
2654 """
2655
2656 # Try to lock HA task here
2657 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
2658 if not task_is_locked_by_me:
2659 self.logger.debug(
2660 "instantiate() task is not locked by me, ns={}".format(nsr_id)
2661 )
2662 return
2663
2664 logging_text = "Task ns={} instantiate={} ".format(nsr_id, nslcmop_id)
2665 self.logger.debug(logging_text + "Enter")
2666
2667 # get all needed from database
2668
2669 # database nsrs record
2670 db_nsr = None
2671
2672 # database nslcmops record
2673 db_nslcmop = None
2674
2675 # update operation on nsrs
2676 db_nsr_update = {}
2677 # update operation on nslcmops
2678 db_nslcmop_update = {}
2679
2680 timeout_ns_deploy = self.timeout.ns_deploy
2681
2682 nslcmop_operation_state = None
2683 db_vnfrs = {} # vnf's info indexed by member-index
2684 # n2vc_info = {}
2685 tasks_dict_info = {} # from task to info text
2686 exc = None
2687 error_list = []
2688 stage = [
2689 "Stage 1/5: preparation of the environment.",
2690 "Waiting for previous operations to terminate.",
2691 "",
2692 ]
2693 # ^ stage, step, VIM progress
2694 try:
2695 # wait for any previous tasks in process
2696 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
2697
2698 # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
2699 stage[1] = "Reading from database."
2700 # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
2701 db_nsr_update["detailed-status"] = "creating"
2702 db_nsr_update["operational-status"] = "init"
2703 self._write_ns_status(
2704 nsr_id=nsr_id,
2705 ns_state="BUILDING",
2706 current_operation="INSTANTIATING",
2707 current_operation_id=nslcmop_id,
2708 other_update=db_nsr_update,
2709 )
2710 self._write_op_status(op_id=nslcmop_id, stage=stage, queuePosition=0)
2711
2712 # read from db: operation
2713 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
2714 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
2715 if db_nslcmop["operationParams"].get("additionalParamsForVnf"):
2716 db_nslcmop["operationParams"]["additionalParamsForVnf"] = json.loads(
2717 db_nslcmop["operationParams"]["additionalParamsForVnf"]
2718 )
2719 ns_params = db_nslcmop.get("operationParams")
2720 if ns_params and ns_params.get("timeout_ns_deploy"):
2721 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
2722
2723 # read from db: ns
2724 stage[1] = "Getting nsr={} from db.".format(nsr_id)
2725 self.logger.debug(logging_text + stage[1])
2726 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
2727 stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
2728 self.logger.debug(logging_text + stage[1])
2729 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
2730 self.fs.sync(db_nsr["nsd-id"])
2731 db_nsr["nsd"] = nsd
2732 # nsr_name = db_nsr["name"] # TODO short-name??
2733
2734 # read from db: vnf's of this ns
2735 stage[1] = "Getting vnfrs from db."
2736 self.logger.debug(logging_text + stage[1])
2737 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
2738
2739 # read from db: vnfd's for every vnf
2740 db_vnfds = [] # every vnfd data
2741
2742 # for each vnf in ns, read vnfd
2743 for vnfr in db_vnfrs_list:
2744 if vnfr.get("kdur"):
2745 kdur_list = []
2746 for kdur in vnfr["kdur"]:
2747 if kdur.get("additionalParams"):
2748 kdur["additionalParams"] = json.loads(
2749 kdur["additionalParams"]
2750 )
2751 kdur_list.append(kdur)
2752 vnfr["kdur"] = kdur_list
2753
2754 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
2755 vnfd_id = vnfr["vnfd-id"]
2756 vnfd_ref = vnfr["vnfd-ref"]
2757 self.fs.sync(vnfd_id)
2758
2759 # if we haven't this vnfd, read it from db
2760 if vnfd_id not in db_vnfds:
2761 # read from db
2762 stage[1] = "Getting vnfd={} id='{}' from db.".format(
2763 vnfd_id, vnfd_ref
2764 )
2765 self.logger.debug(logging_text + stage[1])
2766 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
2767
2768 # store vnfd
2769 db_vnfds.append(vnfd)
2770
2771 # Get or generates the _admin.deployed.VCA list
2772 vca_deployed_list = None
2773 if db_nsr["_admin"].get("deployed"):
2774 vca_deployed_list = db_nsr["_admin"]["deployed"].get("VCA")
2775 if vca_deployed_list is None:
2776 vca_deployed_list = []
2777 configuration_status_list = []
2778 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
2779 db_nsr_update["configurationStatus"] = configuration_status_list
2780 # add _admin.deployed.VCA to db_nsr dictionary, value=vca_deployed_list
2781 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
2782 elif isinstance(vca_deployed_list, dict):
2783 # maintain backward compatibility. Change a dict to list at database
2784 vca_deployed_list = list(vca_deployed_list.values())
2785 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
2786 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
2787
2788 if not isinstance(
2789 deep_get(db_nsr, ("_admin", "deployed", "RO", "vnfd")), list
2790 ):
2791 populate_dict(db_nsr, ("_admin", "deployed", "RO", "vnfd"), [])
2792 db_nsr_update["_admin.deployed.RO.vnfd"] = []
2793
2794 # set state to INSTANTIATED. When instantiated NBI will not delete directly
2795 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
2796 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2797 self.db.set_list(
2798 "vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "INSTANTIATED"}
2799 )
2800
2801 # n2vc_redesign STEP 2 Deploy Network Scenario
2802 stage[0] = "Stage 2/5: deployment of KDUs, VMs and execution environments."
2803 self._write_op_status(op_id=nslcmop_id, stage=stage)
2804
2805 stage[1] = "Deploying KDUs."
2806 # self.logger.debug(logging_text + "Before deploy_kdus")
2807 # Call to deploy_kdus in case exists the "vdu:kdu" param
2808 await self.deploy_kdus(
2809 logging_text=logging_text,
2810 nsr_id=nsr_id,
2811 nslcmop_id=nslcmop_id,
2812 db_vnfrs=db_vnfrs,
2813 db_vnfds=db_vnfds,
2814 task_instantiation_info=tasks_dict_info,
2815 )
2816
2817 stage[1] = "Getting VCA public key."
2818 # n2vc_redesign STEP 1 Get VCA public ssh-key
2819 # feature 1429. Add n2vc public key to needed VMs
2820 n2vc_key = self.n2vc.get_public_key()
2821 n2vc_key_list = [n2vc_key]
2822 if self.vca_config.public_key:
2823 n2vc_key_list.append(self.vca_config.public_key)
2824
2825 stage[1] = "Deploying NS at VIM."
2826 task_ro = asyncio.ensure_future(
2827 self.instantiate_RO(
2828 logging_text=logging_text,
2829 nsr_id=nsr_id,
2830 nsd=nsd,
2831 db_nsr=db_nsr,
2832 db_nslcmop=db_nslcmop,
2833 db_vnfrs=db_vnfrs,
2834 db_vnfds=db_vnfds,
2835 n2vc_key_list=n2vc_key_list,
2836 stage=stage,
2837 )
2838 )
2839 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_RO", task_ro)
2840 tasks_dict_info[task_ro] = "Deploying at VIM"
2841
2842 # n2vc_redesign STEP 3 to 6 Deploy N2VC
2843 stage[1] = "Deploying Execution Environments."
2844 self.logger.debug(logging_text + stage[1])
2845
2846 # create namespace and certificate if any helm based EE is present in the NS
2847 if check_helm_ee_in_ns(db_vnfds):
2848 await self.vca_map["helm-v3"].setup_ns_namespace(
2849 name=nsr_id,
2850 )
2851 # create TLS certificates
2852 await self.vca_map["helm-v3"].create_tls_certificate(
2853 secret_name=self.EE_TLS_NAME,
2854 dns_prefix="*",
2855 nsr_id=nsr_id,
2856 usage="server auth",
2857 namespace=nsr_id,
2858 )
2859
2860 nsi_id = None # TODO put nsi_id when this nsr belongs to a NSI
2861 for vnf_profile in get_vnf_profiles(nsd):
2862 vnfd_id = vnf_profile["vnfd-id"]
2863 vnfd = find_in_list(db_vnfds, lambda a_vnf: a_vnf["id"] == vnfd_id)
2864 member_vnf_index = str(vnf_profile["id"])
2865 db_vnfr = db_vnfrs[member_vnf_index]
2866 base_folder = vnfd["_admin"]["storage"]
2867 vdu_id = None
2868 vdu_index = 0
2869 vdu_name = None
2870 kdu_name = None
2871 kdu_index = None
2872
2873 # Get additional parameters
2874 deploy_params = {"OSM": get_osm_params(db_vnfr)}
2875 if db_vnfr.get("additionalParamsForVnf"):
2876 deploy_params.update(
2877 parse_yaml_strings(db_vnfr["additionalParamsForVnf"].copy())
2878 )
2879
2880 descriptor_config = get_configuration(vnfd, vnfd["id"])
2881 if descriptor_config:
2882 self._deploy_n2vc(
2883 logging_text=logging_text
2884 + "member_vnf_index={} ".format(member_vnf_index),
2885 db_nsr=db_nsr,
2886 db_vnfr=db_vnfr,
2887 nslcmop_id=nslcmop_id,
2888 nsr_id=nsr_id,
2889 nsi_id=nsi_id,
2890 vnfd_id=vnfd_id,
2891 vdu_id=vdu_id,
2892 kdu_name=kdu_name,
2893 member_vnf_index=member_vnf_index,
2894 vdu_index=vdu_index,
2895 kdu_index=kdu_index,
2896 vdu_name=vdu_name,
2897 deploy_params=deploy_params,
2898 descriptor_config=descriptor_config,
2899 base_folder=base_folder,
2900 task_instantiation_info=tasks_dict_info,
2901 stage=stage,
2902 )
2903
2904 # Deploy charms for each VDU that supports one.
2905 for vdud in get_vdu_list(vnfd):
2906 vdu_id = vdud["id"]
2907 descriptor_config = get_configuration(vnfd, vdu_id)
2908 vdur = find_in_list(
2909 db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id
2910 )
2911
2912 if vdur.get("additionalParams"):
2913 deploy_params_vdu = parse_yaml_strings(vdur["additionalParams"])
2914 else:
2915 deploy_params_vdu = deploy_params
2916 deploy_params_vdu["OSM"] = get_osm_params(
2917 db_vnfr, vdu_id, vdu_count_index=0
2918 )
2919 vdud_count = get_number_of_instances(vnfd, vdu_id)
2920
2921 self.logger.debug("VDUD > {}".format(vdud))
2922 self.logger.debug(
2923 "Descriptor config > {}".format(descriptor_config)
2924 )
2925 if descriptor_config:
2926 vdu_name = None
2927 kdu_name = None
2928 kdu_index = None
2929 for vdu_index in range(vdud_count):
2930 # TODO vnfr_params["rw_mgmt_ip"] = vdur["ip-address"]
2931 self._deploy_n2vc(
2932 logging_text=logging_text
2933 + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
2934 member_vnf_index, vdu_id, vdu_index
2935 ),
2936 db_nsr=db_nsr,
2937 db_vnfr=db_vnfr,
2938 nslcmop_id=nslcmop_id,
2939 nsr_id=nsr_id,
2940 nsi_id=nsi_id,
2941 vnfd_id=vnfd_id,
2942 vdu_id=vdu_id,
2943 kdu_name=kdu_name,
2944 kdu_index=kdu_index,
2945 member_vnf_index=member_vnf_index,
2946 vdu_index=vdu_index,
2947 vdu_name=vdu_name,
2948 deploy_params=deploy_params_vdu,
2949 descriptor_config=descriptor_config,
2950 base_folder=base_folder,
2951 task_instantiation_info=tasks_dict_info,
2952 stage=stage,
2953 )
2954 for kdud in get_kdu_list(vnfd):
2955 kdu_name = kdud["name"]
2956 descriptor_config = get_configuration(vnfd, kdu_name)
2957 if descriptor_config:
2958 vdu_id = None
2959 vdu_index = 0
2960 vdu_name = None
2961 kdu_index, kdur = next(
2962 x
2963 for x in enumerate(db_vnfr["kdur"])
2964 if x[1]["kdu-name"] == kdu_name
2965 )
2966 deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
2967 if kdur.get("additionalParams"):
2968 deploy_params_kdu.update(
2969 parse_yaml_strings(kdur["additionalParams"].copy())
2970 )
2971
2972 self._deploy_n2vc(
2973 logging_text=logging_text,
2974 db_nsr=db_nsr,
2975 db_vnfr=db_vnfr,
2976 nslcmop_id=nslcmop_id,
2977 nsr_id=nsr_id,
2978 nsi_id=nsi_id,
2979 vnfd_id=vnfd_id,
2980 vdu_id=vdu_id,
2981 kdu_name=kdu_name,
2982 member_vnf_index=member_vnf_index,
2983 vdu_index=vdu_index,
2984 kdu_index=kdu_index,
2985 vdu_name=vdu_name,
2986 deploy_params=deploy_params_kdu,
2987 descriptor_config=descriptor_config,
2988 base_folder=base_folder,
2989 task_instantiation_info=tasks_dict_info,
2990 stage=stage,
2991 )
2992
2993 # Check if each vnf has exporter for metric collection if so update prometheus job records
2994 if "exporters-endpoints" in vnfd.get("df")[0]:
2995 exporter_config = vnfd.get("df")[0].get("exporters-endpoints")
2996 self.logger.debug("exporter config :{}".format(exporter_config))
2997 artifact_path = "{}/{}/{}".format(
2998 base_folder["folder"],
2999 base_folder["pkg-dir"],
3000 "exporter-endpoint",
3001 )
3002 ee_id = None
3003 ee_config_descriptor = exporter_config
3004 vnfr_id = db_vnfr["id"]
3005 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
3006 logging_text,
3007 nsr_id,
3008 vnfr_id,
3009 vdu_id=None,
3010 vdu_index=None,
3011 user=None,
3012 pub_key=None,
3013 )
3014 self.logger.debug("rw_mgmt_ip:{}".format(rw_mgmt_ip))
3015 self.logger.debug("Artifact_path:{}".format(artifact_path))
3016 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
3017 vdu_id_for_prom = None
3018 vdu_index_for_prom = None
3019 for x in get_iterable(db_vnfr, "vdur"):
3020 vdu_id_for_prom = x.get("vdu-id-ref")
3021 vdu_index_for_prom = x.get("count-index")
3022 prometheus_jobs = await self.extract_prometheus_scrape_jobs(
3023 ee_id=ee_id,
3024 artifact_path=artifact_path,
3025 ee_config_descriptor=ee_config_descriptor,
3026 vnfr_id=vnfr_id,
3027 nsr_id=nsr_id,
3028 target_ip=rw_mgmt_ip,
3029 element_type="VDU",
3030 vdu_id=vdu_id_for_prom,
3031 vdu_index=vdu_index_for_prom,
3032 )
3033
3034 self.logger.debug("Prometheus job:{}".format(prometheus_jobs))
3035 if prometheus_jobs:
3036 db_nsr_update["_admin.deployed.prometheus_jobs"] = prometheus_jobs
3037 self.update_db_2(
3038 "nsrs",
3039 nsr_id,
3040 db_nsr_update,
3041 )
3042
3043 for job in prometheus_jobs:
3044 self.db.set_one(
3045 "prometheus_jobs",
3046 {"job_name": job["job_name"]},
3047 job,
3048 upsert=True,
3049 fail_on_empty=False,
3050 )
3051
3052 # Check if this NS has a charm configuration
3053 descriptor_config = nsd.get("ns-configuration")
3054 if descriptor_config and descriptor_config.get("juju"):
3055 vnfd_id = None
3056 db_vnfr = None
3057 member_vnf_index = None
3058 vdu_id = None
3059 kdu_name = None
3060 kdu_index = None
3061 vdu_index = 0
3062 vdu_name = None
3063
3064 # Get additional parameters
3065 deploy_params = {"OSM": {"vim_account_id": ns_params["vimAccountId"]}}
3066 if db_nsr.get("additionalParamsForNs"):
3067 deploy_params.update(
3068 parse_yaml_strings(db_nsr["additionalParamsForNs"].copy())
3069 )
3070 base_folder = nsd["_admin"]["storage"]
3071 self._deploy_n2vc(
3072 logging_text=logging_text,
3073 db_nsr=db_nsr,
3074 db_vnfr=db_vnfr,
3075 nslcmop_id=nslcmop_id,
3076 nsr_id=nsr_id,
3077 nsi_id=nsi_id,
3078 vnfd_id=vnfd_id,
3079 vdu_id=vdu_id,
3080 kdu_name=kdu_name,
3081 member_vnf_index=member_vnf_index,
3082 vdu_index=vdu_index,
3083 kdu_index=kdu_index,
3084 vdu_name=vdu_name,
3085 deploy_params=deploy_params,
3086 descriptor_config=descriptor_config,
3087 base_folder=base_folder,
3088 task_instantiation_info=tasks_dict_info,
3089 stage=stage,
3090 )
3091
3092 # rest of staff will be done at finally
3093
3094 except (
3095 ROclient.ROClientException,
3096 DbException,
3097 LcmException,
3098 N2VCException,
3099 ) as e:
3100 self.logger.error(
3101 logging_text + "Exit Exception while '{}': {}".format(stage[1], e)
3102 )
3103 exc = e
3104 except asyncio.CancelledError:
3105 self.logger.error(
3106 logging_text + "Cancelled Exception while '{}'".format(stage[1])
3107 )
3108 exc = "Operation was cancelled"
3109 except Exception as e:
3110 exc = traceback.format_exc()
3111 self.logger.critical(
3112 logging_text + "Exit Exception while '{}': {}".format(stage[1], e),
3113 exc_info=True,
3114 )
3115 finally:
3116 if exc:
3117 error_list.append(str(exc))
3118 try:
3119 # wait for pending tasks
3120 if tasks_dict_info:
3121 stage[1] = "Waiting for instantiate pending tasks."
3122 self.logger.debug(logging_text + stage[1])
3123 error_list += await self._wait_for_tasks(
3124 logging_text,
3125 tasks_dict_info,
3126 timeout_ns_deploy,
3127 stage,
3128 nslcmop_id,
3129 nsr_id=nsr_id,
3130 )
3131 stage[1] = stage[2] = ""
3132 except asyncio.CancelledError:
3133 error_list.append("Cancelled")
3134 # TODO cancel all tasks
3135 except Exception as exc:
3136 error_list.append(str(exc))
3137
3138 # update operation-status
3139 db_nsr_update["operational-status"] = "running"
3140 # let's begin with VCA 'configured' status (later we can change it)
3141 db_nsr_update["config-status"] = "configured"
3142 for task, task_name in tasks_dict_info.items():
3143 if not task.done() or task.cancelled() or task.exception():
3144 if task_name.startswith(self.task_name_deploy_vca):
3145 # A N2VC task is pending
3146 db_nsr_update["config-status"] = "failed"
3147 else:
3148 # RO or KDU task is pending
3149 db_nsr_update["operational-status"] = "failed"
3150
3151 # update status at database
3152 if error_list:
3153 error_detail = ". ".join(error_list)
3154 self.logger.error(logging_text + error_detail)
3155 error_description_nslcmop = "{} Detail: {}".format(
3156 stage[0], error_detail
3157 )
3158 error_description_nsr = "Operation: INSTANTIATING.{}, {}".format(
3159 nslcmop_id, stage[0]
3160 )
3161
3162 db_nsr_update["detailed-status"] = (
3163 error_description_nsr + " Detail: " + error_detail
3164 )
3165 db_nslcmop_update["detailed-status"] = error_detail
3166 nslcmop_operation_state = "FAILED"
3167 ns_state = "BROKEN"
3168 else:
3169 error_detail = None
3170 error_description_nsr = error_description_nslcmop = None
3171 ns_state = "READY"
3172 db_nsr_update["detailed-status"] = "Done"
3173 db_nslcmop_update["detailed-status"] = "Done"
3174 nslcmop_operation_state = "COMPLETED"
3175 # Gather auto-healing and auto-scaling alerts for each vnfr
3176 healing_alerts = []
3177 scaling_alerts = []
3178 for vnfr in self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id}):
3179 vnfd = next(
3180 (sub for sub in db_vnfds if sub["_id"] == vnfr["vnfd-id"]), None
3181 )
3182 healing_alerts = self._gather_vnfr_healing_alerts(vnfr, vnfd)
3183 for alert in healing_alerts:
3184 self.logger.info(f"Storing healing alert in MongoDB: {alert}")
3185 self.db.create("alerts", alert)
3186
3187 scaling_alerts = self._gather_vnfr_scaling_alerts(vnfr, vnfd)
3188 for alert in scaling_alerts:
3189 self.logger.info(f"Storing scaling alert in MongoDB: {alert}")
3190 self.db.create("alerts", alert)
3191
3192 alarm_alerts = self._gather_vnfr_alarm_alerts(vnfr, vnfd)
3193 for alert in alarm_alerts:
3194 self.logger.info(f"Storing VNF alarm alert in MongoDB: {alert}")
3195 self.db.create("alerts", alert)
3196 if db_nsr:
3197 self._write_ns_status(
3198 nsr_id=nsr_id,
3199 ns_state=ns_state,
3200 current_operation="IDLE",
3201 current_operation_id=None,
3202 error_description=error_description_nsr,
3203 error_detail=error_detail,
3204 other_update=db_nsr_update,
3205 )
3206 self._write_op_status(
3207 op_id=nslcmop_id,
3208 stage="",
3209 error_message=error_description_nslcmop,
3210 operation_state=nslcmop_operation_state,
3211 other_update=db_nslcmop_update,
3212 )
3213
3214 if nslcmop_operation_state:
3215 try:
3216 await self.msg.aiowrite(
3217 "ns",
3218 "instantiated",
3219 {
3220 "nsr_id": nsr_id,
3221 "nslcmop_id": nslcmop_id,
3222 "operationState": nslcmop_operation_state,
3223 "startTime": db_nslcmop["startTime"],
3224 "links": db_nslcmop["links"],
3225 "operationParams": {
3226 "nsInstanceId": nsr_id,
3227 "nsdId": db_nsr["nsd-id"],
3228 },
3229 },
3230 )
3231 except Exception as e:
3232 self.logger.error(
3233 logging_text + "kafka_write notification Exception {}".format(e)
3234 )
3235
3236 self.logger.debug(logging_text + "Exit")
3237 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
3238
3239 def _get_vnfd(self, vnfd_id: str, projects_read: str, cached_vnfds: Dict[str, Any]):
3240 if vnfd_id not in cached_vnfds:
3241 cached_vnfds[vnfd_id] = self.db.get_one(
3242 "vnfds", {"id": vnfd_id, "_admin.projects_read": projects_read}
3243 )
3244 return cached_vnfds[vnfd_id]
3245
3246 def _get_vnfr(self, nsr_id: str, vnf_profile_id: str, cached_vnfrs: Dict[str, Any]):
3247 if vnf_profile_id not in cached_vnfrs:
3248 cached_vnfrs[vnf_profile_id] = self.db.get_one(
3249 "vnfrs",
3250 {
3251 "member-vnf-index-ref": vnf_profile_id,
3252 "nsr-id-ref": nsr_id,
3253 },
3254 )
3255 return cached_vnfrs[vnf_profile_id]
3256
3257 def _is_deployed_vca_in_relation(
3258 self, vca: DeployedVCA, relation: Relation
3259 ) -> bool:
3260 found = False
3261 for endpoint in (relation.provider, relation.requirer):
3262 if endpoint["kdu-resource-profile-id"]:
3263 continue
3264 found = (
3265 vca.vnf_profile_id == endpoint.vnf_profile_id
3266 and vca.vdu_profile_id == endpoint.vdu_profile_id
3267 and vca.execution_environment_ref == endpoint.execution_environment_ref
3268 )
3269 if found:
3270 break
3271 return found
3272
3273 def _update_ee_relation_data_with_implicit_data(
3274 self, nsr_id, nsd, ee_relation_data, cached_vnfds, vnf_profile_id: str = None
3275 ):
3276 ee_relation_data = safe_get_ee_relation(
3277 nsr_id, ee_relation_data, vnf_profile_id=vnf_profile_id
3278 )
3279 ee_relation_level = EELevel.get_level(ee_relation_data)
3280 if (ee_relation_level in (EELevel.VNF, EELevel.VDU)) and not ee_relation_data[
3281 "execution-environment-ref"
3282 ]:
3283 vnf_profile = get_vnf_profile(nsd, ee_relation_data["vnf-profile-id"])
3284 vnfd_id = vnf_profile["vnfd-id"]
3285 project = nsd["_admin"]["projects_read"][0]
3286 db_vnfd = self._get_vnfd(vnfd_id, project, cached_vnfds)
3287 entity_id = (
3288 vnfd_id
3289 if ee_relation_level == EELevel.VNF
3290 else ee_relation_data["vdu-profile-id"]
3291 )
3292 ee = get_juju_ee_ref(db_vnfd, entity_id)
3293 if not ee:
3294 raise Exception(
3295 f"not execution environments found for ee_relation {ee_relation_data}"
3296 )
3297 ee_relation_data["execution-environment-ref"] = ee["id"]
3298 return ee_relation_data
3299
3300 def _get_ns_relations(
3301 self,
3302 nsr_id: str,
3303 nsd: Dict[str, Any],
3304 vca: DeployedVCA,
3305 cached_vnfds: Dict[str, Any],
3306 ) -> List[Relation]:
3307 relations = []
3308 db_ns_relations = get_ns_configuration_relation_list(nsd)
3309 for r in db_ns_relations:
3310 provider_dict = None
3311 requirer_dict = None
3312 if all(key in r for key in ("provider", "requirer")):
3313 provider_dict = r["provider"]
3314 requirer_dict = r["requirer"]
3315 elif "entities" in r:
3316 provider_id = r["entities"][0]["id"]
3317 provider_dict = {
3318 "nsr-id": nsr_id,
3319 "endpoint": r["entities"][0]["endpoint"],
3320 }
3321 if provider_id != nsd["id"]:
3322 provider_dict["vnf-profile-id"] = provider_id
3323 requirer_id = r["entities"][1]["id"]
3324 requirer_dict = {
3325 "nsr-id": nsr_id,
3326 "endpoint": r["entities"][1]["endpoint"],
3327 }
3328 if requirer_id != nsd["id"]:
3329 requirer_dict["vnf-profile-id"] = requirer_id
3330 else:
3331 raise Exception(
3332 "provider/requirer or entities must be included in the relation."
3333 )
3334 relation_provider = self._update_ee_relation_data_with_implicit_data(
3335 nsr_id, nsd, provider_dict, cached_vnfds
3336 )
3337 relation_requirer = self._update_ee_relation_data_with_implicit_data(
3338 nsr_id, nsd, requirer_dict, cached_vnfds
3339 )
3340 provider = EERelation(relation_provider)
3341 requirer = EERelation(relation_requirer)
3342 relation = Relation(r["name"], provider, requirer)
3343 vca_in_relation = self._is_deployed_vca_in_relation(vca, relation)
3344 if vca_in_relation:
3345 relations.append(relation)
3346 return relations
3347
3348 def _get_vnf_relations(
3349 self,
3350 nsr_id: str,
3351 nsd: Dict[str, Any],
3352 vca: DeployedVCA,
3353 cached_vnfds: Dict[str, Any],
3354 ) -> List[Relation]:
3355 relations = []
3356 if vca.target_element == "ns":
3357 self.logger.debug("VCA is a NS charm, not a VNF.")
3358 return relations
3359 vnf_profile = get_vnf_profile(nsd, vca.vnf_profile_id)
3360 vnf_profile_id = vnf_profile["id"]
3361 vnfd_id = vnf_profile["vnfd-id"]
3362 project = nsd["_admin"]["projects_read"][0]
3363 db_vnfd = self._get_vnfd(vnfd_id, project, cached_vnfds)
3364 db_vnf_relations = get_relation_list(db_vnfd, vnfd_id)
3365 for r in db_vnf_relations:
3366 provider_dict = None
3367 requirer_dict = None
3368 if all(key in r for key in ("provider", "requirer")):
3369 provider_dict = r["provider"]
3370 requirer_dict = r["requirer"]
3371 elif "entities" in r:
3372 provider_id = r["entities"][0]["id"]
3373 provider_dict = {
3374 "nsr-id": nsr_id,
3375 "vnf-profile-id": vnf_profile_id,
3376 "endpoint": r["entities"][0]["endpoint"],
3377 }
3378 if provider_id != vnfd_id:
3379 provider_dict["vdu-profile-id"] = provider_id
3380 requirer_id = r["entities"][1]["id"]
3381 requirer_dict = {
3382 "nsr-id": nsr_id,
3383 "vnf-profile-id": vnf_profile_id,
3384 "endpoint": r["entities"][1]["endpoint"],
3385 }
3386 if requirer_id != vnfd_id:
3387 requirer_dict["vdu-profile-id"] = requirer_id
3388 else:
3389 raise Exception(
3390 "provider/requirer or entities must be included in the relation."
3391 )
3392 relation_provider = self._update_ee_relation_data_with_implicit_data(
3393 nsr_id, nsd, provider_dict, cached_vnfds, vnf_profile_id=vnf_profile_id
3394 )
3395 relation_requirer = self._update_ee_relation_data_with_implicit_data(
3396 nsr_id, nsd, requirer_dict, cached_vnfds, vnf_profile_id=vnf_profile_id
3397 )
3398 provider = EERelation(relation_provider)
3399 requirer = EERelation(relation_requirer)
3400 relation = Relation(r["name"], provider, requirer)
3401 vca_in_relation = self._is_deployed_vca_in_relation(vca, relation)
3402 if vca_in_relation:
3403 relations.append(relation)
3404 return relations
3405
3406 def _get_kdu_resource_data(
3407 self,
3408 ee_relation: EERelation,
3409 db_nsr: Dict[str, Any],
3410 cached_vnfds: Dict[str, Any],
3411 ) -> DeployedK8sResource:
3412 nsd = get_nsd(db_nsr)
3413 vnf_profiles = get_vnf_profiles(nsd)
3414 vnfd_id = find_in_list(
3415 vnf_profiles,
3416 lambda vnf_profile: vnf_profile["id"] == ee_relation.vnf_profile_id,
3417 )["vnfd-id"]
3418 project = nsd["_admin"]["projects_read"][0]
3419 db_vnfd = self._get_vnfd(vnfd_id, project, cached_vnfds)
3420 kdu_resource_profile = get_kdu_resource_profile(
3421 db_vnfd, ee_relation.kdu_resource_profile_id
3422 )
3423 kdu_name = kdu_resource_profile["kdu-name"]
3424 deployed_kdu, _ = get_deployed_kdu(
3425 db_nsr.get("_admin", ()).get("deployed", ()),
3426 kdu_name,
3427 ee_relation.vnf_profile_id,
3428 )
3429 deployed_kdu.update({"resource-name": kdu_resource_profile["resource-name"]})
3430 return deployed_kdu
3431
3432 def _get_deployed_component(
3433 self,
3434 ee_relation: EERelation,
3435 db_nsr: Dict[str, Any],
3436 cached_vnfds: Dict[str, Any],
3437 ) -> DeployedComponent:
3438 nsr_id = db_nsr["_id"]
3439 deployed_component = None
3440 ee_level = EELevel.get_level(ee_relation)
3441 if ee_level == EELevel.NS:
3442 vca = get_deployed_vca(db_nsr, {"vdu_id": None, "member-vnf-index": None})
3443 if vca:
3444 deployed_component = DeployedVCA(nsr_id, vca)
3445 elif ee_level == EELevel.VNF:
3446 vca = get_deployed_vca(
3447 db_nsr,
3448 {
3449 "vdu_id": None,
3450 "member-vnf-index": ee_relation.vnf_profile_id,
3451 "ee_descriptor_id": ee_relation.execution_environment_ref,
3452 },
3453 )
3454 if vca:
3455 deployed_component = DeployedVCA(nsr_id, vca)
3456 elif ee_level == EELevel.VDU:
3457 vca = get_deployed_vca(
3458 db_nsr,
3459 {
3460 "vdu_id": ee_relation.vdu_profile_id,
3461 "member-vnf-index": ee_relation.vnf_profile_id,
3462 "ee_descriptor_id": ee_relation.execution_environment_ref,
3463 },
3464 )
3465 if vca:
3466 deployed_component = DeployedVCA(nsr_id, vca)
3467 elif ee_level == EELevel.KDU:
3468 kdu_resource_data = self._get_kdu_resource_data(
3469 ee_relation, db_nsr, cached_vnfds
3470 )
3471 if kdu_resource_data:
3472 deployed_component = DeployedK8sResource(kdu_resource_data)
3473 return deployed_component
3474
3475 async def _add_relation(
3476 self,
3477 relation: Relation,
3478 vca_type: str,
3479 db_nsr: Dict[str, Any],
3480 cached_vnfds: Dict[str, Any],
3481 cached_vnfrs: Dict[str, Any],
3482 ) -> bool:
3483 deployed_provider = self._get_deployed_component(
3484 relation.provider, db_nsr, cached_vnfds
3485 )
3486 deployed_requirer = self._get_deployed_component(
3487 relation.requirer, db_nsr, cached_vnfds
3488 )
3489 if (
3490 deployed_provider
3491 and deployed_requirer
3492 and deployed_provider.config_sw_installed
3493 and deployed_requirer.config_sw_installed
3494 ):
3495 provider_db_vnfr = (
3496 self._get_vnfr(
3497 relation.provider.nsr_id,
3498 relation.provider.vnf_profile_id,
3499 cached_vnfrs,
3500 )
3501 if relation.provider.vnf_profile_id
3502 else None
3503 )
3504 requirer_db_vnfr = (
3505 self._get_vnfr(
3506 relation.requirer.nsr_id,
3507 relation.requirer.vnf_profile_id,
3508 cached_vnfrs,
3509 )
3510 if relation.requirer.vnf_profile_id
3511 else None
3512 )
3513 provider_vca_id = self.get_vca_id(provider_db_vnfr, db_nsr)
3514 requirer_vca_id = self.get_vca_id(requirer_db_vnfr, db_nsr)
3515 provider_relation_endpoint = RelationEndpoint(
3516 deployed_provider.ee_id,
3517 provider_vca_id,
3518 relation.provider.endpoint,
3519 )
3520 requirer_relation_endpoint = RelationEndpoint(
3521 deployed_requirer.ee_id,
3522 requirer_vca_id,
3523 relation.requirer.endpoint,
3524 )
3525 try:
3526 await self.vca_map[vca_type].add_relation(
3527 provider=provider_relation_endpoint,
3528 requirer=requirer_relation_endpoint,
3529 )
3530 except N2VCException as exception:
3531 self.logger.error(exception)
3532 raise LcmException(exception)
3533 return True
3534 return False
3535
3536 async def _add_vca_relations(
3537 self,
3538 logging_text,
3539 nsr_id,
3540 vca_type: str,
3541 vca_index: int,
3542 timeout: int = 3600,
3543 ) -> bool:
3544 # steps:
3545 # 1. find all relations for this VCA
3546 # 2. wait for other peers related
3547 # 3. add relations
3548
3549 try:
3550 # STEP 1: find all relations for this VCA
3551
3552 # read nsr record
3553 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
3554 nsd = get_nsd(db_nsr)
3555
3556 # this VCA data
3557 deployed_vca_dict = get_deployed_vca_list(db_nsr)[vca_index]
3558 my_vca = DeployedVCA(nsr_id, deployed_vca_dict)
3559
3560 cached_vnfds = {}
3561 cached_vnfrs = {}
3562 relations = []
3563 relations.extend(self._get_ns_relations(nsr_id, nsd, my_vca, cached_vnfds))
3564 relations.extend(self._get_vnf_relations(nsr_id, nsd, my_vca, cached_vnfds))
3565
3566 # if no relations, terminate
3567 if not relations:
3568 self.logger.debug(logging_text + " No relations")
3569 return True
3570
3571 self.logger.debug(logging_text + " adding relations {}".format(relations))
3572
3573 # add all relations
3574 start = time()
3575 while True:
3576 # check timeout
3577 now = time()
3578 if now - start >= timeout:
3579 self.logger.error(logging_text + " : timeout adding relations")
3580 return False
3581
3582 # reload nsr from database (we need to update record: _admin.deployed.VCA)
3583 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
3584
3585 # for each relation, find the VCA's related
3586 for relation in relations.copy():
3587 added = await self._add_relation(
3588 relation,
3589 vca_type,
3590 db_nsr,
3591 cached_vnfds,
3592 cached_vnfrs,
3593 )
3594 if added:
3595 relations.remove(relation)
3596
3597 if not relations:
3598 self.logger.debug("Relations added")
3599 break
3600 await asyncio.sleep(5.0)
3601
3602 return True
3603
3604 except Exception as e:
3605 self.logger.warn(logging_text + " ERROR adding relations: {}".format(e))
3606 return False
3607
3608 async def _install_kdu(
3609 self,
3610 nsr_id: str,
3611 nsr_db_path: str,
3612 vnfr_data: dict,
3613 kdu_index: int,
3614 kdud: dict,
3615 vnfd: dict,
3616 k8s_instance_info: dict,
3617 k8params: dict = None,
3618 timeout: int = 600,
3619 vca_id: str = None,
3620 ):
3621 try:
3622 k8sclustertype = k8s_instance_info["k8scluster-type"]
3623 # Instantiate kdu
3624 db_dict_install = {
3625 "collection": "nsrs",
3626 "filter": {"_id": nsr_id},
3627 "path": nsr_db_path,
3628 }
3629
3630 if k8s_instance_info.get("kdu-deployment-name"):
3631 kdu_instance = k8s_instance_info.get("kdu-deployment-name")
3632 else:
3633 kdu_instance = self.k8scluster_map[
3634 k8sclustertype
3635 ].generate_kdu_instance_name(
3636 db_dict=db_dict_install,
3637 kdu_model=k8s_instance_info["kdu-model"],
3638 kdu_name=k8s_instance_info["kdu-name"],
3639 )
3640
3641 # Update the nsrs table with the kdu-instance value
3642 self.update_db_2(
3643 item="nsrs",
3644 _id=nsr_id,
3645 _desc={nsr_db_path + ".kdu-instance": kdu_instance},
3646 )
3647
3648 # Update the nsrs table with the actual namespace being used, if the k8scluster-type is `juju` or
3649 # `juju-bundle`. This verification is needed because there is not a standard/homogeneous namespace
3650 # between the Helm Charts and Juju Bundles-based KNFs. If we found a way of having an homogeneous
3651 # namespace, this first verification could be removed, and the next step would be done for any kind
3652 # of KNF.
3653 # TODO -> find a way to have an homogeneous namespace between the Helm Charts and Juju Bundles-based
3654 # KNFs (Bug 2027: https://osm.etsi.org/bugzilla/show_bug.cgi?id=2027)
3655 if k8sclustertype in ("juju", "juju-bundle"):
3656 # First, verify if the current namespace is present in the `_admin.projects_read` (if not, it means
3657 # that the user passed a namespace which he wants its KDU to be deployed in)
3658 if (
3659 self.db.count(
3660 table="nsrs",
3661 q_filter={
3662 "_id": nsr_id,
3663 "_admin.projects_write": k8s_instance_info["namespace"],
3664 "_admin.projects_read": k8s_instance_info["namespace"],
3665 },
3666 )
3667 > 0
3668 ):
3669 self.logger.debug(
3670 f"Updating namespace/model for Juju Bundle from {k8s_instance_info['namespace']} to {kdu_instance}"
3671 )
3672 self.update_db_2(
3673 item="nsrs",
3674 _id=nsr_id,
3675 _desc={f"{nsr_db_path}.namespace": kdu_instance},
3676 )
3677 k8s_instance_info["namespace"] = kdu_instance
3678
3679 await self.k8scluster_map[k8sclustertype].install(
3680 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
3681 kdu_model=k8s_instance_info["kdu-model"],
3682 atomic=True,
3683 params=k8params,
3684 db_dict=db_dict_install,
3685 timeout=timeout,
3686 kdu_name=k8s_instance_info["kdu-name"],
3687 namespace=k8s_instance_info["namespace"],
3688 kdu_instance=kdu_instance,
3689 vca_id=vca_id,
3690 )
3691
3692 # Obtain services to obtain management service ip
3693 services = await self.k8scluster_map[k8sclustertype].get_services(
3694 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
3695 kdu_instance=kdu_instance,
3696 namespace=k8s_instance_info["namespace"],
3697 )
3698
3699 # Obtain management service info (if exists)
3700 vnfr_update_dict = {}
3701 kdu_config = get_configuration(vnfd, kdud["name"])
3702 if kdu_config:
3703 target_ee_list = kdu_config.get("execution-environment-list", [])
3704 else:
3705 target_ee_list = []
3706
3707 if services:
3708 vnfr_update_dict["kdur.{}.services".format(kdu_index)] = services
3709 mgmt_services = [
3710 service
3711 for service in kdud.get("service", [])
3712 if service.get("mgmt-service")
3713 ]
3714 for mgmt_service in mgmt_services:
3715 for service in services:
3716 if service["name"].startswith(mgmt_service["name"]):
3717 # Mgmt service found, Obtain service ip
3718 ip = service.get("external_ip", service.get("cluster_ip"))
3719 if isinstance(ip, list) and len(ip) == 1:
3720 ip = ip[0]
3721
3722 vnfr_update_dict[
3723 "kdur.{}.ip-address".format(kdu_index)
3724 ] = ip
3725
3726 # Check if must update also mgmt ip at the vnf
3727 service_external_cp = mgmt_service.get(
3728 "external-connection-point-ref"
3729 )
3730 if service_external_cp:
3731 if (
3732 deep_get(vnfd, ("mgmt-interface", "cp"))
3733 == service_external_cp
3734 ):
3735 vnfr_update_dict["ip-address"] = ip
3736
3737 if find_in_list(
3738 target_ee_list,
3739 lambda ee: ee.get(
3740 "external-connection-point-ref", ""
3741 )
3742 == service_external_cp,
3743 ):
3744 vnfr_update_dict[
3745 "kdur.{}.ip-address".format(kdu_index)
3746 ] = ip
3747 break
3748 else:
3749 self.logger.warn(
3750 "Mgmt service name: {} not found".format(
3751 mgmt_service["name"]
3752 )
3753 )
3754
3755 vnfr_update_dict["kdur.{}.status".format(kdu_index)] = "READY"
3756 self.update_db_2("vnfrs", vnfr_data.get("_id"), vnfr_update_dict)
3757
3758 kdu_config = get_configuration(vnfd, k8s_instance_info["kdu-name"])
3759 if (
3760 kdu_config
3761 and kdu_config.get("initial-config-primitive")
3762 and get_juju_ee_ref(vnfd, k8s_instance_info["kdu-name"]) is None
3763 ):
3764 initial_config_primitive_list = kdu_config.get(
3765 "initial-config-primitive"
3766 )
3767 initial_config_primitive_list.sort(key=lambda val: int(val["seq"]))
3768
3769 for initial_config_primitive in initial_config_primitive_list:
3770 primitive_params_ = self._map_primitive_params(
3771 initial_config_primitive, {}, {}
3772 )
3773
3774 await asyncio.wait_for(
3775 self.k8scluster_map[k8sclustertype].exec_primitive(
3776 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
3777 kdu_instance=kdu_instance,
3778 primitive_name=initial_config_primitive["name"],
3779 params=primitive_params_,
3780 db_dict=db_dict_install,
3781 vca_id=vca_id,
3782 ),
3783 timeout=timeout,
3784 )
3785
3786 except Exception as e:
3787 # Prepare update db with error and raise exception
3788 try:
3789 self.update_db_2(
3790 "nsrs", nsr_id, {nsr_db_path + ".detailed-status": str(e)}
3791 )
3792 self.update_db_2(
3793 "vnfrs",
3794 vnfr_data.get("_id"),
3795 {"kdur.{}.status".format(kdu_index): "ERROR"},
3796 )
3797 except Exception:
3798 # ignore to keep original exception
3799 pass
3800 # reraise original error
3801 raise
3802
3803 return kdu_instance
3804
3805 async def deploy_kdus(
3806 self,
3807 logging_text,
3808 nsr_id,
3809 nslcmop_id,
3810 db_vnfrs,
3811 db_vnfds,
3812 task_instantiation_info,
3813 ):
3814 # Launch kdus if present in the descriptor
3815
3816 k8scluster_id_2_uuic = {
3817 "helm-chart-v3": {},
3818 "helm-chart": {},
3819 "juju-bundle": {},
3820 }
3821
3822 async def _get_cluster_id(cluster_id, cluster_type):
3823 nonlocal k8scluster_id_2_uuic
3824 if cluster_id in k8scluster_id_2_uuic[cluster_type]:
3825 return k8scluster_id_2_uuic[cluster_type][cluster_id]
3826
3827 # check if K8scluster is creating and wait look if previous tasks in process
3828 task_name, task_dependency = self.lcm_tasks.lookfor_related(
3829 "k8scluster", cluster_id
3830 )
3831 if task_dependency:
3832 text = "Waiting for related tasks '{}' on k8scluster {} to be completed".format(
3833 task_name, cluster_id
3834 )
3835 self.logger.debug(logging_text + text)
3836 await asyncio.wait(task_dependency, timeout=3600)
3837
3838 db_k8scluster = self.db.get_one(
3839 "k8sclusters", {"_id": cluster_id}, fail_on_empty=False
3840 )
3841 if not db_k8scluster:
3842 raise LcmException("K8s cluster {} cannot be found".format(cluster_id))
3843
3844 k8s_id = deep_get(db_k8scluster, ("_admin", cluster_type, "id"))
3845 if not k8s_id:
3846 if cluster_type == "helm-chart-v3":
3847 try:
3848 # backward compatibility for existing clusters that have not been initialized for helm v3
3849 k8s_credentials = yaml.safe_dump(
3850 db_k8scluster.get("credentials")
3851 )
3852 k8s_id, uninstall_sw = await self.k8sclusterhelm3.init_env(
3853 k8s_credentials, reuse_cluster_uuid=cluster_id
3854 )
3855 db_k8scluster_update = {}
3856 db_k8scluster_update["_admin.helm-chart-v3.error_msg"] = None
3857 db_k8scluster_update["_admin.helm-chart-v3.id"] = k8s_id
3858 db_k8scluster_update[
3859 "_admin.helm-chart-v3.created"
3860 ] = uninstall_sw
3861 db_k8scluster_update[
3862 "_admin.helm-chart-v3.operationalState"
3863 ] = "ENABLED"
3864 self.update_db_2(
3865 "k8sclusters", cluster_id, db_k8scluster_update
3866 )
3867 except Exception as e:
3868 self.logger.error(
3869 logging_text
3870 + "error initializing helm-v3 cluster: {}".format(str(e))
3871 )
3872 raise LcmException(
3873 "K8s cluster '{}' has not been initialized for '{}'".format(
3874 cluster_id, cluster_type
3875 )
3876 )
3877 else:
3878 raise LcmException(
3879 "K8s cluster '{}' has not been initialized for '{}'".format(
3880 cluster_id, cluster_type
3881 )
3882 )
3883 k8scluster_id_2_uuic[cluster_type][cluster_id] = k8s_id
3884 return k8s_id
3885
3886 logging_text += "Deploy kdus: "
3887 step = ""
3888 try:
3889 db_nsr_update = {"_admin.deployed.K8s": []}
3890 self.update_db_2("nsrs", nsr_id, db_nsr_update)
3891
3892 index = 0
3893 updated_cluster_list = []
3894 updated_v3_cluster_list = []
3895
3896 for vnfr_data in db_vnfrs.values():
3897 vca_id = self.get_vca_id(vnfr_data, {})
3898 for kdu_index, kdur in enumerate(get_iterable(vnfr_data, "kdur")):
3899 # Step 0: Prepare and set parameters
3900 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
3901 vnfd_id = vnfr_data.get("vnfd-id")
3902 vnfd_with_id = find_in_list(
3903 db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id
3904 )
3905 kdud = next(
3906 kdud
3907 for kdud in vnfd_with_id["kdu"]
3908 if kdud["name"] == kdur["kdu-name"]
3909 )
3910 namespace = kdur.get("k8s-namespace")
3911 kdu_deployment_name = kdur.get("kdu-deployment-name")
3912 if kdur.get("helm-chart"):
3913 kdumodel = kdur["helm-chart"]
3914 # Default version: helm3, if helm-version is v2 assign v2
3915 k8sclustertype = "helm-chart-v3"
3916 self.logger.debug("kdur: {}".format(kdur))
3917 if (
3918 kdur.get("helm-version")
3919 and kdur.get("helm-version") == "v2"
3920 ):
3921 k8sclustertype = "helm-chart"
3922 elif kdur.get("juju-bundle"):
3923 kdumodel = kdur["juju-bundle"]
3924 k8sclustertype = "juju-bundle"
3925 else:
3926 raise LcmException(
3927 "kdu type for kdu='{}.{}' is neither helm-chart nor "
3928 "juju-bundle. Maybe an old NBI version is running".format(
3929 vnfr_data["member-vnf-index-ref"], kdur["kdu-name"]
3930 )
3931 )
3932 # check if kdumodel is a file and exists
3933 try:
3934 vnfd_with_id = find_in_list(
3935 db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id
3936 )
3937 storage = deep_get(vnfd_with_id, ("_admin", "storage"))
3938 if storage: # may be not present if vnfd has not artifacts
3939 # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
3940 if storage["pkg-dir"]:
3941 filename = "{}/{}/{}s/{}".format(
3942 storage["folder"],
3943 storage["pkg-dir"],
3944 k8sclustertype,
3945 kdumodel,
3946 )
3947 else:
3948 filename = "{}/Scripts/{}s/{}".format(
3949 storage["folder"],
3950 k8sclustertype,
3951 kdumodel,
3952 )
3953 if self.fs.file_exists(
3954 filename, mode="file"
3955 ) or self.fs.file_exists(filename, mode="dir"):
3956 kdumodel = self.fs.path + filename
3957 except (asyncio.TimeoutError, asyncio.CancelledError):
3958 raise
3959 except Exception: # it is not a file
3960 pass
3961
3962 k8s_cluster_id = kdur["k8s-cluster"]["id"]
3963 step = "Synchronize repos for k8s cluster '{}'".format(
3964 k8s_cluster_id
3965 )
3966 cluster_uuid = await _get_cluster_id(k8s_cluster_id, k8sclustertype)
3967
3968 # Synchronize repos
3969 if (
3970 k8sclustertype == "helm-chart"
3971 and cluster_uuid not in updated_cluster_list
3972 ) or (
3973 k8sclustertype == "helm-chart-v3"
3974 and cluster_uuid not in updated_v3_cluster_list
3975 ):
3976 del_repo_list, added_repo_dict = await asyncio.ensure_future(
3977 self.k8scluster_map[k8sclustertype].synchronize_repos(
3978 cluster_uuid=cluster_uuid
3979 )
3980 )
3981 if del_repo_list or added_repo_dict:
3982 if k8sclustertype == "helm-chart":
3983 unset = {
3984 "_admin.helm_charts_added." + item: None
3985 for item in del_repo_list
3986 }
3987 updated = {
3988 "_admin.helm_charts_added." + item: name
3989 for item, name in added_repo_dict.items()
3990 }
3991 updated_cluster_list.append(cluster_uuid)
3992 elif k8sclustertype == "helm-chart-v3":
3993 unset = {
3994 "_admin.helm_charts_v3_added." + item: None
3995 for item in del_repo_list
3996 }
3997 updated = {
3998 "_admin.helm_charts_v3_added." + item: name
3999 for item, name in added_repo_dict.items()
4000 }
4001 updated_v3_cluster_list.append(cluster_uuid)
4002 self.logger.debug(
4003 logging_text + "repos synchronized on k8s cluster "
4004 "'{}' to_delete: {}, to_add: {}".format(
4005 k8s_cluster_id, del_repo_list, added_repo_dict
4006 )
4007 )
4008 self.db.set_one(
4009 "k8sclusters",
4010 {"_id": k8s_cluster_id},
4011 updated,
4012 unset=unset,
4013 )
4014
4015 # Instantiate kdu
4016 step = "Instantiating KDU {}.{} in k8s cluster {}".format(
4017 vnfr_data["member-vnf-index-ref"],
4018 kdur["kdu-name"],
4019 k8s_cluster_id,
4020 )
4021 k8s_instance_info = {
4022 "kdu-instance": None,
4023 "k8scluster-uuid": cluster_uuid,
4024 "k8scluster-type": k8sclustertype,
4025 "member-vnf-index": vnfr_data["member-vnf-index-ref"],
4026 "kdu-name": kdur["kdu-name"],
4027 "kdu-model": kdumodel,
4028 "namespace": namespace,
4029 "kdu-deployment-name": kdu_deployment_name,
4030 }
4031 db_path = "_admin.deployed.K8s.{}".format(index)
4032 db_nsr_update[db_path] = k8s_instance_info
4033 self.update_db_2("nsrs", nsr_id, db_nsr_update)
4034 vnfd_with_id = find_in_list(
4035 db_vnfds, lambda vnf: vnf["_id"] == vnfd_id
4036 )
4037 task = asyncio.ensure_future(
4038 self._install_kdu(
4039 nsr_id,
4040 db_path,
4041 vnfr_data,
4042 kdu_index,
4043 kdud,
4044 vnfd_with_id,
4045 k8s_instance_info,
4046 k8params=desc_params,
4047 timeout=1800,
4048 vca_id=vca_id,
4049 )
4050 )
4051 self.lcm_tasks.register(
4052 "ns",
4053 nsr_id,
4054 nslcmop_id,
4055 "instantiate_KDU-{}".format(index),
4056 task,
4057 )
4058 task_instantiation_info[task] = "Deploying KDU {}".format(
4059 kdur["kdu-name"]
4060 )
4061
4062 index += 1
4063
4064 except (LcmException, asyncio.CancelledError):
4065 raise
4066 except Exception as e:
4067 msg = "Exception {} while {}: {}".format(type(e).__name__, step, e)
4068 if isinstance(e, (N2VCException, DbException)):
4069 self.logger.error(logging_text + msg)
4070 else:
4071 self.logger.critical(logging_text + msg, exc_info=True)
4072 raise LcmException(msg)
4073 finally:
4074 if db_nsr_update:
4075 self.update_db_2("nsrs", nsr_id, db_nsr_update)
4076
4077 def _deploy_n2vc(
4078 self,
4079 logging_text,
4080 db_nsr,
4081 db_vnfr,
4082 nslcmop_id,
4083 nsr_id,
4084 nsi_id,
4085 vnfd_id,
4086 vdu_id,
4087 kdu_name,
4088 member_vnf_index,
4089 vdu_index,
4090 kdu_index,
4091 vdu_name,
4092 deploy_params,
4093 descriptor_config,
4094 base_folder,
4095 task_instantiation_info,
4096 stage,
4097 ):
4098 # launch instantiate_N2VC in a asyncio task and register task object
4099 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
4100 # if not found, create one entry and update database
4101 # fill db_nsr._admin.deployed.VCA.<index>
4102
4103 self.logger.debug(
4104 logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id)
4105 )
4106
4107 charm_name = ""
4108 get_charm_name = False
4109 if "execution-environment-list" in descriptor_config:
4110 ee_list = descriptor_config.get("execution-environment-list", [])
4111 elif "juju" in descriptor_config:
4112 ee_list = [descriptor_config] # ns charms
4113 if "execution-environment-list" not in descriptor_config:
4114 # charm name is only required for ns charms
4115 get_charm_name = True
4116 else: # other types as script are not supported
4117 ee_list = []
4118
4119 for ee_item in ee_list:
4120 self.logger.debug(
4121 logging_text
4122 + "_deploy_n2vc ee_item juju={}, helm={}".format(
4123 ee_item.get("juju"), ee_item.get("helm-chart")
4124 )
4125 )
4126 ee_descriptor_id = ee_item.get("id")
4127 if ee_item.get("juju"):
4128 vca_name = ee_item["juju"].get("charm")
4129 if get_charm_name:
4130 charm_name = self.find_charm_name(db_nsr, str(vca_name))
4131 vca_type = (
4132 "lxc_proxy_charm"
4133 if ee_item["juju"].get("charm") is not None
4134 else "native_charm"
4135 )
4136 if ee_item["juju"].get("cloud") == "k8s":
4137 vca_type = "k8s_proxy_charm"
4138 elif ee_item["juju"].get("proxy") is False:
4139 vca_type = "native_charm"
4140 elif ee_item.get("helm-chart"):
4141 vca_name = ee_item["helm-chart"]
4142 if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
4143 vca_type = "helm"
4144 else:
4145 vca_type = "helm-v3"
4146 else:
4147 self.logger.debug(
4148 logging_text + "skipping non juju neither charm configuration"
4149 )
4150 continue
4151
4152 vca_index = -1
4153 for vca_index, vca_deployed in enumerate(
4154 db_nsr["_admin"]["deployed"]["VCA"]
4155 ):
4156 if not vca_deployed:
4157 continue
4158 if (
4159 vca_deployed.get("member-vnf-index") == member_vnf_index
4160 and vca_deployed.get("vdu_id") == vdu_id
4161 and vca_deployed.get("kdu_name") == kdu_name
4162 and vca_deployed.get("vdu_count_index", 0) == vdu_index
4163 and vca_deployed.get("ee_descriptor_id") == ee_descriptor_id
4164 ):
4165 break
4166 else:
4167 # not found, create one.
4168 target = (
4169 "ns" if not member_vnf_index else "vnf/{}".format(member_vnf_index)
4170 )
4171 if vdu_id:
4172 target += "/vdu/{}/{}".format(vdu_id, vdu_index or 0)
4173 elif kdu_name:
4174 target += "/kdu/{}".format(kdu_name)
4175 vca_deployed = {
4176 "target_element": target,
4177 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
4178 "member-vnf-index": member_vnf_index,
4179 "vdu_id": vdu_id,
4180 "kdu_name": kdu_name,
4181 "vdu_count_index": vdu_index,
4182 "operational-status": "init", # TODO revise
4183 "detailed-status": "", # TODO revise
4184 "step": "initial-deploy", # TODO revise
4185 "vnfd_id": vnfd_id,
4186 "vdu_name": vdu_name,
4187 "type": vca_type,
4188 "ee_descriptor_id": ee_descriptor_id,
4189 "charm_name": charm_name,
4190 }
4191 vca_index += 1
4192
4193 # create VCA and configurationStatus in db
4194 db_dict = {
4195 "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
4196 "configurationStatus.{}".format(vca_index): dict(),
4197 }
4198 self.update_db_2("nsrs", nsr_id, db_dict)
4199
4200 db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
4201
4202 self.logger.debug("N2VC > NSR_ID > {}".format(nsr_id))
4203 self.logger.debug("N2VC > DB_NSR > {}".format(db_nsr))
4204 self.logger.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed))
4205
4206 # Launch task
4207 task_n2vc = asyncio.ensure_future(
4208 self.instantiate_N2VC(
4209 logging_text=logging_text,
4210 vca_index=vca_index,
4211 nsi_id=nsi_id,
4212 db_nsr=db_nsr,
4213 db_vnfr=db_vnfr,
4214 vdu_id=vdu_id,
4215 kdu_name=kdu_name,
4216 vdu_index=vdu_index,
4217 kdu_index=kdu_index,
4218 deploy_params=deploy_params,
4219 config_descriptor=descriptor_config,
4220 base_folder=base_folder,
4221 nslcmop_id=nslcmop_id,
4222 stage=stage,
4223 vca_type=vca_type,
4224 vca_name=vca_name,
4225 ee_config_descriptor=ee_item,
4226 )
4227 )
4228 self.lcm_tasks.register(
4229 "ns",
4230 nsr_id,
4231 nslcmop_id,
4232 "instantiate_N2VC-{}".format(vca_index),
4233 task_n2vc,
4234 )
4235 task_instantiation_info[
4236 task_n2vc
4237 ] = self.task_name_deploy_vca + " {}.{}".format(
4238 member_vnf_index or "", vdu_id or ""
4239 )
4240
4241 @staticmethod
4242 def _create_nslcmop(nsr_id, operation, params):
4243 """
4244 Creates a ns-lcm-opp content to be stored at database.
4245 :param nsr_id: internal id of the instance
4246 :param operation: instantiate, terminate, scale, action, ...
4247 :param params: user parameters for the operation
4248 :return: dictionary following SOL005 format
4249 """
4250 # Raise exception if invalid arguments
4251 if not (nsr_id and operation and params):
4252 raise LcmException(
4253 "Parameters 'nsr_id', 'operation' and 'params' needed to create primitive not provided"
4254 )
4255 now = time()
4256 _id = str(uuid4())
4257 nslcmop = {
4258 "id": _id,
4259 "_id": _id,
4260 # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
4261 "operationState": "PROCESSING",
4262 "statusEnteredTime": now,
4263 "nsInstanceId": nsr_id,
4264 "lcmOperationType": operation,
4265 "startTime": now,
4266 "isAutomaticInvocation": False,
4267 "operationParams": params,
4268 "isCancelPending": False,
4269 "links": {
4270 "self": "/osm/nslcm/v1/ns_lcm_op_occs/" + _id,
4271 "nsInstance": "/osm/nslcm/v1/ns_instances/" + nsr_id,
4272 },
4273 }
4274 return nslcmop
4275
4276 def _format_additional_params(self, params):
4277 params = params or {}
4278 for key, value in params.items():
4279 if str(value).startswith("!!yaml "):
4280 params[key] = yaml.safe_load(value[7:])
4281 return params
4282
4283 def _get_terminate_primitive_params(self, seq, vnf_index):
4284 primitive = seq.get("name")
4285 primitive_params = {}
4286 params = {
4287 "member_vnf_index": vnf_index,
4288 "primitive": primitive,
4289 "primitive_params": primitive_params,
4290 }
4291 desc_params = {}
4292 return self._map_primitive_params(seq, params, desc_params)
4293
4294 # sub-operations
4295
4296 def _retry_or_skip_suboperation(self, db_nslcmop, op_index):
4297 op = deep_get(db_nslcmop, ("_admin", "operations"), [])[op_index]
4298 if op.get("operationState") == "COMPLETED":
4299 # b. Skip sub-operation
4300 # _ns_execute_primitive() or RO.create_action() will NOT be executed
4301 return self.SUBOPERATION_STATUS_SKIP
4302 else:
4303 # c. retry executing sub-operation
4304 # The sub-operation exists, and operationState != 'COMPLETED'
4305 # Update operationState = 'PROCESSING' to indicate a retry.
4306 operationState = "PROCESSING"
4307 detailed_status = "In progress"
4308 self._update_suboperation_status(
4309 db_nslcmop, op_index, operationState, detailed_status
4310 )
4311 # Return the sub-operation index
4312 # _ns_execute_primitive() or RO.create_action() will be called from scale()
4313 # with arguments extracted from the sub-operation
4314 return op_index
4315
4316 # Find a sub-operation where all keys in a matching dictionary must match
4317 # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
4318 def _find_suboperation(self, db_nslcmop, match):
4319 if db_nslcmop and match:
4320 op_list = db_nslcmop.get("_admin", {}).get("operations", [])
4321 for i, op in enumerate(op_list):
4322 if all(op.get(k) == match[k] for k in match):
4323 return i
4324 return self.SUBOPERATION_STATUS_NOT_FOUND
4325
4326 # Update status for a sub-operation given its index
4327 def _update_suboperation_status(
4328 self, db_nslcmop, op_index, operationState, detailed_status
4329 ):
4330 # Update DB for HA tasks
4331 q_filter = {"_id": db_nslcmop["_id"]}
4332 update_dict = {
4333 "_admin.operations.{}.operationState".format(op_index): operationState,
4334 "_admin.operations.{}.detailed-status".format(op_index): detailed_status,
4335 }
4336 self.db.set_one(
4337 "nslcmops", q_filter=q_filter, update_dict=update_dict, fail_on_empty=False
4338 )
4339
4340 # Add sub-operation, return the index of the added sub-operation
4341 # Optionally, set operationState, detailed-status, and operationType
4342 # Status and type are currently set for 'scale' sub-operations:
4343 # 'operationState' : 'PROCESSING' | 'COMPLETED' | 'FAILED'
4344 # 'detailed-status' : status message
4345 # 'operationType': may be any type, in the case of scaling: 'PRE-SCALE' | 'POST-SCALE'
4346 # Status and operation type are currently only used for 'scale', but NOT for 'terminate' sub-operations.
4347 def _add_suboperation(
4348 self,
4349 db_nslcmop,
4350 vnf_index,
4351 vdu_id,
4352 vdu_count_index,
4353 vdu_name,
4354 primitive,
4355 mapped_primitive_params,
4356 operationState=None,
4357 detailed_status=None,
4358 operationType=None,
4359 RO_nsr_id=None,
4360 RO_scaling_info=None,
4361 ):
4362 if not db_nslcmop:
4363 return self.SUBOPERATION_STATUS_NOT_FOUND
4364 # Get the "_admin.operations" list, if it exists
4365 db_nslcmop_admin = db_nslcmop.get("_admin", {})
4366 op_list = db_nslcmop_admin.get("operations")
4367 # Create or append to the "_admin.operations" list
4368 new_op = {
4369 "member_vnf_index": vnf_index,
4370 "vdu_id": vdu_id,
4371 "vdu_count_index": vdu_count_index,
4372 "primitive": primitive,
4373 "primitive_params": mapped_primitive_params,
4374 }
4375 if operationState:
4376 new_op["operationState"] = operationState
4377 if detailed_status:
4378 new_op["detailed-status"] = detailed_status
4379 if operationType:
4380 new_op["lcmOperationType"] = operationType
4381 if RO_nsr_id:
4382 new_op["RO_nsr_id"] = RO_nsr_id
4383 if RO_scaling_info:
4384 new_op["RO_scaling_info"] = RO_scaling_info
4385 if not op_list:
4386 # No existing operations, create key 'operations' with current operation as first list element
4387 db_nslcmop_admin.update({"operations": [new_op]})
4388 op_list = db_nslcmop_admin.get("operations")
4389 else:
4390 # Existing operations, append operation to list
4391 op_list.append(new_op)
4392
4393 db_nslcmop_update = {"_admin.operations": op_list}
4394 self.update_db_2("nslcmops", db_nslcmop["_id"], db_nslcmop_update)
4395 op_index = len(op_list) - 1
4396 return op_index
4397
4398 # Helper methods for scale() sub-operations
4399
4400 # pre-scale/post-scale:
4401 # Check for 3 different cases:
4402 # a. New: First time execution, return SUBOPERATION_STATUS_NEW
4403 # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
4404 # c. retry: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
4405 def _check_or_add_scale_suboperation(
4406 self,
4407 db_nslcmop,
4408 vnf_index,
4409 vnf_config_primitive,
4410 primitive_params,
4411 operationType,
4412 RO_nsr_id=None,
4413 RO_scaling_info=None,
4414 ):
4415 # Find this sub-operation
4416 if RO_nsr_id and RO_scaling_info:
4417 operationType = "SCALE-RO"
4418 match = {
4419 "member_vnf_index": vnf_index,
4420 "RO_nsr_id": RO_nsr_id,
4421 "RO_scaling_info": RO_scaling_info,
4422 }
4423 else:
4424 match = {
4425 "member_vnf_index": vnf_index,
4426 "primitive": vnf_config_primitive,
4427 "primitive_params": primitive_params,
4428 "lcmOperationType": operationType,
4429 }
4430 op_index = self._find_suboperation(db_nslcmop, match)
4431 if op_index == self.SUBOPERATION_STATUS_NOT_FOUND:
4432 # a. New sub-operation
4433 # The sub-operation does not exist, add it.
4434 # _ns_execute_primitive() will be called from scale() as usual, with non-modified arguments
4435 # The following parameters are set to None for all kind of scaling:
4436 vdu_id = None
4437 vdu_count_index = None
4438 vdu_name = None
4439 if RO_nsr_id and RO_scaling_info:
4440 vnf_config_primitive = None
4441 primitive_params = None
4442 else:
4443 RO_nsr_id = None
4444 RO_scaling_info = None
4445 # Initial status for sub-operation
4446 operationState = "PROCESSING"
4447 detailed_status = "In progress"
4448 # Add sub-operation for pre/post-scaling (zero or more operations)
4449 self._add_suboperation(
4450 db_nslcmop,
4451 vnf_index,
4452 vdu_id,
4453 vdu_count_index,
4454 vdu_name,
4455 vnf_config_primitive,
4456 primitive_params,
4457 operationState,
4458 detailed_status,
4459 operationType,
4460 RO_nsr_id,
4461 RO_scaling_info,
4462 )
4463 return self.SUBOPERATION_STATUS_NEW
4464 else:
4465 # Return either SUBOPERATION_STATUS_SKIP (operationState == 'COMPLETED'),
4466 # or op_index (operationState != 'COMPLETED')
4467 return self._retry_or_skip_suboperation(db_nslcmop, op_index)
4468
4469 # Function to return execution_environment id
4470
4471 def _get_ee_id(self, vnf_index, vdu_id, vca_deployed_list):
4472 # TODO vdu_index_count
4473 for vca in vca_deployed_list:
4474 if vca["member-vnf-index"] == vnf_index and vca["vdu_id"] == vdu_id:
4475 return vca.get("ee_id")
4476
4477 async def destroy_N2VC(
4478 self,
4479 logging_text,
4480 db_nslcmop,
4481 vca_deployed,
4482 config_descriptor,
4483 vca_index,
4484 destroy_ee=True,
4485 exec_primitives=True,
4486 scaling_in=False,
4487 vca_id: str = None,
4488 ):
4489 """
4490 Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
4491 :param logging_text:
4492 :param db_nslcmop:
4493 :param vca_deployed: Dictionary of deployment info at db_nsr._admin.depoloyed.VCA.<INDEX>
4494 :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
4495 :param vca_index: index in the database _admin.deployed.VCA
4496 :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
4497 :param exec_primitives: False to do not execute terminate primitives, because the config is not completed or has
4498 not executed properly
4499 :param scaling_in: True destroys the application, False destroys the model
4500 :return: None or exception
4501 """
4502
4503 self.logger.debug(
4504 logging_text
4505 + " vca_index: {}, vca_deployed: {}, config_descriptor: {}, destroy_ee: {}".format(
4506 vca_index, vca_deployed, config_descriptor, destroy_ee
4507 )
4508 )
4509
4510 vca_type = vca_deployed.get("type", "lxc_proxy_charm")
4511
4512 # execute terminate_primitives
4513 if exec_primitives:
4514 terminate_primitives = get_ee_sorted_terminate_config_primitive_list(
4515 config_descriptor.get("terminate-config-primitive"),
4516 vca_deployed.get("ee_descriptor_id"),
4517 )
4518 vdu_id = vca_deployed.get("vdu_id")
4519 vdu_count_index = vca_deployed.get("vdu_count_index")
4520 vdu_name = vca_deployed.get("vdu_name")
4521 vnf_index = vca_deployed.get("member-vnf-index")
4522 if terminate_primitives and vca_deployed.get("needed_terminate"):
4523 for seq in terminate_primitives:
4524 # For each sequence in list, get primitive and call _ns_execute_primitive()
4525 step = "Calling terminate action for vnf_member_index={} primitive={}".format(
4526 vnf_index, seq.get("name")
4527 )
4528 self.logger.debug(logging_text + step)
4529 # Create the primitive for each sequence, i.e. "primitive": "touch"
4530 primitive = seq.get("name")
4531 mapped_primitive_params = self._get_terminate_primitive_params(
4532 seq, vnf_index
4533 )
4534
4535 # Add sub-operation
4536 self._add_suboperation(
4537 db_nslcmop,
4538 vnf_index,
4539 vdu_id,
4540 vdu_count_index,
4541 vdu_name,
4542 primitive,
4543 mapped_primitive_params,
4544 )
4545 # Sub-operations: Call _ns_execute_primitive() instead of action()
4546 try:
4547 result, result_detail = await self._ns_execute_primitive(
4548 vca_deployed["ee_id"],
4549 primitive,
4550 mapped_primitive_params,
4551 vca_type=vca_type,
4552 vca_id=vca_id,
4553 )
4554 except LcmException:
4555 # this happens when VCA is not deployed. In this case it is not needed to terminate
4556 continue
4557 result_ok = ["COMPLETED", "PARTIALLY_COMPLETED"]
4558 if result not in result_ok:
4559 raise LcmException(
4560 "terminate_primitive {} for vnf_member_index={} fails with "
4561 "error {}".format(seq.get("name"), vnf_index, result_detail)
4562 )
4563 # set that this VCA do not need terminated
4564 db_update_entry = "_admin.deployed.VCA.{}.needed_terminate".format(
4565 vca_index
4566 )
4567 self.update_db_2(
4568 "nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False}
4569 )
4570
4571 # Delete Prometheus Jobs if any
4572 # This uses NSR_ID, so it will destroy any jobs under this index
4573 self.db.del_list("prometheus_jobs", {"nsr_id": db_nslcmop["nsInstanceId"]})
4574
4575 if destroy_ee:
4576 await self.vca_map[vca_type].delete_execution_environment(
4577 vca_deployed["ee_id"],
4578 scaling_in=scaling_in,
4579 vca_type=vca_type,
4580 vca_id=vca_id,
4581 )
4582
4583 async def _delete_all_N2VC(self, db_nsr: dict, vca_id: str = None):
4584 self._write_all_config_status(db_nsr=db_nsr, status="TERMINATING")
4585 namespace = "." + db_nsr["_id"]
4586 try:
4587 await self.n2vc.delete_namespace(
4588 namespace=namespace,
4589 total_timeout=self.timeout.charm_delete,
4590 vca_id=vca_id,
4591 )
4592 except N2VCNotFound: # already deleted. Skip
4593 pass
4594 self._write_all_config_status(db_nsr=db_nsr, status="DELETED")
4595
4596 async def terminate(self, nsr_id, nslcmop_id):
4597 # Try to lock HA task here
4598 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
4599 if not task_is_locked_by_me:
4600 return
4601
4602 logging_text = "Task ns={} terminate={} ".format(nsr_id, nslcmop_id)
4603 self.logger.debug(logging_text + "Enter")
4604 timeout_ns_terminate = self.timeout.ns_terminate
4605 db_nsr = None
4606 db_nslcmop = None
4607 operation_params = None
4608 exc = None
4609 error_list = [] # annotates all failed error messages
4610 db_nslcmop_update = {}
4611 autoremove = False # autoremove after terminated
4612 tasks_dict_info = {}
4613 db_nsr_update = {}
4614 stage = [
4615 "Stage 1/3: Preparing task.",
4616 "Waiting for previous operations to terminate.",
4617 "",
4618 ]
4619 # ^ contains [stage, step, VIM-status]
4620 try:
4621 # wait for any previous tasks in process
4622 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
4623
4624 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
4625 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
4626 operation_params = db_nslcmop.get("operationParams") or {}
4627 if operation_params.get("timeout_ns_terminate"):
4628 timeout_ns_terminate = operation_params["timeout_ns_terminate"]
4629 stage[1] = "Getting nsr={} from db.".format(nsr_id)
4630 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
4631
4632 db_nsr_update["operational-status"] = "terminating"
4633 db_nsr_update["config-status"] = "terminating"
4634 self._write_ns_status(
4635 nsr_id=nsr_id,
4636 ns_state="TERMINATING",
4637 current_operation="TERMINATING",
4638 current_operation_id=nslcmop_id,
4639 other_update=db_nsr_update,
4640 )
4641 self._write_op_status(op_id=nslcmop_id, queuePosition=0, stage=stage)
4642 nsr_deployed = deepcopy(db_nsr["_admin"].get("deployed")) or {}
4643 if db_nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
4644 return
4645
4646 stage[1] = "Getting vnf descriptors from db."
4647 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
4648 db_vnfrs_dict = {
4649 db_vnfr["member-vnf-index-ref"]: db_vnfr for db_vnfr in db_vnfrs_list
4650 }
4651 db_vnfds_from_id = {}
4652 db_vnfds_from_member_index = {}
4653 # Loop over VNFRs
4654 for vnfr in db_vnfrs_list:
4655 vnfd_id = vnfr["vnfd-id"]
4656 if vnfd_id not in db_vnfds_from_id:
4657 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
4658 db_vnfds_from_id[vnfd_id] = vnfd
4659 db_vnfds_from_member_index[
4660 vnfr["member-vnf-index-ref"]
4661 ] = db_vnfds_from_id[vnfd_id]
4662
4663 # Destroy individual execution environments when there are terminating primitives.
4664 # Rest of EE will be deleted at once
4665 # TODO - check before calling _destroy_N2VC
4666 # if not operation_params.get("skip_terminate_primitives"):#
4667 # or not vca.get("needed_terminate"):
4668 stage[0] = "Stage 2/3 execute terminating primitives."
4669 self.logger.debug(logging_text + stage[0])
4670 stage[1] = "Looking execution environment that needs terminate."
4671 self.logger.debug(logging_text + stage[1])
4672
4673 for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
4674 config_descriptor = None
4675 vca_member_vnf_index = vca.get("member-vnf-index")
4676 vca_id = self.get_vca_id(
4677 db_vnfrs_dict.get(vca_member_vnf_index)
4678 if vca_member_vnf_index
4679 else None,
4680 db_nsr,
4681 )
4682 if not vca or not vca.get("ee_id"):
4683 continue
4684 if not vca.get("member-vnf-index"):
4685 # ns
4686 config_descriptor = db_nsr.get("ns-configuration")
4687 elif vca.get("vdu_id"):
4688 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
4689 config_descriptor = get_configuration(db_vnfd, vca.get("vdu_id"))
4690 elif vca.get("kdu_name"):
4691 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
4692 config_descriptor = get_configuration(db_vnfd, vca.get("kdu_name"))
4693 else:
4694 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
4695 config_descriptor = get_configuration(db_vnfd, db_vnfd["id"])
4696 vca_type = vca.get("type")
4697 exec_terminate_primitives = not operation_params.get(
4698 "skip_terminate_primitives"
4699 ) and vca.get("needed_terminate")
4700 # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
4701 # pending native charms
4702 destroy_ee = (
4703 True if vca_type in ("helm", "helm-v3", "native_charm") else False
4704 )
4705 # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
4706 # vca_index, vca.get("ee_id"), vca_type, destroy_ee))
4707 task = asyncio.ensure_future(
4708 self.destroy_N2VC(
4709 logging_text,
4710 db_nslcmop,
4711 vca,
4712 config_descriptor,
4713 vca_index,
4714 destroy_ee,
4715 exec_terminate_primitives,
4716 vca_id=vca_id,
4717 )
4718 )
4719 tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
4720
4721 # wait for pending tasks of terminate primitives
4722 if tasks_dict_info:
4723 self.logger.debug(
4724 logging_text
4725 + "Waiting for tasks {}".format(list(tasks_dict_info.keys()))
4726 )
4727 error_list = await self._wait_for_tasks(
4728 logging_text,
4729 tasks_dict_info,
4730 min(self.timeout.charm_delete, timeout_ns_terminate),
4731 stage,
4732 nslcmop_id,
4733 )
4734 tasks_dict_info.clear()
4735 if error_list:
4736 return # raise LcmException("; ".join(error_list))
4737
4738 # remove All execution environments at once
4739 stage[0] = "Stage 3/3 delete all."
4740
4741 if nsr_deployed.get("VCA"):
4742 stage[1] = "Deleting all execution environments."
4743 self.logger.debug(logging_text + stage[1])
4744 vca_id = self.get_vca_id({}, db_nsr)
4745 task_delete_ee = asyncio.ensure_future(
4746 asyncio.wait_for(
4747 self._delete_all_N2VC(db_nsr=db_nsr, vca_id=vca_id),
4748 timeout=self.timeout.charm_delete,
4749 )
4750 )
4751 # task_delete_ee = asyncio.ensure_future(self.n2vc.delete_namespace(namespace="." + nsr_id))
4752 tasks_dict_info[task_delete_ee] = "Terminating all VCA"
4753
4754 # Delete Namespace and Certificates if necessary
4755 if check_helm_ee_in_ns(list(db_vnfds_from_member_index.values())):
4756 await self.vca_map["helm-v3"].delete_tls_certificate(
4757 namespace=db_nslcmop["nsInstanceId"],
4758 certificate_name=self.EE_TLS_NAME,
4759 )
4760 await self.vca_map["helm-v3"].delete_namespace(
4761 namespace=db_nslcmop["nsInstanceId"],
4762 )
4763
4764 # Delete from k8scluster
4765 stage[1] = "Deleting KDUs."
4766 self.logger.debug(logging_text + stage[1])
4767 # print(nsr_deployed)
4768 for kdu in get_iterable(nsr_deployed, "K8s"):
4769 if not kdu or not kdu.get("kdu-instance"):
4770 continue
4771 kdu_instance = kdu.get("kdu-instance")
4772 if kdu.get("k8scluster-type") in self.k8scluster_map:
4773 # TODO: Uninstall kdu instances taking into account they could be deployed in different VIMs
4774 vca_id = self.get_vca_id({}, db_nsr)
4775 task_delete_kdu_instance = asyncio.ensure_future(
4776 self.k8scluster_map[kdu["k8scluster-type"]].uninstall(
4777 cluster_uuid=kdu.get("k8scluster-uuid"),
4778 kdu_instance=kdu_instance,
4779 vca_id=vca_id,
4780 namespace=kdu.get("namespace"),
4781 )
4782 )
4783 else:
4784 self.logger.error(
4785 logging_text
4786 + "Unknown k8s deployment type {}".format(
4787 kdu.get("k8scluster-type")
4788 )
4789 )
4790 continue
4791 tasks_dict_info[
4792 task_delete_kdu_instance
4793 ] = "Terminating KDU '{}'".format(kdu.get("kdu-name"))
4794
4795 # remove from RO
4796 stage[1] = "Deleting ns from VIM."
4797 if self.ro_config.ng:
4798 task_delete_ro = asyncio.ensure_future(
4799 self._terminate_ng_ro(
4800 logging_text, nsr_deployed, nsr_id, nslcmop_id, stage
4801 )
4802 )
4803 tasks_dict_info[task_delete_ro] = "Removing deployment from VIM"
4804
4805 # rest of staff will be done at finally
4806
4807 except (
4808 ROclient.ROClientException,
4809 DbException,
4810 LcmException,
4811 N2VCException,
4812 ) as e:
4813 self.logger.error(logging_text + "Exit Exception {}".format(e))
4814 exc = e
4815 except asyncio.CancelledError:
4816 self.logger.error(
4817 logging_text + "Cancelled Exception while '{}'".format(stage[1])
4818 )
4819 exc = "Operation was cancelled"
4820 except Exception as e:
4821 exc = traceback.format_exc()
4822 self.logger.critical(
4823 logging_text + "Exit Exception while '{}': {}".format(stage[1], e),
4824 exc_info=True,
4825 )
4826 finally:
4827 if exc:
4828 error_list.append(str(exc))
4829 try:
4830 # wait for pending tasks
4831 if tasks_dict_info:
4832 stage[1] = "Waiting for terminate pending tasks."
4833 self.logger.debug(logging_text + stage[1])
4834 error_list += await self._wait_for_tasks(
4835 logging_text,
4836 tasks_dict_info,
4837 timeout_ns_terminate,
4838 stage,
4839 nslcmop_id,
4840 )
4841 stage[1] = stage[2] = ""
4842 except asyncio.CancelledError:
4843 error_list.append("Cancelled")
4844 # TODO cancell all tasks
4845 except Exception as exc:
4846 error_list.append(str(exc))
4847 # update status at database
4848 if error_list:
4849 error_detail = "; ".join(error_list)
4850 # self.logger.error(logging_text + error_detail)
4851 error_description_nslcmop = "{} Detail: {}".format(
4852 stage[0], error_detail
4853 )
4854 error_description_nsr = "Operation: TERMINATING.{}, {}.".format(
4855 nslcmop_id, stage[0]
4856 )
4857
4858 db_nsr_update["operational-status"] = "failed"
4859 db_nsr_update["detailed-status"] = (
4860 error_description_nsr + " Detail: " + error_detail
4861 )
4862 db_nslcmop_update["detailed-status"] = error_detail
4863 nslcmop_operation_state = "FAILED"
4864 ns_state = "BROKEN"
4865 else:
4866 error_detail = None
4867 error_description_nsr = error_description_nslcmop = None
4868 ns_state = "NOT_INSTANTIATED"
4869 db_nsr_update["operational-status"] = "terminated"
4870 db_nsr_update["detailed-status"] = "Done"
4871 db_nsr_update["_admin.nsState"] = "NOT_INSTANTIATED"
4872 db_nslcmop_update["detailed-status"] = "Done"
4873 nslcmop_operation_state = "COMPLETED"
4874
4875 if db_nsr:
4876 self._write_ns_status(
4877 nsr_id=nsr_id,
4878 ns_state=ns_state,
4879 current_operation="IDLE",
4880 current_operation_id=None,
4881 error_description=error_description_nsr,
4882 error_detail=error_detail,
4883 other_update=db_nsr_update,
4884 )
4885 self._write_op_status(
4886 op_id=nslcmop_id,
4887 stage="",
4888 error_message=error_description_nslcmop,
4889 operation_state=nslcmop_operation_state,
4890 other_update=db_nslcmop_update,
4891 )
4892 if ns_state == "NOT_INSTANTIATED":
4893 try:
4894 self.db.set_list(
4895 "vnfrs",
4896 {"nsr-id-ref": nsr_id},
4897 {"_admin.nsState": "NOT_INSTANTIATED"},
4898 )
4899 except DbException as e:
4900 self.logger.warn(
4901 logging_text
4902 + "Error writing VNFR status for nsr-id-ref: {} -> {}".format(
4903 nsr_id, e
4904 )
4905 )
4906 if operation_params:
4907 autoremove = operation_params.get("autoremove", False)
4908 if nslcmop_operation_state:
4909 try:
4910 await self.msg.aiowrite(
4911 "ns",
4912 "terminated",
4913 {
4914 "nsr_id": nsr_id,
4915 "nslcmop_id": nslcmop_id,
4916 "operationState": nslcmop_operation_state,
4917 "autoremove": autoremove,
4918 },
4919 )
4920 except Exception as e:
4921 self.logger.error(
4922 logging_text + "kafka_write notification Exception {}".format(e)
4923 )
4924 self.logger.debug(f"Deleting alerts: ns_id={nsr_id}")
4925 self.db.del_list("alerts", {"tags.ns_id": nsr_id})
4926
4927 self.logger.debug(logging_text + "Exit")
4928 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_terminate")
4929
4930 async def _wait_for_tasks(
4931 self, logging_text, created_tasks_info, timeout, stage, nslcmop_id, nsr_id=None
4932 ):
4933 time_start = time()
4934 error_detail_list = []
4935 error_list = []
4936 pending_tasks = list(created_tasks_info.keys())
4937 num_tasks = len(pending_tasks)
4938 num_done = 0
4939 stage[1] = "{}/{}.".format(num_done, num_tasks)
4940 self._write_op_status(nslcmop_id, stage)
4941 while pending_tasks:
4942 new_error = None
4943 _timeout = timeout + time_start - time()
4944 done, pending_tasks = await asyncio.wait(
4945 pending_tasks, timeout=_timeout, return_when=asyncio.FIRST_COMPLETED
4946 )
4947 num_done += len(done)
4948 if not done: # Timeout
4949 for task in pending_tasks:
4950 new_error = created_tasks_info[task] + ": Timeout"
4951 error_detail_list.append(new_error)
4952 error_list.append(new_error)
4953 break
4954 for task in done:
4955 if task.cancelled():
4956 exc = "Cancelled"
4957 else:
4958 exc = task.exception()
4959 if exc:
4960 if isinstance(exc, asyncio.TimeoutError):
4961 exc = "Timeout"
4962 new_error = created_tasks_info[task] + ": {}".format(exc)
4963 error_list.append(created_tasks_info[task])
4964 error_detail_list.append(new_error)
4965 if isinstance(
4966 exc,
4967 (
4968 str,
4969 DbException,
4970 N2VCException,
4971 ROclient.ROClientException,
4972 LcmException,
4973 K8sException,
4974 NgRoException,
4975 ),
4976 ):
4977 self.logger.error(logging_text + new_error)
4978 else:
4979 exc_traceback = "".join(
4980 traceback.format_exception(None, exc, exc.__traceback__)
4981 )
4982 self.logger.error(
4983 logging_text
4984 + created_tasks_info[task]
4985 + " "
4986 + exc_traceback
4987 )
4988 else:
4989 self.logger.debug(
4990 logging_text + created_tasks_info[task] + ": Done"
4991 )
4992 stage[1] = "{}/{}.".format(num_done, num_tasks)
4993 if new_error:
4994 stage[1] += " Errors: " + ". ".join(error_detail_list) + "."
4995 if nsr_id: # update also nsr
4996 self.update_db_2(
4997 "nsrs",
4998 nsr_id,
4999 {
5000 "errorDescription": "Error at: " + ", ".join(error_list),
5001 "errorDetail": ". ".join(error_detail_list),
5002 },
5003 )
5004 self._write_op_status(nslcmop_id, stage)
5005 return error_detail_list
5006
5007 @staticmethod
5008 def _map_primitive_params(primitive_desc, params, instantiation_params):
5009 """
5010 Generates the params to be provided to charm before executing primitive. If user does not provide a parameter,
5011 The default-value is used. If it is between < > it look for a value at instantiation_params
5012 :param primitive_desc: portion of VNFD/NSD that describes primitive
5013 :param params: Params provided by user
5014 :param instantiation_params: Instantiation params provided by user
5015 :return: a dictionary with the calculated params
5016 """
5017 calculated_params = {}
5018 for parameter in primitive_desc.get("parameter", ()):
5019 param_name = parameter["name"]
5020 if param_name in params:
5021 calculated_params[param_name] = params[param_name]
5022 elif "default-value" in parameter or "value" in parameter:
5023 if "value" in parameter:
5024 calculated_params[param_name] = parameter["value"]
5025 else:
5026 calculated_params[param_name] = parameter["default-value"]
5027 if (
5028 isinstance(calculated_params[param_name], str)
5029 and calculated_params[param_name].startswith("<")
5030 and calculated_params[param_name].endswith(">")
5031 ):
5032 if calculated_params[param_name][1:-1] in instantiation_params:
5033 calculated_params[param_name] = instantiation_params[
5034 calculated_params[param_name][1:-1]
5035 ]
5036 else:
5037 raise LcmException(
5038 "Parameter {} needed to execute primitive {} not provided".format(
5039 calculated_params[param_name], primitive_desc["name"]
5040 )
5041 )
5042 else:
5043 raise LcmException(
5044 "Parameter {} needed to execute primitive {} not provided".format(
5045 param_name, primitive_desc["name"]
5046 )
5047 )
5048
5049 if isinstance(calculated_params[param_name], (dict, list, tuple)):
5050 calculated_params[param_name] = yaml.safe_dump(
5051 calculated_params[param_name], default_flow_style=True, width=256
5052 )
5053 elif isinstance(calculated_params[param_name], str) and calculated_params[
5054 param_name
5055 ].startswith("!!yaml "):
5056 calculated_params[param_name] = calculated_params[param_name][7:]
5057 if parameter.get("data-type") == "INTEGER":
5058 try:
5059 calculated_params[param_name] = int(calculated_params[param_name])
5060 except ValueError: # error converting string to int
5061 raise LcmException(
5062 "Parameter {} of primitive {} must be integer".format(
5063 param_name, primitive_desc["name"]
5064 )
5065 )
5066 elif parameter.get("data-type") == "BOOLEAN":
5067 calculated_params[param_name] = not (
5068 (str(calculated_params[param_name])).lower() == "false"
5069 )
5070
5071 # add always ns_config_info if primitive name is config
5072 if primitive_desc["name"] == "config":
5073 if "ns_config_info" in instantiation_params:
5074 calculated_params["ns_config_info"] = instantiation_params[
5075 "ns_config_info"
5076 ]
5077 return calculated_params
5078
5079 def _look_for_deployed_vca(
5080 self,
5081 deployed_vca,
5082 member_vnf_index,
5083 vdu_id,
5084 vdu_count_index,
5085 kdu_name=None,
5086 ee_descriptor_id=None,
5087 ):
5088 # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
5089 for vca in deployed_vca:
5090 if not vca:
5091 continue
5092 if member_vnf_index != vca["member-vnf-index"] or vdu_id != vca["vdu_id"]:
5093 continue
5094 if (
5095 vdu_count_index is not None
5096 and vdu_count_index != vca["vdu_count_index"]
5097 ):
5098 continue
5099 if kdu_name and kdu_name != vca["kdu_name"]:
5100 continue
5101 if ee_descriptor_id and ee_descriptor_id != vca["ee_descriptor_id"]:
5102 continue
5103 break
5104 else:
5105 # vca_deployed not found
5106 raise LcmException(
5107 "charm for member_vnf_index={} vdu_id={}.{} kdu_name={} execution-environment-list.id={}"
5108 " is not deployed".format(
5109 member_vnf_index,
5110 vdu_id,
5111 vdu_count_index,
5112 kdu_name,
5113 ee_descriptor_id,
5114 )
5115 )
5116 # get ee_id
5117 ee_id = vca.get("ee_id")
5118 vca_type = vca.get(
5119 "type", "lxc_proxy_charm"
5120 ) # default value for backward compatibility - proxy charm
5121 if not ee_id:
5122 raise LcmException(
5123 "charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
5124 "execution environment".format(
5125 member_vnf_index, vdu_id, kdu_name, vdu_count_index
5126 )
5127 )
5128 return ee_id, vca_type
5129
5130 async def _ns_execute_primitive(
5131 self,
5132 ee_id,
5133 primitive,
5134 primitive_params,
5135 retries=0,
5136 retries_interval=30,
5137 timeout=None,
5138 vca_type=None,
5139 db_dict=None,
5140 vca_id: str = None,
5141 ) -> (str, str):
5142 try:
5143 if primitive == "config":
5144 primitive_params = {"params": primitive_params}
5145
5146 vca_type = vca_type or "lxc_proxy_charm"
5147
5148 while retries >= 0:
5149 try:
5150 output = await asyncio.wait_for(
5151 self.vca_map[vca_type].exec_primitive(
5152 ee_id=ee_id,
5153 primitive_name=primitive,
5154 params_dict=primitive_params,
5155 progress_timeout=self.timeout.progress_primitive,
5156 total_timeout=self.timeout.primitive,
5157 db_dict=db_dict,
5158 vca_id=vca_id,
5159 vca_type=vca_type,
5160 ),
5161 timeout=timeout or self.timeout.primitive,
5162 )
5163 # execution was OK
5164 break
5165 except asyncio.CancelledError:
5166 raise
5167 except Exception as e:
5168 retries -= 1
5169 if retries >= 0:
5170 self.logger.debug(
5171 "Error executing action {} on {} -> {}".format(
5172 primitive, ee_id, e
5173 )
5174 )
5175 # wait and retry
5176 await asyncio.sleep(retries_interval)
5177 else:
5178 if isinstance(e, asyncio.TimeoutError):
5179 e = N2VCException(
5180 message="Timed out waiting for action to complete"
5181 )
5182 return "FAILED", getattr(e, "message", repr(e))
5183
5184 return "COMPLETED", output
5185
5186 except (LcmException, asyncio.CancelledError):
5187 raise
5188 except Exception as e:
5189 return "FAIL", "Error executing action {}: {}".format(primitive, e)
5190
5191 async def vca_status_refresh(self, nsr_id, nslcmop_id):
5192 """
5193 Updating the vca_status with latest juju information in nsrs record
5194 :param: nsr_id: Id of the nsr
5195 :param: nslcmop_id: Id of the nslcmop
5196 :return: None
5197 """
5198
5199 self.logger.debug("Task ns={} action={} Enter".format(nsr_id, nslcmop_id))
5200 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
5201 vca_id = self.get_vca_id({}, db_nsr)
5202 if db_nsr["_admin"]["deployed"]["K8s"]:
5203 for _, k8s in enumerate(db_nsr["_admin"]["deployed"]["K8s"]):
5204 cluster_uuid, kdu_instance, cluster_type = (
5205 k8s["k8scluster-uuid"],
5206 k8s["kdu-instance"],
5207 k8s["k8scluster-type"],
5208 )
5209 await self._on_update_k8s_db(
5210 cluster_uuid=cluster_uuid,
5211 kdu_instance=kdu_instance,
5212 filter={"_id": nsr_id},
5213 vca_id=vca_id,
5214 cluster_type=cluster_type,
5215 )
5216 else:
5217 for vca_index, _ in enumerate(db_nsr["_admin"]["deployed"]["VCA"]):
5218 table, filter = "nsrs", {"_id": nsr_id}
5219 path = "_admin.deployed.VCA.{}.".format(vca_index)
5220 await self._on_update_n2vc_db(table, filter, path, {})
5221
5222 self.logger.debug("Task ns={} action={} Exit".format(nsr_id, nslcmop_id))
5223 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_vca_status_refresh")
5224
5225 async def action(self, nsr_id, nslcmop_id):
5226 # Try to lock HA task here
5227 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
5228 if not task_is_locked_by_me:
5229 return
5230
5231 logging_text = "Task ns={} action={} ".format(nsr_id, nslcmop_id)
5232 self.logger.debug(logging_text + "Enter")
5233 # get all needed from database
5234 db_nsr = None
5235 db_nslcmop = None
5236 db_nsr_update = {}
5237 db_nslcmop_update = {}
5238 nslcmop_operation_state = None
5239 error_description_nslcmop = None
5240 exc = None
5241 step = ""
5242 try:
5243 # wait for any previous tasks in process
5244 step = "Waiting for previous operations to terminate"
5245 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
5246
5247 self._write_ns_status(
5248 nsr_id=nsr_id,
5249 ns_state=None,
5250 current_operation="RUNNING ACTION",
5251 current_operation_id=nslcmop_id,
5252 )
5253
5254 step = "Getting information from database"
5255 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5256 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
5257 if db_nslcmop["operationParams"].get("primitive_params"):
5258 db_nslcmop["operationParams"]["primitive_params"] = json.loads(
5259 db_nslcmop["operationParams"]["primitive_params"]
5260 )
5261
5262 nsr_deployed = db_nsr["_admin"].get("deployed")
5263 vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
5264 vdu_id = db_nslcmop["operationParams"].get("vdu_id")
5265 kdu_name = db_nslcmop["operationParams"].get("kdu_name")
5266 vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
5267 primitive = db_nslcmop["operationParams"]["primitive"]
5268 primitive_params = db_nslcmop["operationParams"]["primitive_params"]
5269 timeout_ns_action = db_nslcmop["operationParams"].get(
5270 "timeout_ns_action", self.timeout.primitive
5271 )
5272
5273 if vnf_index:
5274 step = "Getting vnfr from database"
5275 db_vnfr = self.db.get_one(
5276 "vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id}
5277 )
5278 if db_vnfr.get("kdur"):
5279 kdur_list = []
5280 for kdur in db_vnfr["kdur"]:
5281 if kdur.get("additionalParams"):
5282 kdur["additionalParams"] = json.loads(
5283 kdur["additionalParams"]
5284 )
5285 kdur_list.append(kdur)
5286 db_vnfr["kdur"] = kdur_list
5287 step = "Getting vnfd from database"
5288 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
5289
5290 # Sync filesystem before running a primitive
5291 self.fs.sync(db_vnfr["vnfd-id"])
5292 else:
5293 step = "Getting nsd from database"
5294 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
5295
5296 vca_id = self.get_vca_id(db_vnfr, db_nsr)
5297 # for backward compatibility
5298 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
5299 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
5300 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
5301 self.update_db_2("nsrs", nsr_id, db_nsr_update)
5302
5303 # look for primitive
5304 config_primitive_desc = descriptor_configuration = None
5305 if vdu_id:
5306 descriptor_configuration = get_configuration(db_vnfd, vdu_id)
5307 elif kdu_name:
5308 descriptor_configuration = get_configuration(db_vnfd, kdu_name)
5309 elif vnf_index:
5310 descriptor_configuration = get_configuration(db_vnfd, db_vnfd["id"])
5311 else:
5312 descriptor_configuration = db_nsd.get("ns-configuration")
5313
5314 if descriptor_configuration and descriptor_configuration.get(
5315 "config-primitive"
5316 ):
5317 for config_primitive in descriptor_configuration["config-primitive"]:
5318 if config_primitive["name"] == primitive:
5319 config_primitive_desc = config_primitive
5320 break
5321
5322 if not config_primitive_desc:
5323 if not (kdu_name and primitive in ("upgrade", "rollback", "status")):
5324 raise LcmException(
5325 "Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".format(
5326 primitive
5327 )
5328 )
5329 primitive_name = primitive
5330 ee_descriptor_id = None
5331 else:
5332 primitive_name = config_primitive_desc.get(
5333 "execution-environment-primitive", primitive
5334 )
5335 ee_descriptor_id = config_primitive_desc.get(
5336 "execution-environment-ref"
5337 )
5338
5339 if vnf_index:
5340 if vdu_id:
5341 vdur = next(
5342 (x for x in db_vnfr["vdur"] if x["vdu-id-ref"] == vdu_id), None
5343 )
5344 desc_params = parse_yaml_strings(vdur.get("additionalParams"))
5345 elif kdu_name:
5346 kdur = next(
5347 (x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name), None
5348 )
5349 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
5350 else:
5351 desc_params = parse_yaml_strings(
5352 db_vnfr.get("additionalParamsForVnf")
5353 )
5354 else:
5355 desc_params = parse_yaml_strings(db_nsr.get("additionalParamsForNs"))
5356 if kdu_name and get_configuration(db_vnfd, kdu_name):
5357 kdu_configuration = get_configuration(db_vnfd, kdu_name)
5358 actions = set()
5359 for primitive in kdu_configuration.get("initial-config-primitive", []):
5360 actions.add(primitive["name"])
5361 for primitive in kdu_configuration.get("config-primitive", []):
5362 actions.add(primitive["name"])
5363 kdu = find_in_list(
5364 nsr_deployed["K8s"],
5365 lambda kdu: kdu_name == kdu["kdu-name"]
5366 and kdu["member-vnf-index"] == vnf_index,
5367 )
5368 kdu_action = (
5369 True
5370 if primitive_name in actions
5371 and kdu["k8scluster-type"] not in ("helm-chart", "helm-chart-v3")
5372 else False
5373 )
5374
5375 # TODO check if ns is in a proper status
5376 if kdu_name and (
5377 primitive_name in ("upgrade", "rollback", "status") or kdu_action
5378 ):
5379 # kdur and desc_params already set from before
5380 if primitive_params:
5381 desc_params.update(primitive_params)
5382 # TODO Check if we will need something at vnf level
5383 for index, kdu in enumerate(get_iterable(nsr_deployed, "K8s")):
5384 if (
5385 kdu_name == kdu["kdu-name"]
5386 and kdu["member-vnf-index"] == vnf_index
5387 ):
5388 break
5389 else:
5390 raise LcmException(
5391 "KDU '{}' for vnf '{}' not deployed".format(kdu_name, vnf_index)
5392 )
5393
5394 if kdu.get("k8scluster-type") not in self.k8scluster_map:
5395 msg = "unknown k8scluster-type '{}'".format(
5396 kdu.get("k8scluster-type")
5397 )
5398 raise LcmException(msg)
5399
5400 db_dict = {
5401 "collection": "nsrs",
5402 "filter": {"_id": nsr_id},
5403 "path": "_admin.deployed.K8s.{}".format(index),
5404 }
5405 self.logger.debug(
5406 logging_text
5407 + "Exec k8s {} on {}.{}".format(primitive_name, vnf_index, kdu_name)
5408 )
5409 step = "Executing kdu {}".format(primitive_name)
5410 if primitive_name == "upgrade":
5411 if desc_params.get("kdu_model"):
5412 kdu_model = desc_params.get("kdu_model")
5413 del desc_params["kdu_model"]
5414 else:
5415 kdu_model = kdu.get("kdu-model")
5416 if kdu_model.count("/") < 2: # helm chart is not embedded
5417 parts = kdu_model.split(sep=":")
5418 if len(parts) == 2:
5419 kdu_model = parts[0]
5420 if desc_params.get("kdu_atomic_upgrade"):
5421 atomic_upgrade = desc_params.get(
5422 "kdu_atomic_upgrade"
5423 ).lower() in ("yes", "true", "1")
5424 del desc_params["kdu_atomic_upgrade"]
5425 else:
5426 atomic_upgrade = True
5427
5428 detailed_status = await asyncio.wait_for(
5429 self.k8scluster_map[kdu["k8scluster-type"]].upgrade(
5430 cluster_uuid=kdu.get("k8scluster-uuid"),
5431 kdu_instance=kdu.get("kdu-instance"),
5432 atomic=atomic_upgrade,
5433 kdu_model=kdu_model,
5434 params=desc_params,
5435 db_dict=db_dict,
5436 timeout=timeout_ns_action,
5437 ),
5438 timeout=timeout_ns_action + 10,
5439 )
5440 self.logger.debug(
5441 logging_text + " Upgrade of kdu {} done".format(detailed_status)
5442 )
5443 elif primitive_name == "rollback":
5444 detailed_status = await asyncio.wait_for(
5445 self.k8scluster_map[kdu["k8scluster-type"]].rollback(
5446 cluster_uuid=kdu.get("k8scluster-uuid"),
5447 kdu_instance=kdu.get("kdu-instance"),
5448 db_dict=db_dict,
5449 ),
5450 timeout=timeout_ns_action,
5451 )
5452 elif primitive_name == "status":
5453 detailed_status = await asyncio.wait_for(
5454 self.k8scluster_map[kdu["k8scluster-type"]].status_kdu(
5455 cluster_uuid=kdu.get("k8scluster-uuid"),
5456 kdu_instance=kdu.get("kdu-instance"),
5457 vca_id=vca_id,
5458 ),
5459 timeout=timeout_ns_action,
5460 )
5461 else:
5462 kdu_instance = kdu.get("kdu-instance") or "{}-{}".format(
5463 kdu["kdu-name"], nsr_id
5464 )
5465 params = self._map_primitive_params(
5466 config_primitive_desc, primitive_params, desc_params
5467 )
5468
5469 detailed_status = await asyncio.wait_for(
5470 self.k8scluster_map[kdu["k8scluster-type"]].exec_primitive(
5471 cluster_uuid=kdu.get("k8scluster-uuid"),
5472 kdu_instance=kdu_instance,
5473 primitive_name=primitive_name,
5474 params=params,
5475 db_dict=db_dict,
5476 timeout=timeout_ns_action,
5477 vca_id=vca_id,
5478 ),
5479 timeout=timeout_ns_action,
5480 )
5481
5482 if detailed_status:
5483 nslcmop_operation_state = "COMPLETED"
5484 else:
5485 detailed_status = ""
5486 nslcmop_operation_state = "FAILED"
5487 else:
5488 ee_id, vca_type = self._look_for_deployed_vca(
5489 nsr_deployed["VCA"],
5490 member_vnf_index=vnf_index,
5491 vdu_id=vdu_id,
5492 vdu_count_index=vdu_count_index,
5493 ee_descriptor_id=ee_descriptor_id,
5494 )
5495 for vca_index, vca_deployed in enumerate(
5496 db_nsr["_admin"]["deployed"]["VCA"]
5497 ):
5498 if vca_deployed.get("member-vnf-index") == vnf_index:
5499 db_dict = {
5500 "collection": "nsrs",
5501 "filter": {"_id": nsr_id},
5502 "path": "_admin.deployed.VCA.{}.".format(vca_index),
5503 }
5504 break
5505 (
5506 nslcmop_operation_state,
5507 detailed_status,
5508 ) = await self._ns_execute_primitive(
5509 ee_id,
5510 primitive=primitive_name,
5511 primitive_params=self._map_primitive_params(
5512 config_primitive_desc, primitive_params, desc_params
5513 ),
5514 timeout=timeout_ns_action,
5515 vca_type=vca_type,
5516 db_dict=db_dict,
5517 vca_id=vca_id,
5518 )
5519
5520 db_nslcmop_update["detailed-status"] = detailed_status
5521 error_description_nslcmop = (
5522 detailed_status if nslcmop_operation_state == "FAILED" else ""
5523 )
5524 self.logger.debug(
5525 logging_text
5526 + "Done with result {} {}".format(
5527 nslcmop_operation_state, detailed_status
5528 )
5529 )
5530 return # database update is called inside finally
5531
5532 except (DbException, LcmException, N2VCException, K8sException) as e:
5533 self.logger.error(logging_text + "Exit Exception {}".format(e))
5534 exc = e
5535 except asyncio.CancelledError:
5536 self.logger.error(
5537 logging_text + "Cancelled Exception while '{}'".format(step)
5538 )
5539 exc = "Operation was cancelled"
5540 except asyncio.TimeoutError:
5541 self.logger.error(logging_text + "Timeout while '{}'".format(step))
5542 exc = "Timeout"
5543 except Exception as e:
5544 exc = traceback.format_exc()
5545 self.logger.critical(
5546 logging_text + "Exit Exception {} {}".format(type(e).__name__, e),
5547 exc_info=True,
5548 )
5549 finally:
5550 if exc:
5551 db_nslcmop_update[
5552 "detailed-status"
5553 ] = (
5554 detailed_status
5555 ) = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
5556 nslcmop_operation_state = "FAILED"
5557 if db_nsr:
5558 self._write_ns_status(
5559 nsr_id=nsr_id,
5560 ns_state=db_nsr[
5561 "nsState"
5562 ], # TODO check if degraded. For the moment use previous status
5563 current_operation="IDLE",
5564 current_operation_id=None,
5565 # error_description=error_description_nsr,
5566 # error_detail=error_detail,
5567 other_update=db_nsr_update,
5568 )
5569
5570 self._write_op_status(
5571 op_id=nslcmop_id,
5572 stage="",
5573 error_message=error_description_nslcmop,
5574 operation_state=nslcmop_operation_state,
5575 other_update=db_nslcmop_update,
5576 )
5577
5578 if nslcmop_operation_state:
5579 try:
5580 await self.msg.aiowrite(
5581 "ns",
5582 "actioned",
5583 {
5584 "nsr_id": nsr_id,
5585 "nslcmop_id": nslcmop_id,
5586 "operationState": nslcmop_operation_state,
5587 },
5588 )
5589 except Exception as e:
5590 self.logger.error(
5591 logging_text + "kafka_write notification Exception {}".format(e)
5592 )
5593 self.logger.debug(logging_text + "Exit")
5594 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_action")
5595 return nslcmop_operation_state, detailed_status
5596
5597 async def terminate_vdus(
5598 self, db_vnfr, member_vnf_index, db_nsr, update_db_nslcmops, stage, logging_text
5599 ):
5600 """This method terminates VDUs
5601
5602 Args:
5603 db_vnfr: VNF instance record
5604 member_vnf_index: VNF index to identify the VDUs to be removed
5605 db_nsr: NS instance record
5606 update_db_nslcmops: Nslcmop update record
5607 """
5608 vca_scaling_info = []
5609 scaling_info = {"scaling_group_name": "vdu_autoscale", "vdu": [], "kdu": []}
5610 scaling_info["scaling_direction"] = "IN"
5611 scaling_info["vdu-delete"] = {}
5612 scaling_info["kdu-delete"] = {}
5613 db_vdur = db_vnfr.get("vdur")
5614 vdur_list = copy(db_vdur)
5615 count_index = 0
5616 for index, vdu in enumerate(vdur_list):
5617 vca_scaling_info.append(
5618 {
5619 "osm_vdu_id": vdu["vdu-id-ref"],
5620 "member-vnf-index": member_vnf_index,
5621 "type": "delete",
5622 "vdu_index": count_index,
5623 }
5624 )
5625 scaling_info["vdu-delete"][vdu["vdu-id-ref"]] = count_index
5626 scaling_info["vdu"].append(
5627 {
5628 "name": vdu.get("name") or vdu.get("vdu-name"),
5629 "vdu_id": vdu["vdu-id-ref"],
5630 "interface": [],
5631 }
5632 )
5633 for interface in vdu["interfaces"]:
5634 scaling_info["vdu"][index]["interface"].append(
5635 {
5636 "name": interface["name"],
5637 "ip_address": interface["ip-address"],
5638 "mac_address": interface.get("mac-address"),
5639 }
5640 )
5641 self.logger.info("NS update scaling info{}".format(scaling_info))
5642 stage[2] = "Terminating VDUs"
5643 if scaling_info.get("vdu-delete"):
5644 # scale_process = "RO"
5645 if self.ro_config.ng:
5646 await self._scale_ng_ro(
5647 logging_text,
5648 db_nsr,
5649 update_db_nslcmops,
5650 db_vnfr,
5651 scaling_info,
5652 stage,
5653 )
5654
5655 async def remove_vnf(self, nsr_id, nslcmop_id, vnf_instance_id):
5656 """This method is to Remove VNF instances from NS.
5657
5658 Args:
5659 nsr_id: NS instance id
5660 nslcmop_id: nslcmop id of update
5661 vnf_instance_id: id of the VNF instance to be removed
5662
5663 Returns:
5664 result: (str, str) COMPLETED/FAILED, details
5665 """
5666 try:
5667 db_nsr_update = {}
5668 logging_text = "Task ns={} update ".format(nsr_id)
5669 check_vnfr_count = len(self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id}))
5670 self.logger.info("check_vnfr_count {}".format(check_vnfr_count))
5671 if check_vnfr_count > 1:
5672 stage = ["", "", ""]
5673 step = "Getting nslcmop from database"
5674 self.logger.debug(
5675 step + " after having waited for previous tasks to be completed"
5676 )
5677 # db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5678 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
5679 db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_instance_id})
5680 member_vnf_index = db_vnfr["member-vnf-index-ref"]
5681 """ db_vnfr = self.db.get_one(
5682 "vnfrs", {"member-vnf-index-ref": member_vnf_index, "nsr-id-ref": nsr_id}) """
5683
5684 update_db_nslcmops = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5685 await self.terminate_vdus(
5686 db_vnfr,
5687 member_vnf_index,
5688 db_nsr,
5689 update_db_nslcmops,
5690 stage,
5691 logging_text,
5692 )
5693
5694 constituent_vnfr = db_nsr.get("constituent-vnfr-ref")
5695 constituent_vnfr.remove(db_vnfr.get("_id"))
5696 db_nsr_update["constituent-vnfr-ref"] = db_nsr.get(
5697 "constituent-vnfr-ref"
5698 )
5699 self.update_db_2("nsrs", nsr_id, db_nsr_update)
5700 self.db.del_one("vnfrs", {"_id": db_vnfr.get("_id")})
5701 self.update_db_2("nsrs", nsr_id, db_nsr_update)
5702 return "COMPLETED", "Done"
5703 else:
5704 step = "Terminate VNF Failed with"
5705 raise LcmException(
5706 "{} Cannot terminate the last VNF in this NS.".format(
5707 vnf_instance_id
5708 )
5709 )
5710 except (LcmException, asyncio.CancelledError):
5711 raise
5712 except Exception as e:
5713 self.logger.debug("Error removing VNF {}".format(e))
5714 return "FAILED", "Error removing VNF {}".format(e)
5715
5716 async def _ns_redeploy_vnf(
5717 self,
5718 nsr_id,
5719 nslcmop_id,
5720 db_vnfd,
5721 db_vnfr,
5722 db_nsr,
5723 ):
5724 """This method updates and redeploys VNF instances
5725
5726 Args:
5727 nsr_id: NS instance id
5728 nslcmop_id: nslcmop id
5729 db_vnfd: VNF descriptor
5730 db_vnfr: VNF instance record
5731 db_nsr: NS instance record
5732
5733 Returns:
5734 result: (str, str) COMPLETED/FAILED, details
5735 """
5736 try:
5737 count_index = 0
5738 stage = ["", "", ""]
5739 logging_text = "Task ns={} update ".format(nsr_id)
5740 latest_vnfd_revision = db_vnfd["_admin"].get("revision")
5741 member_vnf_index = db_vnfr["member-vnf-index-ref"]
5742
5743 # Terminate old VNF resources
5744 update_db_nslcmops = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5745 await self.terminate_vdus(
5746 db_vnfr,
5747 member_vnf_index,
5748 db_nsr,
5749 update_db_nslcmops,
5750 stage,
5751 logging_text,
5752 )
5753
5754 # old_vnfd_id = db_vnfr["vnfd-id"]
5755 # new_db_vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
5756 new_db_vnfd = db_vnfd
5757 # new_vnfd_ref = new_db_vnfd["id"]
5758 # new_vnfd_id = vnfd_id
5759
5760 # Create VDUR
5761 new_vnfr_cp = []
5762 for cp in new_db_vnfd.get("ext-cpd", ()):
5763 vnf_cp = {
5764 "name": cp.get("id"),
5765 "connection-point-id": cp.get("int-cpd", {}).get("cpd"),
5766 "connection-point-vdu-id": cp.get("int-cpd", {}).get("vdu-id"),
5767 "id": cp.get("id"),
5768 }
5769 new_vnfr_cp.append(vnf_cp)
5770 new_vdur = update_db_nslcmops["operationParams"]["newVdur"]
5771 # new_vdur = self._create_vdur_descriptor_from_vnfd(db_nsd, db_vnfd, old_db_vnfd, vnfd_id, db_nsr, member_vnf_index)
5772 # new_vnfr_update = {"vnfd-ref": new_vnfd_ref, "vnfd-id": new_vnfd_id, "connection-point": new_vnfr_cp, "vdur": new_vdur, "ip-address": ""}
5773 new_vnfr_update = {
5774 "revision": latest_vnfd_revision,
5775 "connection-point": new_vnfr_cp,
5776 "vdur": new_vdur,
5777 "ip-address": "",
5778 }
5779 self.update_db_2("vnfrs", db_vnfr["_id"], new_vnfr_update)
5780 updated_db_vnfr = self.db.get_one(
5781 "vnfrs",
5782 {"member-vnf-index-ref": member_vnf_index, "nsr-id-ref": nsr_id},
5783 )
5784
5785 # Instantiate new VNF resources
5786 # update_db_nslcmops = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5787 vca_scaling_info = []
5788 scaling_info = {"scaling_group_name": "vdu_autoscale", "vdu": [], "kdu": []}
5789 scaling_info["scaling_direction"] = "OUT"
5790 scaling_info["vdu-create"] = {}
5791 scaling_info["kdu-create"] = {}
5792 vdud_instantiate_list = db_vnfd["vdu"]
5793 for index, vdud in enumerate(vdud_instantiate_list):
5794 cloud_init_text = self._get_vdu_cloud_init_content(vdud, db_vnfd)
5795 if cloud_init_text:
5796 additional_params = (
5797 self._get_vdu_additional_params(updated_db_vnfr, vdud["id"])
5798 or {}
5799 )
5800 cloud_init_list = []
5801 if cloud_init_text:
5802 # TODO Information of its own ip is not available because db_vnfr is not updated.
5803 additional_params["OSM"] = get_osm_params(
5804 updated_db_vnfr, vdud["id"], 1
5805 )
5806 cloud_init_list.append(
5807 self._parse_cloud_init(
5808 cloud_init_text,
5809 additional_params,
5810 db_vnfd["id"],
5811 vdud["id"],
5812 )
5813 )
5814 vca_scaling_info.append(
5815 {
5816 "osm_vdu_id": vdud["id"],
5817 "member-vnf-index": member_vnf_index,
5818 "type": "create",
5819 "vdu_index": count_index,
5820 }
5821 )
5822 scaling_info["vdu-create"][vdud["id"]] = count_index
5823 if self.ro_config.ng:
5824 self.logger.debug(
5825 "New Resources to be deployed: {}".format(scaling_info)
5826 )
5827 await self._scale_ng_ro(
5828 logging_text,
5829 db_nsr,
5830 update_db_nslcmops,
5831 updated_db_vnfr,
5832 scaling_info,
5833 stage,
5834 )
5835 return "COMPLETED", "Done"
5836 except (LcmException, asyncio.CancelledError):
5837 raise
5838 except Exception as e:
5839 self.logger.debug("Error updating VNF {}".format(e))
5840 return "FAILED", "Error updating VNF {}".format(e)
5841
5842 async def _ns_charm_upgrade(
5843 self,
5844 ee_id,
5845 charm_id,
5846 charm_type,
5847 path,
5848 timeout: float = None,
5849 ) -> (str, str):
5850 """This method upgrade charms in VNF instances
5851
5852 Args:
5853 ee_id: Execution environment id
5854 path: Local path to the charm
5855 charm_id: charm-id
5856 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
5857 timeout: (Float) Timeout for the ns update operation
5858
5859 Returns:
5860 result: (str, str) COMPLETED/FAILED, details
5861 """
5862 try:
5863 charm_type = charm_type or "lxc_proxy_charm"
5864 output = await self.vca_map[charm_type].upgrade_charm(
5865 ee_id=ee_id,
5866 path=path,
5867 charm_id=charm_id,
5868 charm_type=charm_type,
5869 timeout=timeout or self.timeout.ns_update,
5870 )
5871
5872 if output:
5873 return "COMPLETED", output
5874
5875 except (LcmException, asyncio.CancelledError):
5876 raise
5877
5878 except Exception as e:
5879 self.logger.debug("Error upgrading charm {}".format(path))
5880
5881 return "FAILED", "Error upgrading charm {}: {}".format(path, e)
5882
5883 async def update(self, nsr_id, nslcmop_id):
5884 """Update NS according to different update types
5885
5886 This method performs upgrade of VNF instances then updates the revision
5887 number in VNF record
5888
5889 Args:
5890 nsr_id: Network service will be updated
5891 nslcmop_id: ns lcm operation id
5892
5893 Returns:
5894 It may raise DbException, LcmException, N2VCException, K8sException
5895
5896 """
5897 # Try to lock HA task here
5898 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
5899 if not task_is_locked_by_me:
5900 return
5901
5902 logging_text = "Task ns={} update={} ".format(nsr_id, nslcmop_id)
5903 self.logger.debug(logging_text + "Enter")
5904
5905 # Set the required variables to be filled up later
5906 db_nsr = None
5907 db_nslcmop_update = {}
5908 vnfr_update = {}
5909 nslcmop_operation_state = None
5910 db_nsr_update = {}
5911 error_description_nslcmop = ""
5912 exc = None
5913 change_type = "updated"
5914 detailed_status = ""
5915 member_vnf_index = None
5916
5917 try:
5918 # wait for any previous tasks in process
5919 step = "Waiting for previous operations to terminate"
5920 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
5921 self._write_ns_status(
5922 nsr_id=nsr_id,
5923 ns_state=None,
5924 current_operation="UPDATING",
5925 current_operation_id=nslcmop_id,
5926 )
5927
5928 step = "Getting nslcmop from database"
5929 db_nslcmop = self.db.get_one(
5930 "nslcmops", {"_id": nslcmop_id}, fail_on_empty=False
5931 )
5932 update_type = db_nslcmop["operationParams"]["updateType"]
5933
5934 step = "Getting nsr from database"
5935 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
5936 old_operational_status = db_nsr["operational-status"]
5937 db_nsr_update["operational-status"] = "updating"
5938 self.update_db_2("nsrs", nsr_id, db_nsr_update)
5939 nsr_deployed = db_nsr["_admin"].get("deployed")
5940
5941 if update_type == "CHANGE_VNFPKG":
5942 # Get the input parameters given through update request
5943 vnf_instance_id = db_nslcmop["operationParams"][
5944 "changeVnfPackageData"
5945 ].get("vnfInstanceId")
5946
5947 vnfd_id = db_nslcmop["operationParams"]["changeVnfPackageData"].get(
5948 "vnfdId"
5949 )
5950 timeout_seconds = db_nslcmop["operationParams"].get("timeout_ns_update")
5951
5952 step = "Getting vnfr from database"
5953 db_vnfr = self.db.get_one(
5954 "vnfrs", {"_id": vnf_instance_id}, fail_on_empty=False
5955 )
5956
5957 step = "Getting vnfds from database"
5958 # Latest VNFD
5959 latest_vnfd = self.db.get_one(
5960 "vnfds", {"_id": vnfd_id}, fail_on_empty=False
5961 )
5962 latest_vnfd_revision = latest_vnfd["_admin"].get("revision")
5963
5964 # Current VNFD
5965 current_vnf_revision = db_vnfr.get("revision", 1)
5966 current_vnfd = self.db.get_one(
5967 "vnfds_revisions",
5968 {"_id": vnfd_id + ":" + str(current_vnf_revision)},
5969 fail_on_empty=False,
5970 )
5971 # Charm artifact paths will be filled up later
5972 (
5973 current_charm_artifact_path,
5974 target_charm_artifact_path,
5975 charm_artifact_paths,
5976 helm_artifacts,
5977 ) = ([], [], [], [])
5978
5979 step = "Checking if revision has changed in VNFD"
5980 if current_vnf_revision != latest_vnfd_revision:
5981 change_type = "policy_updated"
5982
5983 # There is new revision of VNFD, update operation is required
5984 current_vnfd_path = vnfd_id + ":" + str(current_vnf_revision)
5985 latest_vnfd_path = vnfd_id + ":" + str(latest_vnfd_revision)
5986
5987 step = "Removing the VNFD packages if they exist in the local path"
5988 shutil.rmtree(self.fs.path + current_vnfd_path, ignore_errors=True)
5989 shutil.rmtree(self.fs.path + latest_vnfd_path, ignore_errors=True)
5990
5991 step = "Get the VNFD packages from FSMongo"
5992 self.fs.sync(from_path=latest_vnfd_path)
5993 self.fs.sync(from_path=current_vnfd_path)
5994
5995 step = (
5996 "Get the charm-type, charm-id, ee-id if there is deployed VCA"
5997 )
5998 current_base_folder = current_vnfd["_admin"]["storage"]
5999 latest_base_folder = latest_vnfd["_admin"]["storage"]
6000
6001 for vca_index, vca_deployed in enumerate(
6002 get_iterable(nsr_deployed, "VCA")
6003 ):
6004 vnf_index = db_vnfr.get("member-vnf-index-ref")
6005
6006 # Getting charm-id and charm-type
6007 if vca_deployed.get("member-vnf-index") == vnf_index:
6008 vca_id = self.get_vca_id(db_vnfr, db_nsr)
6009 vca_type = vca_deployed.get("type")
6010 vdu_count_index = vca_deployed.get("vdu_count_index")
6011
6012 # Getting ee-id
6013 ee_id = vca_deployed.get("ee_id")
6014
6015 step = "Getting descriptor config"
6016 if current_vnfd.get("kdu"):
6017 search_key = "kdu_name"
6018 else:
6019 search_key = "vnfd_id"
6020
6021 entity_id = vca_deployed.get(search_key)
6022
6023 descriptor_config = get_configuration(
6024 current_vnfd, entity_id
6025 )
6026
6027 if "execution-environment-list" in descriptor_config:
6028 ee_list = descriptor_config.get(
6029 "execution-environment-list", []
6030 )
6031 else:
6032 ee_list = []
6033
6034 # There could be several charm used in the same VNF
6035 for ee_item in ee_list:
6036 if ee_item.get("juju"):
6037 step = "Getting charm name"
6038 charm_name = ee_item["juju"].get("charm")
6039
6040 step = "Setting Charm artifact paths"
6041 current_charm_artifact_path.append(
6042 get_charm_artifact_path(
6043 current_base_folder,
6044 charm_name,
6045 vca_type,
6046 current_vnf_revision,
6047 )
6048 )
6049 target_charm_artifact_path.append(
6050 get_charm_artifact_path(
6051 latest_base_folder,
6052 charm_name,
6053 vca_type,
6054 latest_vnfd_revision,
6055 )
6056 )
6057 elif ee_item.get("helm-chart"):
6058 # add chart to list and all parameters
6059 step = "Getting helm chart name"
6060 chart_name = ee_item.get("helm-chart")
6061 if (
6062 ee_item.get("helm-version")
6063 and ee_item.get("helm-version") == "v2"
6064 ):
6065 vca_type = "helm"
6066 else:
6067 vca_type = "helm-v3"
6068 step = "Setting Helm chart artifact paths"
6069
6070 helm_artifacts.append(
6071 {
6072 "current_artifact_path": get_charm_artifact_path(
6073 current_base_folder,
6074 chart_name,
6075 vca_type,
6076 current_vnf_revision,
6077 ),
6078 "target_artifact_path": get_charm_artifact_path(
6079 latest_base_folder,
6080 chart_name,
6081 vca_type,
6082 latest_vnfd_revision,
6083 ),
6084 "ee_id": ee_id,
6085 "vca_index": vca_index,
6086 "vdu_index": vdu_count_index,
6087 }
6088 )
6089
6090 charm_artifact_paths = zip(
6091 current_charm_artifact_path, target_charm_artifact_path
6092 )
6093
6094 step = "Checking if software version has changed in VNFD"
6095 if find_software_version(current_vnfd) != find_software_version(
6096 latest_vnfd
6097 ):
6098 step = "Checking if existing VNF has charm"
6099 for current_charm_path, target_charm_path in list(
6100 charm_artifact_paths
6101 ):
6102 if current_charm_path:
6103 raise LcmException(
6104 "Software version change is not supported as VNF instance {} has charm.".format(
6105 vnf_instance_id
6106 )
6107 )
6108
6109 # There is no change in the charm package, then redeploy the VNF
6110 # based on new descriptor
6111 step = "Redeploying VNF"
6112 member_vnf_index = db_vnfr["member-vnf-index-ref"]
6113 (result, detailed_status) = await self._ns_redeploy_vnf(
6114 nsr_id, nslcmop_id, latest_vnfd, db_vnfr, db_nsr
6115 )
6116 if result == "FAILED":
6117 nslcmop_operation_state = result
6118 error_description_nslcmop = detailed_status
6119 db_nslcmop_update["detailed-status"] = detailed_status
6120 self.logger.debug(
6121 logging_text
6122 + " step {} Done with result {} {}".format(
6123 step, nslcmop_operation_state, detailed_status
6124 )
6125 )
6126
6127 else:
6128 step = "Checking if any charm package has changed or not"
6129 for current_charm_path, target_charm_path in list(
6130 charm_artifact_paths
6131 ):
6132 if (
6133 current_charm_path
6134 and target_charm_path
6135 and self.check_charm_hash_changed(
6136 current_charm_path, target_charm_path
6137 )
6138 ):
6139 step = "Checking whether VNF uses juju bundle"
6140 if check_juju_bundle_existence(current_vnfd):
6141 raise LcmException(
6142 "Charm upgrade is not supported for the instance which"
6143 " uses juju-bundle: {}".format(
6144 check_juju_bundle_existence(current_vnfd)
6145 )
6146 )
6147
6148 step = "Upgrading Charm"
6149 (
6150 result,
6151 detailed_status,
6152 ) = await self._ns_charm_upgrade(
6153 ee_id=ee_id,
6154 charm_id=vca_id,
6155 charm_type=vca_type,
6156 path=self.fs.path + target_charm_path,
6157 timeout=timeout_seconds,
6158 )
6159
6160 if result == "FAILED":
6161 nslcmop_operation_state = result
6162 error_description_nslcmop = detailed_status
6163
6164 db_nslcmop_update["detailed-status"] = detailed_status
6165 self.logger.debug(
6166 logging_text
6167 + " step {} Done with result {} {}".format(
6168 step, nslcmop_operation_state, detailed_status
6169 )
6170 )
6171
6172 step = "Updating policies"
6173 member_vnf_index = db_vnfr["member-vnf-index-ref"]
6174 result = "COMPLETED"
6175 detailed_status = "Done"
6176 db_nslcmop_update["detailed-status"] = "Done"
6177
6178 # helm base EE
6179 for item in helm_artifacts:
6180 if not (
6181 item["current_artifact_path"]
6182 and item["target_artifact_path"]
6183 and self.check_charm_hash_changed(
6184 item["current_artifact_path"],
6185 item["target_artifact_path"],
6186 )
6187 ):
6188 continue
6189 db_update_entry = "_admin.deployed.VCA.{}.".format(
6190 item["vca_index"]
6191 )
6192 vnfr_id = db_vnfr["_id"]
6193 osm_config = {"osm": {"ns_id": nsr_id, "vnf_id": vnfr_id}}
6194 db_dict = {
6195 "collection": "nsrs",
6196 "filter": {"_id": nsr_id},
6197 "path": db_update_entry,
6198 }
6199 vca_type, namespace, helm_id = get_ee_id_parts(item["ee_id"])
6200 await self.vca_map[vca_type].upgrade_execution_environment(
6201 namespace=namespace,
6202 helm_id=helm_id,
6203 db_dict=db_dict,
6204 config=osm_config,
6205 artifact_path=item["target_artifact_path"],
6206 vca_type=vca_type,
6207 )
6208 vnf_id = db_vnfr.get("vnfd-ref")
6209 config_descriptor = get_configuration(latest_vnfd, vnf_id)
6210 self.logger.debug("get ssh key block")
6211 rw_mgmt_ip = None
6212 if deep_get(
6213 config_descriptor,
6214 ("config-access", "ssh-access", "required"),
6215 ):
6216 # Needed to inject a ssh key
6217 user = deep_get(
6218 config_descriptor,
6219 ("config-access", "ssh-access", "default-user"),
6220 )
6221 step = (
6222 "Install configuration Software, getting public ssh key"
6223 )
6224 pub_key = await self.vca_map[
6225 vca_type
6226 ].get_ee_ssh_public__key(
6227 ee_id=ee_id, db_dict=db_dict, vca_id=vca_id
6228 )
6229
6230 step = (
6231 "Insert public key into VM user={} ssh_key={}".format(
6232 user, pub_key
6233 )
6234 )
6235 self.logger.debug(logging_text + step)
6236
6237 # wait for RO (ip-address) Insert pub_key into VM
6238 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
6239 logging_text,
6240 nsr_id,
6241 vnfr_id,
6242 None,
6243 item["vdu_index"],
6244 user=user,
6245 pub_key=pub_key,
6246 )
6247
6248 initial_config_primitive_list = config_descriptor.get(
6249 "initial-config-primitive"
6250 )
6251 config_primitive = next(
6252 (
6253 p
6254 for p in initial_config_primitive_list
6255 if p["name"] == "config"
6256 ),
6257 None,
6258 )
6259 if not config_primitive:
6260 continue
6261
6262 deploy_params = {"OSM": get_osm_params(db_vnfr)}
6263 if rw_mgmt_ip:
6264 deploy_params["rw_mgmt_ip"] = rw_mgmt_ip
6265 if db_vnfr.get("additionalParamsForVnf"):
6266 deploy_params.update(
6267 parse_yaml_strings(
6268 db_vnfr["additionalParamsForVnf"].copy()
6269 )
6270 )
6271 primitive_params_ = self._map_primitive_params(
6272 config_primitive, {}, deploy_params
6273 )
6274
6275 step = "execute primitive '{}' params '{}'".format(
6276 config_primitive["name"], primitive_params_
6277 )
6278 self.logger.debug(logging_text + step)
6279 await self.vca_map[vca_type].exec_primitive(
6280 ee_id=ee_id,
6281 primitive_name=config_primitive["name"],
6282 params_dict=primitive_params_,
6283 db_dict=db_dict,
6284 vca_id=vca_id,
6285 vca_type=vca_type,
6286 )
6287
6288 step = "Updating policies"
6289 member_vnf_index = db_vnfr["member-vnf-index-ref"]
6290 detailed_status = "Done"
6291 db_nslcmop_update["detailed-status"] = "Done"
6292
6293 # If nslcmop_operation_state is None, so any operation is not failed.
6294 if not nslcmop_operation_state:
6295 nslcmop_operation_state = "COMPLETED"
6296
6297 # If update CHANGE_VNFPKG nslcmop_operation is successful
6298 # vnf revision need to be updated
6299 vnfr_update["revision"] = latest_vnfd_revision
6300 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
6301
6302 self.logger.debug(
6303 logging_text
6304 + " task Done with result {} {}".format(
6305 nslcmop_operation_state, detailed_status
6306 )
6307 )
6308 elif update_type == "REMOVE_VNF":
6309 # This part is included in https://osm.etsi.org/gerrit/11876
6310 vnf_instance_id = db_nslcmop["operationParams"]["removeVnfInstanceId"]
6311 db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_instance_id})
6312 member_vnf_index = db_vnfr["member-vnf-index-ref"]
6313 step = "Removing VNF"
6314 (result, detailed_status) = await self.remove_vnf(
6315 nsr_id, nslcmop_id, vnf_instance_id
6316 )
6317 if result == "FAILED":
6318 nslcmop_operation_state = result
6319 error_description_nslcmop = detailed_status
6320 db_nslcmop_update["detailed-status"] = detailed_status
6321 change_type = "vnf_terminated"
6322 if not nslcmop_operation_state:
6323 nslcmop_operation_state = "COMPLETED"
6324 self.logger.debug(
6325 logging_text
6326 + " task Done with result {} {}".format(
6327 nslcmop_operation_state, detailed_status
6328 )
6329 )
6330
6331 elif update_type == "OPERATE_VNF":
6332 vnf_id = db_nslcmop["operationParams"]["operateVnfData"][
6333 "vnfInstanceId"
6334 ]
6335 operation_type = db_nslcmop["operationParams"]["operateVnfData"][
6336 "changeStateTo"
6337 ]
6338 additional_param = db_nslcmop["operationParams"]["operateVnfData"][
6339 "additionalParam"
6340 ]
6341 (result, detailed_status) = await self.rebuild_start_stop(
6342 nsr_id, nslcmop_id, vnf_id, additional_param, operation_type
6343 )
6344 if result == "FAILED":
6345 nslcmop_operation_state = result
6346 error_description_nslcmop = detailed_status
6347 db_nslcmop_update["detailed-status"] = detailed_status
6348 if not nslcmop_operation_state:
6349 nslcmop_operation_state = "COMPLETED"
6350 self.logger.debug(
6351 logging_text
6352 + " task Done with result {} {}".format(
6353 nslcmop_operation_state, detailed_status
6354 )
6355 )
6356
6357 # If nslcmop_operation_state is None, so any operation is not failed.
6358 # All operations are executed in overall.
6359 if not nslcmop_operation_state:
6360 nslcmop_operation_state = "COMPLETED"
6361 db_nsr_update["operational-status"] = old_operational_status
6362
6363 except (DbException, LcmException, N2VCException, K8sException) as e:
6364 self.logger.error(logging_text + "Exit Exception {}".format(e))
6365 exc = e
6366 except asyncio.CancelledError:
6367 self.logger.error(
6368 logging_text + "Cancelled Exception while '{}'".format(step)
6369 )
6370 exc = "Operation was cancelled"
6371 except asyncio.TimeoutError:
6372 self.logger.error(logging_text + "Timeout while '{}'".format(step))
6373 exc = "Timeout"
6374 except Exception as e:
6375 exc = traceback.format_exc()
6376 self.logger.critical(
6377 logging_text + "Exit Exception {} {}".format(type(e).__name__, e),
6378 exc_info=True,
6379 )
6380 finally:
6381 if exc:
6382 db_nslcmop_update[
6383 "detailed-status"
6384 ] = (
6385 detailed_status
6386 ) = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
6387 nslcmop_operation_state = "FAILED"
6388 db_nsr_update["operational-status"] = old_operational_status
6389 if db_nsr:
6390 self._write_ns_status(
6391 nsr_id=nsr_id,
6392 ns_state=db_nsr["nsState"],
6393 current_operation="IDLE",
6394 current_operation_id=None,
6395 other_update=db_nsr_update,
6396 )
6397
6398 self._write_op_status(
6399 op_id=nslcmop_id,
6400 stage="",
6401 error_message=error_description_nslcmop,
6402 operation_state=nslcmop_operation_state,
6403 other_update=db_nslcmop_update,
6404 )
6405
6406 if nslcmop_operation_state:
6407 try:
6408 msg = {
6409 "nsr_id": nsr_id,
6410 "nslcmop_id": nslcmop_id,
6411 "operationState": nslcmop_operation_state,
6412 }
6413 if (
6414 change_type in ("vnf_terminated", "policy_updated")
6415 and member_vnf_index
6416 ):
6417 msg.update({"vnf_member_index": member_vnf_index})
6418 await self.msg.aiowrite("ns", change_type, msg)
6419 except Exception as e:
6420 self.logger.error(
6421 logging_text + "kafka_write notification Exception {}".format(e)
6422 )
6423 self.logger.debug(logging_text + "Exit")
6424 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_update")
6425 return nslcmop_operation_state, detailed_status
6426
6427 async def scale(self, nsr_id, nslcmop_id):
6428 # Try to lock HA task here
6429 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
6430 if not task_is_locked_by_me:
6431 return
6432
6433 logging_text = "Task ns={} scale={} ".format(nsr_id, nslcmop_id)
6434 stage = ["", "", ""]
6435 tasks_dict_info = {}
6436 # ^ stage, step, VIM progress
6437 self.logger.debug(logging_text + "Enter")
6438 # get all needed from database
6439 db_nsr = None
6440 db_nslcmop_update = {}
6441 db_nsr_update = {}
6442 exc = None
6443 # in case of error, indicates what part of scale was failed to put nsr at error status
6444 scale_process = None
6445 old_operational_status = ""
6446 old_config_status = ""
6447 nsi_id = None
6448 try:
6449 # wait for any previous tasks in process
6450 step = "Waiting for previous operations to terminate"
6451 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
6452 self._write_ns_status(
6453 nsr_id=nsr_id,
6454 ns_state=None,
6455 current_operation="SCALING",
6456 current_operation_id=nslcmop_id,
6457 )
6458
6459 step = "Getting nslcmop from database"
6460 self.logger.debug(
6461 step + " after having waited for previous tasks to be completed"
6462 )
6463 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
6464
6465 step = "Getting nsr from database"
6466 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
6467 old_operational_status = db_nsr["operational-status"]
6468 old_config_status = db_nsr["config-status"]
6469
6470 step = "Parsing scaling parameters"
6471 db_nsr_update["operational-status"] = "scaling"
6472 self.update_db_2("nsrs", nsr_id, db_nsr_update)
6473 nsr_deployed = db_nsr["_admin"].get("deployed")
6474
6475 vnf_index = db_nslcmop["operationParams"]["scaleVnfData"][
6476 "scaleByStepData"
6477 ]["member-vnf-index"]
6478 scaling_group = db_nslcmop["operationParams"]["scaleVnfData"][
6479 "scaleByStepData"
6480 ]["scaling-group-descriptor"]
6481 scaling_type = db_nslcmop["operationParams"]["scaleVnfData"]["scaleVnfType"]
6482 # for backward compatibility
6483 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
6484 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
6485 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
6486 self.update_db_2("nsrs", nsr_id, db_nsr_update)
6487
6488 step = "Getting vnfr from database"
6489 db_vnfr = self.db.get_one(
6490 "vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id}
6491 )
6492
6493 vca_id = self.get_vca_id(db_vnfr, db_nsr)
6494
6495 step = "Getting vnfd from database"
6496 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
6497
6498 base_folder = db_vnfd["_admin"]["storage"]
6499
6500 step = "Getting scaling-group-descriptor"
6501 scaling_descriptor = find_in_list(
6502 get_scaling_aspect(db_vnfd),
6503 lambda scale_desc: scale_desc["name"] == scaling_group,
6504 )
6505 if not scaling_descriptor:
6506 raise LcmException(
6507 "input parameter 'scaleByStepData':'scaling-group-descriptor':'{}' is not present "
6508 "at vnfd:scaling-group-descriptor".format(scaling_group)
6509 )
6510
6511 step = "Sending scale order to VIM"
6512 # TODO check if ns is in a proper status
6513 nb_scale_op = 0
6514 if not db_nsr["_admin"].get("scaling-group"):
6515 self.update_db_2(
6516 "nsrs",
6517 nsr_id,
6518 {
6519 "_admin.scaling-group": [
6520 {"name": scaling_group, "nb-scale-op": 0}
6521 ]
6522 },
6523 )
6524 admin_scale_index = 0
6525 else:
6526 for admin_scale_index, admin_scale_info in enumerate(
6527 db_nsr["_admin"]["scaling-group"]
6528 ):
6529 if admin_scale_info["name"] == scaling_group:
6530 nb_scale_op = admin_scale_info.get("nb-scale-op", 0)
6531 break
6532 else: # not found, set index one plus last element and add new entry with the name
6533 admin_scale_index += 1
6534 db_nsr_update[
6535 "_admin.scaling-group.{}.name".format(admin_scale_index)
6536 ] = scaling_group
6537
6538 vca_scaling_info = []
6539 scaling_info = {"scaling_group_name": scaling_group, "vdu": [], "kdu": []}
6540 if scaling_type == "SCALE_OUT":
6541 if "aspect-delta-details" not in scaling_descriptor:
6542 raise LcmException(
6543 "Aspect delta details not fount in scaling descriptor {}".format(
6544 scaling_descriptor["name"]
6545 )
6546 )
6547 # count if max-instance-count is reached
6548 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
6549
6550 scaling_info["scaling_direction"] = "OUT"
6551 scaling_info["vdu-create"] = {}
6552 scaling_info["kdu-create"] = {}
6553 for delta in deltas:
6554 for vdu_delta in delta.get("vdu-delta", {}):
6555 vdud = get_vdu(db_vnfd, vdu_delta["id"])
6556 # vdu_index also provides the number of instance of the targeted vdu
6557 vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta)
6558 cloud_init_text = self._get_vdu_cloud_init_content(
6559 vdud, db_vnfd
6560 )
6561 if cloud_init_text:
6562 additional_params = (
6563 self._get_vdu_additional_params(db_vnfr, vdud["id"])
6564 or {}
6565 )
6566 cloud_init_list = []
6567
6568 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
6569 max_instance_count = 10
6570 if vdu_profile and "max-number-of-instances" in vdu_profile:
6571 max_instance_count = vdu_profile.get(
6572 "max-number-of-instances", 10
6573 )
6574
6575 default_instance_num = get_number_of_instances(
6576 db_vnfd, vdud["id"]
6577 )
6578 instances_number = vdu_delta.get("number-of-instances", 1)
6579 nb_scale_op += instances_number
6580
6581 new_instance_count = nb_scale_op + default_instance_num
6582 # Control if new count is over max and vdu count is less than max.
6583 # Then assign new instance count
6584 if new_instance_count > max_instance_count > vdu_count:
6585 instances_number = new_instance_count - max_instance_count
6586 else:
6587 instances_number = instances_number
6588
6589 if new_instance_count > max_instance_count:
6590 raise LcmException(
6591 "reached the limit of {} (max-instance-count) "
6592 "scaling-out operations for the "
6593 "scaling-group-descriptor '{}'".format(
6594 nb_scale_op, scaling_group
6595 )
6596 )
6597 for x in range(vdu_delta.get("number-of-instances", 1)):
6598 if cloud_init_text:
6599 # TODO Information of its own ip is not available because db_vnfr is not updated.
6600 additional_params["OSM"] = get_osm_params(
6601 db_vnfr, vdu_delta["id"], vdu_index + x
6602 )
6603 cloud_init_list.append(
6604 self._parse_cloud_init(
6605 cloud_init_text,
6606 additional_params,
6607 db_vnfd["id"],
6608 vdud["id"],
6609 )
6610 )
6611 vca_scaling_info.append(
6612 {
6613 "osm_vdu_id": vdu_delta["id"],
6614 "member-vnf-index": vnf_index,
6615 "type": "create",
6616 "vdu_index": vdu_index + x,
6617 }
6618 )
6619 scaling_info["vdu-create"][vdu_delta["id"]] = instances_number
6620 for kdu_delta in delta.get("kdu-resource-delta", {}):
6621 kdu_profile = get_kdu_resource_profile(db_vnfd, kdu_delta["id"])
6622 kdu_name = kdu_profile["kdu-name"]
6623 resource_name = kdu_profile.get("resource-name", "")
6624
6625 # Might have different kdus in the same delta
6626 # Should have list for each kdu
6627 if not scaling_info["kdu-create"].get(kdu_name, None):
6628 scaling_info["kdu-create"][kdu_name] = []
6629
6630 kdur = get_kdur(db_vnfr, kdu_name)
6631 if kdur.get("helm-chart"):
6632 k8s_cluster_type = "helm-chart-v3"
6633 self.logger.debug("kdur: {}".format(kdur))
6634 if (
6635 kdur.get("helm-version")
6636 and kdur.get("helm-version") == "v2"
6637 ):
6638 k8s_cluster_type = "helm-chart"
6639 elif kdur.get("juju-bundle"):
6640 k8s_cluster_type = "juju-bundle"
6641 else:
6642 raise LcmException(
6643 "kdu type for kdu='{}.{}' is neither helm-chart nor "
6644 "juju-bundle. Maybe an old NBI version is running".format(
6645 db_vnfr["member-vnf-index-ref"], kdu_name
6646 )
6647 )
6648
6649 max_instance_count = 10
6650 if kdu_profile and "max-number-of-instances" in kdu_profile:
6651 max_instance_count = kdu_profile.get(
6652 "max-number-of-instances", 10
6653 )
6654
6655 nb_scale_op += kdu_delta.get("number-of-instances", 1)
6656 deployed_kdu, _ = get_deployed_kdu(
6657 nsr_deployed, kdu_name, vnf_index
6658 )
6659 if deployed_kdu is None:
6660 raise LcmException(
6661 "KDU '{}' for vnf '{}' not deployed".format(
6662 kdu_name, vnf_index
6663 )
6664 )
6665 kdu_instance = deployed_kdu.get("kdu-instance")
6666 instance_num = await self.k8scluster_map[
6667 k8s_cluster_type
6668 ].get_scale_count(
6669 resource_name,
6670 kdu_instance,
6671 vca_id=vca_id,
6672 cluster_uuid=deployed_kdu.get("k8scluster-uuid"),
6673 kdu_model=deployed_kdu.get("kdu-model"),
6674 )
6675 kdu_replica_count = instance_num + kdu_delta.get(
6676 "number-of-instances", 1
6677 )
6678
6679 # Control if new count is over max and instance_num is less than max.
6680 # Then assign max instance number to kdu replica count
6681 if kdu_replica_count > max_instance_count > instance_num:
6682 kdu_replica_count = max_instance_count
6683 if kdu_replica_count > max_instance_count:
6684 raise LcmException(
6685 "reached the limit of {} (max-instance-count) "
6686 "scaling-out operations for the "
6687 "scaling-group-descriptor '{}'".format(
6688 instance_num, scaling_group
6689 )
6690 )
6691
6692 for x in range(kdu_delta.get("number-of-instances", 1)):
6693 vca_scaling_info.append(
6694 {
6695 "osm_kdu_id": kdu_name,
6696 "member-vnf-index": vnf_index,
6697 "type": "create",
6698 "kdu_index": instance_num + x - 1,
6699 }
6700 )
6701 scaling_info["kdu-create"][kdu_name].append(
6702 {
6703 "member-vnf-index": vnf_index,
6704 "type": "create",
6705 "k8s-cluster-type": k8s_cluster_type,
6706 "resource-name": resource_name,
6707 "scale": kdu_replica_count,
6708 }
6709 )
6710 elif scaling_type == "SCALE_IN":
6711 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
6712
6713 scaling_info["scaling_direction"] = "IN"
6714 scaling_info["vdu-delete"] = {}
6715 scaling_info["kdu-delete"] = {}
6716
6717 for delta in deltas:
6718 for vdu_delta in delta.get("vdu-delta", {}):
6719 vdu_count = vdu_index = get_vdur_index(db_vnfr, vdu_delta)
6720 min_instance_count = 0
6721 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
6722 if vdu_profile and "min-number-of-instances" in vdu_profile:
6723 min_instance_count = vdu_profile["min-number-of-instances"]
6724
6725 default_instance_num = get_number_of_instances(
6726 db_vnfd, vdu_delta["id"]
6727 )
6728 instance_num = vdu_delta.get("number-of-instances", 1)
6729 nb_scale_op -= instance_num
6730
6731 new_instance_count = nb_scale_op + default_instance_num
6732
6733 if new_instance_count < min_instance_count < vdu_count:
6734 instances_number = min_instance_count - new_instance_count
6735 else:
6736 instances_number = instance_num
6737
6738 if new_instance_count < min_instance_count:
6739 raise LcmException(
6740 "reached the limit of {} (min-instance-count) scaling-in operations for the "
6741 "scaling-group-descriptor '{}'".format(
6742 nb_scale_op, scaling_group
6743 )
6744 )
6745 for x in range(vdu_delta.get("number-of-instances", 1)):
6746 vca_scaling_info.append(
6747 {
6748 "osm_vdu_id": vdu_delta["id"],
6749 "member-vnf-index": vnf_index,
6750 "type": "delete",
6751 "vdu_index": vdu_index - 1 - x,
6752 }
6753 )
6754 scaling_info["vdu-delete"][vdu_delta["id"]] = instances_number
6755 for kdu_delta in delta.get("kdu-resource-delta", {}):
6756 kdu_profile = get_kdu_resource_profile(db_vnfd, kdu_delta["id"])
6757 kdu_name = kdu_profile["kdu-name"]
6758 resource_name = kdu_profile.get("resource-name", "")
6759
6760 if not scaling_info["kdu-delete"].get(kdu_name, None):
6761 scaling_info["kdu-delete"][kdu_name] = []
6762
6763 kdur = get_kdur(db_vnfr, kdu_name)
6764 if kdur.get("helm-chart"):
6765 k8s_cluster_type = "helm-chart-v3"
6766 self.logger.debug("kdur: {}".format(kdur))
6767 if (
6768 kdur.get("helm-version")
6769 and kdur.get("helm-version") == "v2"
6770 ):
6771 k8s_cluster_type = "helm-chart"
6772 elif kdur.get("juju-bundle"):
6773 k8s_cluster_type = "juju-bundle"
6774 else:
6775 raise LcmException(
6776 "kdu type for kdu='{}.{}' is neither helm-chart nor "
6777 "juju-bundle. Maybe an old NBI version is running".format(
6778 db_vnfr["member-vnf-index-ref"], kdur["kdu-name"]
6779 )
6780 )
6781
6782 min_instance_count = 0
6783 if kdu_profile and "min-number-of-instances" in kdu_profile:
6784 min_instance_count = kdu_profile["min-number-of-instances"]
6785
6786 nb_scale_op -= kdu_delta.get("number-of-instances", 1)
6787 deployed_kdu, _ = get_deployed_kdu(
6788 nsr_deployed, kdu_name, vnf_index
6789 )
6790 if deployed_kdu is None:
6791 raise LcmException(
6792 "KDU '{}' for vnf '{}' not deployed".format(
6793 kdu_name, vnf_index
6794 )
6795 )
6796 kdu_instance = deployed_kdu.get("kdu-instance")
6797 instance_num = await self.k8scluster_map[
6798 k8s_cluster_type
6799 ].get_scale_count(
6800 resource_name,
6801 kdu_instance,
6802 vca_id=vca_id,
6803 cluster_uuid=deployed_kdu.get("k8scluster-uuid"),
6804 kdu_model=deployed_kdu.get("kdu-model"),
6805 )
6806 kdu_replica_count = instance_num - kdu_delta.get(
6807 "number-of-instances", 1
6808 )
6809
6810 if kdu_replica_count < min_instance_count < instance_num:
6811 kdu_replica_count = min_instance_count
6812 if kdu_replica_count < min_instance_count:
6813 raise LcmException(
6814 "reached the limit of {} (min-instance-count) scaling-in operations for the "
6815 "scaling-group-descriptor '{}'".format(
6816 instance_num, scaling_group
6817 )
6818 )
6819
6820 for x in range(kdu_delta.get("number-of-instances", 1)):
6821 vca_scaling_info.append(
6822 {
6823 "osm_kdu_id": kdu_name,
6824 "member-vnf-index": vnf_index,
6825 "type": "delete",
6826 "kdu_index": instance_num - x - 1,
6827 }
6828 )
6829 scaling_info["kdu-delete"][kdu_name].append(
6830 {
6831 "member-vnf-index": vnf_index,
6832 "type": "delete",
6833 "k8s-cluster-type": k8s_cluster_type,
6834 "resource-name": resource_name,
6835 "scale": kdu_replica_count,
6836 }
6837 )
6838
6839 # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
6840 vdu_delete = copy(scaling_info.get("vdu-delete"))
6841 if scaling_info["scaling_direction"] == "IN":
6842 for vdur in reversed(db_vnfr["vdur"]):
6843 if vdu_delete.get(vdur["vdu-id-ref"]):
6844 vdu_delete[vdur["vdu-id-ref"]] -= 1
6845 scaling_info["vdu"].append(
6846 {
6847 "name": vdur.get("name") or vdur.get("vdu-name"),
6848 "vdu_id": vdur["vdu-id-ref"],
6849 "interface": [],
6850 }
6851 )
6852 for interface in vdur["interfaces"]:
6853 scaling_info["vdu"][-1]["interface"].append(
6854 {
6855 "name": interface["name"],
6856 "ip_address": interface["ip-address"],
6857 "mac_address": interface.get("mac-address"),
6858 }
6859 )
6860 # vdu_delete = vdu_scaling_info.pop("vdu-delete")
6861
6862 # PRE-SCALE BEGIN
6863 step = "Executing pre-scale vnf-config-primitive"
6864 if scaling_descriptor.get("scaling-config-action"):
6865 for scaling_config_action in scaling_descriptor[
6866 "scaling-config-action"
6867 ]:
6868 if (
6869 scaling_config_action.get("trigger") == "pre-scale-in"
6870 and scaling_type == "SCALE_IN"
6871 ) or (
6872 scaling_config_action.get("trigger") == "pre-scale-out"
6873 and scaling_type == "SCALE_OUT"
6874 ):
6875 vnf_config_primitive = scaling_config_action[
6876 "vnf-config-primitive-name-ref"
6877 ]
6878 step = db_nslcmop_update[
6879 "detailed-status"
6880 ] = "executing pre-scale scaling-config-action '{}'".format(
6881 vnf_config_primitive
6882 )
6883
6884 # look for primitive
6885 for config_primitive in (
6886 get_configuration(db_vnfd, db_vnfd["id"]) or {}
6887 ).get("config-primitive", ()):
6888 if config_primitive["name"] == vnf_config_primitive:
6889 break
6890 else:
6891 raise LcmException(
6892 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
6893 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
6894 "primitive".format(scaling_group, vnf_config_primitive)
6895 )
6896
6897 vnfr_params = {"VDU_SCALE_INFO": scaling_info}
6898 if db_vnfr.get("additionalParamsForVnf"):
6899 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
6900
6901 scale_process = "VCA"
6902 db_nsr_update["config-status"] = "configuring pre-scaling"
6903 primitive_params = self._map_primitive_params(
6904 config_primitive, {}, vnfr_params
6905 )
6906
6907 # Pre-scale retry check: Check if this sub-operation has been executed before
6908 op_index = self._check_or_add_scale_suboperation(
6909 db_nslcmop,
6910 vnf_index,
6911 vnf_config_primitive,
6912 primitive_params,
6913 "PRE-SCALE",
6914 )
6915 if op_index == self.SUBOPERATION_STATUS_SKIP:
6916 # Skip sub-operation
6917 result = "COMPLETED"
6918 result_detail = "Done"
6919 self.logger.debug(
6920 logging_text
6921 + "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
6922 vnf_config_primitive, result, result_detail
6923 )
6924 )
6925 else:
6926 if op_index == self.SUBOPERATION_STATUS_NEW:
6927 # New sub-operation: Get index of this sub-operation
6928 op_index = (
6929 len(db_nslcmop.get("_admin", {}).get("operations"))
6930 - 1
6931 )
6932 self.logger.debug(
6933 logging_text
6934 + "vnf_config_primitive={} New sub-operation".format(
6935 vnf_config_primitive
6936 )
6937 )
6938 else:
6939 # retry: Get registered params for this existing sub-operation
6940 op = db_nslcmop.get("_admin", {}).get("operations", [])[
6941 op_index
6942 ]
6943 vnf_index = op.get("member_vnf_index")
6944 vnf_config_primitive = op.get("primitive")
6945 primitive_params = op.get("primitive_params")
6946 self.logger.debug(
6947 logging_text
6948 + "vnf_config_primitive={} Sub-operation retry".format(
6949 vnf_config_primitive
6950 )
6951 )
6952 # Execute the primitive, either with new (first-time) or registered (reintent) args
6953 ee_descriptor_id = config_primitive.get(
6954 "execution-environment-ref"
6955 )
6956 primitive_name = config_primitive.get(
6957 "execution-environment-primitive", vnf_config_primitive
6958 )
6959 ee_id, vca_type = self._look_for_deployed_vca(
6960 nsr_deployed["VCA"],
6961 member_vnf_index=vnf_index,
6962 vdu_id=None,
6963 vdu_count_index=None,
6964 ee_descriptor_id=ee_descriptor_id,
6965 )
6966 result, result_detail = await self._ns_execute_primitive(
6967 ee_id,
6968 primitive_name,
6969 primitive_params,
6970 vca_type=vca_type,
6971 vca_id=vca_id,
6972 )
6973 self.logger.debug(
6974 logging_text
6975 + "vnf_config_primitive={} Done with result {} {}".format(
6976 vnf_config_primitive, result, result_detail
6977 )
6978 )
6979 # Update operationState = COMPLETED | FAILED
6980 self._update_suboperation_status(
6981 db_nslcmop, op_index, result, result_detail
6982 )
6983
6984 if result == "FAILED":
6985 raise LcmException(result_detail)
6986 db_nsr_update["config-status"] = old_config_status
6987 scale_process = None
6988 # PRE-SCALE END
6989
6990 db_nsr_update[
6991 "_admin.scaling-group.{}.nb-scale-op".format(admin_scale_index)
6992 ] = nb_scale_op
6993 db_nsr_update[
6994 "_admin.scaling-group.{}.time".format(admin_scale_index)
6995 ] = time()
6996
6997 # SCALE-IN VCA - BEGIN
6998 if vca_scaling_info:
6999 step = db_nslcmop_update[
7000 "detailed-status"
7001 ] = "Deleting the execution environments"
7002 scale_process = "VCA"
7003 for vca_info in vca_scaling_info:
7004 if vca_info["type"] == "delete" and not vca_info.get("osm_kdu_id"):
7005 member_vnf_index = str(vca_info["member-vnf-index"])
7006 self.logger.debug(
7007 logging_text + "vdu info: {}".format(vca_info)
7008 )
7009 if vca_info.get("osm_vdu_id"):
7010 vdu_id = vca_info["osm_vdu_id"]
7011 vdu_index = int(vca_info["vdu_index"])
7012 stage[
7013 1
7014 ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7015 member_vnf_index, vdu_id, vdu_index
7016 )
7017 stage[2] = step = "Scaling in VCA"
7018 self._write_op_status(op_id=nslcmop_id, stage=stage)
7019 vca_update = db_nsr["_admin"]["deployed"]["VCA"]
7020 config_update = db_nsr["configurationStatus"]
7021 for vca_index, vca in enumerate(vca_update):
7022 if (
7023 (vca or vca.get("ee_id"))
7024 and vca["member-vnf-index"] == member_vnf_index
7025 and vca["vdu_count_index"] == vdu_index
7026 ):
7027 if vca.get("vdu_id"):
7028 config_descriptor = get_configuration(
7029 db_vnfd, vca.get("vdu_id")
7030 )
7031 elif vca.get("kdu_name"):
7032 config_descriptor = get_configuration(
7033 db_vnfd, vca.get("kdu_name")
7034 )
7035 else:
7036 config_descriptor = get_configuration(
7037 db_vnfd, db_vnfd["id"]
7038 )
7039 operation_params = (
7040 db_nslcmop.get("operationParams") or {}
7041 )
7042 exec_terminate_primitives = not operation_params.get(
7043 "skip_terminate_primitives"
7044 ) and vca.get("needed_terminate")
7045 task = asyncio.ensure_future(
7046 asyncio.wait_for(
7047 self.destroy_N2VC(
7048 logging_text,
7049 db_nslcmop,
7050 vca,
7051 config_descriptor,
7052 vca_index,
7053 destroy_ee=True,
7054 exec_primitives=exec_terminate_primitives,
7055 scaling_in=True,
7056 vca_id=vca_id,
7057 ),
7058 timeout=self.timeout.charm_delete,
7059 )
7060 )
7061 tasks_dict_info[task] = "Terminating VCA {}".format(
7062 vca.get("ee_id")
7063 )
7064 del vca_update[vca_index]
7065 del config_update[vca_index]
7066 # wait for pending tasks of terminate primitives
7067 if tasks_dict_info:
7068 self.logger.debug(
7069 logging_text
7070 + "Waiting for tasks {}".format(
7071 list(tasks_dict_info.keys())
7072 )
7073 )
7074 error_list = await self._wait_for_tasks(
7075 logging_text,
7076 tasks_dict_info,
7077 min(
7078 self.timeout.charm_delete, self.timeout.ns_terminate
7079 ),
7080 stage,
7081 nslcmop_id,
7082 )
7083 tasks_dict_info.clear()
7084 if error_list:
7085 raise LcmException("; ".join(error_list))
7086
7087 db_vca_and_config_update = {
7088 "_admin.deployed.VCA": vca_update,
7089 "configurationStatus": config_update,
7090 }
7091 self.update_db_2(
7092 "nsrs", db_nsr["_id"], db_vca_and_config_update
7093 )
7094 scale_process = None
7095 # SCALE-IN VCA - END
7096
7097 # SCALE RO - BEGIN
7098 if scaling_info.get("vdu-create") or scaling_info.get("vdu-delete"):
7099 scale_process = "RO"
7100 if self.ro_config.ng:
7101 await self._scale_ng_ro(
7102 logging_text, db_nsr, db_nslcmop, db_vnfr, scaling_info, stage
7103 )
7104 scaling_info.pop("vdu-create", None)
7105 scaling_info.pop("vdu-delete", None)
7106
7107 scale_process = None
7108 # SCALE RO - END
7109
7110 # SCALE KDU - BEGIN
7111 if scaling_info.get("kdu-create") or scaling_info.get("kdu-delete"):
7112 scale_process = "KDU"
7113 await self._scale_kdu(
7114 logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info
7115 )
7116 scaling_info.pop("kdu-create", None)
7117 scaling_info.pop("kdu-delete", None)
7118
7119 scale_process = None
7120 # SCALE KDU - END
7121
7122 if db_nsr_update:
7123 self.update_db_2("nsrs", nsr_id, db_nsr_update)
7124
7125 # SCALE-UP VCA - BEGIN
7126 if vca_scaling_info:
7127 step = db_nslcmop_update[
7128 "detailed-status"
7129 ] = "Creating new execution environments"
7130 scale_process = "VCA"
7131 for vca_info in vca_scaling_info:
7132 if vca_info["type"] == "create" and not vca_info.get("osm_kdu_id"):
7133 member_vnf_index = str(vca_info["member-vnf-index"])
7134 self.logger.debug(
7135 logging_text + "vdu info: {}".format(vca_info)
7136 )
7137 vnfd_id = db_vnfr["vnfd-ref"]
7138 if vca_info.get("osm_vdu_id"):
7139 vdu_index = int(vca_info["vdu_index"])
7140 deploy_params = {"OSM": get_osm_params(db_vnfr)}
7141 if db_vnfr.get("additionalParamsForVnf"):
7142 deploy_params.update(
7143 parse_yaml_strings(
7144 db_vnfr["additionalParamsForVnf"].copy()
7145 )
7146 )
7147 descriptor_config = get_configuration(
7148 db_vnfd, db_vnfd["id"]
7149 )
7150 if descriptor_config:
7151 vdu_id = None
7152 vdu_name = None
7153 kdu_name = None
7154 kdu_index = None
7155 self._deploy_n2vc(
7156 logging_text=logging_text
7157 + "member_vnf_index={} ".format(member_vnf_index),
7158 db_nsr=db_nsr,
7159 db_vnfr=db_vnfr,
7160 nslcmop_id=nslcmop_id,
7161 nsr_id=nsr_id,
7162 nsi_id=nsi_id,
7163 vnfd_id=vnfd_id,
7164 vdu_id=vdu_id,
7165 kdu_name=kdu_name,
7166 kdu_index=kdu_index,
7167 member_vnf_index=member_vnf_index,
7168 vdu_index=vdu_index,
7169 vdu_name=vdu_name,
7170 deploy_params=deploy_params,
7171 descriptor_config=descriptor_config,
7172 base_folder=base_folder,
7173 task_instantiation_info=tasks_dict_info,
7174 stage=stage,
7175 )
7176 vdu_id = vca_info["osm_vdu_id"]
7177 vdur = find_in_list(
7178 db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id
7179 )
7180 descriptor_config = get_configuration(db_vnfd, vdu_id)
7181 if vdur.get("additionalParams"):
7182 deploy_params_vdu = parse_yaml_strings(
7183 vdur["additionalParams"]
7184 )
7185 else:
7186 deploy_params_vdu = deploy_params
7187 deploy_params_vdu["OSM"] = get_osm_params(
7188 db_vnfr, vdu_id, vdu_count_index=vdu_index
7189 )
7190 if descriptor_config:
7191 vdu_name = None
7192 kdu_name = None
7193 kdu_index = None
7194 stage[
7195 1
7196 ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7197 member_vnf_index, vdu_id, vdu_index
7198 )
7199 stage[2] = step = "Scaling out VCA"
7200 self._write_op_status(op_id=nslcmop_id, stage=stage)
7201 self._deploy_n2vc(
7202 logging_text=logging_text
7203 + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7204 member_vnf_index, vdu_id, vdu_index
7205 ),
7206 db_nsr=db_nsr,
7207 db_vnfr=db_vnfr,
7208 nslcmop_id=nslcmop_id,
7209 nsr_id=nsr_id,
7210 nsi_id=nsi_id,
7211 vnfd_id=vnfd_id,
7212 vdu_id=vdu_id,
7213 kdu_name=kdu_name,
7214 member_vnf_index=member_vnf_index,
7215 vdu_index=vdu_index,
7216 kdu_index=kdu_index,
7217 vdu_name=vdu_name,
7218 deploy_params=deploy_params_vdu,
7219 descriptor_config=descriptor_config,
7220 base_folder=base_folder,
7221 task_instantiation_info=tasks_dict_info,
7222 stage=stage,
7223 )
7224 # SCALE-UP VCA - END
7225 scale_process = None
7226
7227 # POST-SCALE BEGIN
7228 # execute primitive service POST-SCALING
7229 step = "Executing post-scale vnf-config-primitive"
7230 if scaling_descriptor.get("scaling-config-action"):
7231 for scaling_config_action in scaling_descriptor[
7232 "scaling-config-action"
7233 ]:
7234 if (
7235 scaling_config_action.get("trigger") == "post-scale-in"
7236 and scaling_type == "SCALE_IN"
7237 ) or (
7238 scaling_config_action.get("trigger") == "post-scale-out"
7239 and scaling_type == "SCALE_OUT"
7240 ):
7241 vnf_config_primitive = scaling_config_action[
7242 "vnf-config-primitive-name-ref"
7243 ]
7244 step = db_nslcmop_update[
7245 "detailed-status"
7246 ] = "executing post-scale scaling-config-action '{}'".format(
7247 vnf_config_primitive
7248 )
7249
7250 vnfr_params = {"VDU_SCALE_INFO": scaling_info}
7251 if db_vnfr.get("additionalParamsForVnf"):
7252 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
7253
7254 # look for primitive
7255 for config_primitive in (
7256 get_configuration(db_vnfd, db_vnfd["id"]) or {}
7257 ).get("config-primitive", ()):
7258 if config_primitive["name"] == vnf_config_primitive:
7259 break
7260 else:
7261 raise LcmException(
7262 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-"
7263 "action[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:"
7264 "config-primitive".format(
7265 scaling_group, vnf_config_primitive
7266 )
7267 )
7268 scale_process = "VCA"
7269 db_nsr_update["config-status"] = "configuring post-scaling"
7270 primitive_params = self._map_primitive_params(
7271 config_primitive, {}, vnfr_params
7272 )
7273
7274 # Post-scale retry check: Check if this sub-operation has been executed before
7275 op_index = self._check_or_add_scale_suboperation(
7276 db_nslcmop,
7277 vnf_index,
7278 vnf_config_primitive,
7279 primitive_params,
7280 "POST-SCALE",
7281 )
7282 if op_index == self.SUBOPERATION_STATUS_SKIP:
7283 # Skip sub-operation
7284 result = "COMPLETED"
7285 result_detail = "Done"
7286 self.logger.debug(
7287 logging_text
7288 + "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
7289 vnf_config_primitive, result, result_detail
7290 )
7291 )
7292 else:
7293 if op_index == self.SUBOPERATION_STATUS_NEW:
7294 # New sub-operation: Get index of this sub-operation
7295 op_index = (
7296 len(db_nslcmop.get("_admin", {}).get("operations"))
7297 - 1
7298 )
7299 self.logger.debug(
7300 logging_text
7301 + "vnf_config_primitive={} New sub-operation".format(
7302 vnf_config_primitive
7303 )
7304 )
7305 else:
7306 # retry: Get registered params for this existing sub-operation
7307 op = db_nslcmop.get("_admin", {}).get("operations", [])[
7308 op_index
7309 ]
7310 vnf_index = op.get("member_vnf_index")
7311 vnf_config_primitive = op.get("primitive")
7312 primitive_params = op.get("primitive_params")
7313 self.logger.debug(
7314 logging_text
7315 + "vnf_config_primitive={} Sub-operation retry".format(
7316 vnf_config_primitive
7317 )
7318 )
7319 # Execute the primitive, either with new (first-time) or registered (reintent) args
7320 ee_descriptor_id = config_primitive.get(
7321 "execution-environment-ref"
7322 )
7323 primitive_name = config_primitive.get(
7324 "execution-environment-primitive", vnf_config_primitive
7325 )
7326 ee_id, vca_type = self._look_for_deployed_vca(
7327 nsr_deployed["VCA"],
7328 member_vnf_index=vnf_index,
7329 vdu_id=None,
7330 vdu_count_index=None,
7331 ee_descriptor_id=ee_descriptor_id,
7332 )
7333 result, result_detail = await self._ns_execute_primitive(
7334 ee_id,
7335 primitive_name,
7336 primitive_params,
7337 vca_type=vca_type,
7338 vca_id=vca_id,
7339 )
7340 self.logger.debug(
7341 logging_text
7342 + "vnf_config_primitive={} Done with result {} {}".format(
7343 vnf_config_primitive, result, result_detail
7344 )
7345 )
7346 # Update operationState = COMPLETED | FAILED
7347 self._update_suboperation_status(
7348 db_nslcmop, op_index, result, result_detail
7349 )
7350
7351 if result == "FAILED":
7352 raise LcmException(result_detail)
7353 db_nsr_update["config-status"] = old_config_status
7354 scale_process = None
7355 # POST-SCALE END
7356
7357 db_nsr_update[
7358 "detailed-status"
7359 ] = "" # "scaled {} {}".format(scaling_group, scaling_type)
7360 db_nsr_update["operational-status"] = (
7361 "running"
7362 if old_operational_status == "failed"
7363 else old_operational_status
7364 )
7365 db_nsr_update["config-status"] = old_config_status
7366 return
7367 except (
7368 ROclient.ROClientException,
7369 DbException,
7370 LcmException,
7371 NgRoException,
7372 ) as e:
7373 self.logger.error(logging_text + "Exit Exception {}".format(e))
7374 exc = e
7375 except asyncio.CancelledError:
7376 self.logger.error(
7377 logging_text + "Cancelled Exception while '{}'".format(step)
7378 )
7379 exc = "Operation was cancelled"
7380 except Exception as e:
7381 exc = traceback.format_exc()
7382 self.logger.critical(
7383 logging_text + "Exit Exception {} {}".format(type(e).__name__, e),
7384 exc_info=True,
7385 )
7386 finally:
7387 self._write_ns_status(
7388 nsr_id=nsr_id,
7389 ns_state=None,
7390 current_operation="IDLE",
7391 current_operation_id=None,
7392 )
7393 if tasks_dict_info:
7394 stage[1] = "Waiting for instantiate pending tasks."
7395 self.logger.debug(logging_text + stage[1])
7396 exc = await self._wait_for_tasks(
7397 logging_text,
7398 tasks_dict_info,
7399 self.timeout.ns_deploy,
7400 stage,
7401 nslcmop_id,
7402 nsr_id=nsr_id,
7403 )
7404 if exc:
7405 db_nslcmop_update[
7406 "detailed-status"
7407 ] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
7408 nslcmop_operation_state = "FAILED"
7409 if db_nsr:
7410 db_nsr_update["operational-status"] = old_operational_status
7411 db_nsr_update["config-status"] = old_config_status
7412 db_nsr_update["detailed-status"] = ""
7413 if scale_process:
7414 if "VCA" in scale_process:
7415 db_nsr_update["config-status"] = "failed"
7416 if "RO" in scale_process:
7417 db_nsr_update["operational-status"] = "failed"
7418 db_nsr_update[
7419 "detailed-status"
7420 ] = "FAILED scaling nslcmop={} {}: {}".format(
7421 nslcmop_id, step, exc
7422 )
7423 else:
7424 error_description_nslcmop = None
7425 nslcmop_operation_state = "COMPLETED"
7426 db_nslcmop_update["detailed-status"] = "Done"
7427
7428 self._write_op_status(
7429 op_id=nslcmop_id,
7430 stage="",
7431 error_message=error_description_nslcmop,
7432 operation_state=nslcmop_operation_state,
7433 other_update=db_nslcmop_update,
7434 )
7435 if db_nsr:
7436 self._write_ns_status(
7437 nsr_id=nsr_id,
7438 ns_state=None,
7439 current_operation="IDLE",
7440 current_operation_id=None,
7441 other_update=db_nsr_update,
7442 )
7443
7444 if nslcmop_operation_state:
7445 try:
7446 msg = {
7447 "nsr_id": nsr_id,
7448 "nslcmop_id": nslcmop_id,
7449 "operationState": nslcmop_operation_state,
7450 }
7451 await self.msg.aiowrite("ns", "scaled", msg)
7452 except Exception as e:
7453 self.logger.error(
7454 logging_text + "kafka_write notification Exception {}".format(e)
7455 )
7456 self.logger.debug(logging_text + "Exit")
7457 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
7458
7459 async def _scale_kdu(
7460 self, logging_text, nsr_id, nsr_deployed, db_vnfd, vca_id, scaling_info
7461 ):
7462 _scaling_info = scaling_info.get("kdu-create") or scaling_info.get("kdu-delete")
7463 for kdu_name in _scaling_info:
7464 for kdu_scaling_info in _scaling_info[kdu_name]:
7465 deployed_kdu, index = get_deployed_kdu(
7466 nsr_deployed, kdu_name, kdu_scaling_info["member-vnf-index"]
7467 )
7468 cluster_uuid = deployed_kdu["k8scluster-uuid"]
7469 kdu_instance = deployed_kdu["kdu-instance"]
7470 kdu_model = deployed_kdu.get("kdu-model")
7471 scale = int(kdu_scaling_info["scale"])
7472 k8s_cluster_type = kdu_scaling_info["k8s-cluster-type"]
7473
7474 db_dict = {
7475 "collection": "nsrs",
7476 "filter": {"_id": nsr_id},
7477 "path": "_admin.deployed.K8s.{}".format(index),
7478 }
7479
7480 step = "scaling application {}".format(
7481 kdu_scaling_info["resource-name"]
7482 )
7483 self.logger.debug(logging_text + step)
7484
7485 if kdu_scaling_info["type"] == "delete":
7486 kdu_config = get_configuration(db_vnfd, kdu_name)
7487 if (
7488 kdu_config
7489 and kdu_config.get("terminate-config-primitive")
7490 and get_juju_ee_ref(db_vnfd, kdu_name) is None
7491 ):
7492 terminate_config_primitive_list = kdu_config.get(
7493 "terminate-config-primitive"
7494 )
7495 terminate_config_primitive_list.sort(
7496 key=lambda val: int(val["seq"])
7497 )
7498
7499 for (
7500 terminate_config_primitive
7501 ) in terminate_config_primitive_list:
7502 primitive_params_ = self._map_primitive_params(
7503 terminate_config_primitive, {}, {}
7504 )
7505 step = "execute terminate config primitive"
7506 self.logger.debug(logging_text + step)
7507 await asyncio.wait_for(
7508 self.k8scluster_map[k8s_cluster_type].exec_primitive(
7509 cluster_uuid=cluster_uuid,
7510 kdu_instance=kdu_instance,
7511 primitive_name=terminate_config_primitive["name"],
7512 params=primitive_params_,
7513 db_dict=db_dict,
7514 total_timeout=self.timeout.primitive,
7515 vca_id=vca_id,
7516 ),
7517 timeout=self.timeout.primitive
7518 * self.timeout.primitive_outer_factor,
7519 )
7520
7521 await asyncio.wait_for(
7522 self.k8scluster_map[k8s_cluster_type].scale(
7523 kdu_instance=kdu_instance,
7524 scale=scale,
7525 resource_name=kdu_scaling_info["resource-name"],
7526 total_timeout=self.timeout.scale_on_error,
7527 vca_id=vca_id,
7528 cluster_uuid=cluster_uuid,
7529 kdu_model=kdu_model,
7530 atomic=True,
7531 db_dict=db_dict,
7532 ),
7533 timeout=self.timeout.scale_on_error
7534 * self.timeout.scale_on_error_outer_factor,
7535 )
7536
7537 if kdu_scaling_info["type"] == "create":
7538 kdu_config = get_configuration(db_vnfd, kdu_name)
7539 if (
7540 kdu_config
7541 and kdu_config.get("initial-config-primitive")
7542 and get_juju_ee_ref(db_vnfd, kdu_name) is None
7543 ):
7544 initial_config_primitive_list = kdu_config.get(
7545 "initial-config-primitive"
7546 )
7547 initial_config_primitive_list.sort(
7548 key=lambda val: int(val["seq"])
7549 )
7550
7551 for initial_config_primitive in initial_config_primitive_list:
7552 primitive_params_ = self._map_primitive_params(
7553 initial_config_primitive, {}, {}
7554 )
7555 step = "execute initial config primitive"
7556 self.logger.debug(logging_text + step)
7557 await asyncio.wait_for(
7558 self.k8scluster_map[k8s_cluster_type].exec_primitive(
7559 cluster_uuid=cluster_uuid,
7560 kdu_instance=kdu_instance,
7561 primitive_name=initial_config_primitive["name"],
7562 params=primitive_params_,
7563 db_dict=db_dict,
7564 vca_id=vca_id,
7565 ),
7566 timeout=600,
7567 )
7568
7569 async def _scale_ng_ro(
7570 self, logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage
7571 ):
7572 nsr_id = db_nslcmop["nsInstanceId"]
7573 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
7574 db_vnfrs = {}
7575
7576 # read from db: vnfd's for every vnf
7577 db_vnfds = []
7578
7579 # for each vnf in ns, read vnfd
7580 for vnfr in self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id}):
7581 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
7582 vnfd_id = vnfr["vnfd-id"] # vnfd uuid for this vnf
7583 # if we haven't this vnfd, read it from db
7584 if not find_in_list(db_vnfds, lambda a_vnfd: a_vnfd["id"] == vnfd_id):
7585 # read from db
7586 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
7587 db_vnfds.append(vnfd)
7588 n2vc_key = self.n2vc.get_public_key()
7589 n2vc_key_list = [n2vc_key]
7590 self.scale_vnfr(
7591 db_vnfr,
7592 vdu_scaling_info.get("vdu-create"),
7593 vdu_scaling_info.get("vdu-delete"),
7594 mark_delete=True,
7595 )
7596 # db_vnfr has been updated, update db_vnfrs to use it
7597 db_vnfrs[db_vnfr["member-vnf-index-ref"]] = db_vnfr
7598 await self._instantiate_ng_ro(
7599 logging_text,
7600 nsr_id,
7601 db_nsd,
7602 db_nsr,
7603 db_nslcmop,
7604 db_vnfrs,
7605 db_vnfds,
7606 n2vc_key_list,
7607 stage=stage,
7608 start_deploy=time(),
7609 timeout_ns_deploy=self.timeout.ns_deploy,
7610 )
7611 if vdu_scaling_info.get("vdu-delete"):
7612 self.scale_vnfr(
7613 db_vnfr, None, vdu_scaling_info["vdu-delete"], mark_delete=False
7614 )
7615
7616 async def extract_prometheus_scrape_jobs(
7617 self,
7618 ee_id: str,
7619 artifact_path: str,
7620 ee_config_descriptor: dict,
7621 vnfr_id: str,
7622 nsr_id: str,
7623 target_ip: str,
7624 element_type: str,
7625 vnf_member_index: str = "",
7626 vdu_id: str = "",
7627 vdu_index: int = None,
7628 kdu_name: str = "",
7629 kdu_index: int = None,
7630 ) -> dict:
7631 """Method to extract prometheus scrape jobs from EE's Prometheus template job file
7632 This method will wait until the corresponding VDU or KDU is fully instantiated
7633
7634 Args:
7635 ee_id (str): Execution Environment ID
7636 artifact_path (str): Path where the EE's content is (including the Prometheus template file)
7637 ee_config_descriptor (dict): Execution Environment's configuration descriptor
7638 vnfr_id (str): VNFR ID where this EE applies
7639 nsr_id (str): NSR ID where this EE applies
7640 target_ip (str): VDU/KDU instance IP address
7641 element_type (str): NS or VNF or VDU or KDU
7642 vnf_member_index (str, optional): VNF index where this EE applies. Defaults to "".
7643 vdu_id (str, optional): VDU ID where this EE applies. Defaults to "".
7644 vdu_index (int, optional): VDU index where this EE applies. Defaults to None.
7645 kdu_name (str, optional): KDU name where this EE applies. Defaults to "".
7646 kdu_index (int, optional): KDU index where this EE applies. Defaults to None.
7647
7648 Raises:
7649 LcmException: When the VDU or KDU instance was not found in an hour
7650
7651 Returns:
7652 _type_: Prometheus jobs
7653 """
7654 # default the vdur and kdur names to an empty string, to avoid any later
7655 # problem with Prometheus when the element type is not VDU or KDU
7656 vdur_name = ""
7657 kdur_name = ""
7658
7659 # look if exist a file called 'prometheus*.j2' and
7660 artifact_content = self.fs.dir_ls(artifact_path)
7661 job_file = next(
7662 (
7663 f
7664 for f in artifact_content
7665 if f.startswith("prometheus") and f.endswith(".j2")
7666 ),
7667 None,
7668 )
7669 if not job_file:
7670 return
7671 self.logger.debug("Artifact path{}".format(artifact_path))
7672 self.logger.debug("job file{}".format(job_file))
7673 with self.fs.file_open((artifact_path, job_file), "r") as f:
7674 job_data = f.read()
7675
7676 # obtain the VDUR or KDUR, if the element type is VDU or KDU
7677 if element_type in ("VDU", "KDU"):
7678 for _ in range(360):
7679 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
7680 if vdu_id and vdu_index is not None:
7681 vdur = next(
7682 (
7683 x
7684 for x in get_iterable(db_vnfr, "vdur")
7685 if (
7686 x.get("vdu-id-ref") == vdu_id
7687 and x.get("count-index") == vdu_index
7688 )
7689 ),
7690 {},
7691 )
7692 if vdur.get("name"):
7693 vdur_name = vdur.get("name")
7694 break
7695 if kdu_name and kdu_index is not None:
7696 kdur = next(
7697 (
7698 x
7699 for x in get_iterable(db_vnfr, "kdur")
7700 if (
7701 x.get("kdu-name") == kdu_name
7702 and x.get("count-index") == kdu_index
7703 )
7704 ),
7705 {},
7706 )
7707 if kdur.get("name"):
7708 kdur_name = kdur.get("name")
7709 break
7710
7711 await asyncio.sleep(10)
7712 else:
7713 if vdu_id and vdu_index is not None:
7714 raise LcmException(
7715 f"Timeout waiting VDU with name={vdu_id} and index={vdu_index} to be intantiated"
7716 )
7717 if kdu_name and kdu_index is not None:
7718 raise LcmException(
7719 f"Timeout waiting KDU with name={kdu_name} and index={kdu_index} to be intantiated"
7720 )
7721
7722 if ee_id is not None:
7723 _, namespace, helm_id = get_ee_id_parts(
7724 ee_id
7725 ) # get namespace and EE gRPC service name
7726 host_name = f'{helm_id}-{ee_config_descriptor["metric-service"]}.{namespace}.svc' # svc_name.namespace.svc
7727 host_port = "80"
7728 vnfr_id = vnfr_id.replace("-", "")
7729 variables = {
7730 "JOB_NAME": vnfr_id,
7731 "TARGET_IP": target_ip,
7732 "EXPORTER_POD_IP": host_name,
7733 "EXPORTER_POD_PORT": host_port,
7734 "NSR_ID": nsr_id,
7735 "VNF_MEMBER_INDEX": vnf_member_index,
7736 "VDUR_NAME": vdur_name,
7737 "KDUR_NAME": kdur_name,
7738 "ELEMENT_TYPE": element_type,
7739 }
7740 else:
7741 metric_path = ee_config_descriptor["metric-path"]
7742 target_port = ee_config_descriptor["metric-port"]
7743 vnfr_id = vnfr_id.replace("-", "")
7744 variables = {
7745 "JOB_NAME": vnfr_id,
7746 "TARGET_IP": target_ip,
7747 "TARGET_PORT": target_port,
7748 "METRIC_PATH": metric_path,
7749 }
7750
7751 job_list = parse_job(job_data, variables)
7752 # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
7753 for job in job_list:
7754 if (
7755 not isinstance(job.get("job_name"), str)
7756 or vnfr_id not in job["job_name"]
7757 ):
7758 job["job_name"] = vnfr_id + "_" + str(SystemRandom().randint(1, 10000))
7759 job["nsr_id"] = nsr_id
7760 job["vnfr_id"] = vnfr_id
7761 return job_list
7762
7763 async def rebuild_start_stop(
7764 self, nsr_id, nslcmop_id, vnf_id, additional_param, operation_type
7765 ):
7766 logging_text = "Task ns={} {}={} ".format(nsr_id, operation_type, nslcmop_id)
7767 self.logger.info(logging_text + "Enter")
7768 stage = ["Preparing the environment", ""]
7769 # database nsrs record
7770 db_nsr_update = {}
7771 vdu_vim_name = None
7772 vim_vm_id = None
7773 # in case of error, indicates what part of scale was failed to put nsr at error status
7774 start_deploy = time()
7775 try:
7776 db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_id})
7777 vim_account_id = db_vnfr.get("vim-account-id")
7778 vim_info_key = "vim:" + vim_account_id
7779 vdu_id = additional_param["vdu_id"]
7780 vdurs = [item for item in db_vnfr["vdur"] if item["vdu-id-ref"] == vdu_id]
7781 vdur = find_in_list(
7782 vdurs, lambda vdu: vdu["count-index"] == additional_param["count-index"]
7783 )
7784 if vdur:
7785 vdu_vim_name = vdur["name"]
7786 vim_vm_id = vdur["vim_info"][vim_info_key]["vim_id"]
7787 target_vim, _ = next(k_v for k_v in vdur["vim_info"].items())
7788 else:
7789 raise LcmException("Target vdu is not found")
7790 self.logger.info("vdu_vim_name >> {} ".format(vdu_vim_name))
7791 # wait for any previous tasks in process
7792 stage[1] = "Waiting for previous operations to terminate"
7793 self.logger.info(stage[1])
7794 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
7795
7796 stage[1] = "Reading from database."
7797 self.logger.info(stage[1])
7798 self._write_ns_status(
7799 nsr_id=nsr_id,
7800 ns_state=None,
7801 current_operation=operation_type.upper(),
7802 current_operation_id=nslcmop_id,
7803 )
7804 self._write_op_status(op_id=nslcmop_id, stage=stage, queuePosition=0)
7805
7806 # read from db: ns
7807 stage[1] = "Getting nsr={} from db.".format(nsr_id)
7808 db_nsr_update["operational-status"] = operation_type
7809 self.update_db_2("nsrs", nsr_id, db_nsr_update)
7810 # Payload for RO
7811 desc = {
7812 operation_type: {
7813 "vim_vm_id": vim_vm_id,
7814 "vnf_id": vnf_id,
7815 "vdu_index": additional_param["count-index"],
7816 "vdu_id": vdur["id"],
7817 "target_vim": target_vim,
7818 "vim_account_id": vim_account_id,
7819 }
7820 }
7821 stage[1] = "Sending rebuild request to RO... {}".format(desc)
7822 self._write_op_status(op_id=nslcmop_id, stage=stage, queuePosition=0)
7823 self.logger.info("ro nsr id: {}".format(nsr_id))
7824 result_dict = await self.RO.operate(nsr_id, desc, operation_type)
7825 self.logger.info("response from RO: {}".format(result_dict))
7826 action_id = result_dict["action_id"]
7827 await self._wait_ng_ro(
7828 nsr_id,
7829 action_id,
7830 nslcmop_id,
7831 start_deploy,
7832 self.timeout.operate,
7833 None,
7834 "start_stop_rebuild",
7835 )
7836 return "COMPLETED", "Done"
7837 except (ROclient.ROClientException, DbException, LcmException) as e:
7838 self.logger.error("Exit Exception {}".format(e))
7839 exc = e
7840 except asyncio.CancelledError:
7841 self.logger.error("Cancelled Exception while '{}'".format(stage))
7842 exc = "Operation was cancelled"
7843 except Exception as e:
7844 exc = traceback.format_exc()
7845 self.logger.critical(
7846 "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True
7847 )
7848 return "FAILED", "Error in operate VNF {}".format(exc)
7849
7850 def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
7851 """
7852 Get VCA Cloud and VCA Cloud Credentials for the VIM account
7853
7854 :param: vim_account_id: VIM Account ID
7855
7856 :return: (cloud_name, cloud_credential)
7857 """
7858 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
7859 return config.get("vca_cloud"), config.get("vca_cloud_credential")
7860
7861 def get_vca_k8s_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
7862 """
7863 Get VCA K8s Cloud and VCA K8s Cloud Credentials for the VIM account
7864
7865 :param: vim_account_id: VIM Account ID
7866
7867 :return: (cloud_name, cloud_credential)
7868 """
7869 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
7870 return config.get("vca_k8s_cloud"), config.get("vca_k8s_cloud_credential")
7871
7872 async def migrate(self, nsr_id, nslcmop_id):
7873 """
7874 Migrate VNFs and VDUs instances in a NS
7875
7876 :param: nsr_id: NS Instance ID
7877 :param: nslcmop_id: nslcmop ID of migrate
7878
7879 """
7880 # Try to lock HA task here
7881 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
7882 if not task_is_locked_by_me:
7883 return
7884 logging_text = "Task ns={} migrate ".format(nsr_id)
7885 self.logger.debug(logging_text + "Enter")
7886 # get all needed from database
7887 db_nslcmop = None
7888 db_nslcmop_update = {}
7889 nslcmop_operation_state = None
7890 db_nsr_update = {}
7891 target = {}
7892 exc = None
7893 # in case of error, indicates what part of scale was failed to put nsr at error status
7894 start_deploy = time()
7895
7896 try:
7897 # wait for any previous tasks in process
7898 step = "Waiting for previous operations to terminate"
7899 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
7900
7901 self._write_ns_status(
7902 nsr_id=nsr_id,
7903 ns_state=None,
7904 current_operation="MIGRATING",
7905 current_operation_id=nslcmop_id,
7906 )
7907 step = "Getting nslcmop from database"
7908 self.logger.debug(
7909 step + " after having waited for previous tasks to be completed"
7910 )
7911 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
7912 migrate_params = db_nslcmop.get("operationParams")
7913
7914 target = {}
7915 target.update(migrate_params)
7916 desc = await self.RO.migrate(nsr_id, target)
7917 self.logger.debug("RO return > {}".format(desc))
7918 action_id = desc["action_id"]
7919 await self._wait_ng_ro(
7920 nsr_id,
7921 action_id,
7922 nslcmop_id,
7923 start_deploy,
7924 self.timeout.migrate,
7925 operation="migrate",
7926 )
7927 except (ROclient.ROClientException, DbException, LcmException) as e:
7928 self.logger.error("Exit Exception {}".format(e))
7929 exc = e
7930 except asyncio.CancelledError:
7931 self.logger.error("Cancelled Exception while '{}'".format(step))
7932 exc = "Operation was cancelled"
7933 except Exception as e:
7934 exc = traceback.format_exc()
7935 self.logger.critical(
7936 "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True
7937 )
7938 finally:
7939 self._write_ns_status(
7940 nsr_id=nsr_id,
7941 ns_state=None,
7942 current_operation="IDLE",
7943 current_operation_id=None,
7944 )
7945 if exc:
7946 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
7947 nslcmop_operation_state = "FAILED"
7948 else:
7949 nslcmop_operation_state = "COMPLETED"
7950 db_nslcmop_update["detailed-status"] = "Done"
7951 db_nsr_update["detailed-status"] = "Done"
7952
7953 self._write_op_status(
7954 op_id=nslcmop_id,
7955 stage="",
7956 error_message="",
7957 operation_state=nslcmop_operation_state,
7958 other_update=db_nslcmop_update,
7959 )
7960 if nslcmop_operation_state:
7961 try:
7962 msg = {
7963 "nsr_id": nsr_id,
7964 "nslcmop_id": nslcmop_id,
7965 "operationState": nslcmop_operation_state,
7966 }
7967 await self.msg.aiowrite("ns", "migrated", msg)
7968 except Exception as e:
7969 self.logger.error(
7970 logging_text + "kafka_write notification Exception {}".format(e)
7971 )
7972 self.logger.debug(logging_text + "Exit")
7973 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_migrate")
7974
7975 async def heal(self, nsr_id, nslcmop_id):
7976 """
7977 Heal NS
7978
7979 :param nsr_id: ns instance to heal
7980 :param nslcmop_id: operation to run
7981 :return:
7982 """
7983
7984 # Try to lock HA task here
7985 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
7986 if not task_is_locked_by_me:
7987 return
7988
7989 logging_text = "Task ns={} heal={} ".format(nsr_id, nslcmop_id)
7990 stage = ["", "", ""]
7991 tasks_dict_info = {}
7992 # ^ stage, step, VIM progress
7993 self.logger.debug(logging_text + "Enter")
7994 # get all needed from database
7995 db_nsr = None
7996 db_nslcmop_update = {}
7997 db_nsr_update = {}
7998 db_vnfrs = {} # vnf's info indexed by _id
7999 exc = None
8000 old_operational_status = ""
8001 old_config_status = ""
8002 nsi_id = None
8003 try:
8004 # wait for any previous tasks in process
8005 step = "Waiting for previous operations to terminate"
8006 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
8007 self._write_ns_status(
8008 nsr_id=nsr_id,
8009 ns_state=None,
8010 current_operation="HEALING",
8011 current_operation_id=nslcmop_id,
8012 )
8013
8014 step = "Getting nslcmop from database"
8015 self.logger.debug(
8016 step + " after having waited for previous tasks to be completed"
8017 )
8018 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
8019
8020 step = "Getting nsr from database"
8021 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
8022 old_operational_status = db_nsr["operational-status"]
8023 old_config_status = db_nsr["config-status"]
8024
8025 db_nsr_update = {
8026 "_admin.deployed.RO.operational-status": "healing",
8027 }
8028 self.update_db_2("nsrs", nsr_id, db_nsr_update)
8029
8030 step = "Sending heal order to VIM"
8031 await self.heal_RO(
8032 logging_text=logging_text,
8033 nsr_id=nsr_id,
8034 db_nslcmop=db_nslcmop,
8035 stage=stage,
8036 )
8037 # VCA tasks
8038 # read from db: nsd
8039 stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
8040 self.logger.debug(logging_text + stage[1])
8041 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
8042 self.fs.sync(db_nsr["nsd-id"])
8043 db_nsr["nsd"] = nsd
8044 # read from db: vnfr's of this ns
8045 step = "Getting vnfrs from db"
8046 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
8047 for vnfr in db_vnfrs_list:
8048 db_vnfrs[vnfr["_id"]] = vnfr
8049 self.logger.debug("ns.heal db_vnfrs={}".format(db_vnfrs))
8050
8051 # Check for each target VNF
8052 target_list = db_nslcmop.get("operationParams", {}).get("healVnfData", {})
8053 for target_vnf in target_list:
8054 # Find this VNF in the list from DB
8055 vnfr_id = target_vnf.get("vnfInstanceId", None)
8056 if vnfr_id:
8057 db_vnfr = db_vnfrs[vnfr_id]
8058 vnfd_id = db_vnfr.get("vnfd-id")
8059 vnfd_ref = db_vnfr.get("vnfd-ref")
8060 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
8061 base_folder = vnfd["_admin"]["storage"]
8062 vdu_id = None
8063 vdu_index = 0
8064 vdu_name = None
8065 kdu_name = None
8066 nsi_id = None # TODO put nsi_id when this nsr belongs to a NSI
8067 member_vnf_index = db_vnfr.get("member-vnf-index-ref")
8068
8069 # Check each target VDU and deploy N2VC
8070 target_vdu_list = target_vnf.get("additionalParams", {}).get(
8071 "vdu", []
8072 )
8073 if not target_vdu_list:
8074 # Codigo nuevo para crear diccionario
8075 target_vdu_list = []
8076 for existing_vdu in db_vnfr.get("vdur"):
8077 vdu_name = existing_vdu.get("vdu-name", None)
8078 vdu_index = existing_vdu.get("count-index", 0)
8079 vdu_run_day1 = target_vnf.get("additionalParams", {}).get(
8080 "run-day1", False
8081 )
8082 vdu_to_be_healed = {
8083 "vdu-id": vdu_name,
8084 "count-index": vdu_index,
8085 "run-day1": vdu_run_day1,
8086 }
8087 target_vdu_list.append(vdu_to_be_healed)
8088 for target_vdu in target_vdu_list:
8089 deploy_params_vdu = target_vdu
8090 # Set run-day1 vnf level value if not vdu level value exists
8091 if not deploy_params_vdu.get("run-day1") and target_vnf.get(
8092 "additionalParams", {}
8093 ).get("run-day1"):
8094 deploy_params_vdu["run-day1"] = target_vnf[
8095 "additionalParams"
8096 ].get("run-day1")
8097 vdu_name = target_vdu.get("vdu-id", None)
8098 # TODO: Get vdu_id from vdud.
8099 vdu_id = vdu_name
8100 # For multi instance VDU count-index is mandatory
8101 # For single session VDU count-indes is 0
8102 vdu_index = target_vdu.get("count-index", 0)
8103
8104 # n2vc_redesign STEP 3 to 6 Deploy N2VC
8105 stage[1] = "Deploying Execution Environments."
8106 self.logger.debug(logging_text + stage[1])
8107
8108 # VNF Level charm. Normal case when proxy charms.
8109 # If target instance is management machine continue with actions: recreate EE for native charms or reinject juju key for proxy charms.
8110 descriptor_config = get_configuration(vnfd, vnfd_ref)
8111 if descriptor_config:
8112 # Continue if healed machine is management machine
8113 vnf_ip_address = db_vnfr.get("ip-address")
8114 target_instance = None
8115 for instance in db_vnfr.get("vdur", None):
8116 if (
8117 instance["vdu-name"] == vdu_name
8118 and instance["count-index"] == vdu_index
8119 ):
8120 target_instance = instance
8121 break
8122 if vnf_ip_address == target_instance.get("ip-address"):
8123 self._heal_n2vc(
8124 logging_text=logging_text
8125 + "member_vnf_index={}, vdu_name={}, vdu_index={} ".format(
8126 member_vnf_index, vdu_name, vdu_index
8127 ),
8128 db_nsr=db_nsr,
8129 db_vnfr=db_vnfr,
8130 nslcmop_id=nslcmop_id,
8131 nsr_id=nsr_id,
8132 nsi_id=nsi_id,
8133 vnfd_id=vnfd_ref,
8134 vdu_id=None,
8135 kdu_name=None,
8136 member_vnf_index=member_vnf_index,
8137 vdu_index=0,
8138 vdu_name=None,
8139 deploy_params=deploy_params_vdu,
8140 descriptor_config=descriptor_config,
8141 base_folder=base_folder,
8142 task_instantiation_info=tasks_dict_info,
8143 stage=stage,
8144 )
8145
8146 # VDU Level charm. Normal case with native charms.
8147 descriptor_config = get_configuration(vnfd, vdu_name)
8148 if descriptor_config:
8149 self._heal_n2vc(
8150 logging_text=logging_text
8151 + "member_vnf_index={}, vdu_name={}, vdu_index={} ".format(
8152 member_vnf_index, vdu_name, vdu_index
8153 ),
8154 db_nsr=db_nsr,
8155 db_vnfr=db_vnfr,
8156 nslcmop_id=nslcmop_id,
8157 nsr_id=nsr_id,
8158 nsi_id=nsi_id,
8159 vnfd_id=vnfd_ref,
8160 vdu_id=vdu_id,
8161 kdu_name=kdu_name,
8162 member_vnf_index=member_vnf_index,
8163 vdu_index=vdu_index,
8164 vdu_name=vdu_name,
8165 deploy_params=deploy_params_vdu,
8166 descriptor_config=descriptor_config,
8167 base_folder=base_folder,
8168 task_instantiation_info=tasks_dict_info,
8169 stage=stage,
8170 )
8171
8172 except (
8173 ROclient.ROClientException,
8174 DbException,
8175 LcmException,
8176 NgRoException,
8177 ) as e:
8178 self.logger.error(logging_text + "Exit Exception {}".format(e))
8179 exc = e
8180 except asyncio.CancelledError:
8181 self.logger.error(
8182 logging_text + "Cancelled Exception while '{}'".format(step)
8183 )
8184 exc = "Operation was cancelled"
8185 except Exception as e:
8186 exc = traceback.format_exc()
8187 self.logger.critical(
8188 logging_text + "Exit Exception {} {}".format(type(e).__name__, e),
8189 exc_info=True,
8190 )
8191 finally:
8192 if tasks_dict_info:
8193 stage[1] = "Waiting for healing pending tasks."
8194 self.logger.debug(logging_text + stage[1])
8195 exc = await self._wait_for_tasks(
8196 logging_text,
8197 tasks_dict_info,
8198 self.timeout.ns_deploy,
8199 stage,
8200 nslcmop_id,
8201 nsr_id=nsr_id,
8202 )
8203 if exc:
8204 db_nslcmop_update[
8205 "detailed-status"
8206 ] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
8207 nslcmop_operation_state = "FAILED"
8208 if db_nsr:
8209 db_nsr_update["operational-status"] = old_operational_status
8210 db_nsr_update["config-status"] = old_config_status
8211 db_nsr_update[
8212 "detailed-status"
8213 ] = "FAILED healing nslcmop={} {}: {}".format(nslcmop_id, step, exc)
8214 for task, task_name in tasks_dict_info.items():
8215 if not task.done() or task.cancelled() or task.exception():
8216 if task_name.startswith(self.task_name_deploy_vca):
8217 # A N2VC task is pending
8218 db_nsr_update["config-status"] = "failed"
8219 else:
8220 # RO task is pending
8221 db_nsr_update["operational-status"] = "failed"
8222 else:
8223 error_description_nslcmop = None
8224 nslcmop_operation_state = "COMPLETED"
8225 db_nslcmop_update["detailed-status"] = "Done"
8226 db_nsr_update["detailed-status"] = "Done"
8227 db_nsr_update["operational-status"] = "running"
8228 db_nsr_update["config-status"] = "configured"
8229
8230 self._write_op_status(
8231 op_id=nslcmop_id,
8232 stage="",
8233 error_message=error_description_nslcmop,
8234 operation_state=nslcmop_operation_state,
8235 other_update=db_nslcmop_update,
8236 )
8237 if db_nsr:
8238 self._write_ns_status(
8239 nsr_id=nsr_id,
8240 ns_state=None,
8241 current_operation="IDLE",
8242 current_operation_id=None,
8243 other_update=db_nsr_update,
8244 )
8245
8246 if nslcmop_operation_state:
8247 try:
8248 msg = {
8249 "nsr_id": nsr_id,
8250 "nslcmop_id": nslcmop_id,
8251 "operationState": nslcmop_operation_state,
8252 }
8253 await self.msg.aiowrite("ns", "healed", msg)
8254 except Exception as e:
8255 self.logger.error(
8256 logging_text + "kafka_write notification Exception {}".format(e)
8257 )
8258 self.logger.debug(logging_text + "Exit")
8259 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_heal")
8260
8261 async def heal_RO(
8262 self,
8263 logging_text,
8264 nsr_id,
8265 db_nslcmop,
8266 stage,
8267 ):
8268 """
8269 Heal at RO
8270 :param logging_text: preffix text to use at logging
8271 :param nsr_id: nsr identity
8272 :param db_nslcmop: database content of ns operation, in this case, 'instantiate'
8273 :param stage: list with 3 items: [general stage, tasks, vim_specific]. This task will write over vim_specific
8274 :return: None or exception
8275 """
8276
8277 def get_vim_account(vim_account_id):
8278 nonlocal db_vims
8279 if vim_account_id in db_vims:
8280 return db_vims[vim_account_id]
8281 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account_id})
8282 db_vims[vim_account_id] = db_vim
8283 return db_vim
8284
8285 try:
8286 start_heal = time()
8287 ns_params = db_nslcmop.get("operationParams")
8288 if ns_params and ns_params.get("timeout_ns_heal"):
8289 timeout_ns_heal = ns_params["timeout_ns_heal"]
8290 else:
8291 timeout_ns_heal = self.timeout.ns_heal
8292
8293 db_vims = {}
8294
8295 nslcmop_id = db_nslcmop["_id"]
8296 target = {
8297 "action_id": nslcmop_id,
8298 }
8299 self.logger.warning(
8300 "db_nslcmop={} and timeout_ns_heal={}".format(
8301 db_nslcmop, timeout_ns_heal
8302 )
8303 )
8304 target.update(db_nslcmop.get("operationParams", {}))
8305
8306 self.logger.debug("Send to RO > nsr_id={} target={}".format(nsr_id, target))
8307 desc = await self.RO.recreate(nsr_id, target)
8308 self.logger.debug("RO return > {}".format(desc))
8309 action_id = desc["action_id"]
8310 # waits for RO to complete because Reinjecting juju key at ro can find VM in state Deleted
8311 await self._wait_ng_ro(
8312 nsr_id,
8313 action_id,
8314 nslcmop_id,
8315 start_heal,
8316 timeout_ns_heal,
8317 stage,
8318 operation="healing",
8319 )
8320
8321 # Updating NSR
8322 db_nsr_update = {
8323 "_admin.deployed.RO.operational-status": "running",
8324 "detailed-status": " ".join(stage),
8325 }
8326 self.update_db_2("nsrs", nsr_id, db_nsr_update)
8327 self._write_op_status(nslcmop_id, stage)
8328 self.logger.debug(
8329 logging_text + "ns healed at RO. RO_id={}".format(action_id)
8330 )
8331
8332 except Exception as e:
8333 stage[2] = "ERROR healing at VIM"
8334 # self.set_vnfr_at_error(db_vnfrs, str(e))
8335 self.logger.error(
8336 "Error healing at VIM {}".format(e),
8337 exc_info=not isinstance(
8338 e,
8339 (
8340 ROclient.ROClientException,
8341 LcmException,
8342 DbException,
8343 NgRoException,
8344 ),
8345 ),
8346 )
8347 raise
8348
8349 def _heal_n2vc(
8350 self,
8351 logging_text,
8352 db_nsr,
8353 db_vnfr,
8354 nslcmop_id,
8355 nsr_id,
8356 nsi_id,
8357 vnfd_id,
8358 vdu_id,
8359 kdu_name,
8360 member_vnf_index,
8361 vdu_index,
8362 vdu_name,
8363 deploy_params,
8364 descriptor_config,
8365 base_folder,
8366 task_instantiation_info,
8367 stage,
8368 ):
8369 # launch instantiate_N2VC in a asyncio task and register task object
8370 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
8371 # if not found, create one entry and update database
8372 # fill db_nsr._admin.deployed.VCA.<index>
8373
8374 self.logger.debug(
8375 logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id)
8376 )
8377
8378 charm_name = ""
8379 get_charm_name = False
8380 if "execution-environment-list" in descriptor_config:
8381 ee_list = descriptor_config.get("execution-environment-list", [])
8382 elif "juju" in descriptor_config:
8383 ee_list = [descriptor_config] # ns charms
8384 if "execution-environment-list" not in descriptor_config:
8385 # charm name is only required for ns charms
8386 get_charm_name = True
8387 else: # other types as script are not supported
8388 ee_list = []
8389
8390 for ee_item in ee_list:
8391 self.logger.debug(
8392 logging_text
8393 + "_deploy_n2vc ee_item juju={}, helm={}".format(
8394 ee_item.get("juju"), ee_item.get("helm-chart")
8395 )
8396 )
8397 ee_descriptor_id = ee_item.get("id")
8398 if ee_item.get("juju"):
8399 vca_name = ee_item["juju"].get("charm")
8400 if get_charm_name:
8401 charm_name = self.find_charm_name(db_nsr, str(vca_name))
8402 vca_type = (
8403 "lxc_proxy_charm"
8404 if ee_item["juju"].get("charm") is not None
8405 else "native_charm"
8406 )
8407 if ee_item["juju"].get("cloud") == "k8s":
8408 vca_type = "k8s_proxy_charm"
8409 elif ee_item["juju"].get("proxy") is False:
8410 vca_type = "native_charm"
8411 elif ee_item.get("helm-chart"):
8412 vca_name = ee_item["helm-chart"]
8413 if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
8414 vca_type = "helm"
8415 else:
8416 vca_type = "helm-v3"
8417 else:
8418 self.logger.debug(
8419 logging_text + "skipping non juju neither charm configuration"
8420 )
8421 continue
8422
8423 vca_index = -1
8424 for vca_index, vca_deployed in enumerate(
8425 db_nsr["_admin"]["deployed"]["VCA"]
8426 ):
8427 if not vca_deployed:
8428 continue
8429 if (
8430 vca_deployed.get("member-vnf-index") == member_vnf_index
8431 and vca_deployed.get("vdu_id") == vdu_id
8432 and vca_deployed.get("kdu_name") == kdu_name
8433 and vca_deployed.get("vdu_count_index", 0) == vdu_index
8434 and vca_deployed.get("ee_descriptor_id") == ee_descriptor_id
8435 ):
8436 break
8437 else:
8438 # not found, create one.
8439 target = (
8440 "ns" if not member_vnf_index else "vnf/{}".format(member_vnf_index)
8441 )
8442 if vdu_id:
8443 target += "/vdu/{}/{}".format(vdu_id, vdu_index or 0)
8444 elif kdu_name:
8445 target += "/kdu/{}".format(kdu_name)
8446 vca_deployed = {
8447 "target_element": target,
8448 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
8449 "member-vnf-index": member_vnf_index,
8450 "vdu_id": vdu_id,
8451 "kdu_name": kdu_name,
8452 "vdu_count_index": vdu_index,
8453 "operational-status": "init", # TODO revise
8454 "detailed-status": "", # TODO revise
8455 "step": "initial-deploy", # TODO revise
8456 "vnfd_id": vnfd_id,
8457 "vdu_name": vdu_name,
8458 "type": vca_type,
8459 "ee_descriptor_id": ee_descriptor_id,
8460 "charm_name": charm_name,
8461 }
8462 vca_index += 1
8463
8464 # create VCA and configurationStatus in db
8465 db_dict = {
8466 "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
8467 "configurationStatus.{}".format(vca_index): dict(),
8468 }
8469 self.update_db_2("nsrs", nsr_id, db_dict)
8470
8471 db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
8472
8473 self.logger.debug("N2VC > NSR_ID > {}".format(nsr_id))
8474 self.logger.debug("N2VC > DB_NSR > {}".format(db_nsr))
8475 self.logger.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed))
8476
8477 # Launch task
8478 task_n2vc = asyncio.ensure_future(
8479 self.heal_N2VC(
8480 logging_text=logging_text,
8481 vca_index=vca_index,
8482 nsi_id=nsi_id,
8483 db_nsr=db_nsr,
8484 db_vnfr=db_vnfr,
8485 vdu_id=vdu_id,
8486 kdu_name=kdu_name,
8487 vdu_index=vdu_index,
8488 deploy_params=deploy_params,
8489 config_descriptor=descriptor_config,
8490 base_folder=base_folder,
8491 nslcmop_id=nslcmop_id,
8492 stage=stage,
8493 vca_type=vca_type,
8494 vca_name=vca_name,
8495 ee_config_descriptor=ee_item,
8496 )
8497 )
8498 self.lcm_tasks.register(
8499 "ns",
8500 nsr_id,
8501 nslcmop_id,
8502 "instantiate_N2VC-{}".format(vca_index),
8503 task_n2vc,
8504 )
8505 task_instantiation_info[
8506 task_n2vc
8507 ] = self.task_name_deploy_vca + " {}.{}".format(
8508 member_vnf_index or "", vdu_id or ""
8509 )
8510
8511 async def heal_N2VC(
8512 self,
8513 logging_text,
8514 vca_index,
8515 nsi_id,
8516 db_nsr,
8517 db_vnfr,
8518 vdu_id,
8519 kdu_name,
8520 vdu_index,
8521 config_descriptor,
8522 deploy_params,
8523 base_folder,
8524 nslcmop_id,
8525 stage,
8526 vca_type,
8527 vca_name,
8528 ee_config_descriptor,
8529 ):
8530 nsr_id = db_nsr["_id"]
8531 db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
8532 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
8533 vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
8534 osm_config = {"osm": {"ns_id": db_nsr["_id"]}}
8535 db_dict = {
8536 "collection": "nsrs",
8537 "filter": {"_id": nsr_id},
8538 "path": db_update_entry,
8539 }
8540 step = ""
8541 try:
8542 element_type = "NS"
8543 element_under_configuration = nsr_id
8544
8545 vnfr_id = None
8546 if db_vnfr:
8547 vnfr_id = db_vnfr["_id"]
8548 osm_config["osm"]["vnf_id"] = vnfr_id
8549
8550 namespace = "{nsi}.{ns}".format(nsi=nsi_id if nsi_id else "", ns=nsr_id)
8551
8552 if vca_type == "native_charm":
8553 index_number = 0
8554 else:
8555 index_number = vdu_index or 0
8556
8557 if vnfr_id:
8558 element_type = "VNF"
8559 element_under_configuration = vnfr_id
8560 namespace += ".{}-{}".format(vnfr_id, index_number)
8561 if vdu_id:
8562 namespace += ".{}-{}".format(vdu_id, index_number)
8563 element_type = "VDU"
8564 element_under_configuration = "{}-{}".format(vdu_id, index_number)
8565 osm_config["osm"]["vdu_id"] = vdu_id
8566 elif kdu_name:
8567 namespace += ".{}".format(kdu_name)
8568 element_type = "KDU"
8569 element_under_configuration = kdu_name
8570 osm_config["osm"]["kdu_name"] = kdu_name
8571
8572 # Get artifact path
8573 if base_folder["pkg-dir"]:
8574 artifact_path = "{}/{}/{}/{}".format(
8575 base_folder["folder"],
8576 base_folder["pkg-dir"],
8577 "charms"
8578 if vca_type
8579 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
8580 else "helm-charts",
8581 vca_name,
8582 )
8583 else:
8584 artifact_path = "{}/Scripts/{}/{}/".format(
8585 base_folder["folder"],
8586 "charms"
8587 if vca_type
8588 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
8589 else "helm-charts",
8590 vca_name,
8591 )
8592
8593 self.logger.debug("Artifact path > {}".format(artifact_path))
8594
8595 # get initial_config_primitive_list that applies to this element
8596 initial_config_primitive_list = config_descriptor.get(
8597 "initial-config-primitive"
8598 )
8599
8600 self.logger.debug(
8601 "Initial config primitive list > {}".format(
8602 initial_config_primitive_list
8603 )
8604 )
8605
8606 # add config if not present for NS charm
8607 ee_descriptor_id = ee_config_descriptor.get("id")
8608 self.logger.debug("EE Descriptor > {}".format(ee_descriptor_id))
8609 initial_config_primitive_list = get_ee_sorted_initial_config_primitive_list(
8610 initial_config_primitive_list, vca_deployed, ee_descriptor_id
8611 )
8612
8613 self.logger.debug(
8614 "Initial config primitive list #2 > {}".format(
8615 initial_config_primitive_list
8616 )
8617 )
8618 # n2vc_redesign STEP 3.1
8619 # find old ee_id if exists
8620 ee_id = vca_deployed.get("ee_id")
8621
8622 vca_id = self.get_vca_id(db_vnfr, db_nsr)
8623 # create or register execution environment in VCA. Only for native charms when healing
8624 if vca_type == "native_charm":
8625 step = "Waiting to VM being up and getting IP address"
8626 self.logger.debug(logging_text + step)
8627 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
8628 logging_text,
8629 nsr_id,
8630 vnfr_id,
8631 vdu_id,
8632 vdu_index,
8633 user=None,
8634 pub_key=None,
8635 )
8636 credentials = {"hostname": rw_mgmt_ip}
8637 # get username
8638 username = deep_get(
8639 config_descriptor, ("config-access", "ssh-access", "default-user")
8640 )
8641 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
8642 # merged. Meanwhile let's get username from initial-config-primitive
8643 if not username and initial_config_primitive_list:
8644 for config_primitive in initial_config_primitive_list:
8645 for param in config_primitive.get("parameter", ()):
8646 if param["name"] == "ssh-username":
8647 username = param["value"]
8648 break
8649 if not username:
8650 raise LcmException(
8651 "Cannot determine the username neither with 'initial-config-primitive' nor with "
8652 "'config-access.ssh-access.default-user'"
8653 )
8654 credentials["username"] = username
8655
8656 # n2vc_redesign STEP 3.2
8657 # TODO: Before healing at RO it is needed to destroy native charm units to be deleted.
8658 self._write_configuration_status(
8659 nsr_id=nsr_id,
8660 vca_index=vca_index,
8661 status="REGISTERING",
8662 element_under_configuration=element_under_configuration,
8663 element_type=element_type,
8664 )
8665
8666 step = "register execution environment {}".format(credentials)
8667 self.logger.debug(logging_text + step)
8668 ee_id = await self.vca_map[vca_type].register_execution_environment(
8669 credentials=credentials,
8670 namespace=namespace,
8671 db_dict=db_dict,
8672 vca_id=vca_id,
8673 )
8674
8675 # update ee_id en db
8676 db_dict_ee_id = {
8677 "_admin.deployed.VCA.{}.ee_id".format(vca_index): ee_id,
8678 }
8679 self.update_db_2("nsrs", nsr_id, db_dict_ee_id)
8680
8681 # for compatibility with MON/POL modules, the need model and application name at database
8682 # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
8683 # Not sure if this need to be done when healing
8684 """
8685 ee_id_parts = ee_id.split(".")
8686 db_nsr_update = {db_update_entry + "ee_id": ee_id}
8687 if len(ee_id_parts) >= 2:
8688 model_name = ee_id_parts[0]
8689 application_name = ee_id_parts[1]
8690 db_nsr_update[db_update_entry + "model"] = model_name
8691 db_nsr_update[db_update_entry + "application"] = application_name
8692 """
8693
8694 # n2vc_redesign STEP 3.3
8695 # Install configuration software. Only for native charms.
8696 step = "Install configuration Software"
8697
8698 self._write_configuration_status(
8699 nsr_id=nsr_id,
8700 vca_index=vca_index,
8701 status="INSTALLING SW",
8702 element_under_configuration=element_under_configuration,
8703 element_type=element_type,
8704 # other_update=db_nsr_update,
8705 other_update=None,
8706 )
8707
8708 # TODO check if already done
8709 self.logger.debug(logging_text + step)
8710 config = None
8711 if vca_type == "native_charm":
8712 config_primitive = next(
8713 (p for p in initial_config_primitive_list if p["name"] == "config"),
8714 None,
8715 )
8716 if config_primitive:
8717 config = self._map_primitive_params(
8718 config_primitive, {}, deploy_params
8719 )
8720 await self.vca_map[vca_type].install_configuration_sw(
8721 ee_id=ee_id,
8722 artifact_path=artifact_path,
8723 db_dict=db_dict,
8724 config=config,
8725 num_units=1,
8726 vca_id=vca_id,
8727 vca_type=vca_type,
8728 )
8729
8730 # write in db flag of configuration_sw already installed
8731 self.update_db_2(
8732 "nsrs", nsr_id, {db_update_entry + "config_sw_installed": True}
8733 )
8734
8735 # Not sure if this need to be done when healing
8736 """
8737 # add relations for this VCA (wait for other peers related with this VCA)
8738 await self._add_vca_relations(
8739 logging_text=logging_text,
8740 nsr_id=nsr_id,
8741 vca_type=vca_type,
8742 vca_index=vca_index,
8743 )
8744 """
8745
8746 # if SSH access is required, then get execution environment SSH public
8747 # if native charm we have waited already to VM be UP
8748 if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
8749 pub_key = None
8750 user = None
8751 # self.logger.debug("get ssh key block")
8752 if deep_get(
8753 config_descriptor, ("config-access", "ssh-access", "required")
8754 ):
8755 # self.logger.debug("ssh key needed")
8756 # Needed to inject a ssh key
8757 user = deep_get(
8758 config_descriptor,
8759 ("config-access", "ssh-access", "default-user"),
8760 )
8761 step = "Install configuration Software, getting public ssh key"
8762 pub_key = await self.vca_map[vca_type].get_ee_ssh_public__key(
8763 ee_id=ee_id, db_dict=db_dict, vca_id=vca_id
8764 )
8765
8766 step = "Insert public key into VM user={} ssh_key={}".format(
8767 user, pub_key
8768 )
8769 else:
8770 # self.logger.debug("no need to get ssh key")
8771 step = "Waiting to VM being up and getting IP address"
8772 self.logger.debug(logging_text + step)
8773
8774 # n2vc_redesign STEP 5.1
8775 # wait for RO (ip-address) Insert pub_key into VM
8776 # IMPORTANT: We need do wait for RO to complete healing operation.
8777 await self._wait_heal_ro(nsr_id, self.timeout.ns_heal)
8778 if vnfr_id:
8779 if kdu_name:
8780 rw_mgmt_ip = await self.wait_kdu_up(
8781 logging_text, nsr_id, vnfr_id, kdu_name
8782 )
8783 else:
8784 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(
8785 logging_text,
8786 nsr_id,
8787 vnfr_id,
8788 vdu_id,
8789 vdu_index,
8790 user=user,
8791 pub_key=pub_key,
8792 )
8793 else:
8794 rw_mgmt_ip = None # This is for a NS configuration
8795
8796 self.logger.debug(logging_text + " VM_ip_address={}".format(rw_mgmt_ip))
8797
8798 # store rw_mgmt_ip in deploy params for later replacement
8799 deploy_params["rw_mgmt_ip"] = rw_mgmt_ip
8800
8801 # Day1 operations.
8802 # get run-day1 operation parameter
8803 runDay1 = deploy_params.get("run-day1", False)
8804 self.logger.debug(
8805 "Healing vnf={}, vdu={}, runDay1 ={}".format(vnfr_id, vdu_id, runDay1)
8806 )
8807 if runDay1:
8808 # n2vc_redesign STEP 6 Execute initial config primitive
8809 step = "execute initial config primitive"
8810
8811 # wait for dependent primitives execution (NS -> VNF -> VDU)
8812 if initial_config_primitive_list:
8813 await self._wait_dependent_n2vc(
8814 nsr_id, vca_deployed_list, vca_index
8815 )
8816
8817 # stage, in function of element type: vdu, kdu, vnf or ns
8818 my_vca = vca_deployed_list[vca_index]
8819 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
8820 # VDU or KDU
8821 stage[0] = "Stage 3/5: running Day-1 primitives for VDU."
8822 elif my_vca.get("member-vnf-index"):
8823 # VNF
8824 stage[0] = "Stage 4/5: running Day-1 primitives for VNF."
8825 else:
8826 # NS
8827 stage[0] = "Stage 5/5: running Day-1 primitives for NS."
8828
8829 self._write_configuration_status(
8830 nsr_id=nsr_id, vca_index=vca_index, status="EXECUTING PRIMITIVE"
8831 )
8832
8833 self._write_op_status(op_id=nslcmop_id, stage=stage)
8834
8835 check_if_terminated_needed = True
8836 for initial_config_primitive in initial_config_primitive_list:
8837 # adding information on the vca_deployed if it is a NS execution environment
8838 if not vca_deployed["member-vnf-index"]:
8839 deploy_params["ns_config_info"] = json.dumps(
8840 self._get_ns_config_info(nsr_id)
8841 )
8842 # TODO check if already done
8843 primitive_params_ = self._map_primitive_params(
8844 initial_config_primitive, {}, deploy_params
8845 )
8846
8847 step = "execute primitive '{}' params '{}'".format(
8848 initial_config_primitive["name"], primitive_params_
8849 )
8850 self.logger.debug(logging_text + step)
8851 await self.vca_map[vca_type].exec_primitive(
8852 ee_id=ee_id,
8853 primitive_name=initial_config_primitive["name"],
8854 params_dict=primitive_params_,
8855 db_dict=db_dict,
8856 vca_id=vca_id,
8857 vca_type=vca_type,
8858 )
8859 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
8860 if check_if_terminated_needed:
8861 if config_descriptor.get("terminate-config-primitive"):
8862 self.update_db_2(
8863 "nsrs",
8864 nsr_id,
8865 {db_update_entry + "needed_terminate": True},
8866 )
8867 check_if_terminated_needed = False
8868
8869 # TODO register in database that primitive is done
8870
8871 # STEP 7 Configure metrics
8872 # Not sure if this need to be done when healing
8873 """
8874 if vca_type == "helm" or vca_type == "helm-v3":
8875 prometheus_jobs = await self.extract_prometheus_scrape_jobs(
8876 ee_id=ee_id,
8877 artifact_path=artifact_path,
8878 ee_config_descriptor=ee_config_descriptor,
8879 vnfr_id=vnfr_id,
8880 nsr_id=nsr_id,
8881 target_ip=rw_mgmt_ip,
8882 )
8883 if prometheus_jobs:
8884 self.update_db_2(
8885 "nsrs",
8886 nsr_id,
8887 {db_update_entry + "prometheus_jobs": prometheus_jobs},
8888 )
8889
8890 for job in prometheus_jobs:
8891 self.db.set_one(
8892 "prometheus_jobs",
8893 {"job_name": job["job_name"]},
8894 job,
8895 upsert=True,
8896 fail_on_empty=False,
8897 )
8898
8899 """
8900 step = "instantiated at VCA"
8901 self.logger.debug(logging_text + step)
8902
8903 self._write_configuration_status(
8904 nsr_id=nsr_id, vca_index=vca_index, status="READY"
8905 )
8906
8907 except Exception as e: # TODO not use Exception but N2VC exception
8908 # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
8909 if not isinstance(
8910 e, (DbException, N2VCException, LcmException, asyncio.CancelledError)
8911 ):
8912 self.logger.error(
8913 "Exception while {} : {}".format(step, e), exc_info=True
8914 )
8915 self._write_configuration_status(
8916 nsr_id=nsr_id, vca_index=vca_index, status="BROKEN"
8917 )
8918 raise LcmException("{} {}".format(step, e)) from e
8919
8920 async def _wait_heal_ro(
8921 self,
8922 nsr_id,
8923 timeout=600,
8924 ):
8925 start_time = time()
8926 while time() <= start_time + timeout:
8927 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
8928 operational_status_ro = db_nsr["_admin"]["deployed"]["RO"][
8929 "operational-status"
8930 ]
8931 self.logger.debug("Wait Heal RO > {}".format(operational_status_ro))
8932 if operational_status_ro != "healing":
8933 break
8934 await asyncio.sleep(15)
8935 else: # timeout_ns_deploy
8936 raise NgRoException("Timeout waiting ns to deploy")
8937
8938 async def vertical_scale(self, nsr_id, nslcmop_id):
8939 """
8940 Vertical Scale the VDUs in a NS
8941
8942 :param: nsr_id: NS Instance ID
8943 :param: nslcmop_id: nslcmop ID of migrate
8944
8945 """
8946 # Try to lock HA task here
8947 task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
8948 if not task_is_locked_by_me:
8949 return
8950 logging_text = "Task ns={} vertical scale ".format(nsr_id)
8951 self.logger.debug(logging_text + "Enter")
8952 # get all needed from database
8953 db_nslcmop = None
8954 db_nslcmop_update = {}
8955 nslcmop_operation_state = None
8956 db_nsr_update = {}
8957 target = {}
8958 exc = None
8959 # in case of error, indicates what part of scale was failed to put nsr at error status
8960 start_deploy = time()
8961
8962 try:
8963 # wait for any previous tasks in process
8964 step = "Waiting for previous operations to terminate"
8965 await self.lcm_tasks.waitfor_related_HA("ns", "nslcmops", nslcmop_id)
8966
8967 self._write_ns_status(
8968 nsr_id=nsr_id,
8969 ns_state=None,
8970 current_operation="VerticalScale",
8971 current_operation_id=nslcmop_id,
8972 )
8973 step = "Getting nslcmop from database"
8974 self.logger.debug(
8975 step + " after having waited for previous tasks to be completed"
8976 )
8977 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
8978 operationParams = db_nslcmop.get("operationParams")
8979 target = {}
8980 target.update(operationParams)
8981 desc = await self.RO.vertical_scale(nsr_id, target)
8982 self.logger.debug("RO return > {}".format(desc))
8983 action_id = desc["action_id"]
8984 await self._wait_ng_ro(
8985 nsr_id,
8986 action_id,
8987 nslcmop_id,
8988 start_deploy,
8989 self.timeout.verticalscale,
8990 operation="verticalscale",
8991 )
8992 except (ROclient.ROClientException, DbException, LcmException) as e:
8993 self.logger.error("Exit Exception {}".format(e))
8994 exc = e
8995 except asyncio.CancelledError:
8996 self.logger.error("Cancelled Exception while '{}'".format(step))
8997 exc = "Operation was cancelled"
8998 except Exception as e:
8999 exc = traceback.format_exc()
9000 self.logger.critical(
9001 "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True
9002 )
9003 finally:
9004 self._write_ns_status(
9005 nsr_id=nsr_id,
9006 ns_state=None,
9007 current_operation="IDLE",
9008 current_operation_id=None,
9009 )
9010 if exc:
9011 db_nslcmop_update["detailed-status"] = "FAILED {}: {}".format(step, exc)
9012 nslcmop_operation_state = "FAILED"
9013 else:
9014 nslcmop_operation_state = "COMPLETED"
9015 db_nslcmop_update["detailed-status"] = "Done"
9016 db_nsr_update["detailed-status"] = "Done"
9017
9018 self._write_op_status(
9019 op_id=nslcmop_id,
9020 stage="",
9021 error_message="",
9022 operation_state=nslcmop_operation_state,
9023 other_update=db_nslcmop_update,
9024 )
9025 if nslcmop_operation_state:
9026 try:
9027 msg = {
9028 "nsr_id": nsr_id,
9029 "nslcmop_id": nslcmop_id,
9030 "operationState": nslcmop_operation_state,
9031 }
9032 await self.msg.aiowrite("ns", "verticalscaled", msg)
9033 except Exception as e:
9034 self.logger.error(
9035 logging_text + "kafka_write notification Exception {}".format(e)
9036 )
9037 self.logger.debug(logging_text + "Exit")
9038 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_verticalscale")