1 # -*- coding: utf-8 -*-
4 # Copyright 2018 Telefonica S.A.
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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
21 from typing
import Any
, Dict
, List
24 import logging
.handlers
36 from osm_lcm
import ROclient
37 from osm_lcm
.data_utils
.lcm_config
import LcmCfg
38 from osm_lcm
.data_utils
.nsr
import (
41 get_deployed_vca_list
,
44 from osm_lcm
.data_utils
.vca
import (
53 from osm_lcm
.ng_ro
import NgRoClient
, NgRoException
54 from osm_lcm
.lcm_utils
import (
61 check_juju_bundle_existence
,
62 get_charm_artifact_path
,
66 from osm_lcm
.data_utils
.nsd
import (
67 get_ns_configuration_relation_list
,
71 from osm_lcm
.data_utils
.vnfd
import (
77 get_ee_sorted_initial_config_primitive_list
,
78 get_ee_sorted_terminate_config_primitive_list
,
80 get_virtual_link_profiles
,
85 get_number_of_instances
,
87 get_kdu_resource_profile
,
88 find_software_version
,
91 from osm_lcm
.data_utils
.list_utils
import find_in_list
92 from osm_lcm
.data_utils
.vnfr
import (
96 get_volumes_from_instantiation_params
,
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
105 from osm_common
.dbbase
import DbException
106 from osm_common
.fsbase
import FsException
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 (
112 get_target_wim_attrs
,
113 select_feasible_wim_account
,
116 from n2vc
.n2vc_juju_conn
import N2VCJujuConnector
117 from n2vc
.exceptions
import N2VCException
, N2VCNotFound
, K8sException
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
123 from copy
import copy
, deepcopy
124 from time
import time
125 from uuid
import uuid4
127 from random
import SystemRandom
129 __author__
= "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
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
= {
147 def __init__(self
, msg
, lcm_tasks
, config
: LcmCfg
):
149 Init, Connect to database, filesystem storage, and messaging
150 :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
153 super().__init
__(msg
=msg
, logger
=logging
.getLogger("lcm.ns"))
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
162 # create N2VC connector
163 self
.n2vc
= N2VCJujuConnector(
165 on_update_db
=self
._on
_update
_n
2vc
_db
,
170 self
.conn_helm_ee
= LCMHelmConn(
172 vca_config
=self
.vca_config
,
173 on_update_db
=self
._on
_update
_n
2vc
_db
,
176 self
.k8sclusterhelm2
= K8sHelmConnector(
177 kubectl_command
=self
.vca_config
.kubectlpath
,
178 helm_command
=self
.vca_config
.helmpath
,
185 self
.k8sclusterhelm3
= K8sHelm3Connector(
186 kubectl_command
=self
.vca_config
.kubectlpath
,
187 helm_command
=self
.vca_config
.helm3path
,
194 self
.k8sclusterjuju
= K8sJujuConnector(
195 kubectl_command
=self
.vca_config
.kubectlpath
,
196 juju_command
=self
.vca_config
.jujupath
,
198 on_update_db
=self
._on
_update
_k
8s
_db
,
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
,
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
,
220 self
.RO
= NgRoClient(**self
.ro_config
.to_dict())
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
,
232 def increment_ip_mac(ip_mac
, vm_index
=1):
233 if not isinstance(ip_mac
, str):
236 # try with ipv4 look for last dot
237 i
= ip_mac
.rfind(".")
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(":")
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
253 def _on_update_ro_db(self
, nsrs_id
, ro_descriptor
):
254 # self.logger.debug('_on_update_ro_db(nsrs_id={}'.format(nsrs_id))
257 # TODO filter RO descriptor fields...
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
)
265 except Exception as e
:
267 "Cannot write database RO deployment for ns={} -> {}".format(nsrs_id
, e
)
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("."):
275 # self.logger.debug('_on_update_n2vc_db(table={}, filter={}, path={}, updated_data={}'
276 # .format(table, filter, path, updated_data))
278 nsr_id
= filter.get("_id")
280 # read ns record from database
281 nsr
= self
.db
.get_one(table
="nsrs", q_filter
=filter)
282 current_ns_status
= nsr
.get("nsState")
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
291 db_dict
["vcaStatus"] = status_dict
293 # update configurationStatus for this VCA
295 vca_index
= int(path
[path
.rfind(".") + 1 :])
298 target_dict
=nsr
, key_list
=("_admin", "deployed", "VCA")
300 vca_status
= vca_list
[vca_index
].get("status")
302 configuration_status_list
= nsr
.get("configurationStatus")
303 config_status
= configuration_status_list
[vca_index
].get("status")
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
))
313 # if nsState = 'READY' check if juju is reporting some error => nsState = 'DEGRADED'
314 # if nsState = 'DEGRADED' check if all is OK
316 if current_ns_status
in ("READY", "DEGRADED"):
317 error_description
= ""
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")
327 error_description
+= (
328 "machine {} agent-status={} ; ".format(
332 # check machine instance status
333 if machine
.get("instance-status"):
334 s
= machine
.get("instance-status").get("status")
337 error_description
+= (
338 "machine {} instance-status={} ; ".format(
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")
351 error_description
+= (
352 "application {} status={} ; ".format(app_id
, s
)
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"
363 self
.update_db_2("nsrs", nsr_id
, db_dict
)
365 except (asyncio
.CancelledError
, asyncio
.TimeoutError
):
367 except Exception as e
:
368 self
.logger
.warn("Error updating NS state for ns={}: {}".format(nsr_id
, e
))
370 async def _on_update_k8s_db(
371 self
, cluster_uuid
, kdu_instance
, filter=None, vca_id
=None, cluster_type
="juju"
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)
382 # self.logger.debug("_on_update_k8s_db(cluster_uuid={}, kdu_instance={}, filter={}"
383 # .format(cluster_uuid, kdu_instance, filter))
385 nsr_id
= filter.get("_id")
387 vca_status
= await self
.k8scluster_map
[cluster_type
].status_kdu(
388 cluster_uuid
=cluster_uuid
,
389 kdu_instance
=kdu_instance
,
391 complete_status
=True,
397 db_dict
["vcaStatus"] = {nsr_id
: vca_status
}
400 f
"Obtained VCA status for cluster type '{cluster_type}': {vca_status}"
404 self
.update_db_2("nsrs", nsr_id
, db_dict
)
405 except (asyncio
.CancelledError
, asyncio
.TimeoutError
):
407 except Exception as e
:
408 self
.logger
.warn("Error updating NS state for ns={}: {}".format(nsr_id
, e
))
411 def _parse_cloud_init(cloud_init_text
, additional_params
, vnfd_id
, vdu_id
):
414 undefined
=StrictUndefined
,
415 autoescape
=select_autoescape(default_for_string
=True, default
=True),
417 template
= env
.from_string(cloud_init_text
)
418 return template
.render(additional_params
or {})
419 except UndefinedError
as e
:
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
)
425 except (TemplateError
, TemplateNotFound
) as e
:
427 "Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".format(
432 def _get_vdu_cloud_init_content(self
, vdu
, vnfd
):
433 cloud_init_content
= cloud_init_file
= None
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"],
444 cloud_init_file
= "{}/Scripts/cloud_init/{}".format(
445 base_folder
["folder"],
446 vdu
["cloud-init-file"],
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"]
453 return cloud_init_content
454 except FsException
as e
:
456 "Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".format(
457 vnfd
["id"], vdu
["id"], cloud_init_file
, e
461 def _get_vdu_additional_params(self
, db_vnfr
, vdu_id
):
463 (vdur
for vdur
in db_vnfr
.get("vdur") if vdu_id
== vdur
["vdu-id-ref"]), {}
465 additional_params
= vdur
.get("additionalParams")
466 return parse_yaml_strings(additional_params
)
468 def vnfd2RO(self
, vnfd
, new_id
=None, additionalParams
=None, nsrId
=None):
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
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)
486 vnfd_RO
["id"] = new_id
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)
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"])
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")
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":
516 "VIM={} is not available. operationalState={}".format(
517 vim_account
, db_vim
["_admin"]["operationalState"]
520 RO_vim_id
= db_vim
["_admin"]["deployed"]["RO"]
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":
528 "WIM={} is not available. operationalState={}".format(
529 wim_account
, db_wim
["_admin"]["operationalState"]
532 RO_wim_id
= db_wim
["_admin"]["deployed"]["RO-account"]
537 def scale_vnfr(self
, db_vnfr
, vdu_create
=None, vdu_delete
=None, mark_delete
=False):
538 db_vdu_push_list
= []
540 db_update
= {"_admin.modified": time()}
542 for vdu_id
, vdu_count
in vdu_create
.items():
546 for vdur
in reversed(db_vnfr
["vdur"])
547 if vdur
["vdu-id-ref"] == vdu_id
552 # Read the template saved in the db:
554 "No vdur in the database. Using the vdur-template to scale"
556 vdur_template
= db_vnfr
.get("vdur-template")
557 if not vdur_template
:
559 "Error scaling OUT VNFR for {}. No vnfr or template exists".format(
563 vdur
= vdur_template
[0]
564 # Delete a template from the database after using it
567 {"_id": db_vnfr
["_id"]},
569 pull
={"vdur-template": {"_id": vdur
["_id"]}},
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"]
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
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
594 iface
.pop("mac-address", 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))
602 if len(db_vnfr
["vdur"]) == 1:
603 # The scale will move to 0 instances
605 "Scaling to 0 !, creating the template with the last vdur"
607 template_vdur
= [db_vnfr
["vdur"][0]]
608 for vdu_id
, vdu_count
in vdu_delete
.items():
610 indexes_to_delete
= [
612 for iv
in enumerate(db_vnfr
["vdur"])
613 if iv
[1]["vdu-id-ref"] == vdu_id
617 "vdur.{}.status".format(i
): "DELETING"
618 for i
in indexes_to_delete
[-vdu_count
:]
622 # it must be deleted one by one because common.db does not allow otherwise
625 for v
in reversed(db_vnfr
["vdur"])
626 if v
["vdu-id-ref"] == vdu_id
628 for vdu
in vdus_to_delete
[:vdu_count
]:
631 {"_id": db_vnfr
["_id"]},
633 pull
={"vdur": {"_id": vdu
["_id"]}},
637 db_push
["vdur"] = db_vdu_push_list
639 db_push
["vdur-template"] = template_vdur
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"]
648 def ns_update_nsr(self
, ns_update_nsr
, db_nsr
, nsr_desc_RO
):
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
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"):
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
669 "ns_update_nsr: Not found vld={} at RO info".format(vld
["id"])
672 def set_vnfr_at_error(self
, db_vnfrs
, error_text
):
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"
681 vdur
["status-detailed"] = str(error_text
)
683 "vdur.{}.status-detailed".format(vdu_index
)
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
))
689 def ns_update_vnfr(self
, db_vnfrs
, nsr_desc_RO
):
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
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
:
701 if vnf_RO
.get("ip_address"):
702 db_vnfr
["ip-address"] = vnfr_update
["ip-address"] = vnf_RO
[
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(
713 for vdu_index
, vdur
in enumerate(get_iterable(db_vnfr
, "vdur")):
714 vdur_RO_count_index
= 0
715 if vdur
.get("pdu-type"):
717 for vdur_RO
in get_iterable(vnf_RO
, "vms"):
718 if vdur
["vdu-id-ref"] != vdur_RO
["vdu_osm_id"]:
720 if vdur
["count-index"] != vdur_RO_count_index
:
721 vdur_RO_count_index
+= 1
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]
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(
738 ifacer
["mac-address"] = interface_RO
.get(
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"]
749 vnfr_update
["vdur.{}".format(vdu_index
)] = vdur
753 "ns_update_vnfr: Not found member_vnf_index={} vdur={} count_index={} from "
755 vnf_index
, vdur
["vdu-id-ref"], vdur
["count-index"]
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"):
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
771 "ns_update_vnfr: Not found member_vnf_index={} vld={} from VIM info".format(
776 self
.update_db_2("vnfrs", db_vnfr
["_id"], vnfr_update
)
781 "ns_update_vnfr: Not found member_vnf_index={} from VIM info".format(
786 def _get_ns_config_info(self
, nsr_id
):
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
794 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
795 vca_deployed_list
= db_nsr
["_admin"]["deployed"]["VCA"]
797 ns_config_info
= {"osm-config-mapping": mapping
}
798 for vca
in vca_deployed_list
:
799 if not vca
["member-vnf-index"]:
801 if not vca
["vdu_id"]:
802 mapping
[vca
["member-vnf-index"]] = vca
["application"]
806 vca
["member-vnf-index"], vca
["vdu_id"], vca
["vdu_count_index"]
808 ] = vca
["application"]
809 return ns_config_info
811 async def _instantiate_ng_ro(
827 def get_vim_account(vim_account_id
):
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
835 # modify target_vld info with instantiation parameters
836 def parse_vld_instantiation_params(
837 target_vim
, target_vld
, vld_params
, target_sdn
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"]
843 if vld_params
.get("provider-network"):
844 target_vld
["vim_info"][target_vim
]["provider_network"] = vld_params
[
847 if "sdn-ports" in vld_params
["provider-network"] and target_sdn
:
848 target_vld
["vim_info"][target_sdn
]["sdn-ports"] = vld_params
[
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
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
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
872 "Target VLD with WIM data: {:s}".format(str(target_vld
))
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
881 target_vld
["vim_info"],
882 (other_target_vim
, param
.replace("-", "_")),
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")
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"):
899 for vnfr
in db_vnfrs
.values()
900 if vnf_params
["member-vnf-index"]
901 == vnfr
["member-vnf-index-ref"]
905 vdur
= next((vdur
for vdur
in target_vnf
.get("vdur", ())), None)
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"],
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"]),
919 if vnf_params
.get("vimAccountId") not in a_vld
.get(
922 target_vim_network_list
= [
923 v
for _
, v
in a_vld
.get("vim_info").items()
925 target_vim_network_name
= next(
927 item
.get("vim_network_name", "")
928 for item
in target_vim_network_list
933 target
["ns"]["vld"][a_index
].get("vim_info").update(
935 "vim:{}".format(vnf_params
["vimAccountId"]): {
936 "vim_network_name": target_vim_network_name
,
942 for param
in ("vim-network-name", "vim-network-id"):
943 if vld_params
.get(param
) and isinstance(
944 vld_params
[param
], dict
946 for vim
, vim_net
in vld_params
[
949 other_target_vim
= "vim:" + vim
951 target
["ns"]["vld"][a_index
].get(
956 param
.replace("-", "_"),
961 nslcmop_id
= db_nslcmop
["_id"]
963 "name": db_nsr
["name"],
966 "image": deepcopy(db_nsr
["image"]),
967 "flavor": deepcopy(db_nsr
["flavor"]),
968 "action_id": nslcmop_id
,
969 "cloud_init_content": {},
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"]
983 for affinity_or_anti_affinity_group
in target
[
984 "affinity-or-anti-affinity-group"
986 affinity_or_anti_affinity_group
["vim_info"] = {}
988 if db_nslcmop
.get("lcmOperationType") != "instantiate":
989 # get parameters of instantiation:
990 db_nslcmop_instantiate
= self
.db
.get_list(
993 "nsInstanceId": db_nslcmop
["nsInstanceId"],
994 "lcmOperationType": "instantiate",
997 ns_params
= db_nslcmop_instantiate
.get("operationParams")
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 [])
1004 for vld_index
, vld
in enumerate(db_nsr
.get("vld")):
1005 target_vim
= "vim:{}".format(ns_params
["vimAccountId"])
1008 "name": vld
["name"],
1009 "mgmt-network": vld
.get("mgmt-network", False),
1010 "type": vld
.get("type"),
1013 "vim_network_name": vld
.get("vim-network-name"),
1014 "vim_account_id": ns_params
["vimAccountId"],
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
] = {
1027 "target_vim": target_vim
,
1029 "type": vld
.get("type"),
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"]:
1037 "member_vnf:{}.{}".format(
1038 cp
["constituent-cpd-id"][0][
1039 "constituent-base-element-id"
1041 cp
["constituent-cpd-id"][0]["constituent-cpd-id"],
1043 ] = "nsrs:{}:vld.{}".format(nsr_id
, vld_index
)
1045 # check at nsd descriptor, if there is an ip-profile
1047 nsd_vlp
= find_in_list(
1048 get_virtual_link_profiles(nsd
),
1049 lambda a_link_profile
: a_link_profile
["virtual-link-desc-id"]
1054 and nsd_vlp
.get("virtual-link-protocol-data")
1055 and nsd_vlp
["virtual-link-protocol-data"].get("l3-protocol-data")
1057 vld_params
["ip-profile"] = nsd_vlp
["virtual-link-protocol-data"][
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"]),
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
)
1073 for vnfr
in db_vnfrs
.values():
1074 vnfd
= find_in_list(
1075 db_vnfds
, lambda db_vnf
: db_vnf
["id"] == vnfr
["vnfd-ref"]
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"],
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"],
1090 ns_cp
= "member_vnf:{}.{}".format(
1091 vnfr
["member-vnf-index-ref"], vnf_cp
["id"]
1093 if cp2target
.get(ns_cp
):
1094 vld
["target"] = cp2target
[ns_cp
]
1097 target_vim
: {"vim_network_name": vld
.get("vim-network-name")}
1099 # check if this network needs SDN assist
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")
1105 sdn_vld
= "vnfrs:{}:vld.{}".format(target_vnf
["_id"], vld
["id"])
1106 target_sdn
= "sdn:{}".format(sdnc_id
)
1107 vld
["vim_info"][target_sdn
] = {
1109 "target_vim": target_vim
,
1111 "type": vld
.get("type"),
1114 # check at vnfd descriptor, if there is an ip-profile
1116 vnfd_vlp
= find_in_list(
1117 get_virtual_link_profiles(vnfd
),
1118 lambda a_link_profile
: a_link_profile
["id"] == vld
["id"],
1122 and vnfd_vlp
.get("virtual-link-protocol-data")
1123 and vnfd_vlp
["virtual-link-protocol-data"].get("l3-protocol-data")
1125 vld_params
["ip-profile"] = vnfd_vlp
["virtual-link-protocol-data"][
1128 # update vld_params with instantiation params
1130 vld_instantiation_params
= find_in_list(
1131 get_iterable(vnf_params
, "internal-vld"),
1132 lambda i_vld
: i_vld
["name"] == vld
["id"],
1134 if vld_instantiation_params
:
1135 vld_params
.update(vld_instantiation_params
)
1136 parse_vld_instantiation_params(target_vim
, vld
, vld_params
, target_sdn
)
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"]}
1144 self
.logger
.debug("NS > ssh_keys > {}".format(ssh_keys_all
))
1147 vdu_configuration
= get_configuration(vnfd
, vdur
["vdu-id-ref"])
1148 vnf_configuration
= get_configuration(vnfd
, vnfd
["id"])
1151 and vdu_configuration
.get("config-access")
1152 and vdu_configuration
.get("config-access").get("ssh-access")
1154 vdur
["ssh-keys"] = ssh_keys_all
1155 vdur
["ssh-access-required"] = vdu_configuration
[
1157 ]["ssh-access"]["required"]
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"])
1164 vdur
["ssh-keys"] = ssh_keys_all
1165 vdur
["ssh-access-required"] = vnf_configuration
[
1167 ]["ssh-access"]["required"]
1168 elif ssh_keys_instantiation
and find_in_list(
1169 vdur
["interfaces"], lambda iface
: iface
.get("mgmt-vnf")
1171 vdur
["ssh-keys"] = ssh_keys_instantiation
1173 self
.logger
.debug("NS > vdur > {}".format(vdur
))
1175 vdud
= get_vdu(vnfd
, vdur
["vdu-id-ref"])
1177 if vdud
.get("cloud-init-file"):
1178 vdur
["cloud-init"] = "{}:file:{}".format(
1179 vnfd
["_id"], vdud
.get("cloud-init-file")
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"),
1191 cloud_init_file
= "{}/Scripts/cloud_init/{}".format(
1192 base_folder
["folder"],
1193 vdud
.get("cloud-init-file"),
1195 with self
.fs
.file_open(cloud_init_file
, "r") as ci_file
:
1196 target
["cloud_init_content"][
1199 elif vdud
.get("cloud-init"):
1200 vdur
["cloud-init"] = "{}:vdu:{}".format(
1201 vnfd
["_id"], get_vdu_index(vnfd
, vdur
["vdu-id-ref"])
1203 # put content at target.cloul_init_content. Avoid ng_ro read vnfd descriptor
1204 target
["cloud_init_content"][vdur
["cloud-init"]] = vdud
[
1207 vdur
["additionalParams"] = vdur
.get("additionalParams") or {}
1208 deploy_params_vdu
= self
._format
_additional
_params
(
1209 vdur
.get("additionalParams") or {}
1211 deploy_params_vdu
["OSM"] = get_osm_params(
1212 vnfr
, vdur
["vdu-id-ref"], vdur
["count-index"]
1214 vdur
["additionalParams"] = deploy_params_vdu
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
] = {}
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
1233 "use alternative image id: {}".format(alt_image_id
)
1235 ns_image_id
= alt_image_id
1236 vdur
["ns-image-id"] = ns_image_id
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
] = {}
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
] = {}
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"]
1256 ns_sv
["vim_info"][target_vim
] = {}
1258 vdur
["vim_info"] = {target_vim
: {}}
1259 # instantiation parameters
1261 vdu_instantiation_params
= find_in_list(
1262 get_iterable(vnf_params
, "vdu"),
1263 lambda i_vdu
: i_vdu
["id"] == vdud
["id"],
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
1270 vdur
["additionalParams"]["OSM"]["vdu_volumes"] = vdu_volumes
1271 vdur
["additionalParams"]["OSM"][
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
)
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
(
1289 operation
="instantiation",
1294 "_admin.deployed.RO.operational-status": "running",
1295 "detailed-status": " ".join(stage
),
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
)
1301 logging_text
+ "ns deployed at RO. RO_id={}".format(action_id
)
1305 async def _wait_ng_ro(
1315 detailed_status_old
= None
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":
1325 stage
[2] = "VIM: ({})".format(desc_status
["details"])
1326 elif desc_status
["status"] == "DONE":
1328 stage
[2] = "Deployed at VIM"
1331 assert False, "ROclient.check_ns_status returns unknown {}".format(
1332 desc_status
["status"]
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")
1343 async def _terminate_ng_ro(
1344 self
, logging_text
, nsr_deployed
, nsr_id
, nslcmop_id
, stage
1349 start_deploy
= time()
1356 "action_id": nslcmop_id
,
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"
1363 + "ns terminate action at RO. action_id={}".format(action_id
)
1367 delete_timeout
= 20 * 60 # 20 minutes
1368 await self
._wait
_ng
_ro
(
1375 operation
="termination",
1377 db_nsr_update
["_admin.deployed.RO.nsr_status"] = "DELETED"
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"
1385 logging_text
+ "RO_action_id={} already deleted".format(action_id
)
1387 elif e
.http_code
== 409: # conflict
1388 failed_detail
.append("delete conflict: {}".format(e
))
1391 + "RO_action_id={} delete conflict: {}".format(action_id
, e
)
1394 failed_detail
.append("delete error: {}".format(e
))
1397 + "RO_action_id={} delete error: {}".format(action_id
, e
)
1399 except Exception as e
:
1400 failed_detail
.append("delete error: {}".format(e
))
1402 logging_text
+ "RO_action_id={} delete error: {}".format(action_id
, e
)
1406 stage
[2] = "Error deleting from VIM"
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
)
1414 raise LcmException("; ".join(failed_detail
))
1417 async def instantiate_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'
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
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"]
1448 timeout_ns_deploy
= self
.timeout
.ns_deploy
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"]:
1458 ns_params
["vimAccountId"] == vnfr
["vim-account-id"]
1460 return await self
._instantiate
_ng
_ro
(
1473 except Exception as e
:
1474 stage
[2] = "ERROR deploying at VIM"
1475 self
.set_vnfr_at_error(db_vnfrs
, str(e
))
1477 "Error deploying at VIM {}".format(e
),
1478 exc_info
=not isinstance(
1481 ROclient
.ROClientException
,
1490 async def wait_kdu_up(self
, logging_text
, nsr_id
, vnfr_id
, kdu_name
):
1492 Wait for kdu to be up, get ip address
1493 :param logging_text: prefix use for logging
1497 :return: IP address, K8s services
1500 # self.logger.debug(logging_text + "Starting wait_kdu_up")
1503 while nb_tries
< 360:
1504 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnfr_id
})
1508 for x
in get_iterable(db_vnfr
, "kdur")
1509 if x
.get("kdu-name") == kdu_name
1515 "Not found vnfr_id={}, kdu_name={}".format(vnfr_id
, kdu_name
)
1517 if kdur
.get("status"):
1518 if kdur
["status"] in ("READY", "ENABLED"):
1519 return kdur
.get("ip-address"), kdur
.get("services")
1522 "target KDU={} is in error state".format(kdu_name
)
1525 await asyncio
.sleep(10)
1527 raise LcmException("Timeout waiting KDU={} instantiated".format(kdu_name
))
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
1533 Wait for ip addres at RO, and optionally, insert public key in virtual machine
1534 :param logging_text: prefix use for logging
1539 :param pub_key: public ssh key to inject, None to skip
1540 :param user: user to apply the public ssh key
1544 self
.logger
.debug(logging_text
+ "Starting wait_vm_up_insert_key_ro")
1546 target_vdu_id
= None
1551 if ro_retries
>= 360: # 1 hour
1553 "Not found _admin.deployed.RO.nsr_id for nsr_id: {}".format(nsr_id
)
1556 await asyncio
.sleep(10)
1559 if not target_vdu_id
:
1560 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnfr_id
})
1562 if not vdu_id
: # for the VNF case
1563 if db_vnfr
.get("status") == "ERROR":
1565 "Cannot inject ssh-key because target VNF is in error state"
1567 ip_address
= db_vnfr
.get("ip-address")
1573 for x
in get_iterable(db_vnfr
, "vdur")
1574 if x
.get("ip-address") == ip_address
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
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]
1595 "Not found vnfr_id={}, vdu_id={}, vdu_index={}".format(
1596 vnfr_id
, vdu_id
, vdu_index
1599 # New generation RO stores information at "vim_info"
1602 if vdur
.get("vim_info"):
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")
1608 vdur
.get("pdu-type")
1609 or vdur
.get("status") == "ACTIVE"
1610 or ng_ro_status
== "ACTIVE"
1612 ip_address
= vdur
.get("ip-address")
1615 target_vdu_id
= vdur
["vdu-id-ref"]
1616 elif vdur
.get("status") == "ERROR" or ng_ro_status
== "ERROR":
1618 "Cannot inject ssh-key because target VM is in error state"
1621 if not target_vdu_id
:
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")
1634 "action": "inject_ssh_key",
1638 "vnf": [{"_id": vnfr_id
, "vdur": [{"id": vdur
["id"]}]}],
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"
1646 except NgRoException
as e
:
1648 "Reaching max tries injecting key. Error: {}".format(e
)
1655 async def _wait_dependent_n2vc(self
, nsr_id
, vca_deployed_list
, vca_index
):
1657 Wait until dependent VCA deployments have been finished. NS wait for VNFs and VDUs. VNFs for VDUs
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
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
:
1672 if not my_vca
.get("member-vnf-index") or (
1673 vca_deployed
.get("member-vnf-index")
1674 == my_vca
.get("member-vnf-index")
1676 internal_status
= configuration_status_list
[index
].get("status")
1677 if internal_status
== "READY":
1679 elif internal_status
== "BROKEN":
1681 "Configuration aborted because dependent charm/s has failed"
1686 # no dependencies, return
1688 await asyncio
.sleep(10)
1691 raise LcmException("Configuration aborted because dependent charm/s timeout")
1693 def get_vca_id(self
, db_vnfr
: dict, db_nsr
: dict):
1696 vca_id
= deep_get(db_vnfr
, ("vca-id",))
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")
1702 async def instantiate_N2VC(
1720 ee_config_descriptor
,
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"]}}
1728 "collection": "nsrs",
1729 "filter": {"_id": nsr_id
},
1730 "path": db_update_entry
,
1735 element_under_configuration
= nsr_id
1739 vnfr_id
= db_vnfr
["_id"]
1740 osm_config
["osm"]["vnf_id"] = vnfr_id
1742 namespace
= "{nsi}.{ns}".format(nsi
=nsi_id
if nsi_id
else "", ns
=nsr_id
)
1744 if vca_type
== "native_charm":
1747 index_number
= vdu_index
or 0
1750 element_type
= "VNF"
1751 element_under_configuration
= vnfr_id
1752 namespace
+= ".{}-{}".format(vnfr_id
, index_number
)
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
1759 namespace
+= ".{}".format(kdu_name
)
1760 element_type
= "KDU"
1761 element_under_configuration
= kdu_name
1762 osm_config
["osm"]["kdu_name"] = kdu_name
1765 if base_folder
["pkg-dir"]:
1766 artifact_path
= "{}/{}/{}/{}".format(
1767 base_folder
["folder"],
1768 base_folder
["pkg-dir"],
1771 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
1776 artifact_path
= "{}/Scripts/{}/{}/".format(
1777 base_folder
["folder"],
1780 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
1785 self
.logger
.debug("Artifact path > {}".format(artifact_path
))
1787 # get initial_config_primitive_list that applies to this element
1788 initial_config_primitive_list
= config_descriptor
.get(
1789 "initial-config-primitive"
1793 "Initial config primitive list > {}".format(
1794 initial_config_primitive_list
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
1806 "Initial config primitive list #2 > {}".format(
1807 initial_config_primitive_list
1810 # n2vc_redesign STEP 3.1
1811 # find old ee_id if exists
1812 ee_id
= vca_deployed
.get("ee_id")
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
(
1819 vca_index
=vca_index
,
1821 element_under_configuration
=element_under_configuration
,
1822 element_type
=element_type
,
1825 step
= "create execution environment"
1826 self
.logger
.debug(logging_text
+ step
)
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
,
1838 elif vca_type
== "helm" or vca_type
== "helm-v3":
1839 ee_id
, credentials
= await self
.vca_map
[
1841 ].create_execution_environment(
1846 artifact_path
=artifact_path
,
1847 chart_model
=vca_name
,
1851 ee_id
, credentials
= await self
.vca_map
[
1853 ].create_execution_environment(
1854 namespace
=namespace
,
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(
1872 credentials
= {"hostname": rw_mgmt_ip
}
1874 username
= deep_get(
1875 config_descriptor
, ("config-access", "ssh-access", "default-user")
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"]
1887 "Cannot determine the username neither with 'initial-config-primitive' nor with "
1888 "'config-access.ssh-access.default-user'"
1890 credentials
["username"] = username
1891 # n2vc_redesign STEP 3.2
1893 self
._write
_configuration
_status
(
1895 vca_index
=vca_index
,
1896 status
="REGISTERING",
1897 element_under_configuration
=element_under_configuration
,
1898 element_type
=element_type
,
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
,
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
1920 # n2vc_redesign STEP 3.3
1921 step
= "Install configuration Software"
1923 self
._write
_configuration
_status
(
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
,
1932 # TODO check if already done
1933 self
.logger
.debug(logging_text
+ step
)
1935 if vca_type
== "native_charm":
1936 config_primitive
= next(
1937 (p
for p
in initial_config_primitive_list
if p
["name"] == "config"),
1940 if config_primitive
:
1941 config
= self
._map
_primitive
_params
(
1942 config_primitive
, {}, deploy_params
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
1955 if vca_type
!= "k8s_proxy_charm":
1956 await self
.vca_map
[vca_type
].install_configuration_sw(
1958 artifact_path
=artifact_path
,
1961 num_units
=num_units
,
1966 # write in db flag of configuration_sw already installed
1968 "nsrs", nsr_id
, {db_update_entry
+ "config_sw_installed": True}
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
,
1976 vca_index
=vca_index
,
1979 if not is_relation_added
:
1980 raise LcmException("Relations could not be added to VCA.")
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"):
1987 # self.logger.debug("get ssh key block")
1989 config_descriptor
, ("config-access", "ssh-access", "required")
1991 # self.logger.debug("ssh key needed")
1992 # Needed to inject a ssh key
1995 ("config-access", "ssh-access", "default-user"),
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
2002 step
= "Insert public key into VM user={} ssh_key={}".format(
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
)
2010 # default rw_mgmt_ip to None, avoiding the non definition of the variable
2013 # n2vc_redesign STEP 5.1
2014 # wait for RO (ip-address) Insert pub_key into VM
2017 rw_mgmt_ip
, services
= await self
.wait_kdu_up(
2018 logging_text
, nsr_id
, vnfr_id
, kdu_name
2020 vnfd
= self
.db
.get_one(
2022 {"_id": f
'{db_vnfr["vnfd-id"]}:{db_vnfr["revision"]}'},
2024 kdu
= get_kdu(vnfd
, kdu_name
)
2026 service
["name"] for service
in get_kdu_services(kdu
)
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(
2034 primitive_name
="config",
2036 "osm-config": json
.dumps(
2038 k8s
={"services": exposed_services
}
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
2050 elif db_vnfr
.get("vdur"):
2051 rw_mgmt_ip
= await self
.wait_vm_up_insert_key_ro(
2061 self
.logger
.debug(logging_text
+ " VM_ip_address={}".format(rw_mgmt_ip
))
2063 # store rw_mgmt_ip in deploy params for later replacement
2064 deploy_params
["rw_mgmt_ip"] = rw_mgmt_ip
2066 # n2vc_redesign STEP 6 Execute initial config primitive
2067 step
= "execute initial config primitive"
2069 # wait for dependent primitives execution (NS -> VNF -> VDU)
2070 if initial_config_primitive_list
:
2071 await self
._wait
_dependent
_n
2vc
(nsr_id
, vca_deployed_list
, vca_index
)
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"):
2077 stage
[0] = "Stage 3/5: running Day-1 primitives for VDU."
2078 elif my_vca
.get("member-vnf-index"):
2080 stage
[0] = "Stage 4/5: running Day-1 primitives for VNF."
2083 stage
[0] = "Stage 5/5: running Day-1 primitives for NS."
2085 self
._write
_configuration
_status
(
2086 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="EXECUTING PRIMITIVE"
2089 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
)
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
)
2098 # TODO check if already done
2099 primitive_params_
= self
._map
_primitive
_params
(
2100 initial_config_primitive
, {}, deploy_params
2103 step
= "execute primitive '{}' params '{}'".format(
2104 initial_config_primitive
["name"], primitive_params_
2106 self
.logger
.debug(logging_text
+ step
)
2107 await self
.vca_map
[vca_type
].exec_primitive(
2109 primitive_name
=initial_config_primitive
["name"],
2110 params_dict
=primitive_params_
,
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"):
2119 "nsrs", nsr_id
, {db_update_entry
+ "needed_terminate": True}
2121 check_if_terminated_needed
= False
2123 # TODO register in database that primitive is done
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(
2131 artifact_path
=artifact_path
,
2132 ee_config_descriptor
=ee_config_descriptor
,
2135 target_ip
=rw_mgmt_ip
,
2136 element_type
=element_type
,
2137 vnf_member_index
=db_vnfr
.get("member-vnf-index-ref", ""),
2139 vdu_index
=vdu_index
,
2141 kdu_index
=kdu_index
,
2147 {db_update_entry
+ "prometheus_jobs": prometheus_jobs
},
2150 for job
in prometheus_jobs
:
2153 {"job_name": job
["job_name"]},
2156 fail_on_empty
=False,
2159 step
= "instantiated at VCA"
2160 self
.logger
.debug(logging_text
+ step
)
2162 self
._write
_configuration
_status
(
2163 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="READY"
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"})
2169 e
, (DbException
, N2VCException
, LcmException
, asyncio
.CancelledError
)
2172 "Exception while {} : {}".format(step
, e
), exc_info
=True
2174 self
._write
_configuration
_status
(
2175 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="BROKEN"
2177 raise LcmException("{}. {}".format(step
, e
)) from e
2179 def _write_ns_status(
2183 current_operation
: str,
2184 current_operation_id
: str,
2185 error_description
: str = None,
2186 error_detail
: str = None,
2187 other_update
: dict = None,
2190 Update db_nsr fields.
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
2201 db_dict
= other_update
or {}
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
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
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
))
2220 def _write_op_status(
2224 error_message
: str = None,
2225 queuePosition
: int = 0,
2226 operation_state
: str = None,
2227 other_update
: dict = None,
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
)
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
:
2246 "Error writing OPERATION status for op_id: {} -> {}".format(op_id
, e
)
2249 def _write_all_config_status(self
, db_nsr
: dict, status
: str):
2251 nsr_id
= db_nsr
["_id"]
2252 # configurationStatus
2253 config_status
= db_nsr
.get("configurationStatus")
2256 "configurationStatus.{}.status".format(index
): status
2257 for index
, v
in enumerate(config_status
)
2261 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
2263 except DbException
as e
:
2265 "Error writing all configuration status, ns={}: {}".format(nsr_id
, e
)
2268 def _write_configuration_status(
2273 element_under_configuration
: str = None,
2274 element_type
: str = None,
2275 other_update
: dict = None,
2277 # self.logger.debug('_write_configuration_status(): vca_index={}, status={}'
2278 # .format(vca_index, status))
2281 db_path
= "configurationStatus.{}.".format(vca_index
)
2282 db_dict
= other_update
or {}
2284 db_dict
[db_path
+ "status"] = status
2285 if element_under_configuration
:
2287 db_path
+ "elementUnderConfiguration"
2288 ] = element_under_configuration
2290 db_dict
[db_path
+ "elementType"] = element_type
2291 self
.update_db_2("nsrs", nsr_id
, db_dict
)
2292 except DbException
as e
:
2294 "Error writing configuration status={}, ns={}, vca_index={}: {}".format(
2295 status
, nsr_id
, vca_index
, e
2299 async def _do_placement(self
, logging_text
, db_nslcmop
, db_vnfrs
):
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'
2311 nslcmop_id
= db_nslcmop
["_id"]
2312 placement_engine
= deep_get(db_nslcmop
, ("operationParams", "placement-engine"))
2313 if placement_engine
== "PLA":
2315 logging_text
+ "Invoke and wait for placement optimization"
2317 await self
.msg
.aiowrite("pla", "get_placement", {"nslcmopId": nslcmop_id
})
2318 db_poll_interval
= 5
2319 wait
= db_poll_interval
* 10
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"))
2329 "Placement timeout for nslcmopId={}".format(nslcmop_id
)
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
:
2339 {"_id": vnfr
["_id"]},
2340 {"vim-account-id": pla_vnf
["vimAccountId"]},
2343 vnfr
["vim-account-id"] = pla_vnf
["vimAccountId"]
2346 def _gather_vnfr_healing_alerts(self
, vnfr
, vnfd
):
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"]
2357 (vdur
for vdur
in vnfr
["vdur"] if vdu_id
== vdur
["vdu-id-ref"]),
2362 metric_name
= "vm_status"
2363 vdu_name
= vdur
.get("name")
2364 vnf_member_index
= vnfr
["member-vnf-index-ref"]
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")
2374 "metric": metric_name
,
2377 "vnf_member_index": vnf_member_index
,
2378 "vdu_name": vdu_name
,
2380 "alarm_status": "ok",
2381 "action_type": "healing",
2384 alerts
.append(alert
)
2387 def _gather_vnfr_scaling_alerts(self
, vnfr
, vnfd
):
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(
2411 for vdu_delta
in delta
.get("vdu-delta", ()):
2412 all_monitored_vdus
.add(vdu_delta
.get("id"))
2413 monitored_vdurs
= list(
2415 lambda vdur
: vdur
["vdu-id-ref"] in all_monitored_vdus
,
2419 if not monitored_vdurs
:
2421 "Scaling criteria is referring to a vnf-monitoring-param that does not contain a reference to a vdu or vnf metric"
2424 for scaling_policy
in scaling_aspect
.get("scaling-policy", ()):
2425 if scaling_policy
["scaling-type"] != "automatic":
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"
2433 vnf_monitoring_param
= all_vnfd_monitoring_params
[
2434 monitoring_param_ref
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"
2444 scaleout_threshold
= scaling_criteria
.get(
2445 "scale-out-threshold"
2447 # Looking for min/max-number-of-instances
2448 instances_min_number
= 1
2449 instances_max_number
= 1
2450 vdu_profile
= df
["vdu-profile"]
2453 item
for item
in vdu_profile
if item
["id"] == vdu_id
2455 instances_min_number
= profile
.get(
2456 "min-number-of-instances", 1
2458 instances_max_number
= profile
.get(
2459 "max-number-of-instances", 1
2462 if scalein_threshold
:
2464 name
= f
"scalein_{uuid}"
2465 operation
= scaling_criteria
[
2466 "scale-in-relational-operation"
2468 rel_operator
= self
.rel_operation_types
.get(
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})"
2475 "vnf_member_index": vnf_member_index
,
2481 "for": str(threshold_time
) + "m",
2484 action
= scaling_policy
2486 "scaling-group": scaling_group_name
,
2487 "cooldown-time": cooldown_time
,
2492 "metric": metric_name
,
2495 "vnf_member_index": vnf_member_index
,
2498 "alarm_status": "ok",
2499 "action_type": "scale_in",
2501 "prometheus_config": prom_cfg
,
2503 alerts
.append(alert
)
2505 if scaleout_threshold
:
2507 name
= f
"scaleout_{uuid}"
2508 operation
= scaling_criteria
[
2509 "scale-out-relational-operation"
2511 rel_operator
= self
.rel_operation_types
.get(
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})"
2518 "vnf_member_index": vnf_member_index
,
2524 "for": str(threshold_time
) + "m",
2527 action
= scaling_policy
2529 "scaling-group": scaling_group_name
,
2530 "cooldown-time": cooldown_time
,
2535 "metric": metric_name
,
2538 "vnf_member_index": vnf_member_index
,
2541 "alarm_status": "ok",
2542 "action_type": "scale_out",
2544 "prometheus_config": prom_cfg
,
2546 alerts
.append(alert
)
2549 def _gather_vnfr_alarm_alerts(self
, vnfr
, vnfd
):
2551 nsr_id
= vnfr
["nsr-id-ref"]
2552 vnf_member_index
= vnfr
["member-vnf-index-ref"]
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"]))
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
:
2565 "VDU alarm refers to a VDU monitoring param, but there are no VDU monitoring params in the VDU"
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", ""
2576 vdu_specific_monitoring_param
= vdu_monitoring_params
.get(
2577 alarm_monitoring_param
, {}
2579 if not vdu_specific_monitoring_param
:
2581 "VDU alarm refers to a VDU monitoring param not present in the VDU"
2584 metric_name
= vdu_specific_monitoring_param
.get(
2585 "performance-metric"
2589 "VDU alarm refers to a VDU monitoring param that has no associated performance-metric"
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")
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}"}}'
2601 f
"avg({metric_selector}) {rel_operator} {metric_threshold}"
2605 "vnf_member_index": vnf_member_index
,
2609 "alert": alert_name
,
2611 "for": "1m", # default value. Ideally, this should be related to an IM param, but there is not such param
2614 alarm_action
= dict()
2615 for action_type
in ["ok", "insufficient-data", "alarm"]:
2617 "actions" in alarm_descriptor
2618 and action_type
in alarm_descriptor
["actions"]
2620 for url
in alarm_descriptor
["actions"][action_type
]:
2621 if "webhook" in alarm_action
:
2622 alarm_action
["webhook"].append(url
["url"])
2624 alarm_action
["webhook"] = [url
["url"]]
2629 "metric": metric_name
,
2632 "vnf_member_index": vnf_member_index
,
2635 "alarm_status": "ok",
2636 "action_type": "vdu_alarm",
2637 "action": alarm_action
,
2638 "prometheus_config": prom_cfg
,
2640 alerts
.append(alert
)
2643 def update_nsrs_with_pla_result(self
, params
):
2645 nslcmop_id
= deep_get(params
, ("placement", "nslcmopId"))
2647 "nslcmops", nslcmop_id
, {"_admin.pla": params
.get("placement")}
2649 except Exception as e
:
2650 self
.logger
.warn("Update failed for nslcmop_id={}:{}".format(nslcmop_id
, e
))
2652 async def instantiate(self
, nsr_id
, nslcmop_id
):
2655 :param nsr_id: ns instance to deploy
2656 :param nslcmop_id: operation to run
2660 # Try to lock HA task here
2661 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
2662 if not task_is_locked_by_me
:
2664 "instantiate() task is not locked by me, ns={}".format(nsr_id
)
2668 logging_text
= "Task ns={} instantiate={} ".format(nsr_id
, nslcmop_id
)
2669 self
.logger
.debug(logging_text
+ "Enter")
2671 # get all needed from database
2673 # database nsrs record
2676 # database nslcmops record
2679 # update operation on nsrs
2681 # update operation on nslcmops
2682 db_nslcmop_update
= {}
2684 timeout_ns_deploy
= self
.timeout
.ns_deploy
2686 nslcmop_operation_state
= None
2687 db_vnfrs
= {} # vnf's info indexed by member-index
2689 tasks_dict_info
= {} # from task to info text
2693 "Stage 1/5: preparation of the environment.",
2694 "Waiting for previous operations to terminate.",
2697 # ^ stage, step, VIM progress
2699 # wait for any previous tasks in process
2700 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
2702 # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
2703 stage
[1] = "Reading from database."
2704 # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
2705 db_nsr_update
["detailed-status"] = "creating"
2706 db_nsr_update
["operational-status"] = "init"
2707 self
._write
_ns
_status
(
2709 ns_state
="BUILDING",
2710 current_operation
="INSTANTIATING",
2711 current_operation_id
=nslcmop_id
,
2712 other_update
=db_nsr_update
,
2714 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
, queuePosition
=0)
2716 # read from db: operation
2717 stage
[1] = "Getting nslcmop={} from db.".format(nslcmop_id
)
2718 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
2719 if db_nslcmop
["operationParams"].get("additionalParamsForVnf"):
2720 db_nslcmop
["operationParams"]["additionalParamsForVnf"] = json
.loads(
2721 db_nslcmop
["operationParams"]["additionalParamsForVnf"]
2723 ns_params
= db_nslcmop
.get("operationParams")
2724 if ns_params
and ns_params
.get("timeout_ns_deploy"):
2725 timeout_ns_deploy
= ns_params
["timeout_ns_deploy"]
2728 stage
[1] = "Getting nsr={} from db.".format(nsr_id
)
2729 self
.logger
.debug(logging_text
+ stage
[1])
2730 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
2731 stage
[1] = "Getting nsd={} from db.".format(db_nsr
["nsd-id"])
2732 self
.logger
.debug(logging_text
+ stage
[1])
2733 nsd
= self
.db
.get_one("nsds", {"_id": db_nsr
["nsd-id"]})
2734 self
.fs
.sync(db_nsr
["nsd-id"])
2736 # nsr_name = db_nsr["name"] # TODO short-name??
2738 # read from db: vnf's of this ns
2739 stage
[1] = "Getting vnfrs from db."
2740 self
.logger
.debug(logging_text
+ stage
[1])
2741 db_vnfrs_list
= self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
})
2743 # read from db: vnfd's for every vnf
2744 db_vnfds
= [] # every vnfd data
2746 # for each vnf in ns, read vnfd
2747 for vnfr
in db_vnfrs_list
:
2748 if vnfr
.get("kdur"):
2750 for kdur
in vnfr
["kdur"]:
2751 if kdur
.get("additionalParams"):
2752 kdur
["additionalParams"] = json
.loads(
2753 kdur
["additionalParams"]
2755 kdur_list
.append(kdur
)
2756 vnfr
["kdur"] = kdur_list
2758 db_vnfrs
[vnfr
["member-vnf-index-ref"]] = vnfr
2759 vnfd_id
= vnfr
["vnfd-id"]
2760 vnfd_ref
= vnfr
["vnfd-ref"]
2761 self
.fs
.sync(vnfd_id
)
2763 # if we haven't this vnfd, read it from db
2764 if vnfd_id
not in db_vnfds
:
2766 stage
[1] = "Getting vnfd={} id='{}' from db.".format(
2769 self
.logger
.debug(logging_text
+ stage
[1])
2770 vnfd
= self
.db
.get_one("vnfds", {"_id": vnfd_id
})
2773 db_vnfds
.append(vnfd
)
2775 # Get or generates the _admin.deployed.VCA list
2776 vca_deployed_list
= None
2777 if db_nsr
["_admin"].get("deployed"):
2778 vca_deployed_list
= db_nsr
["_admin"]["deployed"].get("VCA")
2779 if vca_deployed_list
is None:
2780 vca_deployed_list
= []
2781 configuration_status_list
= []
2782 db_nsr_update
["_admin.deployed.VCA"] = vca_deployed_list
2783 db_nsr_update
["configurationStatus"] = configuration_status_list
2784 # add _admin.deployed.VCA to db_nsr dictionary, value=vca_deployed_list
2785 populate_dict(db_nsr
, ("_admin", "deployed", "VCA"), vca_deployed_list
)
2786 elif isinstance(vca_deployed_list
, dict):
2787 # maintain backward compatibility. Change a dict to list at database
2788 vca_deployed_list
= list(vca_deployed_list
.values())
2789 db_nsr_update
["_admin.deployed.VCA"] = vca_deployed_list
2790 populate_dict(db_nsr
, ("_admin", "deployed", "VCA"), vca_deployed_list
)
2793 deep_get(db_nsr
, ("_admin", "deployed", "RO", "vnfd")), list
2795 populate_dict(db_nsr
, ("_admin", "deployed", "RO", "vnfd"), [])
2796 db_nsr_update
["_admin.deployed.RO.vnfd"] = []
2798 # set state to INSTANTIATED. When instantiated NBI will not delete directly
2799 db_nsr_update
["_admin.nsState"] = "INSTANTIATED"
2800 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
2802 "vnfrs", {"nsr-id-ref": nsr_id
}, {"_admin.nsState": "INSTANTIATED"}
2805 # n2vc_redesign STEP 2 Deploy Network Scenario
2806 stage
[0] = "Stage 2/5: deployment of KDUs, VMs and execution environments."
2807 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
)
2809 stage
[1] = "Deploying KDUs."
2810 # self.logger.debug(logging_text + "Before deploy_kdus")
2811 # Call to deploy_kdus in case exists the "vdu:kdu" param
2812 await self
.deploy_kdus(
2813 logging_text
=logging_text
,
2815 nslcmop_id
=nslcmop_id
,
2818 task_instantiation_info
=tasks_dict_info
,
2821 stage
[1] = "Getting VCA public key."
2822 # n2vc_redesign STEP 1 Get VCA public ssh-key
2823 # feature 1429. Add n2vc public key to needed VMs
2824 n2vc_key
= self
.n2vc
.get_public_key()
2825 n2vc_key_list
= [n2vc_key
]
2826 if self
.vca_config
.public_key
:
2827 n2vc_key_list
.append(self
.vca_config
.public_key
)
2829 stage
[1] = "Deploying NS at VIM."
2830 task_ro
= asyncio
.ensure_future(
2831 self
.instantiate_RO(
2832 logging_text
=logging_text
,
2836 db_nslcmop
=db_nslcmop
,
2839 n2vc_key_list
=n2vc_key_list
,
2843 self
.lcm_tasks
.register("ns", nsr_id
, nslcmop_id
, "instantiate_RO", task_ro
)
2844 tasks_dict_info
[task_ro
] = "Deploying at VIM"
2846 # n2vc_redesign STEP 3 to 6 Deploy N2VC
2847 stage
[1] = "Deploying Execution Environments."
2848 self
.logger
.debug(logging_text
+ stage
[1])
2850 # create namespace and certificate if any helm based EE is present in the NS
2851 if check_helm_ee_in_ns(db_vnfds
):
2852 await self
.vca_map
["helm-v3"].setup_ns_namespace(
2855 # create TLS certificates
2856 await self
.vca_map
["helm-v3"].create_tls_certificate(
2857 secret_name
=self
.EE_TLS_NAME
,
2860 usage
="server auth",
2864 nsi_id
= None # TODO put nsi_id when this nsr belongs to a NSI
2865 for vnf_profile
in get_vnf_profiles(nsd
):
2866 vnfd_id
= vnf_profile
["vnfd-id"]
2867 vnfd
= find_in_list(db_vnfds
, lambda a_vnf
: a_vnf
["id"] == vnfd_id
)
2868 member_vnf_index
= str(vnf_profile
["id"])
2869 db_vnfr
= db_vnfrs
[member_vnf_index
]
2870 base_folder
= vnfd
["_admin"]["storage"]
2877 # Get additional parameters
2878 deploy_params
= {"OSM": get_osm_params(db_vnfr
)}
2879 if db_vnfr
.get("additionalParamsForVnf"):
2880 deploy_params
.update(
2881 parse_yaml_strings(db_vnfr
["additionalParamsForVnf"].copy())
2884 descriptor_config
= get_configuration(vnfd
, vnfd
["id"])
2885 if descriptor_config
:
2887 logging_text
=logging_text
2888 + "member_vnf_index={} ".format(member_vnf_index
),
2891 nslcmop_id
=nslcmop_id
,
2897 member_vnf_index
=member_vnf_index
,
2898 vdu_index
=vdu_index
,
2899 kdu_index
=kdu_index
,
2901 deploy_params
=deploy_params
,
2902 descriptor_config
=descriptor_config
,
2903 base_folder
=base_folder
,
2904 task_instantiation_info
=tasks_dict_info
,
2908 # Deploy charms for each VDU that supports one.
2909 for vdud
in get_vdu_list(vnfd
):
2911 descriptor_config
= get_configuration(vnfd
, vdu_id
)
2912 vdur
= find_in_list(
2913 db_vnfr
["vdur"], lambda vdu
: vdu
["vdu-id-ref"] == vdu_id
2916 if vdur
.get("additionalParams"):
2917 deploy_params_vdu
= parse_yaml_strings(vdur
["additionalParams"])
2919 deploy_params_vdu
= deploy_params
2920 deploy_params_vdu
["OSM"] = get_osm_params(
2921 db_vnfr
, vdu_id
, vdu_count_index
=0
2923 vdud_count
= get_number_of_instances(vnfd
, vdu_id
)
2925 self
.logger
.debug("VDUD > {}".format(vdud
))
2927 "Descriptor config > {}".format(descriptor_config
)
2929 if descriptor_config
:
2933 for vdu_index
in range(vdud_count
):
2934 # TODO vnfr_params["rw_mgmt_ip"] = vdur["ip-address"]
2936 logging_text
=logging_text
2937 + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
2938 member_vnf_index
, vdu_id
, vdu_index
2942 nslcmop_id
=nslcmop_id
,
2948 kdu_index
=kdu_index
,
2949 member_vnf_index
=member_vnf_index
,
2950 vdu_index
=vdu_index
,
2952 deploy_params
=deploy_params_vdu
,
2953 descriptor_config
=descriptor_config
,
2954 base_folder
=base_folder
,
2955 task_instantiation_info
=tasks_dict_info
,
2958 for kdud
in get_kdu_list(vnfd
):
2959 kdu_name
= kdud
["name"]
2960 descriptor_config
= get_configuration(vnfd
, kdu_name
)
2961 if descriptor_config
:
2965 kdu_index
, kdur
= next(
2967 for x
in enumerate(db_vnfr
["kdur"])
2968 if x
[1]["kdu-name"] == kdu_name
2970 deploy_params_kdu
= {"OSM": get_osm_params(db_vnfr
)}
2971 if kdur
.get("additionalParams"):
2972 deploy_params_kdu
.update(
2973 parse_yaml_strings(kdur
["additionalParams"].copy())
2977 logging_text
=logging_text
,
2980 nslcmop_id
=nslcmop_id
,
2986 member_vnf_index
=member_vnf_index
,
2987 vdu_index
=vdu_index
,
2988 kdu_index
=kdu_index
,
2990 deploy_params
=deploy_params_kdu
,
2991 descriptor_config
=descriptor_config
,
2992 base_folder
=base_folder
,
2993 task_instantiation_info
=tasks_dict_info
,
2997 # Check if each vnf has exporter for metric collection if so update prometheus job records
2998 if "exporters-endpoints" in vnfd
.get("df")[0]:
2999 exporter_config
= vnfd
.get("df")[0].get("exporters-endpoints")
3000 self
.logger
.debug("exporter config :{}".format(exporter_config
))
3001 artifact_path
= "{}/{}/{}".format(
3002 base_folder
["folder"],
3003 base_folder
["pkg-dir"],
3004 "exporter-endpoint",
3007 ee_config_descriptor
= exporter_config
3008 vnfr_id
= db_vnfr
["id"]
3009 rw_mgmt_ip
= await self
.wait_vm_up_insert_key_ro(
3018 self
.logger
.debug("rw_mgmt_ip:{}".format(rw_mgmt_ip
))
3019 self
.logger
.debug("Artifact_path:{}".format(artifact_path
))
3020 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnfr_id
})
3021 vdu_id_for_prom
= None
3022 vdu_index_for_prom
= None
3023 for x
in get_iterable(db_vnfr
, "vdur"):
3024 vdu_id_for_prom
= x
.get("vdu-id-ref")
3025 vdu_index_for_prom
= x
.get("count-index")
3026 prometheus_jobs
= await self
.extract_prometheus_scrape_jobs(
3028 artifact_path
=artifact_path
,
3029 ee_config_descriptor
=ee_config_descriptor
,
3032 target_ip
=rw_mgmt_ip
,
3034 vdu_id
=vdu_id_for_prom
,
3035 vdu_index
=vdu_index_for_prom
,
3038 self
.logger
.debug("Prometheus job:{}".format(prometheus_jobs
))
3040 db_nsr_update
["_admin.deployed.prometheus_jobs"] = prometheus_jobs
3047 for job
in prometheus_jobs
:
3050 {"job_name": job
["job_name"]},
3053 fail_on_empty
=False,
3056 # Check if this NS has a charm configuration
3057 descriptor_config
= nsd
.get("ns-configuration")
3058 if descriptor_config
and descriptor_config
.get("juju"):
3061 member_vnf_index
= None
3068 # Get additional parameters
3069 deploy_params
= {"OSM": {"vim_account_id": ns_params
["vimAccountId"]}}
3070 if db_nsr
.get("additionalParamsForNs"):
3071 deploy_params
.update(
3072 parse_yaml_strings(db_nsr
["additionalParamsForNs"].copy())
3074 base_folder
= nsd
["_admin"]["storage"]
3076 logging_text
=logging_text
,
3079 nslcmop_id
=nslcmop_id
,
3085 member_vnf_index
=member_vnf_index
,
3086 vdu_index
=vdu_index
,
3087 kdu_index
=kdu_index
,
3089 deploy_params
=deploy_params
,
3090 descriptor_config
=descriptor_config
,
3091 base_folder
=base_folder
,
3092 task_instantiation_info
=tasks_dict_info
,
3096 # rest of staff will be done at finally
3099 ROclient
.ROClientException
,
3105 logging_text
+ "Exit Exception while '{}': {}".format(stage
[1], e
)
3108 except asyncio
.CancelledError
:
3110 logging_text
+ "Cancelled Exception while '{}'".format(stage
[1])
3112 exc
= "Operation was cancelled"
3113 except Exception as e
:
3114 exc
= traceback
.format_exc()
3115 self
.logger
.critical(
3116 logging_text
+ "Exit Exception while '{}': {}".format(stage
[1], e
),
3121 error_list
.append(str(exc
))
3123 # wait for pending tasks
3125 stage
[1] = "Waiting for instantiate pending tasks."
3126 self
.logger
.debug(logging_text
+ stage
[1])
3127 error_list
+= await self
._wait
_for
_tasks
(
3135 stage
[1] = stage
[2] = ""
3136 except asyncio
.CancelledError
:
3137 error_list
.append("Cancelled")
3138 # TODO cancel all tasks
3139 except Exception as exc
:
3140 error_list
.append(str(exc
))
3142 # update operation-status
3143 db_nsr_update
["operational-status"] = "running"
3144 # let's begin with VCA 'configured' status (later we can change it)
3145 db_nsr_update
["config-status"] = "configured"
3146 for task
, task_name
in tasks_dict_info
.items():
3147 if not task
.done() or task
.cancelled() or task
.exception():
3148 if task_name
.startswith(self
.task_name_deploy_vca
):
3149 # A N2VC task is pending
3150 db_nsr_update
["config-status"] = "failed"
3152 # RO or KDU task is pending
3153 db_nsr_update
["operational-status"] = "failed"
3155 # update status at database
3157 error_detail
= ". ".join(error_list
)
3158 self
.logger
.error(logging_text
+ error_detail
)
3159 error_description_nslcmop
= "{} Detail: {}".format(
3160 stage
[0], error_detail
3162 error_description_nsr
= "Operation: INSTANTIATING.{}, {}".format(
3163 nslcmop_id
, stage
[0]
3166 db_nsr_update
["detailed-status"] = (
3167 error_description_nsr
+ " Detail: " + error_detail
3169 db_nslcmop_update
["detailed-status"] = error_detail
3170 nslcmop_operation_state
= "FAILED"
3174 error_description_nsr
= error_description_nslcmop
= None
3176 db_nsr_update
["detailed-status"] = "Done"
3177 db_nslcmop_update
["detailed-status"] = "Done"
3178 nslcmop_operation_state
= "COMPLETED"
3179 # Gather auto-healing and auto-scaling alerts for each vnfr
3182 for vnfr
in self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
}):
3184 (sub
for sub
in db_vnfds
if sub
["_id"] == vnfr
["vnfd-id"]), None
3186 healing_alerts
= self
._gather
_vnfr
_healing
_alerts
(vnfr
, vnfd
)
3187 for alert
in healing_alerts
:
3188 self
.logger
.info(f
"Storing healing alert in MongoDB: {alert}")
3189 self
.db
.create("alerts", alert
)
3191 scaling_alerts
= self
._gather
_vnfr
_scaling
_alerts
(vnfr
, vnfd
)
3192 for alert
in scaling_alerts
:
3193 self
.logger
.info(f
"Storing scaling alert in MongoDB: {alert}")
3194 self
.db
.create("alerts", alert
)
3196 alarm_alerts
= self
._gather
_vnfr
_alarm
_alerts
(vnfr
, vnfd
)
3197 for alert
in alarm_alerts
:
3198 self
.logger
.info(f
"Storing VNF alarm alert in MongoDB: {alert}")
3199 self
.db
.create("alerts", alert
)
3201 self
._write
_ns
_status
(
3204 current_operation
="IDLE",
3205 current_operation_id
=None,
3206 error_description
=error_description_nsr
,
3207 error_detail
=error_detail
,
3208 other_update
=db_nsr_update
,
3210 self
._write
_op
_status
(
3213 error_message
=error_description_nslcmop
,
3214 operation_state
=nslcmop_operation_state
,
3215 other_update
=db_nslcmop_update
,
3218 if nslcmop_operation_state
:
3220 await self
.msg
.aiowrite(
3225 "nslcmop_id": nslcmop_id
,
3226 "operationState": nslcmop_operation_state
,
3229 except Exception as e
:
3231 logging_text
+ "kafka_write notification Exception {}".format(e
)
3234 self
.logger
.debug(logging_text
+ "Exit")
3235 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_instantiate")
3237 def _get_vnfd(self
, vnfd_id
: str, projects_read
: str, cached_vnfds
: Dict
[str, Any
]):
3238 if vnfd_id
not in cached_vnfds
:
3239 cached_vnfds
[vnfd_id
] = self
.db
.get_one(
3240 "vnfds", {"id": vnfd_id
, "_admin.projects_read": projects_read
}
3242 return cached_vnfds
[vnfd_id
]
3244 def _get_vnfr(self
, nsr_id
: str, vnf_profile_id
: str, cached_vnfrs
: Dict
[str, Any
]):
3245 if vnf_profile_id
not in cached_vnfrs
:
3246 cached_vnfrs
[vnf_profile_id
] = self
.db
.get_one(
3249 "member-vnf-index-ref": vnf_profile_id
,
3250 "nsr-id-ref": nsr_id
,
3253 return cached_vnfrs
[vnf_profile_id
]
3255 def _is_deployed_vca_in_relation(
3256 self
, vca
: DeployedVCA
, relation
: Relation
3259 for endpoint
in (relation
.provider
, relation
.requirer
):
3260 if endpoint
["kdu-resource-profile-id"]:
3263 vca
.vnf_profile_id
== endpoint
.vnf_profile_id
3264 and vca
.vdu_profile_id
== endpoint
.vdu_profile_id
3265 and vca
.execution_environment_ref
== endpoint
.execution_environment_ref
3271 def _update_ee_relation_data_with_implicit_data(
3272 self
, nsr_id
, nsd
, ee_relation_data
, cached_vnfds
, vnf_profile_id
: str = None
3274 ee_relation_data
= safe_get_ee_relation(
3275 nsr_id
, ee_relation_data
, vnf_profile_id
=vnf_profile_id
3277 ee_relation_level
= EELevel
.get_level(ee_relation_data
)
3278 if (ee_relation_level
in (EELevel
.VNF
, EELevel
.VDU
)) and not ee_relation_data
[
3279 "execution-environment-ref"
3281 vnf_profile
= get_vnf_profile(nsd
, ee_relation_data
["vnf-profile-id"])
3282 vnfd_id
= vnf_profile
["vnfd-id"]
3283 project
= nsd
["_admin"]["projects_read"][0]
3284 db_vnfd
= self
._get
_vnfd
(vnfd_id
, project
, cached_vnfds
)
3287 if ee_relation_level
== EELevel
.VNF
3288 else ee_relation_data
["vdu-profile-id"]
3290 ee
= get_juju_ee_ref(db_vnfd
, entity_id
)
3293 f
"not execution environments found for ee_relation {ee_relation_data}"
3295 ee_relation_data
["execution-environment-ref"] = ee
["id"]
3296 return ee_relation_data
3298 def _get_ns_relations(
3301 nsd
: Dict
[str, Any
],
3303 cached_vnfds
: Dict
[str, Any
],
3304 ) -> List
[Relation
]:
3306 db_ns_relations
= get_ns_configuration_relation_list(nsd
)
3307 for r
in db_ns_relations
:
3308 provider_dict
= None
3309 requirer_dict
= None
3310 if all(key
in r
for key
in ("provider", "requirer")):
3311 provider_dict
= r
["provider"]
3312 requirer_dict
= r
["requirer"]
3313 elif "entities" in r
:
3314 provider_id
= r
["entities"][0]["id"]
3317 "endpoint": r
["entities"][0]["endpoint"],
3319 if provider_id
!= nsd
["id"]:
3320 provider_dict
["vnf-profile-id"] = provider_id
3321 requirer_id
= r
["entities"][1]["id"]
3324 "endpoint": r
["entities"][1]["endpoint"],
3326 if requirer_id
!= nsd
["id"]:
3327 requirer_dict
["vnf-profile-id"] = requirer_id
3330 "provider/requirer or entities must be included in the relation."
3332 relation_provider
= self
._update
_ee
_relation
_data
_with
_implicit
_data
(
3333 nsr_id
, nsd
, provider_dict
, cached_vnfds
3335 relation_requirer
= self
._update
_ee
_relation
_data
_with
_implicit
_data
(
3336 nsr_id
, nsd
, requirer_dict
, cached_vnfds
3338 provider
= EERelation(relation_provider
)
3339 requirer
= EERelation(relation_requirer
)
3340 relation
= Relation(r
["name"], provider
, requirer
)
3341 vca_in_relation
= self
._is
_deployed
_vca
_in
_relation
(vca
, relation
)
3343 relations
.append(relation
)
3346 def _get_vnf_relations(
3349 nsd
: Dict
[str, Any
],
3351 cached_vnfds
: Dict
[str, Any
],
3352 ) -> List
[Relation
]:
3354 if vca
.target_element
== "ns":
3355 self
.logger
.debug("VCA is a NS charm, not a VNF.")
3357 vnf_profile
= get_vnf_profile(nsd
, vca
.vnf_profile_id
)
3358 vnf_profile_id
= vnf_profile
["id"]
3359 vnfd_id
= vnf_profile
["vnfd-id"]
3360 project
= nsd
["_admin"]["projects_read"][0]
3361 db_vnfd
= self
._get
_vnfd
(vnfd_id
, project
, cached_vnfds
)
3362 db_vnf_relations
= get_relation_list(db_vnfd
, vnfd_id
)
3363 for r
in db_vnf_relations
:
3364 provider_dict
= None
3365 requirer_dict
= None
3366 if all(key
in r
for key
in ("provider", "requirer")):
3367 provider_dict
= r
["provider"]
3368 requirer_dict
= r
["requirer"]
3369 elif "entities" in r
:
3370 provider_id
= r
["entities"][0]["id"]
3373 "vnf-profile-id": vnf_profile_id
,
3374 "endpoint": r
["entities"][0]["endpoint"],
3376 if provider_id
!= vnfd_id
:
3377 provider_dict
["vdu-profile-id"] = provider_id
3378 requirer_id
= r
["entities"][1]["id"]
3381 "vnf-profile-id": vnf_profile_id
,
3382 "endpoint": r
["entities"][1]["endpoint"],
3384 if requirer_id
!= vnfd_id
:
3385 requirer_dict
["vdu-profile-id"] = requirer_id
3388 "provider/requirer or entities must be included in the relation."
3390 relation_provider
= self
._update
_ee
_relation
_data
_with
_implicit
_data
(
3391 nsr_id
, nsd
, provider_dict
, cached_vnfds
, vnf_profile_id
=vnf_profile_id
3393 relation_requirer
= self
._update
_ee
_relation
_data
_with
_implicit
_data
(
3394 nsr_id
, nsd
, requirer_dict
, cached_vnfds
, vnf_profile_id
=vnf_profile_id
3396 provider
= EERelation(relation_provider
)
3397 requirer
= EERelation(relation_requirer
)
3398 relation
= Relation(r
["name"], provider
, requirer
)
3399 vca_in_relation
= self
._is
_deployed
_vca
_in
_relation
(vca
, relation
)
3401 relations
.append(relation
)
3404 def _get_kdu_resource_data(
3406 ee_relation
: EERelation
,
3407 db_nsr
: Dict
[str, Any
],
3408 cached_vnfds
: Dict
[str, Any
],
3409 ) -> DeployedK8sResource
:
3410 nsd
= get_nsd(db_nsr
)
3411 vnf_profiles
= get_vnf_profiles(nsd
)
3412 vnfd_id
= find_in_list(
3414 lambda vnf_profile
: vnf_profile
["id"] == ee_relation
.vnf_profile_id
,
3416 project
= nsd
["_admin"]["projects_read"][0]
3417 db_vnfd
= self
._get
_vnfd
(vnfd_id
, project
, cached_vnfds
)
3418 kdu_resource_profile
= get_kdu_resource_profile(
3419 db_vnfd
, ee_relation
.kdu_resource_profile_id
3421 kdu_name
= kdu_resource_profile
["kdu-name"]
3422 deployed_kdu
, _
= get_deployed_kdu(
3423 db_nsr
.get("_admin", ()).get("deployed", ()),
3425 ee_relation
.vnf_profile_id
,
3427 deployed_kdu
.update({"resource-name": kdu_resource_profile
["resource-name"]})
3430 def _get_deployed_component(
3432 ee_relation
: EERelation
,
3433 db_nsr
: Dict
[str, Any
],
3434 cached_vnfds
: Dict
[str, Any
],
3435 ) -> DeployedComponent
:
3436 nsr_id
= db_nsr
["_id"]
3437 deployed_component
= None
3438 ee_level
= EELevel
.get_level(ee_relation
)
3439 if ee_level
== EELevel
.NS
:
3440 vca
= get_deployed_vca(db_nsr
, {"vdu_id": None, "member-vnf-index": None})
3442 deployed_component
= DeployedVCA(nsr_id
, vca
)
3443 elif ee_level
== EELevel
.VNF
:
3444 vca
= get_deployed_vca(
3448 "member-vnf-index": ee_relation
.vnf_profile_id
,
3449 "ee_descriptor_id": ee_relation
.execution_environment_ref
,
3453 deployed_component
= DeployedVCA(nsr_id
, vca
)
3454 elif ee_level
== EELevel
.VDU
:
3455 vca
= get_deployed_vca(
3458 "vdu_id": ee_relation
.vdu_profile_id
,
3459 "member-vnf-index": ee_relation
.vnf_profile_id
,
3460 "ee_descriptor_id": ee_relation
.execution_environment_ref
,
3464 deployed_component
= DeployedVCA(nsr_id
, vca
)
3465 elif ee_level
== EELevel
.KDU
:
3466 kdu_resource_data
= self
._get
_kdu
_resource
_data
(
3467 ee_relation
, db_nsr
, cached_vnfds
3469 if kdu_resource_data
:
3470 deployed_component
= DeployedK8sResource(kdu_resource_data
)
3471 return deployed_component
3473 async def _add_relation(
3477 db_nsr
: Dict
[str, Any
],
3478 cached_vnfds
: Dict
[str, Any
],
3479 cached_vnfrs
: Dict
[str, Any
],
3481 deployed_provider
= self
._get
_deployed
_component
(
3482 relation
.provider
, db_nsr
, cached_vnfds
3484 deployed_requirer
= self
._get
_deployed
_component
(
3485 relation
.requirer
, db_nsr
, cached_vnfds
3489 and deployed_requirer
3490 and deployed_provider
.config_sw_installed
3491 and deployed_requirer
.config_sw_installed
3493 provider_db_vnfr
= (
3495 relation
.provider
.nsr_id
,
3496 relation
.provider
.vnf_profile_id
,
3499 if relation
.provider
.vnf_profile_id
3502 requirer_db_vnfr
= (
3504 relation
.requirer
.nsr_id
,
3505 relation
.requirer
.vnf_profile_id
,
3508 if relation
.requirer
.vnf_profile_id
3511 provider_vca_id
= self
.get_vca_id(provider_db_vnfr
, db_nsr
)
3512 requirer_vca_id
= self
.get_vca_id(requirer_db_vnfr
, db_nsr
)
3513 provider_relation_endpoint
= RelationEndpoint(
3514 deployed_provider
.ee_id
,
3516 relation
.provider
.endpoint
,
3518 requirer_relation_endpoint
= RelationEndpoint(
3519 deployed_requirer
.ee_id
,
3521 relation
.requirer
.endpoint
,
3524 await self
.vca_map
[vca_type
].add_relation(
3525 provider
=provider_relation_endpoint
,
3526 requirer
=requirer_relation_endpoint
,
3528 except N2VCException
as exception
:
3529 self
.logger
.error(exception
)
3530 raise LcmException(exception
)
3534 async def _add_vca_relations(
3540 timeout
: int = 3600,
3543 # 1. find all relations for this VCA
3544 # 2. wait for other peers related
3548 # STEP 1: find all relations for this VCA
3551 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
3552 nsd
= get_nsd(db_nsr
)
3555 deployed_vca_dict
= get_deployed_vca_list(db_nsr
)[vca_index
]
3556 my_vca
= DeployedVCA(nsr_id
, deployed_vca_dict
)
3561 relations
.extend(self
._get
_ns
_relations
(nsr_id
, nsd
, my_vca
, cached_vnfds
))
3562 relations
.extend(self
._get
_vnf
_relations
(nsr_id
, nsd
, my_vca
, cached_vnfds
))
3564 # if no relations, terminate
3566 self
.logger
.debug(logging_text
+ " No relations")
3569 self
.logger
.debug(logging_text
+ " adding relations {}".format(relations
))
3576 if now
- start
>= timeout
:
3577 self
.logger
.error(logging_text
+ " : timeout adding relations")
3580 # reload nsr from database (we need to update record: _admin.deployed.VCA)
3581 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
3583 # for each relation, find the VCA's related
3584 for relation
in relations
.copy():
3585 added
= await self
._add
_relation
(
3593 relations
.remove(relation
)
3596 self
.logger
.debug("Relations added")
3598 await asyncio
.sleep(5.0)
3602 except Exception as e
:
3603 self
.logger
.warn(logging_text
+ " ERROR adding relations: {}".format(e
))
3606 async def _install_kdu(
3614 k8s_instance_info
: dict,
3615 k8params
: dict = None,
3620 k8sclustertype
= k8s_instance_info
["k8scluster-type"]
3623 "collection": "nsrs",
3624 "filter": {"_id": nsr_id
},
3625 "path": nsr_db_path
,
3628 if k8s_instance_info
.get("kdu-deployment-name"):
3629 kdu_instance
= k8s_instance_info
.get("kdu-deployment-name")
3631 kdu_instance
= self
.k8scluster_map
[
3633 ].generate_kdu_instance_name(
3634 db_dict
=db_dict_install
,
3635 kdu_model
=k8s_instance_info
["kdu-model"],
3636 kdu_name
=k8s_instance_info
["kdu-name"],
3639 # Update the nsrs table with the kdu-instance value
3643 _desc
={nsr_db_path
+ ".kdu-instance": kdu_instance
},
3646 # Update the nsrs table with the actual namespace being used, if the k8scluster-type is `juju` or
3647 # `juju-bundle`. This verification is needed because there is not a standard/homogeneous namespace
3648 # between the Helm Charts and Juju Bundles-based KNFs. If we found a way of having an homogeneous
3649 # namespace, this first verification could be removed, and the next step would be done for any kind
3651 # TODO -> find a way to have an homogeneous namespace between the Helm Charts and Juju Bundles-based
3652 # KNFs (Bug 2027: https://osm.etsi.org/bugzilla/show_bug.cgi?id=2027)
3653 if k8sclustertype
in ("juju", "juju-bundle"):
3654 # First, verify if the current namespace is present in the `_admin.projects_read` (if not, it means
3655 # that the user passed a namespace which he wants its KDU to be deployed in)
3661 "_admin.projects_write": k8s_instance_info
["namespace"],
3662 "_admin.projects_read": k8s_instance_info
["namespace"],
3668 f
"Updating namespace/model for Juju Bundle from {k8s_instance_info['namespace']} to {kdu_instance}"
3673 _desc
={f
"{nsr_db_path}.namespace": kdu_instance
},
3675 k8s_instance_info
["namespace"] = kdu_instance
3677 await self
.k8scluster_map
[k8sclustertype
].install(
3678 cluster_uuid
=k8s_instance_info
["k8scluster-uuid"],
3679 kdu_model
=k8s_instance_info
["kdu-model"],
3682 db_dict
=db_dict_install
,
3684 kdu_name
=k8s_instance_info
["kdu-name"],
3685 namespace
=k8s_instance_info
["namespace"],
3686 kdu_instance
=kdu_instance
,
3690 # Obtain services to obtain management service ip
3691 services
= await self
.k8scluster_map
[k8sclustertype
].get_services(
3692 cluster_uuid
=k8s_instance_info
["k8scluster-uuid"],
3693 kdu_instance
=kdu_instance
,
3694 namespace
=k8s_instance_info
["namespace"],
3697 # Obtain management service info (if exists)
3698 vnfr_update_dict
= {}
3699 kdu_config
= get_configuration(vnfd
, kdud
["name"])
3701 target_ee_list
= kdu_config
.get("execution-environment-list", [])
3706 vnfr_update_dict
["kdur.{}.services".format(kdu_index
)] = services
3709 for service
in kdud
.get("service", [])
3710 if service
.get("mgmt-service")
3712 for mgmt_service
in mgmt_services
:
3713 for service
in services
:
3714 if service
["name"].startswith(mgmt_service
["name"]):
3715 # Mgmt service found, Obtain service ip
3716 ip
= service
.get("external_ip", service
.get("cluster_ip"))
3717 if isinstance(ip
, list) and len(ip
) == 1:
3721 "kdur.{}.ip-address".format(kdu_index
)
3724 # Check if must update also mgmt ip at the vnf
3725 service_external_cp
= mgmt_service
.get(
3726 "external-connection-point-ref"
3728 if service_external_cp
:
3730 deep_get(vnfd
, ("mgmt-interface", "cp"))
3731 == service_external_cp
3733 vnfr_update_dict
["ip-address"] = ip
3738 "external-connection-point-ref", ""
3740 == service_external_cp
,
3743 "kdur.{}.ip-address".format(kdu_index
)
3748 "Mgmt service name: {} not found".format(
3749 mgmt_service
["name"]
3753 vnfr_update_dict
["kdur.{}.status".format(kdu_index
)] = "READY"
3754 self
.update_db_2("vnfrs", vnfr_data
.get("_id"), vnfr_update_dict
)
3756 kdu_config
= get_configuration(vnfd
, k8s_instance_info
["kdu-name"])
3759 and kdu_config
.get("initial-config-primitive")
3760 and get_juju_ee_ref(vnfd
, k8s_instance_info
["kdu-name"]) is None
3762 initial_config_primitive_list
= kdu_config
.get(
3763 "initial-config-primitive"
3765 initial_config_primitive_list
.sort(key
=lambda val
: int(val
["seq"]))
3767 for initial_config_primitive
in initial_config_primitive_list
:
3768 primitive_params_
= self
._map
_primitive
_params
(
3769 initial_config_primitive
, {}, {}
3772 await asyncio
.wait_for(
3773 self
.k8scluster_map
[k8sclustertype
].exec_primitive(
3774 cluster_uuid
=k8s_instance_info
["k8scluster-uuid"],
3775 kdu_instance
=kdu_instance
,
3776 primitive_name
=initial_config_primitive
["name"],
3777 params
=primitive_params_
,
3778 db_dict
=db_dict_install
,
3784 except Exception as e
:
3785 # Prepare update db with error and raise exception
3788 "nsrs", nsr_id
, {nsr_db_path
+ ".detailed-status": str(e
)}
3792 vnfr_data
.get("_id"),
3793 {"kdur.{}.status".format(kdu_index
): "ERROR"},
3796 # ignore to keep original exception
3798 # reraise original error
3803 async def deploy_kdus(
3810 task_instantiation_info
,
3812 # Launch kdus if present in the descriptor
3814 k8scluster_id_2_uuic
= {
3815 "helm-chart-v3": {},
3820 async def _get_cluster_id(cluster_id
, cluster_type
):
3821 nonlocal k8scluster_id_2_uuic
3822 if cluster_id
in k8scluster_id_2_uuic
[cluster_type
]:
3823 return k8scluster_id_2_uuic
[cluster_type
][cluster_id
]
3825 # check if K8scluster is creating and wait look if previous tasks in process
3826 task_name
, task_dependency
= self
.lcm_tasks
.lookfor_related(
3827 "k8scluster", cluster_id
3830 text
= "Waiting for related tasks '{}' on k8scluster {} to be completed".format(
3831 task_name
, cluster_id
3833 self
.logger
.debug(logging_text
+ text
)
3834 await asyncio
.wait(task_dependency
, timeout
=3600)
3836 db_k8scluster
= self
.db
.get_one(
3837 "k8sclusters", {"_id": cluster_id
}, fail_on_empty
=False
3839 if not db_k8scluster
:
3840 raise LcmException("K8s cluster {} cannot be found".format(cluster_id
))
3842 k8s_id
= deep_get(db_k8scluster
, ("_admin", cluster_type
, "id"))
3844 if cluster_type
== "helm-chart-v3":
3846 # backward compatibility for existing clusters that have not been initialized for helm v3
3847 k8s_credentials
= yaml
.safe_dump(
3848 db_k8scluster
.get("credentials")
3850 k8s_id
, uninstall_sw
= await self
.k8sclusterhelm3
.init_env(
3851 k8s_credentials
, reuse_cluster_uuid
=cluster_id
3853 db_k8scluster_update
= {}
3854 db_k8scluster_update
["_admin.helm-chart-v3.error_msg"] = None
3855 db_k8scluster_update
["_admin.helm-chart-v3.id"] = k8s_id
3856 db_k8scluster_update
[
3857 "_admin.helm-chart-v3.created"
3859 db_k8scluster_update
[
3860 "_admin.helm-chart-v3.operationalState"
3863 "k8sclusters", cluster_id
, db_k8scluster_update
3865 except Exception as e
:
3868 + "error initializing helm-v3 cluster: {}".format(str(e
))
3871 "K8s cluster '{}' has not been initialized for '{}'".format(
3872 cluster_id
, cluster_type
3877 "K8s cluster '{}' has not been initialized for '{}'".format(
3878 cluster_id
, cluster_type
3881 k8scluster_id_2_uuic
[cluster_type
][cluster_id
] = k8s_id
3884 logging_text
+= "Deploy kdus: "
3887 db_nsr_update
= {"_admin.deployed.K8s": []}
3888 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
3891 updated_cluster_list
= []
3892 updated_v3_cluster_list
= []
3894 for vnfr_data
in db_vnfrs
.values():
3895 vca_id
= self
.get_vca_id(vnfr_data
, {})
3896 for kdu_index
, kdur
in enumerate(get_iterable(vnfr_data
, "kdur")):
3897 # Step 0: Prepare and set parameters
3898 desc_params
= parse_yaml_strings(kdur
.get("additionalParams"))
3899 vnfd_id
= vnfr_data
.get("vnfd-id")
3900 vnfd_with_id
= find_in_list(
3901 db_vnfds
, lambda vnfd
: vnfd
["_id"] == vnfd_id
3905 for kdud
in vnfd_with_id
["kdu"]
3906 if kdud
["name"] == kdur
["kdu-name"]
3908 namespace
= kdur
.get("k8s-namespace")
3909 kdu_deployment_name
= kdur
.get("kdu-deployment-name")
3910 if kdur
.get("helm-chart"):
3911 kdumodel
= kdur
["helm-chart"]
3912 # Default version: helm3, if helm-version is v2 assign v2
3913 k8sclustertype
= "helm-chart-v3"
3914 self
.logger
.debug("kdur: {}".format(kdur
))
3916 kdur
.get("helm-version")
3917 and kdur
.get("helm-version") == "v2"
3919 k8sclustertype
= "helm-chart"
3920 elif kdur
.get("juju-bundle"):
3921 kdumodel
= kdur
["juju-bundle"]
3922 k8sclustertype
= "juju-bundle"
3925 "kdu type for kdu='{}.{}' is neither helm-chart nor "
3926 "juju-bundle. Maybe an old NBI version is running".format(
3927 vnfr_data
["member-vnf-index-ref"], kdur
["kdu-name"]
3930 # check if kdumodel is a file and exists
3932 vnfd_with_id
= find_in_list(
3933 db_vnfds
, lambda vnfd
: vnfd
["_id"] == vnfd_id
3935 storage
= deep_get(vnfd_with_id
, ("_admin", "storage"))
3936 if storage
: # may be not present if vnfd has not artifacts
3937 # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
3938 if storage
["pkg-dir"]:
3939 filename
= "{}/{}/{}s/{}".format(
3946 filename
= "{}/Scripts/{}s/{}".format(
3951 if self
.fs
.file_exists(
3952 filename
, mode
="file"
3953 ) or self
.fs
.file_exists(filename
, mode
="dir"):
3954 kdumodel
= self
.fs
.path
+ filename
3955 except (asyncio
.TimeoutError
, asyncio
.CancelledError
):
3957 except Exception: # it is not a file
3960 k8s_cluster_id
= kdur
["k8s-cluster"]["id"]
3961 step
= "Synchronize repos for k8s cluster '{}'".format(
3964 cluster_uuid
= await _get_cluster_id(k8s_cluster_id
, k8sclustertype
)
3968 k8sclustertype
== "helm-chart"
3969 and cluster_uuid
not in updated_cluster_list
3971 k8sclustertype
== "helm-chart-v3"
3972 and cluster_uuid
not in updated_v3_cluster_list
3974 del_repo_list
, added_repo_dict
= await asyncio
.ensure_future(
3975 self
.k8scluster_map
[k8sclustertype
].synchronize_repos(
3976 cluster_uuid
=cluster_uuid
3979 if del_repo_list
or added_repo_dict
:
3980 if k8sclustertype
== "helm-chart":
3982 "_admin.helm_charts_added." + item
: None
3983 for item
in del_repo_list
3986 "_admin.helm_charts_added." + item
: name
3987 for item
, name
in added_repo_dict
.items()
3989 updated_cluster_list
.append(cluster_uuid
)
3990 elif k8sclustertype
== "helm-chart-v3":
3992 "_admin.helm_charts_v3_added." + item
: None
3993 for item
in del_repo_list
3996 "_admin.helm_charts_v3_added." + item
: name
3997 for item
, name
in added_repo_dict
.items()
3999 updated_v3_cluster_list
.append(cluster_uuid
)
4001 logging_text
+ "repos synchronized on k8s cluster "
4002 "'{}' to_delete: {}, to_add: {}".format(
4003 k8s_cluster_id
, del_repo_list
, added_repo_dict
4008 {"_id": k8s_cluster_id
},
4014 step
= "Instantiating KDU {}.{} in k8s cluster {}".format(
4015 vnfr_data
["member-vnf-index-ref"],
4019 k8s_instance_info
= {
4020 "kdu-instance": None,
4021 "k8scluster-uuid": cluster_uuid
,
4022 "k8scluster-type": k8sclustertype
,
4023 "member-vnf-index": vnfr_data
["member-vnf-index-ref"],
4024 "kdu-name": kdur
["kdu-name"],
4025 "kdu-model": kdumodel
,
4026 "namespace": namespace
,
4027 "kdu-deployment-name": kdu_deployment_name
,
4029 db_path
= "_admin.deployed.K8s.{}".format(index
)
4030 db_nsr_update
[db_path
] = k8s_instance_info
4031 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
4032 vnfd_with_id
= find_in_list(
4033 db_vnfds
, lambda vnf
: vnf
["_id"] == vnfd_id
4035 task
= asyncio
.ensure_future(
4044 k8params
=desc_params
,
4049 self
.lcm_tasks
.register(
4053 "instantiate_KDU-{}".format(index
),
4056 task_instantiation_info
[task
] = "Deploying KDU {}".format(
4062 except (LcmException
, asyncio
.CancelledError
):
4064 except Exception as e
:
4065 msg
= "Exception {} while {}: {}".format(type(e
).__name
__, step
, e
)
4066 if isinstance(e
, (N2VCException
, DbException
)):
4067 self
.logger
.error(logging_text
+ msg
)
4069 self
.logger
.critical(logging_text
+ msg
, exc_info
=True)
4070 raise LcmException(msg
)
4073 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
4093 task_instantiation_info
,
4096 # launch instantiate_N2VC in a asyncio task and register task object
4097 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
4098 # if not found, create one entry and update database
4099 # fill db_nsr._admin.deployed.VCA.<index>
4102 logging_text
+ "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id
, vdu_id
)
4106 get_charm_name
= False
4107 if "execution-environment-list" in descriptor_config
:
4108 ee_list
= descriptor_config
.get("execution-environment-list", [])
4109 elif "juju" in descriptor_config
:
4110 ee_list
= [descriptor_config
] # ns charms
4111 if "execution-environment-list" not in descriptor_config
:
4112 # charm name is only required for ns charms
4113 get_charm_name
= True
4114 else: # other types as script are not supported
4117 for ee_item
in ee_list
:
4120 + "_deploy_n2vc ee_item juju={}, helm={}".format(
4121 ee_item
.get("juju"), ee_item
.get("helm-chart")
4124 ee_descriptor_id
= ee_item
.get("id")
4125 if ee_item
.get("juju"):
4126 vca_name
= ee_item
["juju"].get("charm")
4128 charm_name
= self
.find_charm_name(db_nsr
, str(vca_name
))
4131 if ee_item
["juju"].get("charm") is not None
4134 if ee_item
["juju"].get("cloud") == "k8s":
4135 vca_type
= "k8s_proxy_charm"
4136 elif ee_item
["juju"].get("proxy") is False:
4137 vca_type
= "native_charm"
4138 elif ee_item
.get("helm-chart"):
4139 vca_name
= ee_item
["helm-chart"]
4140 if ee_item
.get("helm-version") and ee_item
.get("helm-version") == "v2":
4143 vca_type
= "helm-v3"
4146 logging_text
+ "skipping non juju neither charm configuration"
4151 for vca_index
, vca_deployed
in enumerate(
4152 db_nsr
["_admin"]["deployed"]["VCA"]
4154 if not vca_deployed
:
4157 vca_deployed
.get("member-vnf-index") == member_vnf_index
4158 and vca_deployed
.get("vdu_id") == vdu_id
4159 and vca_deployed
.get("kdu_name") == kdu_name
4160 and vca_deployed
.get("vdu_count_index", 0) == vdu_index
4161 and vca_deployed
.get("ee_descriptor_id") == ee_descriptor_id
4165 # not found, create one.
4167 "ns" if not member_vnf_index
else "vnf/{}".format(member_vnf_index
)
4170 target
+= "/vdu/{}/{}".format(vdu_id
, vdu_index
or 0)
4172 target
+= "/kdu/{}".format(kdu_name
)
4174 "target_element": target
,
4175 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
4176 "member-vnf-index": member_vnf_index
,
4178 "kdu_name": kdu_name
,
4179 "vdu_count_index": vdu_index
,
4180 "operational-status": "init", # TODO revise
4181 "detailed-status": "", # TODO revise
4182 "step": "initial-deploy", # TODO revise
4184 "vdu_name": vdu_name
,
4186 "ee_descriptor_id": ee_descriptor_id
,
4187 "charm_name": charm_name
,
4191 # create VCA and configurationStatus in db
4193 "_admin.deployed.VCA.{}".format(vca_index
): vca_deployed
,
4194 "configurationStatus.{}".format(vca_index
): dict(),
4196 self
.update_db_2("nsrs", nsr_id
, db_dict
)
4198 db_nsr
["_admin"]["deployed"]["VCA"].append(vca_deployed
)
4200 self
.logger
.debug("N2VC > NSR_ID > {}".format(nsr_id
))
4201 self
.logger
.debug("N2VC > DB_NSR > {}".format(db_nsr
))
4202 self
.logger
.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed
))
4205 task_n2vc
= asyncio
.ensure_future(
4206 self
.instantiate_N2VC(
4207 logging_text
=logging_text
,
4208 vca_index
=vca_index
,
4214 vdu_index
=vdu_index
,
4215 kdu_index
=kdu_index
,
4216 deploy_params
=deploy_params
,
4217 config_descriptor
=descriptor_config
,
4218 base_folder
=base_folder
,
4219 nslcmop_id
=nslcmop_id
,
4223 ee_config_descriptor
=ee_item
,
4226 self
.lcm_tasks
.register(
4230 "instantiate_N2VC-{}".format(vca_index
),
4233 task_instantiation_info
[
4235 ] = self
.task_name_deploy_vca
+ " {}.{}".format(
4236 member_vnf_index
or "", vdu_id
or ""
4240 def _create_nslcmop(nsr_id
, operation
, params
):
4242 Creates a ns-lcm-opp content to be stored at database.
4243 :param nsr_id: internal id of the instance
4244 :param operation: instantiate, terminate, scale, action, ...
4245 :param params: user parameters for the operation
4246 :return: dictionary following SOL005 format
4248 # Raise exception if invalid arguments
4249 if not (nsr_id
and operation
and params
):
4251 "Parameters 'nsr_id', 'operation' and 'params' needed to create primitive not provided"
4258 # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
4259 "operationState": "PROCESSING",
4260 "statusEnteredTime": now
,
4261 "nsInstanceId": nsr_id
,
4262 "lcmOperationType": operation
,
4264 "isAutomaticInvocation": False,
4265 "operationParams": params
,
4266 "isCancelPending": False,
4268 "self": "/osm/nslcm/v1/ns_lcm_op_occs/" + _id
,
4269 "nsInstance": "/osm/nslcm/v1/ns_instances/" + nsr_id
,
4274 def _format_additional_params(self
, params
):
4275 params
= params
or {}
4276 for key
, value
in params
.items():
4277 if str(value
).startswith("!!yaml "):
4278 params
[key
] = yaml
.safe_load(value
[7:])
4281 def _get_terminate_primitive_params(self
, seq
, vnf_index
):
4282 primitive
= seq
.get("name")
4283 primitive_params
= {}
4285 "member_vnf_index": vnf_index
,
4286 "primitive": primitive
,
4287 "primitive_params": primitive_params
,
4290 return self
._map
_primitive
_params
(seq
, params
, desc_params
)
4294 def _retry_or_skip_suboperation(self
, db_nslcmop
, op_index
):
4295 op
= deep_get(db_nslcmop
, ("_admin", "operations"), [])[op_index
]
4296 if op
.get("operationState") == "COMPLETED":
4297 # b. Skip sub-operation
4298 # _ns_execute_primitive() or RO.create_action() will NOT be executed
4299 return self
.SUBOPERATION_STATUS_SKIP
4301 # c. retry executing sub-operation
4302 # The sub-operation exists, and operationState != 'COMPLETED'
4303 # Update operationState = 'PROCESSING' to indicate a retry.
4304 operationState
= "PROCESSING"
4305 detailed_status
= "In progress"
4306 self
._update
_suboperation
_status
(
4307 db_nslcmop
, op_index
, operationState
, detailed_status
4309 # Return the sub-operation index
4310 # _ns_execute_primitive() or RO.create_action() will be called from scale()
4311 # with arguments extracted from the sub-operation
4314 # Find a sub-operation where all keys in a matching dictionary must match
4315 # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
4316 def _find_suboperation(self
, db_nslcmop
, match
):
4317 if db_nslcmop
and match
:
4318 op_list
= db_nslcmop
.get("_admin", {}).get("operations", [])
4319 for i
, op
in enumerate(op_list
):
4320 if all(op
.get(k
) == match
[k
] for k
in match
):
4322 return self
.SUBOPERATION_STATUS_NOT_FOUND
4324 # Update status for a sub-operation given its index
4325 def _update_suboperation_status(
4326 self
, db_nslcmop
, op_index
, operationState
, detailed_status
4328 # Update DB for HA tasks
4329 q_filter
= {"_id": db_nslcmop
["_id"]}
4331 "_admin.operations.{}.operationState".format(op_index
): operationState
,
4332 "_admin.operations.{}.detailed-status".format(op_index
): detailed_status
,
4335 "nslcmops", q_filter
=q_filter
, update_dict
=update_dict
, fail_on_empty
=False
4338 # Add sub-operation, return the index of the added sub-operation
4339 # Optionally, set operationState, detailed-status, and operationType
4340 # Status and type are currently set for 'scale' sub-operations:
4341 # 'operationState' : 'PROCESSING' | 'COMPLETED' | 'FAILED'
4342 # 'detailed-status' : status message
4343 # 'operationType': may be any type, in the case of scaling: 'PRE-SCALE' | 'POST-SCALE'
4344 # Status and operation type are currently only used for 'scale', but NOT for 'terminate' sub-operations.
4345 def _add_suboperation(
4353 mapped_primitive_params
,
4354 operationState
=None,
4355 detailed_status
=None,
4358 RO_scaling_info
=None,
4361 return self
.SUBOPERATION_STATUS_NOT_FOUND
4362 # Get the "_admin.operations" list, if it exists
4363 db_nslcmop_admin
= db_nslcmop
.get("_admin", {})
4364 op_list
= db_nslcmop_admin
.get("operations")
4365 # Create or append to the "_admin.operations" list
4367 "member_vnf_index": vnf_index
,
4369 "vdu_count_index": vdu_count_index
,
4370 "primitive": primitive
,
4371 "primitive_params": mapped_primitive_params
,
4374 new_op
["operationState"] = operationState
4376 new_op
["detailed-status"] = detailed_status
4378 new_op
["lcmOperationType"] = operationType
4380 new_op
["RO_nsr_id"] = RO_nsr_id
4382 new_op
["RO_scaling_info"] = RO_scaling_info
4384 # No existing operations, create key 'operations' with current operation as first list element
4385 db_nslcmop_admin
.update({"operations": [new_op
]})
4386 op_list
= db_nslcmop_admin
.get("operations")
4388 # Existing operations, append operation to list
4389 op_list
.append(new_op
)
4391 db_nslcmop_update
= {"_admin.operations": op_list
}
4392 self
.update_db_2("nslcmops", db_nslcmop
["_id"], db_nslcmop_update
)
4393 op_index
= len(op_list
) - 1
4396 # Helper methods for scale() sub-operations
4398 # pre-scale/post-scale:
4399 # Check for 3 different cases:
4400 # a. New: First time execution, return SUBOPERATION_STATUS_NEW
4401 # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
4402 # c. retry: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
4403 def _check_or_add_scale_suboperation(
4407 vnf_config_primitive
,
4411 RO_scaling_info
=None,
4413 # Find this sub-operation
4414 if RO_nsr_id
and RO_scaling_info
:
4415 operationType
= "SCALE-RO"
4417 "member_vnf_index": vnf_index
,
4418 "RO_nsr_id": RO_nsr_id
,
4419 "RO_scaling_info": RO_scaling_info
,
4423 "member_vnf_index": vnf_index
,
4424 "primitive": vnf_config_primitive
,
4425 "primitive_params": primitive_params
,
4426 "lcmOperationType": operationType
,
4428 op_index
= self
._find
_suboperation
(db_nslcmop
, match
)
4429 if op_index
== self
.SUBOPERATION_STATUS_NOT_FOUND
:
4430 # a. New sub-operation
4431 # The sub-operation does not exist, add it.
4432 # _ns_execute_primitive() will be called from scale() as usual, with non-modified arguments
4433 # The following parameters are set to None for all kind of scaling:
4435 vdu_count_index
= None
4437 if RO_nsr_id
and RO_scaling_info
:
4438 vnf_config_primitive
= None
4439 primitive_params
= None
4442 RO_scaling_info
= None
4443 # Initial status for sub-operation
4444 operationState
= "PROCESSING"
4445 detailed_status
= "In progress"
4446 # Add sub-operation for pre/post-scaling (zero or more operations)
4447 self
._add
_suboperation
(
4453 vnf_config_primitive
,
4461 return self
.SUBOPERATION_STATUS_NEW
4463 # Return either SUBOPERATION_STATUS_SKIP (operationState == 'COMPLETED'),
4464 # or op_index (operationState != 'COMPLETED')
4465 return self
._retry
_or
_skip
_suboperation
(db_nslcmop
, op_index
)
4467 # Function to return execution_environment id
4469 def _get_ee_id(self
, vnf_index
, vdu_id
, vca_deployed_list
):
4470 # TODO vdu_index_count
4471 for vca
in vca_deployed_list
:
4472 if vca
["member-vnf-index"] == vnf_index
and vca
["vdu_id"] == vdu_id
:
4473 return vca
.get("ee_id")
4475 async def destroy_N2VC(
4483 exec_primitives
=True,
4488 Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
4489 :param logging_text:
4491 :param vca_deployed: Dictionary of deployment info at db_nsr._admin.depoloyed.VCA.<INDEX>
4492 :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
4493 :param vca_index: index in the database _admin.deployed.VCA
4494 :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
4495 :param exec_primitives: False to do not execute terminate primitives, because the config is not completed or has
4496 not executed properly
4497 :param scaling_in: True destroys the application, False destroys the model
4498 :return: None or exception
4503 + " vca_index: {}, vca_deployed: {}, config_descriptor: {}, destroy_ee: {}".format(
4504 vca_index
, vca_deployed
, config_descriptor
, destroy_ee
4508 vca_type
= vca_deployed
.get("type", "lxc_proxy_charm")
4510 # execute terminate_primitives
4512 terminate_primitives
= get_ee_sorted_terminate_config_primitive_list(
4513 config_descriptor
.get("terminate-config-primitive"),
4514 vca_deployed
.get("ee_descriptor_id"),
4516 vdu_id
= vca_deployed
.get("vdu_id")
4517 vdu_count_index
= vca_deployed
.get("vdu_count_index")
4518 vdu_name
= vca_deployed
.get("vdu_name")
4519 vnf_index
= vca_deployed
.get("member-vnf-index")
4520 if terminate_primitives
and vca_deployed
.get("needed_terminate"):
4521 for seq
in terminate_primitives
:
4522 # For each sequence in list, get primitive and call _ns_execute_primitive()
4523 step
= "Calling terminate action for vnf_member_index={} primitive={}".format(
4524 vnf_index
, seq
.get("name")
4526 self
.logger
.debug(logging_text
+ step
)
4527 # Create the primitive for each sequence, i.e. "primitive": "touch"
4528 primitive
= seq
.get("name")
4529 mapped_primitive_params
= self
._get
_terminate
_primitive
_params
(
4534 self
._add
_suboperation
(
4541 mapped_primitive_params
,
4543 # Sub-operations: Call _ns_execute_primitive() instead of action()
4545 result
, result_detail
= await self
._ns
_execute
_primitive
(
4546 vca_deployed
["ee_id"],
4548 mapped_primitive_params
,
4552 except LcmException
:
4553 # this happens when VCA is not deployed. In this case it is not needed to terminate
4555 result_ok
= ["COMPLETED", "PARTIALLY_COMPLETED"]
4556 if result
not in result_ok
:
4558 "terminate_primitive {} for vnf_member_index={} fails with "
4559 "error {}".format(seq
.get("name"), vnf_index
, result_detail
)
4561 # set that this VCA do not need terminated
4562 db_update_entry
= "_admin.deployed.VCA.{}.needed_terminate".format(
4566 "nsrs", db_nslcmop
["nsInstanceId"], {db_update_entry
: False}
4569 # Delete Prometheus Jobs if any
4570 # This uses NSR_ID, so it will destroy any jobs under this index
4571 self
.db
.del_list("prometheus_jobs", {"nsr_id": db_nslcmop
["nsInstanceId"]})
4574 await self
.vca_map
[vca_type
].delete_execution_environment(
4575 vca_deployed
["ee_id"],
4576 scaling_in
=scaling_in
,
4581 async def _delete_all_N2VC(self
, db_nsr
: dict, vca_id
: str = None):
4582 self
._write
_all
_config
_status
(db_nsr
=db_nsr
, status
="TERMINATING")
4583 namespace
= "." + db_nsr
["_id"]
4585 await self
.n2vc
.delete_namespace(
4586 namespace
=namespace
,
4587 total_timeout
=self
.timeout
.charm_delete
,
4590 except N2VCNotFound
: # already deleted. Skip
4592 self
._write
_all
_config
_status
(db_nsr
=db_nsr
, status
="DELETED")
4594 async def terminate(self
, nsr_id
, nslcmop_id
):
4595 # Try to lock HA task here
4596 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
4597 if not task_is_locked_by_me
:
4600 logging_text
= "Task ns={} terminate={} ".format(nsr_id
, nslcmop_id
)
4601 self
.logger
.debug(logging_text
+ "Enter")
4602 timeout_ns_terminate
= self
.timeout
.ns_terminate
4605 operation_params
= None
4607 error_list
= [] # annotates all failed error messages
4608 db_nslcmop_update
= {}
4609 autoremove
= False # autoremove after terminated
4610 tasks_dict_info
= {}
4613 "Stage 1/3: Preparing task.",
4614 "Waiting for previous operations to terminate.",
4617 # ^ contains [stage, step, VIM-status]
4619 # wait for any previous tasks in process
4620 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
4622 stage
[1] = "Getting nslcmop={} from db.".format(nslcmop_id
)
4623 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
4624 operation_params
= db_nslcmop
.get("operationParams") or {}
4625 if operation_params
.get("timeout_ns_terminate"):
4626 timeout_ns_terminate
= operation_params
["timeout_ns_terminate"]
4627 stage
[1] = "Getting nsr={} from db.".format(nsr_id
)
4628 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
4630 db_nsr_update
["operational-status"] = "terminating"
4631 db_nsr_update
["config-status"] = "terminating"
4632 self
._write
_ns
_status
(
4634 ns_state
="TERMINATING",
4635 current_operation
="TERMINATING",
4636 current_operation_id
=nslcmop_id
,
4637 other_update
=db_nsr_update
,
4639 self
._write
_op
_status
(op_id
=nslcmop_id
, queuePosition
=0, stage
=stage
)
4640 nsr_deployed
= deepcopy(db_nsr
["_admin"].get("deployed")) or {}
4641 if db_nsr
["_admin"]["nsState"] == "NOT_INSTANTIATED":
4644 stage
[1] = "Getting vnf descriptors from db."
4645 db_vnfrs_list
= self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
})
4647 db_vnfr
["member-vnf-index-ref"]: db_vnfr
for db_vnfr
in db_vnfrs_list
4649 db_vnfds_from_id
= {}
4650 db_vnfds_from_member_index
= {}
4652 for vnfr
in db_vnfrs_list
:
4653 vnfd_id
= vnfr
["vnfd-id"]
4654 if vnfd_id
not in db_vnfds_from_id
:
4655 vnfd
= self
.db
.get_one("vnfds", {"_id": vnfd_id
})
4656 db_vnfds_from_id
[vnfd_id
] = vnfd
4657 db_vnfds_from_member_index
[
4658 vnfr
["member-vnf-index-ref"]
4659 ] = db_vnfds_from_id
[vnfd_id
]
4661 # Destroy individual execution environments when there are terminating primitives.
4662 # Rest of EE will be deleted at once
4663 # TODO - check before calling _destroy_N2VC
4664 # if not operation_params.get("skip_terminate_primitives"):#
4665 # or not vca.get("needed_terminate"):
4666 stage
[0] = "Stage 2/3 execute terminating primitives."
4667 self
.logger
.debug(logging_text
+ stage
[0])
4668 stage
[1] = "Looking execution environment that needs terminate."
4669 self
.logger
.debug(logging_text
+ stage
[1])
4671 for vca_index
, vca
in enumerate(get_iterable(nsr_deployed
, "VCA")):
4672 config_descriptor
= None
4673 vca_member_vnf_index
= vca
.get("member-vnf-index")
4674 vca_id
= self
.get_vca_id(
4675 db_vnfrs_dict
.get(vca_member_vnf_index
)
4676 if vca_member_vnf_index
4680 if not vca
or not vca
.get("ee_id"):
4682 if not vca
.get("member-vnf-index"):
4684 config_descriptor
= db_nsr
.get("ns-configuration")
4685 elif vca
.get("vdu_id"):
4686 db_vnfd
= db_vnfds_from_member_index
[vca
["member-vnf-index"]]
4687 config_descriptor
= get_configuration(db_vnfd
, vca
.get("vdu_id"))
4688 elif vca
.get("kdu_name"):
4689 db_vnfd
= db_vnfds_from_member_index
[vca
["member-vnf-index"]]
4690 config_descriptor
= get_configuration(db_vnfd
, vca
.get("kdu_name"))
4692 db_vnfd
= db_vnfds_from_member_index
[vca
["member-vnf-index"]]
4693 config_descriptor
= get_configuration(db_vnfd
, db_vnfd
["id"])
4694 vca_type
= vca
.get("type")
4695 exec_terminate_primitives
= not operation_params
.get(
4696 "skip_terminate_primitives"
4697 ) and vca
.get("needed_terminate")
4698 # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
4699 # pending native charms
4701 True if vca_type
in ("helm", "helm-v3", "native_charm") else False
4703 # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
4704 # vca_index, vca.get("ee_id"), vca_type, destroy_ee))
4705 task
= asyncio
.ensure_future(
4713 exec_terminate_primitives
,
4717 tasks_dict_info
[task
] = "Terminating VCA {}".format(vca
.get("ee_id"))
4719 # wait for pending tasks of terminate primitives
4723 + "Waiting for tasks {}".format(list(tasks_dict_info
.keys()))
4725 error_list
= await self
._wait
_for
_tasks
(
4728 min(self
.timeout
.charm_delete
, timeout_ns_terminate
),
4732 tasks_dict_info
.clear()
4734 return # raise LcmException("; ".join(error_list))
4736 # remove All execution environments at once
4737 stage
[0] = "Stage 3/3 delete all."
4739 if nsr_deployed
.get("VCA"):
4740 stage
[1] = "Deleting all execution environments."
4741 self
.logger
.debug(logging_text
+ stage
[1])
4742 vca_id
= self
.get_vca_id({}, db_nsr
)
4743 task_delete_ee
= asyncio
.ensure_future(
4745 self
._delete
_all
_N
2VC
(db_nsr
=db_nsr
, vca_id
=vca_id
),
4746 timeout
=self
.timeout
.charm_delete
,
4749 # task_delete_ee = asyncio.ensure_future(self.n2vc.delete_namespace(namespace="." + nsr_id))
4750 tasks_dict_info
[task_delete_ee
] = "Terminating all VCA"
4752 # Delete Namespace and Certificates if necessary
4753 if check_helm_ee_in_ns(list(db_vnfds_from_member_index
.values())):
4754 await self
.vca_map
["helm-v3"].delete_tls_certificate(
4755 namespace
=db_nslcmop
["nsInstanceId"],
4756 certificate_name
=self
.EE_TLS_NAME
,
4758 await self
.vca_map
["helm-v3"].delete_namespace(
4759 namespace
=db_nslcmop
["nsInstanceId"],
4762 # Delete from k8scluster
4763 stage
[1] = "Deleting KDUs."
4764 self
.logger
.debug(logging_text
+ stage
[1])
4765 # print(nsr_deployed)
4766 for kdu
in get_iterable(nsr_deployed
, "K8s"):
4767 if not kdu
or not kdu
.get("kdu-instance"):
4769 kdu_instance
= kdu
.get("kdu-instance")
4770 if kdu
.get("k8scluster-type") in self
.k8scluster_map
:
4771 # TODO: Uninstall kdu instances taking into account they could be deployed in different VIMs
4772 vca_id
= self
.get_vca_id({}, db_nsr
)
4773 task_delete_kdu_instance
= asyncio
.ensure_future(
4774 self
.k8scluster_map
[kdu
["k8scluster-type"]].uninstall(
4775 cluster_uuid
=kdu
.get("k8scluster-uuid"),
4776 kdu_instance
=kdu_instance
,
4778 namespace
=kdu
.get("namespace"),
4784 + "Unknown k8s deployment type {}".format(
4785 kdu
.get("k8scluster-type")
4790 task_delete_kdu_instance
4791 ] = "Terminating KDU '{}'".format(kdu
.get("kdu-name"))
4794 stage
[1] = "Deleting ns from VIM."
4795 if self
.ro_config
.ng
:
4796 task_delete_ro
= asyncio
.ensure_future(
4797 self
._terminate
_ng
_ro
(
4798 logging_text
, nsr_deployed
, nsr_id
, nslcmop_id
, stage
4801 tasks_dict_info
[task_delete_ro
] = "Removing deployment from VIM"
4803 # rest of staff will be done at finally
4806 ROclient
.ROClientException
,
4811 self
.logger
.error(logging_text
+ "Exit Exception {}".format(e
))
4813 except asyncio
.CancelledError
:
4815 logging_text
+ "Cancelled Exception while '{}'".format(stage
[1])
4817 exc
= "Operation was cancelled"
4818 except Exception as e
:
4819 exc
= traceback
.format_exc()
4820 self
.logger
.critical(
4821 logging_text
+ "Exit Exception while '{}': {}".format(stage
[1], e
),
4826 error_list
.append(str(exc
))
4828 # wait for pending tasks
4830 stage
[1] = "Waiting for terminate pending tasks."
4831 self
.logger
.debug(logging_text
+ stage
[1])
4832 error_list
+= await self
._wait
_for
_tasks
(
4835 timeout_ns_terminate
,
4839 stage
[1] = stage
[2] = ""
4840 except asyncio
.CancelledError
:
4841 error_list
.append("Cancelled")
4842 # TODO cancell all tasks
4843 except Exception as exc
:
4844 error_list
.append(str(exc
))
4845 # update status at database
4847 error_detail
= "; ".join(error_list
)
4848 # self.logger.error(logging_text + error_detail)
4849 error_description_nslcmop
= "{} Detail: {}".format(
4850 stage
[0], error_detail
4852 error_description_nsr
= "Operation: TERMINATING.{}, {}.".format(
4853 nslcmop_id
, stage
[0]
4856 db_nsr_update
["operational-status"] = "failed"
4857 db_nsr_update
["detailed-status"] = (
4858 error_description_nsr
+ " Detail: " + error_detail
4860 db_nslcmop_update
["detailed-status"] = error_detail
4861 nslcmop_operation_state
= "FAILED"
4865 error_description_nsr
= error_description_nslcmop
= None
4866 ns_state
= "NOT_INSTANTIATED"
4867 db_nsr_update
["operational-status"] = "terminated"
4868 db_nsr_update
["detailed-status"] = "Done"
4869 db_nsr_update
["_admin.nsState"] = "NOT_INSTANTIATED"
4870 db_nslcmop_update
["detailed-status"] = "Done"
4871 nslcmop_operation_state
= "COMPLETED"
4874 self
._write
_ns
_status
(
4877 current_operation
="IDLE",
4878 current_operation_id
=None,
4879 error_description
=error_description_nsr
,
4880 error_detail
=error_detail
,
4881 other_update
=db_nsr_update
,
4883 self
._write
_op
_status
(
4886 error_message
=error_description_nslcmop
,
4887 operation_state
=nslcmop_operation_state
,
4888 other_update
=db_nslcmop_update
,
4890 if ns_state
== "NOT_INSTANTIATED":
4894 {"nsr-id-ref": nsr_id
},
4895 {"_admin.nsState": "NOT_INSTANTIATED"},
4897 except DbException
as e
:
4900 + "Error writing VNFR status for nsr-id-ref: {} -> {}".format(
4904 if operation_params
:
4905 autoremove
= operation_params
.get("autoremove", False)
4906 if nslcmop_operation_state
:
4908 await self
.msg
.aiowrite(
4913 "nslcmop_id": nslcmop_id
,
4914 "operationState": nslcmop_operation_state
,
4915 "autoremove": autoremove
,
4918 except Exception as e
:
4920 logging_text
+ "kafka_write notification Exception {}".format(e
)
4922 self
.logger
.debug(f
"Deleting alerts: ns_id={nsr_id}")
4923 self
.db
.del_list("alerts", {"tags.ns_id": nsr_id
})
4925 self
.logger
.debug(logging_text
+ "Exit")
4926 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_terminate")
4928 async def _wait_for_tasks(
4929 self
, logging_text
, created_tasks_info
, timeout
, stage
, nslcmop_id
, nsr_id
=None
4932 error_detail_list
= []
4934 pending_tasks
= list(created_tasks_info
.keys())
4935 num_tasks
= len(pending_tasks
)
4937 stage
[1] = "{}/{}.".format(num_done
, num_tasks
)
4938 self
._write
_op
_status
(nslcmop_id
, stage
)
4939 while pending_tasks
:
4941 _timeout
= timeout
+ time_start
- time()
4942 done
, pending_tasks
= await asyncio
.wait(
4943 pending_tasks
, timeout
=_timeout
, return_when
=asyncio
.FIRST_COMPLETED
4945 num_done
+= len(done
)
4946 if not done
: # Timeout
4947 for task
in pending_tasks
:
4948 new_error
= created_tasks_info
[task
] + ": Timeout"
4949 error_detail_list
.append(new_error
)
4950 error_list
.append(new_error
)
4953 if task
.cancelled():
4956 exc
= task
.exception()
4958 if isinstance(exc
, asyncio
.TimeoutError
):
4960 new_error
= created_tasks_info
[task
] + ": {}".format(exc
)
4961 error_list
.append(created_tasks_info
[task
])
4962 error_detail_list
.append(new_error
)
4969 ROclient
.ROClientException
,
4975 self
.logger
.error(logging_text
+ new_error
)
4977 exc_traceback
= "".join(
4978 traceback
.format_exception(None, exc
, exc
.__traceback
__)
4982 + created_tasks_info
[task
]
4988 logging_text
+ created_tasks_info
[task
] + ": Done"
4990 stage
[1] = "{}/{}.".format(num_done
, num_tasks
)
4992 stage
[1] += " Errors: " + ". ".join(error_detail_list
) + "."
4993 if nsr_id
: # update also nsr
4998 "errorDescription": "Error at: " + ", ".join(error_list
),
4999 "errorDetail": ". ".join(error_detail_list
),
5002 self
._write
_op
_status
(nslcmop_id
, stage
)
5003 return error_detail_list
5006 def _map_primitive_params(primitive_desc
, params
, instantiation_params
):
5008 Generates the params to be provided to charm before executing primitive. If user does not provide a parameter,
5009 The default-value is used. If it is between < > it look for a value at instantiation_params
5010 :param primitive_desc: portion of VNFD/NSD that describes primitive
5011 :param params: Params provided by user
5012 :param instantiation_params: Instantiation params provided by user
5013 :return: a dictionary with the calculated params
5015 calculated_params
= {}
5016 for parameter
in primitive_desc
.get("parameter", ()):
5017 param_name
= parameter
["name"]
5018 if param_name
in params
:
5019 calculated_params
[param_name
] = params
[param_name
]
5020 elif "default-value" in parameter
or "value" in parameter
:
5021 if "value" in parameter
:
5022 calculated_params
[param_name
] = parameter
["value"]
5024 calculated_params
[param_name
] = parameter
["default-value"]
5026 isinstance(calculated_params
[param_name
], str)
5027 and calculated_params
[param_name
].startswith("<")
5028 and calculated_params
[param_name
].endswith(">")
5030 if calculated_params
[param_name
][1:-1] in instantiation_params
:
5031 calculated_params
[param_name
] = instantiation_params
[
5032 calculated_params
[param_name
][1:-1]
5036 "Parameter {} needed to execute primitive {} not provided".format(
5037 calculated_params
[param_name
], primitive_desc
["name"]
5042 "Parameter {} needed to execute primitive {} not provided".format(
5043 param_name
, primitive_desc
["name"]
5047 if isinstance(calculated_params
[param_name
], (dict, list, tuple)):
5048 calculated_params
[param_name
] = yaml
.safe_dump(
5049 calculated_params
[param_name
], default_flow_style
=True, width
=256
5051 elif isinstance(calculated_params
[param_name
], str) and calculated_params
[
5053 ].startswith("!!yaml "):
5054 calculated_params
[param_name
] = calculated_params
[param_name
][7:]
5055 if parameter
.get("data-type") == "INTEGER":
5057 calculated_params
[param_name
] = int(calculated_params
[param_name
])
5058 except ValueError: # error converting string to int
5060 "Parameter {} of primitive {} must be integer".format(
5061 param_name
, primitive_desc
["name"]
5064 elif parameter
.get("data-type") == "BOOLEAN":
5065 calculated_params
[param_name
] = not (
5066 (str(calculated_params
[param_name
])).lower() == "false"
5069 # add always ns_config_info if primitive name is config
5070 if primitive_desc
["name"] == "config":
5071 if "ns_config_info" in instantiation_params
:
5072 calculated_params
["ns_config_info"] = instantiation_params
[
5075 return calculated_params
5077 def _look_for_deployed_vca(
5084 ee_descriptor_id
=None,
5086 # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
5087 for vca
in deployed_vca
:
5090 if member_vnf_index
!= vca
["member-vnf-index"] or vdu_id
!= vca
["vdu_id"]:
5093 vdu_count_index
is not None
5094 and vdu_count_index
!= vca
["vdu_count_index"]
5097 if kdu_name
and kdu_name
!= vca
["kdu_name"]:
5099 if ee_descriptor_id
and ee_descriptor_id
!= vca
["ee_descriptor_id"]:
5103 # vca_deployed not found
5105 "charm for member_vnf_index={} vdu_id={}.{} kdu_name={} execution-environment-list.id={}"
5106 " is not deployed".format(
5115 ee_id
= vca
.get("ee_id")
5117 "type", "lxc_proxy_charm"
5118 ) # default value for backward compatibility - proxy charm
5121 "charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
5122 "execution environment".format(
5123 member_vnf_index
, vdu_id
, kdu_name
, vdu_count_index
5126 return ee_id
, vca_type
5128 async def _ns_execute_primitive(
5134 retries_interval
=30,
5141 if primitive
== "config":
5142 primitive_params
= {"params": primitive_params
}
5144 vca_type
= vca_type
or "lxc_proxy_charm"
5148 output
= await asyncio
.wait_for(
5149 self
.vca_map
[vca_type
].exec_primitive(
5151 primitive_name
=primitive
,
5152 params_dict
=primitive_params
,
5153 progress_timeout
=self
.timeout
.progress_primitive
,
5154 total_timeout
=self
.timeout
.primitive
,
5159 timeout
=timeout
or self
.timeout
.primitive
,
5163 except asyncio
.CancelledError
:
5165 except Exception as e
:
5169 "Error executing action {} on {} -> {}".format(
5174 await asyncio
.sleep(retries_interval
)
5176 if isinstance(e
, asyncio
.TimeoutError
):
5178 message
="Timed out waiting for action to complete"
5180 return "FAILED", getattr(e
, "message", repr(e
))
5182 return "COMPLETED", output
5184 except (LcmException
, asyncio
.CancelledError
):
5186 except Exception as e
:
5187 return "FAIL", "Error executing action {}: {}".format(primitive
, e
)
5189 async def vca_status_refresh(self
, nsr_id
, nslcmop_id
):
5191 Updating the vca_status with latest juju information in nsrs record
5192 :param: nsr_id: Id of the nsr
5193 :param: nslcmop_id: Id of the nslcmop
5197 self
.logger
.debug("Task ns={} action={} Enter".format(nsr_id
, nslcmop_id
))
5198 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
5199 vca_id
= self
.get_vca_id({}, db_nsr
)
5200 if db_nsr
["_admin"]["deployed"]["K8s"]:
5201 for _
, k8s
in enumerate(db_nsr
["_admin"]["deployed"]["K8s"]):
5202 cluster_uuid
, kdu_instance
, cluster_type
= (
5203 k8s
["k8scluster-uuid"],
5204 k8s
["kdu-instance"],
5205 k8s
["k8scluster-type"],
5207 await self
._on
_update
_k
8s
_db
(
5208 cluster_uuid
=cluster_uuid
,
5209 kdu_instance
=kdu_instance
,
5210 filter={"_id": nsr_id
},
5212 cluster_type
=cluster_type
,
5215 for vca_index
, _
in enumerate(db_nsr
["_admin"]["deployed"]["VCA"]):
5216 table
, filter = "nsrs", {"_id": nsr_id
}
5217 path
= "_admin.deployed.VCA.{}.".format(vca_index
)
5218 await self
._on
_update
_n
2vc
_db
(table
, filter, path
, {})
5220 self
.logger
.debug("Task ns={} action={} Exit".format(nsr_id
, nslcmop_id
))
5221 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_vca_status_refresh")
5223 async def action(self
, nsr_id
, nslcmop_id
):
5224 # Try to lock HA task here
5225 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
5226 if not task_is_locked_by_me
:
5229 logging_text
= "Task ns={} action={} ".format(nsr_id
, nslcmop_id
)
5230 self
.logger
.debug(logging_text
+ "Enter")
5231 # get all needed from database
5235 db_nslcmop_update
= {}
5236 nslcmop_operation_state
= None
5237 error_description_nslcmop
= None
5241 # wait for any previous tasks in process
5242 step
= "Waiting for previous operations to terminate"
5243 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
5245 self
._write
_ns
_status
(
5248 current_operation
="RUNNING ACTION",
5249 current_operation_id
=nslcmop_id
,
5252 step
= "Getting information from database"
5253 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
5254 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
5255 if db_nslcmop
["operationParams"].get("primitive_params"):
5256 db_nslcmop
["operationParams"]["primitive_params"] = json
.loads(
5257 db_nslcmop
["operationParams"]["primitive_params"]
5260 nsr_deployed
= db_nsr
["_admin"].get("deployed")
5261 vnf_index
= db_nslcmop
["operationParams"].get("member_vnf_index")
5262 vdu_id
= db_nslcmop
["operationParams"].get("vdu_id")
5263 kdu_name
= db_nslcmop
["operationParams"].get("kdu_name")
5264 vdu_count_index
= db_nslcmop
["operationParams"].get("vdu_count_index")
5265 primitive
= db_nslcmop
["operationParams"]["primitive"]
5266 primitive_params
= db_nslcmop
["operationParams"]["primitive_params"]
5267 timeout_ns_action
= db_nslcmop
["operationParams"].get(
5268 "timeout_ns_action", self
.timeout
.primitive
5272 step
= "Getting vnfr from database"
5273 db_vnfr
= self
.db
.get_one(
5274 "vnfrs", {"member-vnf-index-ref": vnf_index
, "nsr-id-ref": nsr_id
}
5276 if db_vnfr
.get("kdur"):
5278 for kdur
in db_vnfr
["kdur"]:
5279 if kdur
.get("additionalParams"):
5280 kdur
["additionalParams"] = json
.loads(
5281 kdur
["additionalParams"]
5283 kdur_list
.append(kdur
)
5284 db_vnfr
["kdur"] = kdur_list
5285 step
= "Getting vnfd from database"
5286 db_vnfd
= self
.db
.get_one("vnfds", {"_id": db_vnfr
["vnfd-id"]})
5288 # Sync filesystem before running a primitive
5289 self
.fs
.sync(db_vnfr
["vnfd-id"])
5291 step
= "Getting nsd from database"
5292 db_nsd
= self
.db
.get_one("nsds", {"_id": db_nsr
["nsd-id"]})
5294 vca_id
= self
.get_vca_id(db_vnfr
, db_nsr
)
5295 # for backward compatibility
5296 if nsr_deployed
and isinstance(nsr_deployed
.get("VCA"), dict):
5297 nsr_deployed
["VCA"] = list(nsr_deployed
["VCA"].values())
5298 db_nsr_update
["_admin.deployed.VCA"] = nsr_deployed
["VCA"]
5299 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
5301 # look for primitive
5302 config_primitive_desc
= descriptor_configuration
= None
5304 descriptor_configuration
= get_configuration(db_vnfd
, vdu_id
)
5306 descriptor_configuration
= get_configuration(db_vnfd
, kdu_name
)
5308 descriptor_configuration
= get_configuration(db_vnfd
, db_vnfd
["id"])
5310 descriptor_configuration
= db_nsd
.get("ns-configuration")
5312 if descriptor_configuration
and descriptor_configuration
.get(
5315 for config_primitive
in descriptor_configuration
["config-primitive"]:
5316 if config_primitive
["name"] == primitive
:
5317 config_primitive_desc
= config_primitive
5320 if not config_primitive_desc
:
5321 if not (kdu_name
and primitive
in ("upgrade", "rollback", "status")):
5323 "Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".format(
5327 primitive_name
= primitive
5328 ee_descriptor_id
= None
5330 primitive_name
= config_primitive_desc
.get(
5331 "execution-environment-primitive", primitive
5333 ee_descriptor_id
= config_primitive_desc
.get(
5334 "execution-environment-ref"
5340 (x
for x
in db_vnfr
["vdur"] if x
["vdu-id-ref"] == vdu_id
), None
5342 desc_params
= parse_yaml_strings(vdur
.get("additionalParams"))
5345 (x
for x
in db_vnfr
["kdur"] if x
["kdu-name"] == kdu_name
), None
5347 desc_params
= parse_yaml_strings(kdur
.get("additionalParams"))
5349 desc_params
= parse_yaml_strings(
5350 db_vnfr
.get("additionalParamsForVnf")
5353 desc_params
= parse_yaml_strings(db_nsr
.get("additionalParamsForNs"))
5354 if kdu_name
and get_configuration(db_vnfd
, kdu_name
):
5355 kdu_configuration
= get_configuration(db_vnfd
, kdu_name
)
5357 for primitive
in kdu_configuration
.get("initial-config-primitive", []):
5358 actions
.add(primitive
["name"])
5359 for primitive
in kdu_configuration
.get("config-primitive", []):
5360 actions
.add(primitive
["name"])
5362 nsr_deployed
["K8s"],
5363 lambda kdu
: kdu_name
== kdu
["kdu-name"]
5364 and kdu
["member-vnf-index"] == vnf_index
,
5368 if primitive_name
in actions
5369 and kdu
["k8scluster-type"] not in ("helm-chart", "helm-chart-v3")
5373 # TODO check if ns is in a proper status
5375 primitive_name
in ("upgrade", "rollback", "status") or kdu_action
5377 # kdur and desc_params already set from before
5378 if primitive_params
:
5379 desc_params
.update(primitive_params
)
5380 # TODO Check if we will need something at vnf level
5381 for index
, kdu
in enumerate(get_iterable(nsr_deployed
, "K8s")):
5383 kdu_name
== kdu
["kdu-name"]
5384 and kdu
["member-vnf-index"] == vnf_index
5389 "KDU '{}' for vnf '{}' not deployed".format(kdu_name
, vnf_index
)
5392 if kdu
.get("k8scluster-type") not in self
.k8scluster_map
:
5393 msg
= "unknown k8scluster-type '{}'".format(
5394 kdu
.get("k8scluster-type")
5396 raise LcmException(msg
)
5399 "collection": "nsrs",
5400 "filter": {"_id": nsr_id
},
5401 "path": "_admin.deployed.K8s.{}".format(index
),
5405 + "Exec k8s {} on {}.{}".format(primitive_name
, vnf_index
, kdu_name
)
5407 step
= "Executing kdu {}".format(primitive_name
)
5408 if primitive_name
== "upgrade":
5409 if desc_params
.get("kdu_model"):
5410 kdu_model
= desc_params
.get("kdu_model")
5411 del desc_params
["kdu_model"]
5413 kdu_model
= kdu
.get("kdu-model")
5414 if kdu_model
.count("/") < 2: # helm chart is not embedded
5415 parts
= kdu_model
.split(sep
=":")
5417 kdu_model
= parts
[0]
5418 if desc_params
.get("kdu_atomic_upgrade"):
5419 atomic_upgrade
= desc_params
.get(
5420 "kdu_atomic_upgrade"
5421 ).lower() in ("yes", "true", "1")
5422 del desc_params
["kdu_atomic_upgrade"]
5424 atomic_upgrade
= True
5426 detailed_status
= await asyncio
.wait_for(
5427 self
.k8scluster_map
[kdu
["k8scluster-type"]].upgrade(
5428 cluster_uuid
=kdu
.get("k8scluster-uuid"),
5429 kdu_instance
=kdu
.get("kdu-instance"),
5430 atomic
=atomic_upgrade
,
5431 kdu_model
=kdu_model
,
5434 timeout
=timeout_ns_action
,
5436 timeout
=timeout_ns_action
+ 10,
5439 logging_text
+ " Upgrade of kdu {} done".format(detailed_status
)
5441 elif primitive_name
== "rollback":
5442 detailed_status
= await asyncio
.wait_for(
5443 self
.k8scluster_map
[kdu
["k8scluster-type"]].rollback(
5444 cluster_uuid
=kdu
.get("k8scluster-uuid"),
5445 kdu_instance
=kdu
.get("kdu-instance"),
5448 timeout
=timeout_ns_action
,
5450 elif primitive_name
== "status":
5451 detailed_status
= await asyncio
.wait_for(
5452 self
.k8scluster_map
[kdu
["k8scluster-type"]].status_kdu(
5453 cluster_uuid
=kdu
.get("k8scluster-uuid"),
5454 kdu_instance
=kdu
.get("kdu-instance"),
5457 timeout
=timeout_ns_action
,
5460 kdu_instance
= kdu
.get("kdu-instance") or "{}-{}".format(
5461 kdu
["kdu-name"], nsr_id
5463 params
= self
._map
_primitive
_params
(
5464 config_primitive_desc
, primitive_params
, desc_params
5467 detailed_status
= await asyncio
.wait_for(
5468 self
.k8scluster_map
[kdu
["k8scluster-type"]].exec_primitive(
5469 cluster_uuid
=kdu
.get("k8scluster-uuid"),
5470 kdu_instance
=kdu_instance
,
5471 primitive_name
=primitive_name
,
5474 timeout
=timeout_ns_action
,
5477 timeout
=timeout_ns_action
,
5481 nslcmop_operation_state
= "COMPLETED"
5483 detailed_status
= ""
5484 nslcmop_operation_state
= "FAILED"
5486 ee_id
, vca_type
= self
._look
_for
_deployed
_vca
(
5487 nsr_deployed
["VCA"],
5488 member_vnf_index
=vnf_index
,
5490 vdu_count_index
=vdu_count_index
,
5491 ee_descriptor_id
=ee_descriptor_id
,
5493 for vca_index
, vca_deployed
in enumerate(
5494 db_nsr
["_admin"]["deployed"]["VCA"]
5496 if vca_deployed
.get("member-vnf-index") == vnf_index
:
5498 "collection": "nsrs",
5499 "filter": {"_id": nsr_id
},
5500 "path": "_admin.deployed.VCA.{}.".format(vca_index
),
5504 nslcmop_operation_state
,
5506 ) = await self
._ns
_execute
_primitive
(
5508 primitive
=primitive_name
,
5509 primitive_params
=self
._map
_primitive
_params
(
5510 config_primitive_desc
, primitive_params
, desc_params
5512 timeout
=timeout_ns_action
,
5518 db_nslcmop_update
["detailed-status"] = detailed_status
5519 error_description_nslcmop
= (
5520 detailed_status
if nslcmop_operation_state
== "FAILED" else ""
5524 + "Done with result {} {}".format(
5525 nslcmop_operation_state
, detailed_status
5528 return # database update is called inside finally
5530 except (DbException
, LcmException
, N2VCException
, K8sException
) as e
:
5531 self
.logger
.error(logging_text
+ "Exit Exception {}".format(e
))
5533 except asyncio
.CancelledError
:
5535 logging_text
+ "Cancelled Exception while '{}'".format(step
)
5537 exc
= "Operation was cancelled"
5538 except asyncio
.TimeoutError
:
5539 self
.logger
.error(logging_text
+ "Timeout while '{}'".format(step
))
5541 except Exception as e
:
5542 exc
= traceback
.format_exc()
5543 self
.logger
.critical(
5544 logging_text
+ "Exit Exception {} {}".format(type(e
).__name
__, e
),
5553 ) = error_description_nslcmop
= "FAILED {}: {}".format(step
, exc
)
5554 nslcmop_operation_state
= "FAILED"
5556 self
._write
_ns
_status
(
5560 ], # TODO check if degraded. For the moment use previous status
5561 current_operation
="IDLE",
5562 current_operation_id
=None,
5563 # error_description=error_description_nsr,
5564 # error_detail=error_detail,
5565 other_update
=db_nsr_update
,
5568 self
._write
_op
_status
(
5571 error_message
=error_description_nslcmop
,
5572 operation_state
=nslcmop_operation_state
,
5573 other_update
=db_nslcmop_update
,
5576 if nslcmop_operation_state
:
5578 await self
.msg
.aiowrite(
5583 "nslcmop_id": nslcmop_id
,
5584 "operationState": nslcmop_operation_state
,
5587 except Exception as e
:
5589 logging_text
+ "kafka_write notification Exception {}".format(e
)
5591 self
.logger
.debug(logging_text
+ "Exit")
5592 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_action")
5593 return nslcmop_operation_state
, detailed_status
5595 async def terminate_vdus(
5596 self
, db_vnfr
, member_vnf_index
, db_nsr
, update_db_nslcmops
, stage
, logging_text
5598 """This method terminates VDUs
5601 db_vnfr: VNF instance record
5602 member_vnf_index: VNF index to identify the VDUs to be removed
5603 db_nsr: NS instance record
5604 update_db_nslcmops: Nslcmop update record
5606 vca_scaling_info
= []
5607 scaling_info
= {"scaling_group_name": "vdu_autoscale", "vdu": [], "kdu": []}
5608 scaling_info
["scaling_direction"] = "IN"
5609 scaling_info
["vdu-delete"] = {}
5610 scaling_info
["kdu-delete"] = {}
5611 db_vdur
= db_vnfr
.get("vdur")
5612 vdur_list
= copy(db_vdur
)
5614 for index
, vdu
in enumerate(vdur_list
):
5615 vca_scaling_info
.append(
5617 "osm_vdu_id": vdu
["vdu-id-ref"],
5618 "member-vnf-index": member_vnf_index
,
5620 "vdu_index": count_index
,
5623 scaling_info
["vdu-delete"][vdu
["vdu-id-ref"]] = count_index
5624 scaling_info
["vdu"].append(
5626 "name": vdu
.get("name") or vdu
.get("vdu-name"),
5627 "vdu_id": vdu
["vdu-id-ref"],
5631 for interface
in vdu
["interfaces"]:
5632 scaling_info
["vdu"][index
]["interface"].append(
5634 "name": interface
["name"],
5635 "ip_address": interface
["ip-address"],
5636 "mac_address": interface
.get("mac-address"),
5639 self
.logger
.info("NS update scaling info{}".format(scaling_info
))
5640 stage
[2] = "Terminating VDUs"
5641 if scaling_info
.get("vdu-delete"):
5642 # scale_process = "RO"
5643 if self
.ro_config
.ng
:
5644 await self
._scale
_ng
_ro
(
5653 async def remove_vnf(self
, nsr_id
, nslcmop_id
, vnf_instance_id
):
5654 """This method is to Remove VNF instances from NS.
5657 nsr_id: NS instance id
5658 nslcmop_id: nslcmop id of update
5659 vnf_instance_id: id of the VNF instance to be removed
5662 result: (str, str) COMPLETED/FAILED, details
5666 logging_text
= "Task ns={} update ".format(nsr_id
)
5667 check_vnfr_count
= len(self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
}))
5668 self
.logger
.info("check_vnfr_count {}".format(check_vnfr_count
))
5669 if check_vnfr_count
> 1:
5670 stage
= ["", "", ""]
5671 step
= "Getting nslcmop from database"
5673 step
+ " after having waited for previous tasks to be completed"
5675 # db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5676 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
5677 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnf_instance_id
})
5678 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
5679 """ db_vnfr = self.db.get_one(
5680 "vnfrs", {"member-vnf-index-ref": member_vnf_index, "nsr-id-ref": nsr_id}) """
5682 update_db_nslcmops
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
5683 await self
.terminate_vdus(
5692 constituent_vnfr
= db_nsr
.get("constituent-vnfr-ref")
5693 constituent_vnfr
.remove(db_vnfr
.get("_id"))
5694 db_nsr_update
["constituent-vnfr-ref"] = db_nsr
.get(
5695 "constituent-vnfr-ref"
5697 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
5698 self
.db
.del_one("vnfrs", {"_id": db_vnfr
.get("_id")})
5699 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
5700 return "COMPLETED", "Done"
5702 step
= "Terminate VNF Failed with"
5704 "{} Cannot terminate the last VNF in this NS.".format(
5708 except (LcmException
, asyncio
.CancelledError
):
5710 except Exception as e
:
5711 self
.logger
.debug("Error removing VNF {}".format(e
))
5712 return "FAILED", "Error removing VNF {}".format(e
)
5714 async def _ns_redeploy_vnf(
5722 """This method updates and redeploys VNF instances
5725 nsr_id: NS instance id
5726 nslcmop_id: nslcmop id
5727 db_vnfd: VNF descriptor
5728 db_vnfr: VNF instance record
5729 db_nsr: NS instance record
5732 result: (str, str) COMPLETED/FAILED, details
5736 stage
= ["", "", ""]
5737 logging_text
= "Task ns={} update ".format(nsr_id
)
5738 latest_vnfd_revision
= db_vnfd
["_admin"].get("revision")
5739 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
5741 # Terminate old VNF resources
5742 update_db_nslcmops
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
5743 await self
.terminate_vdus(
5752 # old_vnfd_id = db_vnfr["vnfd-id"]
5753 # new_db_vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
5754 new_db_vnfd
= db_vnfd
5755 # new_vnfd_ref = new_db_vnfd["id"]
5756 # new_vnfd_id = vnfd_id
5760 for cp
in new_db_vnfd
.get("ext-cpd", ()):
5762 "name": cp
.get("id"),
5763 "connection-point-id": cp
.get("int-cpd", {}).get("cpd"),
5764 "connection-point-vdu-id": cp
.get("int-cpd", {}).get("vdu-id"),
5767 new_vnfr_cp
.append(vnf_cp
)
5768 new_vdur
= update_db_nslcmops
["operationParams"]["newVdur"]
5769 # new_vdur = self._create_vdur_descriptor_from_vnfd(db_nsd, db_vnfd, old_db_vnfd, vnfd_id, db_nsr, member_vnf_index)
5770 # new_vnfr_update = {"vnfd-ref": new_vnfd_ref, "vnfd-id": new_vnfd_id, "connection-point": new_vnfr_cp, "vdur": new_vdur, "ip-address": ""}
5772 "revision": latest_vnfd_revision
,
5773 "connection-point": new_vnfr_cp
,
5777 self
.update_db_2("vnfrs", db_vnfr
["_id"], new_vnfr_update
)
5778 updated_db_vnfr
= self
.db
.get_one(
5780 {"member-vnf-index-ref": member_vnf_index
, "nsr-id-ref": nsr_id
},
5783 # Instantiate new VNF resources
5784 # update_db_nslcmops = self.db.get_one("nslcmops", {"_id": nslcmop_id})
5785 vca_scaling_info
= []
5786 scaling_info
= {"scaling_group_name": "vdu_autoscale", "vdu": [], "kdu": []}
5787 scaling_info
["scaling_direction"] = "OUT"
5788 scaling_info
["vdu-create"] = {}
5789 scaling_info
["kdu-create"] = {}
5790 vdud_instantiate_list
= db_vnfd
["vdu"]
5791 for index
, vdud
in enumerate(vdud_instantiate_list
):
5792 cloud_init_text
= self
._get
_vdu
_cloud
_init
_content
(vdud
, db_vnfd
)
5794 additional_params
= (
5795 self
._get
_vdu
_additional
_params
(updated_db_vnfr
, vdud
["id"])
5798 cloud_init_list
= []
5800 # TODO Information of its own ip is not available because db_vnfr is not updated.
5801 additional_params
["OSM"] = get_osm_params(
5802 updated_db_vnfr
, vdud
["id"], 1
5804 cloud_init_list
.append(
5805 self
._parse
_cloud
_init
(
5812 vca_scaling_info
.append(
5814 "osm_vdu_id": vdud
["id"],
5815 "member-vnf-index": member_vnf_index
,
5817 "vdu_index": count_index
,
5820 scaling_info
["vdu-create"][vdud
["id"]] = count_index
5821 if self
.ro_config
.ng
:
5823 "New Resources to be deployed: {}".format(scaling_info
)
5825 await self
._scale
_ng
_ro
(
5833 return "COMPLETED", "Done"
5834 except (LcmException
, asyncio
.CancelledError
):
5836 except Exception as e
:
5837 self
.logger
.debug("Error updating VNF {}".format(e
))
5838 return "FAILED", "Error updating VNF {}".format(e
)
5840 async def _ns_charm_upgrade(
5846 timeout
: float = None,
5848 """This method upgrade charms in VNF instances
5851 ee_id: Execution environment id
5852 path: Local path to the charm
5854 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
5855 timeout: (Float) Timeout for the ns update operation
5858 result: (str, str) COMPLETED/FAILED, details
5861 charm_type
= charm_type
or "lxc_proxy_charm"
5862 output
= await self
.vca_map
[charm_type
].upgrade_charm(
5866 charm_type
=charm_type
,
5867 timeout
=timeout
or self
.timeout
.ns_update
,
5871 return "COMPLETED", output
5873 except (LcmException
, asyncio
.CancelledError
):
5876 except Exception as e
:
5877 self
.logger
.debug("Error upgrading charm {}".format(path
))
5879 return "FAILED", "Error upgrading charm {}: {}".format(path
, e
)
5881 async def update(self
, nsr_id
, nslcmop_id
):
5882 """Update NS according to different update types
5884 This method performs upgrade of VNF instances then updates the revision
5885 number in VNF record
5888 nsr_id: Network service will be updated
5889 nslcmop_id: ns lcm operation id
5892 It may raise DbException, LcmException, N2VCException, K8sException
5895 # Try to lock HA task here
5896 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
5897 if not task_is_locked_by_me
:
5900 logging_text
= "Task ns={} update={} ".format(nsr_id
, nslcmop_id
)
5901 self
.logger
.debug(logging_text
+ "Enter")
5903 # Set the required variables to be filled up later
5905 db_nslcmop_update
= {}
5907 nslcmop_operation_state
= None
5909 error_description_nslcmop
= ""
5911 change_type
= "updated"
5912 detailed_status
= ""
5913 member_vnf_index
= None
5916 # wait for any previous tasks in process
5917 step
= "Waiting for previous operations to terminate"
5918 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
5919 self
._write
_ns
_status
(
5922 current_operation
="UPDATING",
5923 current_operation_id
=nslcmop_id
,
5926 step
= "Getting nslcmop from database"
5927 db_nslcmop
= self
.db
.get_one(
5928 "nslcmops", {"_id": nslcmop_id
}, fail_on_empty
=False
5930 update_type
= db_nslcmop
["operationParams"]["updateType"]
5932 step
= "Getting nsr from database"
5933 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
5934 old_operational_status
= db_nsr
["operational-status"]
5935 db_nsr_update
["operational-status"] = "updating"
5936 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
5937 nsr_deployed
= db_nsr
["_admin"].get("deployed")
5939 if update_type
== "CHANGE_VNFPKG":
5940 # Get the input parameters given through update request
5941 vnf_instance_id
= db_nslcmop
["operationParams"][
5942 "changeVnfPackageData"
5943 ].get("vnfInstanceId")
5945 vnfd_id
= db_nslcmop
["operationParams"]["changeVnfPackageData"].get(
5948 timeout_seconds
= db_nslcmop
["operationParams"].get("timeout_ns_update")
5950 step
= "Getting vnfr from database"
5951 db_vnfr
= self
.db
.get_one(
5952 "vnfrs", {"_id": vnf_instance_id
}, fail_on_empty
=False
5955 step
= "Getting vnfds from database"
5957 latest_vnfd
= self
.db
.get_one(
5958 "vnfds", {"_id": vnfd_id
}, fail_on_empty
=False
5960 latest_vnfd_revision
= latest_vnfd
["_admin"].get("revision")
5963 current_vnf_revision
= db_vnfr
.get("revision", 1)
5964 current_vnfd
= self
.db
.get_one(
5966 {"_id": vnfd_id
+ ":" + str(current_vnf_revision
)},
5967 fail_on_empty
=False,
5969 # Charm artifact paths will be filled up later
5971 current_charm_artifact_path
,
5972 target_charm_artifact_path
,
5973 charm_artifact_paths
,
5975 ) = ([], [], [], [])
5977 step
= "Checking if revision has changed in VNFD"
5978 if current_vnf_revision
!= latest_vnfd_revision
:
5979 change_type
= "policy_updated"
5981 # There is new revision of VNFD, update operation is required
5982 current_vnfd_path
= vnfd_id
+ ":" + str(current_vnf_revision
)
5983 latest_vnfd_path
= vnfd_id
+ ":" + str(latest_vnfd_revision
)
5985 step
= "Removing the VNFD packages if they exist in the local path"
5986 shutil
.rmtree(self
.fs
.path
+ current_vnfd_path
, ignore_errors
=True)
5987 shutil
.rmtree(self
.fs
.path
+ latest_vnfd_path
, ignore_errors
=True)
5989 step
= "Get the VNFD packages from FSMongo"
5990 self
.fs
.sync(from_path
=latest_vnfd_path
)
5991 self
.fs
.sync(from_path
=current_vnfd_path
)
5994 "Get the charm-type, charm-id, ee-id if there is deployed VCA"
5996 current_base_folder
= current_vnfd
["_admin"]["storage"]
5997 latest_base_folder
= latest_vnfd
["_admin"]["storage"]
5999 for vca_index
, vca_deployed
in enumerate(
6000 get_iterable(nsr_deployed
, "VCA")
6002 vnf_index
= db_vnfr
.get("member-vnf-index-ref")
6004 # Getting charm-id and charm-type
6005 if vca_deployed
.get("member-vnf-index") == vnf_index
:
6006 vca_id
= self
.get_vca_id(db_vnfr
, db_nsr
)
6007 vca_type
= vca_deployed
.get("type")
6008 vdu_count_index
= vca_deployed
.get("vdu_count_index")
6011 ee_id
= vca_deployed
.get("ee_id")
6013 step
= "Getting descriptor config"
6014 if current_vnfd
.get("kdu"):
6015 search_key
= "kdu_name"
6017 search_key
= "vnfd_id"
6019 entity_id
= vca_deployed
.get(search_key
)
6021 descriptor_config
= get_configuration(
6022 current_vnfd
, entity_id
6025 if "execution-environment-list" in descriptor_config
:
6026 ee_list
= descriptor_config
.get(
6027 "execution-environment-list", []
6032 # There could be several charm used in the same VNF
6033 for ee_item
in ee_list
:
6034 if ee_item
.get("juju"):
6035 step
= "Getting charm name"
6036 charm_name
= ee_item
["juju"].get("charm")
6038 step
= "Setting Charm artifact paths"
6039 current_charm_artifact_path
.append(
6040 get_charm_artifact_path(
6041 current_base_folder
,
6044 current_vnf_revision
,
6047 target_charm_artifact_path
.append(
6048 get_charm_artifact_path(
6052 latest_vnfd_revision
,
6055 elif ee_item
.get("helm-chart"):
6056 # add chart to list and all parameters
6057 step
= "Getting helm chart name"
6058 chart_name
= ee_item
.get("helm-chart")
6060 ee_item
.get("helm-version")
6061 and ee_item
.get("helm-version") == "v2"
6065 vca_type
= "helm-v3"
6066 step
= "Setting Helm chart artifact paths"
6068 helm_artifacts
.append(
6070 "current_artifact_path": get_charm_artifact_path(
6071 current_base_folder
,
6074 current_vnf_revision
,
6076 "target_artifact_path": get_charm_artifact_path(
6080 latest_vnfd_revision
,
6083 "vca_index": vca_index
,
6084 "vdu_index": vdu_count_index
,
6088 charm_artifact_paths
= zip(
6089 current_charm_artifact_path
, target_charm_artifact_path
6092 step
= "Checking if software version has changed in VNFD"
6093 if find_software_version(current_vnfd
) != find_software_version(
6096 step
= "Checking if existing VNF has charm"
6097 for current_charm_path
, target_charm_path
in list(
6098 charm_artifact_paths
6100 if current_charm_path
:
6102 "Software version change is not supported as VNF instance {} has charm.".format(
6107 # There is no change in the charm package, then redeploy the VNF
6108 # based on new descriptor
6109 step
= "Redeploying VNF"
6110 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
6111 (result
, detailed_status
) = await self
._ns
_redeploy
_vnf
(
6112 nsr_id
, nslcmop_id
, latest_vnfd
, db_vnfr
, db_nsr
6114 if result
== "FAILED":
6115 nslcmop_operation_state
= result
6116 error_description_nslcmop
= detailed_status
6117 db_nslcmop_update
["detailed-status"] = detailed_status
6120 + " step {} Done with result {} {}".format(
6121 step
, nslcmop_operation_state
, detailed_status
6126 step
= "Checking if any charm package has changed or not"
6127 for current_charm_path
, target_charm_path
in list(
6128 charm_artifact_paths
6132 and target_charm_path
6133 and self
.check_charm_hash_changed(
6134 current_charm_path
, target_charm_path
6137 step
= "Checking whether VNF uses juju bundle"
6138 if check_juju_bundle_existence(current_vnfd
):
6140 "Charm upgrade is not supported for the instance which"
6141 " uses juju-bundle: {}".format(
6142 check_juju_bundle_existence(current_vnfd
)
6146 step
= "Upgrading Charm"
6150 ) = await self
._ns
_charm
_upgrade
(
6153 charm_type
=vca_type
,
6154 path
=self
.fs
.path
+ target_charm_path
,
6155 timeout
=timeout_seconds
,
6158 if result
== "FAILED":
6159 nslcmop_operation_state
= result
6160 error_description_nslcmop
= detailed_status
6162 db_nslcmop_update
["detailed-status"] = detailed_status
6165 + " step {} Done with result {} {}".format(
6166 step
, nslcmop_operation_state
, detailed_status
6170 step
= "Updating policies"
6171 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
6172 result
= "COMPLETED"
6173 detailed_status
= "Done"
6174 db_nslcmop_update
["detailed-status"] = "Done"
6177 for item
in helm_artifacts
:
6179 item
["current_artifact_path"]
6180 and item
["target_artifact_path"]
6181 and self
.check_charm_hash_changed(
6182 item
["current_artifact_path"],
6183 item
["target_artifact_path"],
6187 db_update_entry
= "_admin.deployed.VCA.{}.".format(
6190 vnfr_id
= db_vnfr
["_id"]
6191 osm_config
= {"osm": {"ns_id": nsr_id
, "vnf_id": vnfr_id
}}
6193 "collection": "nsrs",
6194 "filter": {"_id": nsr_id
},
6195 "path": db_update_entry
,
6197 vca_type
, namespace
, helm_id
= get_ee_id_parts(item
["ee_id"])
6198 await self
.vca_map
[vca_type
].upgrade_execution_environment(
6199 namespace
=namespace
,
6203 artifact_path
=item
["target_artifact_path"],
6206 vnf_id
= db_vnfr
.get("vnfd-ref")
6207 config_descriptor
= get_configuration(latest_vnfd
, vnf_id
)
6208 self
.logger
.debug("get ssh key block")
6212 ("config-access", "ssh-access", "required"),
6214 # Needed to inject a ssh key
6217 ("config-access", "ssh-access", "default-user"),
6220 "Install configuration Software, getting public ssh key"
6222 pub_key
= await self
.vca_map
[
6224 ].get_ee_ssh_public__key(
6225 ee_id
=ee_id
, db_dict
=db_dict
, vca_id
=vca_id
6229 "Insert public key into VM user={} ssh_key={}".format(
6233 self
.logger
.debug(logging_text
+ step
)
6235 # wait for RO (ip-address) Insert pub_key into VM
6236 rw_mgmt_ip
= await self
.wait_vm_up_insert_key_ro(
6246 initial_config_primitive_list
= config_descriptor
.get(
6247 "initial-config-primitive"
6249 config_primitive
= next(
6252 for p
in initial_config_primitive_list
6253 if p
["name"] == "config"
6257 if not config_primitive
:
6260 deploy_params
= {"OSM": get_osm_params(db_vnfr
)}
6262 deploy_params
["rw_mgmt_ip"] = rw_mgmt_ip
6263 if db_vnfr
.get("additionalParamsForVnf"):
6264 deploy_params
.update(
6266 db_vnfr
["additionalParamsForVnf"].copy()
6269 primitive_params_
= self
._map
_primitive
_params
(
6270 config_primitive
, {}, deploy_params
6273 step
= "execute primitive '{}' params '{}'".format(
6274 config_primitive
["name"], primitive_params_
6276 self
.logger
.debug(logging_text
+ step
)
6277 await self
.vca_map
[vca_type
].exec_primitive(
6279 primitive_name
=config_primitive
["name"],
6280 params_dict
=primitive_params_
,
6286 step
= "Updating policies"
6287 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
6288 detailed_status
= "Done"
6289 db_nslcmop_update
["detailed-status"] = "Done"
6291 # If nslcmop_operation_state is None, so any operation is not failed.
6292 if not nslcmop_operation_state
:
6293 nslcmop_operation_state
= "COMPLETED"
6295 # If update CHANGE_VNFPKG nslcmop_operation is successful
6296 # vnf revision need to be updated
6297 vnfr_update
["revision"] = latest_vnfd_revision
6298 self
.update_db_2("vnfrs", db_vnfr
["_id"], vnfr_update
)
6302 + " task Done with result {} {}".format(
6303 nslcmop_operation_state
, detailed_status
6306 elif update_type
== "REMOVE_VNF":
6307 # This part is included in https://osm.etsi.org/gerrit/11876
6308 vnf_instance_id
= db_nslcmop
["operationParams"]["removeVnfInstanceId"]
6309 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnf_instance_id
})
6310 member_vnf_index
= db_vnfr
["member-vnf-index-ref"]
6311 step
= "Removing VNF"
6312 (result
, detailed_status
) = await self
.remove_vnf(
6313 nsr_id
, nslcmop_id
, vnf_instance_id
6315 if result
== "FAILED":
6316 nslcmop_operation_state
= result
6317 error_description_nslcmop
= detailed_status
6318 db_nslcmop_update
["detailed-status"] = detailed_status
6319 change_type
= "vnf_terminated"
6320 if not nslcmop_operation_state
:
6321 nslcmop_operation_state
= "COMPLETED"
6324 + " task Done with result {} {}".format(
6325 nslcmop_operation_state
, detailed_status
6329 elif update_type
== "OPERATE_VNF":
6330 vnf_id
= db_nslcmop
["operationParams"]["operateVnfData"][
6333 operation_type
= db_nslcmop
["operationParams"]["operateVnfData"][
6336 additional_param
= db_nslcmop
["operationParams"]["operateVnfData"][
6339 (result
, detailed_status
) = await self
.rebuild_start_stop(
6340 nsr_id
, nslcmop_id
, vnf_id
, additional_param
, operation_type
6342 if result
== "FAILED":
6343 nslcmop_operation_state
= result
6344 error_description_nslcmop
= detailed_status
6345 db_nslcmop_update
["detailed-status"] = detailed_status
6346 if not nslcmop_operation_state
:
6347 nslcmop_operation_state
= "COMPLETED"
6350 + " task Done with result {} {}".format(
6351 nslcmop_operation_state
, detailed_status
6355 # If nslcmop_operation_state is None, so any operation is not failed.
6356 # All operations are executed in overall.
6357 if not nslcmop_operation_state
:
6358 nslcmop_operation_state
= "COMPLETED"
6359 db_nsr_update
["operational-status"] = old_operational_status
6361 except (DbException
, LcmException
, N2VCException
, K8sException
) as e
:
6362 self
.logger
.error(logging_text
+ "Exit Exception {}".format(e
))
6364 except asyncio
.CancelledError
:
6366 logging_text
+ "Cancelled Exception while '{}'".format(step
)
6368 exc
= "Operation was cancelled"
6369 except asyncio
.TimeoutError
:
6370 self
.logger
.error(logging_text
+ "Timeout while '{}'".format(step
))
6372 except Exception as e
:
6373 exc
= traceback
.format_exc()
6374 self
.logger
.critical(
6375 logging_text
+ "Exit Exception {} {}".format(type(e
).__name
__, e
),
6384 ) = error_description_nslcmop
= "FAILED {}: {}".format(step
, exc
)
6385 nslcmop_operation_state
= "FAILED"
6386 db_nsr_update
["operational-status"] = old_operational_status
6388 self
._write
_ns
_status
(
6390 ns_state
=db_nsr
["nsState"],
6391 current_operation
="IDLE",
6392 current_operation_id
=None,
6393 other_update
=db_nsr_update
,
6396 self
._write
_op
_status
(
6399 error_message
=error_description_nslcmop
,
6400 operation_state
=nslcmop_operation_state
,
6401 other_update
=db_nslcmop_update
,
6404 if nslcmop_operation_state
:
6408 "nslcmop_id": nslcmop_id
,
6409 "operationState": nslcmop_operation_state
,
6412 change_type
in ("vnf_terminated", "policy_updated")
6413 and member_vnf_index
6415 msg
.update({"vnf_member_index": member_vnf_index
})
6416 await self
.msg
.aiowrite("ns", change_type
, msg
)
6417 except Exception as e
:
6419 logging_text
+ "kafka_write notification Exception {}".format(e
)
6421 self
.logger
.debug(logging_text
+ "Exit")
6422 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_update")
6423 return nslcmop_operation_state
, detailed_status
6425 async def scale(self
, nsr_id
, nslcmop_id
):
6426 # Try to lock HA task here
6427 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
6428 if not task_is_locked_by_me
:
6431 logging_text
= "Task ns={} scale={} ".format(nsr_id
, nslcmop_id
)
6432 stage
= ["", "", ""]
6433 tasks_dict_info
= {}
6434 # ^ stage, step, VIM progress
6435 self
.logger
.debug(logging_text
+ "Enter")
6436 # get all needed from database
6438 db_nslcmop_update
= {}
6441 # in case of error, indicates what part of scale was failed to put nsr at error status
6442 scale_process
= None
6443 old_operational_status
= ""
6444 old_config_status
= ""
6447 # wait for any previous tasks in process
6448 step
= "Waiting for previous operations to terminate"
6449 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
6450 self
._write
_ns
_status
(
6453 current_operation
="SCALING",
6454 current_operation_id
=nslcmop_id
,
6457 step
= "Getting nslcmop from database"
6459 step
+ " after having waited for previous tasks to be completed"
6461 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
6463 step
= "Getting nsr from database"
6464 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
6465 old_operational_status
= db_nsr
["operational-status"]
6466 old_config_status
= db_nsr
["config-status"]
6468 step
= "Parsing scaling parameters"
6469 db_nsr_update
["operational-status"] = "scaling"
6470 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
6471 nsr_deployed
= db_nsr
["_admin"].get("deployed")
6473 vnf_index
= db_nslcmop
["operationParams"]["scaleVnfData"][
6475 ]["member-vnf-index"]
6476 scaling_group
= db_nslcmop
["operationParams"]["scaleVnfData"][
6478 ]["scaling-group-descriptor"]
6479 scaling_type
= db_nslcmop
["operationParams"]["scaleVnfData"]["scaleVnfType"]
6480 # for backward compatibility
6481 if nsr_deployed
and isinstance(nsr_deployed
.get("VCA"), dict):
6482 nsr_deployed
["VCA"] = list(nsr_deployed
["VCA"].values())
6483 db_nsr_update
["_admin.deployed.VCA"] = nsr_deployed
["VCA"]
6484 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
6486 step
= "Getting vnfr from database"
6487 db_vnfr
= self
.db
.get_one(
6488 "vnfrs", {"member-vnf-index-ref": vnf_index
, "nsr-id-ref": nsr_id
}
6491 vca_id
= self
.get_vca_id(db_vnfr
, db_nsr
)
6493 step
= "Getting vnfd from database"
6494 db_vnfd
= self
.db
.get_one("vnfds", {"_id": db_vnfr
["vnfd-id"]})
6496 base_folder
= db_vnfd
["_admin"]["storage"]
6498 step
= "Getting scaling-group-descriptor"
6499 scaling_descriptor
= find_in_list(
6500 get_scaling_aspect(db_vnfd
),
6501 lambda scale_desc
: scale_desc
["name"] == scaling_group
,
6503 if not scaling_descriptor
:
6505 "input parameter 'scaleByStepData':'scaling-group-descriptor':'{}' is not present "
6506 "at vnfd:scaling-group-descriptor".format(scaling_group
)
6509 step
= "Sending scale order to VIM"
6510 # TODO check if ns is in a proper status
6512 if not db_nsr
["_admin"].get("scaling-group"):
6517 "_admin.scaling-group": [
6518 {"name": scaling_group
, "nb-scale-op": 0}
6522 admin_scale_index
= 0
6524 for admin_scale_index
, admin_scale_info
in enumerate(
6525 db_nsr
["_admin"]["scaling-group"]
6527 if admin_scale_info
["name"] == scaling_group
:
6528 nb_scale_op
= admin_scale_info
.get("nb-scale-op", 0)
6530 else: # not found, set index one plus last element and add new entry with the name
6531 admin_scale_index
+= 1
6533 "_admin.scaling-group.{}.name".format(admin_scale_index
)
6536 vca_scaling_info
= []
6537 scaling_info
= {"scaling_group_name": scaling_group
, "vdu": [], "kdu": []}
6538 if scaling_type
== "SCALE_OUT":
6539 if "aspect-delta-details" not in scaling_descriptor
:
6541 "Aspect delta details not fount in scaling descriptor {}".format(
6542 scaling_descriptor
["name"]
6545 # count if max-instance-count is reached
6546 deltas
= scaling_descriptor
.get("aspect-delta-details")["deltas"]
6548 scaling_info
["scaling_direction"] = "OUT"
6549 scaling_info
["vdu-create"] = {}
6550 scaling_info
["kdu-create"] = {}
6551 for delta
in deltas
:
6552 for vdu_delta
in delta
.get("vdu-delta", {}):
6553 vdud
= get_vdu(db_vnfd
, vdu_delta
["id"])
6554 # vdu_index also provides the number of instance of the targeted vdu
6555 vdu_count
= vdu_index
= get_vdur_index(db_vnfr
, vdu_delta
)
6556 cloud_init_text
= self
._get
_vdu
_cloud
_init
_content
(
6560 additional_params
= (
6561 self
._get
_vdu
_additional
_params
(db_vnfr
, vdud
["id"])
6564 cloud_init_list
= []
6566 vdu_profile
= get_vdu_profile(db_vnfd
, vdu_delta
["id"])
6567 max_instance_count
= 10
6568 if vdu_profile
and "max-number-of-instances" in vdu_profile
:
6569 max_instance_count
= vdu_profile
.get(
6570 "max-number-of-instances", 10
6573 default_instance_num
= get_number_of_instances(
6576 instances_number
= vdu_delta
.get("number-of-instances", 1)
6577 nb_scale_op
+= instances_number
6579 new_instance_count
= nb_scale_op
+ default_instance_num
6580 # Control if new count is over max and vdu count is less than max.
6581 # Then assign new instance count
6582 if new_instance_count
> max_instance_count
> vdu_count
:
6583 instances_number
= new_instance_count
- max_instance_count
6585 instances_number
= instances_number
6587 if new_instance_count
> max_instance_count
:
6589 "reached the limit of {} (max-instance-count) "
6590 "scaling-out operations for the "
6591 "scaling-group-descriptor '{}'".format(
6592 nb_scale_op
, scaling_group
6595 for x
in range(vdu_delta
.get("number-of-instances", 1)):
6597 # TODO Information of its own ip is not available because db_vnfr is not updated.
6598 additional_params
["OSM"] = get_osm_params(
6599 db_vnfr
, vdu_delta
["id"], vdu_index
+ x
6601 cloud_init_list
.append(
6602 self
._parse
_cloud
_init
(
6609 vca_scaling_info
.append(
6611 "osm_vdu_id": vdu_delta
["id"],
6612 "member-vnf-index": vnf_index
,
6614 "vdu_index": vdu_index
+ x
,
6617 scaling_info
["vdu-create"][vdu_delta
["id"]] = instances_number
6618 for kdu_delta
in delta
.get("kdu-resource-delta", {}):
6619 kdu_profile
= get_kdu_resource_profile(db_vnfd
, kdu_delta
["id"])
6620 kdu_name
= kdu_profile
["kdu-name"]
6621 resource_name
= kdu_profile
.get("resource-name", "")
6623 # Might have different kdus in the same delta
6624 # Should have list for each kdu
6625 if not scaling_info
["kdu-create"].get(kdu_name
, None):
6626 scaling_info
["kdu-create"][kdu_name
] = []
6628 kdur
= get_kdur(db_vnfr
, kdu_name
)
6629 if kdur
.get("helm-chart"):
6630 k8s_cluster_type
= "helm-chart-v3"
6631 self
.logger
.debug("kdur: {}".format(kdur
))
6633 kdur
.get("helm-version")
6634 and kdur
.get("helm-version") == "v2"
6636 k8s_cluster_type
= "helm-chart"
6637 elif kdur
.get("juju-bundle"):
6638 k8s_cluster_type
= "juju-bundle"
6641 "kdu type for kdu='{}.{}' is neither helm-chart nor "
6642 "juju-bundle. Maybe an old NBI version is running".format(
6643 db_vnfr
["member-vnf-index-ref"], kdu_name
6647 max_instance_count
= 10
6648 if kdu_profile
and "max-number-of-instances" in kdu_profile
:
6649 max_instance_count
= kdu_profile
.get(
6650 "max-number-of-instances", 10
6653 nb_scale_op
+= kdu_delta
.get("number-of-instances", 1)
6654 deployed_kdu
, _
= get_deployed_kdu(
6655 nsr_deployed
, kdu_name
, vnf_index
6657 if deployed_kdu
is None:
6659 "KDU '{}' for vnf '{}' not deployed".format(
6663 kdu_instance
= deployed_kdu
.get("kdu-instance")
6664 instance_num
= await self
.k8scluster_map
[
6670 cluster_uuid
=deployed_kdu
.get("k8scluster-uuid"),
6671 kdu_model
=deployed_kdu
.get("kdu-model"),
6673 kdu_replica_count
= instance_num
+ kdu_delta
.get(
6674 "number-of-instances", 1
6677 # Control if new count is over max and instance_num is less than max.
6678 # Then assign max instance number to kdu replica count
6679 if kdu_replica_count
> max_instance_count
> instance_num
:
6680 kdu_replica_count
= max_instance_count
6681 if kdu_replica_count
> max_instance_count
:
6683 "reached the limit of {} (max-instance-count) "
6684 "scaling-out operations for the "
6685 "scaling-group-descriptor '{}'".format(
6686 instance_num
, scaling_group
6690 for x
in range(kdu_delta
.get("number-of-instances", 1)):
6691 vca_scaling_info
.append(
6693 "osm_kdu_id": kdu_name
,
6694 "member-vnf-index": vnf_index
,
6696 "kdu_index": instance_num
+ x
- 1,
6699 scaling_info
["kdu-create"][kdu_name
].append(
6701 "member-vnf-index": vnf_index
,
6703 "k8s-cluster-type": k8s_cluster_type
,
6704 "resource-name": resource_name
,
6705 "scale": kdu_replica_count
,
6708 elif scaling_type
== "SCALE_IN":
6709 deltas
= scaling_descriptor
.get("aspect-delta-details")["deltas"]
6711 scaling_info
["scaling_direction"] = "IN"
6712 scaling_info
["vdu-delete"] = {}
6713 scaling_info
["kdu-delete"] = {}
6715 for delta
in deltas
:
6716 for vdu_delta
in delta
.get("vdu-delta", {}):
6717 vdu_count
= vdu_index
= get_vdur_index(db_vnfr
, vdu_delta
)
6718 min_instance_count
= 0
6719 vdu_profile
= get_vdu_profile(db_vnfd
, vdu_delta
["id"])
6720 if vdu_profile
and "min-number-of-instances" in vdu_profile
:
6721 min_instance_count
= vdu_profile
["min-number-of-instances"]
6723 default_instance_num
= get_number_of_instances(
6724 db_vnfd
, vdu_delta
["id"]
6726 instance_num
= vdu_delta
.get("number-of-instances", 1)
6727 nb_scale_op
-= instance_num
6729 new_instance_count
= nb_scale_op
+ default_instance_num
6731 if new_instance_count
< min_instance_count
< vdu_count
:
6732 instances_number
= min_instance_count
- new_instance_count
6734 instances_number
= instance_num
6736 if new_instance_count
< min_instance_count
:
6738 "reached the limit of {} (min-instance-count) scaling-in operations for the "
6739 "scaling-group-descriptor '{}'".format(
6740 nb_scale_op
, scaling_group
6743 for x
in range(vdu_delta
.get("number-of-instances", 1)):
6744 vca_scaling_info
.append(
6746 "osm_vdu_id": vdu_delta
["id"],
6747 "member-vnf-index": vnf_index
,
6749 "vdu_index": vdu_index
- 1 - x
,
6752 scaling_info
["vdu-delete"][vdu_delta
["id"]] = instances_number
6753 for kdu_delta
in delta
.get("kdu-resource-delta", {}):
6754 kdu_profile
= get_kdu_resource_profile(db_vnfd
, kdu_delta
["id"])
6755 kdu_name
= kdu_profile
["kdu-name"]
6756 resource_name
= kdu_profile
.get("resource-name", "")
6758 if not scaling_info
["kdu-delete"].get(kdu_name
, None):
6759 scaling_info
["kdu-delete"][kdu_name
] = []
6761 kdur
= get_kdur(db_vnfr
, kdu_name
)
6762 if kdur
.get("helm-chart"):
6763 k8s_cluster_type
= "helm-chart-v3"
6764 self
.logger
.debug("kdur: {}".format(kdur
))
6766 kdur
.get("helm-version")
6767 and kdur
.get("helm-version") == "v2"
6769 k8s_cluster_type
= "helm-chart"
6770 elif kdur
.get("juju-bundle"):
6771 k8s_cluster_type
= "juju-bundle"
6774 "kdu type for kdu='{}.{}' is neither helm-chart nor "
6775 "juju-bundle. Maybe an old NBI version is running".format(
6776 db_vnfr
["member-vnf-index-ref"], kdur
["kdu-name"]
6780 min_instance_count
= 0
6781 if kdu_profile
and "min-number-of-instances" in kdu_profile
:
6782 min_instance_count
= kdu_profile
["min-number-of-instances"]
6784 nb_scale_op
-= kdu_delta
.get("number-of-instances", 1)
6785 deployed_kdu
, _
= get_deployed_kdu(
6786 nsr_deployed
, kdu_name
, vnf_index
6788 if deployed_kdu
is None:
6790 "KDU '{}' for vnf '{}' not deployed".format(
6794 kdu_instance
= deployed_kdu
.get("kdu-instance")
6795 instance_num
= await self
.k8scluster_map
[
6801 cluster_uuid
=deployed_kdu
.get("k8scluster-uuid"),
6802 kdu_model
=deployed_kdu
.get("kdu-model"),
6804 kdu_replica_count
= instance_num
- kdu_delta
.get(
6805 "number-of-instances", 1
6808 if kdu_replica_count
< min_instance_count
< instance_num
:
6809 kdu_replica_count
= min_instance_count
6810 if kdu_replica_count
< min_instance_count
:
6812 "reached the limit of {} (min-instance-count) scaling-in operations for the "
6813 "scaling-group-descriptor '{}'".format(
6814 instance_num
, scaling_group
6818 for x
in range(kdu_delta
.get("number-of-instances", 1)):
6819 vca_scaling_info
.append(
6821 "osm_kdu_id": kdu_name
,
6822 "member-vnf-index": vnf_index
,
6824 "kdu_index": instance_num
- x
- 1,
6827 scaling_info
["kdu-delete"][kdu_name
].append(
6829 "member-vnf-index": vnf_index
,
6831 "k8s-cluster-type": k8s_cluster_type
,
6832 "resource-name": resource_name
,
6833 "scale": kdu_replica_count
,
6837 # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
6838 vdu_delete
= copy(scaling_info
.get("vdu-delete"))
6839 if scaling_info
["scaling_direction"] == "IN":
6840 for vdur
in reversed(db_vnfr
["vdur"]):
6841 if vdu_delete
.get(vdur
["vdu-id-ref"]):
6842 vdu_delete
[vdur
["vdu-id-ref"]] -= 1
6843 scaling_info
["vdu"].append(
6845 "name": vdur
.get("name") or vdur
.get("vdu-name"),
6846 "vdu_id": vdur
["vdu-id-ref"],
6850 for interface
in vdur
["interfaces"]:
6851 scaling_info
["vdu"][-1]["interface"].append(
6853 "name": interface
["name"],
6854 "ip_address": interface
["ip-address"],
6855 "mac_address": interface
.get("mac-address"),
6858 # vdu_delete = vdu_scaling_info.pop("vdu-delete")
6861 step
= "Executing pre-scale vnf-config-primitive"
6862 if scaling_descriptor
.get("scaling-config-action"):
6863 for scaling_config_action
in scaling_descriptor
[
6864 "scaling-config-action"
6867 scaling_config_action
.get("trigger") == "pre-scale-in"
6868 and scaling_type
== "SCALE_IN"
6870 scaling_config_action
.get("trigger") == "pre-scale-out"
6871 and scaling_type
== "SCALE_OUT"
6873 vnf_config_primitive
= scaling_config_action
[
6874 "vnf-config-primitive-name-ref"
6876 step
= db_nslcmop_update
[
6878 ] = "executing pre-scale scaling-config-action '{}'".format(
6879 vnf_config_primitive
6882 # look for primitive
6883 for config_primitive
in (
6884 get_configuration(db_vnfd
, db_vnfd
["id"]) or {}
6885 ).get("config-primitive", ()):
6886 if config_primitive
["name"] == vnf_config_primitive
:
6890 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
6891 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
6892 "primitive".format(scaling_group
, vnf_config_primitive
)
6895 vnfr_params
= {"VDU_SCALE_INFO": scaling_info
}
6896 if db_vnfr
.get("additionalParamsForVnf"):
6897 vnfr_params
.update(db_vnfr
["additionalParamsForVnf"])
6899 scale_process
= "VCA"
6900 db_nsr_update
["config-status"] = "configuring pre-scaling"
6901 primitive_params
= self
._map
_primitive
_params
(
6902 config_primitive
, {}, vnfr_params
6905 # Pre-scale retry check: Check if this sub-operation has been executed before
6906 op_index
= self
._check
_or
_add
_scale
_suboperation
(
6909 vnf_config_primitive
,
6913 if op_index
== self
.SUBOPERATION_STATUS_SKIP
:
6914 # Skip sub-operation
6915 result
= "COMPLETED"
6916 result_detail
= "Done"
6919 + "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
6920 vnf_config_primitive
, result
, result_detail
6924 if op_index
== self
.SUBOPERATION_STATUS_NEW
:
6925 # New sub-operation: Get index of this sub-operation
6927 len(db_nslcmop
.get("_admin", {}).get("operations"))
6932 + "vnf_config_primitive={} New sub-operation".format(
6933 vnf_config_primitive
6937 # retry: Get registered params for this existing sub-operation
6938 op
= db_nslcmop
.get("_admin", {}).get("operations", [])[
6941 vnf_index
= op
.get("member_vnf_index")
6942 vnf_config_primitive
= op
.get("primitive")
6943 primitive_params
= op
.get("primitive_params")
6946 + "vnf_config_primitive={} Sub-operation retry".format(
6947 vnf_config_primitive
6950 # Execute the primitive, either with new (first-time) or registered (reintent) args
6951 ee_descriptor_id
= config_primitive
.get(
6952 "execution-environment-ref"
6954 primitive_name
= config_primitive
.get(
6955 "execution-environment-primitive", vnf_config_primitive
6957 ee_id
, vca_type
= self
._look
_for
_deployed
_vca
(
6958 nsr_deployed
["VCA"],
6959 member_vnf_index
=vnf_index
,
6961 vdu_count_index
=None,
6962 ee_descriptor_id
=ee_descriptor_id
,
6964 result
, result_detail
= await self
._ns
_execute
_primitive
(
6973 + "vnf_config_primitive={} Done with result {} {}".format(
6974 vnf_config_primitive
, result
, result_detail
6977 # Update operationState = COMPLETED | FAILED
6978 self
._update
_suboperation
_status
(
6979 db_nslcmop
, op_index
, result
, result_detail
6982 if result
== "FAILED":
6983 raise LcmException(result_detail
)
6984 db_nsr_update
["config-status"] = old_config_status
6985 scale_process
= None
6989 "_admin.scaling-group.{}.nb-scale-op".format(admin_scale_index
)
6992 "_admin.scaling-group.{}.time".format(admin_scale_index
)
6995 # SCALE-IN VCA - BEGIN
6996 if vca_scaling_info
:
6997 step
= db_nslcmop_update
[
6999 ] = "Deleting the execution environments"
7000 scale_process
= "VCA"
7001 for vca_info
in vca_scaling_info
:
7002 if vca_info
["type"] == "delete" and not vca_info
.get("osm_kdu_id"):
7003 member_vnf_index
= str(vca_info
["member-vnf-index"])
7005 logging_text
+ "vdu info: {}".format(vca_info
)
7007 if vca_info
.get("osm_vdu_id"):
7008 vdu_id
= vca_info
["osm_vdu_id"]
7009 vdu_index
= int(vca_info
["vdu_index"])
7012 ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7013 member_vnf_index
, vdu_id
, vdu_index
7015 stage
[2] = step
= "Scaling in VCA"
7016 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
)
7017 vca_update
= db_nsr
["_admin"]["deployed"]["VCA"]
7018 config_update
= db_nsr
["configurationStatus"]
7019 for vca_index
, vca
in enumerate(vca_update
):
7021 (vca
or vca
.get("ee_id"))
7022 and vca
["member-vnf-index"] == member_vnf_index
7023 and vca
["vdu_count_index"] == vdu_index
7025 if vca
.get("vdu_id"):
7026 config_descriptor
= get_configuration(
7027 db_vnfd
, vca
.get("vdu_id")
7029 elif vca
.get("kdu_name"):
7030 config_descriptor
= get_configuration(
7031 db_vnfd
, vca
.get("kdu_name")
7034 config_descriptor
= get_configuration(
7035 db_vnfd
, db_vnfd
["id"]
7037 operation_params
= (
7038 db_nslcmop
.get("operationParams") or {}
7040 exec_terminate_primitives
= not operation_params
.get(
7041 "skip_terminate_primitives"
7042 ) and vca
.get("needed_terminate")
7043 task
= asyncio
.ensure_future(
7052 exec_primitives
=exec_terminate_primitives
,
7056 timeout
=self
.timeout
.charm_delete
,
7059 tasks_dict_info
[task
] = "Terminating VCA {}".format(
7062 del vca_update
[vca_index
]
7063 del config_update
[vca_index
]
7064 # wait for pending tasks of terminate primitives
7068 + "Waiting for tasks {}".format(
7069 list(tasks_dict_info
.keys())
7072 error_list
= await self
._wait
_for
_tasks
(
7076 self
.timeout
.charm_delete
, self
.timeout
.ns_terminate
7081 tasks_dict_info
.clear()
7083 raise LcmException("; ".join(error_list
))
7085 db_vca_and_config_update
= {
7086 "_admin.deployed.VCA": vca_update
,
7087 "configurationStatus": config_update
,
7090 "nsrs", db_nsr
["_id"], db_vca_and_config_update
7092 scale_process
= None
7093 # SCALE-IN VCA - END
7096 if scaling_info
.get("vdu-create") or scaling_info
.get("vdu-delete"):
7097 scale_process
= "RO"
7098 if self
.ro_config
.ng
:
7099 await self
._scale
_ng
_ro
(
7100 logging_text
, db_nsr
, db_nslcmop
, db_vnfr
, scaling_info
, stage
7102 scaling_info
.pop("vdu-create", None)
7103 scaling_info
.pop("vdu-delete", None)
7105 scale_process
= None
7109 if scaling_info
.get("kdu-create") or scaling_info
.get("kdu-delete"):
7110 scale_process
= "KDU"
7111 await self
._scale
_kdu
(
7112 logging_text
, nsr_id
, nsr_deployed
, db_vnfd
, vca_id
, scaling_info
7114 scaling_info
.pop("kdu-create", None)
7115 scaling_info
.pop("kdu-delete", None)
7117 scale_process
= None
7121 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
7123 # SCALE-UP VCA - BEGIN
7124 if vca_scaling_info
:
7125 step
= db_nslcmop_update
[
7127 ] = "Creating new execution environments"
7128 scale_process
= "VCA"
7129 for vca_info
in vca_scaling_info
:
7130 if vca_info
["type"] == "create" and not vca_info
.get("osm_kdu_id"):
7131 member_vnf_index
= str(vca_info
["member-vnf-index"])
7133 logging_text
+ "vdu info: {}".format(vca_info
)
7135 vnfd_id
= db_vnfr
["vnfd-ref"]
7136 if vca_info
.get("osm_vdu_id"):
7137 vdu_index
= int(vca_info
["vdu_index"])
7138 deploy_params
= {"OSM": get_osm_params(db_vnfr
)}
7139 if db_vnfr
.get("additionalParamsForVnf"):
7140 deploy_params
.update(
7142 db_vnfr
["additionalParamsForVnf"].copy()
7145 descriptor_config
= get_configuration(
7146 db_vnfd
, db_vnfd
["id"]
7148 if descriptor_config
:
7154 logging_text
=logging_text
7155 + "member_vnf_index={} ".format(member_vnf_index
),
7158 nslcmop_id
=nslcmop_id
,
7164 kdu_index
=kdu_index
,
7165 member_vnf_index
=member_vnf_index
,
7166 vdu_index
=vdu_index
,
7168 deploy_params
=deploy_params
,
7169 descriptor_config
=descriptor_config
,
7170 base_folder
=base_folder
,
7171 task_instantiation_info
=tasks_dict_info
,
7174 vdu_id
= vca_info
["osm_vdu_id"]
7175 vdur
= find_in_list(
7176 db_vnfr
["vdur"], lambda vdu
: vdu
["vdu-id-ref"] == vdu_id
7178 descriptor_config
= get_configuration(db_vnfd
, vdu_id
)
7179 if vdur
.get("additionalParams"):
7180 deploy_params_vdu
= parse_yaml_strings(
7181 vdur
["additionalParams"]
7184 deploy_params_vdu
= deploy_params
7185 deploy_params_vdu
["OSM"] = get_osm_params(
7186 db_vnfr
, vdu_id
, vdu_count_index
=vdu_index
7188 if descriptor_config
:
7194 ] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7195 member_vnf_index
, vdu_id
, vdu_index
7197 stage
[2] = step
= "Scaling out VCA"
7198 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
)
7200 logging_text
=logging_text
7201 + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
7202 member_vnf_index
, vdu_id
, vdu_index
7206 nslcmop_id
=nslcmop_id
,
7212 member_vnf_index
=member_vnf_index
,
7213 vdu_index
=vdu_index
,
7214 kdu_index
=kdu_index
,
7216 deploy_params
=deploy_params_vdu
,
7217 descriptor_config
=descriptor_config
,
7218 base_folder
=base_folder
,
7219 task_instantiation_info
=tasks_dict_info
,
7222 # SCALE-UP VCA - END
7223 scale_process
= None
7226 # execute primitive service POST-SCALING
7227 step
= "Executing post-scale vnf-config-primitive"
7228 if scaling_descriptor
.get("scaling-config-action"):
7229 for scaling_config_action
in scaling_descriptor
[
7230 "scaling-config-action"
7233 scaling_config_action
.get("trigger") == "post-scale-in"
7234 and scaling_type
== "SCALE_IN"
7236 scaling_config_action
.get("trigger") == "post-scale-out"
7237 and scaling_type
== "SCALE_OUT"
7239 vnf_config_primitive
= scaling_config_action
[
7240 "vnf-config-primitive-name-ref"
7242 step
= db_nslcmop_update
[
7244 ] = "executing post-scale scaling-config-action '{}'".format(
7245 vnf_config_primitive
7248 vnfr_params
= {"VDU_SCALE_INFO": scaling_info
}
7249 if db_vnfr
.get("additionalParamsForVnf"):
7250 vnfr_params
.update(db_vnfr
["additionalParamsForVnf"])
7252 # look for primitive
7253 for config_primitive
in (
7254 get_configuration(db_vnfd
, db_vnfd
["id"]) or {}
7255 ).get("config-primitive", ()):
7256 if config_primitive
["name"] == vnf_config_primitive
:
7260 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-"
7261 "action[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:"
7262 "config-primitive".format(
7263 scaling_group
, vnf_config_primitive
7266 scale_process
= "VCA"
7267 db_nsr_update
["config-status"] = "configuring post-scaling"
7268 primitive_params
= self
._map
_primitive
_params
(
7269 config_primitive
, {}, vnfr_params
7272 # Post-scale retry check: Check if this sub-operation has been executed before
7273 op_index
= self
._check
_or
_add
_scale
_suboperation
(
7276 vnf_config_primitive
,
7280 if op_index
== self
.SUBOPERATION_STATUS_SKIP
:
7281 # Skip sub-operation
7282 result
= "COMPLETED"
7283 result_detail
= "Done"
7286 + "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
7287 vnf_config_primitive
, result
, result_detail
7291 if op_index
== self
.SUBOPERATION_STATUS_NEW
:
7292 # New sub-operation: Get index of this sub-operation
7294 len(db_nslcmop
.get("_admin", {}).get("operations"))
7299 + "vnf_config_primitive={} New sub-operation".format(
7300 vnf_config_primitive
7304 # retry: Get registered params for this existing sub-operation
7305 op
= db_nslcmop
.get("_admin", {}).get("operations", [])[
7308 vnf_index
= op
.get("member_vnf_index")
7309 vnf_config_primitive
= op
.get("primitive")
7310 primitive_params
= op
.get("primitive_params")
7313 + "vnf_config_primitive={} Sub-operation retry".format(
7314 vnf_config_primitive
7317 # Execute the primitive, either with new (first-time) or registered (reintent) args
7318 ee_descriptor_id
= config_primitive
.get(
7319 "execution-environment-ref"
7321 primitive_name
= config_primitive
.get(
7322 "execution-environment-primitive", vnf_config_primitive
7324 ee_id
, vca_type
= self
._look
_for
_deployed
_vca
(
7325 nsr_deployed
["VCA"],
7326 member_vnf_index
=vnf_index
,
7328 vdu_count_index
=None,
7329 ee_descriptor_id
=ee_descriptor_id
,
7331 result
, result_detail
= await self
._ns
_execute
_primitive
(
7340 + "vnf_config_primitive={} Done with result {} {}".format(
7341 vnf_config_primitive
, result
, result_detail
7344 # Update operationState = COMPLETED | FAILED
7345 self
._update
_suboperation
_status
(
7346 db_nslcmop
, op_index
, result
, result_detail
7349 if result
== "FAILED":
7350 raise LcmException(result_detail
)
7351 db_nsr_update
["config-status"] = old_config_status
7352 scale_process
= None
7357 ] = "" # "scaled {} {}".format(scaling_group, scaling_type)
7358 db_nsr_update
["operational-status"] = (
7360 if old_operational_status
== "failed"
7361 else old_operational_status
7363 db_nsr_update
["config-status"] = old_config_status
7366 ROclient
.ROClientException
,
7371 self
.logger
.error(logging_text
+ "Exit Exception {}".format(e
))
7373 except asyncio
.CancelledError
:
7375 logging_text
+ "Cancelled Exception while '{}'".format(step
)
7377 exc
= "Operation was cancelled"
7378 except Exception as e
:
7379 exc
= traceback
.format_exc()
7380 self
.logger
.critical(
7381 logging_text
+ "Exit Exception {} {}".format(type(e
).__name
__, e
),
7385 self
._write
_ns
_status
(
7388 current_operation
="IDLE",
7389 current_operation_id
=None,
7392 stage
[1] = "Waiting for instantiate pending tasks."
7393 self
.logger
.debug(logging_text
+ stage
[1])
7394 exc
= await self
._wait
_for
_tasks
(
7397 self
.timeout
.ns_deploy
,
7405 ] = error_description_nslcmop
= "FAILED {}: {}".format(step
, exc
)
7406 nslcmop_operation_state
= "FAILED"
7408 db_nsr_update
["operational-status"] = old_operational_status
7409 db_nsr_update
["config-status"] = old_config_status
7410 db_nsr_update
["detailed-status"] = ""
7412 if "VCA" in scale_process
:
7413 db_nsr_update
["config-status"] = "failed"
7414 if "RO" in scale_process
:
7415 db_nsr_update
["operational-status"] = "failed"
7418 ] = "FAILED scaling nslcmop={} {}: {}".format(
7419 nslcmop_id
, step
, exc
7422 error_description_nslcmop
= None
7423 nslcmop_operation_state
= "COMPLETED"
7424 db_nslcmop_update
["detailed-status"] = "Done"
7426 self
._write
_op
_status
(
7429 error_message
=error_description_nslcmop
,
7430 operation_state
=nslcmop_operation_state
,
7431 other_update
=db_nslcmop_update
,
7434 self
._write
_ns
_status
(
7437 current_operation
="IDLE",
7438 current_operation_id
=None,
7439 other_update
=db_nsr_update
,
7442 if nslcmop_operation_state
:
7446 "nslcmop_id": nslcmop_id
,
7447 "operationState": nslcmop_operation_state
,
7449 await self
.msg
.aiowrite("ns", "scaled", msg
)
7450 except Exception as e
:
7452 logging_text
+ "kafka_write notification Exception {}".format(e
)
7454 self
.logger
.debug(logging_text
+ "Exit")
7455 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_scale")
7457 async def _scale_kdu(
7458 self
, logging_text
, nsr_id
, nsr_deployed
, db_vnfd
, vca_id
, scaling_info
7460 _scaling_info
= scaling_info
.get("kdu-create") or scaling_info
.get("kdu-delete")
7461 for kdu_name
in _scaling_info
:
7462 for kdu_scaling_info
in _scaling_info
[kdu_name
]:
7463 deployed_kdu
, index
= get_deployed_kdu(
7464 nsr_deployed
, kdu_name
, kdu_scaling_info
["member-vnf-index"]
7466 cluster_uuid
= deployed_kdu
["k8scluster-uuid"]
7467 kdu_instance
= deployed_kdu
["kdu-instance"]
7468 kdu_model
= deployed_kdu
.get("kdu-model")
7469 scale
= int(kdu_scaling_info
["scale"])
7470 k8s_cluster_type
= kdu_scaling_info
["k8s-cluster-type"]
7473 "collection": "nsrs",
7474 "filter": {"_id": nsr_id
},
7475 "path": "_admin.deployed.K8s.{}".format(index
),
7478 step
= "scaling application {}".format(
7479 kdu_scaling_info
["resource-name"]
7481 self
.logger
.debug(logging_text
+ step
)
7483 if kdu_scaling_info
["type"] == "delete":
7484 kdu_config
= get_configuration(db_vnfd
, kdu_name
)
7487 and kdu_config
.get("terminate-config-primitive")
7488 and get_juju_ee_ref(db_vnfd
, kdu_name
) is None
7490 terminate_config_primitive_list
= kdu_config
.get(
7491 "terminate-config-primitive"
7493 terminate_config_primitive_list
.sort(
7494 key
=lambda val
: int(val
["seq"])
7498 terminate_config_primitive
7499 ) in terminate_config_primitive_list
:
7500 primitive_params_
= self
._map
_primitive
_params
(
7501 terminate_config_primitive
, {}, {}
7503 step
= "execute terminate config primitive"
7504 self
.logger
.debug(logging_text
+ step
)
7505 await asyncio
.wait_for(
7506 self
.k8scluster_map
[k8s_cluster_type
].exec_primitive(
7507 cluster_uuid
=cluster_uuid
,
7508 kdu_instance
=kdu_instance
,
7509 primitive_name
=terminate_config_primitive
["name"],
7510 params
=primitive_params_
,
7512 total_timeout
=self
.timeout
.primitive
,
7515 timeout
=self
.timeout
.primitive
7516 * self
.timeout
.primitive_outer_factor
,
7519 await asyncio
.wait_for(
7520 self
.k8scluster_map
[k8s_cluster_type
].scale(
7521 kdu_instance
=kdu_instance
,
7523 resource_name
=kdu_scaling_info
["resource-name"],
7524 total_timeout
=self
.timeout
.scale_on_error
,
7526 cluster_uuid
=cluster_uuid
,
7527 kdu_model
=kdu_model
,
7531 timeout
=self
.timeout
.scale_on_error
7532 * self
.timeout
.scale_on_error_outer_factor
,
7535 if kdu_scaling_info
["type"] == "create":
7536 kdu_config
= get_configuration(db_vnfd
, kdu_name
)
7539 and kdu_config
.get("initial-config-primitive")
7540 and get_juju_ee_ref(db_vnfd
, kdu_name
) is None
7542 initial_config_primitive_list
= kdu_config
.get(
7543 "initial-config-primitive"
7545 initial_config_primitive_list
.sort(
7546 key
=lambda val
: int(val
["seq"])
7549 for initial_config_primitive
in initial_config_primitive_list
:
7550 primitive_params_
= self
._map
_primitive
_params
(
7551 initial_config_primitive
, {}, {}
7553 step
= "execute initial config primitive"
7554 self
.logger
.debug(logging_text
+ step
)
7555 await asyncio
.wait_for(
7556 self
.k8scluster_map
[k8s_cluster_type
].exec_primitive(
7557 cluster_uuid
=cluster_uuid
,
7558 kdu_instance
=kdu_instance
,
7559 primitive_name
=initial_config_primitive
["name"],
7560 params
=primitive_params_
,
7567 async def _scale_ng_ro(
7568 self
, logging_text
, db_nsr
, db_nslcmop
, db_vnfr
, vdu_scaling_info
, stage
7570 nsr_id
= db_nslcmop
["nsInstanceId"]
7571 db_nsd
= self
.db
.get_one("nsds", {"_id": db_nsr
["nsd-id"]})
7574 # read from db: vnfd's for every vnf
7577 # for each vnf in ns, read vnfd
7578 for vnfr
in self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
}):
7579 db_vnfrs
[vnfr
["member-vnf-index-ref"]] = vnfr
7580 vnfd_id
= vnfr
["vnfd-id"] # vnfd uuid for this vnf
7581 # if we haven't this vnfd, read it from db
7582 if not find_in_list(db_vnfds
, lambda a_vnfd
: a_vnfd
["id"] == vnfd_id
):
7584 vnfd
= self
.db
.get_one("vnfds", {"_id": vnfd_id
})
7585 db_vnfds
.append(vnfd
)
7586 n2vc_key
= self
.n2vc
.get_public_key()
7587 n2vc_key_list
= [n2vc_key
]
7590 vdu_scaling_info
.get("vdu-create"),
7591 vdu_scaling_info
.get("vdu-delete"),
7594 # db_vnfr has been updated, update db_vnfrs to use it
7595 db_vnfrs
[db_vnfr
["member-vnf-index-ref"]] = db_vnfr
7596 await self
._instantiate
_ng
_ro
(
7606 start_deploy
=time(),
7607 timeout_ns_deploy
=self
.timeout
.ns_deploy
,
7609 if vdu_scaling_info
.get("vdu-delete"):
7611 db_vnfr
, None, vdu_scaling_info
["vdu-delete"], mark_delete
=False
7614 async def extract_prometheus_scrape_jobs(
7618 ee_config_descriptor
: dict,
7623 vnf_member_index
: str = "",
7625 vdu_index
: int = None,
7627 kdu_index
: int = None,
7629 """Method to extract prometheus scrape jobs from EE's Prometheus template job file
7630 This method will wait until the corresponding VDU or KDU is fully instantiated
7633 ee_id (str): Execution Environment ID
7634 artifact_path (str): Path where the EE's content is (including the Prometheus template file)
7635 ee_config_descriptor (dict): Execution Environment's configuration descriptor
7636 vnfr_id (str): VNFR ID where this EE applies
7637 nsr_id (str): NSR ID where this EE applies
7638 target_ip (str): VDU/KDU instance IP address
7639 element_type (str): NS or VNF or VDU or KDU
7640 vnf_member_index (str, optional): VNF index where this EE applies. Defaults to "".
7641 vdu_id (str, optional): VDU ID where this EE applies. Defaults to "".
7642 vdu_index (int, optional): VDU index where this EE applies. Defaults to None.
7643 kdu_name (str, optional): KDU name where this EE applies. Defaults to "".
7644 kdu_index (int, optional): KDU index where this EE applies. Defaults to None.
7647 LcmException: When the VDU or KDU instance was not found in an hour
7650 _type_: Prometheus jobs
7652 # default the vdur and kdur names to an empty string, to avoid any later
7653 # problem with Prometheus when the element type is not VDU or KDU
7657 # look if exist a file called 'prometheus*.j2' and
7658 artifact_content
= self
.fs
.dir_ls(artifact_path
)
7662 for f
in artifact_content
7663 if f
.startswith("prometheus") and f
.endswith(".j2")
7669 self
.logger
.debug("Artifact path{}".format(artifact_path
))
7670 self
.logger
.debug("job file{}".format(job_file
))
7671 with self
.fs
.file_open((artifact_path
, job_file
), "r") as f
:
7674 # obtain the VDUR or KDUR, if the element type is VDU or KDU
7675 if element_type
in ("VDU", "KDU"):
7676 for _
in range(360):
7677 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnfr_id
})
7678 if vdu_id
and vdu_index
is not None:
7682 for x
in get_iterable(db_vnfr
, "vdur")
7684 x
.get("vdu-id-ref") == vdu_id
7685 and x
.get("count-index") == vdu_index
7690 if vdur
.get("name"):
7691 vdur_name
= vdur
.get("name")
7693 if kdu_name
and kdu_index
is not None:
7697 for x
in get_iterable(db_vnfr
, "kdur")
7699 x
.get("kdu-name") == kdu_name
7700 and x
.get("count-index") == kdu_index
7705 if kdur
.get("name"):
7706 kdur_name
= kdur
.get("name")
7709 await asyncio
.sleep(10)
7711 if vdu_id
and vdu_index
is not None:
7713 f
"Timeout waiting VDU with name={vdu_id} and index={vdu_index} to be intantiated"
7715 if kdu_name
and kdu_index
is not None:
7717 f
"Timeout waiting KDU with name={kdu_name} and index={kdu_index} to be intantiated"
7721 if ee_id
is not None:
7722 _
, _
, service
= ee_id
.partition(".") # remove prefix "namespace."
7723 host_name
= "{}-{}".format(service
, ee_config_descriptor
["metric-service"])
7725 vnfr_id
= vnfr_id
.replace("-", "")
7727 "JOB_NAME": vnfr_id
,
7728 "TARGET_IP": target_ip
,
7729 "EXPORTER_POD_IP": host_name
,
7730 "EXPORTER_POD_PORT": host_port
,
7732 "VNF_MEMBER_INDEX": vnf_member_index
,
7733 "VDUR_NAME": vdur_name
,
7734 "KDUR_NAME": kdur_name
,
7735 "ELEMENT_TYPE": element_type
,
7738 metric_path
= ee_config_descriptor
["metric-path"]
7739 target_port
= ee_config_descriptor
["metric-port"]
7740 vnfr_id
= vnfr_id
.replace("-", "")
7742 "JOB_NAME": vnfr_id
,
7743 "TARGET_IP": target_ip
,
7744 "TARGET_PORT": target_port
,
7745 "METRIC_PATH": metric_path
,
7748 job_list
= parse_job(job_data
, variables
)
7749 # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
7750 for job
in job_list
:
7752 not isinstance(job
.get("job_name"), str)
7753 or vnfr_id
not in job
["job_name"]
7755 job
["job_name"] = vnfr_id
+ "_" + str(SystemRandom().randint(1, 10000))
7756 job
["nsr_id"] = nsr_id
7757 job
["vnfr_id"] = vnfr_id
7760 async def rebuild_start_stop(
7761 self
, nsr_id
, nslcmop_id
, vnf_id
, additional_param
, operation_type
7763 logging_text
= "Task ns={} {}={} ".format(nsr_id
, operation_type
, nslcmop_id
)
7764 self
.logger
.info(logging_text
+ "Enter")
7765 stage
= ["Preparing the environment", ""]
7766 # database nsrs record
7770 # in case of error, indicates what part of scale was failed to put nsr at error status
7771 start_deploy
= time()
7773 db_vnfr
= self
.db
.get_one("vnfrs", {"_id": vnf_id
})
7774 vim_account_id
= db_vnfr
.get("vim-account-id")
7775 vim_info_key
= "vim:" + vim_account_id
7776 vdu_id
= additional_param
["vdu_id"]
7777 vdurs
= [item
for item
in db_vnfr
["vdur"] if item
["vdu-id-ref"] == vdu_id
]
7778 vdur
= find_in_list(
7779 vdurs
, lambda vdu
: vdu
["count-index"] == additional_param
["count-index"]
7782 vdu_vim_name
= vdur
["name"]
7783 vim_vm_id
= vdur
["vim_info"][vim_info_key
]["vim_id"]
7784 target_vim
, _
= next(k_v
for k_v
in vdur
["vim_info"].items())
7786 raise LcmException("Target vdu is not found")
7787 self
.logger
.info("vdu_vim_name >> {} ".format(vdu_vim_name
))
7788 # wait for any previous tasks in process
7789 stage
[1] = "Waiting for previous operations to terminate"
7790 self
.logger
.info(stage
[1])
7791 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
7793 stage
[1] = "Reading from database."
7794 self
.logger
.info(stage
[1])
7795 self
._write
_ns
_status
(
7798 current_operation
=operation_type
.upper(),
7799 current_operation_id
=nslcmop_id
,
7801 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
, queuePosition
=0)
7804 stage
[1] = "Getting nsr={} from db.".format(nsr_id
)
7805 db_nsr_update
["operational-status"] = operation_type
7806 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
7810 "vim_vm_id": vim_vm_id
,
7812 "vdu_index": additional_param
["count-index"],
7813 "vdu_id": vdur
["id"],
7814 "target_vim": target_vim
,
7815 "vim_account_id": vim_account_id
,
7818 stage
[1] = "Sending rebuild request to RO... {}".format(desc
)
7819 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
, queuePosition
=0)
7820 self
.logger
.info("ro nsr id: {}".format(nsr_id
))
7821 result_dict
= await self
.RO
.operate(nsr_id
, desc
, operation_type
)
7822 self
.logger
.info("response from RO: {}".format(result_dict
))
7823 action_id
= result_dict
["action_id"]
7824 await self
._wait
_ng
_ro
(
7829 self
.timeout
.operate
,
7831 "start_stop_rebuild",
7833 return "COMPLETED", "Done"
7834 except (ROclient
.ROClientException
, DbException
, LcmException
) as e
:
7835 self
.logger
.error("Exit Exception {}".format(e
))
7837 except asyncio
.CancelledError
:
7838 self
.logger
.error("Cancelled Exception while '{}'".format(stage
))
7839 exc
= "Operation was cancelled"
7840 except Exception as e
:
7841 exc
= traceback
.format_exc()
7842 self
.logger
.critical(
7843 "Exit Exception {} {}".format(type(e
).__name
__, e
), exc_info
=True
7845 return "FAILED", "Error in operate VNF {}".format(exc
)
7847 def get_vca_cloud_and_credentials(self
, vim_account_id
: str) -> (str, str):
7849 Get VCA Cloud and VCA Cloud Credentials for the VIM account
7851 :param: vim_account_id: VIM Account ID
7853 :return: (cloud_name, cloud_credential)
7855 config
= VimAccountDB
.get_vim_account_with_id(vim_account_id
).get("config", {})
7856 return config
.get("vca_cloud"), config
.get("vca_cloud_credential")
7858 def get_vca_k8s_cloud_and_credentials(self
, vim_account_id
: str) -> (str, str):
7860 Get VCA K8s Cloud and VCA K8s Cloud Credentials for the VIM account
7862 :param: vim_account_id: VIM Account ID
7864 :return: (cloud_name, cloud_credential)
7866 config
= VimAccountDB
.get_vim_account_with_id(vim_account_id
).get("config", {})
7867 return config
.get("vca_k8s_cloud"), config
.get("vca_k8s_cloud_credential")
7869 async def migrate(self
, nsr_id
, nslcmop_id
):
7871 Migrate VNFs and VDUs instances in a NS
7873 :param: nsr_id: NS Instance ID
7874 :param: nslcmop_id: nslcmop ID of migrate
7877 # Try to lock HA task here
7878 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
7879 if not task_is_locked_by_me
:
7881 logging_text
= "Task ns={} migrate ".format(nsr_id
)
7882 self
.logger
.debug(logging_text
+ "Enter")
7883 # get all needed from database
7885 db_nslcmop_update
= {}
7886 nslcmop_operation_state
= None
7890 # in case of error, indicates what part of scale was failed to put nsr at error status
7891 start_deploy
= time()
7894 # wait for any previous tasks in process
7895 step
= "Waiting for previous operations to terminate"
7896 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
7898 self
._write
_ns
_status
(
7901 current_operation
="MIGRATING",
7902 current_operation_id
=nslcmop_id
,
7904 step
= "Getting nslcmop from database"
7906 step
+ " after having waited for previous tasks to be completed"
7908 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
7909 migrate_params
= db_nslcmop
.get("operationParams")
7912 target
.update(migrate_params
)
7913 desc
= await self
.RO
.migrate(nsr_id
, target
)
7914 self
.logger
.debug("RO return > {}".format(desc
))
7915 action_id
= desc
["action_id"]
7916 await self
._wait
_ng
_ro
(
7921 self
.timeout
.migrate
,
7922 operation
="migrate",
7924 except (ROclient
.ROClientException
, DbException
, LcmException
) as e
:
7925 self
.logger
.error("Exit Exception {}".format(e
))
7927 except asyncio
.CancelledError
:
7928 self
.logger
.error("Cancelled Exception while '{}'".format(step
))
7929 exc
= "Operation was cancelled"
7930 except Exception as e
:
7931 exc
= traceback
.format_exc()
7932 self
.logger
.critical(
7933 "Exit Exception {} {}".format(type(e
).__name
__, e
), exc_info
=True
7936 self
._write
_ns
_status
(
7939 current_operation
="IDLE",
7940 current_operation_id
=None,
7943 db_nslcmop_update
["detailed-status"] = "FAILED {}: {}".format(step
, exc
)
7944 nslcmop_operation_state
= "FAILED"
7946 nslcmop_operation_state
= "COMPLETED"
7947 db_nslcmop_update
["detailed-status"] = "Done"
7948 db_nsr_update
["detailed-status"] = "Done"
7950 self
._write
_op
_status
(
7954 operation_state
=nslcmop_operation_state
,
7955 other_update
=db_nslcmop_update
,
7957 if nslcmop_operation_state
:
7961 "nslcmop_id": nslcmop_id
,
7962 "operationState": nslcmop_operation_state
,
7964 await self
.msg
.aiowrite("ns", "migrated", msg
)
7965 except Exception as e
:
7967 logging_text
+ "kafka_write notification Exception {}".format(e
)
7969 self
.logger
.debug(logging_text
+ "Exit")
7970 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_migrate")
7972 async def heal(self
, nsr_id
, nslcmop_id
):
7976 :param nsr_id: ns instance to heal
7977 :param nslcmop_id: operation to run
7981 # Try to lock HA task here
7982 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
7983 if not task_is_locked_by_me
:
7986 logging_text
= "Task ns={} heal={} ".format(nsr_id
, nslcmop_id
)
7987 stage
= ["", "", ""]
7988 tasks_dict_info
= {}
7989 # ^ stage, step, VIM progress
7990 self
.logger
.debug(logging_text
+ "Enter")
7991 # get all needed from database
7993 db_nslcmop_update
= {}
7995 db_vnfrs
= {} # vnf's info indexed by _id
7997 old_operational_status
= ""
7998 old_config_status
= ""
8001 # wait for any previous tasks in process
8002 step
= "Waiting for previous operations to terminate"
8003 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
8004 self
._write
_ns
_status
(
8007 current_operation
="HEALING",
8008 current_operation_id
=nslcmop_id
,
8011 step
= "Getting nslcmop from database"
8013 step
+ " after having waited for previous tasks to be completed"
8015 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
8017 step
= "Getting nsr from database"
8018 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
8019 old_operational_status
= db_nsr
["operational-status"]
8020 old_config_status
= db_nsr
["config-status"]
8023 "_admin.deployed.RO.operational-status": "healing",
8025 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
8027 step
= "Sending heal order to VIM"
8029 logging_text
=logging_text
,
8031 db_nslcmop
=db_nslcmop
,
8036 stage
[1] = "Getting nsd={} from db.".format(db_nsr
["nsd-id"])
8037 self
.logger
.debug(logging_text
+ stage
[1])
8038 nsd
= self
.db
.get_one("nsds", {"_id": db_nsr
["nsd-id"]})
8039 self
.fs
.sync(db_nsr
["nsd-id"])
8041 # read from db: vnfr's of this ns
8042 step
= "Getting vnfrs from db"
8043 db_vnfrs_list
= self
.db
.get_list("vnfrs", {"nsr-id-ref": nsr_id
})
8044 for vnfr
in db_vnfrs_list
:
8045 db_vnfrs
[vnfr
["_id"]] = vnfr
8046 self
.logger
.debug("ns.heal db_vnfrs={}".format(db_vnfrs
))
8048 # Check for each target VNF
8049 target_list
= db_nslcmop
.get("operationParams", {}).get("healVnfData", {})
8050 for target_vnf
in target_list
:
8051 # Find this VNF in the list from DB
8052 vnfr_id
= target_vnf
.get("vnfInstanceId", None)
8054 db_vnfr
= db_vnfrs
[vnfr_id
]
8055 vnfd_id
= db_vnfr
.get("vnfd-id")
8056 vnfd_ref
= db_vnfr
.get("vnfd-ref")
8057 vnfd
= self
.db
.get_one("vnfds", {"_id": vnfd_id
})
8058 base_folder
= vnfd
["_admin"]["storage"]
8063 nsi_id
= None # TODO put nsi_id when this nsr belongs to a NSI
8064 member_vnf_index
= db_vnfr
.get("member-vnf-index-ref")
8066 # Check each target VDU and deploy N2VC
8067 target_vdu_list
= target_vnf
.get("additionalParams", {}).get(
8070 if not target_vdu_list
:
8071 # Codigo nuevo para crear diccionario
8072 target_vdu_list
= []
8073 for existing_vdu
in db_vnfr
.get("vdur"):
8074 vdu_name
= existing_vdu
.get("vdu-name", None)
8075 vdu_index
= existing_vdu
.get("count-index", 0)
8076 vdu_run_day1
= target_vnf
.get("additionalParams", {}).get(
8079 vdu_to_be_healed
= {
8081 "count-index": vdu_index
,
8082 "run-day1": vdu_run_day1
,
8084 target_vdu_list
.append(vdu_to_be_healed
)
8085 for target_vdu
in target_vdu_list
:
8086 deploy_params_vdu
= target_vdu
8087 # Set run-day1 vnf level value if not vdu level value exists
8088 if not deploy_params_vdu
.get("run-day1") and target_vnf
.get(
8089 "additionalParams", {}
8091 deploy_params_vdu
["run-day1"] = target_vnf
[
8094 vdu_name
= target_vdu
.get("vdu-id", None)
8095 # TODO: Get vdu_id from vdud.
8097 # For multi instance VDU count-index is mandatory
8098 # For single session VDU count-indes is 0
8099 vdu_index
= target_vdu
.get("count-index", 0)
8101 # n2vc_redesign STEP 3 to 6 Deploy N2VC
8102 stage
[1] = "Deploying Execution Environments."
8103 self
.logger
.debug(logging_text
+ stage
[1])
8105 # VNF Level charm. Normal case when proxy charms.
8106 # If target instance is management machine continue with actions: recreate EE for native charms or reinject juju key for proxy charms.
8107 descriptor_config
= get_configuration(vnfd
, vnfd_ref
)
8108 if descriptor_config
:
8109 # Continue if healed machine is management machine
8110 vnf_ip_address
= db_vnfr
.get("ip-address")
8111 target_instance
= None
8112 for instance
in db_vnfr
.get("vdur", None):
8114 instance
["vdu-name"] == vdu_name
8115 and instance
["count-index"] == vdu_index
8117 target_instance
= instance
8119 if vnf_ip_address
== target_instance
.get("ip-address"):
8121 logging_text
=logging_text
8122 + "member_vnf_index={}, vdu_name={}, vdu_index={} ".format(
8123 member_vnf_index
, vdu_name
, vdu_index
8127 nslcmop_id
=nslcmop_id
,
8133 member_vnf_index
=member_vnf_index
,
8136 deploy_params
=deploy_params_vdu
,
8137 descriptor_config
=descriptor_config
,
8138 base_folder
=base_folder
,
8139 task_instantiation_info
=tasks_dict_info
,
8143 # VDU Level charm. Normal case with native charms.
8144 descriptor_config
= get_configuration(vnfd
, vdu_name
)
8145 if descriptor_config
:
8147 logging_text
=logging_text
8148 + "member_vnf_index={}, vdu_name={}, vdu_index={} ".format(
8149 member_vnf_index
, vdu_name
, vdu_index
8153 nslcmop_id
=nslcmop_id
,
8159 member_vnf_index
=member_vnf_index
,
8160 vdu_index
=vdu_index
,
8162 deploy_params
=deploy_params_vdu
,
8163 descriptor_config
=descriptor_config
,
8164 base_folder
=base_folder
,
8165 task_instantiation_info
=tasks_dict_info
,
8170 ROclient
.ROClientException
,
8175 self
.logger
.error(logging_text
+ "Exit Exception {}".format(e
))
8177 except asyncio
.CancelledError
:
8179 logging_text
+ "Cancelled Exception while '{}'".format(step
)
8181 exc
= "Operation was cancelled"
8182 except Exception as e
:
8183 exc
= traceback
.format_exc()
8184 self
.logger
.critical(
8185 logging_text
+ "Exit Exception {} {}".format(type(e
).__name
__, e
),
8190 stage
[1] = "Waiting for healing pending tasks."
8191 self
.logger
.debug(logging_text
+ stage
[1])
8192 exc
= await self
._wait
_for
_tasks
(
8195 self
.timeout
.ns_deploy
,
8203 ] = error_description_nslcmop
= "FAILED {}: {}".format(step
, exc
)
8204 nslcmop_operation_state
= "FAILED"
8206 db_nsr_update
["operational-status"] = old_operational_status
8207 db_nsr_update
["config-status"] = old_config_status
8210 ] = "FAILED healing nslcmop={} {}: {}".format(nslcmop_id
, step
, exc
)
8211 for task
, task_name
in tasks_dict_info
.items():
8212 if not task
.done() or task
.cancelled() or task
.exception():
8213 if task_name
.startswith(self
.task_name_deploy_vca
):
8214 # A N2VC task is pending
8215 db_nsr_update
["config-status"] = "failed"
8217 # RO task is pending
8218 db_nsr_update
["operational-status"] = "failed"
8220 error_description_nslcmop
= None
8221 nslcmop_operation_state
= "COMPLETED"
8222 db_nslcmop_update
["detailed-status"] = "Done"
8223 db_nsr_update
["detailed-status"] = "Done"
8224 db_nsr_update
["operational-status"] = "running"
8225 db_nsr_update
["config-status"] = "configured"
8227 self
._write
_op
_status
(
8230 error_message
=error_description_nslcmop
,
8231 operation_state
=nslcmop_operation_state
,
8232 other_update
=db_nslcmop_update
,
8235 self
._write
_ns
_status
(
8238 current_operation
="IDLE",
8239 current_operation_id
=None,
8240 other_update
=db_nsr_update
,
8243 if nslcmop_operation_state
:
8247 "nslcmop_id": nslcmop_id
,
8248 "operationState": nslcmop_operation_state
,
8250 await self
.msg
.aiowrite("ns", "healed", msg
)
8251 except Exception as e
:
8253 logging_text
+ "kafka_write notification Exception {}".format(e
)
8255 self
.logger
.debug(logging_text
+ "Exit")
8256 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_heal")
8267 :param logging_text: preffix text to use at logging
8268 :param nsr_id: nsr identity
8269 :param db_nslcmop: database content of ns operation, in this case, 'instantiate'
8270 :param stage: list with 3 items: [general stage, tasks, vim_specific]. This task will write over vim_specific
8271 :return: None or exception
8274 def get_vim_account(vim_account_id
):
8276 if vim_account_id
in db_vims
:
8277 return db_vims
[vim_account_id
]
8278 db_vim
= self
.db
.get_one("vim_accounts", {"_id": vim_account_id
})
8279 db_vims
[vim_account_id
] = db_vim
8284 ns_params
= db_nslcmop
.get("operationParams")
8285 if ns_params
and ns_params
.get("timeout_ns_heal"):
8286 timeout_ns_heal
= ns_params
["timeout_ns_heal"]
8288 timeout_ns_heal
= self
.timeout
.ns_heal
8292 nslcmop_id
= db_nslcmop
["_id"]
8294 "action_id": nslcmop_id
,
8296 self
.logger
.warning(
8297 "db_nslcmop={} and timeout_ns_heal={}".format(
8298 db_nslcmop
, timeout_ns_heal
8301 target
.update(db_nslcmop
.get("operationParams", {}))
8303 self
.logger
.debug("Send to RO > nsr_id={} target={}".format(nsr_id
, target
))
8304 desc
= await self
.RO
.recreate(nsr_id
, target
)
8305 self
.logger
.debug("RO return > {}".format(desc
))
8306 action_id
= desc
["action_id"]
8307 # waits for RO to complete because Reinjecting juju key at ro can find VM in state Deleted
8308 await self
._wait
_ng
_ro
(
8315 operation
="healing",
8320 "_admin.deployed.RO.operational-status": "running",
8321 "detailed-status": " ".join(stage
),
8323 self
.update_db_2("nsrs", nsr_id
, db_nsr_update
)
8324 self
._write
_op
_status
(nslcmop_id
, stage
)
8326 logging_text
+ "ns healed at RO. RO_id={}".format(action_id
)
8329 except Exception as e
:
8330 stage
[2] = "ERROR healing at VIM"
8331 # self.set_vnfr_at_error(db_vnfrs, str(e))
8333 "Error healing at VIM {}".format(e
),
8334 exc_info
=not isinstance(
8337 ROclient
.ROClientException
,
8363 task_instantiation_info
,
8366 # launch instantiate_N2VC in a asyncio task and register task object
8367 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
8368 # if not found, create one entry and update database
8369 # fill db_nsr._admin.deployed.VCA.<index>
8372 logging_text
+ "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id
, vdu_id
)
8376 get_charm_name
= False
8377 if "execution-environment-list" in descriptor_config
:
8378 ee_list
= descriptor_config
.get("execution-environment-list", [])
8379 elif "juju" in descriptor_config
:
8380 ee_list
= [descriptor_config
] # ns charms
8381 if "execution-environment-list" not in descriptor_config
:
8382 # charm name is only required for ns charms
8383 get_charm_name
= True
8384 else: # other types as script are not supported
8387 for ee_item
in ee_list
:
8390 + "_deploy_n2vc ee_item juju={}, helm={}".format(
8391 ee_item
.get("juju"), ee_item
.get("helm-chart")
8394 ee_descriptor_id
= ee_item
.get("id")
8395 if ee_item
.get("juju"):
8396 vca_name
= ee_item
["juju"].get("charm")
8398 charm_name
= self
.find_charm_name(db_nsr
, str(vca_name
))
8401 if ee_item
["juju"].get("charm") is not None
8404 if ee_item
["juju"].get("cloud") == "k8s":
8405 vca_type
= "k8s_proxy_charm"
8406 elif ee_item
["juju"].get("proxy") is False:
8407 vca_type
= "native_charm"
8408 elif ee_item
.get("helm-chart"):
8409 vca_name
= ee_item
["helm-chart"]
8410 if ee_item
.get("helm-version") and ee_item
.get("helm-version") == "v2":
8413 vca_type
= "helm-v3"
8416 logging_text
+ "skipping non juju neither charm configuration"
8421 for vca_index
, vca_deployed
in enumerate(
8422 db_nsr
["_admin"]["deployed"]["VCA"]
8424 if not vca_deployed
:
8427 vca_deployed
.get("member-vnf-index") == member_vnf_index
8428 and vca_deployed
.get("vdu_id") == vdu_id
8429 and vca_deployed
.get("kdu_name") == kdu_name
8430 and vca_deployed
.get("vdu_count_index", 0) == vdu_index
8431 and vca_deployed
.get("ee_descriptor_id") == ee_descriptor_id
8435 # not found, create one.
8437 "ns" if not member_vnf_index
else "vnf/{}".format(member_vnf_index
)
8440 target
+= "/vdu/{}/{}".format(vdu_id
, vdu_index
or 0)
8442 target
+= "/kdu/{}".format(kdu_name
)
8444 "target_element": target
,
8445 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
8446 "member-vnf-index": member_vnf_index
,
8448 "kdu_name": kdu_name
,
8449 "vdu_count_index": vdu_index
,
8450 "operational-status": "init", # TODO revise
8451 "detailed-status": "", # TODO revise
8452 "step": "initial-deploy", # TODO revise
8454 "vdu_name": vdu_name
,
8456 "ee_descriptor_id": ee_descriptor_id
,
8457 "charm_name": charm_name
,
8461 # create VCA and configurationStatus in db
8463 "_admin.deployed.VCA.{}".format(vca_index
): vca_deployed
,
8464 "configurationStatus.{}".format(vca_index
): dict(),
8466 self
.update_db_2("nsrs", nsr_id
, db_dict
)
8468 db_nsr
["_admin"]["deployed"]["VCA"].append(vca_deployed
)
8470 self
.logger
.debug("N2VC > NSR_ID > {}".format(nsr_id
))
8471 self
.logger
.debug("N2VC > DB_NSR > {}".format(db_nsr
))
8472 self
.logger
.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed
))
8475 task_n2vc
= asyncio
.ensure_future(
8477 logging_text
=logging_text
,
8478 vca_index
=vca_index
,
8484 vdu_index
=vdu_index
,
8485 deploy_params
=deploy_params
,
8486 config_descriptor
=descriptor_config
,
8487 base_folder
=base_folder
,
8488 nslcmop_id
=nslcmop_id
,
8492 ee_config_descriptor
=ee_item
,
8495 self
.lcm_tasks
.register(
8499 "instantiate_N2VC-{}".format(vca_index
),
8502 task_instantiation_info
[
8504 ] = self
.task_name_deploy_vca
+ " {}.{}".format(
8505 member_vnf_index
or "", vdu_id
or ""
8508 async def heal_N2VC(
8525 ee_config_descriptor
,
8527 nsr_id
= db_nsr
["_id"]
8528 db_update_entry
= "_admin.deployed.VCA.{}.".format(vca_index
)
8529 vca_deployed_list
= db_nsr
["_admin"]["deployed"]["VCA"]
8530 vca_deployed
= db_nsr
["_admin"]["deployed"]["VCA"][vca_index
]
8531 osm_config
= {"osm": {"ns_id": db_nsr
["_id"]}}
8533 "collection": "nsrs",
8534 "filter": {"_id": nsr_id
},
8535 "path": db_update_entry
,
8540 element_under_configuration
= nsr_id
8544 vnfr_id
= db_vnfr
["_id"]
8545 osm_config
["osm"]["vnf_id"] = vnfr_id
8547 namespace
= "{nsi}.{ns}".format(nsi
=nsi_id
if nsi_id
else "", ns
=nsr_id
)
8549 if vca_type
== "native_charm":
8552 index_number
= vdu_index
or 0
8555 element_type
= "VNF"
8556 element_under_configuration
= vnfr_id
8557 namespace
+= ".{}-{}".format(vnfr_id
, index_number
)
8559 namespace
+= ".{}-{}".format(vdu_id
, index_number
)
8560 element_type
= "VDU"
8561 element_under_configuration
= "{}-{}".format(vdu_id
, index_number
)
8562 osm_config
["osm"]["vdu_id"] = vdu_id
8564 namespace
+= ".{}".format(kdu_name
)
8565 element_type
= "KDU"
8566 element_under_configuration
= kdu_name
8567 osm_config
["osm"]["kdu_name"] = kdu_name
8570 if base_folder
["pkg-dir"]:
8571 artifact_path
= "{}/{}/{}/{}".format(
8572 base_folder
["folder"],
8573 base_folder
["pkg-dir"],
8576 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
8581 artifact_path
= "{}/Scripts/{}/{}/".format(
8582 base_folder
["folder"],
8585 in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
8590 self
.logger
.debug("Artifact path > {}".format(artifact_path
))
8592 # get initial_config_primitive_list that applies to this element
8593 initial_config_primitive_list
= config_descriptor
.get(
8594 "initial-config-primitive"
8598 "Initial config primitive list > {}".format(
8599 initial_config_primitive_list
8603 # add config if not present for NS charm
8604 ee_descriptor_id
= ee_config_descriptor
.get("id")
8605 self
.logger
.debug("EE Descriptor > {}".format(ee_descriptor_id
))
8606 initial_config_primitive_list
= get_ee_sorted_initial_config_primitive_list(
8607 initial_config_primitive_list
, vca_deployed
, ee_descriptor_id
8611 "Initial config primitive list #2 > {}".format(
8612 initial_config_primitive_list
8615 # n2vc_redesign STEP 3.1
8616 # find old ee_id if exists
8617 ee_id
= vca_deployed
.get("ee_id")
8619 vca_id
= self
.get_vca_id(db_vnfr
, db_nsr
)
8620 # create or register execution environment in VCA. Only for native charms when healing
8621 if vca_type
== "native_charm":
8622 step
= "Waiting to VM being up and getting IP address"
8623 self
.logger
.debug(logging_text
+ step
)
8624 rw_mgmt_ip
= await self
.wait_vm_up_insert_key_ro(
8633 credentials
= {"hostname": rw_mgmt_ip
}
8635 username
= deep_get(
8636 config_descriptor
, ("config-access", "ssh-access", "default-user")
8638 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
8639 # merged. Meanwhile let's get username from initial-config-primitive
8640 if not username
and initial_config_primitive_list
:
8641 for config_primitive
in initial_config_primitive_list
:
8642 for param
in config_primitive
.get("parameter", ()):
8643 if param
["name"] == "ssh-username":
8644 username
= param
["value"]
8648 "Cannot determine the username neither with 'initial-config-primitive' nor with "
8649 "'config-access.ssh-access.default-user'"
8651 credentials
["username"] = username
8653 # n2vc_redesign STEP 3.2
8654 # TODO: Before healing at RO it is needed to destroy native charm units to be deleted.
8655 self
._write
_configuration
_status
(
8657 vca_index
=vca_index
,
8658 status
="REGISTERING",
8659 element_under_configuration
=element_under_configuration
,
8660 element_type
=element_type
,
8663 step
= "register execution environment {}".format(credentials
)
8664 self
.logger
.debug(logging_text
+ step
)
8665 ee_id
= await self
.vca_map
[vca_type
].register_execution_environment(
8666 credentials
=credentials
,
8667 namespace
=namespace
,
8672 # update ee_id en db
8674 "_admin.deployed.VCA.{}.ee_id".format(vca_index
): ee_id
,
8676 self
.update_db_2("nsrs", nsr_id
, db_dict_ee_id
)
8678 # for compatibility with MON/POL modules, the need model and application name at database
8679 # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
8680 # Not sure if this need to be done when healing
8682 ee_id_parts = ee_id.split(".")
8683 db_nsr_update = {db_update_entry + "ee_id": ee_id}
8684 if len(ee_id_parts) >= 2:
8685 model_name = ee_id_parts[0]
8686 application_name = ee_id_parts[1]
8687 db_nsr_update[db_update_entry + "model"] = model_name
8688 db_nsr_update[db_update_entry + "application"] = application_name
8691 # n2vc_redesign STEP 3.3
8692 # Install configuration software. Only for native charms.
8693 step
= "Install configuration Software"
8695 self
._write
_configuration
_status
(
8697 vca_index
=vca_index
,
8698 status
="INSTALLING SW",
8699 element_under_configuration
=element_under_configuration
,
8700 element_type
=element_type
,
8701 # other_update=db_nsr_update,
8705 # TODO check if already done
8706 self
.logger
.debug(logging_text
+ step
)
8708 if vca_type
== "native_charm":
8709 config_primitive
= next(
8710 (p
for p
in initial_config_primitive_list
if p
["name"] == "config"),
8713 if config_primitive
:
8714 config
= self
._map
_primitive
_params
(
8715 config_primitive
, {}, deploy_params
8717 await self
.vca_map
[vca_type
].install_configuration_sw(
8719 artifact_path
=artifact_path
,
8727 # write in db flag of configuration_sw already installed
8729 "nsrs", nsr_id
, {db_update_entry
+ "config_sw_installed": True}
8732 # Not sure if this need to be done when healing
8734 # add relations for this VCA (wait for other peers related with this VCA)
8735 await self._add_vca_relations(
8736 logging_text=logging_text,
8739 vca_index=vca_index,
8743 # if SSH access is required, then get execution environment SSH public
8744 # if native charm we have waited already to VM be UP
8745 if vca_type
in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
8748 # self.logger.debug("get ssh key block")
8750 config_descriptor
, ("config-access", "ssh-access", "required")
8752 # self.logger.debug("ssh key needed")
8753 # Needed to inject a ssh key
8756 ("config-access", "ssh-access", "default-user"),
8758 step
= "Install configuration Software, getting public ssh key"
8759 pub_key
= await self
.vca_map
[vca_type
].get_ee_ssh_public__key(
8760 ee_id
=ee_id
, db_dict
=db_dict
, vca_id
=vca_id
8763 step
= "Insert public key into VM user={} ssh_key={}".format(
8767 # self.logger.debug("no need to get ssh key")
8768 step
= "Waiting to VM being up and getting IP address"
8769 self
.logger
.debug(logging_text
+ step
)
8771 # n2vc_redesign STEP 5.1
8772 # wait for RO (ip-address) Insert pub_key into VM
8773 # IMPORTANT: We need do wait for RO to complete healing operation.
8774 await self
._wait
_heal
_ro
(nsr_id
, self
.timeout
.ns_heal
)
8777 rw_mgmt_ip
= await self
.wait_kdu_up(
8778 logging_text
, nsr_id
, vnfr_id
, kdu_name
8781 rw_mgmt_ip
= await self
.wait_vm_up_insert_key_ro(
8791 rw_mgmt_ip
= None # This is for a NS configuration
8793 self
.logger
.debug(logging_text
+ " VM_ip_address={}".format(rw_mgmt_ip
))
8795 # store rw_mgmt_ip in deploy params for later replacement
8796 deploy_params
["rw_mgmt_ip"] = rw_mgmt_ip
8799 # get run-day1 operation parameter
8800 runDay1
= deploy_params
.get("run-day1", False)
8802 "Healing vnf={}, vdu={}, runDay1 ={}".format(vnfr_id
, vdu_id
, runDay1
)
8805 # n2vc_redesign STEP 6 Execute initial config primitive
8806 step
= "execute initial config primitive"
8808 # wait for dependent primitives execution (NS -> VNF -> VDU)
8809 if initial_config_primitive_list
:
8810 await self
._wait
_dependent
_n
2vc
(
8811 nsr_id
, vca_deployed_list
, vca_index
8814 # stage, in function of element type: vdu, kdu, vnf or ns
8815 my_vca
= vca_deployed_list
[vca_index
]
8816 if my_vca
.get("vdu_id") or my_vca
.get("kdu_name"):
8818 stage
[0] = "Stage 3/5: running Day-1 primitives for VDU."
8819 elif my_vca
.get("member-vnf-index"):
8821 stage
[0] = "Stage 4/5: running Day-1 primitives for VNF."
8824 stage
[0] = "Stage 5/5: running Day-1 primitives for NS."
8826 self
._write
_configuration
_status
(
8827 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="EXECUTING PRIMITIVE"
8830 self
._write
_op
_status
(op_id
=nslcmop_id
, stage
=stage
)
8832 check_if_terminated_needed
= True
8833 for initial_config_primitive
in initial_config_primitive_list
:
8834 # adding information on the vca_deployed if it is a NS execution environment
8835 if not vca_deployed
["member-vnf-index"]:
8836 deploy_params
["ns_config_info"] = json
.dumps(
8837 self
._get
_ns
_config
_info
(nsr_id
)
8839 # TODO check if already done
8840 primitive_params_
= self
._map
_primitive
_params
(
8841 initial_config_primitive
, {}, deploy_params
8844 step
= "execute primitive '{}' params '{}'".format(
8845 initial_config_primitive
["name"], primitive_params_
8847 self
.logger
.debug(logging_text
+ step
)
8848 await self
.vca_map
[vca_type
].exec_primitive(
8850 primitive_name
=initial_config_primitive
["name"],
8851 params_dict
=primitive_params_
,
8856 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
8857 if check_if_terminated_needed
:
8858 if config_descriptor
.get("terminate-config-primitive"):
8862 {db_update_entry
+ "needed_terminate": True},
8864 check_if_terminated_needed
= False
8866 # TODO register in database that primitive is done
8868 # STEP 7 Configure metrics
8869 # Not sure if this need to be done when healing
8871 if vca_type == "helm" or vca_type == "helm-v3":
8872 prometheus_jobs = await self.extract_prometheus_scrape_jobs(
8874 artifact_path=artifact_path,
8875 ee_config_descriptor=ee_config_descriptor,
8878 target_ip=rw_mgmt_ip,
8884 {db_update_entry + "prometheus_jobs": prometheus_jobs},
8887 for job in prometheus_jobs:
8890 {"job_name": job["job_name"]},
8893 fail_on_empty=False,
8897 step
= "instantiated at VCA"
8898 self
.logger
.debug(logging_text
+ step
)
8900 self
._write
_configuration
_status
(
8901 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="READY"
8904 except Exception as e
: # TODO not use Exception but N2VC exception
8905 # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
8907 e
, (DbException
, N2VCException
, LcmException
, asyncio
.CancelledError
)
8910 "Exception while {} : {}".format(step
, e
), exc_info
=True
8912 self
._write
_configuration
_status
(
8913 nsr_id
=nsr_id
, vca_index
=vca_index
, status
="BROKEN"
8915 raise LcmException("{} {}".format(step
, e
)) from e
8917 async def _wait_heal_ro(
8923 while time() <= start_time
+ timeout
:
8924 db_nsr
= self
.db
.get_one("nsrs", {"_id": nsr_id
})
8925 operational_status_ro
= db_nsr
["_admin"]["deployed"]["RO"][
8926 "operational-status"
8928 self
.logger
.debug("Wait Heal RO > {}".format(operational_status_ro
))
8929 if operational_status_ro
!= "healing":
8931 await asyncio
.sleep(15)
8932 else: # timeout_ns_deploy
8933 raise NgRoException("Timeout waiting ns to deploy")
8935 async def vertical_scale(self
, nsr_id
, nslcmop_id
):
8937 Vertical Scale the VDUs in a NS
8939 :param: nsr_id: NS Instance ID
8940 :param: nslcmop_id: nslcmop ID of migrate
8943 # Try to lock HA task here
8944 task_is_locked_by_me
= self
.lcm_tasks
.lock_HA("ns", "nslcmops", nslcmop_id
)
8945 if not task_is_locked_by_me
:
8947 logging_text
= "Task ns={} vertical scale ".format(nsr_id
)
8948 self
.logger
.debug(logging_text
+ "Enter")
8949 # get all needed from database
8951 db_nslcmop_update
= {}
8952 nslcmop_operation_state
= None
8956 # in case of error, indicates what part of scale was failed to put nsr at error status
8957 start_deploy
= time()
8960 # wait for any previous tasks in process
8961 step
= "Waiting for previous operations to terminate"
8962 await self
.lcm_tasks
.waitfor_related_HA("ns", "nslcmops", nslcmop_id
)
8964 self
._write
_ns
_status
(
8967 current_operation
="VerticalScale",
8968 current_operation_id
=nslcmop_id
,
8970 step
= "Getting nslcmop from database"
8972 step
+ " after having waited for previous tasks to be completed"
8974 db_nslcmop
= self
.db
.get_one("nslcmops", {"_id": nslcmop_id
})
8975 operationParams
= db_nslcmop
.get("operationParams")
8977 target
.update(operationParams
)
8978 desc
= await self
.RO
.vertical_scale(nsr_id
, target
)
8979 self
.logger
.debug("RO return > {}".format(desc
))
8980 action_id
= desc
["action_id"]
8981 await self
._wait
_ng
_ro
(
8986 self
.timeout
.verticalscale
,
8987 operation
="verticalscale",
8989 except (ROclient
.ROClientException
, DbException
, LcmException
) as e
:
8990 self
.logger
.error("Exit Exception {}".format(e
))
8992 except asyncio
.CancelledError
:
8993 self
.logger
.error("Cancelled Exception while '{}'".format(step
))
8994 exc
= "Operation was cancelled"
8995 except Exception as e
:
8996 exc
= traceback
.format_exc()
8997 self
.logger
.critical(
8998 "Exit Exception {} {}".format(type(e
).__name
__, e
), exc_info
=True
9001 self
._write
_ns
_status
(
9004 current_operation
="IDLE",
9005 current_operation_id
=None,
9008 db_nslcmop_update
["detailed-status"] = "FAILED {}: {}".format(step
, exc
)
9009 nslcmop_operation_state
= "FAILED"
9011 nslcmop_operation_state
= "COMPLETED"
9012 db_nslcmop_update
["detailed-status"] = "Done"
9013 db_nsr_update
["detailed-status"] = "Done"
9015 self
._write
_op
_status
(
9019 operation_state
=nslcmop_operation_state
,
9020 other_update
=db_nslcmop_update
,
9022 if nslcmop_operation_state
:
9026 "nslcmop_id": nslcmop_id
,
9027 "operationState": nslcmop_operation_state
,
9029 await self
.msg
.aiowrite("ns", "verticalscaled", msg
)
9030 except Exception as e
:
9032 logging_text
+ "kafka_write notification Exception {}".format(e
)
9034 self
.logger
.debug(logging_text
+ "Exit")
9035 self
.lcm_tasks
.remove("ns", nsr_id
, nslcmop_id
, "ns_verticalscale")