blob: f1c3fa1ffe37f8b19f5ad92eb325a0da9590cf06 [file] [log] [blame]
tierno59d22d22018-09-25 18:10:19 +02001# -*- coding: utf-8 -*-
2
tierno2e215512018-11-28 09:37:52 +00003##
4# Copyright 2018 Telefonica S.A.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17##
18
tierno59d22d22018-09-25 18:10:19 +020019import asyncio
20import yaml
21import logging
22import logging.handlers
tierno59d22d22018-09-25 18:10:19 +020023import traceback
David Garciad4816682019-12-09 14:57:43 +010024import json
tierno72ef84f2020-10-06 08:22:07 +000025from jinja2 import Environment, TemplateError, TemplateNotFound, StrictUndefined, UndefinedError
tierno59d22d22018-09-25 18:10:19 +020026
tierno77677d92019-08-22 13:46:35 +000027from osm_lcm import ROclient
tierno69f0d382020-05-07 13:08:09 +000028from osm_lcm.ng_ro import NgRoClient, NgRoException
tierno744303e2020-01-13 16:46:31 +000029from osm_lcm.lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase, deep_get, get_iterable, populate_dict
bravof922c4172020-11-24 21:21:43 -030030from osm_lcm.data_utils.nsd import get_vnf_profiles
garciaalea77ced72021-02-17 19:09:12 -030031from osm_lcm.data_utils.vnfd import get_vdu_list, get_vdu_profile, \
bravof922c4172020-11-24 21:21:43 -030032 get_ee_sorted_initial_config_primitive_list, get_ee_sorted_terminate_config_primitive_list, \
garciaalea77ced72021-02-17 19:09:12 -030033 get_kdu_list, get_virtual_link_profiles, get_vdu, get_configuration, \
garciaale762b6e72021-02-22 18:02:07 -030034 get_vdu_index, get_scaling_aspect, get_number_of_instances, get_juju_ee_ref
bravof922c4172020-11-24 21:21:43 -030035from osm_lcm.data_utils.list_utils import find_in_list
aktasf0f83a32021-02-12 22:19:10 +030036from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index
bravof922c4172020-11-24 21:21:43 -030037from osm_lcm.data_utils.dict_utils import parse_yaml_strings
38from osm_lcm.data_utils.database.vim_account import VimAccountDB
calvinosanch9f9c6f22019-11-04 13:37:39 +010039from n2vc.k8s_helm_conn import K8sHelmConnector
lloretgalleg18ebc3a2020-10-22 09:54:51 +000040from n2vc.k8s_helm3_conn import K8sHelm3Connector
Adam Israelbaacc302019-12-01 12:41:39 -050041from n2vc.k8s_juju_conn import K8sJujuConnector
tierno59d22d22018-09-25 18:10:19 +020042
tierno27246d82018-09-27 15:59:09 +020043from osm_common.dbbase import DbException
tierno59d22d22018-09-25 18:10:19 +020044from osm_common.fsbase import FsException
quilesj7e13aeb2019-10-08 13:34:55 +020045
bravof922c4172020-11-24 21:21:43 -030046from osm_lcm.data_utils.database.database import Database
47from osm_lcm.data_utils.filesystem.filesystem import Filesystem
48
quilesj7e13aeb2019-10-08 13:34:55 +020049from n2vc.n2vc_juju_conn import N2VCJujuConnector
tiernof59ad6c2020-04-08 12:50:52 +000050from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException
tierno59d22d22018-09-25 18:10:19 +020051
tierno588547c2020-07-01 15:30:20 +000052from osm_lcm.lcm_helm_conn import LCMHelmConn
53
tierno27246d82018-09-27 15:59:09 +020054from copy import copy, deepcopy
tierno59d22d22018-09-25 18:10:19 +020055from time import time
tierno27246d82018-09-27 15:59:09 +020056from uuid import uuid4
lloretgalleg7c121132020-07-08 07:53:22 +000057
tiernob996d942020-07-03 14:52:28 +000058from random import randint
tierno59d22d22018-09-25 18:10:19 +020059
tierno69f0d382020-05-07 13:08:09 +000060__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tierno59d22d22018-09-25 18:10:19 +020061
62
63class NsLcm(LcmBase):
tierno63de62e2018-10-31 16:38:52 +010064 timeout_vca_on_error = 5 * 60 # Time for charm from first time at blocked,error status to mark as failed
tierno744303e2020-01-13 16:46:31 +000065 timeout_ns_deploy = 2 * 3600 # default global timeout for deployment a ns
tiernoe876f672020-02-13 14:34:48 +000066 timeout_ns_terminate = 1800 # default global timeout for un deployment a ns
garciadeblasf9b04952019-04-09 18:53:58 +020067 timeout_charm_delete = 10 * 60
David Garciaf6919842020-05-21 16:41:07 +020068 timeout_primitive = 30 * 60 # timeout for primitive execution
69 timeout_progress_primitive = 10 * 60 # timeout for some progress in a primitive execution
tierno59d22d22018-09-25 18:10:19 +020070
kuuseac3a8882019-10-03 10:48:06 +020071 SUBOPERATION_STATUS_NOT_FOUND = -1
72 SUBOPERATION_STATUS_NEW = -2
73 SUBOPERATION_STATUS_SKIP = -3
tiernoa2143262020-03-27 16:20:40 +000074 task_name_deploy_vca = "Deploying VCA"
kuuseac3a8882019-10-03 10:48:06 +020075
bravof922c4172020-11-24 21:21:43 -030076 def __init__(self, msg, lcm_tasks, config, loop, prometheus=None):
tierno59d22d22018-09-25 18:10:19 +020077 """
78 Init, Connect to database, filesystem storage, and messaging
79 :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
80 :return: None
81 """
quilesj7e13aeb2019-10-08 13:34:55 +020082 super().__init__(
quilesj7e13aeb2019-10-08 13:34:55 +020083 msg=msg,
quilesj7e13aeb2019-10-08 13:34:55 +020084 logger=logging.getLogger('lcm.ns')
85 )
86
bravof922c4172020-11-24 21:21:43 -030087 self.db = Database().instance.db
88 self.fs = Filesystem().instance.fs
tierno59d22d22018-09-25 18:10:19 +020089 self.loop = loop
90 self.lcm_tasks = lcm_tasks
tierno744303e2020-01-13 16:46:31 +000091 self.timeout = config["timeout"]
92 self.ro_config = config["ro_config"]
tierno69f0d382020-05-07 13:08:09 +000093 self.ng_ro = config["ro_config"].get("ng")
tierno744303e2020-01-13 16:46:31 +000094 self.vca_config = config["VCA"].copy()
tierno59d22d22018-09-25 18:10:19 +020095
quilesj7e13aeb2019-10-08 13:34:55 +020096 # create N2VC connector
David Garciaaae391f2020-11-09 11:12:54 +010097 self.n2vc = N2VCJujuConnector(
tierno59d22d22018-09-25 18:10:19 +020098 log=self.logger,
quilesj7e13aeb2019-10-08 13:34:55 +020099 loop=self.loop,
100 url='{}:{}'.format(self.vca_config['host'], self.vca_config['port']),
101 username=self.vca_config.get('user', None),
102 vca_config=self.vca_config,
bravof922c4172020-11-24 21:21:43 -0300103 on_update_db=self._on_update_n2vc_db,
104 fs=self.fs,
105 db=self.db
tierno59d22d22018-09-25 18:10:19 +0200106 )
quilesj7e13aeb2019-10-08 13:34:55 +0200107
tierno588547c2020-07-01 15:30:20 +0000108 self.conn_helm_ee = LCMHelmConn(
tierno588547c2020-07-01 15:30:20 +0000109 log=self.logger,
110 loop=self.loop,
111 url=None,
112 username=None,
113 vca_config=self.vca_config,
114 on_update_db=self._on_update_n2vc_db
115 )
116
lloretgalleg18ebc3a2020-10-22 09:54:51 +0000117 self.k8sclusterhelm2 = K8sHelmConnector(
calvinosanch9f9c6f22019-11-04 13:37:39 +0100118 kubectl_command=self.vca_config.get("kubectlpath"),
119 helm_command=self.vca_config.get("helmpath"),
calvinosanch9f9c6f22019-11-04 13:37:39 +0100120 log=self.logger,
calvinosanch9f9c6f22019-11-04 13:37:39 +0100121 on_update_db=None,
bravof922c4172020-11-24 21:21:43 -0300122 fs=self.fs,
123 db=self.db
calvinosanch9f9c6f22019-11-04 13:37:39 +0100124 )
125
lloretgalleg18ebc3a2020-10-22 09:54:51 +0000126 self.k8sclusterhelm3 = K8sHelm3Connector(
127 kubectl_command=self.vca_config.get("kubectlpath"),
128 helm_command=self.vca_config.get("helm3path"),
129 fs=self.fs,
130 log=self.logger,
131 db=self.db,
132 on_update_db=None,
133 )
134
Adam Israelbaacc302019-12-01 12:41:39 -0500135 self.k8sclusterjuju = K8sJujuConnector(
136 kubectl_command=self.vca_config.get("kubectlpath"),
137 juju_command=self.vca_config.get("jujupath"),
Adam Israelbaacc302019-12-01 12:41:39 -0500138 log=self.logger,
David Garciaba89cbb2020-10-16 13:05:34 +0200139 loop=self.loop,
Adam Israelbaacc302019-12-01 12:41:39 -0500140 on_update_db=None,
David Garciaba89cbb2020-10-16 13:05:34 +0200141 vca_config=self.vca_config,
bravof922c4172020-11-24 21:21:43 -0300142 fs=self.fs,
143 db=self.db
Adam Israelbaacc302019-12-01 12:41:39 -0500144 )
145
tiernoa2143262020-03-27 16:20:40 +0000146 self.k8scluster_map = {
lloretgalleg18ebc3a2020-10-22 09:54:51 +0000147 "helm-chart": self.k8sclusterhelm2,
148 "helm-chart-v3": self.k8sclusterhelm3,
149 "chart": self.k8sclusterhelm3,
tiernoa2143262020-03-27 16:20:40 +0000150 "juju-bundle": self.k8sclusterjuju,
151 "juju": self.k8sclusterjuju,
152 }
tierno588547c2020-07-01 15:30:20 +0000153
154 self.vca_map = {
155 "lxc_proxy_charm": self.n2vc,
156 "native_charm": self.n2vc,
157 "k8s_proxy_charm": self.n2vc,
lloretgalleg18ebc3a2020-10-22 09:54:51 +0000158 "helm": self.conn_helm_ee,
159 "helm-v3": self.conn_helm_ee
tierno588547c2020-07-01 15:30:20 +0000160 }
161
tiernob996d942020-07-03 14:52:28 +0000162 self.prometheus = prometheus
163
quilesj7e13aeb2019-10-08 13:34:55 +0200164 # create RO client
bravof922c4172020-11-24 21:21:43 -0300165 self.RO = NgRoClient(self.loop, **self.ro_config)
tierno59d22d22018-09-25 18:10:19 +0200166
tierno2357f4e2020-10-19 16:38:59 +0000167 @staticmethod
168 def increment_ip_mac(ip_mac, vm_index=1):
169 if not isinstance(ip_mac, str):
170 return ip_mac
171 try:
172 # try with ipv4 look for last dot
173 i = ip_mac.rfind(".")
174 if i > 0:
175 i += 1
176 return "{}{}".format(ip_mac[:i], int(ip_mac[i:]) + vm_index)
177 # try with ipv6 or mac look for last colon. Operate in hex
178 i = ip_mac.rfind(":")
179 if i > 0:
180 i += 1
181 # format in hex, len can be 2 for mac or 4 for ipv6
182 return ("{}{:0" + str(len(ip_mac) - i) + "x}").format(ip_mac[:i], int(ip_mac[i:], 16) + vm_index)
183 except Exception:
184 pass
185 return None
186
quilesj3655ae02019-12-12 16:08:35 +0000187 def _on_update_ro_db(self, nsrs_id, ro_descriptor):
quilesj7e13aeb2019-10-08 13:34:55 +0200188
quilesj3655ae02019-12-12 16:08:35 +0000189 # self.logger.debug('_on_update_ro_db(nsrs_id={}'.format(nsrs_id))
190
191 try:
192 # TODO filter RO descriptor fields...
193
194 # write to database
195 db_dict = dict()
196 # db_dict['deploymentStatus'] = yaml.dump(ro_descriptor, default_flow_style=False, indent=2)
197 db_dict['deploymentStatus'] = ro_descriptor
198 self.update_db_2("nsrs", nsrs_id, db_dict)
199
200 except Exception as e:
201 self.logger.warn('Cannot write database RO deployment for ns={} -> {}'.format(nsrs_id, e))
202
203 async def _on_update_n2vc_db(self, table, filter, path, updated_data):
204
quilesj69a722c2020-01-09 08:30:17 +0000205 # remove last dot from path (if exists)
206 if path.endswith('.'):
207 path = path[:-1]
208
quilesj3655ae02019-12-12 16:08:35 +0000209 # self.logger.debug('_on_update_n2vc_db(table={}, filter={}, path={}, updated_data={}'
210 # .format(table, filter, path, updated_data))
211
212 try:
213
214 nsr_id = filter.get('_id')
215
216 # read ns record from database
217 nsr = self.db.get_one(table='nsrs', q_filter=filter)
218 current_ns_status = nsr.get('nsState')
219
220 # get vca status for NS
quilesj69a722c2020-01-09 08:30:17 +0000221 status_dict = await self.n2vc.get_status(namespace='.' + nsr_id, yaml_format=False)
quilesj3655ae02019-12-12 16:08:35 +0000222
223 # vcaStatus
224 db_dict = dict()
225 db_dict['vcaStatus'] = status_dict
226
227 # update configurationStatus for this VCA
228 try:
229 vca_index = int(path[path.rfind(".")+1:])
230
231 vca_list = deep_get(target_dict=nsr, key_list=('_admin', 'deployed', 'VCA'))
232 vca_status = vca_list[vca_index].get('status')
233
234 configuration_status_list = nsr.get('configurationStatus')
235 config_status = configuration_status_list[vca_index].get('status')
236
237 if config_status == 'BROKEN' and vca_status != 'failed':
238 db_dict['configurationStatus'][vca_index] = 'READY'
239 elif config_status != 'BROKEN' and vca_status == 'failed':
240 db_dict['configurationStatus'][vca_index] = 'BROKEN'
241 except Exception as e:
242 # not update configurationStatus
243 self.logger.debug('Error updating vca_index (ignore): {}'.format(e))
244
245 # if nsState = 'READY' check if juju is reporting some error => nsState = 'DEGRADED'
246 # if nsState = 'DEGRADED' check if all is OK
247 is_degraded = False
248 if current_ns_status in ('READY', 'DEGRADED'):
249 error_description = ''
250 # check machines
251 if status_dict.get('machines'):
252 for machine_id in status_dict.get('machines'):
253 machine = status_dict.get('machines').get(machine_id)
254 # check machine agent-status
255 if machine.get('agent-status'):
256 s = machine.get('agent-status').get('status')
257 if s != 'started':
258 is_degraded = True
259 error_description += 'machine {} agent-status={} ; '.format(machine_id, s)
260 # check machine instance status
261 if machine.get('instance-status'):
262 s = machine.get('instance-status').get('status')
263 if s != 'running':
264 is_degraded = True
265 error_description += 'machine {} instance-status={} ; '.format(machine_id, s)
266 # check applications
267 if status_dict.get('applications'):
268 for app_id in status_dict.get('applications'):
269 app = status_dict.get('applications').get(app_id)
270 # check application status
271 if app.get('status'):
272 s = app.get('status').get('status')
273 if s != 'active':
274 is_degraded = True
275 error_description += 'application {} status={} ; '.format(app_id, s)
276
277 if error_description:
278 db_dict['errorDescription'] = error_description
279 if current_ns_status == 'READY' and is_degraded:
280 db_dict['nsState'] = 'DEGRADED'
281 if current_ns_status == 'DEGRADED' and not is_degraded:
282 db_dict['nsState'] = 'READY'
283
284 # write to database
285 self.update_db_2("nsrs", nsr_id, db_dict)
286
tierno51183952020-04-03 15:48:18 +0000287 except (asyncio.CancelledError, asyncio.TimeoutError):
288 raise
quilesj3655ae02019-12-12 16:08:35 +0000289 except Exception as e:
290 self.logger.warn('Error updating NS state for ns={}: {}'.format(nsr_id, e))
quilesj7e13aeb2019-10-08 13:34:55 +0200291
tierno72ef84f2020-10-06 08:22:07 +0000292 @staticmethod
293 def _parse_cloud_init(cloud_init_text, additional_params, vnfd_id, vdu_id):
294 try:
295 env = Environment(undefined=StrictUndefined)
296 template = env.from_string(cloud_init_text)
297 return template.render(additional_params or {})
298 except UndefinedError as e:
299 raise LcmException("Variable {} at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
300 "file, must be provided in the instantiation parameters inside the "
301 "'additionalParamsForVnf/Vdu' block".format(e, vnfd_id, vdu_id))
302 except (TemplateError, TemplateNotFound) as e:
303 raise LcmException("Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".
304 format(vnfd_id, vdu_id, e))
305
bravof922c4172020-11-24 21:21:43 -0300306 def _get_vdu_cloud_init_content(self, vdu, vnfd):
307 cloud_init_content = cloud_init_file = None
tierno72ef84f2020-10-06 08:22:07 +0000308 try:
tierno72ef84f2020-10-06 08:22:07 +0000309 if vdu.get("cloud-init-file"):
310 base_folder = vnfd["_admin"]["storage"]
311 cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
312 vdu["cloud-init-file"])
313 with self.fs.file_open(cloud_init_file, "r") as ci_file:
314 cloud_init_content = ci_file.read()
315 elif vdu.get("cloud-init"):
316 cloud_init_content = vdu["cloud-init"]
317
318 return cloud_init_content
319 except FsException as e:
320 raise LcmException("Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".
321 format(vnfd["id"], vdu["id"], cloud_init_file, e))
322
tierno72ef84f2020-10-06 08:22:07 +0000323 def _get_vdu_additional_params(self, db_vnfr, vdu_id):
324 vdur = next(vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"])
325 additional_params = vdur.get("additionalParams")
bravof922c4172020-11-24 21:21:43 -0300326 return parse_yaml_strings(additional_params)
tierno72ef84f2020-10-06 08:22:07 +0000327
gcalvino35be9152018-12-20 09:33:12 +0100328 def vnfd2RO(self, vnfd, new_id=None, additionalParams=None, nsrId=None):
tierno59d22d22018-09-25 18:10:19 +0200329 """
330 Converts creates a new vnfd descriptor for RO base on input OSM IM vnfd
331 :param vnfd: input vnfd
332 :param new_id: overrides vnf id if provided
tierno8a518872018-12-21 13:42:14 +0000333 :param additionalParams: Instantiation params for VNFs provided
gcalvino35be9152018-12-20 09:33:12 +0100334 :param nsrId: Id of the NSR
tierno59d22d22018-09-25 18:10:19 +0200335 :return: copy of vnfd
336 """
tierno72ef84f2020-10-06 08:22:07 +0000337 vnfd_RO = deepcopy(vnfd)
338 # remove unused by RO configuration, monitoring, scaling and internal keys
339 vnfd_RO.pop("_id", None)
340 vnfd_RO.pop("_admin", None)
tierno72ef84f2020-10-06 08:22:07 +0000341 vnfd_RO.pop("monitoring-param", None)
342 vnfd_RO.pop("scaling-group-descriptor", None)
343 vnfd_RO.pop("kdu", None)
344 vnfd_RO.pop("k8s-cluster", None)
345 if new_id:
346 vnfd_RO["id"] = new_id
tierno8a518872018-12-21 13:42:14 +0000347
tierno72ef84f2020-10-06 08:22:07 +0000348 # parse cloud-init or cloud-init-file with the provided variables using Jinja2
349 for vdu in get_iterable(vnfd_RO, "vdu"):
350 vdu.pop("cloud-init-file", None)
351 vdu.pop("cloud-init", None)
352 return vnfd_RO
tierno59d22d22018-09-25 18:10:19 +0200353
tierno2357f4e2020-10-19 16:38:59 +0000354 @staticmethod
355 def ip_profile_2_RO(ip_profile):
356 RO_ip_profile = deepcopy(ip_profile)
357 if "dns-server" in RO_ip_profile:
358 if isinstance(RO_ip_profile["dns-server"], list):
359 RO_ip_profile["dns-address"] = []
360 for ds in RO_ip_profile.pop("dns-server"):
361 RO_ip_profile["dns-address"].append(ds['address'])
362 else:
363 RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
364 if RO_ip_profile.get("ip-version") == "ipv4":
365 RO_ip_profile["ip-version"] = "IPv4"
366 if RO_ip_profile.get("ip-version") == "ipv6":
367 RO_ip_profile["ip-version"] = "IPv6"
368 if "dhcp-params" in RO_ip_profile:
369 RO_ip_profile["dhcp"] = RO_ip_profile.pop("dhcp-params")
370 return RO_ip_profile
371
bravof922c4172020-11-24 21:21:43 -0300372 def _get_ro_vim_id_for_vim_account(self, vim_account):
373 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account})
374 if db_vim["_admin"]["operationalState"] != "ENABLED":
375 raise LcmException("VIM={} is not available. operationalState={}".format(
376 vim_account, db_vim["_admin"]["operationalState"]))
377 RO_vim_id = db_vim["_admin"]["deployed"]["RO"]
378 return RO_vim_id
tierno59d22d22018-09-25 18:10:19 +0200379
bravof922c4172020-11-24 21:21:43 -0300380 def get_ro_wim_id_for_wim_account(self, wim_account):
381 if isinstance(wim_account, str):
382 db_wim = self.db.get_one("wim_accounts", {"_id": wim_account})
383 if db_wim["_admin"]["operationalState"] != "ENABLED":
384 raise LcmException("WIM={} is not available. operationalState={}".format(
385 wim_account, db_wim["_admin"]["operationalState"]))
386 RO_wim_id = db_wim["_admin"]["deployed"]["RO-account"]
387 return RO_wim_id
388 else:
389 return wim_account
tierno59d22d22018-09-25 18:10:19 +0200390
tierno2357f4e2020-10-19 16:38:59 +0000391 def scale_vnfr(self, db_vnfr, vdu_create=None, vdu_delete=None, mark_delete=False):
tierno27246d82018-09-27 15:59:09 +0200392
tierno2357f4e2020-10-19 16:38:59 +0000393 db_vdu_push_list = []
394 db_update = {"_admin.modified": time()}
395 if vdu_create:
396 for vdu_id, vdu_count in vdu_create.items():
397 vdur = next((vdur for vdur in reversed(db_vnfr["vdur"]) if vdur["vdu-id-ref"] == vdu_id), None)
398 if not vdur:
399 raise LcmException("Error scaling OUT VNFR for {}. There is not any existing vnfr. Scaled to 0?".
400 format(vdu_id))
401
402 for count in range(vdu_count):
403 vdur_copy = deepcopy(vdur)
404 vdur_copy["status"] = "BUILD"
405 vdur_copy["status-detailed"] = None
406 vdur_copy["ip-address"]: None
tierno683eb392020-09-25 12:33:15 +0000407 vdur_copy["_id"] = str(uuid4())
tierno2357f4e2020-10-19 16:38:59 +0000408 vdur_copy["count-index"] += count + 1
409 vdur_copy["id"] = "{}-{}".format(vdur_copy["vdu-id-ref"], vdur_copy["count-index"])
410 vdur_copy.pop("vim_info", None)
411 for iface in vdur_copy["interfaces"]:
412 if iface.get("fixed-ip"):
413 iface["ip-address"] = self.increment_ip_mac(iface["ip-address"], count+1)
414 else:
415 iface.pop("ip-address", None)
416 if iface.get("fixed-mac"):
417 iface["mac-address"] = self.increment_ip_mac(iface["mac-address"], count+1)
418 else:
419 iface.pop("mac-address", None)
420 iface.pop("mgmt_vnf", None) # only first vdu can be managment of vnf
421 db_vdu_push_list.append(vdur_copy)
422 # self.logger.debug("scale out, adding vdu={}".format(vdur_copy))
tierno27246d82018-09-27 15:59:09 +0200423 if vdu_delete:
tierno2357f4e2020-10-19 16:38:59 +0000424 for vdu_id, vdu_count in vdu_delete.items():
425 if mark_delete:
426 indexes_to_delete = [iv[0] for iv in enumerate(db_vnfr["vdur"]) if iv[1]["vdu-id-ref"] == vdu_id]
427 db_update.update({"vdur.{}.status".format(i): "DELETING" for i in indexes_to_delete[-vdu_count:]})
428 else:
429 # it must be deleted one by one because common.db does not allow otherwise
430 vdus_to_delete = [v for v in reversed(db_vnfr["vdur"]) if v["vdu-id-ref"] == vdu_id]
431 for vdu in vdus_to_delete[:vdu_count]:
432 self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, None, pull={"vdur": {"_id": vdu["_id"]}})
433 db_push = {"vdur": db_vdu_push_list} if db_vdu_push_list else None
434 self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, db_update, push_list=db_push)
435 # modify passed dictionary db_vnfr
436 db_vnfr_ = self.db.get_one("vnfrs", {"_id": db_vnfr["_id"]})
437 db_vnfr["vdur"] = db_vnfr_["vdur"]
tierno27246d82018-09-27 15:59:09 +0200438
tiernof578e552018-11-08 19:07:20 +0100439 def ns_update_nsr(self, ns_update_nsr, db_nsr, nsr_desc_RO):
440 """
441 Updates database nsr with the RO info for the created vld
442 :param ns_update_nsr: dictionary to be filled with the updated info
443 :param db_nsr: content of db_nsr. This is also modified
444 :param nsr_desc_RO: nsr descriptor from RO
445 :return: Nothing, LcmException is raised on errors
446 """
447
448 for vld_index, vld in enumerate(get_iterable(db_nsr, "vld")):
449 for net_RO in get_iterable(nsr_desc_RO, "nets"):
450 if vld["id"] != net_RO.get("ns_net_osm_id"):
451 continue
452 vld["vim-id"] = net_RO.get("vim_net_id")
453 vld["name"] = net_RO.get("vim_name")
454 vld["status"] = net_RO.get("status")
455 vld["status-detailed"] = net_RO.get("error_msg")
456 ns_update_nsr["vld.{}".format(vld_index)] = vld
457 break
458 else:
459 raise LcmException("ns_update_nsr: Not found vld={} at RO info".format(vld["id"]))
460
tiernoe876f672020-02-13 14:34:48 +0000461 def set_vnfr_at_error(self, db_vnfrs, error_text):
462 try:
463 for db_vnfr in db_vnfrs.values():
464 vnfr_update = {"status": "ERROR"}
465 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
466 if "status" not in vdur:
467 vdur["status"] = "ERROR"
468 vnfr_update["vdur.{}.status".format(vdu_index)] = "ERROR"
469 if error_text:
470 vdur["status-detailed"] = str(error_text)
471 vnfr_update["vdur.{}.status-detailed".format(vdu_index)] = "ERROR"
472 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
473 except DbException as e:
474 self.logger.error("Cannot update vnf. {}".format(e))
475
tierno59d22d22018-09-25 18:10:19 +0200476 def ns_update_vnfr(self, db_vnfrs, nsr_desc_RO):
477 """
478 Updates database vnfr with the RO info, e.g. ip_address, vim_id... Descriptor db_vnfrs is also updated
tierno27246d82018-09-27 15:59:09 +0200479 :param db_vnfrs: dictionary with member-vnf-index: vnfr-content
480 :param nsr_desc_RO: nsr descriptor from RO
481 :return: Nothing, LcmException is raised on errors
tierno59d22d22018-09-25 18:10:19 +0200482 """
483 for vnf_index, db_vnfr in db_vnfrs.items():
484 for vnf_RO in nsr_desc_RO["vnfs"]:
tierno27246d82018-09-27 15:59:09 +0200485 if vnf_RO["member_vnf_index"] != vnf_index:
486 continue
487 vnfr_update = {}
tiernof578e552018-11-08 19:07:20 +0100488 if vnf_RO.get("ip_address"):
tierno1674de82019-04-09 13:03:14 +0000489 db_vnfr["ip-address"] = vnfr_update["ip-address"] = vnf_RO["ip_address"].split(";")[0]
tiernof578e552018-11-08 19:07:20 +0100490 elif not db_vnfr.get("ip-address"):
tierno0ec0c272020-02-19 17:43:01 +0000491 if db_vnfr.get("vdur"): # if not VDUs, there is not ip_address
492 raise LcmExceptionNoMgmtIP("ns member_vnf_index '{}' has no IP address".format(vnf_index))
tierno59d22d22018-09-25 18:10:19 +0200493
tierno27246d82018-09-27 15:59:09 +0200494 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
495 vdur_RO_count_index = 0
496 if vdur.get("pdu-type"):
497 continue
498 for vdur_RO in get_iterable(vnf_RO, "vms"):
499 if vdur["vdu-id-ref"] != vdur_RO["vdu_osm_id"]:
500 continue
501 if vdur["count-index"] != vdur_RO_count_index:
502 vdur_RO_count_index += 1
503 continue
504 vdur["vim-id"] = vdur_RO.get("vim_vm_id")
tierno1674de82019-04-09 13:03:14 +0000505 if vdur_RO.get("ip_address"):
506 vdur["ip-address"] = vdur_RO["ip_address"].split(";")[0]
tierno274ed572019-04-04 13:33:27 +0000507 else:
508 vdur["ip-address"] = None
tierno27246d82018-09-27 15:59:09 +0200509 vdur["vdu-id-ref"] = vdur_RO.get("vdu_osm_id")
510 vdur["name"] = vdur_RO.get("vim_name")
511 vdur["status"] = vdur_RO.get("status")
512 vdur["status-detailed"] = vdur_RO.get("error_msg")
513 for ifacer in get_iterable(vdur, "interfaces"):
514 for interface_RO in get_iterable(vdur_RO, "interfaces"):
515 if ifacer["name"] == interface_RO.get("internal_name"):
516 ifacer["ip-address"] = interface_RO.get("ip_address")
517 ifacer["mac-address"] = interface_RO.get("mac_address")
518 break
519 else:
520 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} interface={} "
quilesj7e13aeb2019-10-08 13:34:55 +0200521 "from VIM info"
522 .format(vnf_index, vdur["vdu-id-ref"], ifacer["name"]))
tierno27246d82018-09-27 15:59:09 +0200523 vnfr_update["vdur.{}".format(vdu_index)] = vdur
524 break
525 else:
tierno15b1cf12019-08-29 13:21:40 +0000526 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} count_index={} from "
527 "VIM info".format(vnf_index, vdur["vdu-id-ref"], vdur["count-index"]))
tiernof578e552018-11-08 19:07:20 +0100528
529 for vld_index, vld in enumerate(get_iterable(db_vnfr, "vld")):
530 for net_RO in get_iterable(nsr_desc_RO, "nets"):
531 if vld["id"] != net_RO.get("vnf_net_osm_id"):
532 continue
533 vld["vim-id"] = net_RO.get("vim_net_id")
534 vld["name"] = net_RO.get("vim_name")
535 vld["status"] = net_RO.get("status")
536 vld["status-detailed"] = net_RO.get("error_msg")
537 vnfr_update["vld.{}".format(vld_index)] = vld
538 break
539 else:
tierno15b1cf12019-08-29 13:21:40 +0000540 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vld={} from VIM info".format(
tiernof578e552018-11-08 19:07:20 +0100541 vnf_index, vld["id"]))
542
tierno27246d82018-09-27 15:59:09 +0200543 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
544 break
tierno59d22d22018-09-25 18:10:19 +0200545
546 else:
tierno15b1cf12019-08-29 13:21:40 +0000547 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} from VIM info".format(vnf_index))
tierno59d22d22018-09-25 18:10:19 +0200548
tierno5ee02052019-12-05 19:55:02 +0000549 def _get_ns_config_info(self, nsr_id):
tiernoc3f2a822019-11-05 13:45:04 +0000550 """
551 Generates a mapping between vnf,vdu elements and the N2VC id
tierno5ee02052019-12-05 19:55:02 +0000552 :param nsr_id: id of nsr to get last database _admin.deployed.VCA that contains this list
tiernoc3f2a822019-11-05 13:45:04 +0000553 :return: a dictionary with {osm-config-mapping: {}} where its element contains:
554 "<member-vnf-index>": <N2VC-id> for a vnf configuration, or
555 "<member-vnf-index>.<vdu.id>.<vdu replica(0, 1,..)>": <N2VC-id> for a vdu configuration
556 """
tierno5ee02052019-12-05 19:55:02 +0000557 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
558 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
tiernoc3f2a822019-11-05 13:45:04 +0000559 mapping = {}
560 ns_config_info = {"osm-config-mapping": mapping}
561 for vca in vca_deployed_list:
562 if not vca["member-vnf-index"]:
563 continue
564 if not vca["vdu_id"]:
565 mapping[vca["member-vnf-index"]] = vca["application"]
566 else:
567 mapping["{}.{}.{}".format(vca["member-vnf-index"], vca["vdu_id"], vca["vdu_count_index"])] =\
568 vca["application"]
569 return ns_config_info
570
bravof922c4172020-11-24 21:21:43 -0300571 async def _instantiate_ng_ro(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds,
tierno69f0d382020-05-07 13:08:09 +0000572 n2vc_key_list, stage, start_deploy, timeout_ns_deploy):
tierno2357f4e2020-10-19 16:38:59 +0000573
574 db_vims = {}
575
576 def get_vim_account(vim_account_id):
577 nonlocal db_vims
578 if vim_account_id in db_vims:
579 return db_vims[vim_account_id]
580 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account_id})
581 db_vims[vim_account_id] = db_vim
582 return db_vim
583
584 # modify target_vld info with instantiation parameters
585 def parse_vld_instantiation_params(target_vim, target_vld, vld_params, target_sdn):
586 if vld_params.get("ip-profile"):
587 target_vld["vim_info"][target_vim]["ip_profile"] = vld_params["ip-profile"]
588 if vld_params.get("provider-network"):
589 target_vld["vim_info"][target_vim]["provider_network"] = vld_params["provider-network"]
590 if "sdn-ports" in vld_params["provider-network"] and target_sdn:
591 target_vld["vim_info"][target_sdn]["sdn-ports"] = vld_params["provider-network"]["sdn-ports"]
592 if vld_params.get("wimAccountId"):
593 target_wim = "wim:{}".format(vld_params["wimAccountId"])
594 target_vld["vim_info"][target_wim] = {}
595 for param in ("vim-network-name", "vim-network-id"):
596 if vld_params.get(param):
597 if isinstance(vld_params[param], dict):
garciaale71bf0dd2021-03-02 10:49:28 -0300598 for vim, vim_net in vld_params[param].items():
bravof922c4172020-11-24 21:21:43 -0300599 other_target_vim = "vim:" + vim
600 populate_dict(target_vld["vim_info"], (other_target_vim, param.replace("-", "_")), vim_net)
tierno2357f4e2020-10-19 16:38:59 +0000601 else: # isinstance str
602 target_vld["vim_info"][target_vim][param.replace("-", "_")] = vld_params[param]
bravof922c4172020-11-24 21:21:43 -0300603 if vld_params.get("common_id"):
604 target_vld["common_id"] = vld_params.get("common_id")
tierno2357f4e2020-10-19 16:38:59 +0000605
tierno69f0d382020-05-07 13:08:09 +0000606 nslcmop_id = db_nslcmop["_id"]
607 target = {
608 "name": db_nsr["name"],
609 "ns": {"vld": []},
610 "vnf": [],
611 "image": deepcopy(db_nsr["image"]),
612 "flavor": deepcopy(db_nsr["flavor"]),
613 "action_id": nslcmop_id,
tierno2357f4e2020-10-19 16:38:59 +0000614 "cloud_init_content": {},
tierno69f0d382020-05-07 13:08:09 +0000615 }
616 for image in target["image"]:
tierno2357f4e2020-10-19 16:38:59 +0000617 image["vim_info"] = {}
tierno69f0d382020-05-07 13:08:09 +0000618 for flavor in target["flavor"]:
tierno2357f4e2020-10-19 16:38:59 +0000619 flavor["vim_info"] = {}
tierno69f0d382020-05-07 13:08:09 +0000620
tierno2357f4e2020-10-19 16:38:59 +0000621 if db_nslcmop.get("lcmOperationType") != "instantiate":
622 # get parameters of instantiation:
623 db_nslcmop_instantiate = self.db.get_list("nslcmops", {"nsInstanceId": db_nslcmop["nsInstanceId"],
624 "lcmOperationType": "instantiate"})[-1]
625 ns_params = db_nslcmop_instantiate.get("operationParams")
626 else:
627 ns_params = db_nslcmop.get("operationParams")
bravof922c4172020-11-24 21:21:43 -0300628 ssh_keys_instantiation = ns_params.get("ssh_keys") or []
629 ssh_keys_all = ssh_keys_instantiation + (n2vc_key_list or [])
tierno69f0d382020-05-07 13:08:09 +0000630
631 cp2target = {}
tierno2357f4e2020-10-19 16:38:59 +0000632 for vld_index, vld in enumerate(db_nsr.get("vld")):
633 target_vim = "vim:{}".format(ns_params["vimAccountId"])
634 target_vld = {
635 "id": vld["id"],
636 "name": vld["name"],
637 "mgmt-network": vld.get("mgmt-network", False),
638 "type": vld.get("type"),
639 "vim_info": {
bravof922c4172020-11-24 21:21:43 -0300640 target_vim: {
641 "vim_network_name": vld.get("vim-network-name"),
642 "vim_account_id": ns_params["vimAccountId"]
643 }
tierno2357f4e2020-10-19 16:38:59 +0000644 }
645 }
646 # check if this network needs SDN assist
tierno2357f4e2020-10-19 16:38:59 +0000647 if vld.get("pci-interfaces"):
garciaale6be14002021-02-12 11:26:46 +0000648 db_vim = get_vim_account(ns_params["vimAccountId"])
tierno2357f4e2020-10-19 16:38:59 +0000649 sdnc_id = db_vim["config"].get("sdn-controller")
650 if sdnc_id:
garciaale6be14002021-02-12 11:26:46 +0000651 sdn_vld = "nsrs:{}:vld.{}".format(nsr_id, vld["id"])
652 target_sdn = "sdn:{}".format(sdnc_id)
653 target_vld["vim_info"][target_sdn] = {
654 "sdn": True, "target_vim": target_vim, "vlds": [sdn_vld], "type": vld.get("type")}
tierno2357f4e2020-10-19 16:38:59 +0000655
bravof922c4172020-11-24 21:21:43 -0300656 nsd_vnf_profiles = get_vnf_profiles(nsd)
657 for nsd_vnf_profile in nsd_vnf_profiles:
658 for cp in nsd_vnf_profile["virtual-link-connectivity"]:
659 if cp["virtual-link-profile-id"] == vld["id"]:
660 cp2target["member_vnf:{}.{}".format(
661 cp["constituent-cpd-id"][0]["constituent-base-element-id"],
662 cp["constituent-cpd-id"][0]["constituent-cpd-id"]
663 )] = "nsrs:{}:vld.{}".format(nsr_id, vld_index)
tierno2357f4e2020-10-19 16:38:59 +0000664
665 # check at nsd descriptor, if there is an ip-profile
666 vld_params = {}
bravofea2f4a62021-04-23 12:34:52 -0400667 nsd_vlp = find_in_list(
668 get_virtual_link_profiles(nsd),
669 lambda a_link_profile: a_link_profile["virtual-link-desc-id"] == vld["id"])
670 if nsd_vlp and nsd_vlp.get("virtual-link-protocol-data") and \
671 nsd_vlp["virtual-link-protocol-data"].get("l3-protocol-data"):
672 ip_profile_source_data = nsd_vlp["virtual-link-protocol-data"]["l3-protocol-data"]
673 ip_profile_dest_data = {}
674 if "ip-version" in ip_profile_source_data:
675 ip_profile_dest_data["ip-version"] = ip_profile_source_data["ip-version"]
676 if "cidr" in ip_profile_source_data:
677 ip_profile_dest_data["subnet-address"] = ip_profile_source_data["cidr"]
678 if "gateway-ip" in ip_profile_source_data:
679 ip_profile_dest_data["gateway-address"] = ip_profile_source_data["gateway-ip"]
680 if "dhcp-enabled" in ip_profile_source_data:
681 ip_profile_dest_data["dhcp-params"] = {
682 "enabled": ip_profile_source_data["dhcp-enabled"]
683 }
684 vld_params["ip-profile"] = ip_profile_dest_data
bravof922c4172020-11-24 21:21:43 -0300685
tierno2357f4e2020-10-19 16:38:59 +0000686 # update vld_params with instantiation params
bravof922c4172020-11-24 21:21:43 -0300687 vld_instantiation_params = find_in_list(get_iterable(ns_params, "vld"),
688 lambda a_vld: a_vld["name"] in (vld["name"], vld["id"]))
tierno2357f4e2020-10-19 16:38:59 +0000689 if vld_instantiation_params:
690 vld_params.update(vld_instantiation_params)
bravof922c4172020-11-24 21:21:43 -0300691 parse_vld_instantiation_params(target_vim, target_vld, vld_params, None)
tierno69f0d382020-05-07 13:08:09 +0000692 target["ns"]["vld"].append(target_vld)
bravof922c4172020-11-24 21:21:43 -0300693
tierno69f0d382020-05-07 13:08:09 +0000694 for vnfr in db_vnfrs.values():
bravof922c4172020-11-24 21:21:43 -0300695 vnfd = find_in_list(db_vnfds, lambda db_vnf: db_vnf["id"] == vnfr["vnfd-ref"])
696 vnf_params = find_in_list(get_iterable(ns_params, "vnf"),
697 lambda a_vnf: a_vnf["member-vnf-index"] == vnfr["member-vnf-index-ref"])
tierno69f0d382020-05-07 13:08:09 +0000698 target_vnf = deepcopy(vnfr)
tierno2357f4e2020-10-19 16:38:59 +0000699 target_vim = "vim:{}".format(vnfr["vim-account-id"])
tierno69f0d382020-05-07 13:08:09 +0000700 for vld in target_vnf.get("vld", ()):
tierno2357f4e2020-10-19 16:38:59 +0000701 # check if connected to a ns.vld, to fill target'
bravof922c4172020-11-24 21:21:43 -0300702 vnf_cp = find_in_list(vnfd.get("int-virtual-link-desc", ()),
703 lambda cpd: cpd.get("id") == vld["id"])
tierno69f0d382020-05-07 13:08:09 +0000704 if vnf_cp:
705 ns_cp = "member_vnf:{}.{}".format(vnfr["member-vnf-index-ref"], vnf_cp["id"])
706 if cp2target.get(ns_cp):
707 vld["target"] = cp2target[ns_cp]
bravof922c4172020-11-24 21:21:43 -0300708
709 vld["vim_info"] = {target_vim: {"vim_network_name": vld.get("vim-network-name")}}
tierno2357f4e2020-10-19 16:38:59 +0000710 # check if this network needs SDN assist
711 target_sdn = None
712 if vld.get("pci-interfaces"):
713 db_vim = get_vim_account(vnfr["vim-account-id"])
714 sdnc_id = db_vim["config"].get("sdn-controller")
715 if sdnc_id:
716 sdn_vld = "vnfrs:{}:vld.{}".format(target_vnf["_id"], vld["id"])
717 target_sdn = "sdn:{}".format(sdnc_id)
718 vld["vim_info"][target_sdn] = {
719 "sdn": True, "target_vim": target_vim, "vlds": [sdn_vld], "type": vld.get("type")}
tierno69f0d382020-05-07 13:08:09 +0000720
tierno2357f4e2020-10-19 16:38:59 +0000721 # check at vnfd descriptor, if there is an ip-profile
722 vld_params = {}
bravof922c4172020-11-24 21:21:43 -0300723 vnfd_vlp = find_in_list(
724 get_virtual_link_profiles(vnfd),
725 lambda a_link_profile: a_link_profile["id"] == vld["id"]
726 )
727 if vnfd_vlp and vnfd_vlp.get("virtual-link-protocol-data") and \
728 vnfd_vlp["virtual-link-protocol-data"].get("l3-protocol-data"):
729 ip_profile_source_data = vnfd_vlp["virtual-link-protocol-data"]["l3-protocol-data"]
730 ip_profile_dest_data = {}
731 if "ip-version" in ip_profile_source_data:
732 ip_profile_dest_data["ip-version"] = ip_profile_source_data["ip-version"]
733 if "cidr" in ip_profile_source_data:
734 ip_profile_dest_data["subnet-address"] = ip_profile_source_data["cidr"]
735 if "gateway-ip" in ip_profile_source_data:
736 ip_profile_dest_data["gateway-address"] = ip_profile_source_data["gateway-ip"]
737 if "dhcp-enabled" in ip_profile_source_data:
738 ip_profile_dest_data["dhcp-params"] = {
739 "enabled": ip_profile_source_data["dhcp-enabled"]
740 }
741
742 vld_params["ip-profile"] = ip_profile_dest_data
tierno2357f4e2020-10-19 16:38:59 +0000743 # update vld_params with instantiation params
744 if vnf_params:
bravof922c4172020-11-24 21:21:43 -0300745 vld_instantiation_params = find_in_list(get_iterable(vnf_params, "internal-vld"),
746 lambda i_vld: i_vld["name"] == vld["id"])
tierno2357f4e2020-10-19 16:38:59 +0000747 if vld_instantiation_params:
748 vld_params.update(vld_instantiation_params)
749 parse_vld_instantiation_params(target_vim, vld, vld_params, target_sdn)
750
751 vdur_list = []
tierno69f0d382020-05-07 13:08:09 +0000752 for vdur in target_vnf.get("vdur", ()):
tierno2357f4e2020-10-19 16:38:59 +0000753 if vdur.get("status") == "DELETING" or vdur.get("pdu-type"):
754 continue # This vdu must not be created
bravof922c4172020-11-24 21:21:43 -0300755 vdur["vim_info"] = {"vim_account_id": vnfr["vim-account-id"]}
tierno69f0d382020-05-07 13:08:09 +0000756
bravof922c4172020-11-24 21:21:43 -0300757 self.logger.debug("NS > ssh_keys > {}".format(ssh_keys_all))
758
759 if ssh_keys_all:
garciaalea77ced72021-02-17 19:09:12 -0300760 vdu_configuration = get_configuration(vnfd, vdur["vdu-id-ref"])
761 vnf_configuration = get_configuration(vnfd, vnfd["id"])
bravof922c4172020-11-24 21:21:43 -0300762 if vdu_configuration and vdu_configuration.get("config-access") and \
763 vdu_configuration.get("config-access").get("ssh-access"):
764 vdur["ssh-keys"] = ssh_keys_all
765 vdur["ssh-access-required"] = vdu_configuration["config-access"]["ssh-access"]["required"]
766 elif vnf_configuration and vnf_configuration.get("config-access") and \
767 vnf_configuration.get("config-access").get("ssh-access") and \
tierno69f0d382020-05-07 13:08:09 +0000768 any(iface.get("mgmt-vnf") for iface in vdur["interfaces"]):
bravof922c4172020-11-24 21:21:43 -0300769 vdur["ssh-keys"] = ssh_keys_all
770 vdur["ssh-access-required"] = vnf_configuration["config-access"]["ssh-access"]["required"]
771 elif ssh_keys_instantiation and \
772 find_in_list(vdur["interfaces"], lambda iface: iface.get("mgmt-vnf")):
773 vdur["ssh-keys"] = ssh_keys_instantiation
tierno69f0d382020-05-07 13:08:09 +0000774
bravof922c4172020-11-24 21:21:43 -0300775 self.logger.debug("NS > vdur > {}".format(vdur))
776
777 vdud = get_vdu(vnfd, vdur["vdu-id-ref"])
tierno69f0d382020-05-07 13:08:09 +0000778 # cloud-init
779 if vdud.get("cloud-init-file"):
780 vdur["cloud-init"] = "{}:file:{}".format(vnfd["_id"], vdud.get("cloud-init-file"))
tierno2357f4e2020-10-19 16:38:59 +0000781 # read file and put content at target.cloul_init_content. Avoid ng_ro to use shared package system
782 if vdur["cloud-init"] not in target["cloud_init_content"]:
783 base_folder = vnfd["_admin"]["storage"]
784 cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
785 vdud.get("cloud-init-file"))
786 with self.fs.file_open(cloud_init_file, "r") as ci_file:
787 target["cloud_init_content"][vdur["cloud-init"]] = ci_file.read()
tierno69f0d382020-05-07 13:08:09 +0000788 elif vdud.get("cloud-init"):
bravof922c4172020-11-24 21:21:43 -0300789 vdur["cloud-init"] = "{}:vdu:{}".format(vnfd["_id"], get_vdu_index(vnfd, vdur["vdu-id-ref"]))
tierno2357f4e2020-10-19 16:38:59 +0000790 # put content at target.cloul_init_content. Avoid ng_ro read vnfd descriptor
791 target["cloud_init_content"][vdur["cloud-init"]] = vdud["cloud-init"]
792 vdur["additionalParams"] = vdur.get("additionalParams") or {}
793 deploy_params_vdu = self._format_additional_params(vdur.get("additionalParams") or {})
bravof922c4172020-11-24 21:21:43 -0300794 deploy_params_vdu["OSM"] = get_osm_params(vnfr, vdur["vdu-id-ref"], vdur["count-index"])
tierno2357f4e2020-10-19 16:38:59 +0000795 vdur["additionalParams"] = deploy_params_vdu
tierno69f0d382020-05-07 13:08:09 +0000796
797 # flavor
798 ns_flavor = target["flavor"][int(vdur["ns-flavor-id"])]
tierno2357f4e2020-10-19 16:38:59 +0000799 if target_vim not in ns_flavor["vim_info"]:
800 ns_flavor["vim_info"][target_vim] = {}
lloretgalleg46ab4d82021-02-08 11:49:50 +0000801
802 # deal with images
803 # in case alternative images are provided we must check if they should be applied
804 # for the vim_type, modify the vim_type taking into account
805 ns_image_id = int(vdur["ns-image-id"])
806 if vdur.get("alt-image-ids"):
807 db_vim = get_vim_account(vnfr["vim-account-id"])
808 vim_type = db_vim["vim_type"]
809 for alt_image_id in vdur.get("alt-image-ids"):
810 ns_alt_image = target["image"][int(alt_image_id)]
811 if vim_type == ns_alt_image.get("vim-type"):
812 # must use alternative image
813 self.logger.debug("use alternative image id: {}".format(alt_image_id))
814 ns_image_id = alt_image_id
815 vdur["ns-image-id"] = ns_image_id
816 break
817 ns_image = target["image"][int(ns_image_id)]
tierno2357f4e2020-10-19 16:38:59 +0000818 if target_vim not in ns_image["vim_info"]:
819 ns_image["vim_info"][target_vim] = {}
tierno69f0d382020-05-07 13:08:09 +0000820
tierno2357f4e2020-10-19 16:38:59 +0000821 vdur["vim_info"] = {target_vim: {}}
822 # instantiation parameters
823 # if vnf_params:
824 # vdu_instantiation_params = next((v for v in get_iterable(vnf_params, "vdu") if v["id"] ==
825 # vdud["id"]), None)
826 vdur_list.append(vdur)
827 target_vnf["vdur"] = vdur_list
tierno69f0d382020-05-07 13:08:09 +0000828 target["vnf"].append(target_vnf)
829
830 desc = await self.RO.deploy(nsr_id, target)
bravof922c4172020-11-24 21:21:43 -0300831 self.logger.debug("RO return > {}".format(desc))
tierno69f0d382020-05-07 13:08:09 +0000832 action_id = desc["action_id"]
tierno2357f4e2020-10-19 16:38:59 +0000833 await self._wait_ng_ro(nsr_id, action_id, nslcmop_id, start_deploy, timeout_ns_deploy, stage)
tierno69f0d382020-05-07 13:08:09 +0000834
835 # Updating NSR
836 db_nsr_update = {
837 "_admin.deployed.RO.operational-status": "running",
838 "detailed-status": " ".join(stage)
839 }
840 # db_nsr["_admin.deployed.RO.detailed-status"] = "Deployed at VIM"
841 self.update_db_2("nsrs", nsr_id, db_nsr_update)
842 self._write_op_status(nslcmop_id, stage)
843 self.logger.debug(logging_text + "ns deployed at RO. RO_id={}".format(action_id))
844 return
845
tierno2357f4e2020-10-19 16:38:59 +0000846 async def _wait_ng_ro(self, nsr_id, action_id, nslcmop_id=None, start_time=None, timeout=600, stage=None):
tierno69f0d382020-05-07 13:08:09 +0000847 detailed_status_old = None
848 db_nsr_update = {}
tierno2357f4e2020-10-19 16:38:59 +0000849 start_time = start_time or time()
tierno69f0d382020-05-07 13:08:09 +0000850 while time() <= start_time + timeout:
851 desc_status = await self.RO.status(nsr_id, action_id)
bravof922c4172020-11-24 21:21:43 -0300852 self.logger.debug("Wait NG RO > {}".format(desc_status))
tierno69f0d382020-05-07 13:08:09 +0000853 if desc_status["status"] == "FAILED":
854 raise NgRoException(desc_status["details"])
855 elif desc_status["status"] == "BUILD":
tierno2357f4e2020-10-19 16:38:59 +0000856 if stage:
857 stage[2] = "VIM: ({})".format(desc_status["details"])
tierno69f0d382020-05-07 13:08:09 +0000858 elif desc_status["status"] == "DONE":
tierno2357f4e2020-10-19 16:38:59 +0000859 if stage:
860 stage[2] = "Deployed at VIM"
tierno69f0d382020-05-07 13:08:09 +0000861 break
862 else:
863 assert False, "ROclient.check_ns_status returns unknown {}".format(desc_status["status"])
tierno2357f4e2020-10-19 16:38:59 +0000864 if stage and nslcmop_id and stage[2] != detailed_status_old:
tierno69f0d382020-05-07 13:08:09 +0000865 detailed_status_old = stage[2]
866 db_nsr_update["detailed-status"] = " ".join(stage)
867 self.update_db_2("nsrs", nsr_id, db_nsr_update)
868 self._write_op_status(nslcmop_id, stage)
bravof922c4172020-11-24 21:21:43 -0300869 await asyncio.sleep(15, loop=self.loop)
tierno69f0d382020-05-07 13:08:09 +0000870 else: # timeout_ns_deploy
871 raise NgRoException("Timeout waiting ns to deploy")
872
873 async def _terminate_ng_ro(self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage):
874 db_nsr_update = {}
875 failed_detail = []
876 action_id = None
877 start_deploy = time()
878 try:
879 target = {
880 "ns": {"vld": []},
881 "vnf": [],
882 "image": [],
883 "flavor": [],
tierno2357f4e2020-10-19 16:38:59 +0000884 "action_id": nslcmop_id
tierno69f0d382020-05-07 13:08:09 +0000885 }
886 desc = await self.RO.deploy(nsr_id, target)
887 action_id = desc["action_id"]
888 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = action_id
889 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETING"
890 self.logger.debug(logging_text + "ns terminate action at RO. action_id={}".format(action_id))
891
892 # wait until done
893 delete_timeout = 20 * 60 # 20 minutes
tierno2357f4e2020-10-19 16:38:59 +0000894 await self._wait_ng_ro(nsr_id, action_id, nslcmop_id, start_deploy, delete_timeout, stage)
tierno69f0d382020-05-07 13:08:09 +0000895
896 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
897 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
898 # delete all nsr
899 await self.RO.delete(nsr_id)
900 except Exception as e:
901 if isinstance(e, NgRoException) and e.http_code == 404: # not found
902 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
903 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
904 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
905 self.logger.debug(logging_text + "RO_action_id={} already deleted".format(action_id))
906 elif isinstance(e, NgRoException) and e.http_code == 409: # conflict
907 failed_detail.append("delete conflict: {}".format(e))
908 self.logger.debug(logging_text + "RO_action_id={} delete conflict: {}".format(action_id, e))
909 else:
910 failed_detail.append("delete error: {}".format(e))
911 self.logger.error(logging_text + "RO_action_id={} delete error: {}".format(action_id, e))
912
913 if failed_detail:
914 stage[2] = "Error deleting from VIM"
915 else:
916 stage[2] = "Deleted from VIM"
917 db_nsr_update["detailed-status"] = " ".join(stage)
918 self.update_db_2("nsrs", nsr_id, db_nsr_update)
919 self._write_op_status(nslcmop_id, stage)
920
921 if failed_detail:
922 raise LcmException("; ".join(failed_detail))
923 return
924
bravof922c4172020-11-24 21:21:43 -0300925 async def instantiate_RO(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds,
tiernoe876f672020-02-13 14:34:48 +0000926 n2vc_key_list, stage):
tiernoe95ed362020-04-23 08:24:57 +0000927 """
928 Instantiate at RO
929 :param logging_text: preffix text to use at logging
930 :param nsr_id: nsr identity
931 :param nsd: database content of ns descriptor
932 :param db_nsr: database content of ns record
933 :param db_nslcmop: database content of ns operation, in this case, 'instantiate'
934 :param db_vnfrs:
bravof922c4172020-11-24 21:21:43 -0300935 :param db_vnfds: database content of vnfds, indexed by id (not _id). {id: {vnfd_object}, ...}
tiernoe95ed362020-04-23 08:24:57 +0000936 :param n2vc_key_list: ssh-public-key list to be inserted to management vdus via cloud-init
937 :param stage: list with 3 items: [general stage, tasks, vim_specific]. This task will write over vim_specific
938 :return: None or exception
939 """
tiernoe876f672020-02-13 14:34:48 +0000940 try:
tiernoe876f672020-02-13 14:34:48 +0000941 start_deploy = time()
942 ns_params = db_nslcmop.get("operationParams")
943 if ns_params and ns_params.get("timeout_ns_deploy"):
944 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
945 else:
946 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
quilesj7e13aeb2019-10-08 13:34:55 +0200947
tiernoe876f672020-02-13 14:34:48 +0000948 # Check for and optionally request placement optimization. Database will be updated if placement activated
949 stage[2] = "Waiting for Placement."
tierno8790a3d2020-04-23 22:49:52 +0000950 if await self._do_placement(logging_text, db_nslcmop, db_vnfrs):
951 # in case of placement change ns_params[vimAcountId) if not present at any vnfrs
952 for vnfr in db_vnfrs.values():
953 if ns_params["vimAccountId"] == vnfr["vim-account-id"]:
954 break
955 else:
956 ns_params["vimAccountId"] == vnfr["vim-account-id"]
quilesj7e13aeb2019-10-08 13:34:55 +0200957
bravof922c4172020-11-24 21:21:43 -0300958 return await self._instantiate_ng_ro(logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs,
959 db_vnfds, n2vc_key_list, stage, start_deploy, timeout_ns_deploy)
tierno2357f4e2020-10-19 16:38:59 +0000960 except Exception as e:
tierno067e04a2020-03-31 12:53:13 +0000961 stage[2] = "ERROR deploying at VIM"
tiernoe876f672020-02-13 14:34:48 +0000962 self.set_vnfr_at_error(db_vnfrs, str(e))
tierno2357f4e2020-10-19 16:38:59 +0000963 self.logger.error("Error deploying at VIM {}".format(e),
964 exc_info=not isinstance(e, (ROclient.ROClientException, LcmException, DbException,
965 NgRoException)))
tiernoe876f672020-02-13 14:34:48 +0000966 raise
quilesj7e13aeb2019-10-08 13:34:55 +0200967
tierno7ecbc342020-09-21 14:05:39 +0000968 async def wait_kdu_up(self, logging_text, nsr_id, vnfr_id, kdu_name):
969 """
970 Wait for kdu to be up, get ip address
971 :param logging_text: prefix use for logging
972 :param nsr_id:
973 :param vnfr_id:
974 :param kdu_name:
975 :return: IP address
976 """
977
978 # self.logger.debug(logging_text + "Starting wait_kdu_up")
979 nb_tries = 0
980
981 while nb_tries < 360:
982 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
tiernoe5d05972020-10-09 12:03:24 +0000983 kdur = next((x for x in get_iterable(db_vnfr, "kdur") if x.get("kdu-name") == kdu_name), None)
tierno7ecbc342020-09-21 14:05:39 +0000984 if not kdur:
985 raise LcmException("Not found vnfr_id={}, kdu_name={}".format(vnfr_id, kdu_name))
986 if kdur.get("status"):
987 if kdur["status"] in ("READY", "ENABLED"):
988 return kdur.get("ip-address")
989 else:
990 raise LcmException("target KDU={} is in error state".format(kdu_name))
991
992 await asyncio.sleep(10, loop=self.loop)
993 nb_tries += 1
994 raise LcmException("Timeout waiting KDU={} instantiated".format(kdu_name))
995
tiernoa5088192019-11-26 16:12:53 +0000996 async def wait_vm_up_insert_key_ro(self, logging_text, nsr_id, vnfr_id, vdu_id, vdu_index, pub_key=None, user=None):
997 """
998 Wait for ip addres at RO, and optionally, insert public key in virtual machine
999 :param logging_text: prefix use for logging
1000 :param nsr_id:
1001 :param vnfr_id:
1002 :param vdu_id:
1003 :param vdu_index:
1004 :param pub_key: public ssh key to inject, None to skip
1005 :param user: user to apply the public ssh key
1006 :return: IP address
1007 """
quilesj7e13aeb2019-10-08 13:34:55 +02001008
tierno2357f4e2020-10-19 16:38:59 +00001009 self.logger.debug(logging_text + "Starting wait_vm_up_insert_key_ro")
tiernod8323042019-08-09 11:32:23 +00001010 ro_nsr_id = None
1011 ip_address = None
1012 nb_tries = 0
1013 target_vdu_id = None
quilesj3149f262019-12-03 10:58:10 +00001014 ro_retries = 0
quilesj7e13aeb2019-10-08 13:34:55 +02001015
tiernod8323042019-08-09 11:32:23 +00001016 while True:
quilesj7e13aeb2019-10-08 13:34:55 +02001017
quilesj3149f262019-12-03 10:58:10 +00001018 ro_retries += 1
1019 if ro_retries >= 360: # 1 hour
1020 raise LcmException("Not found _admin.deployed.RO.nsr_id for nsr_id: {}".format(nsr_id))
1021
tiernod8323042019-08-09 11:32:23 +00001022 await asyncio.sleep(10, loop=self.loop)
quilesj7e13aeb2019-10-08 13:34:55 +02001023
1024 # get ip address
tiernod8323042019-08-09 11:32:23 +00001025 if not target_vdu_id:
1026 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
quilesj3149f262019-12-03 10:58:10 +00001027
1028 if not vdu_id: # for the VNF case
tiernoe876f672020-02-13 14:34:48 +00001029 if db_vnfr.get("status") == "ERROR":
1030 raise LcmException("Cannot inject ssh-key because target VNF is in error state")
tiernod8323042019-08-09 11:32:23 +00001031 ip_address = db_vnfr.get("ip-address")
1032 if not ip_address:
1033 continue
quilesj3149f262019-12-03 10:58:10 +00001034 vdur = next((x for x in get_iterable(db_vnfr, "vdur") if x.get("ip-address") == ip_address), None)
1035 else: # VDU case
1036 vdur = next((x for x in get_iterable(db_vnfr, "vdur")
1037 if x.get("vdu-id-ref") == vdu_id and x.get("count-index") == vdu_index), None)
1038
tierno0e8c3f02020-03-12 17:18:21 +00001039 if not vdur and len(db_vnfr.get("vdur", ())) == 1: # If only one, this should be the target vdu
1040 vdur = db_vnfr["vdur"][0]
quilesj3149f262019-12-03 10:58:10 +00001041 if not vdur:
tierno0e8c3f02020-03-12 17:18:21 +00001042 raise LcmException("Not found vnfr_id={}, vdu_id={}, vdu_index={}".format(vnfr_id, vdu_id,
1043 vdu_index))
tierno2357f4e2020-10-19 16:38:59 +00001044 # New generation RO stores information at "vim_info"
1045 ng_ro_status = None
David Garciaa8bbe672020-11-19 13:06:54 +01001046 target_vim = None
tierno2357f4e2020-10-19 16:38:59 +00001047 if vdur.get("vim_info"):
1048 target_vim = next(t for t in vdur["vim_info"]) # there should be only one key
1049 ng_ro_status = vdur["vim_info"][target_vim].get("vim_status")
1050 if vdur.get("pdu-type") or vdur.get("status") == "ACTIVE" or ng_ro_status == "ACTIVE":
quilesj3149f262019-12-03 10:58:10 +00001051 ip_address = vdur.get("ip-address")
1052 if not ip_address:
1053 continue
1054 target_vdu_id = vdur["vdu-id-ref"]
bravof922c4172020-11-24 21:21:43 -03001055 elif vdur.get("status") == "ERROR" or ng_ro_status == "ERROR":
quilesj3149f262019-12-03 10:58:10 +00001056 raise LcmException("Cannot inject ssh-key because target VM is in error state")
1057
tiernod8323042019-08-09 11:32:23 +00001058 if not target_vdu_id:
1059 continue
tiernod8323042019-08-09 11:32:23 +00001060
quilesj7e13aeb2019-10-08 13:34:55 +02001061 # inject public key into machine
1062 if pub_key and user:
tierno2357f4e2020-10-19 16:38:59 +00001063 self.logger.debug(logging_text + "Inserting RO key")
bravof922c4172020-11-24 21:21:43 -03001064 self.logger.debug("SSH > PubKey > {}".format(pub_key))
tierno0e8c3f02020-03-12 17:18:21 +00001065 if vdur.get("pdu-type"):
1066 self.logger.error(logging_text + "Cannot inject ssh-ky to a PDU")
1067 return ip_address
quilesj7e13aeb2019-10-08 13:34:55 +02001068 try:
1069 ro_vm_id = "{}-{}".format(db_vnfr["member-vnf-index-ref"], target_vdu_id) # TODO add vdu_index
tierno69f0d382020-05-07 13:08:09 +00001070 if self.ng_ro:
bravof922c4172020-11-24 21:21:43 -03001071 target = {"action": {"action": "inject_ssh_key", "key": pub_key, "user": user},
tierno2357f4e2020-10-19 16:38:59 +00001072 "vnf": [{"_id": vnfr_id, "vdur": [{"id": vdur["id"]}]}],
tierno69f0d382020-05-07 13:08:09 +00001073 }
tierno2357f4e2020-10-19 16:38:59 +00001074 desc = await self.RO.deploy(nsr_id, target)
1075 action_id = desc["action_id"]
1076 await self._wait_ng_ro(nsr_id, action_id, timeout=600)
1077 break
tierno69f0d382020-05-07 13:08:09 +00001078 else:
tierno2357f4e2020-10-19 16:38:59 +00001079 # wait until NS is deployed at RO
1080 if not ro_nsr_id:
1081 db_nsrs = self.db.get_one("nsrs", {"_id": nsr_id})
1082 ro_nsr_id = deep_get(db_nsrs, ("_admin", "deployed", "RO", "nsr_id"))
1083 if not ro_nsr_id:
1084 continue
tierno69f0d382020-05-07 13:08:09 +00001085 result_dict = await self.RO.create_action(
1086 item="ns",
1087 item_id_name=ro_nsr_id,
1088 descriptor={"add_public_key": pub_key, "vms": [ro_vm_id], "user": user}
1089 )
1090 # result_dict contains the format {VM-id: {vim_result: 200, description: text}}
1091 if not result_dict or not isinstance(result_dict, dict):
1092 raise LcmException("Unknown response from RO when injecting key")
1093 for result in result_dict.values():
1094 if result.get("vim_result") == 200:
1095 break
1096 else:
1097 raise ROclient.ROClientException("error injecting key: {}".format(
1098 result.get("description")))
1099 break
1100 except NgRoException as e:
1101 raise LcmException("Reaching max tries injecting key. Error: {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02001102 except ROclient.ROClientException as e:
tiernoa5088192019-11-26 16:12:53 +00001103 if not nb_tries:
1104 self.logger.debug(logging_text + "error injecting key: {}. Retrying until {} seconds".
1105 format(e, 20*10))
quilesj7e13aeb2019-10-08 13:34:55 +02001106 nb_tries += 1
tiernoa5088192019-11-26 16:12:53 +00001107 if nb_tries >= 20:
quilesj7e13aeb2019-10-08 13:34:55 +02001108 raise LcmException("Reaching max tries injecting key. Error: {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02001109 else:
quilesj7e13aeb2019-10-08 13:34:55 +02001110 break
1111
1112 return ip_address
1113
tierno5ee02052019-12-05 19:55:02 +00001114 async def _wait_dependent_n2vc(self, nsr_id, vca_deployed_list, vca_index):
1115 """
1116 Wait until dependent VCA deployments have been finished. NS wait for VNFs and VDUs. VNFs for VDUs
1117 """
1118 my_vca = vca_deployed_list[vca_index]
1119 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
quilesj3655ae02019-12-12 16:08:35 +00001120 # vdu or kdu: no dependencies
tierno5ee02052019-12-05 19:55:02 +00001121 return
1122 timeout = 300
1123 while timeout >= 0:
quilesj3655ae02019-12-12 16:08:35 +00001124 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1125 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
1126 configuration_status_list = db_nsr["configurationStatus"]
1127 for index, vca_deployed in enumerate(configuration_status_list):
tierno5ee02052019-12-05 19:55:02 +00001128 if index == vca_index:
quilesj3655ae02019-12-12 16:08:35 +00001129 # myself
tierno5ee02052019-12-05 19:55:02 +00001130 continue
1131 if not my_vca.get("member-vnf-index") or \
1132 (vca_deployed.get("member-vnf-index") == my_vca.get("member-vnf-index")):
quilesj3655ae02019-12-12 16:08:35 +00001133 internal_status = configuration_status_list[index].get("status")
1134 if internal_status == 'READY':
1135 continue
1136 elif internal_status == 'BROKEN':
tierno5ee02052019-12-05 19:55:02 +00001137 raise LcmException("Configuration aborted because dependent charm/s has failed")
quilesj3655ae02019-12-12 16:08:35 +00001138 else:
1139 break
tierno5ee02052019-12-05 19:55:02 +00001140 else:
quilesj3655ae02019-12-12 16:08:35 +00001141 # no dependencies, return
tierno5ee02052019-12-05 19:55:02 +00001142 return
1143 await asyncio.sleep(10)
1144 timeout -= 1
tierno5ee02052019-12-05 19:55:02 +00001145
1146 raise LcmException("Configuration aborted because dependent charm/s timeout")
1147
tiernoe876f672020-02-13 14:34:48 +00001148 async def instantiate_N2VC(self, logging_text, vca_index, nsi_id, db_nsr, db_vnfr, vdu_id, kdu_name, vdu_index,
tiernob996d942020-07-03 14:52:28 +00001149 config_descriptor, deploy_params, base_folder, nslcmop_id, stage, vca_type, vca_name,
1150 ee_config_descriptor):
tiernod8323042019-08-09 11:32:23 +00001151 nsr_id = db_nsr["_id"]
1152 db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
tiernoda6fb102019-11-23 00:36:52 +00001153 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
tiernod8323042019-08-09 11:32:23 +00001154 vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
tiernob996d942020-07-03 14:52:28 +00001155 osm_config = {"osm": {"ns_id": db_nsr["_id"]}}
quilesj7e13aeb2019-10-08 13:34:55 +02001156 db_dict = {
1157 'collection': 'nsrs',
1158 'filter': {'_id': nsr_id},
1159 'path': db_update_entry
1160 }
tiernod8323042019-08-09 11:32:23 +00001161 step = ""
1162 try:
quilesj3655ae02019-12-12 16:08:35 +00001163
1164 element_type = 'NS'
1165 element_under_configuration = nsr_id
1166
tiernod8323042019-08-09 11:32:23 +00001167 vnfr_id = None
1168 if db_vnfr:
1169 vnfr_id = db_vnfr["_id"]
tiernob996d942020-07-03 14:52:28 +00001170 osm_config["osm"]["vnf_id"] = vnfr_id
tiernod8323042019-08-09 11:32:23 +00001171
1172 namespace = "{nsi}.{ns}".format(
1173 nsi=nsi_id if nsi_id else "",
1174 ns=nsr_id)
quilesj3655ae02019-12-12 16:08:35 +00001175
tiernod8323042019-08-09 11:32:23 +00001176 if vnfr_id:
quilesj3655ae02019-12-12 16:08:35 +00001177 element_type = 'VNF'
1178 element_under_configuration = vnfr_id
aktasf0f83a32021-02-12 22:19:10 +03001179 namespace += ".{}-{}".format(vnfr_id, vdu_index or 0)
tiernod8323042019-08-09 11:32:23 +00001180 if vdu_id:
1181 namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
quilesj3655ae02019-12-12 16:08:35 +00001182 element_type = 'VDU'
quilesjb8a35dd2020-01-09 15:10:14 +00001183 element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
tiernob996d942020-07-03 14:52:28 +00001184 osm_config["osm"]["vdu_id"] = vdu_id
tierno51183952020-04-03 15:48:18 +00001185 elif kdu_name:
aktasf0f83a32021-02-12 22:19:10 +03001186 namespace += ".{}.{}".format(kdu_name, vdu_index or 0)
tierno51183952020-04-03 15:48:18 +00001187 element_type = 'KDU'
1188 element_under_configuration = kdu_name
tiernob996d942020-07-03 14:52:28 +00001189 osm_config["osm"]["kdu_name"] = kdu_name
tiernod8323042019-08-09 11:32:23 +00001190
1191 # Get artifact path
tierno588547c2020-07-01 15:30:20 +00001192 artifact_path = "{}/{}/{}/{}".format(
tiernod8323042019-08-09 11:32:23 +00001193 base_folder["folder"],
1194 base_folder["pkg-dir"],
tierno588547c2020-07-01 15:30:20 +00001195 "charms" if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm") else "helm-charts",
1196 vca_name
tiernod8323042019-08-09 11:32:23 +00001197 )
bravof922c4172020-11-24 21:21:43 -03001198
1199 self.logger.debug("Artifact path > {}".format(artifact_path))
1200
tiernoa278b842020-07-08 15:33:55 +00001201 # get initial_config_primitive_list that applies to this element
1202 initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
1203
bravof922c4172020-11-24 21:21:43 -03001204 self.logger.debug("Initial config primitive list > {}".format(initial_config_primitive_list))
1205
tiernoa278b842020-07-08 15:33:55 +00001206 # add config if not present for NS charm
1207 ee_descriptor_id = ee_config_descriptor.get("id")
bravof922c4172020-11-24 21:21:43 -03001208 self.logger.debug("EE Descriptor > {}".format(ee_descriptor_id))
1209 initial_config_primitive_list = get_ee_sorted_initial_config_primitive_list(initial_config_primitive_list,
1210 vca_deployed, ee_descriptor_id)
tiernod8323042019-08-09 11:32:23 +00001211
bravof922c4172020-11-24 21:21:43 -03001212 self.logger.debug("Initial config primitive list #2 > {}".format(initial_config_primitive_list))
tierno588547c2020-07-01 15:30:20 +00001213 # n2vc_redesign STEP 3.1
tierno588547c2020-07-01 15:30:20 +00001214 # find old ee_id if exists
1215 ee_id = vca_deployed.get("ee_id")
tiernod8323042019-08-09 11:32:23 +00001216
David Garciaaae391f2020-11-09 11:12:54 +01001217 vim_account_id = (
1218 deep_get(db_vnfr, ("vim-account-id",)) or
1219 deep_get(deploy_params, ("OSM", "vim_account_id"))
1220 )
1221 vca_cloud, vca_cloud_credential = self.get_vca_cloud_and_credentials(vim_account_id)
1222 vca_k8s_cloud, vca_k8s_cloud_credential = self.get_vca_k8s_cloud_and_credentials(vim_account_id)
tierno588547c2020-07-01 15:30:20 +00001223 # create or register execution environment in VCA
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001224 if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm", "helm-v3"):
quilesj7e13aeb2019-10-08 13:34:55 +02001225
tierno588547c2020-07-01 15:30:20 +00001226 self._write_configuration_status(
1227 nsr_id=nsr_id,
1228 vca_index=vca_index,
1229 status='CREATING',
1230 element_under_configuration=element_under_configuration,
1231 element_type=element_type
1232 )
tiernod8323042019-08-09 11:32:23 +00001233
tierno588547c2020-07-01 15:30:20 +00001234 step = "create execution environment"
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001235 self.logger.debug(logging_text + step)
David Garciaaae391f2020-11-09 11:12:54 +01001236
1237 ee_id = None
1238 credentials = None
1239 if vca_type == "k8s_proxy_charm":
1240 ee_id = await self.vca_map[vca_type].install_k8s_proxy_charm(
1241 charm_name=artifact_path[artifact_path.rfind("/") + 1:],
1242 namespace=namespace,
1243 artifact_path=artifact_path,
1244 db_dict=db_dict,
1245 cloud_name=vca_k8s_cloud,
1246 credential_name=vca_k8s_cloud_credential,
1247 )
David Garciaffbf6ed2021-02-25 20:19:18 +01001248 elif vca_type == "helm" or vca_type == "helm-v3":
bravof922c4172020-11-24 21:21:43 -03001249 ee_id, credentials = await self.vca_map[vca_type].create_execution_environment(
1250 namespace=namespace,
1251 reuse_ee_id=ee_id,
1252 db_dict=db_dict,
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001253 config=osm_config,
1254 artifact_path=artifact_path,
1255 vca_type=vca_type
bravof922c4172020-11-24 21:21:43 -03001256 )
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001257 else:
David Garciaaae391f2020-11-09 11:12:54 +01001258 ee_id, credentials = await self.vca_map[vca_type].create_execution_environment(
1259 namespace=namespace,
1260 reuse_ee_id=ee_id,
1261 db_dict=db_dict,
David Garciaaae391f2020-11-09 11:12:54 +01001262 cloud_name=vca_cloud,
1263 credential_name=vca_cloud_credential,
1264 )
quilesj3655ae02019-12-12 16:08:35 +00001265
tierno588547c2020-07-01 15:30:20 +00001266 elif vca_type == "native_charm":
1267 step = "Waiting to VM being up and getting IP address"
1268 self.logger.debug(logging_text + step)
1269 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id, vdu_index,
1270 user=None, pub_key=None)
1271 credentials = {"hostname": rw_mgmt_ip}
1272 # get username
1273 username = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
1274 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
1275 # merged. Meanwhile let's get username from initial-config-primitive
tiernoa278b842020-07-08 15:33:55 +00001276 if not username and initial_config_primitive_list:
1277 for config_primitive in initial_config_primitive_list:
tierno588547c2020-07-01 15:30:20 +00001278 for param in config_primitive.get("parameter", ()):
1279 if param["name"] == "ssh-username":
1280 username = param["value"]
1281 break
1282 if not username:
tiernoa278b842020-07-08 15:33:55 +00001283 raise LcmException("Cannot determine the username neither with 'initial-config-primitive' nor with "
tierno588547c2020-07-01 15:30:20 +00001284 "'config-access.ssh-access.default-user'")
1285 credentials["username"] = username
1286 # n2vc_redesign STEP 3.2
quilesj3655ae02019-12-12 16:08:35 +00001287
tierno588547c2020-07-01 15:30:20 +00001288 self._write_configuration_status(
1289 nsr_id=nsr_id,
1290 vca_index=vca_index,
1291 status='REGISTERING',
1292 element_under_configuration=element_under_configuration,
1293 element_type=element_type
1294 )
quilesj3655ae02019-12-12 16:08:35 +00001295
tierno588547c2020-07-01 15:30:20 +00001296 step = "register execution environment {}".format(credentials)
1297 self.logger.debug(logging_text + step)
1298 ee_id = await self.vca_map[vca_type].register_execution_environment(
David Garciaaae391f2020-11-09 11:12:54 +01001299 credentials=credentials,
1300 namespace=namespace,
1301 db_dict=db_dict,
1302 cloud_name=vca_cloud,
1303 credential_name=vca_cloud_credential,
1304 )
tierno3bedc9b2019-11-27 15:46:57 +00001305
tierno588547c2020-07-01 15:30:20 +00001306 # for compatibility with MON/POL modules, the need model and application name at database
1307 # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
1308 ee_id_parts = ee_id.split('.')
1309 db_nsr_update = {db_update_entry + "ee_id": ee_id}
1310 if len(ee_id_parts) >= 2:
1311 model_name = ee_id_parts[0]
1312 application_name = ee_id_parts[1]
1313 db_nsr_update[db_update_entry + "model"] = model_name
1314 db_nsr_update[db_update_entry + "application"] = application_name
tiernod8323042019-08-09 11:32:23 +00001315
1316 # n2vc_redesign STEP 3.3
tiernod8323042019-08-09 11:32:23 +00001317 step = "Install configuration Software"
quilesj3655ae02019-12-12 16:08:35 +00001318
tiernoc231a872020-01-21 08:49:05 +00001319 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001320 nsr_id=nsr_id,
1321 vca_index=vca_index,
1322 status='INSTALLING SW',
1323 element_under_configuration=element_under_configuration,
tierno51183952020-04-03 15:48:18 +00001324 element_type=element_type,
tierno588547c2020-07-01 15:30:20 +00001325 other_update=db_nsr_update
quilesj3655ae02019-12-12 16:08:35 +00001326 )
1327
tierno3bedc9b2019-11-27 15:46:57 +00001328 # TODO check if already done
quilesj7e13aeb2019-10-08 13:34:55 +02001329 self.logger.debug(logging_text + step)
David Garcia18a63322020-04-01 16:14:59 +02001330 config = None
tierno588547c2020-07-01 15:30:20 +00001331 if vca_type == "native_charm":
tiernoa278b842020-07-08 15:33:55 +00001332 config_primitive = next((p for p in initial_config_primitive_list if p["name"] == "config"), None)
1333 if config_primitive:
1334 config = self._map_primitive_params(
1335 config_primitive,
1336 {},
1337 deploy_params
1338 )
tierno588547c2020-07-01 15:30:20 +00001339 num_units = 1
1340 if vca_type == "lxc_proxy_charm":
1341 if element_type == "NS":
1342 num_units = db_nsr.get("config-units") or 1
1343 elif element_type == "VNF":
1344 num_units = db_vnfr.get("config-units") or 1
1345 elif element_type == "VDU":
1346 for v in db_vnfr["vdur"]:
1347 if vdu_id == v["vdu-id-ref"]:
1348 num_units = v.get("config-units") or 1
1349 break
David Garciaaae391f2020-11-09 11:12:54 +01001350 if vca_type != "k8s_proxy_charm":
1351 await self.vca_map[vca_type].install_configuration_sw(
1352 ee_id=ee_id,
1353 artifact_path=artifact_path,
1354 db_dict=db_dict,
1355 config=config,
1356 num_units=num_units,
1357 )
quilesj7e13aeb2019-10-08 13:34:55 +02001358
quilesj63f90042020-01-17 09:53:55 +00001359 # write in db flag of configuration_sw already installed
1360 self.update_db_2("nsrs", nsr_id, {db_update_entry + "config_sw_installed": True})
1361
1362 # add relations for this VCA (wait for other peers related with this VCA)
tierno588547c2020-07-01 15:30:20 +00001363 await self._add_vca_relations(logging_text=logging_text, nsr_id=nsr_id,
1364 vca_index=vca_index, vca_type=vca_type)
quilesj63f90042020-01-17 09:53:55 +00001365
quilesj7e13aeb2019-10-08 13:34:55 +02001366 # if SSH access is required, then get execution environment SSH public
David Garciaa27e20a2020-07-10 13:12:44 +02001367 # if native charm we have waited already to VM be UP
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001368 if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
tierno3bedc9b2019-11-27 15:46:57 +00001369 pub_key = None
1370 user = None
tierno588547c2020-07-01 15:30:20 +00001371 # self.logger.debug("get ssh key block")
tierno3bedc9b2019-11-27 15:46:57 +00001372 if deep_get(config_descriptor, ("config-access", "ssh-access", "required")):
tierno588547c2020-07-01 15:30:20 +00001373 # self.logger.debug("ssh key needed")
tierno3bedc9b2019-11-27 15:46:57 +00001374 # Needed to inject a ssh key
1375 user = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
1376 step = "Install configuration Software, getting public ssh key"
tierno588547c2020-07-01 15:30:20 +00001377 pub_key = await self.vca_map[vca_type].get_ee_ssh_public__key(ee_id=ee_id, db_dict=db_dict)
quilesj7e13aeb2019-10-08 13:34:55 +02001378
tiernoacc90452019-12-10 11:06:54 +00001379 step = "Insert public key into VM user={} ssh_key={}".format(user, pub_key)
tierno3bedc9b2019-11-27 15:46:57 +00001380 else:
tierno588547c2020-07-01 15:30:20 +00001381 # self.logger.debug("no need to get ssh key")
tierno3bedc9b2019-11-27 15:46:57 +00001382 step = "Waiting to VM being up and getting IP address"
1383 self.logger.debug(logging_text + step)
quilesj7e13aeb2019-10-08 13:34:55 +02001384
tierno3bedc9b2019-11-27 15:46:57 +00001385 # n2vc_redesign STEP 5.1
1386 # wait for RO (ip-address) Insert pub_key into VM
tierno5ee02052019-12-05 19:55:02 +00001387 if vnfr_id:
tierno7ecbc342020-09-21 14:05:39 +00001388 if kdu_name:
1389 rw_mgmt_ip = await self.wait_kdu_up(logging_text, nsr_id, vnfr_id, kdu_name)
1390 else:
1391 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id,
1392 vdu_index, user=user, pub_key=pub_key)
tierno5ee02052019-12-05 19:55:02 +00001393 else:
1394 rw_mgmt_ip = None # This is for a NS configuration
tierno3bedc9b2019-11-27 15:46:57 +00001395
1396 self.logger.debug(logging_text + ' VM_ip_address={}'.format(rw_mgmt_ip))
quilesj7e13aeb2019-10-08 13:34:55 +02001397
tiernoa5088192019-11-26 16:12:53 +00001398 # store rw_mgmt_ip in deploy params for later replacement
quilesj7e13aeb2019-10-08 13:34:55 +02001399 deploy_params["rw_mgmt_ip"] = rw_mgmt_ip
tiernod8323042019-08-09 11:32:23 +00001400
1401 # n2vc_redesign STEP 6 Execute initial config primitive
quilesj7e13aeb2019-10-08 13:34:55 +02001402 step = 'execute initial config primitive'
quilesj3655ae02019-12-12 16:08:35 +00001403
1404 # wait for dependent primitives execution (NS -> VNF -> VDU)
tierno5ee02052019-12-05 19:55:02 +00001405 if initial_config_primitive_list:
1406 await self._wait_dependent_n2vc(nsr_id, vca_deployed_list, vca_index)
quilesj3655ae02019-12-12 16:08:35 +00001407
1408 # stage, in function of element type: vdu, kdu, vnf or ns
1409 my_vca = vca_deployed_list[vca_index]
1410 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
1411 # VDU or KDU
tiernoe876f672020-02-13 14:34:48 +00001412 stage[0] = 'Stage 3/5: running Day-1 primitives for VDU.'
quilesj3655ae02019-12-12 16:08:35 +00001413 elif my_vca.get("member-vnf-index"):
1414 # VNF
tiernoe876f672020-02-13 14:34:48 +00001415 stage[0] = 'Stage 4/5: running Day-1 primitives for VNF.'
quilesj3655ae02019-12-12 16:08:35 +00001416 else:
1417 # NS
tiernoe876f672020-02-13 14:34:48 +00001418 stage[0] = 'Stage 5/5: running Day-1 primitives for NS.'
quilesj3655ae02019-12-12 16:08:35 +00001419
tiernoc231a872020-01-21 08:49:05 +00001420 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001421 nsr_id=nsr_id,
1422 vca_index=vca_index,
1423 status='EXECUTING PRIMITIVE'
1424 )
1425
1426 self._write_op_status(
1427 op_id=nslcmop_id,
1428 stage=stage
1429 )
1430
tiernoe876f672020-02-13 14:34:48 +00001431 check_if_terminated_needed = True
tiernod8323042019-08-09 11:32:23 +00001432 for initial_config_primitive in initial_config_primitive_list:
tiernoda6fb102019-11-23 00:36:52 +00001433 # adding information on the vca_deployed if it is a NS execution environment
1434 if not vca_deployed["member-vnf-index"]:
David Garciad4816682019-12-09 14:57:43 +01001435 deploy_params["ns_config_info"] = json.dumps(self._get_ns_config_info(nsr_id))
tiernod8323042019-08-09 11:32:23 +00001436 # TODO check if already done
1437 primitive_params_ = self._map_primitive_params(initial_config_primitive, {}, deploy_params)
tierno3bedc9b2019-11-27 15:46:57 +00001438
tiernod8323042019-08-09 11:32:23 +00001439 step = "execute primitive '{}' params '{}'".format(initial_config_primitive["name"], primitive_params_)
1440 self.logger.debug(logging_text + step)
tierno588547c2020-07-01 15:30:20 +00001441 await self.vca_map[vca_type].exec_primitive(
quilesj7e13aeb2019-10-08 13:34:55 +02001442 ee_id=ee_id,
1443 primitive_name=initial_config_primitive["name"],
1444 params_dict=primitive_params_,
1445 db_dict=db_dict
1446 )
tiernoe876f672020-02-13 14:34:48 +00001447 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
1448 if check_if_terminated_needed:
1449 if config_descriptor.get('terminate-config-primitive'):
1450 self.update_db_2("nsrs", nsr_id, {db_update_entry + "needed_terminate": True})
1451 check_if_terminated_needed = False
quilesj3655ae02019-12-12 16:08:35 +00001452
tiernod8323042019-08-09 11:32:23 +00001453 # TODO register in database that primitive is done
quilesj7e13aeb2019-10-08 13:34:55 +02001454
tiernob996d942020-07-03 14:52:28 +00001455 # STEP 7 Configure metrics
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001456 if vca_type == "helm" or vca_type == "helm-v3":
tiernob996d942020-07-03 14:52:28 +00001457 prometheus_jobs = await self.add_prometheus_metrics(
1458 ee_id=ee_id,
1459 artifact_path=artifact_path,
1460 ee_config_descriptor=ee_config_descriptor,
1461 vnfr_id=vnfr_id,
1462 nsr_id=nsr_id,
1463 target_ip=rw_mgmt_ip,
1464 )
1465 if prometheus_jobs:
1466 self.update_db_2("nsrs", nsr_id, {db_update_entry + "prometheus_jobs": prometheus_jobs})
1467
quilesj7e13aeb2019-10-08 13:34:55 +02001468 step = "instantiated at VCA"
1469 self.logger.debug(logging_text + step)
1470
tiernoc231a872020-01-21 08:49:05 +00001471 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001472 nsr_id=nsr_id,
1473 vca_index=vca_index,
1474 status='READY'
1475 )
1476
tiernod8323042019-08-09 11:32:23 +00001477 except Exception as e: # TODO not use Exception but N2VC exception
quilesj3655ae02019-12-12 16:08:35 +00001478 # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
tiernoe876f672020-02-13 14:34:48 +00001479 if not isinstance(e, (DbException, N2VCException, LcmException, asyncio.CancelledError)):
1480 self.logger.error("Exception while {} : {}".format(step, e), exc_info=True)
tiernoc231a872020-01-21 08:49:05 +00001481 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001482 nsr_id=nsr_id,
1483 vca_index=vca_index,
1484 status='BROKEN'
1485 )
tiernoe876f672020-02-13 14:34:48 +00001486 raise LcmException("{} {}".format(step, e)) from e
tiernod8323042019-08-09 11:32:23 +00001487
quilesj4cda56b2019-12-05 10:02:20 +00001488 def _write_ns_status(self, nsr_id: str, ns_state: str, current_operation: str, current_operation_id: str,
tiernoa2143262020-03-27 16:20:40 +00001489 error_description: str = None, error_detail: str = None, other_update: dict = None):
tiernoe876f672020-02-13 14:34:48 +00001490 """
1491 Update db_nsr fields.
1492 :param nsr_id:
1493 :param ns_state:
1494 :param current_operation:
1495 :param current_operation_id:
1496 :param error_description:
tiernoa2143262020-03-27 16:20:40 +00001497 :param error_detail:
tiernoe876f672020-02-13 14:34:48 +00001498 :param other_update: Other required changes at database if provided, will be cleared
1499 :return:
1500 """
quilesj4cda56b2019-12-05 10:02:20 +00001501 try:
tiernoe876f672020-02-13 14:34:48 +00001502 db_dict = other_update or {}
1503 db_dict["_admin.nslcmop"] = current_operation_id # for backward compatibility
1504 db_dict["_admin.current-operation"] = current_operation_id
1505 db_dict["_admin.operation-type"] = current_operation if current_operation != "IDLE" else None
quilesj4cda56b2019-12-05 10:02:20 +00001506 db_dict["currentOperation"] = current_operation
1507 db_dict["currentOperationID"] = current_operation_id
1508 db_dict["errorDescription"] = error_description
tiernoa2143262020-03-27 16:20:40 +00001509 db_dict["errorDetail"] = error_detail
tiernoe876f672020-02-13 14:34:48 +00001510
1511 if ns_state:
1512 db_dict["nsState"] = ns_state
quilesj4cda56b2019-12-05 10:02:20 +00001513 self.update_db_2("nsrs", nsr_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001514 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001515 self.logger.warn('Error writing NS status, ns={}: {}'.format(nsr_id, e))
1516
tiernoe876f672020-02-13 14:34:48 +00001517 def _write_op_status(self, op_id: str, stage: list = None, error_message: str = None, queuePosition: int = 0,
1518 operation_state: str = None, other_update: dict = None):
quilesj3655ae02019-12-12 16:08:35 +00001519 try:
tiernoe876f672020-02-13 14:34:48 +00001520 db_dict = other_update or {}
quilesj3655ae02019-12-12 16:08:35 +00001521 db_dict['queuePosition'] = queuePosition
tiernoe876f672020-02-13 14:34:48 +00001522 if isinstance(stage, list):
1523 db_dict['stage'] = stage[0]
1524 db_dict['detailed-status'] = " ".join(stage)
1525 elif stage is not None:
1526 db_dict['stage'] = str(stage)
1527
1528 if error_message is not None:
quilesj3655ae02019-12-12 16:08:35 +00001529 db_dict['errorMessage'] = error_message
tiernoe876f672020-02-13 14:34:48 +00001530 if operation_state is not None:
1531 db_dict['operationState'] = operation_state
1532 db_dict["statusEnteredTime"] = time()
quilesj3655ae02019-12-12 16:08:35 +00001533 self.update_db_2("nslcmops", op_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001534 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001535 self.logger.warn('Error writing OPERATION status for op_id: {} -> {}'.format(op_id, e))
1536
tierno51183952020-04-03 15:48:18 +00001537 def _write_all_config_status(self, db_nsr: dict, status: str):
quilesj3655ae02019-12-12 16:08:35 +00001538 try:
tierno51183952020-04-03 15:48:18 +00001539 nsr_id = db_nsr["_id"]
quilesj3655ae02019-12-12 16:08:35 +00001540 # configurationStatus
1541 config_status = db_nsr.get('configurationStatus')
1542 if config_status:
tierno51183952020-04-03 15:48:18 +00001543 db_nsr_update = {"configurationStatus.{}.status".format(index): status for index, v in
1544 enumerate(config_status) if v}
quilesj3655ae02019-12-12 16:08:35 +00001545 # update status
tierno51183952020-04-03 15:48:18 +00001546 self.update_db_2("nsrs", nsr_id, db_nsr_update)
quilesj3655ae02019-12-12 16:08:35 +00001547
tiernoe876f672020-02-13 14:34:48 +00001548 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001549 self.logger.warn('Error writing all configuration status, ns={}: {}'.format(nsr_id, e))
1550
quilesj63f90042020-01-17 09:53:55 +00001551 def _write_configuration_status(self, nsr_id: str, vca_index: int, status: str = None,
tierno51183952020-04-03 15:48:18 +00001552 element_under_configuration: str = None, element_type: str = None,
1553 other_update: dict = None):
quilesj3655ae02019-12-12 16:08:35 +00001554
1555 # self.logger.debug('_write_configuration_status(): vca_index={}, status={}'
1556 # .format(vca_index, status))
1557
1558 try:
1559 db_path = 'configurationStatus.{}.'.format(vca_index)
tierno51183952020-04-03 15:48:18 +00001560 db_dict = other_update or {}
quilesj63f90042020-01-17 09:53:55 +00001561 if status:
1562 db_dict[db_path + 'status'] = status
quilesj3655ae02019-12-12 16:08:35 +00001563 if element_under_configuration:
1564 db_dict[db_path + 'elementUnderConfiguration'] = element_under_configuration
1565 if element_type:
1566 db_dict[db_path + 'elementType'] = element_type
1567 self.update_db_2("nsrs", nsr_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001568 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001569 self.logger.warn('Error writing configuration status={}, ns={}, vca_index={}: {}'
1570 .format(status, nsr_id, vca_index, e))
quilesj4cda56b2019-12-05 10:02:20 +00001571
tierno38089af2020-04-16 07:56:58 +00001572 async def _do_placement(self, logging_text, db_nslcmop, db_vnfrs):
1573 """
1574 Check and computes the placement, (vim account where to deploy). If it is decided by an external tool, it
1575 sends the request via kafka and wait until the result is wrote at database (nslcmops _admin.plca).
1576 Database is used because the result can be obtained from a different LCM worker in case of HA.
1577 :param logging_text: contains the prefix for logging, with the ns and nslcmop identifiers
1578 :param db_nslcmop: database content of nslcmop
1579 :param db_vnfrs: database content of vnfrs, indexed by member-vnf-index.
tierno8790a3d2020-04-23 22:49:52 +00001580 :return: True if some modification is done. Modifies database vnfrs and parameter db_vnfr with the
1581 computed 'vim-account-id'
tierno38089af2020-04-16 07:56:58 +00001582 """
tierno8790a3d2020-04-23 22:49:52 +00001583 modified = False
tierno38089af2020-04-16 07:56:58 +00001584 nslcmop_id = db_nslcmop['_id']
magnussonle9198bb2020-01-21 13:00:51 +01001585 placement_engine = deep_get(db_nslcmop, ('operationParams', 'placement-engine'))
1586 if placement_engine == "PLA":
tierno38089af2020-04-16 07:56:58 +00001587 self.logger.debug(logging_text + "Invoke and wait for placement optimization")
1588 await self.msg.aiowrite("pla", "get_placement", {'nslcmopId': nslcmop_id}, loop=self.loop)
magnussonle9198bb2020-01-21 13:00:51 +01001589 db_poll_interval = 5
tierno38089af2020-04-16 07:56:58 +00001590 wait = db_poll_interval * 10
magnussonle9198bb2020-01-21 13:00:51 +01001591 pla_result = None
1592 while not pla_result and wait >= 0:
1593 await asyncio.sleep(db_poll_interval)
1594 wait -= db_poll_interval
tierno38089af2020-04-16 07:56:58 +00001595 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
magnussonle9198bb2020-01-21 13:00:51 +01001596 pla_result = deep_get(db_nslcmop, ('_admin', 'pla'))
1597
1598 if not pla_result:
tierno38089af2020-04-16 07:56:58 +00001599 raise LcmException("Placement timeout for nslcmopId={}".format(nslcmop_id))
magnussonle9198bb2020-01-21 13:00:51 +01001600
1601 for pla_vnf in pla_result['vnf']:
1602 vnfr = db_vnfrs.get(pla_vnf['member-vnf-index'])
1603 if not pla_vnf.get('vimAccountId') or not vnfr:
1604 continue
tierno8790a3d2020-04-23 22:49:52 +00001605 modified = True
magnussonle9198bb2020-01-21 13:00:51 +01001606 self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, {"vim-account-id": pla_vnf['vimAccountId']})
tierno38089af2020-04-16 07:56:58 +00001607 # Modifies db_vnfrs
1608 vnfr["vim-account-id"] = pla_vnf['vimAccountId']
tierno8790a3d2020-04-23 22:49:52 +00001609 return modified
magnussonle9198bb2020-01-21 13:00:51 +01001610
1611 def update_nsrs_with_pla_result(self, params):
1612 try:
1613 nslcmop_id = deep_get(params, ('placement', 'nslcmopId'))
1614 self.update_db_2("nslcmops", nslcmop_id, {"_admin.pla": params.get('placement')})
1615 except Exception as e:
1616 self.logger.warn('Update failed for nslcmop_id={}:{}'.format(nslcmop_id, e))
1617
tierno59d22d22018-09-25 18:10:19 +02001618 async def instantiate(self, nsr_id, nslcmop_id):
quilesj7e13aeb2019-10-08 13:34:55 +02001619 """
1620
1621 :param nsr_id: ns instance to deploy
1622 :param nslcmop_id: operation to run
1623 :return:
1624 """
kuused124bfe2019-06-18 12:09:24 +02001625
1626 # Try to lock HA task here
1627 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
1628 if not task_is_locked_by_me:
quilesj3655ae02019-12-12 16:08:35 +00001629 self.logger.debug('instantiate() task is not locked by me, ns={}'.format(nsr_id))
kuused124bfe2019-06-18 12:09:24 +02001630 return
1631
tierno59d22d22018-09-25 18:10:19 +02001632 logging_text = "Task ns={} instantiate={} ".format(nsr_id, nslcmop_id)
1633 self.logger.debug(logging_text + "Enter")
quilesj7e13aeb2019-10-08 13:34:55 +02001634
tierno59d22d22018-09-25 18:10:19 +02001635 # get all needed from database
quilesj7e13aeb2019-10-08 13:34:55 +02001636
1637 # database nsrs record
tierno59d22d22018-09-25 18:10:19 +02001638 db_nsr = None
quilesj7e13aeb2019-10-08 13:34:55 +02001639
1640 # database nslcmops record
tierno59d22d22018-09-25 18:10:19 +02001641 db_nslcmop = None
quilesj7e13aeb2019-10-08 13:34:55 +02001642
1643 # update operation on nsrs
tiernoe876f672020-02-13 14:34:48 +00001644 db_nsr_update = {}
quilesj7e13aeb2019-10-08 13:34:55 +02001645 # update operation on nslcmops
tierno59d22d22018-09-25 18:10:19 +02001646 db_nslcmop_update = {}
quilesj7e13aeb2019-10-08 13:34:55 +02001647
tierno59d22d22018-09-25 18:10:19 +02001648 nslcmop_operation_state = None
quilesj7e13aeb2019-10-08 13:34:55 +02001649 db_vnfrs = {} # vnf's info indexed by member-index
1650 # n2vc_info = {}
tiernoe876f672020-02-13 14:34:48 +00001651 tasks_dict_info = {} # from task to info text
tierno59d22d22018-09-25 18:10:19 +02001652 exc = None
tiernoe876f672020-02-13 14:34:48 +00001653 error_list = []
1654 stage = ['Stage 1/5: preparation of the environment.', "Waiting for previous operations to terminate.", ""]
1655 # ^ stage, step, VIM progress
tierno59d22d22018-09-25 18:10:19 +02001656 try:
kuused124bfe2019-06-18 12:09:24 +02001657 # wait for any previous tasks in process
1658 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
1659
quilesj7e13aeb2019-10-08 13:34:55 +02001660 # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
tiernob5203912020-08-11 11:20:13 +00001661 stage[1] = "Reading from database."
quilesj4cda56b2019-12-05 10:02:20 +00001662 # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
tiernoe876f672020-02-13 14:34:48 +00001663 db_nsr_update["detailed-status"] = "creating"
1664 db_nsr_update["operational-status"] = "init"
quilesj4cda56b2019-12-05 10:02:20 +00001665 self._write_ns_status(
1666 nsr_id=nsr_id,
1667 ns_state="BUILDING",
1668 current_operation="INSTANTIATING",
tiernoe876f672020-02-13 14:34:48 +00001669 current_operation_id=nslcmop_id,
1670 other_update=db_nsr_update
1671 )
1672 self._write_op_status(
1673 op_id=nslcmop_id,
1674 stage=stage,
1675 queuePosition=0
quilesj4cda56b2019-12-05 10:02:20 +00001676 )
1677
quilesj7e13aeb2019-10-08 13:34:55 +02001678 # read from db: operation
tiernob5203912020-08-11 11:20:13 +00001679 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
tierno59d22d22018-09-25 18:10:19 +02001680 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
tierno744303e2020-01-13 16:46:31 +00001681 ns_params = db_nslcmop.get("operationParams")
1682 if ns_params and ns_params.get("timeout_ns_deploy"):
1683 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
1684 else:
1685 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
quilesj7e13aeb2019-10-08 13:34:55 +02001686
1687 # read from db: ns
tiernob5203912020-08-11 11:20:13 +00001688 stage[1] = "Getting nsr={} from db.".format(nsr_id)
tierno59d22d22018-09-25 18:10:19 +02001689 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
tiernob5203912020-08-11 11:20:13 +00001690 stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
tiernod732fb82020-05-21 13:18:23 +00001691 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
bravof24260252021-03-11 12:03:30 -03001692 self.fs.sync(db_nsr["nsd-id"])
tiernod732fb82020-05-21 13:18:23 +00001693 db_nsr["nsd"] = nsd
tiernod8323042019-08-09 11:32:23 +00001694 # nsr_name = db_nsr["name"] # TODO short-name??
tierno47e86b52018-10-10 14:05:55 +02001695
quilesj7e13aeb2019-10-08 13:34:55 +02001696 # read from db: vnf's of this ns
tiernob5203912020-08-11 11:20:13 +00001697 stage[1] = "Getting vnfrs from db."
tiernoe876f672020-02-13 14:34:48 +00001698 self.logger.debug(logging_text + stage[1])
tierno27246d82018-09-27 15:59:09 +02001699 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
tierno27246d82018-09-27 15:59:09 +02001700
quilesj7e13aeb2019-10-08 13:34:55 +02001701 # read from db: vnfd's for every vnf
bravof922c4172020-11-24 21:21:43 -03001702 db_vnfds = [] # every vnfd data
quilesj7e13aeb2019-10-08 13:34:55 +02001703
1704 # for each vnf in ns, read vnfd
tierno27246d82018-09-27 15:59:09 +02001705 for vnfr in db_vnfrs_list:
bravof922c4172020-11-24 21:21:43 -03001706 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
1707 vnfd_id = vnfr["vnfd-id"]
1708 vnfd_ref = vnfr["vnfd-ref"]
bravof24260252021-03-11 12:03:30 -03001709 self.fs.sync(vnfd_id)
lloretgalleg6d488782020-07-22 10:13:46 +00001710
quilesj7e13aeb2019-10-08 13:34:55 +02001711 # if we haven't this vnfd, read it from db
tierno27246d82018-09-27 15:59:09 +02001712 if vnfd_id not in db_vnfds:
quilesj63f90042020-01-17 09:53:55 +00001713 # read from db
tiernob5203912020-08-11 11:20:13 +00001714 stage[1] = "Getting vnfd={} id='{}' from db.".format(vnfd_id, vnfd_ref)
tiernoe876f672020-02-13 14:34:48 +00001715 self.logger.debug(logging_text + stage[1])
tierno27246d82018-09-27 15:59:09 +02001716 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
tierno27246d82018-09-27 15:59:09 +02001717
quilesj7e13aeb2019-10-08 13:34:55 +02001718 # store vnfd
David Garciad41dbd62020-12-10 12:52:52 +01001719 db_vnfds.append(vnfd)
quilesj7e13aeb2019-10-08 13:34:55 +02001720
1721 # Get or generates the _admin.deployed.VCA list
tiernoe4f7e6c2018-11-27 14:55:30 +00001722 vca_deployed_list = None
1723 if db_nsr["_admin"].get("deployed"):
1724 vca_deployed_list = db_nsr["_admin"]["deployed"].get("VCA")
1725 if vca_deployed_list is None:
1726 vca_deployed_list = []
quilesj3655ae02019-12-12 16:08:35 +00001727 configuration_status_list = []
tiernoe4f7e6c2018-11-27 14:55:30 +00001728 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
quilesj3655ae02019-12-12 16:08:35 +00001729 db_nsr_update["configurationStatus"] = configuration_status_list
quilesj7e13aeb2019-10-08 13:34:55 +02001730 # add _admin.deployed.VCA to db_nsr dictionary, value=vca_deployed_list
tierno98ad6ea2019-05-30 17:16:28 +00001731 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
tiernoe4f7e6c2018-11-27 14:55:30 +00001732 elif isinstance(vca_deployed_list, dict):
1733 # maintain backward compatibility. Change a dict to list at database
1734 vca_deployed_list = list(vca_deployed_list.values())
1735 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
tierno98ad6ea2019-05-30 17:16:28 +00001736 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
tiernoe4f7e6c2018-11-27 14:55:30 +00001737
tierno6cf25f52019-09-12 09:33:40 +00001738 if not isinstance(deep_get(db_nsr, ("_admin", "deployed", "RO", "vnfd")), list):
tiernoa009e552019-01-30 16:45:44 +00001739 populate_dict(db_nsr, ("_admin", "deployed", "RO", "vnfd"), [])
1740 db_nsr_update["_admin.deployed.RO.vnfd"] = []
tierno59d22d22018-09-25 18:10:19 +02001741
tiernobaa51102018-12-14 13:16:18 +00001742 # set state to INSTANTIATED. When instantiated NBI will not delete directly
1743 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
1744 self.update_db_2("nsrs", nsr_id, db_nsr_update)
lloretgalleg6d488782020-07-22 10:13:46 +00001745 self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "INSTANTIATED"})
quilesj3655ae02019-12-12 16:08:35 +00001746
1747 # n2vc_redesign STEP 2 Deploy Network Scenario
tiernoe876f672020-02-13 14:34:48 +00001748 stage[0] = 'Stage 2/5: deployment of KDUs, VMs and execution environments.'
quilesj3655ae02019-12-12 16:08:35 +00001749 self._write_op_status(
1750 op_id=nslcmop_id,
tiernoe876f672020-02-13 14:34:48 +00001751 stage=stage
quilesj3655ae02019-12-12 16:08:35 +00001752 )
1753
tiernob5203912020-08-11 11:20:13 +00001754 stage[1] = "Deploying KDUs."
tiernoe876f672020-02-13 14:34:48 +00001755 # self.logger.debug(logging_text + "Before deploy_kdus")
calvinosanch9f9c6f22019-11-04 13:37:39 +01001756 # Call to deploy_kdus in case exists the "vdu:kdu" param
tiernoe876f672020-02-13 14:34:48 +00001757 await self.deploy_kdus(
1758 logging_text=logging_text,
1759 nsr_id=nsr_id,
1760 nslcmop_id=nslcmop_id,
1761 db_vnfrs=db_vnfrs,
1762 db_vnfds=db_vnfds,
1763 task_instantiation_info=tasks_dict_info,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001764 )
tiernoe876f672020-02-13 14:34:48 +00001765
1766 stage[1] = "Getting VCA public key."
tiernod8323042019-08-09 11:32:23 +00001767 # n2vc_redesign STEP 1 Get VCA public ssh-key
1768 # feature 1429. Add n2vc public key to needed VMs
tierno3bedc9b2019-11-27 15:46:57 +00001769 n2vc_key = self.n2vc.get_public_key()
tiernoa5088192019-11-26 16:12:53 +00001770 n2vc_key_list = [n2vc_key]
1771 if self.vca_config.get("public_key"):
1772 n2vc_key_list.append(self.vca_config["public_key"])
tierno98ad6ea2019-05-30 17:16:28 +00001773
tiernoe876f672020-02-13 14:34:48 +00001774 stage[1] = "Deploying NS at VIM."
tiernod8323042019-08-09 11:32:23 +00001775 task_ro = asyncio.ensure_future(
quilesj7e13aeb2019-10-08 13:34:55 +02001776 self.instantiate_RO(
1777 logging_text=logging_text,
1778 nsr_id=nsr_id,
1779 nsd=nsd,
1780 db_nsr=db_nsr,
1781 db_nslcmop=db_nslcmop,
1782 db_vnfrs=db_vnfrs,
bravof922c4172020-11-24 21:21:43 -03001783 db_vnfds=db_vnfds,
tiernoe876f672020-02-13 14:34:48 +00001784 n2vc_key_list=n2vc_key_list,
1785 stage=stage
tierno98ad6ea2019-05-30 17:16:28 +00001786 )
tiernod8323042019-08-09 11:32:23 +00001787 )
1788 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_RO", task_ro)
tiernoa2143262020-03-27 16:20:40 +00001789 tasks_dict_info[task_ro] = "Deploying at VIM"
tierno98ad6ea2019-05-30 17:16:28 +00001790
tiernod8323042019-08-09 11:32:23 +00001791 # n2vc_redesign STEP 3 to 6 Deploy N2VC
tiernoe876f672020-02-13 14:34:48 +00001792 stage[1] = "Deploying Execution Environments."
1793 self.logger.debug(logging_text + stage[1])
tierno98ad6ea2019-05-30 17:16:28 +00001794
tiernod8323042019-08-09 11:32:23 +00001795 nsi_id = None # TODO put nsi_id when this nsr belongs to a NSI
bravof922c4172020-11-24 21:21:43 -03001796 for vnf_profile in get_vnf_profiles(nsd):
1797 vnfd_id = vnf_profile["vnfd-id"]
1798 vnfd = find_in_list(db_vnfds, lambda a_vnf: a_vnf["id"] == vnfd_id)
1799 member_vnf_index = str(vnf_profile["id"])
tiernod8323042019-08-09 11:32:23 +00001800 db_vnfr = db_vnfrs[member_vnf_index]
1801 base_folder = vnfd["_admin"]["storage"]
1802 vdu_id = None
1803 vdu_index = 0
tierno98ad6ea2019-05-30 17:16:28 +00001804 vdu_name = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001805 kdu_name = None
tierno59d22d22018-09-25 18:10:19 +02001806
tierno8a518872018-12-21 13:42:14 +00001807 # Get additional parameters
bravof922c4172020-11-24 21:21:43 -03001808 deploy_params = {"OSM": get_osm_params(db_vnfr)}
tiernod8323042019-08-09 11:32:23 +00001809 if db_vnfr.get("additionalParamsForVnf"):
bravof922c4172020-11-24 21:21:43 -03001810 deploy_params.update(parse_yaml_strings(db_vnfr["additionalParamsForVnf"].copy()))
tierno8a518872018-12-21 13:42:14 +00001811
garciaalea77ced72021-02-17 19:09:12 -03001812 descriptor_config = get_configuration(vnfd, vnfd["id"])
tierno588547c2020-07-01 15:30:20 +00001813 if descriptor_config:
quilesj7e13aeb2019-10-08 13:34:55 +02001814 self._deploy_n2vc(
tiernoa54150d2019-12-05 17:15:10 +00001815 logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
quilesj7e13aeb2019-10-08 13:34:55 +02001816 db_nsr=db_nsr,
1817 db_vnfr=db_vnfr,
1818 nslcmop_id=nslcmop_id,
1819 nsr_id=nsr_id,
1820 nsi_id=nsi_id,
1821 vnfd_id=vnfd_id,
1822 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001823 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001824 member_vnf_index=member_vnf_index,
1825 vdu_index=vdu_index,
1826 vdu_name=vdu_name,
1827 deploy_params=deploy_params,
1828 descriptor_config=descriptor_config,
1829 base_folder=base_folder,
tiernoe876f672020-02-13 14:34:48 +00001830 task_instantiation_info=tasks_dict_info,
1831 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001832 )
tierno59d22d22018-09-25 18:10:19 +02001833
1834 # Deploy charms for each VDU that supports one.
bravof922c4172020-11-24 21:21:43 -03001835 for vdud in get_vdu_list(vnfd):
tiernod8323042019-08-09 11:32:23 +00001836 vdu_id = vdud["id"]
garciaalea77ced72021-02-17 19:09:12 -03001837 descriptor_config = get_configuration(vnfd, vdu_id)
bravof922c4172020-11-24 21:21:43 -03001838 vdur = find_in_list(db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id)
1839
tierno626e0152019-11-29 14:16:16 +00001840 if vdur.get("additionalParams"):
bravof922c4172020-11-24 21:21:43 -03001841 deploy_params_vdu = parse_yaml_strings(vdur["additionalParams"])
tierno626e0152019-11-29 14:16:16 +00001842 else:
1843 deploy_params_vdu = deploy_params
bravof922c4172020-11-24 21:21:43 -03001844 deploy_params_vdu["OSM"] = get_osm_params(db_vnfr, vdu_id, vdu_count_index=0)
1845 vdud_count = get_vdu_profile(vnfd, vdu_id).get("max-number-of-instances", 1)
1846
1847 self.logger.debug("VDUD > {}".format(vdud))
1848 self.logger.debug("Descriptor config > {}".format(descriptor_config))
tierno588547c2020-07-01 15:30:20 +00001849 if descriptor_config:
tiernod8323042019-08-09 11:32:23 +00001850 vdu_name = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001851 kdu_name = None
bravof922c4172020-11-24 21:21:43 -03001852 for vdu_index in range(vdud_count):
tiernod8323042019-08-09 11:32:23 +00001853 # TODO vnfr_params["rw_mgmt_ip"] = vdur["ip-address"]
quilesj7e13aeb2019-10-08 13:34:55 +02001854 self._deploy_n2vc(
tiernoa54150d2019-12-05 17:15:10 +00001855 logging_text=logging_text + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
1856 member_vnf_index, vdu_id, vdu_index),
quilesj7e13aeb2019-10-08 13:34:55 +02001857 db_nsr=db_nsr,
1858 db_vnfr=db_vnfr,
1859 nslcmop_id=nslcmop_id,
1860 nsr_id=nsr_id,
1861 nsi_id=nsi_id,
1862 vnfd_id=vnfd_id,
1863 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001864 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001865 member_vnf_index=member_vnf_index,
1866 vdu_index=vdu_index,
1867 vdu_name=vdu_name,
tierno626e0152019-11-29 14:16:16 +00001868 deploy_params=deploy_params_vdu,
quilesj7e13aeb2019-10-08 13:34:55 +02001869 descriptor_config=descriptor_config,
1870 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001871 task_instantiation_info=tasks_dict_info,
1872 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001873 )
bravof922c4172020-11-24 21:21:43 -03001874 for kdud in get_kdu_list(vnfd):
calvinosanch9f9c6f22019-11-04 13:37:39 +01001875 kdu_name = kdud["name"]
garciaalea77ced72021-02-17 19:09:12 -03001876 descriptor_config = get_configuration(vnfd, kdu_name)
tierno588547c2020-07-01 15:30:20 +00001877 if descriptor_config:
calvinosanch9f9c6f22019-11-04 13:37:39 +01001878 vdu_id = None
1879 vdu_index = 0
1880 vdu_name = None
tierno72ef84f2020-10-06 08:22:07 +00001881 kdur = next(x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name)
bravof922c4172020-11-24 21:21:43 -03001882 deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
tierno72ef84f2020-10-06 08:22:07 +00001883 if kdur.get("additionalParams"):
bravof922c4172020-11-24 21:21:43 -03001884 deploy_params_kdu = parse_yaml_strings(kdur["additionalParams"])
tierno59d22d22018-09-25 18:10:19 +02001885
calvinosanch9f9c6f22019-11-04 13:37:39 +01001886 self._deploy_n2vc(
1887 logging_text=logging_text,
1888 db_nsr=db_nsr,
1889 db_vnfr=db_vnfr,
1890 nslcmop_id=nslcmop_id,
1891 nsr_id=nsr_id,
1892 nsi_id=nsi_id,
1893 vnfd_id=vnfd_id,
1894 vdu_id=vdu_id,
1895 kdu_name=kdu_name,
1896 member_vnf_index=member_vnf_index,
1897 vdu_index=vdu_index,
1898 vdu_name=vdu_name,
tierno72ef84f2020-10-06 08:22:07 +00001899 deploy_params=deploy_params_kdu,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001900 descriptor_config=descriptor_config,
1901 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001902 task_instantiation_info=tasks_dict_info,
1903 stage=stage
calvinosanch9f9c6f22019-11-04 13:37:39 +01001904 )
tierno59d22d22018-09-25 18:10:19 +02001905
tierno1b633412019-02-25 16:48:23 +00001906 # Check if this NS has a charm configuration
tiernod8323042019-08-09 11:32:23 +00001907 descriptor_config = nsd.get("ns-configuration")
1908 if descriptor_config and descriptor_config.get("juju"):
1909 vnfd_id = None
1910 db_vnfr = None
1911 member_vnf_index = None
1912 vdu_id = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001913 kdu_name = None
tiernod8323042019-08-09 11:32:23 +00001914 vdu_index = 0
1915 vdu_name = None
tierno1b633412019-02-25 16:48:23 +00001916
tiernod8323042019-08-09 11:32:23 +00001917 # Get additional parameters
David Garcia40603572020-12-10 20:10:53 +01001918 deploy_params = {"OSM": {"vim_account_id": ns_params["vimAccountId"]}}
tiernod8323042019-08-09 11:32:23 +00001919 if db_nsr.get("additionalParamsForNs"):
bravof922c4172020-11-24 21:21:43 -03001920 deploy_params.update(parse_yaml_strings(db_nsr["additionalParamsForNs"].copy()))
tiernod8323042019-08-09 11:32:23 +00001921 base_folder = nsd["_admin"]["storage"]
quilesj7e13aeb2019-10-08 13:34:55 +02001922 self._deploy_n2vc(
1923 logging_text=logging_text,
1924 db_nsr=db_nsr,
1925 db_vnfr=db_vnfr,
1926 nslcmop_id=nslcmop_id,
1927 nsr_id=nsr_id,
1928 nsi_id=nsi_id,
1929 vnfd_id=vnfd_id,
1930 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001931 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001932 member_vnf_index=member_vnf_index,
1933 vdu_index=vdu_index,
1934 vdu_name=vdu_name,
1935 deploy_params=deploy_params,
1936 descriptor_config=descriptor_config,
1937 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001938 task_instantiation_info=tasks_dict_info,
1939 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001940 )
tierno1b633412019-02-25 16:48:23 +00001941
tiernoe876f672020-02-13 14:34:48 +00001942 # rest of staff will be done at finally
tierno1b633412019-02-25 16:48:23 +00001943
tiernoe876f672020-02-13 14:34:48 +00001944 except (ROclient.ROClientException, DbException, LcmException, N2VCException) as e:
1945 self.logger.error(logging_text + "Exit Exception while '{}': {}".format(stage[1], e))
tierno59d22d22018-09-25 18:10:19 +02001946 exc = e
1947 except asyncio.CancelledError:
tiernoe876f672020-02-13 14:34:48 +00001948 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(stage[1]))
tierno59d22d22018-09-25 18:10:19 +02001949 exc = "Operation was cancelled"
1950 except Exception as e:
1951 exc = traceback.format_exc()
tiernoe876f672020-02-13 14:34:48 +00001952 self.logger.critical(logging_text + "Exit Exception while '{}': {}".format(stage[1], e), exc_info=True)
tierno59d22d22018-09-25 18:10:19 +02001953 finally:
1954 if exc:
tiernoe876f672020-02-13 14:34:48 +00001955 error_list.append(str(exc))
tiernobaa51102018-12-14 13:16:18 +00001956 try:
tiernoe876f672020-02-13 14:34:48 +00001957 # wait for pending tasks
1958 if tasks_dict_info:
1959 stage[1] = "Waiting for instantiate pending tasks."
1960 self.logger.debug(logging_text + stage[1])
1961 error_list += await self._wait_for_tasks(logging_text, tasks_dict_info, timeout_ns_deploy,
1962 stage, nslcmop_id, nsr_id=nsr_id)
1963 stage[1] = stage[2] = ""
1964 except asyncio.CancelledError:
1965 error_list.append("Cancelled")
1966 # TODO cancel all tasks
1967 except Exception as exc:
1968 error_list.append(str(exc))
quilesj4cda56b2019-12-05 10:02:20 +00001969
tiernoe876f672020-02-13 14:34:48 +00001970 # update operation-status
1971 db_nsr_update["operational-status"] = "running"
1972 # let's begin with VCA 'configured' status (later we can change it)
1973 db_nsr_update["config-status"] = "configured"
1974 for task, task_name in tasks_dict_info.items():
1975 if not task.done() or task.cancelled() or task.exception():
1976 if task_name.startswith(self.task_name_deploy_vca):
1977 # A N2VC task is pending
1978 db_nsr_update["config-status"] = "failed"
quilesj4cda56b2019-12-05 10:02:20 +00001979 else:
tiernoe876f672020-02-13 14:34:48 +00001980 # RO or KDU task is pending
1981 db_nsr_update["operational-status"] = "failed"
quilesj3655ae02019-12-12 16:08:35 +00001982
tiernoe876f672020-02-13 14:34:48 +00001983 # update status at database
1984 if error_list:
tiernoa2143262020-03-27 16:20:40 +00001985 error_detail = ". ".join(error_list)
tiernoe876f672020-02-13 14:34:48 +00001986 self.logger.error(logging_text + error_detail)
tiernob5203912020-08-11 11:20:13 +00001987 error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
1988 error_description_nsr = 'Operation: INSTANTIATING.{}, {}'.format(nslcmop_id, stage[0])
quilesj3655ae02019-12-12 16:08:35 +00001989
tiernoa2143262020-03-27 16:20:40 +00001990 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
tiernoe876f672020-02-13 14:34:48 +00001991 db_nslcmop_update["detailed-status"] = error_detail
1992 nslcmop_operation_state = "FAILED"
1993 ns_state = "BROKEN"
1994 else:
tiernoa2143262020-03-27 16:20:40 +00001995 error_detail = None
tiernoe876f672020-02-13 14:34:48 +00001996 error_description_nsr = error_description_nslcmop = None
1997 ns_state = "READY"
1998 db_nsr_update["detailed-status"] = "Done"
1999 db_nslcmop_update["detailed-status"] = "Done"
2000 nslcmop_operation_state = "COMPLETED"
quilesj4cda56b2019-12-05 10:02:20 +00002001
tiernoe876f672020-02-13 14:34:48 +00002002 if db_nsr:
2003 self._write_ns_status(
2004 nsr_id=nsr_id,
2005 ns_state=ns_state,
2006 current_operation="IDLE",
2007 current_operation_id=None,
2008 error_description=error_description_nsr,
tiernoa2143262020-03-27 16:20:40 +00002009 error_detail=error_detail,
tiernoe876f672020-02-13 14:34:48 +00002010 other_update=db_nsr_update
2011 )
tiernoa17d4f42020-04-28 09:59:23 +00002012 self._write_op_status(
2013 op_id=nslcmop_id,
2014 stage="",
2015 error_message=error_description_nslcmop,
2016 operation_state=nslcmop_operation_state,
2017 other_update=db_nslcmop_update,
2018 )
quilesj3655ae02019-12-12 16:08:35 +00002019
tierno59d22d22018-09-25 18:10:19 +02002020 if nslcmop_operation_state:
2021 try:
2022 await self.msg.aiowrite("ns", "instantiated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tierno8a518872018-12-21 13:42:14 +00002023 "operationState": nslcmop_operation_state},
2024 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02002025 except Exception as e:
2026 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
2027
2028 self.logger.debug(logging_text + "Exit")
2029 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
2030
tierno588547c2020-07-01 15:30:20 +00002031 async def _add_vca_relations(self, logging_text, nsr_id, vca_index: int,
2032 timeout: int = 3600, vca_type: str = None) -> bool:
quilesj63f90042020-01-17 09:53:55 +00002033
2034 # steps:
2035 # 1. find all relations for this VCA
2036 # 2. wait for other peers related
2037 # 3. add relations
2038
2039 try:
tierno588547c2020-07-01 15:30:20 +00002040 vca_type = vca_type or "lxc_proxy_charm"
quilesj63f90042020-01-17 09:53:55 +00002041
2042 # STEP 1: find all relations for this VCA
2043
2044 # read nsr record
2045 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
David Garcia171f3542020-05-21 16:41:07 +02002046 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
quilesj63f90042020-01-17 09:53:55 +00002047
2048 # this VCA data
2049 my_vca = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))[vca_index]
2050
2051 # read all ns-configuration relations
2052 ns_relations = list()
David Garcia171f3542020-05-21 16:41:07 +02002053 db_ns_relations = deep_get(nsd, ('ns-configuration', 'relation'))
quilesj63f90042020-01-17 09:53:55 +00002054 if db_ns_relations:
2055 for r in db_ns_relations:
2056 # check if this VCA is in the relation
2057 if my_vca.get('member-vnf-index') in\
2058 (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
2059 ns_relations.append(r)
2060
2061 # read all vnf-configuration relations
2062 vnf_relations = list()
2063 db_vnfd_list = db_nsr.get('vnfd-id')
2064 if db_vnfd_list:
2065 for vnfd in db_vnfd_list:
aktas97ca0272021-05-04 19:32:45 +03002066 db_vnf_relations = None
quilesj63f90042020-01-17 09:53:55 +00002067 db_vnfd = self.db.get_one("vnfds", {"_id": vnfd})
aktas97ca0272021-05-04 19:32:45 +03002068 db_vnf_configuration = get_configuration(db_vnfd, db_vnfd["id"])
2069 if db_vnf_configuration:
2070 db_vnf_relations = db_vnf_configuration.get("relation", [])
quilesj63f90042020-01-17 09:53:55 +00002071 if db_vnf_relations:
2072 for r in db_vnf_relations:
2073 # check if this VCA is in the relation
2074 if my_vca.get('vdu_id') in (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
2075 vnf_relations.append(r)
2076
2077 # if no relations, terminate
2078 if not ns_relations and not vnf_relations:
2079 self.logger.debug(logging_text + ' No relations')
2080 return True
2081
2082 self.logger.debug(logging_text + ' adding relations\n {}\n {}'.format(ns_relations, vnf_relations))
2083
2084 # add all relations
2085 start = time()
2086 while True:
2087 # check timeout
2088 now = time()
2089 if now - start >= timeout:
2090 self.logger.error(logging_text + ' : timeout adding relations')
2091 return False
2092
2093 # reload nsr from database (we need to update record: _admin.deloyed.VCA)
2094 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
2095
2096 # for each defined NS relation, find the VCA's related
tierno364c4572020-09-14 12:11:32 +00002097 for r in ns_relations.copy():
quilesj63f90042020-01-17 09:53:55 +00002098 from_vca_ee_id = None
2099 to_vca_ee_id = None
2100 from_vca_endpoint = None
2101 to_vca_endpoint = None
2102 vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
2103 for vca in vca_list:
2104 if vca.get('member-vnf-index') == r.get('entities')[0].get('id') \
2105 and vca.get('config_sw_installed'):
2106 from_vca_ee_id = vca.get('ee_id')
2107 from_vca_endpoint = r.get('entities')[0].get('endpoint')
2108 if vca.get('member-vnf-index') == r.get('entities')[1].get('id') \
2109 and vca.get('config_sw_installed'):
2110 to_vca_ee_id = vca.get('ee_id')
2111 to_vca_endpoint = r.get('entities')[1].get('endpoint')
2112 if from_vca_ee_id and to_vca_ee_id:
2113 # add relation
tierno588547c2020-07-01 15:30:20 +00002114 await self.vca_map[vca_type].add_relation(
quilesj63f90042020-01-17 09:53:55 +00002115 ee_id_1=from_vca_ee_id,
2116 ee_id_2=to_vca_ee_id,
2117 endpoint_1=from_vca_endpoint,
2118 endpoint_2=to_vca_endpoint)
2119 # remove entry from relations list
2120 ns_relations.remove(r)
2121 else:
2122 # check failed peers
2123 try:
2124 vca_status_list = db_nsr.get('configurationStatus')
2125 if vca_status_list:
2126 for i in range(len(vca_list)):
2127 vca = vca_list[i]
2128 vca_status = vca_status_list[i]
2129 if vca.get('member-vnf-index') == r.get('entities')[0].get('id'):
2130 if vca_status.get('status') == 'BROKEN':
2131 # peer broken: remove relation from list
2132 ns_relations.remove(r)
2133 if vca.get('member-vnf-index') == r.get('entities')[1].get('id'):
2134 if vca_status.get('status') == 'BROKEN':
2135 # peer broken: remove relation from list
2136 ns_relations.remove(r)
2137 except Exception:
2138 # ignore
2139 pass
2140
2141 # for each defined VNF relation, find the VCA's related
tierno364c4572020-09-14 12:11:32 +00002142 for r in vnf_relations.copy():
quilesj63f90042020-01-17 09:53:55 +00002143 from_vca_ee_id = None
2144 to_vca_ee_id = None
2145 from_vca_endpoint = None
2146 to_vca_endpoint = None
2147 vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
2148 for vca in vca_list:
David Garcia97be6832020-09-09 15:40:44 +02002149 key_to_check = "vdu_id"
2150 if vca.get("vdu_id") is None:
2151 key_to_check = "vnfd_id"
2152 if vca.get(key_to_check) == r.get('entities')[0].get('id') and vca.get('config_sw_installed'):
quilesj63f90042020-01-17 09:53:55 +00002153 from_vca_ee_id = vca.get('ee_id')
2154 from_vca_endpoint = r.get('entities')[0].get('endpoint')
David Garcia97be6832020-09-09 15:40:44 +02002155 if vca.get(key_to_check) == r.get('entities')[1].get('id') and vca.get('config_sw_installed'):
quilesj63f90042020-01-17 09:53:55 +00002156 to_vca_ee_id = vca.get('ee_id')
2157 to_vca_endpoint = r.get('entities')[1].get('endpoint')
2158 if from_vca_ee_id and to_vca_ee_id:
2159 # add relation
tierno588547c2020-07-01 15:30:20 +00002160 await self.vca_map[vca_type].add_relation(
quilesj63f90042020-01-17 09:53:55 +00002161 ee_id_1=from_vca_ee_id,
2162 ee_id_2=to_vca_ee_id,
2163 endpoint_1=from_vca_endpoint,
2164 endpoint_2=to_vca_endpoint)
2165 # remove entry from relations list
2166 vnf_relations.remove(r)
2167 else:
2168 # check failed peers
2169 try:
2170 vca_status_list = db_nsr.get('configurationStatus')
2171 if vca_status_list:
2172 for i in range(len(vca_list)):
2173 vca = vca_list[i]
2174 vca_status = vca_status_list[i]
2175 if vca.get('vdu_id') == r.get('entities')[0].get('id'):
2176 if vca_status.get('status') == 'BROKEN':
2177 # peer broken: remove relation from list
David Garcia092afbd2020-08-25 13:17:25 +02002178 vnf_relations.remove(r)
quilesj63f90042020-01-17 09:53:55 +00002179 if vca.get('vdu_id') == r.get('entities')[1].get('id'):
2180 if vca_status.get('status') == 'BROKEN':
2181 # peer broken: remove relation from list
David Garcia092afbd2020-08-25 13:17:25 +02002182 vnf_relations.remove(r)
quilesj63f90042020-01-17 09:53:55 +00002183 except Exception:
2184 # ignore
2185 pass
2186
2187 # wait for next try
2188 await asyncio.sleep(5.0)
2189
2190 if not ns_relations and not vnf_relations:
2191 self.logger.debug('Relations added')
2192 break
2193
2194 return True
2195
2196 except Exception as e:
2197 self.logger.warn(logging_text + ' ERROR adding relations: {}'.format(e))
2198 return False
2199
tierno7ecbc342020-09-21 14:05:39 +00002200 async def _install_kdu(self, nsr_id: str, nsr_db_path: str, vnfr_data: dict, kdu_index: int, kdud: dict,
lloretgalleg7c121132020-07-08 07:53:22 +00002201 vnfd: dict, k8s_instance_info: dict, k8params: dict = None, timeout: int = 600):
2202
tiernob9018152020-04-16 14:18:24 +00002203 try:
lloretgalleg7c121132020-07-08 07:53:22 +00002204 k8sclustertype = k8s_instance_info["k8scluster-type"]
2205 # Instantiate kdu
2206 db_dict_install = {"collection": "nsrs",
2207 "filter": {"_id": nsr_id},
2208 "path": nsr_db_path}
2209
romeromonser226a85f2021-05-28 11:53:17 +02002210 if k8s_instance_info.get("kdu-deployment-name"):
2211 kdu_instance = k8s_instance_info.get("kdu-deployment-name")
2212 else:
2213 kdu_instance = self.k8scluster_map[k8sclustertype].generate_kdu_instance_name(
2214 db_dict=db_dict_install,
2215 kdu_model=k8s_instance_info["kdu-model"],
2216 kdu_name=k8s_instance_info["kdu-name"],
2217 )
David Garciaffbf6ed2021-02-25 20:19:18 +01002218 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance})
2219 await self.k8scluster_map[k8sclustertype].install(
lloretgalleg7c121132020-07-08 07:53:22 +00002220 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2221 kdu_model=k8s_instance_info["kdu-model"],
2222 atomic=True,
2223 params=k8params,
2224 db_dict=db_dict_install,
2225 timeout=timeout,
2226 kdu_name=k8s_instance_info["kdu-name"],
David Garciaffbf6ed2021-02-25 20:19:18 +01002227 namespace=k8s_instance_info["namespace"],
2228 kdu_instance=kdu_instance,
2229 )
lloretgalleg7c121132020-07-08 07:53:22 +00002230 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance})
2231
2232 # Obtain services to obtain management service ip
2233 services = await self.k8scluster_map[k8sclustertype].get_services(
2234 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2235 kdu_instance=kdu_instance,
2236 namespace=k8s_instance_info["namespace"])
2237
2238 # Obtain management service info (if exists)
tierno7ecbc342020-09-21 14:05:39 +00002239 vnfr_update_dict = {}
bravof2f8b2512021-02-25 17:20:35 -03002240 kdu_config = get_configuration(vnfd, kdud["name"])
2241 if kdu_config:
2242 target_ee_list = kdu_config.get("execution-environment-list", [])
2243 else:
2244 target_ee_list = []
2245
lloretgalleg7c121132020-07-08 07:53:22 +00002246 if services:
tierno7ecbc342020-09-21 14:05:39 +00002247 vnfr_update_dict["kdur.{}.services".format(kdu_index)] = services
lloretgalleg7c121132020-07-08 07:53:22 +00002248 mgmt_services = [service for service in kdud.get("service", []) if service.get("mgmt-service")]
2249 for mgmt_service in mgmt_services:
2250 for service in services:
2251 if service["name"].startswith(mgmt_service["name"]):
2252 # Mgmt service found, Obtain service ip
2253 ip = service.get("external_ip", service.get("cluster_ip"))
2254 if isinstance(ip, list) and len(ip) == 1:
2255 ip = ip[0]
2256
2257 vnfr_update_dict["kdur.{}.ip-address".format(kdu_index)] = ip
2258
2259 # Check if must update also mgmt ip at the vnf
2260 service_external_cp = mgmt_service.get("external-connection-point-ref")
2261 if service_external_cp:
2262 if deep_get(vnfd, ("mgmt-interface", "cp")) == service_external_cp:
2263 vnfr_update_dict["ip-address"] = ip
2264
bravof2f8b2512021-02-25 17:20:35 -03002265 if find_in_list(
2266 target_ee_list,
2267 lambda ee: ee.get("external-connection-point-ref", "") == service_external_cp
2268 ):
2269 vnfr_update_dict["kdur.{}.ip-address".format(kdu_index)] = ip
lloretgalleg7c121132020-07-08 07:53:22 +00002270 break
2271 else:
2272 self.logger.warn("Mgmt service name: {} not found".format(mgmt_service["name"]))
2273
tierno7ecbc342020-09-21 14:05:39 +00002274 vnfr_update_dict["kdur.{}.status".format(kdu_index)] = "READY"
2275 self.update_db_2("vnfrs", vnfr_data.get("_id"), vnfr_update_dict)
lloretgalleg7c121132020-07-08 07:53:22 +00002276
garciaale762b6e72021-02-22 18:02:07 -03002277 kdu_config = get_configuration(vnfd, k8s_instance_info["kdu-name"])
2278 if kdu_config and kdu_config.get("initial-config-primitive") and \
2279 get_juju_ee_ref(vnfd, k8s_instance_info["kdu-name"]) is None:
Dominik Fleischmannc1975dd2020-08-19 12:17:51 +02002280 initial_config_primitive_list = kdu_config.get("initial-config-primitive")
2281 initial_config_primitive_list.sort(key=lambda val: int(val["seq"]))
2282
2283 for initial_config_primitive in initial_config_primitive_list:
2284 primitive_params_ = self._map_primitive_params(initial_config_primitive, {}, {})
2285
2286 await asyncio.wait_for(
2287 self.k8scluster_map[k8sclustertype].exec_primitive(
2288 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2289 kdu_instance=kdu_instance,
2290 primitive_name=initial_config_primitive["name"],
2291 params=primitive_params_, db_dict={}),
2292 timeout=timeout)
2293
tiernob9018152020-04-16 14:18:24 +00002294 except Exception as e:
lloretgalleg7c121132020-07-08 07:53:22 +00002295 # Prepare update db with error and raise exception
tiernob9018152020-04-16 14:18:24 +00002296 try:
lloretgalleg7c121132020-07-08 07:53:22 +00002297 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".detailed-status": str(e)})
tierno7ecbc342020-09-21 14:05:39 +00002298 self.update_db_2("vnfrs", vnfr_data.get("_id"), {"kdur.{}.status".format(kdu_index): "ERROR"})
tiernob9018152020-04-16 14:18:24 +00002299 except Exception:
lloretgalleg7c121132020-07-08 07:53:22 +00002300 # ignore to keep original exception
tiernob9018152020-04-16 14:18:24 +00002301 pass
lloretgalleg7c121132020-07-08 07:53:22 +00002302 # reraise original error
2303 raise
2304
2305 return kdu_instance
tiernob9018152020-04-16 14:18:24 +00002306
tiernoe876f672020-02-13 14:34:48 +00002307 async def deploy_kdus(self, logging_text, nsr_id, nslcmop_id, db_vnfrs, db_vnfds, task_instantiation_info):
calvinosanch9f9c6f22019-11-04 13:37:39 +01002308 # Launch kdus if present in the descriptor
tierno626e0152019-11-29 14:16:16 +00002309
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002310 k8scluster_id_2_uuic = {"helm-chart-v3": {}, "helm-chart": {}, "juju-bundle": {}}
tierno626e0152019-11-29 14:16:16 +00002311
tierno16f4a4e2020-07-20 09:05:51 +00002312 async def _get_cluster_id(cluster_id, cluster_type):
tierno626e0152019-11-29 14:16:16 +00002313 nonlocal k8scluster_id_2_uuic
2314 if cluster_id in k8scluster_id_2_uuic[cluster_type]:
2315 return k8scluster_id_2_uuic[cluster_type][cluster_id]
2316
tierno16f4a4e2020-07-20 09:05:51 +00002317 # check if K8scluster is creating and wait look if previous tasks in process
2318 task_name, task_dependency = self.lcm_tasks.lookfor_related("k8scluster", cluster_id)
2319 if task_dependency:
2320 text = "Waiting for related tasks '{}' on k8scluster {} to be completed".format(task_name, cluster_id)
2321 self.logger.debug(logging_text + text)
2322 await asyncio.wait(task_dependency, timeout=3600)
2323
tierno626e0152019-11-29 14:16:16 +00002324 db_k8scluster = self.db.get_one("k8sclusters", {"_id": cluster_id}, fail_on_empty=False)
2325 if not db_k8scluster:
2326 raise LcmException("K8s cluster {} cannot be found".format(cluster_id))
tierno16f4a4e2020-07-20 09:05:51 +00002327
tierno626e0152019-11-29 14:16:16 +00002328 k8s_id = deep_get(db_k8scluster, ("_admin", cluster_type, "id"))
2329 if not k8s_id:
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002330 if cluster_type == "helm-chart-v3":
2331 try:
2332 # backward compatibility for existing clusters that have not been initialized for helm v3
2333 k8s_credentials = yaml.safe_dump(db_k8scluster.get("credentials"))
2334 k8s_id, uninstall_sw = await self.k8sclusterhelm3.init_env(k8s_credentials,
2335 reuse_cluster_uuid=cluster_id)
2336 db_k8scluster_update = {}
2337 db_k8scluster_update["_admin.helm-chart-v3.error_msg"] = None
2338 db_k8scluster_update["_admin.helm-chart-v3.id"] = k8s_id
2339 db_k8scluster_update["_admin.helm-chart-v3.created"] = uninstall_sw
2340 db_k8scluster_update["_admin.helm-chart-v3.operationalState"] = "ENABLED"
2341 self.update_db_2("k8sclusters", cluster_id, db_k8scluster_update)
2342 except Exception as e:
2343 self.logger.error(logging_text + "error initializing helm-v3 cluster: {}".format(str(e)))
2344 raise LcmException("K8s cluster '{}' has not been initialized for '{}'".format(cluster_id,
2345 cluster_type))
2346 else:
2347 raise LcmException("K8s cluster '{}' has not been initialized for '{}'".
2348 format(cluster_id, cluster_type))
tierno626e0152019-11-29 14:16:16 +00002349 k8scluster_id_2_uuic[cluster_type][cluster_id] = k8s_id
2350 return k8s_id
2351
2352 logging_text += "Deploy kdus: "
tiernoe876f672020-02-13 14:34:48 +00002353 step = ""
calvinosanch9f9c6f22019-11-04 13:37:39 +01002354 try:
tierno626e0152019-11-29 14:16:16 +00002355 db_nsr_update = {"_admin.deployed.K8s": []}
calvinosanch9f9c6f22019-11-04 13:37:39 +01002356 self.update_db_2("nsrs", nsr_id, db_nsr_update)
calvinosanch9f9c6f22019-11-04 13:37:39 +01002357
tierno626e0152019-11-29 14:16:16 +00002358 index = 0
tiernoe876f672020-02-13 14:34:48 +00002359 updated_cluster_list = []
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002360 updated_v3_cluster_list = []
tiernoe876f672020-02-13 14:34:48 +00002361
tierno626e0152019-11-29 14:16:16 +00002362 for vnfr_data in db_vnfrs.values():
lloretgalleg7c121132020-07-08 07:53:22 +00002363 for kdu_index, kdur in enumerate(get_iterable(vnfr_data, "kdur")):
2364 # Step 0: Prepare and set parameters
bravof922c4172020-11-24 21:21:43 -03002365 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
quilesjacde94f2020-01-23 10:07:08 +00002366 vnfd_id = vnfr_data.get('vnfd-id')
David Garciad41dbd62020-12-10 12:52:52 +01002367 vnfd_with_id = find_in_list(db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id)
2368 kdud = next(kdud for kdud in vnfd_with_id["kdu"] if kdud["name"] == kdur["kdu-name"])
tiernode1584f2020-04-07 09:07:33 +00002369 namespace = kdur.get("k8s-namespace")
romeromonser226a85f2021-05-28 11:53:17 +02002370 kdu_deployment_name = kdur.get("kdu-deployment-name")
tierno626e0152019-11-29 14:16:16 +00002371 if kdur.get("helm-chart"):
lloretgalleg07e53f52020-12-15 10:54:02 +00002372 kdumodel = kdur["helm-chart"]
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002373 # Default version: helm3, if helm-version is v2 assign v2
2374 k8sclustertype = "helm-chart-v3"
2375 self.logger.debug("kdur: {}".format(kdur))
2376 if kdur.get("helm-version") and kdur.get("helm-version") == "v2":
2377 k8sclustertype = "helm-chart"
tierno626e0152019-11-29 14:16:16 +00002378 elif kdur.get("juju-bundle"):
lloretgalleg07e53f52020-12-15 10:54:02 +00002379 kdumodel = kdur["juju-bundle"]
tiernoe876f672020-02-13 14:34:48 +00002380 k8sclustertype = "juju-bundle"
tierno626e0152019-11-29 14:16:16 +00002381 else:
tiernoe876f672020-02-13 14:34:48 +00002382 raise LcmException("kdu type for kdu='{}.{}' is neither helm-chart nor "
2383 "juju-bundle. Maybe an old NBI version is running".
2384 format(vnfr_data["member-vnf-index-ref"], kdur["kdu-name"]))
quilesjacde94f2020-01-23 10:07:08 +00002385 # check if kdumodel is a file and exists
2386 try:
David Garciad41dbd62020-12-10 12:52:52 +01002387 vnfd_with_id = find_in_list(db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id)
2388 storage = deep_get(vnfd_with_id, ('_admin', 'storage'))
tierno51183952020-04-03 15:48:18 +00002389 if storage and storage.get('pkg-dir'): # may be not present if vnfd has not artifacts
2390 # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
Dominik Fleischmann010c0e72020-05-18 15:19:11 +02002391 filename = '{}/{}/{}s/{}'.format(storage["folder"], storage["pkg-dir"], k8sclustertype,
tierno51183952020-04-03 15:48:18 +00002392 kdumodel)
2393 if self.fs.file_exists(filename, mode='file') or self.fs.file_exists(filename, mode='dir'):
2394 kdumodel = self.fs.path + filename
2395 except (asyncio.TimeoutError, asyncio.CancelledError):
tiernoe876f672020-02-13 14:34:48 +00002396 raise
2397 except Exception: # it is not a file
quilesjacde94f2020-01-23 10:07:08 +00002398 pass
lloretgallegedc5f332020-02-20 11:50:50 +01002399
tiernoe876f672020-02-13 14:34:48 +00002400 k8s_cluster_id = kdur["k8s-cluster"]["id"]
2401 step = "Synchronize repos for k8s cluster '{}'".format(k8s_cluster_id)
tierno16f4a4e2020-07-20 09:05:51 +00002402 cluster_uuid = await _get_cluster_id(k8s_cluster_id, k8sclustertype)
lloretgallegedc5f332020-02-20 11:50:50 +01002403
lloretgalleg7c121132020-07-08 07:53:22 +00002404 # Synchronize repos
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002405 if (k8sclustertype == "helm-chart" and cluster_uuid not in updated_cluster_list)\
2406 or (k8sclustertype == "helm-chart-v3" and cluster_uuid not in updated_v3_cluster_list):
tiernoe876f672020-02-13 14:34:48 +00002407 del_repo_list, added_repo_dict = await asyncio.ensure_future(
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002408 self.k8scluster_map[k8sclustertype].synchronize_repos(cluster_uuid=cluster_uuid))
tiernoe876f672020-02-13 14:34:48 +00002409 if del_repo_list or added_repo_dict:
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002410 if k8sclustertype == "helm-chart":
2411 unset = {'_admin.helm_charts_added.' + item: None for item in del_repo_list}
2412 updated = {'_admin.helm_charts_added.' +
2413 item: name for item, name in added_repo_dict.items()}
2414 updated_cluster_list.append(cluster_uuid)
2415 elif k8sclustertype == "helm-chart-v3":
2416 unset = {'_admin.helm_charts_v3_added.' + item: None for item in del_repo_list}
2417 updated = {'_admin.helm_charts_v3_added.' +
2418 item: name for item, name in added_repo_dict.items()}
2419 updated_v3_cluster_list.append(cluster_uuid)
2420 self.logger.debug(logging_text + "repos synchronized on k8s cluster "
2421 "'{}' to_delete: {}, to_add: {}".
2422 format(k8s_cluster_id, del_repo_list, added_repo_dict))
tiernoe876f672020-02-13 14:34:48 +00002423 self.db.set_one("k8sclusters", {"_id": k8s_cluster_id}, updated, unset=unset)
lloretgallegedc5f332020-02-20 11:50:50 +01002424
lloretgalleg7c121132020-07-08 07:53:22 +00002425 # Instantiate kdu
tiernoe876f672020-02-13 14:34:48 +00002426 step = "Instantiating KDU {}.{} in k8s cluster {}".format(vnfr_data["member-vnf-index-ref"],
2427 kdur["kdu-name"], k8s_cluster_id)
lloretgalleg7c121132020-07-08 07:53:22 +00002428 k8s_instance_info = {"kdu-instance": None,
2429 "k8scluster-uuid": cluster_uuid,
2430 "k8scluster-type": k8sclustertype,
2431 "member-vnf-index": vnfr_data["member-vnf-index-ref"],
2432 "kdu-name": kdur["kdu-name"],
2433 "kdu-model": kdumodel,
romeromonser226a85f2021-05-28 11:53:17 +02002434 "namespace": namespace,
2435 "kdu-deployment-name": kdu_deployment_name}
tiernob9018152020-04-16 14:18:24 +00002436 db_path = "_admin.deployed.K8s.{}".format(index)
lloretgalleg7c121132020-07-08 07:53:22 +00002437 db_nsr_update[db_path] = k8s_instance_info
tierno626e0152019-11-29 14:16:16 +00002438 self.update_db_2("nsrs", nsr_id, db_nsr_update)
David Garciad41dbd62020-12-10 12:52:52 +01002439 vnfd_with_id = find_in_list(db_vnfds, lambda vnf: vnf["_id"] == vnfd_id)
tiernoa2143262020-03-27 16:20:40 +00002440 task = asyncio.ensure_future(
David Garciad41dbd62020-12-10 12:52:52 +01002441 self._install_kdu(nsr_id, db_path, vnfr_data, kdu_index, kdud, vnfd_with_id,
lloretgalleg7c121132020-07-08 07:53:22 +00002442 k8s_instance_info, k8params=desc_params, timeout=600))
tiernoe876f672020-02-13 14:34:48 +00002443 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_KDU-{}".format(index), task)
tiernoa2143262020-03-27 16:20:40 +00002444 task_instantiation_info[task] = "Deploying KDU {}".format(kdur["kdu-name"])
tiernoe876f672020-02-13 14:34:48 +00002445
tierno626e0152019-11-29 14:16:16 +00002446 index += 1
quilesjdd799ac2020-01-23 16:31:11 +00002447
tiernoe876f672020-02-13 14:34:48 +00002448 except (LcmException, asyncio.CancelledError):
2449 raise
calvinosanch9f9c6f22019-11-04 13:37:39 +01002450 except Exception as e:
tiernoe876f672020-02-13 14:34:48 +00002451 msg = "Exception {} while {}: {}".format(type(e).__name__, step, e)
2452 if isinstance(e, (N2VCException, DbException)):
2453 self.logger.error(logging_text + msg)
2454 else:
2455 self.logger.critical(logging_text + msg, exc_info=True)
quilesjdd799ac2020-01-23 16:31:11 +00002456 raise LcmException(msg)
calvinosanch9f9c6f22019-11-04 13:37:39 +01002457 finally:
calvinosanch9f9c6f22019-11-04 13:37:39 +01002458 if db_nsr_update:
2459 self.update_db_2("nsrs", nsr_id, db_nsr_update)
tiernoda6fb102019-11-23 00:36:52 +00002460
quilesj7e13aeb2019-10-08 13:34:55 +02002461 def _deploy_n2vc(self, logging_text, db_nsr, db_vnfr, nslcmop_id, nsr_id, nsi_id, vnfd_id, vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01002462 kdu_name, member_vnf_index, vdu_index, vdu_name, deploy_params, descriptor_config,
tiernoe876f672020-02-13 14:34:48 +00002463 base_folder, task_instantiation_info, stage):
quilesj7e13aeb2019-10-08 13:34:55 +02002464 # launch instantiate_N2VC in a asyncio task and register task object
2465 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
2466 # if not found, create one entry and update database
quilesj7e13aeb2019-10-08 13:34:55 +02002467 # fill db_nsr._admin.deployed.VCA.<index>
tierno588547c2020-07-01 15:30:20 +00002468
2469 self.logger.debug(logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id))
garciaale762b6e72021-02-22 18:02:07 -03002470 if "execution-environment-list" in descriptor_config:
2471 ee_list = descriptor_config.get("execution-environment-list", [])
David Garcia25da7f12021-05-28 12:08:18 +02002472 elif "juju" in descriptor_config:
2473 ee_list = [descriptor_config] # ns charms
tierno588547c2020-07-01 15:30:20 +00002474 else: # other types as script are not supported
2475 ee_list = []
2476
2477 for ee_item in ee_list:
2478 self.logger.debug(logging_text + "_deploy_n2vc ee_item juju={}, helm={}".format(ee_item.get('juju'),
2479 ee_item.get("helm-chart")))
tiernoa278b842020-07-08 15:33:55 +00002480 ee_descriptor_id = ee_item.get("id")
tierno588547c2020-07-01 15:30:20 +00002481 if ee_item.get("juju"):
2482 vca_name = ee_item['juju'].get('charm')
2483 vca_type = "lxc_proxy_charm" if ee_item['juju'].get('charm') is not None else "native_charm"
2484 if ee_item['juju'].get('cloud') == "k8s":
2485 vca_type = "k8s_proxy_charm"
2486 elif ee_item['juju'].get('proxy') is False:
2487 vca_type = "native_charm"
2488 elif ee_item.get("helm-chart"):
2489 vca_name = ee_item['helm-chart']
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002490 if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
2491 vca_type = "helm"
2492 else:
2493 vca_type = "helm-v3"
tierno588547c2020-07-01 15:30:20 +00002494 else:
2495 self.logger.debug(logging_text + "skipping non juju neither charm configuration")
quilesj7e13aeb2019-10-08 13:34:55 +02002496 continue
quilesj3655ae02019-12-12 16:08:35 +00002497
tierno588547c2020-07-01 15:30:20 +00002498 vca_index = -1
2499 for vca_index, vca_deployed in enumerate(db_nsr["_admin"]["deployed"]["VCA"]):
2500 if not vca_deployed:
2501 continue
2502 if vca_deployed.get("member-vnf-index") == member_vnf_index and \
2503 vca_deployed.get("vdu_id") == vdu_id and \
2504 vca_deployed.get("kdu_name") == kdu_name and \
tiernoa278b842020-07-08 15:33:55 +00002505 vca_deployed.get("vdu_count_index", 0) == vdu_index and \
2506 vca_deployed.get("ee_descriptor_id") == ee_descriptor_id:
tierno588547c2020-07-01 15:30:20 +00002507 break
2508 else:
2509 # not found, create one.
tiernoa278b842020-07-08 15:33:55 +00002510 target = "ns" if not member_vnf_index else "vnf/{}".format(member_vnf_index)
2511 if vdu_id:
2512 target += "/vdu/{}/{}".format(vdu_id, vdu_index or 0)
2513 elif kdu_name:
2514 target += "/kdu/{}".format(kdu_name)
tierno588547c2020-07-01 15:30:20 +00002515 vca_deployed = {
tiernoa278b842020-07-08 15:33:55 +00002516 "target_element": target,
2517 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
tierno588547c2020-07-01 15:30:20 +00002518 "member-vnf-index": member_vnf_index,
2519 "vdu_id": vdu_id,
2520 "kdu_name": kdu_name,
2521 "vdu_count_index": vdu_index,
2522 "operational-status": "init", # TODO revise
2523 "detailed-status": "", # TODO revise
2524 "step": "initial-deploy", # TODO revise
2525 "vnfd_id": vnfd_id,
2526 "vdu_name": vdu_name,
tiernoa278b842020-07-08 15:33:55 +00002527 "type": vca_type,
2528 "ee_descriptor_id": ee_descriptor_id
tierno588547c2020-07-01 15:30:20 +00002529 }
2530 vca_index += 1
quilesj3655ae02019-12-12 16:08:35 +00002531
tierno588547c2020-07-01 15:30:20 +00002532 # create VCA and configurationStatus in db
2533 db_dict = {
2534 "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
2535 "configurationStatus.{}".format(vca_index): dict()
2536 }
2537 self.update_db_2("nsrs", nsr_id, db_dict)
quilesj7e13aeb2019-10-08 13:34:55 +02002538
tierno588547c2020-07-01 15:30:20 +00002539 db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
2540
bravof922c4172020-11-24 21:21:43 -03002541 self.logger.debug("N2VC > NSR_ID > {}".format(nsr_id))
2542 self.logger.debug("N2VC > DB_NSR > {}".format(db_nsr))
2543 self.logger.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed))
2544
tierno588547c2020-07-01 15:30:20 +00002545 # Launch task
2546 task_n2vc = asyncio.ensure_future(
2547 self.instantiate_N2VC(
2548 logging_text=logging_text,
2549 vca_index=vca_index,
2550 nsi_id=nsi_id,
2551 db_nsr=db_nsr,
2552 db_vnfr=db_vnfr,
2553 vdu_id=vdu_id,
2554 kdu_name=kdu_name,
2555 vdu_index=vdu_index,
2556 deploy_params=deploy_params,
2557 config_descriptor=descriptor_config,
2558 base_folder=base_folder,
2559 nslcmop_id=nslcmop_id,
2560 stage=stage,
2561 vca_type=vca_type,
tiernob996d942020-07-03 14:52:28 +00002562 vca_name=vca_name,
2563 ee_config_descriptor=ee_item
tierno588547c2020-07-01 15:30:20 +00002564 )
quilesj7e13aeb2019-10-08 13:34:55 +02002565 )
tierno588547c2020-07-01 15:30:20 +00002566 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_N2VC-{}".format(vca_index), task_n2vc)
2567 task_instantiation_info[task_n2vc] = self.task_name_deploy_vca + " {}.{}".format(
2568 member_vnf_index or "", vdu_id or "")
tiernobaa51102018-12-14 13:16:18 +00002569
tiernoc9556972019-07-05 15:25:25 +00002570 @staticmethod
kuuse0ca67472019-05-13 15:59:27 +02002571 def _create_nslcmop(nsr_id, operation, params):
2572 """
2573 Creates a ns-lcm-opp content to be stored at database.
2574 :param nsr_id: internal id of the instance
2575 :param operation: instantiate, terminate, scale, action, ...
2576 :param params: user parameters for the operation
2577 :return: dictionary following SOL005 format
2578 """
2579 # Raise exception if invalid arguments
2580 if not (nsr_id and operation and params):
2581 raise LcmException(
2582 "Parameters 'nsr_id', 'operation' and 'params' needed to create primitive not provided")
2583 now = time()
2584 _id = str(uuid4())
2585 nslcmop = {
2586 "id": _id,
2587 "_id": _id,
2588 # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
2589 "operationState": "PROCESSING",
2590 "statusEnteredTime": now,
2591 "nsInstanceId": nsr_id,
2592 "lcmOperationType": operation,
2593 "startTime": now,
2594 "isAutomaticInvocation": False,
2595 "operationParams": params,
2596 "isCancelPending": False,
2597 "links": {
2598 "self": "/osm/nslcm/v1/ns_lcm_op_occs/" + _id,
2599 "nsInstance": "/osm/nslcm/v1/ns_instances/" + nsr_id,
2600 }
2601 }
2602 return nslcmop
2603
calvinosanch9f9c6f22019-11-04 13:37:39 +01002604 def _format_additional_params(self, params):
tierno626e0152019-11-29 14:16:16 +00002605 params = params or {}
calvinosanch9f9c6f22019-11-04 13:37:39 +01002606 for key, value in params.items():
2607 if str(value).startswith("!!yaml "):
2608 params[key] = yaml.safe_load(value[7:])
calvinosanch9f9c6f22019-11-04 13:37:39 +01002609 return params
2610
kuuse8b998e42019-07-30 15:22:16 +02002611 def _get_terminate_primitive_params(self, seq, vnf_index):
2612 primitive = seq.get('name')
2613 primitive_params = {}
2614 params = {
2615 "member_vnf_index": vnf_index,
2616 "primitive": primitive,
2617 "primitive_params": primitive_params,
2618 }
2619 desc_params = {}
2620 return self._map_primitive_params(seq, params, desc_params)
2621
kuuseac3a8882019-10-03 10:48:06 +02002622 # sub-operations
2623
tierno51183952020-04-03 15:48:18 +00002624 def _retry_or_skip_suboperation(self, db_nslcmop, op_index):
2625 op = deep_get(db_nslcmop, ('_admin', 'operations'), [])[op_index]
2626 if op.get('operationState') == 'COMPLETED':
kuuseac3a8882019-10-03 10:48:06 +02002627 # b. Skip sub-operation
2628 # _ns_execute_primitive() or RO.create_action() will NOT be executed
2629 return self.SUBOPERATION_STATUS_SKIP
2630 else:
tierno7c4e24c2020-05-13 08:41:35 +00002631 # c. retry executing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02002632 # The sub-operation exists, and operationState != 'COMPLETED'
tierno7c4e24c2020-05-13 08:41:35 +00002633 # Update operationState = 'PROCESSING' to indicate a retry.
kuuseac3a8882019-10-03 10:48:06 +02002634 operationState = 'PROCESSING'
2635 detailed_status = 'In progress'
2636 self._update_suboperation_status(
2637 db_nslcmop, op_index, operationState, detailed_status)
2638 # Return the sub-operation index
2639 # _ns_execute_primitive() or RO.create_action() will be called from scale()
2640 # with arguments extracted from the sub-operation
2641 return op_index
2642
2643 # Find a sub-operation where all keys in a matching dictionary must match
2644 # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
2645 def _find_suboperation(self, db_nslcmop, match):
tierno7c4e24c2020-05-13 08:41:35 +00002646 if db_nslcmop and match:
kuuseac3a8882019-10-03 10:48:06 +02002647 op_list = db_nslcmop.get('_admin', {}).get('operations', [])
2648 for i, op in enumerate(op_list):
2649 if all(op.get(k) == match[k] for k in match):
2650 return i
2651 return self.SUBOPERATION_STATUS_NOT_FOUND
2652
2653 # Update status for a sub-operation given its index
2654 def _update_suboperation_status(self, db_nslcmop, op_index, operationState, detailed_status):
2655 # Update DB for HA tasks
2656 q_filter = {'_id': db_nslcmop['_id']}
2657 update_dict = {'_admin.operations.{}.operationState'.format(op_index): operationState,
2658 '_admin.operations.{}.detailed-status'.format(op_index): detailed_status}
2659 self.db.set_one("nslcmops",
2660 q_filter=q_filter,
2661 update_dict=update_dict,
2662 fail_on_empty=False)
2663
2664 # Add sub-operation, return the index of the added sub-operation
2665 # Optionally, set operationState, detailed-status, and operationType
2666 # Status and type are currently set for 'scale' sub-operations:
2667 # 'operationState' : 'PROCESSING' | 'COMPLETED' | 'FAILED'
2668 # 'detailed-status' : status message
2669 # 'operationType': may be any type, in the case of scaling: 'PRE-SCALE' | 'POST-SCALE'
2670 # Status and operation type are currently only used for 'scale', but NOT for 'terminate' sub-operations.
tierno2357f4e2020-10-19 16:38:59 +00002671 def _add_suboperation(self, db_nslcmop, vnf_index, vdu_id, vdu_count_index, vdu_name, primitive,
quilesj7e13aeb2019-10-08 13:34:55 +02002672 mapped_primitive_params, operationState=None, detailed_status=None, operationType=None,
kuuseac3a8882019-10-03 10:48:06 +02002673 RO_nsr_id=None, RO_scaling_info=None):
tiernoe876f672020-02-13 14:34:48 +00002674 if not db_nslcmop:
kuuseac3a8882019-10-03 10:48:06 +02002675 return self.SUBOPERATION_STATUS_NOT_FOUND
2676 # Get the "_admin.operations" list, if it exists
2677 db_nslcmop_admin = db_nslcmop.get('_admin', {})
2678 op_list = db_nslcmop_admin.get('operations')
2679 # Create or append to the "_admin.operations" list
kuuse8b998e42019-07-30 15:22:16 +02002680 new_op = {'member_vnf_index': vnf_index,
2681 'vdu_id': vdu_id,
2682 'vdu_count_index': vdu_count_index,
2683 'primitive': primitive,
2684 'primitive_params': mapped_primitive_params}
kuuseac3a8882019-10-03 10:48:06 +02002685 if operationState:
2686 new_op['operationState'] = operationState
2687 if detailed_status:
2688 new_op['detailed-status'] = detailed_status
2689 if operationType:
2690 new_op['lcmOperationType'] = operationType
2691 if RO_nsr_id:
2692 new_op['RO_nsr_id'] = RO_nsr_id
2693 if RO_scaling_info:
2694 new_op['RO_scaling_info'] = RO_scaling_info
2695 if not op_list:
2696 # No existing operations, create key 'operations' with current operation as first list element
2697 db_nslcmop_admin.update({'operations': [new_op]})
2698 op_list = db_nslcmop_admin.get('operations')
2699 else:
2700 # Existing operations, append operation to list
2701 op_list.append(new_op)
kuuse8b998e42019-07-30 15:22:16 +02002702
kuuseac3a8882019-10-03 10:48:06 +02002703 db_nslcmop_update = {'_admin.operations': op_list}
2704 self.update_db_2("nslcmops", db_nslcmop['_id'], db_nslcmop_update)
2705 op_index = len(op_list) - 1
2706 return op_index
2707
2708 # Helper methods for scale() sub-operations
2709
2710 # pre-scale/post-scale:
2711 # Check for 3 different cases:
2712 # a. New: First time execution, return SUBOPERATION_STATUS_NEW
2713 # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
tierno7c4e24c2020-05-13 08:41:35 +00002714 # c. retry: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
quilesj7e13aeb2019-10-08 13:34:55 +02002715 def _check_or_add_scale_suboperation(self, db_nslcmop, vnf_index, vnf_config_primitive, primitive_params,
2716 operationType, RO_nsr_id=None, RO_scaling_info=None):
kuuseac3a8882019-10-03 10:48:06 +02002717 # Find this sub-operation
tierno7c4e24c2020-05-13 08:41:35 +00002718 if RO_nsr_id and RO_scaling_info:
kuuseac3a8882019-10-03 10:48:06 +02002719 operationType = 'SCALE-RO'
2720 match = {
2721 'member_vnf_index': vnf_index,
2722 'RO_nsr_id': RO_nsr_id,
2723 'RO_scaling_info': RO_scaling_info,
2724 }
2725 else:
2726 match = {
2727 'member_vnf_index': vnf_index,
2728 'primitive': vnf_config_primitive,
2729 'primitive_params': primitive_params,
2730 'lcmOperationType': operationType
2731 }
2732 op_index = self._find_suboperation(db_nslcmop, match)
tierno51183952020-04-03 15:48:18 +00002733 if op_index == self.SUBOPERATION_STATUS_NOT_FOUND:
kuuseac3a8882019-10-03 10:48:06 +02002734 # a. New sub-operation
2735 # The sub-operation does not exist, add it.
2736 # _ns_execute_primitive() will be called from scale() as usual, with non-modified arguments
2737 # The following parameters are set to None for all kind of scaling:
2738 vdu_id = None
2739 vdu_count_index = None
2740 vdu_name = None
tierno51183952020-04-03 15:48:18 +00002741 if RO_nsr_id and RO_scaling_info:
kuuseac3a8882019-10-03 10:48:06 +02002742 vnf_config_primitive = None
2743 primitive_params = None
2744 else:
2745 RO_nsr_id = None
2746 RO_scaling_info = None
2747 # Initial status for sub-operation
2748 operationState = 'PROCESSING'
2749 detailed_status = 'In progress'
2750 # Add sub-operation for pre/post-scaling (zero or more operations)
2751 self._add_suboperation(db_nslcmop,
2752 vnf_index,
2753 vdu_id,
2754 vdu_count_index,
2755 vdu_name,
2756 vnf_config_primitive,
2757 primitive_params,
2758 operationState,
2759 detailed_status,
2760 operationType,
2761 RO_nsr_id,
2762 RO_scaling_info)
2763 return self.SUBOPERATION_STATUS_NEW
2764 else:
2765 # Return either SUBOPERATION_STATUS_SKIP (operationState == 'COMPLETED'),
2766 # or op_index (operationState != 'COMPLETED')
tierno51183952020-04-03 15:48:18 +00002767 return self._retry_or_skip_suboperation(db_nslcmop, op_index)
kuuseac3a8882019-10-03 10:48:06 +02002768
preethika.pdf7d8e02019-12-10 13:10:48 +00002769 # Function to return execution_environment id
2770
2771 def _get_ee_id(self, vnf_index, vdu_id, vca_deployed_list):
tiernoe876f672020-02-13 14:34:48 +00002772 # TODO vdu_index_count
preethika.pdf7d8e02019-12-10 13:10:48 +00002773 for vca in vca_deployed_list:
2774 if vca["member-vnf-index"] == vnf_index and vca["vdu_id"] == vdu_id:
2775 return vca["ee_id"]
2776
tierno588547c2020-07-01 15:30:20 +00002777 async def destroy_N2VC(self, logging_text, db_nslcmop, vca_deployed, config_descriptor,
aktasf0f83a32021-02-12 22:19:10 +03002778 vca_index, destroy_ee=True, exec_primitives=True, scaling_in=False):
tiernoe876f672020-02-13 14:34:48 +00002779 """
2780 Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
2781 :param logging_text:
2782 :param db_nslcmop:
2783 :param vca_deployed: Dictionary of deployment info at db_nsr._admin.depoloyed.VCA.<INDEX>
2784 :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
2785 :param vca_index: index in the database _admin.deployed.VCA
2786 :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
tierno588547c2020-07-01 15:30:20 +00002787 :param exec_primitives: False to do not execute terminate primitives, because the config is not completed or has
2788 not executed properly
aktasf0f83a32021-02-12 22:19:10 +03002789 :param scaling_in: True destroys the application, False destroys the model
tiernoe876f672020-02-13 14:34:48 +00002790 :return: None or exception
2791 """
tiernoe876f672020-02-13 14:34:48 +00002792
tierno588547c2020-07-01 15:30:20 +00002793 self.logger.debug(
2794 logging_text + " vca_index: {}, vca_deployed: {}, config_descriptor: {}, destroy_ee: {}".format(
2795 vca_index, vca_deployed, config_descriptor, destroy_ee
2796 )
2797 )
2798
2799 vca_type = vca_deployed.get("type", "lxc_proxy_charm")
2800
2801 # execute terminate_primitives
2802 if exec_primitives:
bravof922c4172020-11-24 21:21:43 -03002803 terminate_primitives = get_ee_sorted_terminate_config_primitive_list(
2804 config_descriptor.get("terminate-config-primitive"), vca_deployed.get("ee_descriptor_id"))
tierno588547c2020-07-01 15:30:20 +00002805 vdu_id = vca_deployed.get("vdu_id")
2806 vdu_count_index = vca_deployed.get("vdu_count_index")
2807 vdu_name = vca_deployed.get("vdu_name")
2808 vnf_index = vca_deployed.get("member-vnf-index")
2809 if terminate_primitives and vca_deployed.get("needed_terminate"):
tierno588547c2020-07-01 15:30:20 +00002810 for seq in terminate_primitives:
2811 # For each sequence in list, get primitive and call _ns_execute_primitive()
2812 step = "Calling terminate action for vnf_member_index={} primitive={}".format(
2813 vnf_index, seq.get("name"))
2814 self.logger.debug(logging_text + step)
2815 # Create the primitive for each sequence, i.e. "primitive": "touch"
2816 primitive = seq.get('name')
2817 mapped_primitive_params = self._get_terminate_primitive_params(seq, vnf_index)
tierno588547c2020-07-01 15:30:20 +00002818
2819 # Add sub-operation
2820 self._add_suboperation(db_nslcmop,
2821 vnf_index,
2822 vdu_id,
2823 vdu_count_index,
2824 vdu_name,
2825 primitive,
2826 mapped_primitive_params)
2827 # Sub-operations: Call _ns_execute_primitive() instead of action()
2828 try:
2829 result, result_detail = await self._ns_execute_primitive(vca_deployed["ee_id"], primitive,
2830 mapped_primitive_params,
2831 vca_type=vca_type)
2832 except LcmException:
2833 # this happens when VCA is not deployed. In this case it is not needed to terminate
2834 continue
2835 result_ok = ['COMPLETED', 'PARTIALLY_COMPLETED']
2836 if result not in result_ok:
2837 raise LcmException("terminate_primitive {} for vnf_member_index={} fails with "
2838 "error {}".format(seq.get("name"), vnf_index, result_detail))
2839 # set that this VCA do not need terminated
2840 db_update_entry = "_admin.deployed.VCA.{}.needed_terminate".format(vca_index)
2841 self.update_db_2("nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False})
tiernoe876f672020-02-13 14:34:48 +00002842
tiernob996d942020-07-03 14:52:28 +00002843 if vca_deployed.get("prometheus_jobs") and self.prometheus:
2844 await self.prometheus.update(remove_jobs=vca_deployed["prometheus_jobs"])
2845
tiernoe876f672020-02-13 14:34:48 +00002846 if destroy_ee:
aktasf0f83a32021-02-12 22:19:10 +03002847 await self.vca_map[vca_type].delete_execution_environment(vca_deployed["ee_id"], scaling_in=scaling_in)
kuuse0ca67472019-05-13 15:59:27 +02002848
tierno51183952020-04-03 15:48:18 +00002849 async def _delete_all_N2VC(self, db_nsr: dict):
2850 self._write_all_config_status(db_nsr=db_nsr, status='TERMINATING')
2851 namespace = "." + db_nsr["_id"]
tiernof59ad6c2020-04-08 12:50:52 +00002852 try:
2853 await self.n2vc.delete_namespace(namespace=namespace, total_timeout=self.timeout_charm_delete)
2854 except N2VCNotFound: # already deleted. Skip
2855 pass
tierno51183952020-04-03 15:48:18 +00002856 self._write_all_config_status(db_nsr=db_nsr, status='DELETED')
quilesj3655ae02019-12-12 16:08:35 +00002857
tiernoe876f672020-02-13 14:34:48 +00002858 async def _terminate_RO(self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage):
2859 """
2860 Terminates a deployment from RO
2861 :param logging_text:
2862 :param nsr_deployed: db_nsr._admin.deployed
2863 :param nsr_id:
2864 :param nslcmop_id:
2865 :param stage: list of string with the content to write on db_nslcmop.detailed-status.
2866 this method will update only the index 2, but it will write on database the concatenated content of the list
2867 :return:
2868 """
2869 db_nsr_update = {}
2870 failed_detail = []
2871 ro_nsr_id = ro_delete_action = None
2872 if nsr_deployed and nsr_deployed.get("RO"):
2873 ro_nsr_id = nsr_deployed["RO"].get("nsr_id")
2874 ro_delete_action = nsr_deployed["RO"].get("nsr_delete_action_id")
2875 try:
2876 if ro_nsr_id:
2877 stage[2] = "Deleting ns from VIM."
2878 db_nsr_update["detailed-status"] = " ".join(stage)
2879 self._write_op_status(nslcmop_id, stage)
2880 self.logger.debug(logging_text + stage[2])
2881 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2882 self._write_op_status(nslcmop_id, stage)
2883 desc = await self.RO.delete("ns", ro_nsr_id)
2884 ro_delete_action = desc["action_id"]
2885 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = ro_delete_action
2886 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
2887 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2888 if ro_delete_action:
2889 # wait until NS is deleted from VIM
2890 stage[2] = "Waiting ns deleted from VIM."
2891 detailed_status_old = None
2892 self.logger.debug(logging_text + stage[2] + " RO_id={} ro_delete_action={}".format(ro_nsr_id,
2893 ro_delete_action))
2894 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2895 self._write_op_status(nslcmop_id, stage)
kuused124bfe2019-06-18 12:09:24 +02002896
tiernoe876f672020-02-13 14:34:48 +00002897 delete_timeout = 20 * 60 # 20 minutes
2898 while delete_timeout > 0:
2899 desc = await self.RO.show(
2900 "ns",
2901 item_id_name=ro_nsr_id,
2902 extra_item="action",
2903 extra_item_id=ro_delete_action)
2904
2905 # deploymentStatus
2906 self._on_update_ro_db(nsrs_id=nsr_id, ro_descriptor=desc)
2907
2908 ns_status, ns_status_info = self.RO.check_action_status(desc)
2909 if ns_status == "ERROR":
2910 raise ROclient.ROClientException(ns_status_info)
2911 elif ns_status == "BUILD":
2912 stage[2] = "Deleting from VIM {}".format(ns_status_info)
2913 elif ns_status == "ACTIVE":
2914 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
2915 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2916 break
2917 else:
2918 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
2919 if stage[2] != detailed_status_old:
2920 detailed_status_old = stage[2]
2921 db_nsr_update["detailed-status"] = " ".join(stage)
2922 self._write_op_status(nslcmop_id, stage)
2923 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2924 await asyncio.sleep(5, loop=self.loop)
2925 delete_timeout -= 5
2926 else: # delete_timeout <= 0:
2927 raise ROclient.ROClientException("Timeout waiting ns deleted from VIM")
2928
2929 except Exception as e:
2930 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2931 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2932 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
2933 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2934 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
2935 self.logger.debug(logging_text + "RO_ns_id={} already deleted".format(ro_nsr_id))
2936 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
tiernoa2143262020-03-27 16:20:40 +00002937 failed_detail.append("delete conflict: {}".format(e))
2938 self.logger.debug(logging_text + "RO_ns_id={} delete conflict: {}".format(ro_nsr_id, e))
tiernoe876f672020-02-13 14:34:48 +00002939 else:
tiernoa2143262020-03-27 16:20:40 +00002940 failed_detail.append("delete error: {}".format(e))
2941 self.logger.error(logging_text + "RO_ns_id={} delete error: {}".format(ro_nsr_id, e))
tiernoe876f672020-02-13 14:34:48 +00002942
2943 # Delete nsd
2944 if not failed_detail and deep_get(nsr_deployed, ("RO", "nsd_id")):
2945 ro_nsd_id = nsr_deployed["RO"]["nsd_id"]
2946 try:
2947 stage[2] = "Deleting nsd from RO."
2948 db_nsr_update["detailed-status"] = " ".join(stage)
2949 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2950 self._write_op_status(nslcmop_id, stage)
2951 await self.RO.delete("nsd", ro_nsd_id)
2952 self.logger.debug(logging_text + "ro_nsd_id={} deleted".format(ro_nsd_id))
2953 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
2954 except Exception as e:
2955 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2956 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
2957 self.logger.debug(logging_text + "ro_nsd_id={} already deleted".format(ro_nsd_id))
2958 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
2959 failed_detail.append("ro_nsd_id={} delete conflict: {}".format(ro_nsd_id, e))
2960 self.logger.debug(logging_text + failed_detail[-1])
2961 else:
2962 failed_detail.append("ro_nsd_id={} delete error: {}".format(ro_nsd_id, e))
2963 self.logger.error(logging_text + failed_detail[-1])
2964
2965 if not failed_detail and deep_get(nsr_deployed, ("RO", "vnfd")):
2966 for index, vnf_deployed in enumerate(nsr_deployed["RO"]["vnfd"]):
2967 if not vnf_deployed or not vnf_deployed["id"]:
2968 continue
2969 try:
2970 ro_vnfd_id = vnf_deployed["id"]
2971 stage[2] = "Deleting member_vnf_index={} ro_vnfd_id={} from RO.".format(
2972 vnf_deployed["member-vnf-index"], ro_vnfd_id)
2973 db_nsr_update["detailed-status"] = " ".join(stage)
2974 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2975 self._write_op_status(nslcmop_id, stage)
2976 await self.RO.delete("vnfd", ro_vnfd_id)
2977 self.logger.debug(logging_text + "ro_vnfd_id={} deleted".format(ro_vnfd_id))
2978 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
2979 except Exception as e:
2980 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2981 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
2982 self.logger.debug(logging_text + "ro_vnfd_id={} already deleted ".format(ro_vnfd_id))
2983 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
2984 failed_detail.append("ro_vnfd_id={} delete conflict: {}".format(ro_vnfd_id, e))
2985 self.logger.debug(logging_text + failed_detail[-1])
2986 else:
2987 failed_detail.append("ro_vnfd_id={} delete error: {}".format(ro_vnfd_id, e))
2988 self.logger.error(logging_text + failed_detail[-1])
2989
tiernoa2143262020-03-27 16:20:40 +00002990 if failed_detail:
2991 stage[2] = "Error deleting from VIM"
2992 else:
2993 stage[2] = "Deleted from VIM"
tiernoe876f672020-02-13 14:34:48 +00002994 db_nsr_update["detailed-status"] = " ".join(stage)
2995 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2996 self._write_op_status(nslcmop_id, stage)
2997
2998 if failed_detail:
tiernoa2143262020-03-27 16:20:40 +00002999 raise LcmException("; ".join(failed_detail))
tiernoe876f672020-02-13 14:34:48 +00003000
3001 async def terminate(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003002 # Try to lock HA task here
3003 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3004 if not task_is_locked_by_me:
3005 return
3006
tierno59d22d22018-09-25 18:10:19 +02003007 logging_text = "Task ns={} terminate={} ".format(nsr_id, nslcmop_id)
3008 self.logger.debug(logging_text + "Enter")
tiernoe876f672020-02-13 14:34:48 +00003009 timeout_ns_terminate = self.timeout_ns_terminate
tierno59d22d22018-09-25 18:10:19 +02003010 db_nsr = None
3011 db_nslcmop = None
tiernoa17d4f42020-04-28 09:59:23 +00003012 operation_params = None
tierno59d22d22018-09-25 18:10:19 +02003013 exc = None
tiernoe876f672020-02-13 14:34:48 +00003014 error_list = [] # annotates all failed error messages
tierno59d22d22018-09-25 18:10:19 +02003015 db_nslcmop_update = {}
tiernoc2564fe2019-01-28 16:18:56 +00003016 autoremove = False # autoremove after terminated
tiernoe876f672020-02-13 14:34:48 +00003017 tasks_dict_info = {}
3018 db_nsr_update = {}
3019 stage = ["Stage 1/3: Preparing task.", "Waiting for previous operations to terminate.", ""]
3020 # ^ contains [stage, step, VIM-status]
tierno59d22d22018-09-25 18:10:19 +02003021 try:
kuused124bfe2019-06-18 12:09:24 +02003022 # wait for any previous tasks in process
3023 await self.lcm_tasks.waitfor_related_HA("ns", 'nslcmops', nslcmop_id)
3024
tiernoe876f672020-02-13 14:34:48 +00003025 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
3026 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
3027 operation_params = db_nslcmop.get("operationParams") or {}
3028 if operation_params.get("timeout_ns_terminate"):
3029 timeout_ns_terminate = operation_params["timeout_ns_terminate"]
3030 stage[1] = "Getting nsr={} from db.".format(nsr_id)
3031 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
3032
3033 db_nsr_update["operational-status"] = "terminating"
3034 db_nsr_update["config-status"] = "terminating"
quilesj4cda56b2019-12-05 10:02:20 +00003035 self._write_ns_status(
3036 nsr_id=nsr_id,
3037 ns_state="TERMINATING",
3038 current_operation="TERMINATING",
tiernoe876f672020-02-13 14:34:48 +00003039 current_operation_id=nslcmop_id,
3040 other_update=db_nsr_update
quilesj4cda56b2019-12-05 10:02:20 +00003041 )
quilesj3655ae02019-12-12 16:08:35 +00003042 self._write_op_status(
3043 op_id=nslcmop_id,
tiernoe876f672020-02-13 14:34:48 +00003044 queuePosition=0,
3045 stage=stage
quilesj3655ae02019-12-12 16:08:35 +00003046 )
tiernoe876f672020-02-13 14:34:48 +00003047 nsr_deployed = deepcopy(db_nsr["_admin"].get("deployed")) or {}
tierno59d22d22018-09-25 18:10:19 +02003048 if db_nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
3049 return
tierno59d22d22018-09-25 18:10:19 +02003050
tiernoe876f672020-02-13 14:34:48 +00003051 stage[1] = "Getting vnf descriptors from db."
3052 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
3053 db_vnfds_from_id = {}
3054 db_vnfds_from_member_index = {}
3055 # Loop over VNFRs
3056 for vnfr in db_vnfrs_list:
3057 vnfd_id = vnfr["vnfd-id"]
3058 if vnfd_id not in db_vnfds_from_id:
3059 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
3060 db_vnfds_from_id[vnfd_id] = vnfd
3061 db_vnfds_from_member_index[vnfr["member-vnf-index-ref"]] = db_vnfds_from_id[vnfd_id]
calvinosanch9f9c6f22019-11-04 13:37:39 +01003062
tiernoe876f672020-02-13 14:34:48 +00003063 # Destroy individual execution environments when there are terminating primitives.
3064 # Rest of EE will be deleted at once
tierno588547c2020-07-01 15:30:20 +00003065 # TODO - check before calling _destroy_N2VC
3066 # if not operation_params.get("skip_terminate_primitives"):#
3067 # or not vca.get("needed_terminate"):
3068 stage[0] = "Stage 2/3 execute terminating primitives."
3069 self.logger.debug(logging_text + stage[0])
3070 stage[1] = "Looking execution environment that needs terminate."
3071 self.logger.debug(logging_text + stage[1])
bravof922c4172020-11-24 21:21:43 -03003072
tierno588547c2020-07-01 15:30:20 +00003073 for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
tierno588547c2020-07-01 15:30:20 +00003074 config_descriptor = None
3075 if not vca or not vca.get("ee_id"):
3076 continue
3077 if not vca.get("member-vnf-index"):
3078 # ns
3079 config_descriptor = db_nsr.get("ns-configuration")
3080 elif vca.get("vdu_id"):
3081 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
garciaalea77ced72021-02-17 19:09:12 -03003082 config_descriptor = get_configuration(db_vnfd, vca.get("vdu_id"))
tierno588547c2020-07-01 15:30:20 +00003083 elif vca.get("kdu_name"):
3084 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
garciaalea77ced72021-02-17 19:09:12 -03003085 config_descriptor = get_configuration(db_vnfd, vca.get("kdu_name"))
tierno588547c2020-07-01 15:30:20 +00003086 else:
garciaalea77ced72021-02-17 19:09:12 -03003087 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
aktasf0f83a32021-02-12 22:19:10 +03003088 config_descriptor = get_configuration(db_vnfd, db_vnfd["id"])
tierno588547c2020-07-01 15:30:20 +00003089 vca_type = vca.get("type")
3090 exec_terminate_primitives = (not operation_params.get("skip_terminate_primitives") and
3091 vca.get("needed_terminate"))
tiernoaebd7da2020-08-07 06:36:38 +00003092 # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
3093 # pending native charms
lloretgalleg18ebc3a2020-10-22 09:54:51 +00003094 destroy_ee = True if vca_type in ("helm", "helm-v3", "native_charm") else False
tierno86e33612020-09-16 14:13:06 +00003095 # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
3096 # vca_index, vca.get("ee_id"), vca_type, destroy_ee))
tiernob996d942020-07-03 14:52:28 +00003097 task = asyncio.ensure_future(
3098 self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor, vca_index,
3099 destroy_ee, exec_terminate_primitives))
tierno588547c2020-07-01 15:30:20 +00003100 tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
tierno59d22d22018-09-25 18:10:19 +02003101
tierno588547c2020-07-01 15:30:20 +00003102 # wait for pending tasks of terminate primitives
3103 if tasks_dict_info:
tierno86e33612020-09-16 14:13:06 +00003104 self.logger.debug(logging_text + 'Waiting for tasks {}'.format(list(tasks_dict_info.keys())))
tierno588547c2020-07-01 15:30:20 +00003105 error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
3106 min(self.timeout_charm_delete, timeout_ns_terminate),
3107 stage, nslcmop_id)
tierno86e33612020-09-16 14:13:06 +00003108 tasks_dict_info.clear()
tierno588547c2020-07-01 15:30:20 +00003109 if error_list:
3110 return # raise LcmException("; ".join(error_list))
tierno82974b22018-11-27 21:55:36 +00003111
tiernoe876f672020-02-13 14:34:48 +00003112 # remove All execution environments at once
3113 stage[0] = "Stage 3/3 delete all."
quilesj3655ae02019-12-12 16:08:35 +00003114
tierno49676be2020-04-07 16:34:35 +00003115 if nsr_deployed.get("VCA"):
3116 stage[1] = "Deleting all execution environments."
3117 self.logger.debug(logging_text + stage[1])
3118 task_delete_ee = asyncio.ensure_future(asyncio.wait_for(self._delete_all_N2VC(db_nsr=db_nsr),
3119 timeout=self.timeout_charm_delete))
3120 # task_delete_ee = asyncio.ensure_future(self.n2vc.delete_namespace(namespace="." + nsr_id))
3121 tasks_dict_info[task_delete_ee] = "Terminating all VCA"
tierno59d22d22018-09-25 18:10:19 +02003122
tiernoe876f672020-02-13 14:34:48 +00003123 # Delete from k8scluster
3124 stage[1] = "Deleting KDUs."
3125 self.logger.debug(logging_text + stage[1])
3126 # print(nsr_deployed)
3127 for kdu in get_iterable(nsr_deployed, "K8s"):
3128 if not kdu or not kdu.get("kdu-instance"):
3129 continue
3130 kdu_instance = kdu.get("kdu-instance")
tiernoa2143262020-03-27 16:20:40 +00003131 if kdu.get("k8scluster-type") in self.k8scluster_map:
tiernoe876f672020-02-13 14:34:48 +00003132 task_delete_kdu_instance = asyncio.ensure_future(
tiernoa2143262020-03-27 16:20:40 +00003133 self.k8scluster_map[kdu["k8scluster-type"]].uninstall(
3134 cluster_uuid=kdu.get("k8scluster-uuid"),
3135 kdu_instance=kdu_instance))
tiernoe876f672020-02-13 14:34:48 +00003136 else:
3137 self.logger.error(logging_text + "Unknown k8s deployment type {}".
3138 format(kdu.get("k8scluster-type")))
3139 continue
3140 tasks_dict_info[task_delete_kdu_instance] = "Terminating KDU '{}'".format(kdu.get("kdu-name"))
tierno59d22d22018-09-25 18:10:19 +02003141
3142 # remove from RO
tiernoe876f672020-02-13 14:34:48 +00003143 stage[1] = "Deleting ns from VIM."
tierno69f0d382020-05-07 13:08:09 +00003144 if self.ng_ro:
3145 task_delete_ro = asyncio.ensure_future(
3146 self._terminate_ng_ro(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
3147 else:
3148 task_delete_ro = asyncio.ensure_future(
3149 self._terminate_RO(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
tiernoe876f672020-02-13 14:34:48 +00003150 tasks_dict_info[task_delete_ro] = "Removing deployment from VIM"
tierno59d22d22018-09-25 18:10:19 +02003151
tiernoe876f672020-02-13 14:34:48 +00003152 # rest of staff will be done at finally
3153
3154 except (ROclient.ROClientException, DbException, LcmException, N2VCException) as e:
3155 self.logger.error(logging_text + "Exit Exception {}".format(e))
3156 exc = e
3157 except asyncio.CancelledError:
3158 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(stage[1]))
3159 exc = "Operation was cancelled"
3160 except Exception as e:
3161 exc = traceback.format_exc()
3162 self.logger.critical(logging_text + "Exit Exception while '{}': {}".format(stage[1], e), exc_info=True)
3163 finally:
3164 if exc:
3165 error_list.append(str(exc))
tierno59d22d22018-09-25 18:10:19 +02003166 try:
tiernoe876f672020-02-13 14:34:48 +00003167 # wait for pending tasks
3168 if tasks_dict_info:
3169 stage[1] = "Waiting for terminate pending tasks."
3170 self.logger.debug(logging_text + stage[1])
3171 error_list += await self._wait_for_tasks(logging_text, tasks_dict_info, timeout_ns_terminate,
3172 stage, nslcmop_id)
3173 stage[1] = stage[2] = ""
3174 except asyncio.CancelledError:
3175 error_list.append("Cancelled")
3176 # TODO cancell all tasks
3177 except Exception as exc:
3178 error_list.append(str(exc))
3179 # update status at database
3180 if error_list:
3181 error_detail = "; ".join(error_list)
3182 # self.logger.error(logging_text + error_detail)
tiernob5203912020-08-11 11:20:13 +00003183 error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
3184 error_description_nsr = 'Operation: TERMINATING.{}, {}.'.format(nslcmop_id, stage[0])
tierno59d22d22018-09-25 18:10:19 +02003185
tierno59d22d22018-09-25 18:10:19 +02003186 db_nsr_update["operational-status"] = "failed"
tiernoa2143262020-03-27 16:20:40 +00003187 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
tiernoe876f672020-02-13 14:34:48 +00003188 db_nslcmop_update["detailed-status"] = error_detail
3189 nslcmop_operation_state = "FAILED"
3190 ns_state = "BROKEN"
tierno59d22d22018-09-25 18:10:19 +02003191 else:
tiernoa2143262020-03-27 16:20:40 +00003192 error_detail = None
tiernoe876f672020-02-13 14:34:48 +00003193 error_description_nsr = error_description_nslcmop = None
3194 ns_state = "NOT_INSTANTIATED"
tierno59d22d22018-09-25 18:10:19 +02003195 db_nsr_update["operational-status"] = "terminated"
3196 db_nsr_update["detailed-status"] = "Done"
3197 db_nsr_update["_admin.nsState"] = "NOT_INSTANTIATED"
3198 db_nslcmop_update["detailed-status"] = "Done"
tiernoe876f672020-02-13 14:34:48 +00003199 nslcmop_operation_state = "COMPLETED"
tierno59d22d22018-09-25 18:10:19 +02003200
tiernoe876f672020-02-13 14:34:48 +00003201 if db_nsr:
3202 self._write_ns_status(
3203 nsr_id=nsr_id,
3204 ns_state=ns_state,
3205 current_operation="IDLE",
3206 current_operation_id=None,
3207 error_description=error_description_nsr,
tiernoa2143262020-03-27 16:20:40 +00003208 error_detail=error_detail,
tiernoe876f672020-02-13 14:34:48 +00003209 other_update=db_nsr_update
3210 )
tiernoa17d4f42020-04-28 09:59:23 +00003211 self._write_op_status(
3212 op_id=nslcmop_id,
3213 stage="",
3214 error_message=error_description_nslcmop,
3215 operation_state=nslcmop_operation_state,
3216 other_update=db_nslcmop_update,
3217 )
lloretgalleg6d488782020-07-22 10:13:46 +00003218 if ns_state == "NOT_INSTANTIATED":
3219 try:
3220 self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "NOT_INSTANTIATED"})
3221 except DbException as e:
3222 self.logger.warn(logging_text + 'Error writing VNFR status for nsr-id-ref: {} -> {}'.
3223 format(nsr_id, e))
tiernoa17d4f42020-04-28 09:59:23 +00003224 if operation_params:
tiernoe876f672020-02-13 14:34:48 +00003225 autoremove = operation_params.get("autoremove", False)
tierno59d22d22018-09-25 18:10:19 +02003226 if nslcmop_operation_state:
3227 try:
3228 await self.msg.aiowrite("ns", "terminated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tiernoc2564fe2019-01-28 16:18:56 +00003229 "operationState": nslcmop_operation_state,
3230 "autoremove": autoremove},
tierno8a518872018-12-21 13:42:14 +00003231 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02003232 except Exception as e:
3233 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02003234
tierno59d22d22018-09-25 18:10:19 +02003235 self.logger.debug(logging_text + "Exit")
3236 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_terminate")
3237
tiernoe876f672020-02-13 14:34:48 +00003238 async def _wait_for_tasks(self, logging_text, created_tasks_info, timeout, stage, nslcmop_id, nsr_id=None):
3239 time_start = time()
tiernoa2143262020-03-27 16:20:40 +00003240 error_detail_list = []
tiernoe876f672020-02-13 14:34:48 +00003241 error_list = []
3242 pending_tasks = list(created_tasks_info.keys())
3243 num_tasks = len(pending_tasks)
3244 num_done = 0
3245 stage[1] = "{}/{}.".format(num_done, num_tasks)
3246 self._write_op_status(nslcmop_id, stage)
tiernoe876f672020-02-13 14:34:48 +00003247 while pending_tasks:
tiernoa2143262020-03-27 16:20:40 +00003248 new_error = None
tiernoe876f672020-02-13 14:34:48 +00003249 _timeout = timeout + time_start - time()
3250 done, pending_tasks = await asyncio.wait(pending_tasks, timeout=_timeout,
3251 return_when=asyncio.FIRST_COMPLETED)
3252 num_done += len(done)
3253 if not done: # Timeout
3254 for task in pending_tasks:
tiernoa2143262020-03-27 16:20:40 +00003255 new_error = created_tasks_info[task] + ": Timeout"
3256 error_detail_list.append(new_error)
3257 error_list.append(new_error)
tiernoe876f672020-02-13 14:34:48 +00003258 break
3259 for task in done:
3260 if task.cancelled():
tierno067e04a2020-03-31 12:53:13 +00003261 exc = "Cancelled"
tiernoe876f672020-02-13 14:34:48 +00003262 else:
3263 exc = task.exception()
tierno067e04a2020-03-31 12:53:13 +00003264 if exc:
3265 if isinstance(exc, asyncio.TimeoutError):
3266 exc = "Timeout"
3267 new_error = created_tasks_info[task] + ": {}".format(exc)
3268 error_list.append(created_tasks_info[task])
3269 error_detail_list.append(new_error)
tierno28c63da2020-04-20 16:28:56 +00003270 if isinstance(exc, (str, DbException, N2VCException, ROclient.ROClientException, LcmException,
tierno2357f4e2020-10-19 16:38:59 +00003271 K8sException, NgRoException)):
tierno067e04a2020-03-31 12:53:13 +00003272 self.logger.error(logging_text + new_error)
tiernoe876f672020-02-13 14:34:48 +00003273 else:
tierno067e04a2020-03-31 12:53:13 +00003274 exc_traceback = "".join(traceback.format_exception(None, exc, exc.__traceback__))
tierno2357f4e2020-10-19 16:38:59 +00003275 self.logger.error(logging_text + created_tasks_info[task] + " " + exc_traceback)
tierno067e04a2020-03-31 12:53:13 +00003276 else:
3277 self.logger.debug(logging_text + created_tasks_info[task] + ": Done")
tiernoe876f672020-02-13 14:34:48 +00003278 stage[1] = "{}/{}.".format(num_done, num_tasks)
3279 if new_error:
tiernoa2143262020-03-27 16:20:40 +00003280 stage[1] += " Errors: " + ". ".join(error_detail_list) + "."
tiernoe876f672020-02-13 14:34:48 +00003281 if nsr_id: # update also nsr
tiernoa2143262020-03-27 16:20:40 +00003282 self.update_db_2("nsrs", nsr_id, {"errorDescription": "Error at: " + ", ".join(error_list),
3283 "errorDetail": ". ".join(error_detail_list)})
tiernoe876f672020-02-13 14:34:48 +00003284 self._write_op_status(nslcmop_id, stage)
tiernoa2143262020-03-27 16:20:40 +00003285 return error_detail_list
tiernoe876f672020-02-13 14:34:48 +00003286
tiernoda1ff8c2020-10-22 14:12:46 +00003287 @staticmethod
3288 def _map_primitive_params(primitive_desc, params, instantiation_params):
tiernoda964822019-01-14 15:53:47 +00003289 """
3290 Generates the params to be provided to charm before executing primitive. If user does not provide a parameter,
3291 The default-value is used. If it is between < > it look for a value at instantiation_params
3292 :param primitive_desc: portion of VNFD/NSD that describes primitive
3293 :param params: Params provided by user
3294 :param instantiation_params: Instantiation params provided by user
3295 :return: a dictionary with the calculated params
3296 """
3297 calculated_params = {}
3298 for parameter in primitive_desc.get("parameter", ()):
3299 param_name = parameter["name"]
3300 if param_name in params:
3301 calculated_params[param_name] = params[param_name]
tierno98ad6ea2019-05-30 17:16:28 +00003302 elif "default-value" in parameter or "value" in parameter:
3303 if "value" in parameter:
3304 calculated_params[param_name] = parameter["value"]
3305 else:
3306 calculated_params[param_name] = parameter["default-value"]
3307 if isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("<") \
3308 and calculated_params[param_name].endswith(">"):
3309 if calculated_params[param_name][1:-1] in instantiation_params:
3310 calculated_params[param_name] = instantiation_params[calculated_params[param_name][1:-1]]
tiernoda964822019-01-14 15:53:47 +00003311 else:
3312 raise LcmException("Parameter {} needed to execute primitive {} not provided".
tiernod8323042019-08-09 11:32:23 +00003313 format(calculated_params[param_name], primitive_desc["name"]))
tiernoda964822019-01-14 15:53:47 +00003314 else:
3315 raise LcmException("Parameter {} needed to execute primitive {} not provided".
3316 format(param_name, primitive_desc["name"]))
tierno59d22d22018-09-25 18:10:19 +02003317
tiernoda964822019-01-14 15:53:47 +00003318 if isinstance(calculated_params[param_name], (dict, list, tuple)):
bravof922c4172020-11-24 21:21:43 -03003319 calculated_params[param_name] = yaml.safe_dump(calculated_params[param_name],
3320 default_flow_style=True, width=256)
tiernoda964822019-01-14 15:53:47 +00003321 elif isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("!!yaml "):
3322 calculated_params[param_name] = calculated_params[param_name][7:]
tiernofa40e692020-10-14 14:59:36 +00003323 if parameter.get("data-type") == "INTEGER":
3324 try:
3325 calculated_params[param_name] = int(calculated_params[param_name])
3326 except ValueError: # error converting string to int
3327 raise LcmException(
3328 "Parameter {} of primitive {} must be integer".format(param_name, primitive_desc["name"]))
3329 elif parameter.get("data-type") == "BOOLEAN":
3330 calculated_params[param_name] = not ((str(calculated_params[param_name])).lower() == 'false')
tiernoc3f2a822019-11-05 13:45:04 +00003331
3332 # add always ns_config_info if primitive name is config
3333 if primitive_desc["name"] == "config":
3334 if "ns_config_info" in instantiation_params:
3335 calculated_params["ns_config_info"] = instantiation_params["ns_config_info"]
tiernoda964822019-01-14 15:53:47 +00003336 return calculated_params
3337
tiernoa278b842020-07-08 15:33:55 +00003338 def _look_for_deployed_vca(self, deployed_vca, member_vnf_index, vdu_id, vdu_count_index, kdu_name=None,
3339 ee_descriptor_id=None):
tiernoe876f672020-02-13 14:34:48 +00003340 # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
3341 for vca in deployed_vca:
3342 if not vca:
3343 continue
3344 if member_vnf_index != vca["member-vnf-index"] or vdu_id != vca["vdu_id"]:
3345 continue
tiernoe876f672020-02-13 14:34:48 +00003346 if vdu_count_index is not None and vdu_count_index != vca["vdu_count_index"]:
3347 continue
3348 if kdu_name and kdu_name != vca["kdu_name"]:
3349 continue
tiernoa278b842020-07-08 15:33:55 +00003350 if ee_descriptor_id and ee_descriptor_id != vca["ee_descriptor_id"]:
3351 continue
tiernoe876f672020-02-13 14:34:48 +00003352 break
3353 else:
3354 # vca_deployed not found
tiernoa278b842020-07-08 15:33:55 +00003355 raise LcmException("charm for member_vnf_index={} vdu_id={}.{} kdu_name={} execution-environment-list.id={}"
3356 " is not deployed".format(member_vnf_index, vdu_id, vdu_count_index, kdu_name,
3357 ee_descriptor_id))
tiernoe876f672020-02-13 14:34:48 +00003358 # get ee_id
3359 ee_id = vca.get("ee_id")
tierno588547c2020-07-01 15:30:20 +00003360 vca_type = vca.get("type", "lxc_proxy_charm") # default value for backward compatibility - proxy charm
tiernoe876f672020-02-13 14:34:48 +00003361 if not ee_id:
tierno067e04a2020-03-31 12:53:13 +00003362 raise LcmException("charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
tiernoe876f672020-02-13 14:34:48 +00003363 "execution environment"
tierno067e04a2020-03-31 12:53:13 +00003364 .format(member_vnf_index, vdu_id, kdu_name, vdu_count_index))
tierno588547c2020-07-01 15:30:20 +00003365 return ee_id, vca_type
tiernoe876f672020-02-13 14:34:48 +00003366
bravof922c4172020-11-24 21:21:43 -03003367 async def _ns_execute_primitive(self, ee_id, primitive, primitive_params, retries=0, retries_interval=30,
3368 timeout=None, vca_type=None, db_dict=None) -> (str, str):
tiernoda964822019-01-14 15:53:47 +00003369 try:
tierno98ad6ea2019-05-30 17:16:28 +00003370 if primitive == "config":
3371 primitive_params = {"params": primitive_params}
tierno2fc7ce52019-06-11 22:50:01 +00003372
tierno588547c2020-07-01 15:30:20 +00003373 vca_type = vca_type or "lxc_proxy_charm"
3374
quilesj7e13aeb2019-10-08 13:34:55 +02003375 while retries >= 0:
3376 try:
tierno067e04a2020-03-31 12:53:13 +00003377 output = await asyncio.wait_for(
tierno588547c2020-07-01 15:30:20 +00003378 self.vca_map[vca_type].exec_primitive(
tierno067e04a2020-03-31 12:53:13 +00003379 ee_id=ee_id,
3380 primitive_name=primitive,
3381 params_dict=primitive_params,
3382 progress_timeout=self.timeout_progress_primitive,
tierno588547c2020-07-01 15:30:20 +00003383 total_timeout=self.timeout_primitive,
3384 db_dict=db_dict),
tierno067e04a2020-03-31 12:53:13 +00003385 timeout=timeout or self.timeout_primitive)
quilesj7e13aeb2019-10-08 13:34:55 +02003386 # execution was OK
3387 break
tierno067e04a2020-03-31 12:53:13 +00003388 except asyncio.CancelledError:
3389 raise
3390 except Exception as e: # asyncio.TimeoutError
3391 if isinstance(e, asyncio.TimeoutError):
3392 e = "Timeout"
quilesj7e13aeb2019-10-08 13:34:55 +02003393 retries -= 1
3394 if retries >= 0:
tierno73d8bd02019-11-18 17:33:27 +00003395 self.logger.debug('Error executing action {} on {} -> {}'.format(primitive, ee_id, e))
quilesj7e13aeb2019-10-08 13:34:55 +02003396 # wait and retry
3397 await asyncio.sleep(retries_interval, loop=self.loop)
tierno73d8bd02019-11-18 17:33:27 +00003398 else:
tierno067e04a2020-03-31 12:53:13 +00003399 return 'FAILED', str(e)
quilesj7e13aeb2019-10-08 13:34:55 +02003400
tiernoe876f672020-02-13 14:34:48 +00003401 return 'COMPLETED', output
quilesj7e13aeb2019-10-08 13:34:55 +02003402
tierno067e04a2020-03-31 12:53:13 +00003403 except (LcmException, asyncio.CancelledError):
tiernoe876f672020-02-13 14:34:48 +00003404 raise
quilesj7e13aeb2019-10-08 13:34:55 +02003405 except Exception as e:
tiernoe876f672020-02-13 14:34:48 +00003406 return 'FAIL', 'Error executing action {}: {}'.format(primitive, e)
tierno59d22d22018-09-25 18:10:19 +02003407
3408 async def action(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003409 # Try to lock HA task here
3410 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3411 if not task_is_locked_by_me:
3412 return
3413
tierno59d22d22018-09-25 18:10:19 +02003414 logging_text = "Task ns={} action={} ".format(nsr_id, nslcmop_id)
3415 self.logger.debug(logging_text + "Enter")
3416 # get all needed from database
3417 db_nsr = None
3418 db_nslcmop = None
tiernoe876f672020-02-13 14:34:48 +00003419 db_nsr_update = {}
tierno59d22d22018-09-25 18:10:19 +02003420 db_nslcmop_update = {}
3421 nslcmop_operation_state = None
tierno067e04a2020-03-31 12:53:13 +00003422 error_description_nslcmop = None
tierno59d22d22018-09-25 18:10:19 +02003423 exc = None
3424 try:
kuused124bfe2019-06-18 12:09:24 +02003425 # wait for any previous tasks in process
tierno3cf81a32019-11-11 17:07:00 +00003426 step = "Waiting for previous operations to terminate"
kuused124bfe2019-06-18 12:09:24 +02003427 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
3428
quilesj4cda56b2019-12-05 10:02:20 +00003429 self._write_ns_status(
3430 nsr_id=nsr_id,
3431 ns_state=None,
3432 current_operation="RUNNING ACTION",
3433 current_operation_id=nslcmop_id
3434 )
3435
tierno59d22d22018-09-25 18:10:19 +02003436 step = "Getting information from database"
3437 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
3438 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
tiernoda964822019-01-14 15:53:47 +00003439
tiernoe4f7e6c2018-11-27 14:55:30 +00003440 nsr_deployed = db_nsr["_admin"].get("deployed")
tierno1b633412019-02-25 16:48:23 +00003441 vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
tierno59d22d22018-09-25 18:10:19 +02003442 vdu_id = db_nslcmop["operationParams"].get("vdu_id")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003443 kdu_name = db_nslcmop["operationParams"].get("kdu_name")
tiernoe4f7e6c2018-11-27 14:55:30 +00003444 vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
tierno067e04a2020-03-31 12:53:13 +00003445 primitive = db_nslcmop["operationParams"]["primitive"]
3446 primitive_params = db_nslcmop["operationParams"]["primitive_params"]
3447 timeout_ns_action = db_nslcmop["operationParams"].get("timeout_ns_action", self.timeout_primitive)
tierno59d22d22018-09-25 18:10:19 +02003448
tierno1b633412019-02-25 16:48:23 +00003449 if vnf_index:
3450 step = "Getting vnfr from database"
3451 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
3452 step = "Getting vnfd from database"
3453 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
3454 else:
tierno067e04a2020-03-31 12:53:13 +00003455 step = "Getting nsd from database"
3456 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
tiernoda964822019-01-14 15:53:47 +00003457
tierno82974b22018-11-27 21:55:36 +00003458 # for backward compatibility
3459 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
3460 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
3461 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
3462 self.update_db_2("nsrs", nsr_id, db_nsr_update)
3463
tiernoda964822019-01-14 15:53:47 +00003464 # look for primitive
tiernoa278b842020-07-08 15:33:55 +00003465 config_primitive_desc = descriptor_configuration = None
tiernoda964822019-01-14 15:53:47 +00003466 if vdu_id:
garciaalea77ced72021-02-17 19:09:12 -03003467 descriptor_configuration = get_configuration(db_vnfd, vdu_id)
calvinosanch9f9c6f22019-11-04 13:37:39 +01003468 elif kdu_name:
garciaalea77ced72021-02-17 19:09:12 -03003469 descriptor_configuration = get_configuration(db_vnfd, kdu_name)
tierno1b633412019-02-25 16:48:23 +00003470 elif vnf_index:
garciaalea77ced72021-02-17 19:09:12 -03003471 descriptor_configuration = get_configuration(db_vnfd, db_vnfd["id"])
tierno1b633412019-02-25 16:48:23 +00003472 else:
tiernoa278b842020-07-08 15:33:55 +00003473 descriptor_configuration = db_nsd.get("ns-configuration")
3474
3475 if descriptor_configuration and descriptor_configuration.get("config-primitive"):
3476 for config_primitive in descriptor_configuration["config-primitive"]:
tierno1b633412019-02-25 16:48:23 +00003477 if config_primitive["name"] == primitive:
3478 config_primitive_desc = config_primitive
3479 break
tiernoda964822019-01-14 15:53:47 +00003480
garciadeblas6bed6b32020-07-20 11:05:42 +00003481 if not config_primitive_desc:
3482 if not (kdu_name and primitive in ("upgrade", "rollback", "status")):
3483 raise LcmException("Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".
3484 format(primitive))
3485 primitive_name = primitive
3486 ee_descriptor_id = None
3487 else:
3488 primitive_name = config_primitive_desc.get("execution-environment-primitive", primitive)
3489 ee_descriptor_id = config_primitive_desc.get("execution-environment-ref")
tierno1b633412019-02-25 16:48:23 +00003490
tierno1b633412019-02-25 16:48:23 +00003491 if vnf_index:
tierno626e0152019-11-29 14:16:16 +00003492 if vdu_id:
3493 vdur = next((x for x in db_vnfr["vdur"] if x["vdu-id-ref"] == vdu_id), None)
bravof922c4172020-11-24 21:21:43 -03003494 desc_params = parse_yaml_strings(vdur.get("additionalParams"))
tierno067e04a2020-03-31 12:53:13 +00003495 elif kdu_name:
3496 kdur = next((x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name), None)
bravof922c4172020-11-24 21:21:43 -03003497 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
tierno067e04a2020-03-31 12:53:13 +00003498 else:
bravof922c4172020-11-24 21:21:43 -03003499 desc_params = parse_yaml_strings(db_vnfr.get("additionalParamsForVnf"))
tierno1b633412019-02-25 16:48:23 +00003500 else:
bravof922c4172020-11-24 21:21:43 -03003501 desc_params = parse_yaml_strings(db_nsr.get("additionalParamsForNs"))
garciaalea77ced72021-02-17 19:09:12 -03003502 if kdu_name and get_configuration(db_vnfd, kdu_name):
3503 kdu_configuration = get_configuration(db_vnfd, kdu_name)
David Garciad41dbd62020-12-10 12:52:52 +01003504 actions = set()
David Garcia95cc9c52021-02-16 21:07:58 +01003505 for primitive in kdu_configuration.get("initial-config-primitive", []):
David Garciad41dbd62020-12-10 12:52:52 +01003506 actions.add(primitive["name"])
David Garcia95cc9c52021-02-16 21:07:58 +01003507 for primitive in kdu_configuration.get("config-primitive", []):
David Garciad41dbd62020-12-10 12:52:52 +01003508 actions.add(primitive["name"])
3509 kdu_action = True if primitive_name in actions else False
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003510
tiernoda964822019-01-14 15:53:47 +00003511 # TODO check if ns is in a proper status
tiernoa278b842020-07-08 15:33:55 +00003512 if kdu_name and (primitive_name in ("upgrade", "rollback", "status") or kdu_action):
tierno067e04a2020-03-31 12:53:13 +00003513 # kdur and desc_params already set from before
3514 if primitive_params:
3515 desc_params.update(primitive_params)
3516 # TODO Check if we will need something at vnf level
3517 for index, kdu in enumerate(get_iterable(nsr_deployed, "K8s")):
3518 if kdu_name == kdu["kdu-name"] and kdu["member-vnf-index"] == vnf_index:
3519 break
3520 else:
3521 raise LcmException("KDU '{}' for vnf '{}' not deployed".format(kdu_name, vnf_index))
quilesj7e13aeb2019-10-08 13:34:55 +02003522
tierno067e04a2020-03-31 12:53:13 +00003523 if kdu.get("k8scluster-type") not in self.k8scluster_map:
3524 msg = "unknown k8scluster-type '{}'".format(kdu.get("k8scluster-type"))
3525 raise LcmException(msg)
3526
3527 db_dict = {"collection": "nsrs",
3528 "filter": {"_id": nsr_id},
3529 "path": "_admin.deployed.K8s.{}".format(index)}
tiernoa278b842020-07-08 15:33:55 +00003530 self.logger.debug(logging_text + "Exec k8s {} on {}.{}".format(primitive_name, vnf_index, kdu_name))
3531 step = "Executing kdu {}".format(primitive_name)
3532 if primitive_name == "upgrade":
tierno067e04a2020-03-31 12:53:13 +00003533 if desc_params.get("kdu_model"):
3534 kdu_model = desc_params.get("kdu_model")
3535 del desc_params["kdu_model"]
3536 else:
3537 kdu_model = kdu.get("kdu-model")
3538 parts = kdu_model.split(sep=":")
3539 if len(parts) == 2:
3540 kdu_model = parts[0]
3541
3542 detailed_status = await asyncio.wait_for(
3543 self.k8scluster_map[kdu["k8scluster-type"]].upgrade(
3544 cluster_uuid=kdu.get("k8scluster-uuid"),
3545 kdu_instance=kdu.get("kdu-instance"),
3546 atomic=True, kdu_model=kdu_model,
3547 params=desc_params, db_dict=db_dict,
3548 timeout=timeout_ns_action),
3549 timeout=timeout_ns_action + 10)
3550 self.logger.debug(logging_text + " Upgrade of kdu {} done".format(detailed_status))
tiernoa278b842020-07-08 15:33:55 +00003551 elif primitive_name == "rollback":
tierno067e04a2020-03-31 12:53:13 +00003552 detailed_status = await asyncio.wait_for(
3553 self.k8scluster_map[kdu["k8scluster-type"]].rollback(
3554 cluster_uuid=kdu.get("k8scluster-uuid"),
3555 kdu_instance=kdu.get("kdu-instance"),
3556 db_dict=db_dict),
3557 timeout=timeout_ns_action)
tiernoa278b842020-07-08 15:33:55 +00003558 elif primitive_name == "status":
tierno067e04a2020-03-31 12:53:13 +00003559 detailed_status = await asyncio.wait_for(
3560 self.k8scluster_map[kdu["k8scluster-type"]].status_kdu(
3561 cluster_uuid=kdu.get("k8scluster-uuid"),
3562 kdu_instance=kdu.get("kdu-instance")),
3563 timeout=timeout_ns_action)
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003564 else:
3565 kdu_instance = kdu.get("kdu-instance") or "{}-{}".format(kdu["kdu-name"], nsr_id)
3566 params = self._map_primitive_params(config_primitive_desc, primitive_params, desc_params)
3567
3568 detailed_status = await asyncio.wait_for(
3569 self.k8scluster_map[kdu["k8scluster-type"]].exec_primitive(
3570 cluster_uuid=kdu.get("k8scluster-uuid"),
3571 kdu_instance=kdu_instance,
tiernoa278b842020-07-08 15:33:55 +00003572 primitive_name=primitive_name,
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003573 params=params, db_dict=db_dict,
3574 timeout=timeout_ns_action),
3575 timeout=timeout_ns_action)
tierno067e04a2020-03-31 12:53:13 +00003576
3577 if detailed_status:
3578 nslcmop_operation_state = 'COMPLETED'
3579 else:
3580 detailed_status = ''
3581 nslcmop_operation_state = 'FAILED'
tierno067e04a2020-03-31 12:53:13 +00003582 else:
bravof922c4172020-11-24 21:21:43 -03003583 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"], member_vnf_index=vnf_index,
3584 vdu_id=vdu_id, vdu_count_index=vdu_count_index,
tiernoa278b842020-07-08 15:33:55 +00003585 ee_descriptor_id=ee_descriptor_id)
tierno588547c2020-07-01 15:30:20 +00003586 db_nslcmop_notif = {"collection": "nslcmops",
3587 "filter": {"_id": nslcmop_id},
3588 "path": "admin.VCA"}
tierno067e04a2020-03-31 12:53:13 +00003589 nslcmop_operation_state, detailed_status = await self._ns_execute_primitive(
tierno588547c2020-07-01 15:30:20 +00003590 ee_id,
tiernoa278b842020-07-08 15:33:55 +00003591 primitive=primitive_name,
tierno067e04a2020-03-31 12:53:13 +00003592 primitive_params=self._map_primitive_params(config_primitive_desc, primitive_params, desc_params),
tierno588547c2020-07-01 15:30:20 +00003593 timeout=timeout_ns_action,
3594 vca_type=vca_type,
3595 db_dict=db_nslcmop_notif)
tierno067e04a2020-03-31 12:53:13 +00003596
3597 db_nslcmop_update["detailed-status"] = detailed_status
3598 error_description_nslcmop = detailed_status if nslcmop_operation_state == "FAILED" else ""
3599 self.logger.debug(logging_text + " task Done with result {} {}".format(nslcmop_operation_state,
3600 detailed_status))
tierno59d22d22018-09-25 18:10:19 +02003601 return # database update is called inside finally
3602
tiernof59ad6c2020-04-08 12:50:52 +00003603 except (DbException, LcmException, N2VCException, K8sException) as e:
tierno59d22d22018-09-25 18:10:19 +02003604 self.logger.error(logging_text + "Exit Exception {}".format(e))
3605 exc = e
3606 except asyncio.CancelledError:
3607 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
3608 exc = "Operation was cancelled"
tierno067e04a2020-03-31 12:53:13 +00003609 except asyncio.TimeoutError:
3610 self.logger.error(logging_text + "Timeout while '{}'".format(step))
3611 exc = "Timeout"
tierno59d22d22018-09-25 18:10:19 +02003612 except Exception as e:
3613 exc = traceback.format_exc()
3614 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
3615 finally:
tierno067e04a2020-03-31 12:53:13 +00003616 if exc:
3617 db_nslcmop_update["detailed-status"] = detailed_status = error_description_nslcmop = \
kuuse0ca67472019-05-13 15:59:27 +02003618 "FAILED {}: {}".format(step, exc)
tierno067e04a2020-03-31 12:53:13 +00003619 nslcmop_operation_state = "FAILED"
3620 if db_nsr:
3621 self._write_ns_status(
3622 nsr_id=nsr_id,
3623 ns_state=db_nsr["nsState"], # TODO check if degraded. For the moment use previous status
3624 current_operation="IDLE",
3625 current_operation_id=None,
3626 # error_description=error_description_nsr,
3627 # error_detail=error_detail,
3628 other_update=db_nsr_update
3629 )
3630
bravof922c4172020-11-24 21:21:43 -03003631 self._write_op_status(op_id=nslcmop_id, stage="", error_message=error_description_nslcmop,
3632 operation_state=nslcmop_operation_state, other_update=db_nslcmop_update)
tierno067e04a2020-03-31 12:53:13 +00003633
tierno59d22d22018-09-25 18:10:19 +02003634 if nslcmop_operation_state:
3635 try:
3636 await self.msg.aiowrite("ns", "actioned", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tierno8a518872018-12-21 13:42:14 +00003637 "operationState": nslcmop_operation_state},
3638 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02003639 except Exception as e:
3640 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
3641 self.logger.debug(logging_text + "Exit")
3642 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_action")
tierno067e04a2020-03-31 12:53:13 +00003643 return nslcmop_operation_state, detailed_status
tierno59d22d22018-09-25 18:10:19 +02003644
3645 async def scale(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003646 # Try to lock HA task here
3647 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3648 if not task_is_locked_by_me:
3649 return
3650
tierno59d22d22018-09-25 18:10:19 +02003651 logging_text = "Task ns={} scale={} ".format(nsr_id, nslcmop_id)
tierno2357f4e2020-10-19 16:38:59 +00003652 stage = ['', '', '']
aktasf0f83a32021-02-12 22:19:10 +03003653 tasks_dict_info = {}
tierno2357f4e2020-10-19 16:38:59 +00003654 # ^ stage, step, VIM progress
tierno59d22d22018-09-25 18:10:19 +02003655 self.logger.debug(logging_text + "Enter")
3656 # get all needed from database
3657 db_nsr = None
tierno59d22d22018-09-25 18:10:19 +02003658 db_nslcmop_update = {}
tiernoe876f672020-02-13 14:34:48 +00003659 db_nsr_update = {}
tierno59d22d22018-09-25 18:10:19 +02003660 exc = None
tierno9ab95942018-10-10 16:44:22 +02003661 # in case of error, indicates what part of scale was failed to put nsr at error status
3662 scale_process = None
tiernod6de1992018-10-11 13:05:52 +02003663 old_operational_status = ""
3664 old_config_status = ""
aktasf0f83a32021-02-12 22:19:10 +03003665 nsi_id = None
tierno59d22d22018-09-25 18:10:19 +02003666 try:
kuused124bfe2019-06-18 12:09:24 +02003667 # wait for any previous tasks in process
tierno3cf81a32019-11-11 17:07:00 +00003668 step = "Waiting for previous operations to terminate"
kuused124bfe2019-06-18 12:09:24 +02003669 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
bravof922c4172020-11-24 21:21:43 -03003670 self._write_ns_status(nsr_id=nsr_id, ns_state=None,
3671 current_operation="SCALING", current_operation_id=nslcmop_id)
quilesj4cda56b2019-12-05 10:02:20 +00003672
ikalyvas02d9e7b2019-05-27 18:16:01 +03003673 step = "Getting nslcmop from database"
ikalyvas02d9e7b2019-05-27 18:16:01 +03003674 self.logger.debug(step + " after having waited for previous tasks to be completed")
3675 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
bravof922c4172020-11-24 21:21:43 -03003676
ikalyvas02d9e7b2019-05-27 18:16:01 +03003677 step = "Getting nsr from database"
3678 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
ikalyvas02d9e7b2019-05-27 18:16:01 +03003679 old_operational_status = db_nsr["operational-status"]
3680 old_config_status = db_nsr["config-status"]
bravof922c4172020-11-24 21:21:43 -03003681
tierno59d22d22018-09-25 18:10:19 +02003682 step = "Parsing scaling parameters"
3683 db_nsr_update["operational-status"] = "scaling"
3684 self.update_db_2("nsrs", nsr_id, db_nsr_update)
tiernoe4f7e6c2018-11-27 14:55:30 +00003685 nsr_deployed = db_nsr["_admin"].get("deployed")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003686
3687 #######
3688 nsr_deployed = db_nsr["_admin"].get("deployed")
3689 vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
tiernoda6fb102019-11-23 00:36:52 +00003690 # vdu_id = db_nslcmop["operationParams"].get("vdu_id")
3691 # vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
3692 # vdu_name = db_nslcmop["operationParams"].get("vdu_name")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003693 #######
3694
tierno59d22d22018-09-25 18:10:19 +02003695 vnf_index = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["member-vnf-index"]
3696 scaling_group = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]
3697 scaling_type = db_nslcmop["operationParams"]["scaleVnfData"]["scaleVnfType"]
tierno82974b22018-11-27 21:55:36 +00003698 # for backward compatibility
3699 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
3700 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
3701 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
3702 self.update_db_2("nsrs", nsr_id, db_nsr_update)
3703
tierno59d22d22018-09-25 18:10:19 +02003704 step = "Getting vnfr from database"
3705 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
bravof922c4172020-11-24 21:21:43 -03003706
tierno59d22d22018-09-25 18:10:19 +02003707 step = "Getting vnfd from database"
3708 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
ikalyvas02d9e7b2019-05-27 18:16:01 +03003709
aktasf0f83a32021-02-12 22:19:10 +03003710 base_folder = db_vnfd["_admin"]["storage"]
3711
tierno59d22d22018-09-25 18:10:19 +02003712 step = "Getting scaling-group-descriptor"
bravof832f8992020-12-07 12:57:31 -03003713 scaling_descriptor = find_in_list(
3714 get_scaling_aspect(
3715 db_vnfd
3716 ),
3717 lambda scale_desc: scale_desc["name"] == scaling_group
3718 )
3719 if not scaling_descriptor:
tierno59d22d22018-09-25 18:10:19 +02003720 raise LcmException("input parameter 'scaleByStepData':'scaling-group-descriptor':'{}' is not present "
3721 "at vnfd:scaling-group-descriptor".format(scaling_group))
ikalyvas02d9e7b2019-05-27 18:16:01 +03003722
tierno15b1cf12019-08-29 13:21:40 +00003723 step = "Sending scale order to VIM"
bravof922c4172020-11-24 21:21:43 -03003724 # TODO check if ns is in a proper status
tierno59d22d22018-09-25 18:10:19 +02003725 nb_scale_op = 0
3726 if not db_nsr["_admin"].get("scaling-group"):
3727 self.update_db_2("nsrs", nsr_id, {"_admin.scaling-group": [{"name": scaling_group, "nb-scale-op": 0}]})
3728 admin_scale_index = 0
3729 else:
3730 for admin_scale_index, admin_scale_info in enumerate(db_nsr["_admin"]["scaling-group"]):
3731 if admin_scale_info["name"] == scaling_group:
3732 nb_scale_op = admin_scale_info.get("nb-scale-op", 0)
3733 break
tierno9ab95942018-10-10 16:44:22 +02003734 else: # not found, set index one plus last element and add new entry with the name
3735 admin_scale_index += 1
3736 db_nsr_update["_admin.scaling-group.{}.name".format(admin_scale_index)] = scaling_group
tierno59d22d22018-09-25 18:10:19 +02003737 RO_scaling_info = []
aktasf0f83a32021-02-12 22:19:10 +03003738 VCA_scaling_info = []
tierno59d22d22018-09-25 18:10:19 +02003739 vdu_scaling_info = {"scaling_group_name": scaling_group, "vdu": []}
3740 if scaling_type == "SCALE_OUT":
bravof832f8992020-12-07 12:57:31 -03003741 if "aspect-delta-details" not in scaling_descriptor:
3742 raise LcmException(
3743 "Aspect delta details not fount in scaling descriptor {}".format(
3744 scaling_descriptor["name"]
3745 )
3746 )
tierno59d22d22018-09-25 18:10:19 +02003747 # count if max-instance-count is reached
bravof832f8992020-12-07 12:57:31 -03003748 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
kuuse8b998e42019-07-30 15:22:16 +02003749
tierno59d22d22018-09-25 18:10:19 +02003750 vdu_scaling_info["scaling_direction"] = "OUT"
3751 vdu_scaling_info["vdu-create"] = {}
bravof832f8992020-12-07 12:57:31 -03003752 for delta in deltas:
3753 for vdu_delta in delta["vdu-delta"]:
3754 vdud = get_vdu(db_vnfd, vdu_delta["id"])
aktasf0f83a32021-02-12 22:19:10 +03003755 vdu_index = get_vdur_index(db_vnfr, vdu_delta)
bravof832f8992020-12-07 12:57:31 -03003756 cloud_init_text = self._get_vdu_cloud_init_content(vdud, db_vnfd)
tierno72ef84f2020-10-06 08:22:07 +00003757 if cloud_init_text:
bravof832f8992020-12-07 12:57:31 -03003758 additional_params = self._get_vdu_additional_params(db_vnfr, vdud["id"]) or {}
3759 cloud_init_list = []
3760
3761 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
3762 max_instance_count = 10
3763 if vdu_profile and "max-number-of-instances" in vdu_profile:
3764 max_instance_count = vdu_profile.get("max-number-of-instances", 10)
3765
aktasf0f83a32021-02-12 22:19:10 +03003766 default_instance_num = get_number_of_instances(db_vnfd, vdud["id"])
bravof832f8992020-12-07 12:57:31 -03003767
3768 nb_scale_op += vdu_delta.get("number-of-instances", 1)
3769
aktasf0f83a32021-02-12 22:19:10 +03003770 if nb_scale_op + default_instance_num > max_instance_count:
bravof832f8992020-12-07 12:57:31 -03003771 raise LcmException(
3772 "reached the limit of {} (max-instance-count) "
3773 "scaling-out operations for the "
3774 "scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group)
bravof922c4172020-11-24 21:21:43 -03003775 )
bravof832f8992020-12-07 12:57:31 -03003776 for x in range(vdu_delta.get("number-of-instances", 1)):
3777 if cloud_init_text:
3778 # TODO Information of its own ip is not available because db_vnfr is not updated.
3779 additional_params["OSM"] = get_osm_params(
3780 db_vnfr,
3781 vdu_delta["id"],
3782 vdu_index + x
bravof922c4172020-11-24 21:21:43 -03003783 )
bravof832f8992020-12-07 12:57:31 -03003784 cloud_init_list.append(
3785 self._parse_cloud_init(
3786 cloud_init_text,
3787 additional_params,
3788 db_vnfd["id"],
3789 vdud["id"]
3790 )
3791 )
aktasf0f83a32021-02-12 22:19:10 +03003792 VCA_scaling_info.append(
3793 {
3794 "osm_vdu_id": vdu_delta["id"],
3795 "member-vnf-index": vnf_index,
3796 "type": "create",
3797 "vdu_index": vdu_index + x
3798 }
3799 )
bravof832f8992020-12-07 12:57:31 -03003800 RO_scaling_info.append(
3801 {
3802 "osm_vdu_id": vdu_delta["id"],
3803 "member-vnf-index": vnf_index,
3804 "type": "create",
3805 "count": vdu_delta.get("number-of-instances", 1)
3806 }
3807 )
3808 if cloud_init_list:
3809 RO_scaling_info[-1]["cloud_init"] = cloud_init_list
3810 vdu_scaling_info["vdu-create"][vdu_delta["id"]] = vdu_delta.get("number-of-instances", 1)
ikalyvas02d9e7b2019-05-27 18:16:01 +03003811
tierno59d22d22018-09-25 18:10:19 +02003812 elif scaling_type == "SCALE_IN":
tierno59d22d22018-09-25 18:10:19 +02003813 if "min-instance-count" in scaling_descriptor and scaling_descriptor["min-instance-count"] is not None:
3814 min_instance_count = int(scaling_descriptor["min-instance-count"])
bravof832f8992020-12-07 12:57:31 -03003815
tierno59d22d22018-09-25 18:10:19 +02003816 vdu_scaling_info["scaling_direction"] = "IN"
3817 vdu_scaling_info["vdu-delete"] = {}
bravof832f8992020-12-07 12:57:31 -03003818 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
3819 for delta in deltas:
3820 for vdu_delta in delta["vdu-delta"]:
aktasf0f83a32021-02-12 22:19:10 +03003821 vdu_index = get_vdur_index(db_vnfr, vdu_delta)
bravof832f8992020-12-07 12:57:31 -03003822 min_instance_count = 0
3823 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
3824 if vdu_profile and "min-number-of-instances" in vdu_profile:
3825 min_instance_count = vdu_profile["min-number-of-instances"]
3826
aktasf0f83a32021-02-12 22:19:10 +03003827 default_instance_num = get_number_of_instances(db_vnfd, vdu_delta["id"])
bravof832f8992020-12-07 12:57:31 -03003828
3829 nb_scale_op -= vdu_delta.get("number-of-instances", 1)
aktasf0f83a32021-02-12 22:19:10 +03003830 if nb_scale_op + default_instance_num < min_instance_count:
bravof832f8992020-12-07 12:57:31 -03003831 raise LcmException(
3832 "reached the limit of {} (min-instance-count) scaling-in operations for the "
3833 "scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group)
3834 )
3835 RO_scaling_info.append({"osm_vdu_id": vdu_delta["id"], "member-vnf-index": vnf_index,
aktasf0f83a32021-02-12 22:19:10 +03003836 "type": "delete", "count": vdu_delta.get("number-of-instances", 1),
3837 "vdu_index": vdu_index - 1})
3838 for x in range(vdu_delta.get("number-of-instances", 1)):
3839 VCA_scaling_info.append(
3840 {
3841 "osm_vdu_id": vdu_delta["id"],
3842 "member-vnf-index": vnf_index,
3843 "type": "delete",
3844 "vdu_index": vdu_index - 1 - x
3845 }
3846 )
bravof832f8992020-12-07 12:57:31 -03003847 vdu_scaling_info["vdu-delete"][vdu_delta["id"]] = vdu_delta.get("number-of-instances", 1)
tierno59d22d22018-09-25 18:10:19 +02003848
3849 # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
tierno27246d82018-09-27 15:59:09 +02003850 vdu_delete = copy(vdu_scaling_info.get("vdu-delete"))
tierno59d22d22018-09-25 18:10:19 +02003851 if vdu_scaling_info["scaling_direction"] == "IN":
3852 for vdur in reversed(db_vnfr["vdur"]):
tierno27246d82018-09-27 15:59:09 +02003853 if vdu_delete.get(vdur["vdu-id-ref"]):
3854 vdu_delete[vdur["vdu-id-ref"]] -= 1
tierno59d22d22018-09-25 18:10:19 +02003855 vdu_scaling_info["vdu"].append({
tierno2357f4e2020-10-19 16:38:59 +00003856 "name": vdur.get("name") or vdur.get("vdu-name"),
tierno59d22d22018-09-25 18:10:19 +02003857 "vdu_id": vdur["vdu-id-ref"],
3858 "interface": []
3859 })
3860 for interface in vdur["interfaces"]:
3861 vdu_scaling_info["vdu"][-1]["interface"].append({
3862 "name": interface["name"],
3863 "ip_address": interface["ip-address"],
3864 "mac_address": interface.get("mac-address"),
3865 })
tierno2357f4e2020-10-19 16:38:59 +00003866 # vdu_delete = vdu_scaling_info.pop("vdu-delete")
tierno59d22d22018-09-25 18:10:19 +02003867
kuuseac3a8882019-10-03 10:48:06 +02003868 # PRE-SCALE BEGIN
tierno59d22d22018-09-25 18:10:19 +02003869 step = "Executing pre-scale vnf-config-primitive"
3870 if scaling_descriptor.get("scaling-config-action"):
3871 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
kuuseac3a8882019-10-03 10:48:06 +02003872 if (scaling_config_action.get("trigger") == "pre-scale-in" and scaling_type == "SCALE_IN") \
3873 or (scaling_config_action.get("trigger") == "pre-scale-out" and scaling_type == "SCALE_OUT"):
tierno59d22d22018-09-25 18:10:19 +02003874 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
3875 step = db_nslcmop_update["detailed-status"] = \
3876 "executing pre-scale scaling-config-action '{}'".format(vnf_config_primitive)
tiernoda964822019-01-14 15:53:47 +00003877
tierno59d22d22018-09-25 18:10:19 +02003878 # look for primitive
garciaalea77ced72021-02-17 19:09:12 -03003879 for config_primitive in (get_configuration(
3880 db_vnfd, db_vnfd["id"]
3881 ) or {}).get("config-primitive", ()):
tierno59d22d22018-09-25 18:10:19 +02003882 if config_primitive["name"] == vnf_config_primitive:
tierno59d22d22018-09-25 18:10:19 +02003883 break
3884 else:
3885 raise LcmException(
3886 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
tiernoda964822019-01-14 15:53:47 +00003887 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
tiernoa278b842020-07-08 15:33:55 +00003888 "primitive".format(scaling_group, vnf_config_primitive))
tiernoda964822019-01-14 15:53:47 +00003889
tierno16fedf52019-05-24 08:38:26 +00003890 vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
tiernoda964822019-01-14 15:53:47 +00003891 if db_vnfr.get("additionalParamsForVnf"):
3892 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
quilesj7e13aeb2019-10-08 13:34:55 +02003893
tierno9ab95942018-10-10 16:44:22 +02003894 scale_process = "VCA"
tiernod6de1992018-10-11 13:05:52 +02003895 db_nsr_update["config-status"] = "configuring pre-scaling"
kuuseac3a8882019-10-03 10:48:06 +02003896 primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
3897
tierno7c4e24c2020-05-13 08:41:35 +00003898 # Pre-scale retry check: Check if this sub-operation has been executed before
kuuseac3a8882019-10-03 10:48:06 +02003899 op_index = self._check_or_add_scale_suboperation(
Dat Le07d2eb02021-06-04 10:18:50 +07003900 db_nslcmop, vnf_index, vnf_config_primitive, primitive_params, 'PRE-SCALE')
tierno7c4e24c2020-05-13 08:41:35 +00003901 if op_index == self.SUBOPERATION_STATUS_SKIP:
kuuseac3a8882019-10-03 10:48:06 +02003902 # Skip sub-operation
3903 result = 'COMPLETED'
3904 result_detail = 'Done'
3905 self.logger.debug(logging_text +
3906 "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
3907 vnf_config_primitive, result, result_detail))
3908 else:
tierno7c4e24c2020-05-13 08:41:35 +00003909 if op_index == self.SUBOPERATION_STATUS_NEW:
kuuseac3a8882019-10-03 10:48:06 +02003910 # New sub-operation: Get index of this sub-operation
3911 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
3912 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
3913 format(vnf_config_primitive))
3914 else:
tierno7c4e24c2020-05-13 08:41:35 +00003915 # retry: Get registered params for this existing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02003916 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
3917 vnf_index = op.get('member_vnf_index')
3918 vnf_config_primitive = op.get('primitive')
3919 primitive_params = op.get('primitive_params')
tierno7c4e24c2020-05-13 08:41:35 +00003920 self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
kuuseac3a8882019-10-03 10:48:06 +02003921 format(vnf_config_primitive))
tierno588547c2020-07-01 15:30:20 +00003922 # Execute the primitive, either with new (first-time) or registered (reintent) args
tiernoa278b842020-07-08 15:33:55 +00003923 ee_descriptor_id = config_primitive.get("execution-environment-ref")
3924 primitive_name = config_primitive.get("execution-environment-primitive",
3925 vnf_config_primitive)
tierno588547c2020-07-01 15:30:20 +00003926 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
3927 member_vnf_index=vnf_index,
3928 vdu_id=None,
tiernoa278b842020-07-08 15:33:55 +00003929 vdu_count_index=None,
3930 ee_descriptor_id=ee_descriptor_id)
kuuseac3a8882019-10-03 10:48:06 +02003931 result, result_detail = await self._ns_execute_primitive(
limonb577d312021-03-15 09:39:43 +01003932 ee_id, primitive_name, primitive_params, vca_type=vca_type)
kuuseac3a8882019-10-03 10:48:06 +02003933 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
3934 vnf_config_primitive, result, result_detail))
3935 # Update operationState = COMPLETED | FAILED
3936 self._update_suboperation_status(
3937 db_nslcmop, op_index, result, result_detail)
3938
tierno59d22d22018-09-25 18:10:19 +02003939 if result == "FAILED":
3940 raise LcmException(result_detail)
tiernod6de1992018-10-11 13:05:52 +02003941 db_nsr_update["config-status"] = old_config_status
3942 scale_process = None
kuuseac3a8882019-10-03 10:48:06 +02003943 # PRE-SCALE END
tierno59d22d22018-09-25 18:10:19 +02003944
tierno2357f4e2020-10-19 16:38:59 +00003945 db_nsr_update["_admin.scaling-group.{}.nb-scale-op".format(admin_scale_index)] = nb_scale_op
3946 db_nsr_update["_admin.scaling-group.{}.time".format(admin_scale_index)] = time()
3947
aktasf0f83a32021-02-12 22:19:10 +03003948 # SCALE-IN VCA - BEGIN
3949 if VCA_scaling_info:
3950 step = db_nslcmop_update["detailed-status"] = \
3951 "Deleting the execution environments"
3952 scale_process = "VCA"
3953 for vdu_info in VCA_scaling_info:
3954 if vdu_info["type"] == "delete":
3955 member_vnf_index = str(vdu_info["member-vnf-index"])
3956 self.logger.debug(logging_text + "vdu info: {}".format(vdu_info))
3957 vdu_id = vdu_info["osm_vdu_id"]
3958 vdu_index = int(vdu_info["vdu_index"])
3959 stage[1] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
3960 member_vnf_index, vdu_id, vdu_index)
3961 stage[2] = step = "Scaling in VCA"
3962 self._write_op_status(
3963 op_id=nslcmop_id,
3964 stage=stage
3965 )
3966 vca_update = db_nsr["_admin"]["deployed"]["VCA"]
3967 config_update = db_nsr["configurationStatus"]
3968 for vca_index, vca in enumerate(vca_update):
3969 if (vca or vca.get("ee_id")) and vca["member-vnf-index"] == member_vnf_index and \
3970 vca["vdu_count_index"] == vdu_index:
3971 if vca.get("vdu_id"):
3972 config_descriptor = get_configuration(db_vnfd, vca.get("vdu_id"))
3973 elif vca.get("kdu_name"):
3974 config_descriptor = get_configuration(db_vnfd, vca.get("kdu_name"))
3975 else:
3976 config_descriptor = get_configuration(db_vnfd, db_vnfd["id"])
3977 operation_params = db_nslcmop.get("operationParams") or {}
3978 exec_terminate_primitives = (not operation_params.get("skip_terminate_primitives") and
3979 vca.get("needed_terminate"))
3980 task = asyncio.ensure_future(asyncio.wait_for(
3981 self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor,
3982 vca_index, destroy_ee=True,
3983 exec_primitives=exec_terminate_primitives,
3984 scaling_in=True), timeout=self.timeout_charm_delete))
3985 # wait before next removal
3986 await asyncio.sleep(30)
3987 tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
3988 del vca_update[vca_index]
3989 del config_update[vca_index]
3990 # wait for pending tasks of terminate primitives
3991 if tasks_dict_info:
3992 self.logger.debug(logging_text +
3993 'Waiting for tasks {}'.format(list(tasks_dict_info.keys())))
3994 error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
3995 min(self.timeout_charm_delete,
3996 self.timeout_ns_terminate),
3997 stage, nslcmop_id)
3998 tasks_dict_info.clear()
3999 if error_list:
4000 raise LcmException("; ".join(error_list))
4001
4002 db_vca_and_config_update = {
4003 "_admin.deployed.VCA": vca_update,
4004 "configurationStatus": config_update
4005 }
4006 self.update_db_2("nsrs", db_nsr["_id"], db_vca_and_config_update)
4007 scale_process = None
4008 # SCALE-IN VCA - END
4009
kuuseac3a8882019-10-03 10:48:06 +02004010 # SCALE RO - BEGIN
tierno59d22d22018-09-25 18:10:19 +02004011 if RO_scaling_info:
tierno9ab95942018-10-10 16:44:22 +02004012 scale_process = "RO"
tierno2357f4e2020-10-19 16:38:59 +00004013 if self.ro_config.get("ng"):
4014 await self._scale_ng_ro(logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage)
tierno2357f4e2020-10-19 16:38:59 +00004015 vdu_scaling_info.pop("vdu-create", None)
4016 vdu_scaling_info.pop("vdu-delete", None)
tierno59d22d22018-09-25 18:10:19 +02004017
tierno9ab95942018-10-10 16:44:22 +02004018 scale_process = None
tierno59d22d22018-09-25 18:10:19 +02004019 if db_nsr_update:
4020 self.update_db_2("nsrs", nsr_id, db_nsr_update)
aktasf0f83a32021-02-12 22:19:10 +03004021 # SCALE RO - END
4022
4023 # SCALE-UP VCA - BEGIN
4024 if VCA_scaling_info:
4025 step = db_nslcmop_update["detailed-status"] = \
4026 "Creating new execution environments"
4027 scale_process = "VCA"
4028 for vdu_info in VCA_scaling_info:
4029 if vdu_info["type"] == "create":
4030 member_vnf_index = str(vdu_info["member-vnf-index"])
4031 self.logger.debug(logging_text + "vdu info: {}".format(vdu_info))
4032 vnfd_id = db_vnfr["vnfd-ref"]
4033 vdu_index = int(vdu_info["vdu_index"])
4034 deploy_params = {"OSM": get_osm_params(db_vnfr)}
4035 if db_vnfr.get("additionalParamsForVnf"):
4036 deploy_params.update(parse_yaml_strings(db_vnfr["additionalParamsForVnf"].copy()))
4037 descriptor_config = get_configuration(db_vnfd, db_vnfd["id"])
4038 if descriptor_config:
4039 vdu_id = None
4040 vdu_name = None
4041 kdu_name = None
4042 self._deploy_n2vc(
4043 logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
4044 db_nsr=db_nsr,
4045 db_vnfr=db_vnfr,
4046 nslcmop_id=nslcmop_id,
4047 nsr_id=nsr_id,
4048 nsi_id=nsi_id,
4049 vnfd_id=vnfd_id,
4050 vdu_id=vdu_id,
4051 kdu_name=kdu_name,
4052 member_vnf_index=member_vnf_index,
4053 vdu_index=vdu_index,
4054 vdu_name=vdu_name,
4055 deploy_params=deploy_params,
4056 descriptor_config=descriptor_config,
4057 base_folder=base_folder,
4058 task_instantiation_info=tasks_dict_info,
4059 stage=stage
4060 )
4061 vdu_id = vdu_info["osm_vdu_id"]
4062 vdur = find_in_list(db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id)
4063 descriptor_config = get_configuration(db_vnfd, vdu_id)
4064 if vdur.get("additionalParams"):
4065 deploy_params_vdu = parse_yaml_strings(vdur["additionalParams"])
4066 else:
4067 deploy_params_vdu = deploy_params
4068 deploy_params_vdu["OSM"] = get_osm_params(db_vnfr, vdu_id, vdu_count_index=vdu_index)
4069 if descriptor_config:
4070 vdu_name = None
4071 kdu_name = None
4072 stage[1] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
4073 member_vnf_index, vdu_id, vdu_index)
4074 stage[2] = step = "Scaling out VCA"
4075 self._write_op_status(
4076 op_id=nslcmop_id,
4077 stage=stage
4078 )
4079 self._deploy_n2vc(
4080 logging_text=logging_text + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
4081 member_vnf_index, vdu_id, vdu_index),
4082 db_nsr=db_nsr,
4083 db_vnfr=db_vnfr,
4084 nslcmop_id=nslcmop_id,
4085 nsr_id=nsr_id,
4086 nsi_id=nsi_id,
4087 vnfd_id=vnfd_id,
4088 vdu_id=vdu_id,
4089 kdu_name=kdu_name,
4090 member_vnf_index=member_vnf_index,
4091 vdu_index=vdu_index,
4092 vdu_name=vdu_name,
4093 deploy_params=deploy_params_vdu,
4094 descriptor_config=descriptor_config,
4095 base_folder=base_folder,
4096 task_instantiation_info=tasks_dict_info,
4097 stage=stage
4098 )
4099 # TODO: scaling for kdu is not implemented yet.
4100 kdu_name = vdu_info["osm_vdu_id"]
4101 descriptor_config = get_configuration(db_vnfd, kdu_name)
4102 if descriptor_config:
4103 vdu_id = None
4104 vdu_index = vdu_index
4105 vdu_name = None
4106 kdur = next(x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name)
4107 deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
4108 if kdur.get("additionalParams"):
4109 deploy_params_kdu = parse_yaml_strings(kdur["additionalParams"])
4110
4111 self._deploy_n2vc(
4112 logging_text=logging_text,
4113 db_nsr=db_nsr,
4114 db_vnfr=db_vnfr,
4115 nslcmop_id=nslcmop_id,
4116 nsr_id=nsr_id,
4117 nsi_id=nsi_id,
4118 vnfd_id=vnfd_id,
4119 vdu_id=vdu_id,
4120 kdu_name=kdu_name,
4121 member_vnf_index=member_vnf_index,
4122 vdu_index=vdu_index,
4123 vdu_name=vdu_name,
4124 deploy_params=deploy_params_kdu,
4125 descriptor_config=descriptor_config,
4126 base_folder=base_folder,
4127 task_instantiation_info=tasks_dict_info,
4128 stage=stage
4129 )
4130 # SCALE-UP VCA - END
4131 scale_process = None
tierno59d22d22018-09-25 18:10:19 +02004132
kuuseac3a8882019-10-03 10:48:06 +02004133 # POST-SCALE BEGIN
tierno59d22d22018-09-25 18:10:19 +02004134 # execute primitive service POST-SCALING
4135 step = "Executing post-scale vnf-config-primitive"
4136 if scaling_descriptor.get("scaling-config-action"):
4137 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
kuuseac3a8882019-10-03 10:48:06 +02004138 if (scaling_config_action.get("trigger") == "post-scale-in" and scaling_type == "SCALE_IN") \
4139 or (scaling_config_action.get("trigger") == "post-scale-out" and scaling_type == "SCALE_OUT"):
tierno59d22d22018-09-25 18:10:19 +02004140 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
4141 step = db_nslcmop_update["detailed-status"] = \
4142 "executing post-scale scaling-config-action '{}'".format(vnf_config_primitive)
tiernoda964822019-01-14 15:53:47 +00004143
tierno589befb2019-05-29 07:06:23 +00004144 vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
tiernoda964822019-01-14 15:53:47 +00004145 if db_vnfr.get("additionalParamsForVnf"):
4146 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
4147
tierno59d22d22018-09-25 18:10:19 +02004148 # look for primitive
garciaale762b6e72021-02-22 18:02:07 -03004149 for config_primitive in (
4150 get_configuration(db_vnfd, db_vnfd["id"]) or {}
4151 ).get("config-primitive", ()):
tierno59d22d22018-09-25 18:10:19 +02004152 if config_primitive["name"] == vnf_config_primitive:
tierno59d22d22018-09-25 18:10:19 +02004153 break
4154 else:
tiernoa278b842020-07-08 15:33:55 +00004155 raise LcmException(
4156 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-"
4157 "action[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:"
4158 "config-primitive".format(scaling_group, vnf_config_primitive))
tierno9ab95942018-10-10 16:44:22 +02004159 scale_process = "VCA"
tiernod6de1992018-10-11 13:05:52 +02004160 db_nsr_update["config-status"] = "configuring post-scaling"
kuuseac3a8882019-10-03 10:48:06 +02004161 primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
tiernod6de1992018-10-11 13:05:52 +02004162
tierno7c4e24c2020-05-13 08:41:35 +00004163 # Post-scale retry check: Check if this sub-operation has been executed before
kuuseac3a8882019-10-03 10:48:06 +02004164 op_index = self._check_or_add_scale_suboperation(
Dat Le07d2eb02021-06-04 10:18:50 +07004165 db_nslcmop, vnf_index, vnf_config_primitive, primitive_params, 'POST-SCALE')
quilesj4cda56b2019-12-05 10:02:20 +00004166 if op_index == self.SUBOPERATION_STATUS_SKIP:
kuuseac3a8882019-10-03 10:48:06 +02004167 # Skip sub-operation
4168 result = 'COMPLETED'
4169 result_detail = 'Done'
4170 self.logger.debug(logging_text +
4171 "vnf_config_primitive={} Skipped sub-operation, result {} {}".
4172 format(vnf_config_primitive, result, result_detail))
4173 else:
quilesj4cda56b2019-12-05 10:02:20 +00004174 if op_index == self.SUBOPERATION_STATUS_NEW:
kuuseac3a8882019-10-03 10:48:06 +02004175 # New sub-operation: Get index of this sub-operation
4176 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
4177 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
4178 format(vnf_config_primitive))
4179 else:
tierno7c4e24c2020-05-13 08:41:35 +00004180 # retry: Get registered params for this existing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02004181 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
4182 vnf_index = op.get('member_vnf_index')
4183 vnf_config_primitive = op.get('primitive')
4184 primitive_params = op.get('primitive_params')
tierno7c4e24c2020-05-13 08:41:35 +00004185 self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
kuuseac3a8882019-10-03 10:48:06 +02004186 format(vnf_config_primitive))
tierno588547c2020-07-01 15:30:20 +00004187 # Execute the primitive, either with new (first-time) or registered (reintent) args
tiernoa278b842020-07-08 15:33:55 +00004188 ee_descriptor_id = config_primitive.get("execution-environment-ref")
4189 primitive_name = config_primitive.get("execution-environment-primitive",
4190 vnf_config_primitive)
tierno588547c2020-07-01 15:30:20 +00004191 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
4192 member_vnf_index=vnf_index,
4193 vdu_id=None,
tiernoa278b842020-07-08 15:33:55 +00004194 vdu_count_index=None,
4195 ee_descriptor_id=ee_descriptor_id)
kuuseac3a8882019-10-03 10:48:06 +02004196 result, result_detail = await self._ns_execute_primitive(
limonb577d312021-03-15 09:39:43 +01004197 ee_id, primitive_name, primitive_params, vca_type=vca_type)
kuuseac3a8882019-10-03 10:48:06 +02004198 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
4199 vnf_config_primitive, result, result_detail))
4200 # Update operationState = COMPLETED | FAILED
4201 self._update_suboperation_status(
4202 db_nslcmop, op_index, result, result_detail)
4203
tierno59d22d22018-09-25 18:10:19 +02004204 if result == "FAILED":
4205 raise LcmException(result_detail)
tiernod6de1992018-10-11 13:05:52 +02004206 db_nsr_update["config-status"] = old_config_status
4207 scale_process = None
kuuseac3a8882019-10-03 10:48:06 +02004208 # POST-SCALE END
tierno59d22d22018-09-25 18:10:19 +02004209
tiernod6de1992018-10-11 13:05:52 +02004210 db_nsr_update["detailed-status"] = "" # "scaled {} {}".format(scaling_group, scaling_type)
ikalyvas02d9e7b2019-05-27 18:16:01 +03004211 db_nsr_update["operational-status"] = "running" if old_operational_status == "failed" \
4212 else old_operational_status
tiernod6de1992018-10-11 13:05:52 +02004213 db_nsr_update["config-status"] = old_config_status
tierno59d22d22018-09-25 18:10:19 +02004214 return
tierno2357f4e2020-10-19 16:38:59 +00004215 except (ROclient.ROClientException, DbException, LcmException, NgRoException) as e:
tierno59d22d22018-09-25 18:10:19 +02004216 self.logger.error(logging_text + "Exit Exception {}".format(e))
4217 exc = e
4218 except asyncio.CancelledError:
4219 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
4220 exc = "Operation was cancelled"
4221 except Exception as e:
4222 exc = traceback.format_exc()
4223 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
4224 finally:
bravof922c4172020-11-24 21:21:43 -03004225 self._write_ns_status(nsr_id=nsr_id, ns_state=None, current_operation="IDLE", current_operation_id=None)
aktasf0f83a32021-02-12 22:19:10 +03004226 if tasks_dict_info:
4227 stage[1] = "Waiting for instantiate pending tasks."
4228 self.logger.debug(logging_text + stage[1])
4229 exc = await self._wait_for_tasks(logging_text, tasks_dict_info, self.timeout_ns_deploy,
4230 stage, nslcmop_id, nsr_id=nsr_id)
tierno59d22d22018-09-25 18:10:19 +02004231 if exc:
tiernoa17d4f42020-04-28 09:59:23 +00004232 db_nslcmop_update["detailed-status"] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
4233 nslcmop_operation_state = "FAILED"
tierno59d22d22018-09-25 18:10:19 +02004234 if db_nsr:
tiernod6de1992018-10-11 13:05:52 +02004235 db_nsr_update["operational-status"] = old_operational_status
4236 db_nsr_update["config-status"] = old_config_status
4237 db_nsr_update["detailed-status"] = ""
4238 if scale_process:
4239 if "VCA" in scale_process:
4240 db_nsr_update["config-status"] = "failed"
4241 if "RO" in scale_process:
4242 db_nsr_update["operational-status"] = "failed"
4243 db_nsr_update["detailed-status"] = "FAILED scaling nslcmop={} {}: {}".format(nslcmop_id, step,
4244 exc)
tiernoa17d4f42020-04-28 09:59:23 +00004245 else:
4246 error_description_nslcmop = None
4247 nslcmop_operation_state = "COMPLETED"
4248 db_nslcmop_update["detailed-status"] = "Done"
quilesj4cda56b2019-12-05 10:02:20 +00004249
bravof922c4172020-11-24 21:21:43 -03004250 self._write_op_status(op_id=nslcmop_id, stage="", error_message=error_description_nslcmop,
4251 operation_state=nslcmop_operation_state, other_update=db_nslcmop_update)
tiernoa17d4f42020-04-28 09:59:23 +00004252 if db_nsr:
bravof922c4172020-11-24 21:21:43 -03004253 self._write_ns_status(nsr_id=nsr_id, ns_state=None, current_operation="IDLE",
4254 current_operation_id=None, other_update=db_nsr_update)
tiernoa17d4f42020-04-28 09:59:23 +00004255
tierno59d22d22018-09-25 18:10:19 +02004256 if nslcmop_operation_state:
4257 try:
bravof922c4172020-11-24 21:21:43 -03004258 msg = {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id, "operationState": nslcmop_operation_state}
4259 await self.msg.aiowrite("ns", "scaled", msg, loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02004260 except Exception as e:
4261 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
4262 self.logger.debug(logging_text + "Exit")
4263 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
tiernob996d942020-07-03 14:52:28 +00004264
tierno2357f4e2020-10-19 16:38:59 +00004265 async def _scale_ng_ro(self, logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage):
4266 nsr_id = db_nslcmop["nsInstanceId"]
4267 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
4268 db_vnfrs = {}
4269
4270 # read from db: vnfd's for every vnf
bravof832f8992020-12-07 12:57:31 -03004271 db_vnfds = []
tierno2357f4e2020-10-19 16:38:59 +00004272
4273 # for each vnf in ns, read vnfd
4274 for vnfr in self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id}):
4275 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
4276 vnfd_id = vnfr["vnfd-id"] # vnfd uuid for this vnf
tierno2357f4e2020-10-19 16:38:59 +00004277 # if we haven't this vnfd, read it from db
bravof832f8992020-12-07 12:57:31 -03004278 if not find_in_list(db_vnfds, lambda a_vnfd: a_vnfd["id"] == vnfd_id):
tierno2357f4e2020-10-19 16:38:59 +00004279 # read from db
4280 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
bravof832f8992020-12-07 12:57:31 -03004281 db_vnfds.append(vnfd)
tierno2357f4e2020-10-19 16:38:59 +00004282 n2vc_key = self.n2vc.get_public_key()
4283 n2vc_key_list = [n2vc_key]
4284 self.scale_vnfr(db_vnfr, vdu_scaling_info.get("vdu-create"), vdu_scaling_info.get("vdu-delete"),
4285 mark_delete=True)
4286 # db_vnfr has been updated, update db_vnfrs to use it
4287 db_vnfrs[db_vnfr["member-vnf-index-ref"]] = db_vnfr
4288 await self._instantiate_ng_ro(logging_text, nsr_id, db_nsd, db_nsr, db_nslcmop, db_vnfrs,
bravof922c4172020-11-24 21:21:43 -03004289 db_vnfds, n2vc_key_list, stage=stage, start_deploy=time(),
tierno2357f4e2020-10-19 16:38:59 +00004290 timeout_ns_deploy=self.timeout_ns_deploy)
4291 if vdu_scaling_info.get("vdu-delete"):
4292 self.scale_vnfr(db_vnfr, None, vdu_scaling_info["vdu-delete"], mark_delete=False)
4293
tiernob996d942020-07-03 14:52:28 +00004294 async def add_prometheus_metrics(self, ee_id, artifact_path, ee_config_descriptor, vnfr_id, nsr_id, target_ip):
4295 if not self.prometheus:
4296 return
4297 # look if exist a file called 'prometheus*.j2' and
4298 artifact_content = self.fs.dir_ls(artifact_path)
4299 job_file = next((f for f in artifact_content if f.startswith("prometheus") and f.endswith(".j2")), None)
4300 if not job_file:
4301 return
4302 with self.fs.file_open((artifact_path, job_file), "r") as f:
4303 job_data = f.read()
4304
4305 # TODO get_service
4306 _, _, service = ee_id.partition(".") # remove prefix "namespace."
4307 host_name = "{}-{}".format(service, ee_config_descriptor["metric-service"])
4308 host_port = "80"
4309 vnfr_id = vnfr_id.replace("-", "")
4310 variables = {
4311 "JOB_NAME": vnfr_id,
4312 "TARGET_IP": target_ip,
4313 "EXPORTER_POD_IP": host_name,
4314 "EXPORTER_POD_PORT": host_port,
4315 }
4316 job_list = self.prometheus.parse_job(job_data, variables)
4317 # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
4318 for job in job_list:
4319 if not isinstance(job.get("job_name"), str) or vnfr_id not in job["job_name"]:
4320 job["job_name"] = vnfr_id + "_" + str(randint(1, 10000))
4321 job["nsr_id"] = nsr_id
4322 job_dict = {jl["job_name"]: jl for jl in job_list}
4323 if await self.prometheus.update(job_dict):
4324 return list(job_dict.keys())
David Garciaaae391f2020-11-09 11:12:54 +01004325
4326 def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
4327 """
4328 Get VCA Cloud and VCA Cloud Credentials for the VIM account
4329
4330 :param: vim_account_id: VIM Account ID
4331
4332 :return: (cloud_name, cloud_credential)
4333 """
bravof922c4172020-11-24 21:21:43 -03004334 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
David Garciaaae391f2020-11-09 11:12:54 +01004335 return config.get("vca_cloud"), config.get("vca_cloud_credential")
4336
4337 def get_vca_k8s_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
4338 """
4339 Get VCA K8s Cloud and VCA K8s Cloud Credentials for the VIM account
4340
4341 :param: vim_account_id: VIM Account ID
4342
4343 :return: (cloud_name, cloud_credential)
4344 """
bravof922c4172020-11-24 21:21:43 -03004345 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
David Garciaaae391f2020-11-09 11:12:54 +01004346 return config.get("vca_k8s_cloud"), config.get("vca_k8s_cloud_credential")