blob: 5b0b7b0c5cbdc8ef5a7469d5d3a1fbb25fc61e42 [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
bravofe5a31bc2021-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, \
bravofe5a31bc2021-02-17 19:09:12 -030033 get_kdu_list, get_virtual_link_profiles, get_vdu, get_configuration, \
bravof9a256db2021-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
aktas13251562021-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,
ksaikiranr656b6dd2021-02-19 10:25:18 +0530140 on_update_db=self._on_update_k8s_db,
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))
quilesj3655ae02019-12-12 16:08:35 +0000211 try:
212
213 nsr_id = filter.get('_id')
214
215 # read ns record from database
216 nsr = self.db.get_one(table='nsrs', q_filter=filter)
217 current_ns_status = nsr.get('nsState')
218
219 # get vca status for NS
quilesj69a722c2020-01-09 08:30:17 +0000220 status_dict = await self.n2vc.get_status(namespace='.' + nsr_id, yaml_format=False)
quilesj3655ae02019-12-12 16:08:35 +0000221
222 # vcaStatus
223 db_dict = dict()
224 db_dict['vcaStatus'] = status_dict
ksaikiranr5b2c45a2021-01-27 22:13:22 +0530225 await self.n2vc.update_vca_status(db_dict['vcaStatus'])
quilesj3655ae02019-12-12 16:08:35 +0000226
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
ksaikiranr656b6dd2021-02-19 10:25:18 +0530292 async def _on_update_k8s_db(self, cluster_uuid, kdu_instance, filter=None):
293 """
294 Updating vca status in NSR record
295 :param cluster_uuid: UUID of a k8s cluster
296 :param kdu_instance: The unique name of the KDU instance
297 :param filter: To get nsr_id
298 :return: none
299 """
300
301 # self.logger.debug("_on_update_k8s_db(cluster_uuid={}, kdu_instance={}, filter={}"
302 # .format(cluster_uuid, kdu_instance, filter))
303
304 try:
305 nsr_id = filter.get('_id')
306
307 # get vca status for NS
308 vca_status = await self.k8sclusterjuju.status_kdu(cluster_uuid,
309 kdu_instance,
310 complete_status=True,
311 yaml_format=False)
312 # vcaStatus
313 db_dict = dict()
314 db_dict['vcaStatus'] = {nsr_id: vca_status}
315
316 await self.k8sclusterjuju.update_vca_status(db_dict['vcaStatus'], kdu_instance)
317
318 # write to database
319 self.update_db_2("nsrs", nsr_id, db_dict)
320
321 except (asyncio.CancelledError, asyncio.TimeoutError):
322 raise
323 except Exception as e:
324 self.logger.warn('Error updating NS state for ns={}: {}'.format(nsr_id, e))
325
tierno72ef84f2020-10-06 08:22:07 +0000326 @staticmethod
327 def _parse_cloud_init(cloud_init_text, additional_params, vnfd_id, vdu_id):
328 try:
329 env = Environment(undefined=StrictUndefined)
330 template = env.from_string(cloud_init_text)
331 return template.render(additional_params or {})
332 except UndefinedError as e:
333 raise LcmException("Variable {} at vnfd[id={}]:vdu[id={}]:cloud-init/cloud-init-"
334 "file, must be provided in the instantiation parameters inside the "
335 "'additionalParamsForVnf/Vdu' block".format(e, vnfd_id, vdu_id))
336 except (TemplateError, TemplateNotFound) as e:
337 raise LcmException("Error parsing Jinja2 to cloud-init content at vnfd[id={}]:vdu[id={}]: {}".
338 format(vnfd_id, vdu_id, e))
339
bravof922c4172020-11-24 21:21:43 -0300340 def _get_vdu_cloud_init_content(self, vdu, vnfd):
341 cloud_init_content = cloud_init_file = None
tierno72ef84f2020-10-06 08:22:07 +0000342 try:
tierno72ef84f2020-10-06 08:22:07 +0000343 if vdu.get("cloud-init-file"):
344 base_folder = vnfd["_admin"]["storage"]
345 cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
346 vdu["cloud-init-file"])
347 with self.fs.file_open(cloud_init_file, "r") as ci_file:
348 cloud_init_content = ci_file.read()
349 elif vdu.get("cloud-init"):
350 cloud_init_content = vdu["cloud-init"]
351
352 return cloud_init_content
353 except FsException as e:
354 raise LcmException("Error reading vnfd[id={}]:vdu[id={}]:cloud-init-file={}: {}".
355 format(vnfd["id"], vdu["id"], cloud_init_file, e))
356
tierno72ef84f2020-10-06 08:22:07 +0000357 def _get_vdu_additional_params(self, db_vnfr, vdu_id):
358 vdur = next(vdur for vdur in db_vnfr.get("vdur") if vdu_id == vdur["vdu-id-ref"])
359 additional_params = vdur.get("additionalParams")
bravof922c4172020-11-24 21:21:43 -0300360 return parse_yaml_strings(additional_params)
tierno72ef84f2020-10-06 08:22:07 +0000361
gcalvino35be9152018-12-20 09:33:12 +0100362 def vnfd2RO(self, vnfd, new_id=None, additionalParams=None, nsrId=None):
tierno59d22d22018-09-25 18:10:19 +0200363 """
364 Converts creates a new vnfd descriptor for RO base on input OSM IM vnfd
365 :param vnfd: input vnfd
366 :param new_id: overrides vnf id if provided
tierno8a518872018-12-21 13:42:14 +0000367 :param additionalParams: Instantiation params for VNFs provided
gcalvino35be9152018-12-20 09:33:12 +0100368 :param nsrId: Id of the NSR
tierno59d22d22018-09-25 18:10:19 +0200369 :return: copy of vnfd
370 """
tierno72ef84f2020-10-06 08:22:07 +0000371 vnfd_RO = deepcopy(vnfd)
372 # remove unused by RO configuration, monitoring, scaling and internal keys
373 vnfd_RO.pop("_id", None)
374 vnfd_RO.pop("_admin", None)
tierno72ef84f2020-10-06 08:22:07 +0000375 vnfd_RO.pop("monitoring-param", None)
376 vnfd_RO.pop("scaling-group-descriptor", None)
377 vnfd_RO.pop("kdu", None)
378 vnfd_RO.pop("k8s-cluster", None)
379 if new_id:
380 vnfd_RO["id"] = new_id
tierno8a518872018-12-21 13:42:14 +0000381
tierno72ef84f2020-10-06 08:22:07 +0000382 # parse cloud-init or cloud-init-file with the provided variables using Jinja2
383 for vdu in get_iterable(vnfd_RO, "vdu"):
384 vdu.pop("cloud-init-file", None)
385 vdu.pop("cloud-init", None)
386 return vnfd_RO
tierno59d22d22018-09-25 18:10:19 +0200387
tierno2357f4e2020-10-19 16:38:59 +0000388 @staticmethod
389 def ip_profile_2_RO(ip_profile):
390 RO_ip_profile = deepcopy(ip_profile)
391 if "dns-server" in RO_ip_profile:
392 if isinstance(RO_ip_profile["dns-server"], list):
393 RO_ip_profile["dns-address"] = []
394 for ds in RO_ip_profile.pop("dns-server"):
395 RO_ip_profile["dns-address"].append(ds['address'])
396 else:
397 RO_ip_profile["dns-address"] = RO_ip_profile.pop("dns-server")
398 if RO_ip_profile.get("ip-version") == "ipv4":
399 RO_ip_profile["ip-version"] = "IPv4"
400 if RO_ip_profile.get("ip-version") == "ipv6":
401 RO_ip_profile["ip-version"] = "IPv6"
402 if "dhcp-params" in RO_ip_profile:
403 RO_ip_profile["dhcp"] = RO_ip_profile.pop("dhcp-params")
404 return RO_ip_profile
405
bravof922c4172020-11-24 21:21:43 -0300406 def _get_ro_vim_id_for_vim_account(self, vim_account):
407 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account})
408 if db_vim["_admin"]["operationalState"] != "ENABLED":
409 raise LcmException("VIM={} is not available. operationalState={}".format(
410 vim_account, db_vim["_admin"]["operationalState"]))
411 RO_vim_id = db_vim["_admin"]["deployed"]["RO"]
412 return RO_vim_id
tierno59d22d22018-09-25 18:10:19 +0200413
bravof922c4172020-11-24 21:21:43 -0300414 def get_ro_wim_id_for_wim_account(self, wim_account):
415 if isinstance(wim_account, str):
416 db_wim = self.db.get_one("wim_accounts", {"_id": wim_account})
417 if db_wim["_admin"]["operationalState"] != "ENABLED":
418 raise LcmException("WIM={} is not available. operationalState={}".format(
419 wim_account, db_wim["_admin"]["operationalState"]))
420 RO_wim_id = db_wim["_admin"]["deployed"]["RO-account"]
421 return RO_wim_id
422 else:
423 return wim_account
tierno59d22d22018-09-25 18:10:19 +0200424
tierno2357f4e2020-10-19 16:38:59 +0000425 def scale_vnfr(self, db_vnfr, vdu_create=None, vdu_delete=None, mark_delete=False):
tierno27246d82018-09-27 15:59:09 +0200426
tierno2357f4e2020-10-19 16:38:59 +0000427 db_vdu_push_list = []
428 db_update = {"_admin.modified": time()}
429 if vdu_create:
430 for vdu_id, vdu_count in vdu_create.items():
431 vdur = next((vdur for vdur in reversed(db_vnfr["vdur"]) if vdur["vdu-id-ref"] == vdu_id), None)
432 if not vdur:
433 raise LcmException("Error scaling OUT VNFR for {}. There is not any existing vnfr. Scaled to 0?".
434 format(vdu_id))
435
436 for count in range(vdu_count):
437 vdur_copy = deepcopy(vdur)
438 vdur_copy["status"] = "BUILD"
439 vdur_copy["status-detailed"] = None
440 vdur_copy["ip-address"]: None
tierno683eb392020-09-25 12:33:15 +0000441 vdur_copy["_id"] = str(uuid4())
tierno2357f4e2020-10-19 16:38:59 +0000442 vdur_copy["count-index"] += count + 1
443 vdur_copy["id"] = "{}-{}".format(vdur_copy["vdu-id-ref"], vdur_copy["count-index"])
444 vdur_copy.pop("vim_info", None)
445 for iface in vdur_copy["interfaces"]:
446 if iface.get("fixed-ip"):
447 iface["ip-address"] = self.increment_ip_mac(iface["ip-address"], count+1)
448 else:
449 iface.pop("ip-address", None)
450 if iface.get("fixed-mac"):
451 iface["mac-address"] = self.increment_ip_mac(iface["mac-address"], count+1)
452 else:
453 iface.pop("mac-address", None)
454 iface.pop("mgmt_vnf", None) # only first vdu can be managment of vnf
455 db_vdu_push_list.append(vdur_copy)
456 # self.logger.debug("scale out, adding vdu={}".format(vdur_copy))
tierno27246d82018-09-27 15:59:09 +0200457 if vdu_delete:
tierno2357f4e2020-10-19 16:38:59 +0000458 for vdu_id, vdu_count in vdu_delete.items():
459 if mark_delete:
460 indexes_to_delete = [iv[0] for iv in enumerate(db_vnfr["vdur"]) if iv[1]["vdu-id-ref"] == vdu_id]
461 db_update.update({"vdur.{}.status".format(i): "DELETING" for i in indexes_to_delete[-vdu_count:]})
462 else:
463 # it must be deleted one by one because common.db does not allow otherwise
464 vdus_to_delete = [v for v in reversed(db_vnfr["vdur"]) if v["vdu-id-ref"] == vdu_id]
465 for vdu in vdus_to_delete[:vdu_count]:
466 self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, None, pull={"vdur": {"_id": vdu["_id"]}})
467 db_push = {"vdur": db_vdu_push_list} if db_vdu_push_list else None
468 self.db.set_one("vnfrs", {"_id": db_vnfr["_id"]}, db_update, push_list=db_push)
469 # modify passed dictionary db_vnfr
470 db_vnfr_ = self.db.get_one("vnfrs", {"_id": db_vnfr["_id"]})
471 db_vnfr["vdur"] = db_vnfr_["vdur"]
tierno27246d82018-09-27 15:59:09 +0200472
tiernof578e552018-11-08 19:07:20 +0100473 def ns_update_nsr(self, ns_update_nsr, db_nsr, nsr_desc_RO):
474 """
475 Updates database nsr with the RO info for the created vld
476 :param ns_update_nsr: dictionary to be filled with the updated info
477 :param db_nsr: content of db_nsr. This is also modified
478 :param nsr_desc_RO: nsr descriptor from RO
479 :return: Nothing, LcmException is raised on errors
480 """
481
482 for vld_index, vld in enumerate(get_iterable(db_nsr, "vld")):
483 for net_RO in get_iterable(nsr_desc_RO, "nets"):
484 if vld["id"] != net_RO.get("ns_net_osm_id"):
485 continue
486 vld["vim-id"] = net_RO.get("vim_net_id")
487 vld["name"] = net_RO.get("vim_name")
488 vld["status"] = net_RO.get("status")
489 vld["status-detailed"] = net_RO.get("error_msg")
490 ns_update_nsr["vld.{}".format(vld_index)] = vld
491 break
492 else:
493 raise LcmException("ns_update_nsr: Not found vld={} at RO info".format(vld["id"]))
494
tiernoe876f672020-02-13 14:34:48 +0000495 def set_vnfr_at_error(self, db_vnfrs, error_text):
496 try:
497 for db_vnfr in db_vnfrs.values():
498 vnfr_update = {"status": "ERROR"}
499 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
500 if "status" not in vdur:
501 vdur["status"] = "ERROR"
502 vnfr_update["vdur.{}.status".format(vdu_index)] = "ERROR"
503 if error_text:
504 vdur["status-detailed"] = str(error_text)
505 vnfr_update["vdur.{}.status-detailed".format(vdu_index)] = "ERROR"
506 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
507 except DbException as e:
508 self.logger.error("Cannot update vnf. {}".format(e))
509
tierno59d22d22018-09-25 18:10:19 +0200510 def ns_update_vnfr(self, db_vnfrs, nsr_desc_RO):
511 """
512 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 +0200513 :param db_vnfrs: dictionary with member-vnf-index: vnfr-content
514 :param nsr_desc_RO: nsr descriptor from RO
515 :return: Nothing, LcmException is raised on errors
tierno59d22d22018-09-25 18:10:19 +0200516 """
517 for vnf_index, db_vnfr in db_vnfrs.items():
518 for vnf_RO in nsr_desc_RO["vnfs"]:
tierno27246d82018-09-27 15:59:09 +0200519 if vnf_RO["member_vnf_index"] != vnf_index:
520 continue
521 vnfr_update = {}
tiernof578e552018-11-08 19:07:20 +0100522 if vnf_RO.get("ip_address"):
tierno1674de82019-04-09 13:03:14 +0000523 db_vnfr["ip-address"] = vnfr_update["ip-address"] = vnf_RO["ip_address"].split(";")[0]
tiernof578e552018-11-08 19:07:20 +0100524 elif not db_vnfr.get("ip-address"):
tierno0ec0c272020-02-19 17:43:01 +0000525 if db_vnfr.get("vdur"): # if not VDUs, there is not ip_address
526 raise LcmExceptionNoMgmtIP("ns member_vnf_index '{}' has no IP address".format(vnf_index))
tierno59d22d22018-09-25 18:10:19 +0200527
tierno27246d82018-09-27 15:59:09 +0200528 for vdu_index, vdur in enumerate(get_iterable(db_vnfr, "vdur")):
529 vdur_RO_count_index = 0
530 if vdur.get("pdu-type"):
531 continue
532 for vdur_RO in get_iterable(vnf_RO, "vms"):
533 if vdur["vdu-id-ref"] != vdur_RO["vdu_osm_id"]:
534 continue
535 if vdur["count-index"] != vdur_RO_count_index:
536 vdur_RO_count_index += 1
537 continue
538 vdur["vim-id"] = vdur_RO.get("vim_vm_id")
tierno1674de82019-04-09 13:03:14 +0000539 if vdur_RO.get("ip_address"):
540 vdur["ip-address"] = vdur_RO["ip_address"].split(";")[0]
tierno274ed572019-04-04 13:33:27 +0000541 else:
542 vdur["ip-address"] = None
tierno27246d82018-09-27 15:59:09 +0200543 vdur["vdu-id-ref"] = vdur_RO.get("vdu_osm_id")
544 vdur["name"] = vdur_RO.get("vim_name")
545 vdur["status"] = vdur_RO.get("status")
546 vdur["status-detailed"] = vdur_RO.get("error_msg")
547 for ifacer in get_iterable(vdur, "interfaces"):
548 for interface_RO in get_iterable(vdur_RO, "interfaces"):
549 if ifacer["name"] == interface_RO.get("internal_name"):
550 ifacer["ip-address"] = interface_RO.get("ip_address")
551 ifacer["mac-address"] = interface_RO.get("mac_address")
552 break
553 else:
554 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} interface={} "
quilesj7e13aeb2019-10-08 13:34:55 +0200555 "from VIM info"
556 .format(vnf_index, vdur["vdu-id-ref"], ifacer["name"]))
tierno27246d82018-09-27 15:59:09 +0200557 vnfr_update["vdur.{}".format(vdu_index)] = vdur
558 break
559 else:
tierno15b1cf12019-08-29 13:21:40 +0000560 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vdur={} count_index={} from "
561 "VIM info".format(vnf_index, vdur["vdu-id-ref"], vdur["count-index"]))
tiernof578e552018-11-08 19:07:20 +0100562
563 for vld_index, vld in enumerate(get_iterable(db_vnfr, "vld")):
564 for net_RO in get_iterable(nsr_desc_RO, "nets"):
565 if vld["id"] != net_RO.get("vnf_net_osm_id"):
566 continue
567 vld["vim-id"] = net_RO.get("vim_net_id")
568 vld["name"] = net_RO.get("vim_name")
569 vld["status"] = net_RO.get("status")
570 vld["status-detailed"] = net_RO.get("error_msg")
571 vnfr_update["vld.{}".format(vld_index)] = vld
572 break
573 else:
tierno15b1cf12019-08-29 13:21:40 +0000574 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} vld={} from VIM info".format(
tiernof578e552018-11-08 19:07:20 +0100575 vnf_index, vld["id"]))
576
tierno27246d82018-09-27 15:59:09 +0200577 self.update_db_2("vnfrs", db_vnfr["_id"], vnfr_update)
578 break
tierno59d22d22018-09-25 18:10:19 +0200579
580 else:
tierno15b1cf12019-08-29 13:21:40 +0000581 raise LcmException("ns_update_vnfr: Not found member_vnf_index={} from VIM info".format(vnf_index))
tierno59d22d22018-09-25 18:10:19 +0200582
tierno5ee02052019-12-05 19:55:02 +0000583 def _get_ns_config_info(self, nsr_id):
tiernoc3f2a822019-11-05 13:45:04 +0000584 """
585 Generates a mapping between vnf,vdu elements and the N2VC id
tierno5ee02052019-12-05 19:55:02 +0000586 :param nsr_id: id of nsr to get last database _admin.deployed.VCA that contains this list
tiernoc3f2a822019-11-05 13:45:04 +0000587 :return: a dictionary with {osm-config-mapping: {}} where its element contains:
588 "<member-vnf-index>": <N2VC-id> for a vnf configuration, or
589 "<member-vnf-index>.<vdu.id>.<vdu replica(0, 1,..)>": <N2VC-id> for a vdu configuration
590 """
tierno5ee02052019-12-05 19:55:02 +0000591 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
592 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
tiernoc3f2a822019-11-05 13:45:04 +0000593 mapping = {}
594 ns_config_info = {"osm-config-mapping": mapping}
595 for vca in vca_deployed_list:
596 if not vca["member-vnf-index"]:
597 continue
598 if not vca["vdu_id"]:
599 mapping[vca["member-vnf-index"]] = vca["application"]
600 else:
601 mapping["{}.{}.{}".format(vca["member-vnf-index"], vca["vdu_id"], vca["vdu_count_index"])] =\
602 vca["application"]
603 return ns_config_info
604
bravof922c4172020-11-24 21:21:43 -0300605 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 +0000606 n2vc_key_list, stage, start_deploy, timeout_ns_deploy):
tierno2357f4e2020-10-19 16:38:59 +0000607
608 db_vims = {}
609
610 def get_vim_account(vim_account_id):
611 nonlocal db_vims
612 if vim_account_id in db_vims:
613 return db_vims[vim_account_id]
614 db_vim = self.db.get_one("vim_accounts", {"_id": vim_account_id})
615 db_vims[vim_account_id] = db_vim
616 return db_vim
617
618 # modify target_vld info with instantiation parameters
619 def parse_vld_instantiation_params(target_vim, target_vld, vld_params, target_sdn):
620 if vld_params.get("ip-profile"):
621 target_vld["vim_info"][target_vim]["ip_profile"] = vld_params["ip-profile"]
622 if vld_params.get("provider-network"):
623 target_vld["vim_info"][target_vim]["provider_network"] = vld_params["provider-network"]
624 if "sdn-ports" in vld_params["provider-network"] and target_sdn:
625 target_vld["vim_info"][target_sdn]["sdn-ports"] = vld_params["provider-network"]["sdn-ports"]
626 if vld_params.get("wimAccountId"):
627 target_wim = "wim:{}".format(vld_params["wimAccountId"])
628 target_vld["vim_info"][target_wim] = {}
629 for param in ("vim-network-name", "vim-network-id"):
630 if vld_params.get(param):
631 if isinstance(vld_params[param], dict):
garciaale04694c62021-03-02 10:49:28 -0300632 for vim, vim_net in vld_params[param].items():
bravof922c4172020-11-24 21:21:43 -0300633 other_target_vim = "vim:" + vim
634 populate_dict(target_vld["vim_info"], (other_target_vim, param.replace("-", "_")), vim_net)
tierno2357f4e2020-10-19 16:38:59 +0000635 else: # isinstance str
636 target_vld["vim_info"][target_vim][param.replace("-", "_")] = vld_params[param]
bravof922c4172020-11-24 21:21:43 -0300637 if vld_params.get("common_id"):
638 target_vld["common_id"] = vld_params.get("common_id")
tierno2357f4e2020-10-19 16:38:59 +0000639
tierno69f0d382020-05-07 13:08:09 +0000640 nslcmop_id = db_nslcmop["_id"]
641 target = {
642 "name": db_nsr["name"],
643 "ns": {"vld": []},
644 "vnf": [],
645 "image": deepcopy(db_nsr["image"]),
646 "flavor": deepcopy(db_nsr["flavor"]),
647 "action_id": nslcmop_id,
tierno2357f4e2020-10-19 16:38:59 +0000648 "cloud_init_content": {},
tierno69f0d382020-05-07 13:08:09 +0000649 }
650 for image in target["image"]:
tierno2357f4e2020-10-19 16:38:59 +0000651 image["vim_info"] = {}
tierno69f0d382020-05-07 13:08:09 +0000652 for flavor in target["flavor"]:
tierno2357f4e2020-10-19 16:38:59 +0000653 flavor["vim_info"] = {}
tierno69f0d382020-05-07 13:08:09 +0000654
tierno2357f4e2020-10-19 16:38:59 +0000655 if db_nslcmop.get("lcmOperationType") != "instantiate":
656 # get parameters of instantiation:
657 db_nslcmop_instantiate = self.db.get_list("nslcmops", {"nsInstanceId": db_nslcmop["nsInstanceId"],
658 "lcmOperationType": "instantiate"})[-1]
659 ns_params = db_nslcmop_instantiate.get("operationParams")
660 else:
661 ns_params = db_nslcmop.get("operationParams")
bravof922c4172020-11-24 21:21:43 -0300662 ssh_keys_instantiation = ns_params.get("ssh_keys") or []
663 ssh_keys_all = ssh_keys_instantiation + (n2vc_key_list or [])
tierno69f0d382020-05-07 13:08:09 +0000664
665 cp2target = {}
tierno2357f4e2020-10-19 16:38:59 +0000666 for vld_index, vld in enumerate(db_nsr.get("vld")):
667 target_vim = "vim:{}".format(ns_params["vimAccountId"])
668 target_vld = {
669 "id": vld["id"],
670 "name": vld["name"],
671 "mgmt-network": vld.get("mgmt-network", False),
672 "type": vld.get("type"),
673 "vim_info": {
bravof922c4172020-11-24 21:21:43 -0300674 target_vim: {
675 "vim_network_name": vld.get("vim-network-name"),
676 "vim_account_id": ns_params["vimAccountId"]
677 }
tierno2357f4e2020-10-19 16:38:59 +0000678 }
679 }
680 # check if this network needs SDN assist
tierno2357f4e2020-10-19 16:38:59 +0000681 if vld.get("pci-interfaces"):
garciadeblasa5ae90b2021-02-12 11:26:46 +0000682 db_vim = get_vim_account(ns_params["vimAccountId"])
tierno2357f4e2020-10-19 16:38:59 +0000683 sdnc_id = db_vim["config"].get("sdn-controller")
684 if sdnc_id:
garciadeblasa5ae90b2021-02-12 11:26:46 +0000685 sdn_vld = "nsrs:{}:vld.{}".format(nsr_id, vld["id"])
686 target_sdn = "sdn:{}".format(sdnc_id)
687 target_vld["vim_info"][target_sdn] = {
688 "sdn": True, "target_vim": target_vim, "vlds": [sdn_vld], "type": vld.get("type")}
tierno2357f4e2020-10-19 16:38:59 +0000689
bravof922c4172020-11-24 21:21:43 -0300690 nsd_vnf_profiles = get_vnf_profiles(nsd)
691 for nsd_vnf_profile in nsd_vnf_profiles:
692 for cp in nsd_vnf_profile["virtual-link-connectivity"]:
693 if cp["virtual-link-profile-id"] == vld["id"]:
694 cp2target["member_vnf:{}.{}".format(
695 cp["constituent-cpd-id"][0]["constituent-base-element-id"],
696 cp["constituent-cpd-id"][0]["constituent-cpd-id"]
697 )] = "nsrs:{}:vld.{}".format(nsr_id, vld_index)
tierno2357f4e2020-10-19 16:38:59 +0000698
699 # check at nsd descriptor, if there is an ip-profile
700 vld_params = {}
bravof922c4172020-11-24 21:21:43 -0300701 virtual_link_profiles = get_virtual_link_profiles(nsd)
702
703 for vlp in virtual_link_profiles:
704 ip_profile = find_in_list(nsd["ip-profiles"],
705 lambda profile: profile["name"] == vlp["ip-profile-ref"])
tierno2357f4e2020-10-19 16:38:59 +0000706 vld_params["ip-profile"] = ip_profile["ip-profile-params"]
707 # update vld_params with instantiation params
bravof922c4172020-11-24 21:21:43 -0300708 vld_instantiation_params = find_in_list(get_iterable(ns_params, "vld"),
709 lambda a_vld: a_vld["name"] in (vld["name"], vld["id"]))
tierno2357f4e2020-10-19 16:38:59 +0000710 if vld_instantiation_params:
711 vld_params.update(vld_instantiation_params)
bravof922c4172020-11-24 21:21:43 -0300712 parse_vld_instantiation_params(target_vim, target_vld, vld_params, None)
tierno69f0d382020-05-07 13:08:09 +0000713 target["ns"]["vld"].append(target_vld)
bravof922c4172020-11-24 21:21:43 -0300714
tierno69f0d382020-05-07 13:08:09 +0000715 for vnfr in db_vnfrs.values():
bravof922c4172020-11-24 21:21:43 -0300716 vnfd = find_in_list(db_vnfds, lambda db_vnf: db_vnf["id"] == vnfr["vnfd-ref"])
717 vnf_params = find_in_list(get_iterable(ns_params, "vnf"),
718 lambda a_vnf: a_vnf["member-vnf-index"] == vnfr["member-vnf-index-ref"])
tierno69f0d382020-05-07 13:08:09 +0000719 target_vnf = deepcopy(vnfr)
tierno2357f4e2020-10-19 16:38:59 +0000720 target_vim = "vim:{}".format(vnfr["vim-account-id"])
tierno69f0d382020-05-07 13:08:09 +0000721 for vld in target_vnf.get("vld", ()):
tierno2357f4e2020-10-19 16:38:59 +0000722 # check if connected to a ns.vld, to fill target'
bravof922c4172020-11-24 21:21:43 -0300723 vnf_cp = find_in_list(vnfd.get("int-virtual-link-desc", ()),
724 lambda cpd: cpd.get("id") == vld["id"])
tierno69f0d382020-05-07 13:08:09 +0000725 if vnf_cp:
726 ns_cp = "member_vnf:{}.{}".format(vnfr["member-vnf-index-ref"], vnf_cp["id"])
727 if cp2target.get(ns_cp):
728 vld["target"] = cp2target[ns_cp]
bravof922c4172020-11-24 21:21:43 -0300729
730 vld["vim_info"] = {target_vim: {"vim_network_name": vld.get("vim-network-name")}}
tierno2357f4e2020-10-19 16:38:59 +0000731 # check if this network needs SDN assist
732 target_sdn = None
733 if vld.get("pci-interfaces"):
734 db_vim = get_vim_account(vnfr["vim-account-id"])
735 sdnc_id = db_vim["config"].get("sdn-controller")
736 if sdnc_id:
737 sdn_vld = "vnfrs:{}:vld.{}".format(target_vnf["_id"], vld["id"])
738 target_sdn = "sdn:{}".format(sdnc_id)
739 vld["vim_info"][target_sdn] = {
740 "sdn": True, "target_vim": target_vim, "vlds": [sdn_vld], "type": vld.get("type")}
tierno69f0d382020-05-07 13:08:09 +0000741
tierno2357f4e2020-10-19 16:38:59 +0000742 # check at vnfd descriptor, if there is an ip-profile
743 vld_params = {}
bravof922c4172020-11-24 21:21:43 -0300744 vnfd_vlp = find_in_list(
745 get_virtual_link_profiles(vnfd),
746 lambda a_link_profile: a_link_profile["id"] == vld["id"]
747 )
748 if vnfd_vlp and vnfd_vlp.get("virtual-link-protocol-data") and \
749 vnfd_vlp["virtual-link-protocol-data"].get("l3-protocol-data"):
750 ip_profile_source_data = vnfd_vlp["virtual-link-protocol-data"]["l3-protocol-data"]
751 ip_profile_dest_data = {}
752 if "ip-version" in ip_profile_source_data:
753 ip_profile_dest_data["ip-version"] = ip_profile_source_data["ip-version"]
754 if "cidr" in ip_profile_source_data:
755 ip_profile_dest_data["subnet-address"] = ip_profile_source_data["cidr"]
756 if "gateway-ip" in ip_profile_source_data:
757 ip_profile_dest_data["gateway-address"] = ip_profile_source_data["gateway-ip"]
758 if "dhcp-enabled" in ip_profile_source_data:
759 ip_profile_dest_data["dhcp-params"] = {
760 "enabled": ip_profile_source_data["dhcp-enabled"]
761 }
762
763 vld_params["ip-profile"] = ip_profile_dest_data
tierno2357f4e2020-10-19 16:38:59 +0000764 # update vld_params with instantiation params
765 if vnf_params:
bravof922c4172020-11-24 21:21:43 -0300766 vld_instantiation_params = find_in_list(get_iterable(vnf_params, "internal-vld"),
767 lambda i_vld: i_vld["name"] == vld["id"])
tierno2357f4e2020-10-19 16:38:59 +0000768 if vld_instantiation_params:
769 vld_params.update(vld_instantiation_params)
770 parse_vld_instantiation_params(target_vim, vld, vld_params, target_sdn)
771
772 vdur_list = []
tierno69f0d382020-05-07 13:08:09 +0000773 for vdur in target_vnf.get("vdur", ()):
tierno2357f4e2020-10-19 16:38:59 +0000774 if vdur.get("status") == "DELETING" or vdur.get("pdu-type"):
775 continue # This vdu must not be created
bravof922c4172020-11-24 21:21:43 -0300776 vdur["vim_info"] = {"vim_account_id": vnfr["vim-account-id"]}
tierno69f0d382020-05-07 13:08:09 +0000777
bravof922c4172020-11-24 21:21:43 -0300778 self.logger.debug("NS > ssh_keys > {}".format(ssh_keys_all))
779
780 if ssh_keys_all:
bravofe5a31bc2021-02-17 19:09:12 -0300781 vdu_configuration = get_configuration(vnfd, vdur["vdu-id-ref"])
782 vnf_configuration = get_configuration(vnfd, vnfd["id"])
bravof922c4172020-11-24 21:21:43 -0300783 if vdu_configuration and vdu_configuration.get("config-access") and \
784 vdu_configuration.get("config-access").get("ssh-access"):
785 vdur["ssh-keys"] = ssh_keys_all
786 vdur["ssh-access-required"] = vdu_configuration["config-access"]["ssh-access"]["required"]
787 elif vnf_configuration and vnf_configuration.get("config-access") and \
788 vnf_configuration.get("config-access").get("ssh-access") and \
tierno69f0d382020-05-07 13:08:09 +0000789 any(iface.get("mgmt-vnf") for iface in vdur["interfaces"]):
bravof922c4172020-11-24 21:21:43 -0300790 vdur["ssh-keys"] = ssh_keys_all
791 vdur["ssh-access-required"] = vnf_configuration["config-access"]["ssh-access"]["required"]
792 elif ssh_keys_instantiation and \
793 find_in_list(vdur["interfaces"], lambda iface: iface.get("mgmt-vnf")):
794 vdur["ssh-keys"] = ssh_keys_instantiation
tierno69f0d382020-05-07 13:08:09 +0000795
bravof922c4172020-11-24 21:21:43 -0300796 self.logger.debug("NS > vdur > {}".format(vdur))
797
798 vdud = get_vdu(vnfd, vdur["vdu-id-ref"])
tierno69f0d382020-05-07 13:08:09 +0000799 # cloud-init
800 if vdud.get("cloud-init-file"):
801 vdur["cloud-init"] = "{}:file:{}".format(vnfd["_id"], vdud.get("cloud-init-file"))
tierno2357f4e2020-10-19 16:38:59 +0000802 # read file and put content at target.cloul_init_content. Avoid ng_ro to use shared package system
803 if vdur["cloud-init"] not in target["cloud_init_content"]:
804 base_folder = vnfd["_admin"]["storage"]
805 cloud_init_file = "{}/{}/cloud_init/{}".format(base_folder["folder"], base_folder["pkg-dir"],
806 vdud.get("cloud-init-file"))
807 with self.fs.file_open(cloud_init_file, "r") as ci_file:
808 target["cloud_init_content"][vdur["cloud-init"]] = ci_file.read()
tierno69f0d382020-05-07 13:08:09 +0000809 elif vdud.get("cloud-init"):
bravof922c4172020-11-24 21:21:43 -0300810 vdur["cloud-init"] = "{}:vdu:{}".format(vnfd["_id"], get_vdu_index(vnfd, vdur["vdu-id-ref"]))
tierno2357f4e2020-10-19 16:38:59 +0000811 # put content at target.cloul_init_content. Avoid ng_ro read vnfd descriptor
812 target["cloud_init_content"][vdur["cloud-init"]] = vdud["cloud-init"]
813 vdur["additionalParams"] = vdur.get("additionalParams") or {}
814 deploy_params_vdu = self._format_additional_params(vdur.get("additionalParams") or {})
bravof922c4172020-11-24 21:21:43 -0300815 deploy_params_vdu["OSM"] = get_osm_params(vnfr, vdur["vdu-id-ref"], vdur["count-index"])
tierno2357f4e2020-10-19 16:38:59 +0000816 vdur["additionalParams"] = deploy_params_vdu
tierno69f0d382020-05-07 13:08:09 +0000817
818 # flavor
819 ns_flavor = target["flavor"][int(vdur["ns-flavor-id"])]
tierno2357f4e2020-10-19 16:38:59 +0000820 if target_vim not in ns_flavor["vim_info"]:
821 ns_flavor["vim_info"][target_vim] = {}
lloretgalleg7dc94672021-02-08 11:49:50 +0000822
823 # deal with images
824 # in case alternative images are provided we must check if they should be applied
825 # for the vim_type, modify the vim_type taking into account
826 ns_image_id = int(vdur["ns-image-id"])
827 if vdur.get("alt-image-ids"):
828 db_vim = get_vim_account(vnfr["vim-account-id"])
829 vim_type = db_vim["vim_type"]
830 for alt_image_id in vdur.get("alt-image-ids"):
831 ns_alt_image = target["image"][int(alt_image_id)]
832 if vim_type == ns_alt_image.get("vim-type"):
833 # must use alternative image
834 self.logger.debug("use alternative image id: {}".format(alt_image_id))
835 ns_image_id = alt_image_id
836 vdur["ns-image-id"] = ns_image_id
837 break
838 ns_image = target["image"][int(ns_image_id)]
tierno2357f4e2020-10-19 16:38:59 +0000839 if target_vim not in ns_image["vim_info"]:
840 ns_image["vim_info"][target_vim] = {}
tierno69f0d382020-05-07 13:08:09 +0000841
tierno2357f4e2020-10-19 16:38:59 +0000842 vdur["vim_info"] = {target_vim: {}}
843 # instantiation parameters
844 # if vnf_params:
845 # vdu_instantiation_params = next((v for v in get_iterable(vnf_params, "vdu") if v["id"] ==
846 # vdud["id"]), None)
847 vdur_list.append(vdur)
848 target_vnf["vdur"] = vdur_list
tierno69f0d382020-05-07 13:08:09 +0000849 target["vnf"].append(target_vnf)
850
851 desc = await self.RO.deploy(nsr_id, target)
bravof922c4172020-11-24 21:21:43 -0300852 self.logger.debug("RO return > {}".format(desc))
tierno69f0d382020-05-07 13:08:09 +0000853 action_id = desc["action_id"]
tierno2357f4e2020-10-19 16:38:59 +0000854 await self._wait_ng_ro(nsr_id, action_id, nslcmop_id, start_deploy, timeout_ns_deploy, stage)
tierno69f0d382020-05-07 13:08:09 +0000855
856 # Updating NSR
857 db_nsr_update = {
858 "_admin.deployed.RO.operational-status": "running",
859 "detailed-status": " ".join(stage)
860 }
861 # db_nsr["_admin.deployed.RO.detailed-status"] = "Deployed at VIM"
862 self.update_db_2("nsrs", nsr_id, db_nsr_update)
863 self._write_op_status(nslcmop_id, stage)
864 self.logger.debug(logging_text + "ns deployed at RO. RO_id={}".format(action_id))
865 return
866
tierno2357f4e2020-10-19 16:38:59 +0000867 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 +0000868 detailed_status_old = None
869 db_nsr_update = {}
tierno2357f4e2020-10-19 16:38:59 +0000870 start_time = start_time or time()
tierno69f0d382020-05-07 13:08:09 +0000871 while time() <= start_time + timeout:
872 desc_status = await self.RO.status(nsr_id, action_id)
bravof922c4172020-11-24 21:21:43 -0300873 self.logger.debug("Wait NG RO > {}".format(desc_status))
tierno69f0d382020-05-07 13:08:09 +0000874 if desc_status["status"] == "FAILED":
875 raise NgRoException(desc_status["details"])
876 elif desc_status["status"] == "BUILD":
tierno2357f4e2020-10-19 16:38:59 +0000877 if stage:
878 stage[2] = "VIM: ({})".format(desc_status["details"])
tierno69f0d382020-05-07 13:08:09 +0000879 elif desc_status["status"] == "DONE":
tierno2357f4e2020-10-19 16:38:59 +0000880 if stage:
881 stage[2] = "Deployed at VIM"
tierno69f0d382020-05-07 13:08:09 +0000882 break
883 else:
884 assert False, "ROclient.check_ns_status returns unknown {}".format(desc_status["status"])
tierno2357f4e2020-10-19 16:38:59 +0000885 if stage and nslcmop_id and stage[2] != detailed_status_old:
tierno69f0d382020-05-07 13:08:09 +0000886 detailed_status_old = stage[2]
887 db_nsr_update["detailed-status"] = " ".join(stage)
888 self.update_db_2("nsrs", nsr_id, db_nsr_update)
889 self._write_op_status(nslcmop_id, stage)
bravof922c4172020-11-24 21:21:43 -0300890 await asyncio.sleep(15, loop=self.loop)
tierno69f0d382020-05-07 13:08:09 +0000891 else: # timeout_ns_deploy
892 raise NgRoException("Timeout waiting ns to deploy")
893
894 async def _terminate_ng_ro(self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage):
895 db_nsr_update = {}
896 failed_detail = []
897 action_id = None
898 start_deploy = time()
899 try:
900 target = {
901 "ns": {"vld": []},
902 "vnf": [],
903 "image": [],
904 "flavor": [],
tierno2357f4e2020-10-19 16:38:59 +0000905 "action_id": nslcmop_id
tierno69f0d382020-05-07 13:08:09 +0000906 }
907 desc = await self.RO.deploy(nsr_id, target)
908 action_id = desc["action_id"]
909 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = action_id
910 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETING"
911 self.logger.debug(logging_text + "ns terminate action at RO. action_id={}".format(action_id))
912
913 # wait until done
914 delete_timeout = 20 * 60 # 20 minutes
tierno2357f4e2020-10-19 16:38:59 +0000915 await self._wait_ng_ro(nsr_id, action_id, nslcmop_id, start_deploy, delete_timeout, stage)
tierno69f0d382020-05-07 13:08:09 +0000916
917 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
918 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
919 # delete all nsr
920 await self.RO.delete(nsr_id)
921 except Exception as e:
922 if isinstance(e, NgRoException) and e.http_code == 404: # not found
923 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
924 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
925 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
926 self.logger.debug(logging_text + "RO_action_id={} already deleted".format(action_id))
927 elif isinstance(e, NgRoException) and e.http_code == 409: # conflict
928 failed_detail.append("delete conflict: {}".format(e))
929 self.logger.debug(logging_text + "RO_action_id={} delete conflict: {}".format(action_id, e))
930 else:
931 failed_detail.append("delete error: {}".format(e))
932 self.logger.error(logging_text + "RO_action_id={} delete error: {}".format(action_id, e))
933
934 if failed_detail:
935 stage[2] = "Error deleting from VIM"
936 else:
937 stage[2] = "Deleted from VIM"
938 db_nsr_update["detailed-status"] = " ".join(stage)
939 self.update_db_2("nsrs", nsr_id, db_nsr_update)
940 self._write_op_status(nslcmop_id, stage)
941
942 if failed_detail:
943 raise LcmException("; ".join(failed_detail))
944 return
945
bravof922c4172020-11-24 21:21:43 -0300946 async def instantiate_RO(self, logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs, db_vnfds,
tiernoe876f672020-02-13 14:34:48 +0000947 n2vc_key_list, stage):
tiernoe95ed362020-04-23 08:24:57 +0000948 """
949 Instantiate at RO
950 :param logging_text: preffix text to use at logging
951 :param nsr_id: nsr identity
952 :param nsd: database content of ns descriptor
953 :param db_nsr: database content of ns record
954 :param db_nslcmop: database content of ns operation, in this case, 'instantiate'
955 :param db_vnfrs:
bravof922c4172020-11-24 21:21:43 -0300956 :param db_vnfds: database content of vnfds, indexed by id (not _id). {id: {vnfd_object}, ...}
tiernoe95ed362020-04-23 08:24:57 +0000957 :param n2vc_key_list: ssh-public-key list to be inserted to management vdus via cloud-init
958 :param stage: list with 3 items: [general stage, tasks, vim_specific]. This task will write over vim_specific
959 :return: None or exception
960 """
tiernoe876f672020-02-13 14:34:48 +0000961 try:
tiernoe876f672020-02-13 14:34:48 +0000962 start_deploy = time()
963 ns_params = db_nslcmop.get("operationParams")
964 if ns_params and ns_params.get("timeout_ns_deploy"):
965 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
966 else:
967 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
quilesj7e13aeb2019-10-08 13:34:55 +0200968
tiernoe876f672020-02-13 14:34:48 +0000969 # Check for and optionally request placement optimization. Database will be updated if placement activated
970 stage[2] = "Waiting for Placement."
tierno8790a3d2020-04-23 22:49:52 +0000971 if await self._do_placement(logging_text, db_nslcmop, db_vnfrs):
972 # in case of placement change ns_params[vimAcountId) if not present at any vnfrs
973 for vnfr in db_vnfrs.values():
974 if ns_params["vimAccountId"] == vnfr["vim-account-id"]:
975 break
976 else:
977 ns_params["vimAccountId"] == vnfr["vim-account-id"]
quilesj7e13aeb2019-10-08 13:34:55 +0200978
bravof922c4172020-11-24 21:21:43 -0300979 return await self._instantiate_ng_ro(logging_text, nsr_id, nsd, db_nsr, db_nslcmop, db_vnfrs,
980 db_vnfds, n2vc_key_list, stage, start_deploy, timeout_ns_deploy)
tierno2357f4e2020-10-19 16:38:59 +0000981 except Exception as e:
tierno067e04a2020-03-31 12:53:13 +0000982 stage[2] = "ERROR deploying at VIM"
tiernoe876f672020-02-13 14:34:48 +0000983 self.set_vnfr_at_error(db_vnfrs, str(e))
tierno2357f4e2020-10-19 16:38:59 +0000984 self.logger.error("Error deploying at VIM {}".format(e),
985 exc_info=not isinstance(e, (ROclient.ROClientException, LcmException, DbException,
986 NgRoException)))
tiernoe876f672020-02-13 14:34:48 +0000987 raise
quilesj7e13aeb2019-10-08 13:34:55 +0200988
tierno7ecbc342020-09-21 14:05:39 +0000989 async def wait_kdu_up(self, logging_text, nsr_id, vnfr_id, kdu_name):
990 """
991 Wait for kdu to be up, get ip address
992 :param logging_text: prefix use for logging
993 :param nsr_id:
994 :param vnfr_id:
995 :param kdu_name:
996 :return: IP address
997 """
998
999 # self.logger.debug(logging_text + "Starting wait_kdu_up")
1000 nb_tries = 0
1001
1002 while nb_tries < 360:
1003 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
tiernoe5d05972020-10-09 12:03:24 +00001004 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 +00001005 if not kdur:
1006 raise LcmException("Not found vnfr_id={}, kdu_name={}".format(vnfr_id, kdu_name))
1007 if kdur.get("status"):
1008 if kdur["status"] in ("READY", "ENABLED"):
1009 return kdur.get("ip-address")
1010 else:
1011 raise LcmException("target KDU={} is in error state".format(kdu_name))
1012
1013 await asyncio.sleep(10, loop=self.loop)
1014 nb_tries += 1
1015 raise LcmException("Timeout waiting KDU={} instantiated".format(kdu_name))
1016
tiernoa5088192019-11-26 16:12:53 +00001017 async def wait_vm_up_insert_key_ro(self, logging_text, nsr_id, vnfr_id, vdu_id, vdu_index, pub_key=None, user=None):
1018 """
1019 Wait for ip addres at RO, and optionally, insert public key in virtual machine
1020 :param logging_text: prefix use for logging
1021 :param nsr_id:
1022 :param vnfr_id:
1023 :param vdu_id:
1024 :param vdu_index:
1025 :param pub_key: public ssh key to inject, None to skip
1026 :param user: user to apply the public ssh key
1027 :return: IP address
1028 """
quilesj7e13aeb2019-10-08 13:34:55 +02001029
tierno2357f4e2020-10-19 16:38:59 +00001030 self.logger.debug(logging_text + "Starting wait_vm_up_insert_key_ro")
tiernod8323042019-08-09 11:32:23 +00001031 ro_nsr_id = None
1032 ip_address = None
1033 nb_tries = 0
1034 target_vdu_id = None
quilesj3149f262019-12-03 10:58:10 +00001035 ro_retries = 0
quilesj7e13aeb2019-10-08 13:34:55 +02001036
tiernod8323042019-08-09 11:32:23 +00001037 while True:
quilesj7e13aeb2019-10-08 13:34:55 +02001038
quilesj3149f262019-12-03 10:58:10 +00001039 ro_retries += 1
1040 if ro_retries >= 360: # 1 hour
1041 raise LcmException("Not found _admin.deployed.RO.nsr_id for nsr_id: {}".format(nsr_id))
1042
tiernod8323042019-08-09 11:32:23 +00001043 await asyncio.sleep(10, loop=self.loop)
quilesj7e13aeb2019-10-08 13:34:55 +02001044
1045 # get ip address
tiernod8323042019-08-09 11:32:23 +00001046 if not target_vdu_id:
1047 db_vnfr = self.db.get_one("vnfrs", {"_id": vnfr_id})
quilesj3149f262019-12-03 10:58:10 +00001048
1049 if not vdu_id: # for the VNF case
tiernoe876f672020-02-13 14:34:48 +00001050 if db_vnfr.get("status") == "ERROR":
1051 raise LcmException("Cannot inject ssh-key because target VNF is in error state")
tiernod8323042019-08-09 11:32:23 +00001052 ip_address = db_vnfr.get("ip-address")
1053 if not ip_address:
1054 continue
quilesj3149f262019-12-03 10:58:10 +00001055 vdur = next((x for x in get_iterable(db_vnfr, "vdur") if x.get("ip-address") == ip_address), None)
1056 else: # VDU case
1057 vdur = next((x for x in get_iterable(db_vnfr, "vdur")
1058 if x.get("vdu-id-ref") == vdu_id and x.get("count-index") == vdu_index), None)
1059
tierno0e8c3f02020-03-12 17:18:21 +00001060 if not vdur and len(db_vnfr.get("vdur", ())) == 1: # If only one, this should be the target vdu
1061 vdur = db_vnfr["vdur"][0]
quilesj3149f262019-12-03 10:58:10 +00001062 if not vdur:
tierno0e8c3f02020-03-12 17:18:21 +00001063 raise LcmException("Not found vnfr_id={}, vdu_id={}, vdu_index={}".format(vnfr_id, vdu_id,
1064 vdu_index))
tierno2357f4e2020-10-19 16:38:59 +00001065 # New generation RO stores information at "vim_info"
1066 ng_ro_status = None
David Garciaa8bbe672020-11-19 13:06:54 +01001067 target_vim = None
tierno2357f4e2020-10-19 16:38:59 +00001068 if vdur.get("vim_info"):
1069 target_vim = next(t for t in vdur["vim_info"]) # there should be only one key
1070 ng_ro_status = vdur["vim_info"][target_vim].get("vim_status")
1071 if vdur.get("pdu-type") or vdur.get("status") == "ACTIVE" or ng_ro_status == "ACTIVE":
quilesj3149f262019-12-03 10:58:10 +00001072 ip_address = vdur.get("ip-address")
1073 if not ip_address:
1074 continue
1075 target_vdu_id = vdur["vdu-id-ref"]
bravof922c4172020-11-24 21:21:43 -03001076 elif vdur.get("status") == "ERROR" or ng_ro_status == "ERROR":
quilesj3149f262019-12-03 10:58:10 +00001077 raise LcmException("Cannot inject ssh-key because target VM is in error state")
1078
tiernod8323042019-08-09 11:32:23 +00001079 if not target_vdu_id:
1080 continue
tiernod8323042019-08-09 11:32:23 +00001081
quilesj7e13aeb2019-10-08 13:34:55 +02001082 # inject public key into machine
1083 if pub_key and user:
tierno2357f4e2020-10-19 16:38:59 +00001084 self.logger.debug(logging_text + "Inserting RO key")
bravof922c4172020-11-24 21:21:43 -03001085 self.logger.debug("SSH > PubKey > {}".format(pub_key))
tierno0e8c3f02020-03-12 17:18:21 +00001086 if vdur.get("pdu-type"):
1087 self.logger.error(logging_text + "Cannot inject ssh-ky to a PDU")
1088 return ip_address
quilesj7e13aeb2019-10-08 13:34:55 +02001089 try:
1090 ro_vm_id = "{}-{}".format(db_vnfr["member-vnf-index-ref"], target_vdu_id) # TODO add vdu_index
tierno69f0d382020-05-07 13:08:09 +00001091 if self.ng_ro:
bravof922c4172020-11-24 21:21:43 -03001092 target = {"action": {"action": "inject_ssh_key", "key": pub_key, "user": user},
tierno2357f4e2020-10-19 16:38:59 +00001093 "vnf": [{"_id": vnfr_id, "vdur": [{"id": vdur["id"]}]}],
tierno69f0d382020-05-07 13:08:09 +00001094 }
tierno2357f4e2020-10-19 16:38:59 +00001095 desc = await self.RO.deploy(nsr_id, target)
1096 action_id = desc["action_id"]
1097 await self._wait_ng_ro(nsr_id, action_id, timeout=600)
1098 break
tierno69f0d382020-05-07 13:08:09 +00001099 else:
tierno2357f4e2020-10-19 16:38:59 +00001100 # wait until NS is deployed at RO
1101 if not ro_nsr_id:
1102 db_nsrs = self.db.get_one("nsrs", {"_id": nsr_id})
1103 ro_nsr_id = deep_get(db_nsrs, ("_admin", "deployed", "RO", "nsr_id"))
1104 if not ro_nsr_id:
1105 continue
tierno69f0d382020-05-07 13:08:09 +00001106 result_dict = await self.RO.create_action(
1107 item="ns",
1108 item_id_name=ro_nsr_id,
1109 descriptor={"add_public_key": pub_key, "vms": [ro_vm_id], "user": user}
1110 )
1111 # result_dict contains the format {VM-id: {vim_result: 200, description: text}}
1112 if not result_dict or not isinstance(result_dict, dict):
1113 raise LcmException("Unknown response from RO when injecting key")
1114 for result in result_dict.values():
1115 if result.get("vim_result") == 200:
1116 break
1117 else:
1118 raise ROclient.ROClientException("error injecting key: {}".format(
1119 result.get("description")))
1120 break
1121 except NgRoException as e:
1122 raise LcmException("Reaching max tries injecting key. Error: {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02001123 except ROclient.ROClientException as e:
tiernoa5088192019-11-26 16:12:53 +00001124 if not nb_tries:
1125 self.logger.debug(logging_text + "error injecting key: {}. Retrying until {} seconds".
1126 format(e, 20*10))
quilesj7e13aeb2019-10-08 13:34:55 +02001127 nb_tries += 1
tiernoa5088192019-11-26 16:12:53 +00001128 if nb_tries >= 20:
quilesj7e13aeb2019-10-08 13:34:55 +02001129 raise LcmException("Reaching max tries injecting key. Error: {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02001130 else:
quilesj7e13aeb2019-10-08 13:34:55 +02001131 break
1132
1133 return ip_address
1134
tierno5ee02052019-12-05 19:55:02 +00001135 async def _wait_dependent_n2vc(self, nsr_id, vca_deployed_list, vca_index):
1136 """
1137 Wait until dependent VCA deployments have been finished. NS wait for VNFs and VDUs. VNFs for VDUs
1138 """
1139 my_vca = vca_deployed_list[vca_index]
1140 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
quilesj3655ae02019-12-12 16:08:35 +00001141 # vdu or kdu: no dependencies
tierno5ee02052019-12-05 19:55:02 +00001142 return
1143 timeout = 300
1144 while timeout >= 0:
quilesj3655ae02019-12-12 16:08:35 +00001145 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
1146 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
1147 configuration_status_list = db_nsr["configurationStatus"]
1148 for index, vca_deployed in enumerate(configuration_status_list):
tierno5ee02052019-12-05 19:55:02 +00001149 if index == vca_index:
quilesj3655ae02019-12-12 16:08:35 +00001150 # myself
tierno5ee02052019-12-05 19:55:02 +00001151 continue
1152 if not my_vca.get("member-vnf-index") or \
1153 (vca_deployed.get("member-vnf-index") == my_vca.get("member-vnf-index")):
quilesj3655ae02019-12-12 16:08:35 +00001154 internal_status = configuration_status_list[index].get("status")
1155 if internal_status == 'READY':
1156 continue
1157 elif internal_status == 'BROKEN':
tierno5ee02052019-12-05 19:55:02 +00001158 raise LcmException("Configuration aborted because dependent charm/s has failed")
quilesj3655ae02019-12-12 16:08:35 +00001159 else:
1160 break
tierno5ee02052019-12-05 19:55:02 +00001161 else:
quilesj3655ae02019-12-12 16:08:35 +00001162 # no dependencies, return
tierno5ee02052019-12-05 19:55:02 +00001163 return
1164 await asyncio.sleep(10)
1165 timeout -= 1
tierno5ee02052019-12-05 19:55:02 +00001166
1167 raise LcmException("Configuration aborted because dependent charm/s timeout")
1168
tiernoe876f672020-02-13 14:34:48 +00001169 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 +00001170 config_descriptor, deploy_params, base_folder, nslcmop_id, stage, vca_type, vca_name,
1171 ee_config_descriptor):
tiernod8323042019-08-09 11:32:23 +00001172 nsr_id = db_nsr["_id"]
1173 db_update_entry = "_admin.deployed.VCA.{}.".format(vca_index)
tiernoda6fb102019-11-23 00:36:52 +00001174 vca_deployed_list = db_nsr["_admin"]["deployed"]["VCA"]
tiernod8323042019-08-09 11:32:23 +00001175 vca_deployed = db_nsr["_admin"]["deployed"]["VCA"][vca_index]
tiernob996d942020-07-03 14:52:28 +00001176 osm_config = {"osm": {"ns_id": db_nsr["_id"]}}
quilesj7e13aeb2019-10-08 13:34:55 +02001177 db_dict = {
1178 'collection': 'nsrs',
1179 'filter': {'_id': nsr_id},
1180 'path': db_update_entry
1181 }
tiernod8323042019-08-09 11:32:23 +00001182 step = ""
1183 try:
quilesj3655ae02019-12-12 16:08:35 +00001184
1185 element_type = 'NS'
1186 element_under_configuration = nsr_id
1187
tiernod8323042019-08-09 11:32:23 +00001188 vnfr_id = None
1189 if db_vnfr:
1190 vnfr_id = db_vnfr["_id"]
tiernob996d942020-07-03 14:52:28 +00001191 osm_config["osm"]["vnf_id"] = vnfr_id
tiernod8323042019-08-09 11:32:23 +00001192
1193 namespace = "{nsi}.{ns}".format(
1194 nsi=nsi_id if nsi_id else "",
1195 ns=nsr_id)
quilesj3655ae02019-12-12 16:08:35 +00001196
tiernod8323042019-08-09 11:32:23 +00001197 if vnfr_id:
quilesj3655ae02019-12-12 16:08:35 +00001198 element_type = 'VNF'
1199 element_under_configuration = vnfr_id
aktas13251562021-02-12 22:19:10 +03001200 namespace += ".{}-{}".format(vnfr_id, vdu_index or 0)
tiernod8323042019-08-09 11:32:23 +00001201 if vdu_id:
1202 namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
quilesj3655ae02019-12-12 16:08:35 +00001203 element_type = 'VDU'
quilesjb8a35dd2020-01-09 15:10:14 +00001204 element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
tiernob996d942020-07-03 14:52:28 +00001205 osm_config["osm"]["vdu_id"] = vdu_id
tierno51183952020-04-03 15:48:18 +00001206 elif kdu_name:
aktas13251562021-02-12 22:19:10 +03001207 namespace += ".{}.{}".format(kdu_name, vdu_index or 0)
tierno51183952020-04-03 15:48:18 +00001208 element_type = 'KDU'
1209 element_under_configuration = kdu_name
tiernob996d942020-07-03 14:52:28 +00001210 osm_config["osm"]["kdu_name"] = kdu_name
tiernod8323042019-08-09 11:32:23 +00001211
1212 # Get artifact path
tierno588547c2020-07-01 15:30:20 +00001213 artifact_path = "{}/{}/{}/{}".format(
tiernod8323042019-08-09 11:32:23 +00001214 base_folder["folder"],
1215 base_folder["pkg-dir"],
tierno588547c2020-07-01 15:30:20 +00001216 "charms" if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm") else "helm-charts",
1217 vca_name
tiernod8323042019-08-09 11:32:23 +00001218 )
bravof922c4172020-11-24 21:21:43 -03001219
1220 self.logger.debug("Artifact path > {}".format(artifact_path))
1221
tiernoa278b842020-07-08 15:33:55 +00001222 # get initial_config_primitive_list that applies to this element
1223 initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
1224
bravof922c4172020-11-24 21:21:43 -03001225 self.logger.debug("Initial config primitive list > {}".format(initial_config_primitive_list))
1226
tiernoa278b842020-07-08 15:33:55 +00001227 # add config if not present for NS charm
1228 ee_descriptor_id = ee_config_descriptor.get("id")
bravof922c4172020-11-24 21:21:43 -03001229 self.logger.debug("EE Descriptor > {}".format(ee_descriptor_id))
1230 initial_config_primitive_list = get_ee_sorted_initial_config_primitive_list(initial_config_primitive_list,
1231 vca_deployed, ee_descriptor_id)
tiernod8323042019-08-09 11:32:23 +00001232
bravof922c4172020-11-24 21:21:43 -03001233 self.logger.debug("Initial config primitive list #2 > {}".format(initial_config_primitive_list))
tierno588547c2020-07-01 15:30:20 +00001234 # n2vc_redesign STEP 3.1
tierno588547c2020-07-01 15:30:20 +00001235 # find old ee_id if exists
1236 ee_id = vca_deployed.get("ee_id")
tiernod8323042019-08-09 11:32:23 +00001237
David Garciaaae391f2020-11-09 11:12:54 +01001238 vim_account_id = (
1239 deep_get(db_vnfr, ("vim-account-id",)) or
1240 deep_get(deploy_params, ("OSM", "vim_account_id"))
1241 )
1242 vca_cloud, vca_cloud_credential = self.get_vca_cloud_and_credentials(vim_account_id)
1243 vca_k8s_cloud, vca_k8s_cloud_credential = self.get_vca_k8s_cloud_and_credentials(vim_account_id)
tierno588547c2020-07-01 15:30:20 +00001244 # create or register execution environment in VCA
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001245 if vca_type in ("lxc_proxy_charm", "k8s_proxy_charm", "helm", "helm-v3"):
quilesj7e13aeb2019-10-08 13:34:55 +02001246
tierno588547c2020-07-01 15:30:20 +00001247 self._write_configuration_status(
1248 nsr_id=nsr_id,
1249 vca_index=vca_index,
1250 status='CREATING',
1251 element_under_configuration=element_under_configuration,
1252 element_type=element_type
1253 )
tiernod8323042019-08-09 11:32:23 +00001254
tierno588547c2020-07-01 15:30:20 +00001255 step = "create execution environment"
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001256 self.logger.debug(logging_text + step)
David Garciaaae391f2020-11-09 11:12:54 +01001257
1258 ee_id = None
1259 credentials = None
1260 if vca_type == "k8s_proxy_charm":
1261 ee_id = await self.vca_map[vca_type].install_k8s_proxy_charm(
1262 charm_name=artifact_path[artifact_path.rfind("/") + 1:],
1263 namespace=namespace,
1264 artifact_path=artifact_path,
1265 db_dict=db_dict,
1266 cloud_name=vca_k8s_cloud,
1267 credential_name=vca_k8s_cloud_credential,
1268 )
David Garciad64e2742021-02-25 20:19:18 +01001269 elif vca_type == "helm" or vca_type == "helm-v3":
bravof922c4172020-11-24 21:21:43 -03001270 ee_id, credentials = await self.vca_map[vca_type].create_execution_environment(
1271 namespace=namespace,
1272 reuse_ee_id=ee_id,
1273 db_dict=db_dict,
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001274 config=osm_config,
1275 artifact_path=artifact_path,
1276 vca_type=vca_type
bravof922c4172020-11-24 21:21:43 -03001277 )
lloretgalleg18cb3cb2020-12-10 14:21:10 +00001278 else:
David Garciaaae391f2020-11-09 11:12:54 +01001279 ee_id, credentials = await self.vca_map[vca_type].create_execution_environment(
1280 namespace=namespace,
1281 reuse_ee_id=ee_id,
1282 db_dict=db_dict,
David Garciaaae391f2020-11-09 11:12:54 +01001283 cloud_name=vca_cloud,
1284 credential_name=vca_cloud_credential,
1285 )
quilesj3655ae02019-12-12 16:08:35 +00001286
tierno588547c2020-07-01 15:30:20 +00001287 elif vca_type == "native_charm":
1288 step = "Waiting to VM being up and getting IP address"
1289 self.logger.debug(logging_text + step)
1290 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id, vdu_index,
1291 user=None, pub_key=None)
1292 credentials = {"hostname": rw_mgmt_ip}
1293 # get username
1294 username = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
1295 # TODO remove this when changes on IM regarding config-access:ssh-access:default-user were
1296 # merged. Meanwhile let's get username from initial-config-primitive
tiernoa278b842020-07-08 15:33:55 +00001297 if not username and initial_config_primitive_list:
1298 for config_primitive in initial_config_primitive_list:
tierno588547c2020-07-01 15:30:20 +00001299 for param in config_primitive.get("parameter", ()):
1300 if param["name"] == "ssh-username":
1301 username = param["value"]
1302 break
1303 if not username:
tiernoa278b842020-07-08 15:33:55 +00001304 raise LcmException("Cannot determine the username neither with 'initial-config-primitive' nor with "
tierno588547c2020-07-01 15:30:20 +00001305 "'config-access.ssh-access.default-user'")
1306 credentials["username"] = username
1307 # n2vc_redesign STEP 3.2
quilesj3655ae02019-12-12 16:08:35 +00001308
tierno588547c2020-07-01 15:30:20 +00001309 self._write_configuration_status(
1310 nsr_id=nsr_id,
1311 vca_index=vca_index,
1312 status='REGISTERING',
1313 element_under_configuration=element_under_configuration,
1314 element_type=element_type
1315 )
quilesj3655ae02019-12-12 16:08:35 +00001316
tierno588547c2020-07-01 15:30:20 +00001317 step = "register execution environment {}".format(credentials)
1318 self.logger.debug(logging_text + step)
1319 ee_id = await self.vca_map[vca_type].register_execution_environment(
David Garciaaae391f2020-11-09 11:12:54 +01001320 credentials=credentials,
1321 namespace=namespace,
1322 db_dict=db_dict,
1323 cloud_name=vca_cloud,
1324 credential_name=vca_cloud_credential,
1325 )
tierno3bedc9b2019-11-27 15:46:57 +00001326
tierno588547c2020-07-01 15:30:20 +00001327 # for compatibility with MON/POL modules, the need model and application name at database
1328 # TODO ask MON/POL if needed to not assuming anymore the format "model_name.application_name"
1329 ee_id_parts = ee_id.split('.')
1330 db_nsr_update = {db_update_entry + "ee_id": ee_id}
1331 if len(ee_id_parts) >= 2:
1332 model_name = ee_id_parts[0]
1333 application_name = ee_id_parts[1]
1334 db_nsr_update[db_update_entry + "model"] = model_name
1335 db_nsr_update[db_update_entry + "application"] = application_name
tiernod8323042019-08-09 11:32:23 +00001336
1337 # n2vc_redesign STEP 3.3
tiernod8323042019-08-09 11:32:23 +00001338 step = "Install configuration Software"
quilesj3655ae02019-12-12 16:08:35 +00001339
tiernoc231a872020-01-21 08:49:05 +00001340 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001341 nsr_id=nsr_id,
1342 vca_index=vca_index,
1343 status='INSTALLING SW',
1344 element_under_configuration=element_under_configuration,
tierno51183952020-04-03 15:48:18 +00001345 element_type=element_type,
tierno588547c2020-07-01 15:30:20 +00001346 other_update=db_nsr_update
quilesj3655ae02019-12-12 16:08:35 +00001347 )
1348
tierno3bedc9b2019-11-27 15:46:57 +00001349 # TODO check if already done
quilesj7e13aeb2019-10-08 13:34:55 +02001350 self.logger.debug(logging_text + step)
David Garcia18a63322020-04-01 16:14:59 +02001351 config = None
tierno588547c2020-07-01 15:30:20 +00001352 if vca_type == "native_charm":
tiernoa278b842020-07-08 15:33:55 +00001353 config_primitive = next((p for p in initial_config_primitive_list if p["name"] == "config"), None)
1354 if config_primitive:
1355 config = self._map_primitive_params(
1356 config_primitive,
1357 {},
1358 deploy_params
1359 )
tierno588547c2020-07-01 15:30:20 +00001360 num_units = 1
1361 if vca_type == "lxc_proxy_charm":
1362 if element_type == "NS":
1363 num_units = db_nsr.get("config-units") or 1
1364 elif element_type == "VNF":
1365 num_units = db_vnfr.get("config-units") or 1
1366 elif element_type == "VDU":
1367 for v in db_vnfr["vdur"]:
1368 if vdu_id == v["vdu-id-ref"]:
1369 num_units = v.get("config-units") or 1
1370 break
David Garciaaae391f2020-11-09 11:12:54 +01001371 if vca_type != "k8s_proxy_charm":
1372 await self.vca_map[vca_type].install_configuration_sw(
1373 ee_id=ee_id,
1374 artifact_path=artifact_path,
1375 db_dict=db_dict,
1376 config=config,
1377 num_units=num_units,
1378 )
quilesj7e13aeb2019-10-08 13:34:55 +02001379
quilesj63f90042020-01-17 09:53:55 +00001380 # write in db flag of configuration_sw already installed
1381 self.update_db_2("nsrs", nsr_id, {db_update_entry + "config_sw_installed": True})
1382
1383 # add relations for this VCA (wait for other peers related with this VCA)
tierno588547c2020-07-01 15:30:20 +00001384 await self._add_vca_relations(logging_text=logging_text, nsr_id=nsr_id,
1385 vca_index=vca_index, vca_type=vca_type)
quilesj63f90042020-01-17 09:53:55 +00001386
quilesj7e13aeb2019-10-08 13:34:55 +02001387 # if SSH access is required, then get execution environment SSH public
David Garciaa27e20a2020-07-10 13:12:44 +02001388 # if native charm we have waited already to VM be UP
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001389 if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"):
tierno3bedc9b2019-11-27 15:46:57 +00001390 pub_key = None
1391 user = None
tierno588547c2020-07-01 15:30:20 +00001392 # self.logger.debug("get ssh key block")
tierno3bedc9b2019-11-27 15:46:57 +00001393 if deep_get(config_descriptor, ("config-access", "ssh-access", "required")):
tierno588547c2020-07-01 15:30:20 +00001394 # self.logger.debug("ssh key needed")
tierno3bedc9b2019-11-27 15:46:57 +00001395 # Needed to inject a ssh key
1396 user = deep_get(config_descriptor, ("config-access", "ssh-access", "default-user"))
1397 step = "Install configuration Software, getting public ssh key"
tierno588547c2020-07-01 15:30:20 +00001398 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 +02001399
tiernoacc90452019-12-10 11:06:54 +00001400 step = "Insert public key into VM user={} ssh_key={}".format(user, pub_key)
tierno3bedc9b2019-11-27 15:46:57 +00001401 else:
tierno588547c2020-07-01 15:30:20 +00001402 # self.logger.debug("no need to get ssh key")
tierno3bedc9b2019-11-27 15:46:57 +00001403 step = "Waiting to VM being up and getting IP address"
1404 self.logger.debug(logging_text + step)
quilesj7e13aeb2019-10-08 13:34:55 +02001405
tierno3bedc9b2019-11-27 15:46:57 +00001406 # n2vc_redesign STEP 5.1
1407 # wait for RO (ip-address) Insert pub_key into VM
tierno5ee02052019-12-05 19:55:02 +00001408 if vnfr_id:
tierno7ecbc342020-09-21 14:05:39 +00001409 if kdu_name:
1410 rw_mgmt_ip = await self.wait_kdu_up(logging_text, nsr_id, vnfr_id, kdu_name)
1411 else:
1412 rw_mgmt_ip = await self.wait_vm_up_insert_key_ro(logging_text, nsr_id, vnfr_id, vdu_id,
1413 vdu_index, user=user, pub_key=pub_key)
tierno5ee02052019-12-05 19:55:02 +00001414 else:
1415 rw_mgmt_ip = None # This is for a NS configuration
tierno3bedc9b2019-11-27 15:46:57 +00001416
1417 self.logger.debug(logging_text + ' VM_ip_address={}'.format(rw_mgmt_ip))
quilesj7e13aeb2019-10-08 13:34:55 +02001418
tiernoa5088192019-11-26 16:12:53 +00001419 # store rw_mgmt_ip in deploy params for later replacement
quilesj7e13aeb2019-10-08 13:34:55 +02001420 deploy_params["rw_mgmt_ip"] = rw_mgmt_ip
tiernod8323042019-08-09 11:32:23 +00001421
1422 # n2vc_redesign STEP 6 Execute initial config primitive
quilesj7e13aeb2019-10-08 13:34:55 +02001423 step = 'execute initial config primitive'
quilesj3655ae02019-12-12 16:08:35 +00001424
1425 # wait for dependent primitives execution (NS -> VNF -> VDU)
tierno5ee02052019-12-05 19:55:02 +00001426 if initial_config_primitive_list:
1427 await self._wait_dependent_n2vc(nsr_id, vca_deployed_list, vca_index)
quilesj3655ae02019-12-12 16:08:35 +00001428
1429 # stage, in function of element type: vdu, kdu, vnf or ns
1430 my_vca = vca_deployed_list[vca_index]
1431 if my_vca.get("vdu_id") or my_vca.get("kdu_name"):
1432 # VDU or KDU
tiernoe876f672020-02-13 14:34:48 +00001433 stage[0] = 'Stage 3/5: running Day-1 primitives for VDU.'
quilesj3655ae02019-12-12 16:08:35 +00001434 elif my_vca.get("member-vnf-index"):
1435 # VNF
tiernoe876f672020-02-13 14:34:48 +00001436 stage[0] = 'Stage 4/5: running Day-1 primitives for VNF.'
quilesj3655ae02019-12-12 16:08:35 +00001437 else:
1438 # NS
tiernoe876f672020-02-13 14:34:48 +00001439 stage[0] = 'Stage 5/5: running Day-1 primitives for NS.'
quilesj3655ae02019-12-12 16:08:35 +00001440
tiernoc231a872020-01-21 08:49:05 +00001441 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001442 nsr_id=nsr_id,
1443 vca_index=vca_index,
1444 status='EXECUTING PRIMITIVE'
1445 )
1446
1447 self._write_op_status(
1448 op_id=nslcmop_id,
1449 stage=stage
1450 )
1451
tiernoe876f672020-02-13 14:34:48 +00001452 check_if_terminated_needed = True
tiernod8323042019-08-09 11:32:23 +00001453 for initial_config_primitive in initial_config_primitive_list:
tiernoda6fb102019-11-23 00:36:52 +00001454 # adding information on the vca_deployed if it is a NS execution environment
1455 if not vca_deployed["member-vnf-index"]:
David Garciad4816682019-12-09 14:57:43 +01001456 deploy_params["ns_config_info"] = json.dumps(self._get_ns_config_info(nsr_id))
tiernod8323042019-08-09 11:32:23 +00001457 # TODO check if already done
1458 primitive_params_ = self._map_primitive_params(initial_config_primitive, {}, deploy_params)
tierno3bedc9b2019-11-27 15:46:57 +00001459
tiernod8323042019-08-09 11:32:23 +00001460 step = "execute primitive '{}' params '{}'".format(initial_config_primitive["name"], primitive_params_)
1461 self.logger.debug(logging_text + step)
tierno588547c2020-07-01 15:30:20 +00001462 await self.vca_map[vca_type].exec_primitive(
quilesj7e13aeb2019-10-08 13:34:55 +02001463 ee_id=ee_id,
1464 primitive_name=initial_config_primitive["name"],
1465 params_dict=primitive_params_,
1466 db_dict=db_dict
1467 )
tiernoe876f672020-02-13 14:34:48 +00001468 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
1469 if check_if_terminated_needed:
1470 if config_descriptor.get('terminate-config-primitive'):
1471 self.update_db_2("nsrs", nsr_id, {db_update_entry + "needed_terminate": True})
1472 check_if_terminated_needed = False
quilesj3655ae02019-12-12 16:08:35 +00001473
tiernod8323042019-08-09 11:32:23 +00001474 # TODO register in database that primitive is done
quilesj7e13aeb2019-10-08 13:34:55 +02001475
tiernob996d942020-07-03 14:52:28 +00001476 # STEP 7 Configure metrics
lloretgalleg18ebc3a2020-10-22 09:54:51 +00001477 if vca_type == "helm" or vca_type == "helm-v3":
tiernob996d942020-07-03 14:52:28 +00001478 prometheus_jobs = await self.add_prometheus_metrics(
1479 ee_id=ee_id,
1480 artifact_path=artifact_path,
1481 ee_config_descriptor=ee_config_descriptor,
1482 vnfr_id=vnfr_id,
1483 nsr_id=nsr_id,
1484 target_ip=rw_mgmt_ip,
1485 )
1486 if prometheus_jobs:
1487 self.update_db_2("nsrs", nsr_id, {db_update_entry + "prometheus_jobs": prometheus_jobs})
1488
quilesj7e13aeb2019-10-08 13:34:55 +02001489 step = "instantiated at VCA"
1490 self.logger.debug(logging_text + step)
1491
tiernoc231a872020-01-21 08:49:05 +00001492 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001493 nsr_id=nsr_id,
1494 vca_index=vca_index,
1495 status='READY'
1496 )
1497
tiernod8323042019-08-09 11:32:23 +00001498 except Exception as e: # TODO not use Exception but N2VC exception
quilesj3655ae02019-12-12 16:08:35 +00001499 # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
tiernoe876f672020-02-13 14:34:48 +00001500 if not isinstance(e, (DbException, N2VCException, LcmException, asyncio.CancelledError)):
1501 self.logger.error("Exception while {} : {}".format(step, e), exc_info=True)
tiernoc231a872020-01-21 08:49:05 +00001502 self._write_configuration_status(
quilesj3655ae02019-12-12 16:08:35 +00001503 nsr_id=nsr_id,
1504 vca_index=vca_index,
1505 status='BROKEN'
1506 )
tiernoe876f672020-02-13 14:34:48 +00001507 raise LcmException("{} {}".format(step, e)) from e
tiernod8323042019-08-09 11:32:23 +00001508
quilesj4cda56b2019-12-05 10:02:20 +00001509 def _write_ns_status(self, nsr_id: str, ns_state: str, current_operation: str, current_operation_id: str,
tiernoa2143262020-03-27 16:20:40 +00001510 error_description: str = None, error_detail: str = None, other_update: dict = None):
tiernoe876f672020-02-13 14:34:48 +00001511 """
1512 Update db_nsr fields.
1513 :param nsr_id:
1514 :param ns_state:
1515 :param current_operation:
1516 :param current_operation_id:
1517 :param error_description:
tiernoa2143262020-03-27 16:20:40 +00001518 :param error_detail:
tiernoe876f672020-02-13 14:34:48 +00001519 :param other_update: Other required changes at database if provided, will be cleared
1520 :return:
1521 """
quilesj4cda56b2019-12-05 10:02:20 +00001522 try:
tiernoe876f672020-02-13 14:34:48 +00001523 db_dict = other_update or {}
1524 db_dict["_admin.nslcmop"] = current_operation_id # for backward compatibility
1525 db_dict["_admin.current-operation"] = current_operation_id
1526 db_dict["_admin.operation-type"] = current_operation if current_operation != "IDLE" else None
quilesj4cda56b2019-12-05 10:02:20 +00001527 db_dict["currentOperation"] = current_operation
1528 db_dict["currentOperationID"] = current_operation_id
1529 db_dict["errorDescription"] = error_description
tiernoa2143262020-03-27 16:20:40 +00001530 db_dict["errorDetail"] = error_detail
tiernoe876f672020-02-13 14:34:48 +00001531
1532 if ns_state:
1533 db_dict["nsState"] = ns_state
quilesj4cda56b2019-12-05 10:02:20 +00001534 self.update_db_2("nsrs", nsr_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001535 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001536 self.logger.warn('Error writing NS status, ns={}: {}'.format(nsr_id, e))
1537
tiernoe876f672020-02-13 14:34:48 +00001538 def _write_op_status(self, op_id: str, stage: list = None, error_message: str = None, queuePosition: int = 0,
1539 operation_state: str = None, other_update: dict = None):
quilesj3655ae02019-12-12 16:08:35 +00001540 try:
tiernoe876f672020-02-13 14:34:48 +00001541 db_dict = other_update or {}
quilesj3655ae02019-12-12 16:08:35 +00001542 db_dict['queuePosition'] = queuePosition
tiernoe876f672020-02-13 14:34:48 +00001543 if isinstance(stage, list):
1544 db_dict['stage'] = stage[0]
1545 db_dict['detailed-status'] = " ".join(stage)
1546 elif stage is not None:
1547 db_dict['stage'] = str(stage)
1548
1549 if error_message is not None:
quilesj3655ae02019-12-12 16:08:35 +00001550 db_dict['errorMessage'] = error_message
tiernoe876f672020-02-13 14:34:48 +00001551 if operation_state is not None:
1552 db_dict['operationState'] = operation_state
1553 db_dict["statusEnteredTime"] = time()
quilesj3655ae02019-12-12 16:08:35 +00001554 self.update_db_2("nslcmops", op_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001555 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001556 self.logger.warn('Error writing OPERATION status for op_id: {} -> {}'.format(op_id, e))
1557
tierno51183952020-04-03 15:48:18 +00001558 def _write_all_config_status(self, db_nsr: dict, status: str):
quilesj3655ae02019-12-12 16:08:35 +00001559 try:
tierno51183952020-04-03 15:48:18 +00001560 nsr_id = db_nsr["_id"]
quilesj3655ae02019-12-12 16:08:35 +00001561 # configurationStatus
1562 config_status = db_nsr.get('configurationStatus')
1563 if config_status:
tierno51183952020-04-03 15:48:18 +00001564 db_nsr_update = {"configurationStatus.{}.status".format(index): status for index, v in
1565 enumerate(config_status) if v}
quilesj3655ae02019-12-12 16:08:35 +00001566 # update status
tierno51183952020-04-03 15:48:18 +00001567 self.update_db_2("nsrs", nsr_id, db_nsr_update)
quilesj3655ae02019-12-12 16:08:35 +00001568
tiernoe876f672020-02-13 14:34:48 +00001569 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001570 self.logger.warn('Error writing all configuration status, ns={}: {}'.format(nsr_id, e))
1571
quilesj63f90042020-01-17 09:53:55 +00001572 def _write_configuration_status(self, nsr_id: str, vca_index: int, status: str = None,
tierno51183952020-04-03 15:48:18 +00001573 element_under_configuration: str = None, element_type: str = None,
1574 other_update: dict = None):
quilesj3655ae02019-12-12 16:08:35 +00001575
1576 # self.logger.debug('_write_configuration_status(): vca_index={}, status={}'
1577 # .format(vca_index, status))
1578
1579 try:
1580 db_path = 'configurationStatus.{}.'.format(vca_index)
tierno51183952020-04-03 15:48:18 +00001581 db_dict = other_update or {}
quilesj63f90042020-01-17 09:53:55 +00001582 if status:
1583 db_dict[db_path + 'status'] = status
quilesj3655ae02019-12-12 16:08:35 +00001584 if element_under_configuration:
1585 db_dict[db_path + 'elementUnderConfiguration'] = element_under_configuration
1586 if element_type:
1587 db_dict[db_path + 'elementType'] = element_type
1588 self.update_db_2("nsrs", nsr_id, db_dict)
tiernoe876f672020-02-13 14:34:48 +00001589 except DbException as e:
quilesj3655ae02019-12-12 16:08:35 +00001590 self.logger.warn('Error writing configuration status={}, ns={}, vca_index={}: {}'
1591 .format(status, nsr_id, vca_index, e))
quilesj4cda56b2019-12-05 10:02:20 +00001592
tierno38089af2020-04-16 07:56:58 +00001593 async def _do_placement(self, logging_text, db_nslcmop, db_vnfrs):
1594 """
1595 Check and computes the placement, (vim account where to deploy). If it is decided by an external tool, it
1596 sends the request via kafka and wait until the result is wrote at database (nslcmops _admin.plca).
1597 Database is used because the result can be obtained from a different LCM worker in case of HA.
1598 :param logging_text: contains the prefix for logging, with the ns and nslcmop identifiers
1599 :param db_nslcmop: database content of nslcmop
1600 :param db_vnfrs: database content of vnfrs, indexed by member-vnf-index.
tierno8790a3d2020-04-23 22:49:52 +00001601 :return: True if some modification is done. Modifies database vnfrs and parameter db_vnfr with the
1602 computed 'vim-account-id'
tierno38089af2020-04-16 07:56:58 +00001603 """
tierno8790a3d2020-04-23 22:49:52 +00001604 modified = False
tierno38089af2020-04-16 07:56:58 +00001605 nslcmop_id = db_nslcmop['_id']
magnussonle9198bb2020-01-21 13:00:51 +01001606 placement_engine = deep_get(db_nslcmop, ('operationParams', 'placement-engine'))
1607 if placement_engine == "PLA":
tierno38089af2020-04-16 07:56:58 +00001608 self.logger.debug(logging_text + "Invoke and wait for placement optimization")
1609 await self.msg.aiowrite("pla", "get_placement", {'nslcmopId': nslcmop_id}, loop=self.loop)
magnussonle9198bb2020-01-21 13:00:51 +01001610 db_poll_interval = 5
tierno38089af2020-04-16 07:56:58 +00001611 wait = db_poll_interval * 10
magnussonle9198bb2020-01-21 13:00:51 +01001612 pla_result = None
1613 while not pla_result and wait >= 0:
1614 await asyncio.sleep(db_poll_interval)
1615 wait -= db_poll_interval
tierno38089af2020-04-16 07:56:58 +00001616 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
magnussonle9198bb2020-01-21 13:00:51 +01001617 pla_result = deep_get(db_nslcmop, ('_admin', 'pla'))
1618
1619 if not pla_result:
tierno38089af2020-04-16 07:56:58 +00001620 raise LcmException("Placement timeout for nslcmopId={}".format(nslcmop_id))
magnussonle9198bb2020-01-21 13:00:51 +01001621
1622 for pla_vnf in pla_result['vnf']:
1623 vnfr = db_vnfrs.get(pla_vnf['member-vnf-index'])
1624 if not pla_vnf.get('vimAccountId') or not vnfr:
1625 continue
tierno8790a3d2020-04-23 22:49:52 +00001626 modified = True
magnussonle9198bb2020-01-21 13:00:51 +01001627 self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, {"vim-account-id": pla_vnf['vimAccountId']})
tierno38089af2020-04-16 07:56:58 +00001628 # Modifies db_vnfrs
1629 vnfr["vim-account-id"] = pla_vnf['vimAccountId']
tierno8790a3d2020-04-23 22:49:52 +00001630 return modified
magnussonle9198bb2020-01-21 13:00:51 +01001631
1632 def update_nsrs_with_pla_result(self, params):
1633 try:
1634 nslcmop_id = deep_get(params, ('placement', 'nslcmopId'))
1635 self.update_db_2("nslcmops", nslcmop_id, {"_admin.pla": params.get('placement')})
1636 except Exception as e:
1637 self.logger.warn('Update failed for nslcmop_id={}:{}'.format(nslcmop_id, e))
1638
tierno59d22d22018-09-25 18:10:19 +02001639 async def instantiate(self, nsr_id, nslcmop_id):
quilesj7e13aeb2019-10-08 13:34:55 +02001640 """
1641
1642 :param nsr_id: ns instance to deploy
1643 :param nslcmop_id: operation to run
1644 :return:
1645 """
kuused124bfe2019-06-18 12:09:24 +02001646
1647 # Try to lock HA task here
1648 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
1649 if not task_is_locked_by_me:
quilesj3655ae02019-12-12 16:08:35 +00001650 self.logger.debug('instantiate() task is not locked by me, ns={}'.format(nsr_id))
kuused124bfe2019-06-18 12:09:24 +02001651 return
1652
tierno59d22d22018-09-25 18:10:19 +02001653 logging_text = "Task ns={} instantiate={} ".format(nsr_id, nslcmop_id)
1654 self.logger.debug(logging_text + "Enter")
quilesj7e13aeb2019-10-08 13:34:55 +02001655
tierno59d22d22018-09-25 18:10:19 +02001656 # get all needed from database
quilesj7e13aeb2019-10-08 13:34:55 +02001657
1658 # database nsrs record
tierno59d22d22018-09-25 18:10:19 +02001659 db_nsr = None
quilesj7e13aeb2019-10-08 13:34:55 +02001660
1661 # database nslcmops record
tierno59d22d22018-09-25 18:10:19 +02001662 db_nslcmop = None
quilesj7e13aeb2019-10-08 13:34:55 +02001663
1664 # update operation on nsrs
tiernoe876f672020-02-13 14:34:48 +00001665 db_nsr_update = {}
quilesj7e13aeb2019-10-08 13:34:55 +02001666 # update operation on nslcmops
tierno59d22d22018-09-25 18:10:19 +02001667 db_nslcmop_update = {}
quilesj7e13aeb2019-10-08 13:34:55 +02001668
tierno59d22d22018-09-25 18:10:19 +02001669 nslcmop_operation_state = None
quilesj7e13aeb2019-10-08 13:34:55 +02001670 db_vnfrs = {} # vnf's info indexed by member-index
1671 # n2vc_info = {}
tiernoe876f672020-02-13 14:34:48 +00001672 tasks_dict_info = {} # from task to info text
tierno59d22d22018-09-25 18:10:19 +02001673 exc = None
tiernoe876f672020-02-13 14:34:48 +00001674 error_list = []
1675 stage = ['Stage 1/5: preparation of the environment.', "Waiting for previous operations to terminate.", ""]
1676 # ^ stage, step, VIM progress
tierno59d22d22018-09-25 18:10:19 +02001677 try:
kuused124bfe2019-06-18 12:09:24 +02001678 # wait for any previous tasks in process
1679 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
1680
tiernob5203912020-08-11 11:20:13 +00001681 stage[1] = "Sync filesystem from database."
tierno21e42212020-07-09 13:51:20 +00001682 self.fs.sync() # TODO, make use of partial sync, only for the needed packages
1683
quilesj7e13aeb2019-10-08 13:34:55 +02001684 # STEP 0: Reading database (nslcmops, nsrs, nsds, vnfrs, vnfds)
tiernob5203912020-08-11 11:20:13 +00001685 stage[1] = "Reading from database."
quilesj4cda56b2019-12-05 10:02:20 +00001686 # nsState="BUILDING", currentOperation="INSTANTIATING", currentOperationID=nslcmop_id
tiernoe876f672020-02-13 14:34:48 +00001687 db_nsr_update["detailed-status"] = "creating"
1688 db_nsr_update["operational-status"] = "init"
quilesj4cda56b2019-12-05 10:02:20 +00001689 self._write_ns_status(
1690 nsr_id=nsr_id,
1691 ns_state="BUILDING",
1692 current_operation="INSTANTIATING",
tiernoe876f672020-02-13 14:34:48 +00001693 current_operation_id=nslcmop_id,
1694 other_update=db_nsr_update
1695 )
1696 self._write_op_status(
1697 op_id=nslcmop_id,
1698 stage=stage,
1699 queuePosition=0
quilesj4cda56b2019-12-05 10:02:20 +00001700 )
1701
quilesj7e13aeb2019-10-08 13:34:55 +02001702 # read from db: operation
tiernob5203912020-08-11 11:20:13 +00001703 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
tierno59d22d22018-09-25 18:10:19 +02001704 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
tierno744303e2020-01-13 16:46:31 +00001705 ns_params = db_nslcmop.get("operationParams")
1706 if ns_params and ns_params.get("timeout_ns_deploy"):
1707 timeout_ns_deploy = ns_params["timeout_ns_deploy"]
1708 else:
1709 timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
quilesj7e13aeb2019-10-08 13:34:55 +02001710
1711 # read from db: ns
tiernob5203912020-08-11 11:20:13 +00001712 stage[1] = "Getting nsr={} from db.".format(nsr_id)
tierno59d22d22018-09-25 18:10:19 +02001713 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
tiernob5203912020-08-11 11:20:13 +00001714 stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
tiernod732fb82020-05-21 13:18:23 +00001715 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
1716 db_nsr["nsd"] = nsd
tiernod8323042019-08-09 11:32:23 +00001717 # nsr_name = db_nsr["name"] # TODO short-name??
tierno47e86b52018-10-10 14:05:55 +02001718
quilesj7e13aeb2019-10-08 13:34:55 +02001719 # read from db: vnf's of this ns
tiernob5203912020-08-11 11:20:13 +00001720 stage[1] = "Getting vnfrs from db."
tiernoe876f672020-02-13 14:34:48 +00001721 self.logger.debug(logging_text + stage[1])
tierno27246d82018-09-27 15:59:09 +02001722 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
tierno27246d82018-09-27 15:59:09 +02001723
quilesj7e13aeb2019-10-08 13:34:55 +02001724 # read from db: vnfd's for every vnf
bravof922c4172020-11-24 21:21:43 -03001725 db_vnfds = [] # every vnfd data
quilesj7e13aeb2019-10-08 13:34:55 +02001726
1727 # for each vnf in ns, read vnfd
tierno27246d82018-09-27 15:59:09 +02001728 for vnfr in db_vnfrs_list:
bravof922c4172020-11-24 21:21:43 -03001729 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
1730 vnfd_id = vnfr["vnfd-id"]
1731 vnfd_ref = vnfr["vnfd-ref"]
lloretgalleg6d488782020-07-22 10:13:46 +00001732
quilesj7e13aeb2019-10-08 13:34:55 +02001733 # if we haven't this vnfd, read it from db
tierno27246d82018-09-27 15:59:09 +02001734 if vnfd_id not in db_vnfds:
quilesj63f90042020-01-17 09:53:55 +00001735 # read from db
tiernob5203912020-08-11 11:20:13 +00001736 stage[1] = "Getting vnfd={} id='{}' from db.".format(vnfd_id, vnfd_ref)
tiernoe876f672020-02-13 14:34:48 +00001737 self.logger.debug(logging_text + stage[1])
tierno27246d82018-09-27 15:59:09 +02001738 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
tierno27246d82018-09-27 15:59:09 +02001739
quilesj7e13aeb2019-10-08 13:34:55 +02001740 # store vnfd
David Garciad41dbd62020-12-10 12:52:52 +01001741 db_vnfds.append(vnfd)
quilesj7e13aeb2019-10-08 13:34:55 +02001742
1743 # Get or generates the _admin.deployed.VCA list
tiernoe4f7e6c2018-11-27 14:55:30 +00001744 vca_deployed_list = None
1745 if db_nsr["_admin"].get("deployed"):
1746 vca_deployed_list = db_nsr["_admin"]["deployed"].get("VCA")
1747 if vca_deployed_list is None:
1748 vca_deployed_list = []
quilesj3655ae02019-12-12 16:08:35 +00001749 configuration_status_list = []
tiernoe4f7e6c2018-11-27 14:55:30 +00001750 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
quilesj3655ae02019-12-12 16:08:35 +00001751 db_nsr_update["configurationStatus"] = configuration_status_list
quilesj7e13aeb2019-10-08 13:34:55 +02001752 # add _admin.deployed.VCA to db_nsr dictionary, value=vca_deployed_list
tierno98ad6ea2019-05-30 17:16:28 +00001753 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
tiernoe4f7e6c2018-11-27 14:55:30 +00001754 elif isinstance(vca_deployed_list, dict):
1755 # maintain backward compatibility. Change a dict to list at database
1756 vca_deployed_list = list(vca_deployed_list.values())
1757 db_nsr_update["_admin.deployed.VCA"] = vca_deployed_list
tierno98ad6ea2019-05-30 17:16:28 +00001758 populate_dict(db_nsr, ("_admin", "deployed", "VCA"), vca_deployed_list)
tiernoe4f7e6c2018-11-27 14:55:30 +00001759
tierno6cf25f52019-09-12 09:33:40 +00001760 if not isinstance(deep_get(db_nsr, ("_admin", "deployed", "RO", "vnfd")), list):
tiernoa009e552019-01-30 16:45:44 +00001761 populate_dict(db_nsr, ("_admin", "deployed", "RO", "vnfd"), [])
1762 db_nsr_update["_admin.deployed.RO.vnfd"] = []
tierno59d22d22018-09-25 18:10:19 +02001763
tiernobaa51102018-12-14 13:16:18 +00001764 # set state to INSTANTIATED. When instantiated NBI will not delete directly
1765 db_nsr_update["_admin.nsState"] = "INSTANTIATED"
1766 self.update_db_2("nsrs", nsr_id, db_nsr_update)
lloretgalleg6d488782020-07-22 10:13:46 +00001767 self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "INSTANTIATED"})
quilesj3655ae02019-12-12 16:08:35 +00001768
1769 # n2vc_redesign STEP 2 Deploy Network Scenario
tiernoe876f672020-02-13 14:34:48 +00001770 stage[0] = 'Stage 2/5: deployment of KDUs, VMs and execution environments.'
quilesj3655ae02019-12-12 16:08:35 +00001771 self._write_op_status(
1772 op_id=nslcmop_id,
tiernoe876f672020-02-13 14:34:48 +00001773 stage=stage
quilesj3655ae02019-12-12 16:08:35 +00001774 )
1775
tiernob5203912020-08-11 11:20:13 +00001776 stage[1] = "Deploying KDUs."
tiernoe876f672020-02-13 14:34:48 +00001777 # self.logger.debug(logging_text + "Before deploy_kdus")
calvinosanch9f9c6f22019-11-04 13:37:39 +01001778 # Call to deploy_kdus in case exists the "vdu:kdu" param
tiernoe876f672020-02-13 14:34:48 +00001779 await self.deploy_kdus(
1780 logging_text=logging_text,
1781 nsr_id=nsr_id,
1782 nslcmop_id=nslcmop_id,
1783 db_vnfrs=db_vnfrs,
1784 db_vnfds=db_vnfds,
1785 task_instantiation_info=tasks_dict_info,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001786 )
tiernoe876f672020-02-13 14:34:48 +00001787
1788 stage[1] = "Getting VCA public key."
tiernod8323042019-08-09 11:32:23 +00001789 # n2vc_redesign STEP 1 Get VCA public ssh-key
1790 # feature 1429. Add n2vc public key to needed VMs
tierno3bedc9b2019-11-27 15:46:57 +00001791 n2vc_key = self.n2vc.get_public_key()
tiernoa5088192019-11-26 16:12:53 +00001792 n2vc_key_list = [n2vc_key]
1793 if self.vca_config.get("public_key"):
1794 n2vc_key_list.append(self.vca_config["public_key"])
tierno98ad6ea2019-05-30 17:16:28 +00001795
tiernoe876f672020-02-13 14:34:48 +00001796 stage[1] = "Deploying NS at VIM."
tiernod8323042019-08-09 11:32:23 +00001797 task_ro = asyncio.ensure_future(
quilesj7e13aeb2019-10-08 13:34:55 +02001798 self.instantiate_RO(
1799 logging_text=logging_text,
1800 nsr_id=nsr_id,
1801 nsd=nsd,
1802 db_nsr=db_nsr,
1803 db_nslcmop=db_nslcmop,
1804 db_vnfrs=db_vnfrs,
bravof922c4172020-11-24 21:21:43 -03001805 db_vnfds=db_vnfds,
tiernoe876f672020-02-13 14:34:48 +00001806 n2vc_key_list=n2vc_key_list,
1807 stage=stage
tierno98ad6ea2019-05-30 17:16:28 +00001808 )
tiernod8323042019-08-09 11:32:23 +00001809 )
1810 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_RO", task_ro)
tiernoa2143262020-03-27 16:20:40 +00001811 tasks_dict_info[task_ro] = "Deploying at VIM"
tierno98ad6ea2019-05-30 17:16:28 +00001812
tiernod8323042019-08-09 11:32:23 +00001813 # n2vc_redesign STEP 3 to 6 Deploy N2VC
tiernoe876f672020-02-13 14:34:48 +00001814 stage[1] = "Deploying Execution Environments."
1815 self.logger.debug(logging_text + stage[1])
tierno98ad6ea2019-05-30 17:16:28 +00001816
tiernod8323042019-08-09 11:32:23 +00001817 nsi_id = None # TODO put nsi_id when this nsr belongs to a NSI
bravof922c4172020-11-24 21:21:43 -03001818 for vnf_profile in get_vnf_profiles(nsd):
1819 vnfd_id = vnf_profile["vnfd-id"]
1820 vnfd = find_in_list(db_vnfds, lambda a_vnf: a_vnf["id"] == vnfd_id)
1821 member_vnf_index = str(vnf_profile["id"])
tiernod8323042019-08-09 11:32:23 +00001822 db_vnfr = db_vnfrs[member_vnf_index]
1823 base_folder = vnfd["_admin"]["storage"]
1824 vdu_id = None
1825 vdu_index = 0
tierno98ad6ea2019-05-30 17:16:28 +00001826 vdu_name = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001827 kdu_name = None
tierno59d22d22018-09-25 18:10:19 +02001828
tierno8a518872018-12-21 13:42:14 +00001829 # Get additional parameters
bravof922c4172020-11-24 21:21:43 -03001830 deploy_params = {"OSM": get_osm_params(db_vnfr)}
tiernod8323042019-08-09 11:32:23 +00001831 if db_vnfr.get("additionalParamsForVnf"):
bravof922c4172020-11-24 21:21:43 -03001832 deploy_params.update(parse_yaml_strings(db_vnfr["additionalParamsForVnf"].copy()))
tierno8a518872018-12-21 13:42:14 +00001833
bravofe5a31bc2021-02-17 19:09:12 -03001834 descriptor_config = get_configuration(vnfd, vnfd["id"])
tierno588547c2020-07-01 15:30:20 +00001835 if descriptor_config:
quilesj7e13aeb2019-10-08 13:34:55 +02001836 self._deploy_n2vc(
tiernoa54150d2019-12-05 17:15:10 +00001837 logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
quilesj7e13aeb2019-10-08 13:34:55 +02001838 db_nsr=db_nsr,
1839 db_vnfr=db_vnfr,
1840 nslcmop_id=nslcmop_id,
1841 nsr_id=nsr_id,
1842 nsi_id=nsi_id,
1843 vnfd_id=vnfd_id,
1844 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001845 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001846 member_vnf_index=member_vnf_index,
1847 vdu_index=vdu_index,
1848 vdu_name=vdu_name,
1849 deploy_params=deploy_params,
1850 descriptor_config=descriptor_config,
1851 base_folder=base_folder,
tiernoe876f672020-02-13 14:34:48 +00001852 task_instantiation_info=tasks_dict_info,
1853 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001854 )
tierno59d22d22018-09-25 18:10:19 +02001855
1856 # Deploy charms for each VDU that supports one.
bravof922c4172020-11-24 21:21:43 -03001857 for vdud in get_vdu_list(vnfd):
tiernod8323042019-08-09 11:32:23 +00001858 vdu_id = vdud["id"]
bravofe5a31bc2021-02-17 19:09:12 -03001859 descriptor_config = get_configuration(vnfd, vdu_id)
bravof922c4172020-11-24 21:21:43 -03001860 vdur = find_in_list(db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id)
1861
tierno626e0152019-11-29 14:16:16 +00001862 if vdur.get("additionalParams"):
bravof922c4172020-11-24 21:21:43 -03001863 deploy_params_vdu = parse_yaml_strings(vdur["additionalParams"])
tierno626e0152019-11-29 14:16:16 +00001864 else:
1865 deploy_params_vdu = deploy_params
bravof922c4172020-11-24 21:21:43 -03001866 deploy_params_vdu["OSM"] = get_osm_params(db_vnfr, vdu_id, vdu_count_index=0)
1867 vdud_count = get_vdu_profile(vnfd, vdu_id).get("max-number-of-instances", 1)
1868
1869 self.logger.debug("VDUD > {}".format(vdud))
1870 self.logger.debug("Descriptor config > {}".format(descriptor_config))
tierno588547c2020-07-01 15:30:20 +00001871 if descriptor_config:
tiernod8323042019-08-09 11:32:23 +00001872 vdu_name = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001873 kdu_name = None
bravof922c4172020-11-24 21:21:43 -03001874 for vdu_index in range(vdud_count):
tiernod8323042019-08-09 11:32:23 +00001875 # TODO vnfr_params["rw_mgmt_ip"] = vdur["ip-address"]
quilesj7e13aeb2019-10-08 13:34:55 +02001876 self._deploy_n2vc(
tiernoa54150d2019-12-05 17:15:10 +00001877 logging_text=logging_text + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
1878 member_vnf_index, vdu_id, vdu_index),
quilesj7e13aeb2019-10-08 13:34:55 +02001879 db_nsr=db_nsr,
1880 db_vnfr=db_vnfr,
1881 nslcmop_id=nslcmop_id,
1882 nsr_id=nsr_id,
1883 nsi_id=nsi_id,
1884 vnfd_id=vnfd_id,
1885 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001886 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001887 member_vnf_index=member_vnf_index,
1888 vdu_index=vdu_index,
1889 vdu_name=vdu_name,
tierno626e0152019-11-29 14:16:16 +00001890 deploy_params=deploy_params_vdu,
quilesj7e13aeb2019-10-08 13:34:55 +02001891 descriptor_config=descriptor_config,
1892 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001893 task_instantiation_info=tasks_dict_info,
1894 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001895 )
bravof922c4172020-11-24 21:21:43 -03001896 for kdud in get_kdu_list(vnfd):
calvinosanch9f9c6f22019-11-04 13:37:39 +01001897 kdu_name = kdud["name"]
bravofe5a31bc2021-02-17 19:09:12 -03001898 descriptor_config = get_configuration(vnfd, kdu_name)
tierno588547c2020-07-01 15:30:20 +00001899 if descriptor_config:
calvinosanch9f9c6f22019-11-04 13:37:39 +01001900 vdu_id = None
1901 vdu_index = 0
1902 vdu_name = None
tierno72ef84f2020-10-06 08:22:07 +00001903 kdur = next(x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name)
bravof922c4172020-11-24 21:21:43 -03001904 deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
tierno72ef84f2020-10-06 08:22:07 +00001905 if kdur.get("additionalParams"):
bravof922c4172020-11-24 21:21:43 -03001906 deploy_params_kdu = parse_yaml_strings(kdur["additionalParams"])
tierno59d22d22018-09-25 18:10:19 +02001907
calvinosanch9f9c6f22019-11-04 13:37:39 +01001908 self._deploy_n2vc(
1909 logging_text=logging_text,
1910 db_nsr=db_nsr,
1911 db_vnfr=db_vnfr,
1912 nslcmop_id=nslcmop_id,
1913 nsr_id=nsr_id,
1914 nsi_id=nsi_id,
1915 vnfd_id=vnfd_id,
1916 vdu_id=vdu_id,
1917 kdu_name=kdu_name,
1918 member_vnf_index=member_vnf_index,
1919 vdu_index=vdu_index,
1920 vdu_name=vdu_name,
tierno72ef84f2020-10-06 08:22:07 +00001921 deploy_params=deploy_params_kdu,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001922 descriptor_config=descriptor_config,
1923 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001924 task_instantiation_info=tasks_dict_info,
1925 stage=stage
calvinosanch9f9c6f22019-11-04 13:37:39 +01001926 )
tierno59d22d22018-09-25 18:10:19 +02001927
tierno1b633412019-02-25 16:48:23 +00001928 # Check if this NS has a charm configuration
tiernod8323042019-08-09 11:32:23 +00001929 descriptor_config = nsd.get("ns-configuration")
1930 if descriptor_config and descriptor_config.get("juju"):
1931 vnfd_id = None
1932 db_vnfr = None
1933 member_vnf_index = None
1934 vdu_id = None
calvinosanch9f9c6f22019-11-04 13:37:39 +01001935 kdu_name = None
tiernod8323042019-08-09 11:32:23 +00001936 vdu_index = 0
1937 vdu_name = None
tierno1b633412019-02-25 16:48:23 +00001938
tiernod8323042019-08-09 11:32:23 +00001939 # Get additional parameters
David Garcia40603572020-12-10 20:10:53 +01001940 deploy_params = {"OSM": {"vim_account_id": ns_params["vimAccountId"]}}
tiernod8323042019-08-09 11:32:23 +00001941 if db_nsr.get("additionalParamsForNs"):
bravof922c4172020-11-24 21:21:43 -03001942 deploy_params.update(parse_yaml_strings(db_nsr["additionalParamsForNs"].copy()))
tiernod8323042019-08-09 11:32:23 +00001943 base_folder = nsd["_admin"]["storage"]
quilesj7e13aeb2019-10-08 13:34:55 +02001944 self._deploy_n2vc(
1945 logging_text=logging_text,
1946 db_nsr=db_nsr,
1947 db_vnfr=db_vnfr,
1948 nslcmop_id=nslcmop_id,
1949 nsr_id=nsr_id,
1950 nsi_id=nsi_id,
1951 vnfd_id=vnfd_id,
1952 vdu_id=vdu_id,
calvinosanch9f9c6f22019-11-04 13:37:39 +01001953 kdu_name=kdu_name,
quilesj7e13aeb2019-10-08 13:34:55 +02001954 member_vnf_index=member_vnf_index,
1955 vdu_index=vdu_index,
1956 vdu_name=vdu_name,
1957 deploy_params=deploy_params,
1958 descriptor_config=descriptor_config,
1959 base_folder=base_folder,
tierno8e2fae72020-04-01 15:21:15 +00001960 task_instantiation_info=tasks_dict_info,
1961 stage=stage
quilesj7e13aeb2019-10-08 13:34:55 +02001962 )
tierno1b633412019-02-25 16:48:23 +00001963
tiernoe876f672020-02-13 14:34:48 +00001964 # rest of staff will be done at finally
tierno1b633412019-02-25 16:48:23 +00001965
tiernoe876f672020-02-13 14:34:48 +00001966 except (ROclient.ROClientException, DbException, LcmException, N2VCException) as e:
1967 self.logger.error(logging_text + "Exit Exception while '{}': {}".format(stage[1], e))
tierno59d22d22018-09-25 18:10:19 +02001968 exc = e
1969 except asyncio.CancelledError:
tiernoe876f672020-02-13 14:34:48 +00001970 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(stage[1]))
tierno59d22d22018-09-25 18:10:19 +02001971 exc = "Operation was cancelled"
1972 except Exception as e:
1973 exc = traceback.format_exc()
tiernoe876f672020-02-13 14:34:48 +00001974 self.logger.critical(logging_text + "Exit Exception while '{}': {}".format(stage[1], e), exc_info=True)
tierno59d22d22018-09-25 18:10:19 +02001975 finally:
1976 if exc:
tiernoe876f672020-02-13 14:34:48 +00001977 error_list.append(str(exc))
tiernobaa51102018-12-14 13:16:18 +00001978 try:
tiernoe876f672020-02-13 14:34:48 +00001979 # wait for pending tasks
1980 if tasks_dict_info:
1981 stage[1] = "Waiting for instantiate pending tasks."
1982 self.logger.debug(logging_text + stage[1])
1983 error_list += await self._wait_for_tasks(logging_text, tasks_dict_info, timeout_ns_deploy,
1984 stage, nslcmop_id, nsr_id=nsr_id)
1985 stage[1] = stage[2] = ""
1986 except asyncio.CancelledError:
1987 error_list.append("Cancelled")
1988 # TODO cancel all tasks
1989 except Exception as exc:
1990 error_list.append(str(exc))
quilesj4cda56b2019-12-05 10:02:20 +00001991
tiernoe876f672020-02-13 14:34:48 +00001992 # update operation-status
1993 db_nsr_update["operational-status"] = "running"
1994 # let's begin with VCA 'configured' status (later we can change it)
1995 db_nsr_update["config-status"] = "configured"
1996 for task, task_name in tasks_dict_info.items():
1997 if not task.done() or task.cancelled() or task.exception():
1998 if task_name.startswith(self.task_name_deploy_vca):
1999 # A N2VC task is pending
2000 db_nsr_update["config-status"] = "failed"
quilesj4cda56b2019-12-05 10:02:20 +00002001 else:
tiernoe876f672020-02-13 14:34:48 +00002002 # RO or KDU task is pending
2003 db_nsr_update["operational-status"] = "failed"
quilesj3655ae02019-12-12 16:08:35 +00002004
tiernoe876f672020-02-13 14:34:48 +00002005 # update status at database
2006 if error_list:
tiernoa2143262020-03-27 16:20:40 +00002007 error_detail = ". ".join(error_list)
tiernoe876f672020-02-13 14:34:48 +00002008 self.logger.error(logging_text + error_detail)
tiernob5203912020-08-11 11:20:13 +00002009 error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
2010 error_description_nsr = 'Operation: INSTANTIATING.{}, {}'.format(nslcmop_id, stage[0])
quilesj3655ae02019-12-12 16:08:35 +00002011
tiernoa2143262020-03-27 16:20:40 +00002012 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
tiernoe876f672020-02-13 14:34:48 +00002013 db_nslcmop_update["detailed-status"] = error_detail
2014 nslcmop_operation_state = "FAILED"
2015 ns_state = "BROKEN"
2016 else:
tiernoa2143262020-03-27 16:20:40 +00002017 error_detail = None
tiernoe876f672020-02-13 14:34:48 +00002018 error_description_nsr = error_description_nslcmop = None
2019 ns_state = "READY"
2020 db_nsr_update["detailed-status"] = "Done"
2021 db_nslcmop_update["detailed-status"] = "Done"
2022 nslcmop_operation_state = "COMPLETED"
quilesj4cda56b2019-12-05 10:02:20 +00002023
tiernoe876f672020-02-13 14:34:48 +00002024 if db_nsr:
2025 self._write_ns_status(
2026 nsr_id=nsr_id,
2027 ns_state=ns_state,
2028 current_operation="IDLE",
2029 current_operation_id=None,
2030 error_description=error_description_nsr,
tiernoa2143262020-03-27 16:20:40 +00002031 error_detail=error_detail,
tiernoe876f672020-02-13 14:34:48 +00002032 other_update=db_nsr_update
2033 )
tiernoa17d4f42020-04-28 09:59:23 +00002034 self._write_op_status(
2035 op_id=nslcmop_id,
2036 stage="",
2037 error_message=error_description_nslcmop,
2038 operation_state=nslcmop_operation_state,
2039 other_update=db_nslcmop_update,
2040 )
quilesj3655ae02019-12-12 16:08:35 +00002041
tierno59d22d22018-09-25 18:10:19 +02002042 if nslcmop_operation_state:
2043 try:
2044 await self.msg.aiowrite("ns", "instantiated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tierno8a518872018-12-21 13:42:14 +00002045 "operationState": nslcmop_operation_state},
2046 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02002047 except Exception as e:
2048 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
2049
2050 self.logger.debug(logging_text + "Exit")
2051 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
2052
tierno588547c2020-07-01 15:30:20 +00002053 async def _add_vca_relations(self, logging_text, nsr_id, vca_index: int,
2054 timeout: int = 3600, vca_type: str = None) -> bool:
quilesj63f90042020-01-17 09:53:55 +00002055
2056 # steps:
2057 # 1. find all relations for this VCA
2058 # 2. wait for other peers related
2059 # 3. add relations
2060
2061 try:
tierno588547c2020-07-01 15:30:20 +00002062 vca_type = vca_type or "lxc_proxy_charm"
quilesj63f90042020-01-17 09:53:55 +00002063
2064 # STEP 1: find all relations for this VCA
2065
2066 # read nsr record
2067 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
David Garcia171f3542020-05-21 16:41:07 +02002068 nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
quilesj63f90042020-01-17 09:53:55 +00002069
2070 # this VCA data
2071 my_vca = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))[vca_index]
2072
2073 # read all ns-configuration relations
2074 ns_relations = list()
David Garcia171f3542020-05-21 16:41:07 +02002075 db_ns_relations = deep_get(nsd, ('ns-configuration', 'relation'))
quilesj63f90042020-01-17 09:53:55 +00002076 if db_ns_relations:
2077 for r in db_ns_relations:
2078 # check if this VCA is in the relation
2079 if my_vca.get('member-vnf-index') in\
2080 (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
2081 ns_relations.append(r)
2082
2083 # read all vnf-configuration relations
2084 vnf_relations = list()
2085 db_vnfd_list = db_nsr.get('vnfd-id')
2086 if db_vnfd_list:
2087 for vnfd in db_vnfd_list:
2088 db_vnfd = self.db.get_one("vnfds", {"_id": vnfd})
bravofe5a31bc2021-02-17 19:09:12 -03002089 db_vnf_relations = get_configuration(db_vnfd, db_vnfd["id"]).get("relation", [])
quilesj63f90042020-01-17 09:53:55 +00002090 if db_vnf_relations:
2091 for r in db_vnf_relations:
2092 # check if this VCA is in the relation
2093 if my_vca.get('vdu_id') in (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
2094 vnf_relations.append(r)
2095
2096 # if no relations, terminate
2097 if not ns_relations and not vnf_relations:
2098 self.logger.debug(logging_text + ' No relations')
2099 return True
2100
2101 self.logger.debug(logging_text + ' adding relations\n {}\n {}'.format(ns_relations, vnf_relations))
2102
2103 # add all relations
2104 start = time()
2105 while True:
2106 # check timeout
2107 now = time()
2108 if now - start >= timeout:
2109 self.logger.error(logging_text + ' : timeout adding relations')
2110 return False
2111
2112 # reload nsr from database (we need to update record: _admin.deloyed.VCA)
2113 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
2114
2115 # for each defined NS relation, find the VCA's related
tierno364c4572020-09-14 12:11:32 +00002116 for r in ns_relations.copy():
quilesj63f90042020-01-17 09:53:55 +00002117 from_vca_ee_id = None
2118 to_vca_ee_id = None
2119 from_vca_endpoint = None
2120 to_vca_endpoint = None
2121 vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
2122 for vca in vca_list:
2123 if vca.get('member-vnf-index') == r.get('entities')[0].get('id') \
2124 and vca.get('config_sw_installed'):
2125 from_vca_ee_id = vca.get('ee_id')
2126 from_vca_endpoint = r.get('entities')[0].get('endpoint')
2127 if vca.get('member-vnf-index') == r.get('entities')[1].get('id') \
2128 and vca.get('config_sw_installed'):
2129 to_vca_ee_id = vca.get('ee_id')
2130 to_vca_endpoint = r.get('entities')[1].get('endpoint')
2131 if from_vca_ee_id and to_vca_ee_id:
2132 # add relation
tierno588547c2020-07-01 15:30:20 +00002133 await self.vca_map[vca_type].add_relation(
quilesj63f90042020-01-17 09:53:55 +00002134 ee_id_1=from_vca_ee_id,
2135 ee_id_2=to_vca_ee_id,
2136 endpoint_1=from_vca_endpoint,
2137 endpoint_2=to_vca_endpoint)
2138 # remove entry from relations list
2139 ns_relations.remove(r)
2140 else:
2141 # check failed peers
2142 try:
2143 vca_status_list = db_nsr.get('configurationStatus')
2144 if vca_status_list:
2145 for i in range(len(vca_list)):
2146 vca = vca_list[i]
2147 vca_status = vca_status_list[i]
2148 if vca.get('member-vnf-index') == r.get('entities')[0].get('id'):
2149 if vca_status.get('status') == 'BROKEN':
2150 # peer broken: remove relation from list
2151 ns_relations.remove(r)
2152 if vca.get('member-vnf-index') == r.get('entities')[1].get('id'):
2153 if vca_status.get('status') == 'BROKEN':
2154 # peer broken: remove relation from list
2155 ns_relations.remove(r)
2156 except Exception:
2157 # ignore
2158 pass
2159
2160 # for each defined VNF relation, find the VCA's related
tierno364c4572020-09-14 12:11:32 +00002161 for r in vnf_relations.copy():
quilesj63f90042020-01-17 09:53:55 +00002162 from_vca_ee_id = None
2163 to_vca_ee_id = None
2164 from_vca_endpoint = None
2165 to_vca_endpoint = None
2166 vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
2167 for vca in vca_list:
David Garcia97be6832020-09-09 15:40:44 +02002168 key_to_check = "vdu_id"
2169 if vca.get("vdu_id") is None:
2170 key_to_check = "vnfd_id"
2171 if vca.get(key_to_check) == r.get('entities')[0].get('id') and vca.get('config_sw_installed'):
quilesj63f90042020-01-17 09:53:55 +00002172 from_vca_ee_id = vca.get('ee_id')
2173 from_vca_endpoint = r.get('entities')[0].get('endpoint')
David Garcia97be6832020-09-09 15:40:44 +02002174 if vca.get(key_to_check) == r.get('entities')[1].get('id') and vca.get('config_sw_installed'):
quilesj63f90042020-01-17 09:53:55 +00002175 to_vca_ee_id = vca.get('ee_id')
2176 to_vca_endpoint = r.get('entities')[1].get('endpoint')
2177 if from_vca_ee_id and to_vca_ee_id:
2178 # add relation
tierno588547c2020-07-01 15:30:20 +00002179 await self.vca_map[vca_type].add_relation(
quilesj63f90042020-01-17 09:53:55 +00002180 ee_id_1=from_vca_ee_id,
2181 ee_id_2=to_vca_ee_id,
2182 endpoint_1=from_vca_endpoint,
2183 endpoint_2=to_vca_endpoint)
2184 # remove entry from relations list
2185 vnf_relations.remove(r)
2186 else:
2187 # check failed peers
2188 try:
2189 vca_status_list = db_nsr.get('configurationStatus')
2190 if vca_status_list:
2191 for i in range(len(vca_list)):
2192 vca = vca_list[i]
2193 vca_status = vca_status_list[i]
2194 if vca.get('vdu_id') == r.get('entities')[0].get('id'):
2195 if vca_status.get('status') == 'BROKEN':
2196 # peer broken: remove relation from list
David Garcia092afbd2020-08-25 13:17:25 +02002197 vnf_relations.remove(r)
quilesj63f90042020-01-17 09:53:55 +00002198 if vca.get('vdu_id') == r.get('entities')[1].get('id'):
2199 if vca_status.get('status') == 'BROKEN':
2200 # peer broken: remove relation from list
David Garcia092afbd2020-08-25 13:17:25 +02002201 vnf_relations.remove(r)
quilesj63f90042020-01-17 09:53:55 +00002202 except Exception:
2203 # ignore
2204 pass
2205
2206 # wait for next try
2207 await asyncio.sleep(5.0)
2208
2209 if not ns_relations and not vnf_relations:
2210 self.logger.debug('Relations added')
2211 break
2212
2213 return True
2214
2215 except Exception as e:
2216 self.logger.warn(logging_text + ' ERROR adding relations: {}'.format(e))
2217 return False
2218
tierno7ecbc342020-09-21 14:05:39 +00002219 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 +00002220 vnfd: dict, k8s_instance_info: dict, k8params: dict = None, timeout: int = 600):
2221
tiernob9018152020-04-16 14:18:24 +00002222 try:
lloretgalleg7c121132020-07-08 07:53:22 +00002223 k8sclustertype = k8s_instance_info["k8scluster-type"]
2224 # Instantiate kdu
2225 db_dict_install = {"collection": "nsrs",
2226 "filter": {"_id": nsr_id},
2227 "path": nsr_db_path}
2228
David Garciad64e2742021-02-25 20:19:18 +01002229 kdu_instance = self.k8scluster_map[k8sclustertype].generate_kdu_instance_name(
2230 db_dict=db_dict_install,
2231 kdu_model=k8s_instance_info["kdu-model"],
David Garcia6d9a7512021-03-05 15:01:54 +01002232 kdu_name=k8s_instance_info["kdu-name"],
David Garciad64e2742021-02-25 20:19:18 +01002233 )
2234 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance})
2235 await self.k8scluster_map[k8sclustertype].install(
lloretgalleg7c121132020-07-08 07:53:22 +00002236 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2237 kdu_model=k8s_instance_info["kdu-model"],
2238 atomic=True,
2239 params=k8params,
2240 db_dict=db_dict_install,
2241 timeout=timeout,
2242 kdu_name=k8s_instance_info["kdu-name"],
David Garciad64e2742021-02-25 20:19:18 +01002243 namespace=k8s_instance_info["namespace"],
2244 kdu_instance=kdu_instance,
2245 )
lloretgalleg7c121132020-07-08 07:53:22 +00002246 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".kdu-instance": kdu_instance})
2247
2248 # Obtain services to obtain management service ip
2249 services = await self.k8scluster_map[k8sclustertype].get_services(
2250 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2251 kdu_instance=kdu_instance,
2252 namespace=k8s_instance_info["namespace"])
2253
2254 # Obtain management service info (if exists)
tierno7ecbc342020-09-21 14:05:39 +00002255 vnfr_update_dict = {}
lloretgalleg7c121132020-07-08 07:53:22 +00002256 if services:
tierno7ecbc342020-09-21 14:05:39 +00002257 vnfr_update_dict["kdur.{}.services".format(kdu_index)] = services
lloretgalleg7c121132020-07-08 07:53:22 +00002258 mgmt_services = [service for service in kdud.get("service", []) if service.get("mgmt-service")]
2259 for mgmt_service in mgmt_services:
2260 for service in services:
2261 if service["name"].startswith(mgmt_service["name"]):
2262 # Mgmt service found, Obtain service ip
2263 ip = service.get("external_ip", service.get("cluster_ip"))
2264 if isinstance(ip, list) and len(ip) == 1:
2265 ip = ip[0]
2266
2267 vnfr_update_dict["kdur.{}.ip-address".format(kdu_index)] = ip
2268
2269 # Check if must update also mgmt ip at the vnf
2270 service_external_cp = mgmt_service.get("external-connection-point-ref")
2271 if service_external_cp:
2272 if deep_get(vnfd, ("mgmt-interface", "cp")) == service_external_cp:
2273 vnfr_update_dict["ip-address"] = ip
2274
2275 break
2276 else:
2277 self.logger.warn("Mgmt service name: {} not found".format(mgmt_service["name"]))
2278
tierno7ecbc342020-09-21 14:05:39 +00002279 vnfr_update_dict["kdur.{}.status".format(kdu_index)] = "READY"
2280 self.update_db_2("vnfrs", vnfr_data.get("_id"), vnfr_update_dict)
lloretgalleg7c121132020-07-08 07:53:22 +00002281
bravof9a256db2021-02-22 18:02:07 -03002282 kdu_config = get_configuration(vnfd, k8s_instance_info["kdu-name"])
2283 if kdu_config and kdu_config.get("initial-config-primitive") and \
2284 get_juju_ee_ref(vnfd, k8s_instance_info["kdu-name"]) is None:
Dominik Fleischmannc1975dd2020-08-19 12:17:51 +02002285 initial_config_primitive_list = kdu_config.get("initial-config-primitive")
2286 initial_config_primitive_list.sort(key=lambda val: int(val["seq"]))
2287
2288 for initial_config_primitive in initial_config_primitive_list:
2289 primitive_params_ = self._map_primitive_params(initial_config_primitive, {}, {})
2290
2291 await asyncio.wait_for(
2292 self.k8scluster_map[k8sclustertype].exec_primitive(
2293 cluster_uuid=k8s_instance_info["k8scluster-uuid"],
2294 kdu_instance=kdu_instance,
2295 primitive_name=initial_config_primitive["name"],
ksaikiranr656b6dd2021-02-19 10:25:18 +05302296 params=primitive_params_, db_dict=db_dict_install),
Dominik Fleischmannc1975dd2020-08-19 12:17:51 +02002297 timeout=timeout)
2298
tiernob9018152020-04-16 14:18:24 +00002299 except Exception as e:
lloretgalleg7c121132020-07-08 07:53:22 +00002300 # Prepare update db with error and raise exception
tiernob9018152020-04-16 14:18:24 +00002301 try:
lloretgalleg7c121132020-07-08 07:53:22 +00002302 self.update_db_2("nsrs", nsr_id, {nsr_db_path + ".detailed-status": str(e)})
tierno7ecbc342020-09-21 14:05:39 +00002303 self.update_db_2("vnfrs", vnfr_data.get("_id"), {"kdur.{}.status".format(kdu_index): "ERROR"})
tiernob9018152020-04-16 14:18:24 +00002304 except Exception:
lloretgalleg7c121132020-07-08 07:53:22 +00002305 # ignore to keep original exception
tiernob9018152020-04-16 14:18:24 +00002306 pass
lloretgalleg7c121132020-07-08 07:53:22 +00002307 # reraise original error
2308 raise
2309
2310 return kdu_instance
tiernob9018152020-04-16 14:18:24 +00002311
tiernoe876f672020-02-13 14:34:48 +00002312 async def deploy_kdus(self, logging_text, nsr_id, nslcmop_id, db_vnfrs, db_vnfds, task_instantiation_info):
calvinosanch9f9c6f22019-11-04 13:37:39 +01002313 # Launch kdus if present in the descriptor
tierno626e0152019-11-29 14:16:16 +00002314
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002315 k8scluster_id_2_uuic = {"helm-chart-v3": {}, "helm-chart": {}, "juju-bundle": {}}
tierno626e0152019-11-29 14:16:16 +00002316
tierno16f4a4e2020-07-20 09:05:51 +00002317 async def _get_cluster_id(cluster_id, cluster_type):
tierno626e0152019-11-29 14:16:16 +00002318 nonlocal k8scluster_id_2_uuic
2319 if cluster_id in k8scluster_id_2_uuic[cluster_type]:
2320 return k8scluster_id_2_uuic[cluster_type][cluster_id]
2321
tierno16f4a4e2020-07-20 09:05:51 +00002322 # check if K8scluster is creating and wait look if previous tasks in process
2323 task_name, task_dependency = self.lcm_tasks.lookfor_related("k8scluster", cluster_id)
2324 if task_dependency:
2325 text = "Waiting for related tasks '{}' on k8scluster {} to be completed".format(task_name, cluster_id)
2326 self.logger.debug(logging_text + text)
2327 await asyncio.wait(task_dependency, timeout=3600)
2328
tierno626e0152019-11-29 14:16:16 +00002329 db_k8scluster = self.db.get_one("k8sclusters", {"_id": cluster_id}, fail_on_empty=False)
2330 if not db_k8scluster:
2331 raise LcmException("K8s cluster {} cannot be found".format(cluster_id))
tierno16f4a4e2020-07-20 09:05:51 +00002332
tierno626e0152019-11-29 14:16:16 +00002333 k8s_id = deep_get(db_k8scluster, ("_admin", cluster_type, "id"))
2334 if not k8s_id:
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002335 if cluster_type == "helm-chart-v3":
2336 try:
2337 # backward compatibility for existing clusters that have not been initialized for helm v3
2338 k8s_credentials = yaml.safe_dump(db_k8scluster.get("credentials"))
2339 k8s_id, uninstall_sw = await self.k8sclusterhelm3.init_env(k8s_credentials,
2340 reuse_cluster_uuid=cluster_id)
2341 db_k8scluster_update = {}
2342 db_k8scluster_update["_admin.helm-chart-v3.error_msg"] = None
2343 db_k8scluster_update["_admin.helm-chart-v3.id"] = k8s_id
2344 db_k8scluster_update["_admin.helm-chart-v3.created"] = uninstall_sw
2345 db_k8scluster_update["_admin.helm-chart-v3.operationalState"] = "ENABLED"
2346 self.update_db_2("k8sclusters", cluster_id, db_k8scluster_update)
2347 except Exception as e:
2348 self.logger.error(logging_text + "error initializing helm-v3 cluster: {}".format(str(e)))
2349 raise LcmException("K8s cluster '{}' has not been initialized for '{}'".format(cluster_id,
2350 cluster_type))
2351 else:
2352 raise LcmException("K8s cluster '{}' has not been initialized for '{}'".
2353 format(cluster_id, cluster_type))
tierno626e0152019-11-29 14:16:16 +00002354 k8scluster_id_2_uuic[cluster_type][cluster_id] = k8s_id
2355 return k8s_id
2356
2357 logging_text += "Deploy kdus: "
tiernoe876f672020-02-13 14:34:48 +00002358 step = ""
calvinosanch9f9c6f22019-11-04 13:37:39 +01002359 try:
tierno626e0152019-11-29 14:16:16 +00002360 db_nsr_update = {"_admin.deployed.K8s": []}
calvinosanch9f9c6f22019-11-04 13:37:39 +01002361 self.update_db_2("nsrs", nsr_id, db_nsr_update)
calvinosanch9f9c6f22019-11-04 13:37:39 +01002362
tierno626e0152019-11-29 14:16:16 +00002363 index = 0
tiernoe876f672020-02-13 14:34:48 +00002364 updated_cluster_list = []
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002365 updated_v3_cluster_list = []
tiernoe876f672020-02-13 14:34:48 +00002366
tierno626e0152019-11-29 14:16:16 +00002367 for vnfr_data in db_vnfrs.values():
lloretgalleg7c121132020-07-08 07:53:22 +00002368 for kdu_index, kdur in enumerate(get_iterable(vnfr_data, "kdur")):
2369 # Step 0: Prepare and set parameters
bravof922c4172020-11-24 21:21:43 -03002370 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
quilesjacde94f2020-01-23 10:07:08 +00002371 vnfd_id = vnfr_data.get('vnfd-id')
David Garciad41dbd62020-12-10 12:52:52 +01002372 vnfd_with_id = find_in_list(db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id)
2373 kdud = next(kdud for kdud in vnfd_with_id["kdu"] if kdud["name"] == kdur["kdu-name"])
tiernode1584f2020-04-07 09:07:33 +00002374 namespace = kdur.get("k8s-namespace")
tierno626e0152019-11-29 14:16:16 +00002375 if kdur.get("helm-chart"):
lloretgalleg07e53f52020-12-15 10:54:02 +00002376 kdumodel = kdur["helm-chart"]
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002377 # Default version: helm3, if helm-version is v2 assign v2
2378 k8sclustertype = "helm-chart-v3"
2379 self.logger.debug("kdur: {}".format(kdur))
2380 if kdur.get("helm-version") and kdur.get("helm-version") == "v2":
2381 k8sclustertype = "helm-chart"
tierno626e0152019-11-29 14:16:16 +00002382 elif kdur.get("juju-bundle"):
lloretgalleg07e53f52020-12-15 10:54:02 +00002383 kdumodel = kdur["juju-bundle"]
tiernoe876f672020-02-13 14:34:48 +00002384 k8sclustertype = "juju-bundle"
tierno626e0152019-11-29 14:16:16 +00002385 else:
tiernoe876f672020-02-13 14:34:48 +00002386 raise LcmException("kdu type for kdu='{}.{}' is neither helm-chart nor "
2387 "juju-bundle. Maybe an old NBI version is running".
2388 format(vnfr_data["member-vnf-index-ref"], kdur["kdu-name"]))
quilesjacde94f2020-01-23 10:07:08 +00002389 # check if kdumodel is a file and exists
2390 try:
David Garciad41dbd62020-12-10 12:52:52 +01002391 vnfd_with_id = find_in_list(db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id)
2392 storage = deep_get(vnfd_with_id, ('_admin', 'storage'))
tierno51183952020-04-03 15:48:18 +00002393 if storage and storage.get('pkg-dir'): # may be not present if vnfd has not artifacts
2394 # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
Dominik Fleischmann010c0e72020-05-18 15:19:11 +02002395 filename = '{}/{}/{}s/{}'.format(storage["folder"], storage["pkg-dir"], k8sclustertype,
tierno51183952020-04-03 15:48:18 +00002396 kdumodel)
2397 if self.fs.file_exists(filename, mode='file') or self.fs.file_exists(filename, mode='dir'):
2398 kdumodel = self.fs.path + filename
2399 except (asyncio.TimeoutError, asyncio.CancelledError):
tiernoe876f672020-02-13 14:34:48 +00002400 raise
2401 except Exception: # it is not a file
quilesjacde94f2020-01-23 10:07:08 +00002402 pass
lloretgallegedc5f332020-02-20 11:50:50 +01002403
tiernoe876f672020-02-13 14:34:48 +00002404 k8s_cluster_id = kdur["k8s-cluster"]["id"]
2405 step = "Synchronize repos for k8s cluster '{}'".format(k8s_cluster_id)
tierno16f4a4e2020-07-20 09:05:51 +00002406 cluster_uuid = await _get_cluster_id(k8s_cluster_id, k8sclustertype)
lloretgallegedc5f332020-02-20 11:50:50 +01002407
lloretgalleg7c121132020-07-08 07:53:22 +00002408 # Synchronize repos
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002409 if (k8sclustertype == "helm-chart" and cluster_uuid not in updated_cluster_list)\
2410 or (k8sclustertype == "helm-chart-v3" and cluster_uuid not in updated_v3_cluster_list):
tiernoe876f672020-02-13 14:34:48 +00002411 del_repo_list, added_repo_dict = await asyncio.ensure_future(
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002412 self.k8scluster_map[k8sclustertype].synchronize_repos(cluster_uuid=cluster_uuid))
tiernoe876f672020-02-13 14:34:48 +00002413 if del_repo_list or added_repo_dict:
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002414 if k8sclustertype == "helm-chart":
2415 unset = {'_admin.helm_charts_added.' + item: None for item in del_repo_list}
2416 updated = {'_admin.helm_charts_added.' +
2417 item: name for item, name in added_repo_dict.items()}
2418 updated_cluster_list.append(cluster_uuid)
2419 elif k8sclustertype == "helm-chart-v3":
2420 unset = {'_admin.helm_charts_v3_added.' + item: None for item in del_repo_list}
2421 updated = {'_admin.helm_charts_v3_added.' +
2422 item: name for item, name in added_repo_dict.items()}
2423 updated_v3_cluster_list.append(cluster_uuid)
2424 self.logger.debug(logging_text + "repos synchronized on k8s cluster "
2425 "'{}' to_delete: {}, to_add: {}".
2426 format(k8s_cluster_id, del_repo_list, added_repo_dict))
tiernoe876f672020-02-13 14:34:48 +00002427 self.db.set_one("k8sclusters", {"_id": k8s_cluster_id}, updated, unset=unset)
lloretgallegedc5f332020-02-20 11:50:50 +01002428
lloretgalleg7c121132020-07-08 07:53:22 +00002429 # Instantiate kdu
tiernoe876f672020-02-13 14:34:48 +00002430 step = "Instantiating KDU {}.{} in k8s cluster {}".format(vnfr_data["member-vnf-index-ref"],
2431 kdur["kdu-name"], k8s_cluster_id)
lloretgalleg7c121132020-07-08 07:53:22 +00002432 k8s_instance_info = {"kdu-instance": None,
2433 "k8scluster-uuid": cluster_uuid,
2434 "k8scluster-type": k8sclustertype,
2435 "member-vnf-index": vnfr_data["member-vnf-index-ref"],
2436 "kdu-name": kdur["kdu-name"],
2437 "kdu-model": kdumodel,
2438 "namespace": namespace}
tiernob9018152020-04-16 14:18:24 +00002439 db_path = "_admin.deployed.K8s.{}".format(index)
lloretgalleg7c121132020-07-08 07:53:22 +00002440 db_nsr_update[db_path] = k8s_instance_info
tierno626e0152019-11-29 14:16:16 +00002441 self.update_db_2("nsrs", nsr_id, db_nsr_update)
David Garciad41dbd62020-12-10 12:52:52 +01002442 vnfd_with_id = find_in_list(db_vnfds, lambda vnf: vnf["_id"] == vnfd_id)
tiernoa2143262020-03-27 16:20:40 +00002443 task = asyncio.ensure_future(
David Garciad41dbd62020-12-10 12:52:52 +01002444 self._install_kdu(nsr_id, db_path, vnfr_data, kdu_index, kdud, vnfd_with_id,
lloretgalleg7c121132020-07-08 07:53:22 +00002445 k8s_instance_info, k8params=desc_params, timeout=600))
tiernoe876f672020-02-13 14:34:48 +00002446 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_KDU-{}".format(index), task)
tiernoa2143262020-03-27 16:20:40 +00002447 task_instantiation_info[task] = "Deploying KDU {}".format(kdur["kdu-name"])
tiernoe876f672020-02-13 14:34:48 +00002448
tierno626e0152019-11-29 14:16:16 +00002449 index += 1
quilesjdd799ac2020-01-23 16:31:11 +00002450
tiernoe876f672020-02-13 14:34:48 +00002451 except (LcmException, asyncio.CancelledError):
2452 raise
calvinosanch9f9c6f22019-11-04 13:37:39 +01002453 except Exception as e:
tiernoe876f672020-02-13 14:34:48 +00002454 msg = "Exception {} while {}: {}".format(type(e).__name__, step, e)
2455 if isinstance(e, (N2VCException, DbException)):
2456 self.logger.error(logging_text + msg)
2457 else:
2458 self.logger.critical(logging_text + msg, exc_info=True)
quilesjdd799ac2020-01-23 16:31:11 +00002459 raise LcmException(msg)
calvinosanch9f9c6f22019-11-04 13:37:39 +01002460 finally:
calvinosanch9f9c6f22019-11-04 13:37:39 +01002461 if db_nsr_update:
2462 self.update_db_2("nsrs", nsr_id, db_nsr_update)
tiernoda6fb102019-11-23 00:36:52 +00002463
quilesj7e13aeb2019-10-08 13:34:55 +02002464 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 +01002465 kdu_name, member_vnf_index, vdu_index, vdu_name, deploy_params, descriptor_config,
tiernoe876f672020-02-13 14:34:48 +00002466 base_folder, task_instantiation_info, stage):
quilesj7e13aeb2019-10-08 13:34:55 +02002467 # launch instantiate_N2VC in a asyncio task and register task object
2468 # Look where information of this charm is at database <nsrs>._admin.deployed.VCA
2469 # if not found, create one entry and update database
quilesj7e13aeb2019-10-08 13:34:55 +02002470 # fill db_nsr._admin.deployed.VCA.<index>
tierno588547c2020-07-01 15:30:20 +00002471
2472 self.logger.debug(logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id))
bravof9a256db2021-02-22 18:02:07 -03002473 if "execution-environment-list" in descriptor_config:
2474 ee_list = descriptor_config.get("execution-environment-list", [])
tierno588547c2020-07-01 15:30:20 +00002475 else: # other types as script are not supported
2476 ee_list = []
2477
2478 for ee_item in ee_list:
2479 self.logger.debug(logging_text + "_deploy_n2vc ee_item juju={}, helm={}".format(ee_item.get('juju'),
2480 ee_item.get("helm-chart")))
tiernoa278b842020-07-08 15:33:55 +00002481 ee_descriptor_id = ee_item.get("id")
tierno588547c2020-07-01 15:30:20 +00002482 if ee_item.get("juju"):
2483 vca_name = ee_item['juju'].get('charm')
2484 vca_type = "lxc_proxy_charm" if ee_item['juju'].get('charm') is not None else "native_charm"
2485 if ee_item['juju'].get('cloud') == "k8s":
2486 vca_type = "k8s_proxy_charm"
2487 elif ee_item['juju'].get('proxy') is False:
2488 vca_type = "native_charm"
2489 elif ee_item.get("helm-chart"):
2490 vca_name = ee_item['helm-chart']
lloretgalleg18ebc3a2020-10-22 09:54:51 +00002491 if ee_item.get("helm-version") and ee_item.get("helm-version") == "v2":
2492 vca_type = "helm"
2493 else:
2494 vca_type = "helm-v3"
tierno588547c2020-07-01 15:30:20 +00002495 else:
2496 self.logger.debug(logging_text + "skipping non juju neither charm configuration")
quilesj7e13aeb2019-10-08 13:34:55 +02002497 continue
quilesj3655ae02019-12-12 16:08:35 +00002498
tierno588547c2020-07-01 15:30:20 +00002499 vca_index = -1
2500 for vca_index, vca_deployed in enumerate(db_nsr["_admin"]["deployed"]["VCA"]):
2501 if not vca_deployed:
2502 continue
2503 if vca_deployed.get("member-vnf-index") == member_vnf_index and \
2504 vca_deployed.get("vdu_id") == vdu_id and \
2505 vca_deployed.get("kdu_name") == kdu_name and \
tiernoa278b842020-07-08 15:33:55 +00002506 vca_deployed.get("vdu_count_index", 0) == vdu_index and \
2507 vca_deployed.get("ee_descriptor_id") == ee_descriptor_id:
tierno588547c2020-07-01 15:30:20 +00002508 break
2509 else:
2510 # not found, create one.
tiernoa278b842020-07-08 15:33:55 +00002511 target = "ns" if not member_vnf_index else "vnf/{}".format(member_vnf_index)
2512 if vdu_id:
2513 target += "/vdu/{}/{}".format(vdu_id, vdu_index or 0)
2514 elif kdu_name:
2515 target += "/kdu/{}".format(kdu_name)
tierno588547c2020-07-01 15:30:20 +00002516 vca_deployed = {
tiernoa278b842020-07-08 15:33:55 +00002517 "target_element": target,
2518 # ^ target_element will replace member-vnf-index, kdu_name, vdu_id ... in a single string
tierno588547c2020-07-01 15:30:20 +00002519 "member-vnf-index": member_vnf_index,
2520 "vdu_id": vdu_id,
2521 "kdu_name": kdu_name,
2522 "vdu_count_index": vdu_index,
2523 "operational-status": "init", # TODO revise
2524 "detailed-status": "", # TODO revise
2525 "step": "initial-deploy", # TODO revise
2526 "vnfd_id": vnfd_id,
2527 "vdu_name": vdu_name,
tiernoa278b842020-07-08 15:33:55 +00002528 "type": vca_type,
2529 "ee_descriptor_id": ee_descriptor_id
tierno588547c2020-07-01 15:30:20 +00002530 }
2531 vca_index += 1
quilesj3655ae02019-12-12 16:08:35 +00002532
tierno588547c2020-07-01 15:30:20 +00002533 # create VCA and configurationStatus in db
2534 db_dict = {
2535 "_admin.deployed.VCA.{}".format(vca_index): vca_deployed,
2536 "configurationStatus.{}".format(vca_index): dict()
2537 }
2538 self.update_db_2("nsrs", nsr_id, db_dict)
quilesj7e13aeb2019-10-08 13:34:55 +02002539
tierno588547c2020-07-01 15:30:20 +00002540 db_nsr["_admin"]["deployed"]["VCA"].append(vca_deployed)
2541
bravof922c4172020-11-24 21:21:43 -03002542 self.logger.debug("N2VC > NSR_ID > {}".format(nsr_id))
2543 self.logger.debug("N2VC > DB_NSR > {}".format(db_nsr))
2544 self.logger.debug("N2VC > VCA_DEPLOYED > {}".format(vca_deployed))
2545
tierno588547c2020-07-01 15:30:20 +00002546 # Launch task
2547 task_n2vc = asyncio.ensure_future(
2548 self.instantiate_N2VC(
2549 logging_text=logging_text,
2550 vca_index=vca_index,
2551 nsi_id=nsi_id,
2552 db_nsr=db_nsr,
2553 db_vnfr=db_vnfr,
2554 vdu_id=vdu_id,
2555 kdu_name=kdu_name,
2556 vdu_index=vdu_index,
2557 deploy_params=deploy_params,
2558 config_descriptor=descriptor_config,
2559 base_folder=base_folder,
2560 nslcmop_id=nslcmop_id,
2561 stage=stage,
2562 vca_type=vca_type,
tiernob996d942020-07-03 14:52:28 +00002563 vca_name=vca_name,
2564 ee_config_descriptor=ee_item
tierno588547c2020-07-01 15:30:20 +00002565 )
quilesj7e13aeb2019-10-08 13:34:55 +02002566 )
tierno588547c2020-07-01 15:30:20 +00002567 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_N2VC-{}".format(vca_index), task_n2vc)
2568 task_instantiation_info[task_n2vc] = self.task_name_deploy_vca + " {}.{}".format(
2569 member_vnf_index or "", vdu_id or "")
tiernobaa51102018-12-14 13:16:18 +00002570
tiernoc9556972019-07-05 15:25:25 +00002571 @staticmethod
kuuse0ca67472019-05-13 15:59:27 +02002572 def _create_nslcmop(nsr_id, operation, params):
2573 """
2574 Creates a ns-lcm-opp content to be stored at database.
2575 :param nsr_id: internal id of the instance
2576 :param operation: instantiate, terminate, scale, action, ...
2577 :param params: user parameters for the operation
2578 :return: dictionary following SOL005 format
2579 """
2580 # Raise exception if invalid arguments
2581 if not (nsr_id and operation and params):
2582 raise LcmException(
2583 "Parameters 'nsr_id', 'operation' and 'params' needed to create primitive not provided")
2584 now = time()
2585 _id = str(uuid4())
2586 nslcmop = {
2587 "id": _id,
2588 "_id": _id,
2589 # COMPLETED,PARTIALLY_COMPLETED,FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
2590 "operationState": "PROCESSING",
2591 "statusEnteredTime": now,
2592 "nsInstanceId": nsr_id,
2593 "lcmOperationType": operation,
2594 "startTime": now,
2595 "isAutomaticInvocation": False,
2596 "operationParams": params,
2597 "isCancelPending": False,
2598 "links": {
2599 "self": "/osm/nslcm/v1/ns_lcm_op_occs/" + _id,
2600 "nsInstance": "/osm/nslcm/v1/ns_instances/" + nsr_id,
2601 }
2602 }
2603 return nslcmop
2604
calvinosanch9f9c6f22019-11-04 13:37:39 +01002605 def _format_additional_params(self, params):
tierno626e0152019-11-29 14:16:16 +00002606 params = params or {}
calvinosanch9f9c6f22019-11-04 13:37:39 +01002607 for key, value in params.items():
2608 if str(value).startswith("!!yaml "):
2609 params[key] = yaml.safe_load(value[7:])
calvinosanch9f9c6f22019-11-04 13:37:39 +01002610 return params
2611
kuuse8b998e42019-07-30 15:22:16 +02002612 def _get_terminate_primitive_params(self, seq, vnf_index):
2613 primitive = seq.get('name')
2614 primitive_params = {}
2615 params = {
2616 "member_vnf_index": vnf_index,
2617 "primitive": primitive,
2618 "primitive_params": primitive_params,
2619 }
2620 desc_params = {}
2621 return self._map_primitive_params(seq, params, desc_params)
2622
kuuseac3a8882019-10-03 10:48:06 +02002623 # sub-operations
2624
tierno51183952020-04-03 15:48:18 +00002625 def _retry_or_skip_suboperation(self, db_nslcmop, op_index):
2626 op = deep_get(db_nslcmop, ('_admin', 'operations'), [])[op_index]
2627 if op.get('operationState') == 'COMPLETED':
kuuseac3a8882019-10-03 10:48:06 +02002628 # b. Skip sub-operation
2629 # _ns_execute_primitive() or RO.create_action() will NOT be executed
2630 return self.SUBOPERATION_STATUS_SKIP
2631 else:
tierno7c4e24c2020-05-13 08:41:35 +00002632 # c. retry executing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02002633 # The sub-operation exists, and operationState != 'COMPLETED'
tierno7c4e24c2020-05-13 08:41:35 +00002634 # Update operationState = 'PROCESSING' to indicate a retry.
kuuseac3a8882019-10-03 10:48:06 +02002635 operationState = 'PROCESSING'
2636 detailed_status = 'In progress'
2637 self._update_suboperation_status(
2638 db_nslcmop, op_index, operationState, detailed_status)
2639 # Return the sub-operation index
2640 # _ns_execute_primitive() or RO.create_action() will be called from scale()
2641 # with arguments extracted from the sub-operation
2642 return op_index
2643
2644 # Find a sub-operation where all keys in a matching dictionary must match
2645 # Returns the index of the matching sub-operation, or SUBOPERATION_STATUS_NOT_FOUND if no match
2646 def _find_suboperation(self, db_nslcmop, match):
tierno7c4e24c2020-05-13 08:41:35 +00002647 if db_nslcmop and match:
kuuseac3a8882019-10-03 10:48:06 +02002648 op_list = db_nslcmop.get('_admin', {}).get('operations', [])
2649 for i, op in enumerate(op_list):
2650 if all(op.get(k) == match[k] for k in match):
2651 return i
2652 return self.SUBOPERATION_STATUS_NOT_FOUND
2653
2654 # Update status for a sub-operation given its index
2655 def _update_suboperation_status(self, db_nslcmop, op_index, operationState, detailed_status):
2656 # Update DB for HA tasks
2657 q_filter = {'_id': db_nslcmop['_id']}
2658 update_dict = {'_admin.operations.{}.operationState'.format(op_index): operationState,
2659 '_admin.operations.{}.detailed-status'.format(op_index): detailed_status}
2660 self.db.set_one("nslcmops",
2661 q_filter=q_filter,
2662 update_dict=update_dict,
2663 fail_on_empty=False)
2664
2665 # Add sub-operation, return the index of the added sub-operation
2666 # Optionally, set operationState, detailed-status, and operationType
2667 # Status and type are currently set for 'scale' sub-operations:
2668 # 'operationState' : 'PROCESSING' | 'COMPLETED' | 'FAILED'
2669 # 'detailed-status' : status message
2670 # 'operationType': may be any type, in the case of scaling: 'PRE-SCALE' | 'POST-SCALE'
2671 # Status and operation type are currently only used for 'scale', but NOT for 'terminate' sub-operations.
tierno2357f4e2020-10-19 16:38:59 +00002672 def _add_suboperation(self, db_nslcmop, vnf_index, vdu_id, vdu_count_index, vdu_name, primitive,
quilesj7e13aeb2019-10-08 13:34:55 +02002673 mapped_primitive_params, operationState=None, detailed_status=None, operationType=None,
kuuseac3a8882019-10-03 10:48:06 +02002674 RO_nsr_id=None, RO_scaling_info=None):
tiernoe876f672020-02-13 14:34:48 +00002675 if not db_nslcmop:
kuuseac3a8882019-10-03 10:48:06 +02002676 return self.SUBOPERATION_STATUS_NOT_FOUND
2677 # Get the "_admin.operations" list, if it exists
2678 db_nslcmop_admin = db_nslcmop.get('_admin', {})
2679 op_list = db_nslcmop_admin.get('operations')
2680 # Create or append to the "_admin.operations" list
kuuse8b998e42019-07-30 15:22:16 +02002681 new_op = {'member_vnf_index': vnf_index,
2682 'vdu_id': vdu_id,
2683 'vdu_count_index': vdu_count_index,
2684 'primitive': primitive,
2685 'primitive_params': mapped_primitive_params}
kuuseac3a8882019-10-03 10:48:06 +02002686 if operationState:
2687 new_op['operationState'] = operationState
2688 if detailed_status:
2689 new_op['detailed-status'] = detailed_status
2690 if operationType:
2691 new_op['lcmOperationType'] = operationType
2692 if RO_nsr_id:
2693 new_op['RO_nsr_id'] = RO_nsr_id
2694 if RO_scaling_info:
2695 new_op['RO_scaling_info'] = RO_scaling_info
2696 if not op_list:
2697 # No existing operations, create key 'operations' with current operation as first list element
2698 db_nslcmop_admin.update({'operations': [new_op]})
2699 op_list = db_nslcmop_admin.get('operations')
2700 else:
2701 # Existing operations, append operation to list
2702 op_list.append(new_op)
kuuse8b998e42019-07-30 15:22:16 +02002703
kuuseac3a8882019-10-03 10:48:06 +02002704 db_nslcmop_update = {'_admin.operations': op_list}
2705 self.update_db_2("nslcmops", db_nslcmop['_id'], db_nslcmop_update)
2706 op_index = len(op_list) - 1
2707 return op_index
2708
2709 # Helper methods for scale() sub-operations
2710
2711 # pre-scale/post-scale:
2712 # Check for 3 different cases:
2713 # a. New: First time execution, return SUBOPERATION_STATUS_NEW
2714 # b. Skip: Existing sub-operation exists, operationState == 'COMPLETED', return SUBOPERATION_STATUS_SKIP
tierno7c4e24c2020-05-13 08:41:35 +00002715 # c. retry: Existing sub-operation exists, operationState != 'COMPLETED', return op_index to re-execute
quilesj7e13aeb2019-10-08 13:34:55 +02002716 def _check_or_add_scale_suboperation(self, db_nslcmop, vnf_index, vnf_config_primitive, primitive_params,
2717 operationType, RO_nsr_id=None, RO_scaling_info=None):
kuuseac3a8882019-10-03 10:48:06 +02002718 # Find this sub-operation
tierno7c4e24c2020-05-13 08:41:35 +00002719 if RO_nsr_id and RO_scaling_info:
kuuseac3a8882019-10-03 10:48:06 +02002720 operationType = 'SCALE-RO'
2721 match = {
2722 'member_vnf_index': vnf_index,
2723 'RO_nsr_id': RO_nsr_id,
2724 'RO_scaling_info': RO_scaling_info,
2725 }
2726 else:
2727 match = {
2728 'member_vnf_index': vnf_index,
2729 'primitive': vnf_config_primitive,
2730 'primitive_params': primitive_params,
2731 'lcmOperationType': operationType
2732 }
2733 op_index = self._find_suboperation(db_nslcmop, match)
tierno51183952020-04-03 15:48:18 +00002734 if op_index == self.SUBOPERATION_STATUS_NOT_FOUND:
kuuseac3a8882019-10-03 10:48:06 +02002735 # a. New sub-operation
2736 # The sub-operation does not exist, add it.
2737 # _ns_execute_primitive() will be called from scale() as usual, with non-modified arguments
2738 # The following parameters are set to None for all kind of scaling:
2739 vdu_id = None
2740 vdu_count_index = None
2741 vdu_name = None
tierno51183952020-04-03 15:48:18 +00002742 if RO_nsr_id and RO_scaling_info:
kuuseac3a8882019-10-03 10:48:06 +02002743 vnf_config_primitive = None
2744 primitive_params = None
2745 else:
2746 RO_nsr_id = None
2747 RO_scaling_info = None
2748 # Initial status for sub-operation
2749 operationState = 'PROCESSING'
2750 detailed_status = 'In progress'
2751 # Add sub-operation for pre/post-scaling (zero or more operations)
2752 self._add_suboperation(db_nslcmop,
2753 vnf_index,
2754 vdu_id,
2755 vdu_count_index,
2756 vdu_name,
2757 vnf_config_primitive,
2758 primitive_params,
2759 operationState,
2760 detailed_status,
2761 operationType,
2762 RO_nsr_id,
2763 RO_scaling_info)
2764 return self.SUBOPERATION_STATUS_NEW
2765 else:
2766 # Return either SUBOPERATION_STATUS_SKIP (operationState == 'COMPLETED'),
2767 # or op_index (operationState != 'COMPLETED')
tierno51183952020-04-03 15:48:18 +00002768 return self._retry_or_skip_suboperation(db_nslcmop, op_index)
kuuseac3a8882019-10-03 10:48:06 +02002769
preethika.pdf7d8e02019-12-10 13:10:48 +00002770 # Function to return execution_environment id
2771
2772 def _get_ee_id(self, vnf_index, vdu_id, vca_deployed_list):
tiernoe876f672020-02-13 14:34:48 +00002773 # TODO vdu_index_count
preethika.pdf7d8e02019-12-10 13:10:48 +00002774 for vca in vca_deployed_list:
2775 if vca["member-vnf-index"] == vnf_index and vca["vdu_id"] == vdu_id:
2776 return vca["ee_id"]
2777
tierno588547c2020-07-01 15:30:20 +00002778 async def destroy_N2VC(self, logging_text, db_nslcmop, vca_deployed, config_descriptor,
aktas13251562021-02-12 22:19:10 +03002779 vca_index, destroy_ee=True, exec_primitives=True, scaling_in=False):
tiernoe876f672020-02-13 14:34:48 +00002780 """
2781 Execute the terminate primitives and destroy the execution environment (if destroy_ee=False
2782 :param logging_text:
2783 :param db_nslcmop:
2784 :param vca_deployed: Dictionary of deployment info at db_nsr._admin.depoloyed.VCA.<INDEX>
2785 :param config_descriptor: Configuration descriptor of the NSD, VNFD, VNFD.vdu or VNFD.kdu
2786 :param vca_index: index in the database _admin.deployed.VCA
2787 :param destroy_ee: False to do not destroy, because it will be destroyed all of then at once
tierno588547c2020-07-01 15:30:20 +00002788 :param exec_primitives: False to do not execute terminate primitives, because the config is not completed or has
2789 not executed properly
aktas13251562021-02-12 22:19:10 +03002790 :param scaling_in: True destroys the application, False destroys the model
tiernoe876f672020-02-13 14:34:48 +00002791 :return: None or exception
2792 """
tiernoe876f672020-02-13 14:34:48 +00002793
tierno588547c2020-07-01 15:30:20 +00002794 self.logger.debug(
2795 logging_text + " vca_index: {}, vca_deployed: {}, config_descriptor: {}, destroy_ee: {}".format(
2796 vca_index, vca_deployed, config_descriptor, destroy_ee
2797 )
2798 )
2799
2800 vca_type = vca_deployed.get("type", "lxc_proxy_charm")
2801
2802 # execute terminate_primitives
2803 if exec_primitives:
bravof922c4172020-11-24 21:21:43 -03002804 terminate_primitives = get_ee_sorted_terminate_config_primitive_list(
2805 config_descriptor.get("terminate-config-primitive"), vca_deployed.get("ee_descriptor_id"))
tierno588547c2020-07-01 15:30:20 +00002806 vdu_id = vca_deployed.get("vdu_id")
2807 vdu_count_index = vca_deployed.get("vdu_count_index")
2808 vdu_name = vca_deployed.get("vdu_name")
2809 vnf_index = vca_deployed.get("member-vnf-index")
2810 if terminate_primitives and vca_deployed.get("needed_terminate"):
tierno588547c2020-07-01 15:30:20 +00002811 for seq in terminate_primitives:
2812 # For each sequence in list, get primitive and call _ns_execute_primitive()
2813 step = "Calling terminate action for vnf_member_index={} primitive={}".format(
2814 vnf_index, seq.get("name"))
2815 self.logger.debug(logging_text + step)
2816 # Create the primitive for each sequence, i.e. "primitive": "touch"
2817 primitive = seq.get('name')
2818 mapped_primitive_params = self._get_terminate_primitive_params(seq, vnf_index)
tierno588547c2020-07-01 15:30:20 +00002819
2820 # Add sub-operation
2821 self._add_suboperation(db_nslcmop,
2822 vnf_index,
2823 vdu_id,
2824 vdu_count_index,
2825 vdu_name,
2826 primitive,
2827 mapped_primitive_params)
2828 # Sub-operations: Call _ns_execute_primitive() instead of action()
2829 try:
2830 result, result_detail = await self._ns_execute_primitive(vca_deployed["ee_id"], primitive,
2831 mapped_primitive_params,
2832 vca_type=vca_type)
2833 except LcmException:
2834 # this happens when VCA is not deployed. In this case it is not needed to terminate
2835 continue
2836 result_ok = ['COMPLETED', 'PARTIALLY_COMPLETED']
2837 if result not in result_ok:
2838 raise LcmException("terminate_primitive {} for vnf_member_index={} fails with "
2839 "error {}".format(seq.get("name"), vnf_index, result_detail))
2840 # set that this VCA do not need terminated
2841 db_update_entry = "_admin.deployed.VCA.{}.needed_terminate".format(vca_index)
2842 self.update_db_2("nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False})
tiernoe876f672020-02-13 14:34:48 +00002843
tiernob996d942020-07-03 14:52:28 +00002844 if vca_deployed.get("prometheus_jobs") and self.prometheus:
2845 await self.prometheus.update(remove_jobs=vca_deployed["prometheus_jobs"])
2846
tiernoe876f672020-02-13 14:34:48 +00002847 if destroy_ee:
aktas13251562021-02-12 22:19:10 +03002848 await self.vca_map[vca_type].delete_execution_environment(vca_deployed["ee_id"], scaling_in=scaling_in)
kuuse0ca67472019-05-13 15:59:27 +02002849
tierno51183952020-04-03 15:48:18 +00002850 async def _delete_all_N2VC(self, db_nsr: dict):
2851 self._write_all_config_status(db_nsr=db_nsr, status='TERMINATING')
2852 namespace = "." + db_nsr["_id"]
tiernof59ad6c2020-04-08 12:50:52 +00002853 try:
2854 await self.n2vc.delete_namespace(namespace=namespace, total_timeout=self.timeout_charm_delete)
2855 except N2VCNotFound: # already deleted. Skip
2856 pass
tierno51183952020-04-03 15:48:18 +00002857 self._write_all_config_status(db_nsr=db_nsr, status='DELETED')
quilesj3655ae02019-12-12 16:08:35 +00002858
tiernoe876f672020-02-13 14:34:48 +00002859 async def _terminate_RO(self, logging_text, nsr_deployed, nsr_id, nslcmop_id, stage):
2860 """
2861 Terminates a deployment from RO
2862 :param logging_text:
2863 :param nsr_deployed: db_nsr._admin.deployed
2864 :param nsr_id:
2865 :param nslcmop_id:
2866 :param stage: list of string with the content to write on db_nslcmop.detailed-status.
2867 this method will update only the index 2, but it will write on database the concatenated content of the list
2868 :return:
2869 """
2870 db_nsr_update = {}
2871 failed_detail = []
2872 ro_nsr_id = ro_delete_action = None
2873 if nsr_deployed and nsr_deployed.get("RO"):
2874 ro_nsr_id = nsr_deployed["RO"].get("nsr_id")
2875 ro_delete_action = nsr_deployed["RO"].get("nsr_delete_action_id")
2876 try:
2877 if ro_nsr_id:
2878 stage[2] = "Deleting ns from VIM."
2879 db_nsr_update["detailed-status"] = " ".join(stage)
2880 self._write_op_status(nslcmop_id, stage)
2881 self.logger.debug(logging_text + stage[2])
2882 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2883 self._write_op_status(nslcmop_id, stage)
2884 desc = await self.RO.delete("ns", ro_nsr_id)
2885 ro_delete_action = desc["action_id"]
2886 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = ro_delete_action
2887 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
2888 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2889 if ro_delete_action:
2890 # wait until NS is deleted from VIM
2891 stage[2] = "Waiting ns deleted from VIM."
2892 detailed_status_old = None
2893 self.logger.debug(logging_text + stage[2] + " RO_id={} ro_delete_action={}".format(ro_nsr_id,
2894 ro_delete_action))
2895 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2896 self._write_op_status(nslcmop_id, stage)
kuused124bfe2019-06-18 12:09:24 +02002897
tiernoe876f672020-02-13 14:34:48 +00002898 delete_timeout = 20 * 60 # 20 minutes
2899 while delete_timeout > 0:
2900 desc = await self.RO.show(
2901 "ns",
2902 item_id_name=ro_nsr_id,
2903 extra_item="action",
2904 extra_item_id=ro_delete_action)
2905
2906 # deploymentStatus
2907 self._on_update_ro_db(nsrs_id=nsr_id, ro_descriptor=desc)
2908
2909 ns_status, ns_status_info = self.RO.check_action_status(desc)
2910 if ns_status == "ERROR":
2911 raise ROclient.ROClientException(ns_status_info)
2912 elif ns_status == "BUILD":
2913 stage[2] = "Deleting from VIM {}".format(ns_status_info)
2914 elif ns_status == "ACTIVE":
2915 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
2916 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2917 break
2918 else:
2919 assert False, "ROclient.check_action_status returns unknown {}".format(ns_status)
2920 if stage[2] != detailed_status_old:
2921 detailed_status_old = stage[2]
2922 db_nsr_update["detailed-status"] = " ".join(stage)
2923 self._write_op_status(nslcmop_id, stage)
2924 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2925 await asyncio.sleep(5, loop=self.loop)
2926 delete_timeout -= 5
2927 else: # delete_timeout <= 0:
2928 raise ROclient.ROClientException("Timeout waiting ns deleted from VIM")
2929
2930 except Exception as e:
2931 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2932 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2933 db_nsr_update["_admin.deployed.RO.nsr_id"] = None
2934 db_nsr_update["_admin.deployed.RO.nsr_status"] = "DELETED"
2935 db_nsr_update["_admin.deployed.RO.nsr_delete_action_id"] = None
2936 self.logger.debug(logging_text + "RO_ns_id={} already deleted".format(ro_nsr_id))
2937 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
tiernoa2143262020-03-27 16:20:40 +00002938 failed_detail.append("delete conflict: {}".format(e))
2939 self.logger.debug(logging_text + "RO_ns_id={} delete conflict: {}".format(ro_nsr_id, e))
tiernoe876f672020-02-13 14:34:48 +00002940 else:
tiernoa2143262020-03-27 16:20:40 +00002941 failed_detail.append("delete error: {}".format(e))
2942 self.logger.error(logging_text + "RO_ns_id={} delete error: {}".format(ro_nsr_id, e))
tiernoe876f672020-02-13 14:34:48 +00002943
2944 # Delete nsd
2945 if not failed_detail and deep_get(nsr_deployed, ("RO", "nsd_id")):
2946 ro_nsd_id = nsr_deployed["RO"]["nsd_id"]
2947 try:
2948 stage[2] = "Deleting nsd from RO."
2949 db_nsr_update["detailed-status"] = " ".join(stage)
2950 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2951 self._write_op_status(nslcmop_id, stage)
2952 await self.RO.delete("nsd", ro_nsd_id)
2953 self.logger.debug(logging_text + "ro_nsd_id={} deleted".format(ro_nsd_id))
2954 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
2955 except Exception as e:
2956 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2957 db_nsr_update["_admin.deployed.RO.nsd_id"] = None
2958 self.logger.debug(logging_text + "ro_nsd_id={} already deleted".format(ro_nsd_id))
2959 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
2960 failed_detail.append("ro_nsd_id={} delete conflict: {}".format(ro_nsd_id, e))
2961 self.logger.debug(logging_text + failed_detail[-1])
2962 else:
2963 failed_detail.append("ro_nsd_id={} delete error: {}".format(ro_nsd_id, e))
2964 self.logger.error(logging_text + failed_detail[-1])
2965
2966 if not failed_detail and deep_get(nsr_deployed, ("RO", "vnfd")):
2967 for index, vnf_deployed in enumerate(nsr_deployed["RO"]["vnfd"]):
2968 if not vnf_deployed or not vnf_deployed["id"]:
2969 continue
2970 try:
2971 ro_vnfd_id = vnf_deployed["id"]
2972 stage[2] = "Deleting member_vnf_index={} ro_vnfd_id={} from RO.".format(
2973 vnf_deployed["member-vnf-index"], ro_vnfd_id)
2974 db_nsr_update["detailed-status"] = " ".join(stage)
2975 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2976 self._write_op_status(nslcmop_id, stage)
2977 await self.RO.delete("vnfd", ro_vnfd_id)
2978 self.logger.debug(logging_text + "ro_vnfd_id={} deleted".format(ro_vnfd_id))
2979 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
2980 except Exception as e:
2981 if isinstance(e, ROclient.ROClientException) and e.http_code == 404: # not found
2982 db_nsr_update["_admin.deployed.RO.vnfd.{}.id".format(index)] = None
2983 self.logger.debug(logging_text + "ro_vnfd_id={} already deleted ".format(ro_vnfd_id))
2984 elif isinstance(e, ROclient.ROClientException) and e.http_code == 409: # conflict
2985 failed_detail.append("ro_vnfd_id={} delete conflict: {}".format(ro_vnfd_id, e))
2986 self.logger.debug(logging_text + failed_detail[-1])
2987 else:
2988 failed_detail.append("ro_vnfd_id={} delete error: {}".format(ro_vnfd_id, e))
2989 self.logger.error(logging_text + failed_detail[-1])
2990
tiernoa2143262020-03-27 16:20:40 +00002991 if failed_detail:
2992 stage[2] = "Error deleting from VIM"
2993 else:
2994 stage[2] = "Deleted from VIM"
tiernoe876f672020-02-13 14:34:48 +00002995 db_nsr_update["detailed-status"] = " ".join(stage)
2996 self.update_db_2("nsrs", nsr_id, db_nsr_update)
2997 self._write_op_status(nslcmop_id, stage)
2998
2999 if failed_detail:
tiernoa2143262020-03-27 16:20:40 +00003000 raise LcmException("; ".join(failed_detail))
tiernoe876f672020-02-13 14:34:48 +00003001
3002 async def terminate(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003003 # Try to lock HA task here
3004 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3005 if not task_is_locked_by_me:
3006 return
3007
tierno59d22d22018-09-25 18:10:19 +02003008 logging_text = "Task ns={} terminate={} ".format(nsr_id, nslcmop_id)
3009 self.logger.debug(logging_text + "Enter")
tiernoe876f672020-02-13 14:34:48 +00003010 timeout_ns_terminate = self.timeout_ns_terminate
tierno59d22d22018-09-25 18:10:19 +02003011 db_nsr = None
3012 db_nslcmop = None
tiernoa17d4f42020-04-28 09:59:23 +00003013 operation_params = None
tierno59d22d22018-09-25 18:10:19 +02003014 exc = None
tiernoe876f672020-02-13 14:34:48 +00003015 error_list = [] # annotates all failed error messages
tierno59d22d22018-09-25 18:10:19 +02003016 db_nslcmop_update = {}
tiernoc2564fe2019-01-28 16:18:56 +00003017 autoremove = False # autoremove after terminated
tiernoe876f672020-02-13 14:34:48 +00003018 tasks_dict_info = {}
3019 db_nsr_update = {}
3020 stage = ["Stage 1/3: Preparing task.", "Waiting for previous operations to terminate.", ""]
3021 # ^ contains [stage, step, VIM-status]
tierno59d22d22018-09-25 18:10:19 +02003022 try:
kuused124bfe2019-06-18 12:09:24 +02003023 # wait for any previous tasks in process
3024 await self.lcm_tasks.waitfor_related_HA("ns", 'nslcmops', nslcmop_id)
3025
tiernoe876f672020-02-13 14:34:48 +00003026 stage[1] = "Getting nslcmop={} from db.".format(nslcmop_id)
3027 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
3028 operation_params = db_nslcmop.get("operationParams") or {}
3029 if operation_params.get("timeout_ns_terminate"):
3030 timeout_ns_terminate = operation_params["timeout_ns_terminate"]
3031 stage[1] = "Getting nsr={} from db.".format(nsr_id)
3032 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
3033
3034 db_nsr_update["operational-status"] = "terminating"
3035 db_nsr_update["config-status"] = "terminating"
quilesj4cda56b2019-12-05 10:02:20 +00003036 self._write_ns_status(
3037 nsr_id=nsr_id,
3038 ns_state="TERMINATING",
3039 current_operation="TERMINATING",
tiernoe876f672020-02-13 14:34:48 +00003040 current_operation_id=nslcmop_id,
3041 other_update=db_nsr_update
quilesj4cda56b2019-12-05 10:02:20 +00003042 )
quilesj3655ae02019-12-12 16:08:35 +00003043 self._write_op_status(
3044 op_id=nslcmop_id,
tiernoe876f672020-02-13 14:34:48 +00003045 queuePosition=0,
3046 stage=stage
quilesj3655ae02019-12-12 16:08:35 +00003047 )
tiernoe876f672020-02-13 14:34:48 +00003048 nsr_deployed = deepcopy(db_nsr["_admin"].get("deployed")) or {}
tierno59d22d22018-09-25 18:10:19 +02003049 if db_nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
3050 return
tierno59d22d22018-09-25 18:10:19 +02003051
tiernoe876f672020-02-13 14:34:48 +00003052 stage[1] = "Getting vnf descriptors from db."
3053 db_vnfrs_list = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
3054 db_vnfds_from_id = {}
3055 db_vnfds_from_member_index = {}
3056 # Loop over VNFRs
3057 for vnfr in db_vnfrs_list:
3058 vnfd_id = vnfr["vnfd-id"]
3059 if vnfd_id not in db_vnfds_from_id:
3060 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
3061 db_vnfds_from_id[vnfd_id] = vnfd
3062 db_vnfds_from_member_index[vnfr["member-vnf-index-ref"]] = db_vnfds_from_id[vnfd_id]
calvinosanch9f9c6f22019-11-04 13:37:39 +01003063
tiernoe876f672020-02-13 14:34:48 +00003064 # Destroy individual execution environments when there are terminating primitives.
3065 # Rest of EE will be deleted at once
tierno588547c2020-07-01 15:30:20 +00003066 # TODO - check before calling _destroy_N2VC
3067 # if not operation_params.get("skip_terminate_primitives"):#
3068 # or not vca.get("needed_terminate"):
3069 stage[0] = "Stage 2/3 execute terminating primitives."
3070 self.logger.debug(logging_text + stage[0])
3071 stage[1] = "Looking execution environment that needs terminate."
3072 self.logger.debug(logging_text + stage[1])
bravof922c4172020-11-24 21:21:43 -03003073
tierno588547c2020-07-01 15:30:20 +00003074 for vca_index, vca in enumerate(get_iterable(nsr_deployed, "VCA")):
tierno588547c2020-07-01 15:30:20 +00003075 config_descriptor = None
3076 if not vca or not vca.get("ee_id"):
3077 continue
3078 if not vca.get("member-vnf-index"):
3079 # ns
3080 config_descriptor = db_nsr.get("ns-configuration")
3081 elif vca.get("vdu_id"):
3082 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
bravofe5a31bc2021-02-17 19:09:12 -03003083 config_descriptor = get_configuration(db_vnfd, vca.get("vdu_id"))
tierno588547c2020-07-01 15:30:20 +00003084 elif vca.get("kdu_name"):
3085 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
bravofe5a31bc2021-02-17 19:09:12 -03003086 config_descriptor = get_configuration(db_vnfd, vca.get("kdu_name"))
tierno588547c2020-07-01 15:30:20 +00003087 else:
bravofe5a31bc2021-02-17 19:09:12 -03003088 db_vnfd = db_vnfds_from_member_index[vca["member-vnf-index"]]
aktas13251562021-02-12 22:19:10 +03003089 config_descriptor = get_configuration(db_vnfd, db_vnfd["id"])
tierno588547c2020-07-01 15:30:20 +00003090 vca_type = vca.get("type")
3091 exec_terminate_primitives = (not operation_params.get("skip_terminate_primitives") and
3092 vca.get("needed_terminate"))
tiernoaebd7da2020-08-07 06:36:38 +00003093 # For helm we must destroy_ee. Also for native_charm, as juju_model cannot be deleted if there are
3094 # pending native charms
lloretgalleg18ebc3a2020-10-22 09:54:51 +00003095 destroy_ee = True if vca_type in ("helm", "helm-v3", "native_charm") else False
tierno86e33612020-09-16 14:13:06 +00003096 # self.logger.debug(logging_text + "vca_index: {}, ee_id: {}, vca_type: {} destroy_ee: {}".format(
3097 # vca_index, vca.get("ee_id"), vca_type, destroy_ee))
tiernob996d942020-07-03 14:52:28 +00003098 task = asyncio.ensure_future(
3099 self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor, vca_index,
3100 destroy_ee, exec_terminate_primitives))
tierno588547c2020-07-01 15:30:20 +00003101 tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
tierno59d22d22018-09-25 18:10:19 +02003102
tierno588547c2020-07-01 15:30:20 +00003103 # wait for pending tasks of terminate primitives
3104 if tasks_dict_info:
tierno86e33612020-09-16 14:13:06 +00003105 self.logger.debug(logging_text + 'Waiting for tasks {}'.format(list(tasks_dict_info.keys())))
tierno588547c2020-07-01 15:30:20 +00003106 error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
3107 min(self.timeout_charm_delete, timeout_ns_terminate),
3108 stage, nslcmop_id)
tierno86e33612020-09-16 14:13:06 +00003109 tasks_dict_info.clear()
tierno588547c2020-07-01 15:30:20 +00003110 if error_list:
3111 return # raise LcmException("; ".join(error_list))
tierno82974b22018-11-27 21:55:36 +00003112
tiernoe876f672020-02-13 14:34:48 +00003113 # remove All execution environments at once
3114 stage[0] = "Stage 3/3 delete all."
quilesj3655ae02019-12-12 16:08:35 +00003115
tierno49676be2020-04-07 16:34:35 +00003116 if nsr_deployed.get("VCA"):
3117 stage[1] = "Deleting all execution environments."
3118 self.logger.debug(logging_text + stage[1])
3119 task_delete_ee = asyncio.ensure_future(asyncio.wait_for(self._delete_all_N2VC(db_nsr=db_nsr),
3120 timeout=self.timeout_charm_delete))
3121 # task_delete_ee = asyncio.ensure_future(self.n2vc.delete_namespace(namespace="." + nsr_id))
3122 tasks_dict_info[task_delete_ee] = "Terminating all VCA"
tierno59d22d22018-09-25 18:10:19 +02003123
tiernoe876f672020-02-13 14:34:48 +00003124 # Delete from k8scluster
3125 stage[1] = "Deleting KDUs."
3126 self.logger.debug(logging_text + stage[1])
3127 # print(nsr_deployed)
3128 for kdu in get_iterable(nsr_deployed, "K8s"):
3129 if not kdu or not kdu.get("kdu-instance"):
3130 continue
3131 kdu_instance = kdu.get("kdu-instance")
tiernoa2143262020-03-27 16:20:40 +00003132 if kdu.get("k8scluster-type") in self.k8scluster_map:
tiernoe876f672020-02-13 14:34:48 +00003133 task_delete_kdu_instance = asyncio.ensure_future(
tiernoa2143262020-03-27 16:20:40 +00003134 self.k8scluster_map[kdu["k8scluster-type"]].uninstall(
3135 cluster_uuid=kdu.get("k8scluster-uuid"),
3136 kdu_instance=kdu_instance))
tiernoe876f672020-02-13 14:34:48 +00003137 else:
3138 self.logger.error(logging_text + "Unknown k8s deployment type {}".
3139 format(kdu.get("k8scluster-type")))
3140 continue
3141 tasks_dict_info[task_delete_kdu_instance] = "Terminating KDU '{}'".format(kdu.get("kdu-name"))
tierno59d22d22018-09-25 18:10:19 +02003142
3143 # remove from RO
tiernoe876f672020-02-13 14:34:48 +00003144 stage[1] = "Deleting ns from VIM."
tierno69f0d382020-05-07 13:08:09 +00003145 if self.ng_ro:
3146 task_delete_ro = asyncio.ensure_future(
3147 self._terminate_ng_ro(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
3148 else:
3149 task_delete_ro = asyncio.ensure_future(
3150 self._terminate_RO(logging_text, nsr_deployed, nsr_id, nslcmop_id, stage))
tiernoe876f672020-02-13 14:34:48 +00003151 tasks_dict_info[task_delete_ro] = "Removing deployment from VIM"
tierno59d22d22018-09-25 18:10:19 +02003152
tiernoe876f672020-02-13 14:34:48 +00003153 # rest of staff will be done at finally
3154
3155 except (ROclient.ROClientException, DbException, LcmException, N2VCException) as e:
3156 self.logger.error(logging_text + "Exit Exception {}".format(e))
3157 exc = e
3158 except asyncio.CancelledError:
3159 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(stage[1]))
3160 exc = "Operation was cancelled"
3161 except Exception as e:
3162 exc = traceback.format_exc()
3163 self.logger.critical(logging_text + "Exit Exception while '{}': {}".format(stage[1], e), exc_info=True)
3164 finally:
3165 if exc:
3166 error_list.append(str(exc))
tierno59d22d22018-09-25 18:10:19 +02003167 try:
tiernoe876f672020-02-13 14:34:48 +00003168 # wait for pending tasks
3169 if tasks_dict_info:
3170 stage[1] = "Waiting for terminate pending tasks."
3171 self.logger.debug(logging_text + stage[1])
3172 error_list += await self._wait_for_tasks(logging_text, tasks_dict_info, timeout_ns_terminate,
3173 stage, nslcmop_id)
3174 stage[1] = stage[2] = ""
3175 except asyncio.CancelledError:
3176 error_list.append("Cancelled")
3177 # TODO cancell all tasks
3178 except Exception as exc:
3179 error_list.append(str(exc))
3180 # update status at database
3181 if error_list:
3182 error_detail = "; ".join(error_list)
3183 # self.logger.error(logging_text + error_detail)
tiernob5203912020-08-11 11:20:13 +00003184 error_description_nslcmop = '{} Detail: {}'.format(stage[0], error_detail)
3185 error_description_nsr = 'Operation: TERMINATING.{}, {}.'.format(nslcmop_id, stage[0])
tierno59d22d22018-09-25 18:10:19 +02003186
tierno59d22d22018-09-25 18:10:19 +02003187 db_nsr_update["operational-status"] = "failed"
tiernoa2143262020-03-27 16:20:40 +00003188 db_nsr_update["detailed-status"] = error_description_nsr + " Detail: " + error_detail
tiernoe876f672020-02-13 14:34:48 +00003189 db_nslcmop_update["detailed-status"] = error_detail
3190 nslcmop_operation_state = "FAILED"
3191 ns_state = "BROKEN"
tierno59d22d22018-09-25 18:10:19 +02003192 else:
tiernoa2143262020-03-27 16:20:40 +00003193 error_detail = None
tiernoe876f672020-02-13 14:34:48 +00003194 error_description_nsr = error_description_nslcmop = None
3195 ns_state = "NOT_INSTANTIATED"
tierno59d22d22018-09-25 18:10:19 +02003196 db_nsr_update["operational-status"] = "terminated"
3197 db_nsr_update["detailed-status"] = "Done"
3198 db_nsr_update["_admin.nsState"] = "NOT_INSTANTIATED"
3199 db_nslcmop_update["detailed-status"] = "Done"
tiernoe876f672020-02-13 14:34:48 +00003200 nslcmop_operation_state = "COMPLETED"
tierno59d22d22018-09-25 18:10:19 +02003201
tiernoe876f672020-02-13 14:34:48 +00003202 if db_nsr:
3203 self._write_ns_status(
3204 nsr_id=nsr_id,
3205 ns_state=ns_state,
3206 current_operation="IDLE",
3207 current_operation_id=None,
3208 error_description=error_description_nsr,
tiernoa2143262020-03-27 16:20:40 +00003209 error_detail=error_detail,
tiernoe876f672020-02-13 14:34:48 +00003210 other_update=db_nsr_update
3211 )
tiernoa17d4f42020-04-28 09:59:23 +00003212 self._write_op_status(
3213 op_id=nslcmop_id,
3214 stage="",
3215 error_message=error_description_nslcmop,
3216 operation_state=nslcmop_operation_state,
3217 other_update=db_nslcmop_update,
3218 )
lloretgalleg6d488782020-07-22 10:13:46 +00003219 if ns_state == "NOT_INSTANTIATED":
3220 try:
3221 self.db.set_list("vnfrs", {"nsr-id-ref": nsr_id}, {"_admin.nsState": "NOT_INSTANTIATED"})
3222 except DbException as e:
3223 self.logger.warn(logging_text + 'Error writing VNFR status for nsr-id-ref: {} -> {}'.
3224 format(nsr_id, e))
tiernoa17d4f42020-04-28 09:59:23 +00003225 if operation_params:
tiernoe876f672020-02-13 14:34:48 +00003226 autoremove = operation_params.get("autoremove", False)
tierno59d22d22018-09-25 18:10:19 +02003227 if nslcmop_operation_state:
3228 try:
3229 await self.msg.aiowrite("ns", "terminated", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tiernoc2564fe2019-01-28 16:18:56 +00003230 "operationState": nslcmop_operation_state,
3231 "autoremove": autoremove},
tierno8a518872018-12-21 13:42:14 +00003232 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02003233 except Exception as e:
3234 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
quilesj7e13aeb2019-10-08 13:34:55 +02003235
tierno59d22d22018-09-25 18:10:19 +02003236 self.logger.debug(logging_text + "Exit")
3237 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_terminate")
3238
tiernoe876f672020-02-13 14:34:48 +00003239 async def _wait_for_tasks(self, logging_text, created_tasks_info, timeout, stage, nslcmop_id, nsr_id=None):
3240 time_start = time()
tiernoa2143262020-03-27 16:20:40 +00003241 error_detail_list = []
tiernoe876f672020-02-13 14:34:48 +00003242 error_list = []
3243 pending_tasks = list(created_tasks_info.keys())
3244 num_tasks = len(pending_tasks)
3245 num_done = 0
3246 stage[1] = "{}/{}.".format(num_done, num_tasks)
3247 self._write_op_status(nslcmop_id, stage)
tiernoe876f672020-02-13 14:34:48 +00003248 while pending_tasks:
tiernoa2143262020-03-27 16:20:40 +00003249 new_error = None
tiernoe876f672020-02-13 14:34:48 +00003250 _timeout = timeout + time_start - time()
3251 done, pending_tasks = await asyncio.wait(pending_tasks, timeout=_timeout,
3252 return_when=asyncio.FIRST_COMPLETED)
3253 num_done += len(done)
3254 if not done: # Timeout
3255 for task in pending_tasks:
tiernoa2143262020-03-27 16:20:40 +00003256 new_error = created_tasks_info[task] + ": Timeout"
3257 error_detail_list.append(new_error)
3258 error_list.append(new_error)
tiernoe876f672020-02-13 14:34:48 +00003259 break
3260 for task in done:
3261 if task.cancelled():
tierno067e04a2020-03-31 12:53:13 +00003262 exc = "Cancelled"
tiernoe876f672020-02-13 14:34:48 +00003263 else:
3264 exc = task.exception()
tierno067e04a2020-03-31 12:53:13 +00003265 if exc:
3266 if isinstance(exc, asyncio.TimeoutError):
3267 exc = "Timeout"
3268 new_error = created_tasks_info[task] + ": {}".format(exc)
3269 error_list.append(created_tasks_info[task])
3270 error_detail_list.append(new_error)
tierno28c63da2020-04-20 16:28:56 +00003271 if isinstance(exc, (str, DbException, N2VCException, ROclient.ROClientException, LcmException,
tierno2357f4e2020-10-19 16:38:59 +00003272 K8sException, NgRoException)):
tierno067e04a2020-03-31 12:53:13 +00003273 self.logger.error(logging_text + new_error)
tiernoe876f672020-02-13 14:34:48 +00003274 else:
tierno067e04a2020-03-31 12:53:13 +00003275 exc_traceback = "".join(traceback.format_exception(None, exc, exc.__traceback__))
tierno2357f4e2020-10-19 16:38:59 +00003276 self.logger.error(logging_text + created_tasks_info[task] + " " + exc_traceback)
tierno067e04a2020-03-31 12:53:13 +00003277 else:
3278 self.logger.debug(logging_text + created_tasks_info[task] + ": Done")
tiernoe876f672020-02-13 14:34:48 +00003279 stage[1] = "{}/{}.".format(num_done, num_tasks)
3280 if new_error:
tiernoa2143262020-03-27 16:20:40 +00003281 stage[1] += " Errors: " + ". ".join(error_detail_list) + "."
tiernoe876f672020-02-13 14:34:48 +00003282 if nsr_id: # update also nsr
tiernoa2143262020-03-27 16:20:40 +00003283 self.update_db_2("nsrs", nsr_id, {"errorDescription": "Error at: " + ", ".join(error_list),
3284 "errorDetail": ". ".join(error_detail_list)})
tiernoe876f672020-02-13 14:34:48 +00003285 self._write_op_status(nslcmop_id, stage)
tiernoa2143262020-03-27 16:20:40 +00003286 return error_detail_list
tiernoe876f672020-02-13 14:34:48 +00003287
tiernoda1ff8c2020-10-22 14:12:46 +00003288 @staticmethod
3289 def _map_primitive_params(primitive_desc, params, instantiation_params):
tiernoda964822019-01-14 15:53:47 +00003290 """
3291 Generates the params to be provided to charm before executing primitive. If user does not provide a parameter,
3292 The default-value is used. If it is between < > it look for a value at instantiation_params
3293 :param primitive_desc: portion of VNFD/NSD that describes primitive
3294 :param params: Params provided by user
3295 :param instantiation_params: Instantiation params provided by user
3296 :return: a dictionary with the calculated params
3297 """
3298 calculated_params = {}
3299 for parameter in primitive_desc.get("parameter", ()):
3300 param_name = parameter["name"]
3301 if param_name in params:
3302 calculated_params[param_name] = params[param_name]
tierno98ad6ea2019-05-30 17:16:28 +00003303 elif "default-value" in parameter or "value" in parameter:
3304 if "value" in parameter:
3305 calculated_params[param_name] = parameter["value"]
3306 else:
3307 calculated_params[param_name] = parameter["default-value"]
3308 if isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("<") \
3309 and calculated_params[param_name].endswith(">"):
3310 if calculated_params[param_name][1:-1] in instantiation_params:
3311 calculated_params[param_name] = instantiation_params[calculated_params[param_name][1:-1]]
tiernoda964822019-01-14 15:53:47 +00003312 else:
3313 raise LcmException("Parameter {} needed to execute primitive {} not provided".
tiernod8323042019-08-09 11:32:23 +00003314 format(calculated_params[param_name], primitive_desc["name"]))
tiernoda964822019-01-14 15:53:47 +00003315 else:
3316 raise LcmException("Parameter {} needed to execute primitive {} not provided".
3317 format(param_name, primitive_desc["name"]))
tierno59d22d22018-09-25 18:10:19 +02003318
tiernoda964822019-01-14 15:53:47 +00003319 if isinstance(calculated_params[param_name], (dict, list, tuple)):
bravof922c4172020-11-24 21:21:43 -03003320 calculated_params[param_name] = yaml.safe_dump(calculated_params[param_name],
3321 default_flow_style=True, width=256)
tiernoda964822019-01-14 15:53:47 +00003322 elif isinstance(calculated_params[param_name], str) and calculated_params[param_name].startswith("!!yaml "):
3323 calculated_params[param_name] = calculated_params[param_name][7:]
tiernofa40e692020-10-14 14:59:36 +00003324 if parameter.get("data-type") == "INTEGER":
3325 try:
3326 calculated_params[param_name] = int(calculated_params[param_name])
3327 except ValueError: # error converting string to int
3328 raise LcmException(
3329 "Parameter {} of primitive {} must be integer".format(param_name, primitive_desc["name"]))
3330 elif parameter.get("data-type") == "BOOLEAN":
3331 calculated_params[param_name] = not ((str(calculated_params[param_name])).lower() == 'false')
tiernoc3f2a822019-11-05 13:45:04 +00003332
3333 # add always ns_config_info if primitive name is config
3334 if primitive_desc["name"] == "config":
3335 if "ns_config_info" in instantiation_params:
3336 calculated_params["ns_config_info"] = instantiation_params["ns_config_info"]
tiernoda964822019-01-14 15:53:47 +00003337 return calculated_params
3338
tiernoa278b842020-07-08 15:33:55 +00003339 def _look_for_deployed_vca(self, deployed_vca, member_vnf_index, vdu_id, vdu_count_index, kdu_name=None,
3340 ee_descriptor_id=None):
tiernoe876f672020-02-13 14:34:48 +00003341 # find vca_deployed record for this action. Raise LcmException if not found or there is not any id.
3342 for vca in deployed_vca:
3343 if not vca:
3344 continue
3345 if member_vnf_index != vca["member-vnf-index"] or vdu_id != vca["vdu_id"]:
3346 continue
tiernoe876f672020-02-13 14:34:48 +00003347 if vdu_count_index is not None and vdu_count_index != vca["vdu_count_index"]:
3348 continue
3349 if kdu_name and kdu_name != vca["kdu_name"]:
3350 continue
tiernoa278b842020-07-08 15:33:55 +00003351 if ee_descriptor_id and ee_descriptor_id != vca["ee_descriptor_id"]:
3352 continue
tiernoe876f672020-02-13 14:34:48 +00003353 break
3354 else:
3355 # vca_deployed not found
tiernoa278b842020-07-08 15:33:55 +00003356 raise LcmException("charm for member_vnf_index={} vdu_id={}.{} kdu_name={} execution-environment-list.id={}"
3357 " is not deployed".format(member_vnf_index, vdu_id, vdu_count_index, kdu_name,
3358 ee_descriptor_id))
tiernoe876f672020-02-13 14:34:48 +00003359 # get ee_id
3360 ee_id = vca.get("ee_id")
tierno588547c2020-07-01 15:30:20 +00003361 vca_type = vca.get("type", "lxc_proxy_charm") # default value for backward compatibility - proxy charm
tiernoe876f672020-02-13 14:34:48 +00003362 if not ee_id:
tierno067e04a2020-03-31 12:53:13 +00003363 raise LcmException("charm for member_vnf_index={} vdu_id={} kdu_name={} vdu_count_index={} has not "
tiernoe876f672020-02-13 14:34:48 +00003364 "execution environment"
tierno067e04a2020-03-31 12:53:13 +00003365 .format(member_vnf_index, vdu_id, kdu_name, vdu_count_index))
tierno588547c2020-07-01 15:30:20 +00003366 return ee_id, vca_type
tiernoe876f672020-02-13 14:34:48 +00003367
bravof922c4172020-11-24 21:21:43 -03003368 async def _ns_execute_primitive(self, ee_id, primitive, primitive_params, retries=0, retries_interval=30,
3369 timeout=None, vca_type=None, db_dict=None) -> (str, str):
tiernoda964822019-01-14 15:53:47 +00003370 try:
tierno98ad6ea2019-05-30 17:16:28 +00003371 if primitive == "config":
3372 primitive_params = {"params": primitive_params}
tierno2fc7ce52019-06-11 22:50:01 +00003373
tierno588547c2020-07-01 15:30:20 +00003374 vca_type = vca_type or "lxc_proxy_charm"
3375
quilesj7e13aeb2019-10-08 13:34:55 +02003376 while retries >= 0:
3377 try:
tierno067e04a2020-03-31 12:53:13 +00003378 output = await asyncio.wait_for(
tierno588547c2020-07-01 15:30:20 +00003379 self.vca_map[vca_type].exec_primitive(
tierno067e04a2020-03-31 12:53:13 +00003380 ee_id=ee_id,
3381 primitive_name=primitive,
3382 params_dict=primitive_params,
3383 progress_timeout=self.timeout_progress_primitive,
tierno588547c2020-07-01 15:30:20 +00003384 total_timeout=self.timeout_primitive,
3385 db_dict=db_dict),
tierno067e04a2020-03-31 12:53:13 +00003386 timeout=timeout or self.timeout_primitive)
quilesj7e13aeb2019-10-08 13:34:55 +02003387 # execution was OK
3388 break
tierno067e04a2020-03-31 12:53:13 +00003389 except asyncio.CancelledError:
3390 raise
3391 except Exception as e: # asyncio.TimeoutError
3392 if isinstance(e, asyncio.TimeoutError):
3393 e = "Timeout"
quilesj7e13aeb2019-10-08 13:34:55 +02003394 retries -= 1
3395 if retries >= 0:
tierno73d8bd02019-11-18 17:33:27 +00003396 self.logger.debug('Error executing action {} on {} -> {}'.format(primitive, ee_id, e))
quilesj7e13aeb2019-10-08 13:34:55 +02003397 # wait and retry
3398 await asyncio.sleep(retries_interval, loop=self.loop)
tierno73d8bd02019-11-18 17:33:27 +00003399 else:
tierno067e04a2020-03-31 12:53:13 +00003400 return 'FAILED', str(e)
quilesj7e13aeb2019-10-08 13:34:55 +02003401
tiernoe876f672020-02-13 14:34:48 +00003402 return 'COMPLETED', output
quilesj7e13aeb2019-10-08 13:34:55 +02003403
tierno067e04a2020-03-31 12:53:13 +00003404 except (LcmException, asyncio.CancelledError):
tiernoe876f672020-02-13 14:34:48 +00003405 raise
quilesj7e13aeb2019-10-08 13:34:55 +02003406 except Exception as e:
tiernoe876f672020-02-13 14:34:48 +00003407 return 'FAIL', 'Error executing action {}: {}'.format(primitive, e)
tierno59d22d22018-09-25 18:10:19 +02003408
ksaikiranr3fde2c72021-03-15 10:39:06 +05303409 async def vca_status_refresh(self, nsr_id, nslcmop_id):
3410 """
3411 Updating the vca_status with latest juju information in nsrs record
3412 :param: nsr_id: Id of the nsr
3413 :param: nslcmop_id: Id of the nslcmop
3414 :return: None
3415 """
3416
3417 self.logger.debug("Task ns={} action={} Enter".format(nsr_id, nslcmop_id))
3418 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
ksaikiranr656b6dd2021-02-19 10:25:18 +05303419 if db_nsr['_admin']['deployed']['K8s']:
3420 for k8s_index, k8s in enumerate(db_nsr['_admin']['deployed']['K8s']):
3421 cluster_uuid, kdu_instance = k8s["k8scluster-uuid"], k8s["kdu-instance"]
3422 await self._on_update_k8s_db(cluster_uuid, kdu_instance, filter={'_id': nsr_id})
3423 else:
3424 for vca_index, _ in enumerate(db_nsr['_admin']['deployed']['VCA']):
3425 table, filter = "nsrs", {"_id": nsr_id}
3426 path = "_admin.deployed.VCA.{}.".format(vca_index)
3427 await self._on_update_n2vc_db(table, filter, path, {})
ksaikiranr3fde2c72021-03-15 10:39:06 +05303428
3429 self.logger.debug("Task ns={} action={} Exit".format(nsr_id, nslcmop_id))
3430 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_vca_status_refresh")
3431
tierno59d22d22018-09-25 18:10:19 +02003432 async def action(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003433 # Try to lock HA task here
3434 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3435 if not task_is_locked_by_me:
3436 return
3437
tierno59d22d22018-09-25 18:10:19 +02003438 logging_text = "Task ns={} action={} ".format(nsr_id, nslcmop_id)
3439 self.logger.debug(logging_text + "Enter")
3440 # get all needed from database
3441 db_nsr = None
3442 db_nslcmop = None
tiernoe876f672020-02-13 14:34:48 +00003443 db_nsr_update = {}
tierno59d22d22018-09-25 18:10:19 +02003444 db_nslcmop_update = {}
3445 nslcmop_operation_state = None
tierno067e04a2020-03-31 12:53:13 +00003446 error_description_nslcmop = None
tierno59d22d22018-09-25 18:10:19 +02003447 exc = None
3448 try:
kuused124bfe2019-06-18 12:09:24 +02003449 # wait for any previous tasks in process
tierno3cf81a32019-11-11 17:07:00 +00003450 step = "Waiting for previous operations to terminate"
kuused124bfe2019-06-18 12:09:24 +02003451 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
3452
quilesj4cda56b2019-12-05 10:02:20 +00003453 self._write_ns_status(
3454 nsr_id=nsr_id,
3455 ns_state=None,
3456 current_operation="RUNNING ACTION",
3457 current_operation_id=nslcmop_id
3458 )
3459
tierno59d22d22018-09-25 18:10:19 +02003460 step = "Getting information from database"
3461 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
3462 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
tiernoda964822019-01-14 15:53:47 +00003463
tiernoe4f7e6c2018-11-27 14:55:30 +00003464 nsr_deployed = db_nsr["_admin"].get("deployed")
tierno1b633412019-02-25 16:48:23 +00003465 vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
tierno59d22d22018-09-25 18:10:19 +02003466 vdu_id = db_nslcmop["operationParams"].get("vdu_id")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003467 kdu_name = db_nslcmop["operationParams"].get("kdu_name")
tiernoe4f7e6c2018-11-27 14:55:30 +00003468 vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
tierno067e04a2020-03-31 12:53:13 +00003469 primitive = db_nslcmop["operationParams"]["primitive"]
3470 primitive_params = db_nslcmop["operationParams"]["primitive_params"]
3471 timeout_ns_action = db_nslcmop["operationParams"].get("timeout_ns_action", self.timeout_primitive)
tierno59d22d22018-09-25 18:10:19 +02003472
tierno1b633412019-02-25 16:48:23 +00003473 if vnf_index:
3474 step = "Getting vnfr from database"
3475 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
3476 step = "Getting vnfd from database"
3477 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
3478 else:
tierno067e04a2020-03-31 12:53:13 +00003479 step = "Getting nsd from database"
3480 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
tiernoda964822019-01-14 15:53:47 +00003481
tierno82974b22018-11-27 21:55:36 +00003482 # for backward compatibility
3483 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
3484 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
3485 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
3486 self.update_db_2("nsrs", nsr_id, db_nsr_update)
3487
tiernoda964822019-01-14 15:53:47 +00003488 # look for primitive
tiernoa278b842020-07-08 15:33:55 +00003489 config_primitive_desc = descriptor_configuration = None
tiernoda964822019-01-14 15:53:47 +00003490 if vdu_id:
bravofe5a31bc2021-02-17 19:09:12 -03003491 descriptor_configuration = get_configuration(db_vnfd, vdu_id)
calvinosanch9f9c6f22019-11-04 13:37:39 +01003492 elif kdu_name:
bravofe5a31bc2021-02-17 19:09:12 -03003493 descriptor_configuration = get_configuration(db_vnfd, kdu_name)
tierno1b633412019-02-25 16:48:23 +00003494 elif vnf_index:
bravofe5a31bc2021-02-17 19:09:12 -03003495 descriptor_configuration = get_configuration(db_vnfd, db_vnfd["id"])
tierno1b633412019-02-25 16:48:23 +00003496 else:
tiernoa278b842020-07-08 15:33:55 +00003497 descriptor_configuration = db_nsd.get("ns-configuration")
3498
3499 if descriptor_configuration and descriptor_configuration.get("config-primitive"):
3500 for config_primitive in descriptor_configuration["config-primitive"]:
tierno1b633412019-02-25 16:48:23 +00003501 if config_primitive["name"] == primitive:
3502 config_primitive_desc = config_primitive
3503 break
tiernoda964822019-01-14 15:53:47 +00003504
garciadeblas6bed6b32020-07-20 11:05:42 +00003505 if not config_primitive_desc:
3506 if not (kdu_name and primitive in ("upgrade", "rollback", "status")):
3507 raise LcmException("Primitive {} not found at [ns|vnf|vdu]-configuration:config-primitive ".
3508 format(primitive))
3509 primitive_name = primitive
3510 ee_descriptor_id = None
3511 else:
3512 primitive_name = config_primitive_desc.get("execution-environment-primitive", primitive)
3513 ee_descriptor_id = config_primitive_desc.get("execution-environment-ref")
tierno1b633412019-02-25 16:48:23 +00003514
tierno1b633412019-02-25 16:48:23 +00003515 if vnf_index:
tierno626e0152019-11-29 14:16:16 +00003516 if vdu_id:
3517 vdur = next((x for x in db_vnfr["vdur"] if x["vdu-id-ref"] == vdu_id), None)
bravof922c4172020-11-24 21:21:43 -03003518 desc_params = parse_yaml_strings(vdur.get("additionalParams"))
tierno067e04a2020-03-31 12:53:13 +00003519 elif kdu_name:
3520 kdur = next((x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name), None)
bravof922c4172020-11-24 21:21:43 -03003521 desc_params = parse_yaml_strings(kdur.get("additionalParams"))
tierno067e04a2020-03-31 12:53:13 +00003522 else:
bravof922c4172020-11-24 21:21:43 -03003523 desc_params = parse_yaml_strings(db_vnfr.get("additionalParamsForVnf"))
tierno1b633412019-02-25 16:48:23 +00003524 else:
bravof922c4172020-11-24 21:21:43 -03003525 desc_params = parse_yaml_strings(db_nsr.get("additionalParamsForNs"))
bravofe5a31bc2021-02-17 19:09:12 -03003526 if kdu_name and get_configuration(db_vnfd, kdu_name):
3527 kdu_configuration = get_configuration(db_vnfd, kdu_name)
David Garciad41dbd62020-12-10 12:52:52 +01003528 actions = set()
David Garciaa1003662021-02-16 21:07:58 +01003529 for primitive in kdu_configuration.get("initial-config-primitive", []):
David Garciad41dbd62020-12-10 12:52:52 +01003530 actions.add(primitive["name"])
David Garciaa1003662021-02-16 21:07:58 +01003531 for primitive in kdu_configuration.get("config-primitive", []):
David Garciad41dbd62020-12-10 12:52:52 +01003532 actions.add(primitive["name"])
3533 kdu_action = True if primitive_name in actions else False
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003534
tiernoda964822019-01-14 15:53:47 +00003535 # TODO check if ns is in a proper status
tiernoa278b842020-07-08 15:33:55 +00003536 if kdu_name and (primitive_name in ("upgrade", "rollback", "status") or kdu_action):
tierno067e04a2020-03-31 12:53:13 +00003537 # kdur and desc_params already set from before
3538 if primitive_params:
3539 desc_params.update(primitive_params)
3540 # TODO Check if we will need something at vnf level
3541 for index, kdu in enumerate(get_iterable(nsr_deployed, "K8s")):
3542 if kdu_name == kdu["kdu-name"] and kdu["member-vnf-index"] == vnf_index:
3543 break
3544 else:
3545 raise LcmException("KDU '{}' for vnf '{}' not deployed".format(kdu_name, vnf_index))
quilesj7e13aeb2019-10-08 13:34:55 +02003546
tierno067e04a2020-03-31 12:53:13 +00003547 if kdu.get("k8scluster-type") not in self.k8scluster_map:
3548 msg = "unknown k8scluster-type '{}'".format(kdu.get("k8scluster-type"))
3549 raise LcmException(msg)
3550
3551 db_dict = {"collection": "nsrs",
3552 "filter": {"_id": nsr_id},
3553 "path": "_admin.deployed.K8s.{}".format(index)}
tiernoa278b842020-07-08 15:33:55 +00003554 self.logger.debug(logging_text + "Exec k8s {} on {}.{}".format(primitive_name, vnf_index, kdu_name))
3555 step = "Executing kdu {}".format(primitive_name)
3556 if primitive_name == "upgrade":
tierno067e04a2020-03-31 12:53:13 +00003557 if desc_params.get("kdu_model"):
3558 kdu_model = desc_params.get("kdu_model")
3559 del desc_params["kdu_model"]
3560 else:
3561 kdu_model = kdu.get("kdu-model")
3562 parts = kdu_model.split(sep=":")
3563 if len(parts) == 2:
3564 kdu_model = parts[0]
3565
3566 detailed_status = await asyncio.wait_for(
3567 self.k8scluster_map[kdu["k8scluster-type"]].upgrade(
3568 cluster_uuid=kdu.get("k8scluster-uuid"),
3569 kdu_instance=kdu.get("kdu-instance"),
3570 atomic=True, kdu_model=kdu_model,
3571 params=desc_params, db_dict=db_dict,
3572 timeout=timeout_ns_action),
3573 timeout=timeout_ns_action + 10)
3574 self.logger.debug(logging_text + " Upgrade of kdu {} done".format(detailed_status))
tiernoa278b842020-07-08 15:33:55 +00003575 elif primitive_name == "rollback":
tierno067e04a2020-03-31 12:53:13 +00003576 detailed_status = await asyncio.wait_for(
3577 self.k8scluster_map[kdu["k8scluster-type"]].rollback(
3578 cluster_uuid=kdu.get("k8scluster-uuid"),
3579 kdu_instance=kdu.get("kdu-instance"),
3580 db_dict=db_dict),
3581 timeout=timeout_ns_action)
tiernoa278b842020-07-08 15:33:55 +00003582 elif primitive_name == "status":
tierno067e04a2020-03-31 12:53:13 +00003583 detailed_status = await asyncio.wait_for(
3584 self.k8scluster_map[kdu["k8scluster-type"]].status_kdu(
3585 cluster_uuid=kdu.get("k8scluster-uuid"),
3586 kdu_instance=kdu.get("kdu-instance")),
3587 timeout=timeout_ns_action)
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003588 else:
3589 kdu_instance = kdu.get("kdu-instance") or "{}-{}".format(kdu["kdu-name"], nsr_id)
3590 params = self._map_primitive_params(config_primitive_desc, primitive_params, desc_params)
3591
3592 detailed_status = await asyncio.wait_for(
3593 self.k8scluster_map[kdu["k8scluster-type"]].exec_primitive(
3594 cluster_uuid=kdu.get("k8scluster-uuid"),
3595 kdu_instance=kdu_instance,
tiernoa278b842020-07-08 15:33:55 +00003596 primitive_name=primitive_name,
Dominik Fleischmann771c32b2020-04-07 12:39:36 +02003597 params=params, db_dict=db_dict,
3598 timeout=timeout_ns_action),
3599 timeout=timeout_ns_action)
tierno067e04a2020-03-31 12:53:13 +00003600
3601 if detailed_status:
3602 nslcmop_operation_state = 'COMPLETED'
3603 else:
3604 detailed_status = ''
3605 nslcmop_operation_state = 'FAILED'
tierno067e04a2020-03-31 12:53:13 +00003606 else:
bravof922c4172020-11-24 21:21:43 -03003607 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"], member_vnf_index=vnf_index,
3608 vdu_id=vdu_id, vdu_count_index=vdu_count_index,
tiernoa278b842020-07-08 15:33:55 +00003609 ee_descriptor_id=ee_descriptor_id)
ksaikiranrb1c9f372021-03-15 11:07:29 +05303610 for vca_index, vca_deployed in enumerate(db_nsr['_admin']['deployed']['VCA']):
3611 if vca_deployed.get("member-vnf-index") == vnf_index:
3612 db_dict = {"collection": "nsrs",
3613 "filter": {"_id": nsr_id},
3614 "path": "_admin.deployed.VCA.{}.".format(vca_index)}
3615 break
tierno067e04a2020-03-31 12:53:13 +00003616 nslcmop_operation_state, detailed_status = await self._ns_execute_primitive(
tierno588547c2020-07-01 15:30:20 +00003617 ee_id,
tiernoa278b842020-07-08 15:33:55 +00003618 primitive=primitive_name,
tierno067e04a2020-03-31 12:53:13 +00003619 primitive_params=self._map_primitive_params(config_primitive_desc, primitive_params, desc_params),
tierno588547c2020-07-01 15:30:20 +00003620 timeout=timeout_ns_action,
3621 vca_type=vca_type,
ksaikiranrb1c9f372021-03-15 11:07:29 +05303622 db_dict=db_dict)
tierno067e04a2020-03-31 12:53:13 +00003623
3624 db_nslcmop_update["detailed-status"] = detailed_status
3625 error_description_nslcmop = detailed_status if nslcmop_operation_state == "FAILED" else ""
3626 self.logger.debug(logging_text + " task Done with result {} {}".format(nslcmop_operation_state,
3627 detailed_status))
tierno59d22d22018-09-25 18:10:19 +02003628 return # database update is called inside finally
3629
tiernof59ad6c2020-04-08 12:50:52 +00003630 except (DbException, LcmException, N2VCException, K8sException) as e:
tierno59d22d22018-09-25 18:10:19 +02003631 self.logger.error(logging_text + "Exit Exception {}".format(e))
3632 exc = e
3633 except asyncio.CancelledError:
3634 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
3635 exc = "Operation was cancelled"
tierno067e04a2020-03-31 12:53:13 +00003636 except asyncio.TimeoutError:
3637 self.logger.error(logging_text + "Timeout while '{}'".format(step))
3638 exc = "Timeout"
tierno59d22d22018-09-25 18:10:19 +02003639 except Exception as e:
3640 exc = traceback.format_exc()
3641 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
3642 finally:
tierno067e04a2020-03-31 12:53:13 +00003643 if exc:
3644 db_nslcmop_update["detailed-status"] = detailed_status = error_description_nslcmop = \
kuuse0ca67472019-05-13 15:59:27 +02003645 "FAILED {}: {}".format(step, exc)
tierno067e04a2020-03-31 12:53:13 +00003646 nslcmop_operation_state = "FAILED"
3647 if db_nsr:
3648 self._write_ns_status(
3649 nsr_id=nsr_id,
3650 ns_state=db_nsr["nsState"], # TODO check if degraded. For the moment use previous status
3651 current_operation="IDLE",
3652 current_operation_id=None,
3653 # error_description=error_description_nsr,
3654 # error_detail=error_detail,
3655 other_update=db_nsr_update
3656 )
3657
bravof922c4172020-11-24 21:21:43 -03003658 self._write_op_status(op_id=nslcmop_id, stage="", error_message=error_description_nslcmop,
3659 operation_state=nslcmop_operation_state, other_update=db_nslcmop_update)
tierno067e04a2020-03-31 12:53:13 +00003660
tierno59d22d22018-09-25 18:10:19 +02003661 if nslcmop_operation_state:
3662 try:
3663 await self.msg.aiowrite("ns", "actioned", {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id,
tierno8a518872018-12-21 13:42:14 +00003664 "operationState": nslcmop_operation_state},
3665 loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02003666 except Exception as e:
3667 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
3668 self.logger.debug(logging_text + "Exit")
3669 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_action")
tierno067e04a2020-03-31 12:53:13 +00003670 return nslcmop_operation_state, detailed_status
tierno59d22d22018-09-25 18:10:19 +02003671
3672 async def scale(self, nsr_id, nslcmop_id):
kuused124bfe2019-06-18 12:09:24 +02003673 # Try to lock HA task here
3674 task_is_locked_by_me = self.lcm_tasks.lock_HA('ns', 'nslcmops', nslcmop_id)
3675 if not task_is_locked_by_me:
3676 return
3677
tierno59d22d22018-09-25 18:10:19 +02003678 logging_text = "Task ns={} scale={} ".format(nsr_id, nslcmop_id)
tierno2357f4e2020-10-19 16:38:59 +00003679 stage = ['', '', '']
aktas13251562021-02-12 22:19:10 +03003680 tasks_dict_info = {}
tierno2357f4e2020-10-19 16:38:59 +00003681 # ^ stage, step, VIM progress
tierno59d22d22018-09-25 18:10:19 +02003682 self.logger.debug(logging_text + "Enter")
3683 # get all needed from database
3684 db_nsr = None
tierno59d22d22018-09-25 18:10:19 +02003685 db_nslcmop_update = {}
tiernoe876f672020-02-13 14:34:48 +00003686 db_nsr_update = {}
tierno59d22d22018-09-25 18:10:19 +02003687 exc = None
tierno9ab95942018-10-10 16:44:22 +02003688 # in case of error, indicates what part of scale was failed to put nsr at error status
3689 scale_process = None
tiernod6de1992018-10-11 13:05:52 +02003690 old_operational_status = ""
3691 old_config_status = ""
aktas13251562021-02-12 22:19:10 +03003692 nsi_id = None
tierno59d22d22018-09-25 18:10:19 +02003693 try:
kuused124bfe2019-06-18 12:09:24 +02003694 # wait for any previous tasks in process
tierno3cf81a32019-11-11 17:07:00 +00003695 step = "Waiting for previous operations to terminate"
kuused124bfe2019-06-18 12:09:24 +02003696 await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
bravof922c4172020-11-24 21:21:43 -03003697 self._write_ns_status(nsr_id=nsr_id, ns_state=None,
3698 current_operation="SCALING", current_operation_id=nslcmop_id)
quilesj4cda56b2019-12-05 10:02:20 +00003699
ikalyvas02d9e7b2019-05-27 18:16:01 +03003700 step = "Getting nslcmop from database"
ikalyvas02d9e7b2019-05-27 18:16:01 +03003701 self.logger.debug(step + " after having waited for previous tasks to be completed")
3702 db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
bravof922c4172020-11-24 21:21:43 -03003703
ikalyvas02d9e7b2019-05-27 18:16:01 +03003704 step = "Getting nsr from database"
3705 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
ikalyvas02d9e7b2019-05-27 18:16:01 +03003706 old_operational_status = db_nsr["operational-status"]
3707 old_config_status = db_nsr["config-status"]
bravof922c4172020-11-24 21:21:43 -03003708
tierno59d22d22018-09-25 18:10:19 +02003709 step = "Parsing scaling parameters"
3710 db_nsr_update["operational-status"] = "scaling"
3711 self.update_db_2("nsrs", nsr_id, db_nsr_update)
tiernoe4f7e6c2018-11-27 14:55:30 +00003712 nsr_deployed = db_nsr["_admin"].get("deployed")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003713
3714 #######
3715 nsr_deployed = db_nsr["_admin"].get("deployed")
3716 vnf_index = db_nslcmop["operationParams"].get("member_vnf_index")
tiernoda6fb102019-11-23 00:36:52 +00003717 # vdu_id = db_nslcmop["operationParams"].get("vdu_id")
3718 # vdu_count_index = db_nslcmop["operationParams"].get("vdu_count_index")
3719 # vdu_name = db_nslcmop["operationParams"].get("vdu_name")
calvinosanch9f9c6f22019-11-04 13:37:39 +01003720 #######
3721
tierno59d22d22018-09-25 18:10:19 +02003722 vnf_index = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["member-vnf-index"]
3723 scaling_group = db_nslcmop["operationParams"]["scaleVnfData"]["scaleByStepData"]["scaling-group-descriptor"]
3724 scaling_type = db_nslcmop["operationParams"]["scaleVnfData"]["scaleVnfType"]
tierno82974b22018-11-27 21:55:36 +00003725 # for backward compatibility
3726 if nsr_deployed and isinstance(nsr_deployed.get("VCA"), dict):
3727 nsr_deployed["VCA"] = list(nsr_deployed["VCA"].values())
3728 db_nsr_update["_admin.deployed.VCA"] = nsr_deployed["VCA"]
3729 self.update_db_2("nsrs", nsr_id, db_nsr_update)
3730
tierno59d22d22018-09-25 18:10:19 +02003731 step = "Getting vnfr from database"
3732 db_vnfr = self.db.get_one("vnfrs", {"member-vnf-index-ref": vnf_index, "nsr-id-ref": nsr_id})
bravof922c4172020-11-24 21:21:43 -03003733
tierno59d22d22018-09-25 18:10:19 +02003734 step = "Getting vnfd from database"
3735 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
ikalyvas02d9e7b2019-05-27 18:16:01 +03003736
aktas13251562021-02-12 22:19:10 +03003737 base_folder = db_vnfd["_admin"]["storage"]
3738
tierno59d22d22018-09-25 18:10:19 +02003739 step = "Getting scaling-group-descriptor"
bravof832f8992020-12-07 12:57:31 -03003740 scaling_descriptor = find_in_list(
3741 get_scaling_aspect(
3742 db_vnfd
3743 ),
3744 lambda scale_desc: scale_desc["name"] == scaling_group
3745 )
3746 if not scaling_descriptor:
tierno59d22d22018-09-25 18:10:19 +02003747 raise LcmException("input parameter 'scaleByStepData':'scaling-group-descriptor':'{}' is not present "
3748 "at vnfd:scaling-group-descriptor".format(scaling_group))
ikalyvas02d9e7b2019-05-27 18:16:01 +03003749
tierno15b1cf12019-08-29 13:21:40 +00003750 step = "Sending scale order to VIM"
bravof922c4172020-11-24 21:21:43 -03003751 # TODO check if ns is in a proper status
tierno59d22d22018-09-25 18:10:19 +02003752 nb_scale_op = 0
3753 if not db_nsr["_admin"].get("scaling-group"):
3754 self.update_db_2("nsrs", nsr_id, {"_admin.scaling-group": [{"name": scaling_group, "nb-scale-op": 0}]})
3755 admin_scale_index = 0
3756 else:
3757 for admin_scale_index, admin_scale_info in enumerate(db_nsr["_admin"]["scaling-group"]):
3758 if admin_scale_info["name"] == scaling_group:
3759 nb_scale_op = admin_scale_info.get("nb-scale-op", 0)
3760 break
tierno9ab95942018-10-10 16:44:22 +02003761 else: # not found, set index one plus last element and add new entry with the name
3762 admin_scale_index += 1
3763 db_nsr_update["_admin.scaling-group.{}.name".format(admin_scale_index)] = scaling_group
tierno59d22d22018-09-25 18:10:19 +02003764 RO_scaling_info = []
aktas13251562021-02-12 22:19:10 +03003765 VCA_scaling_info = []
tierno59d22d22018-09-25 18:10:19 +02003766 vdu_scaling_info = {"scaling_group_name": scaling_group, "vdu": []}
3767 if scaling_type == "SCALE_OUT":
bravof832f8992020-12-07 12:57:31 -03003768 if "aspect-delta-details" not in scaling_descriptor:
3769 raise LcmException(
3770 "Aspect delta details not fount in scaling descriptor {}".format(
3771 scaling_descriptor["name"]
3772 )
3773 )
tierno59d22d22018-09-25 18:10:19 +02003774 # count if max-instance-count is reached
bravof832f8992020-12-07 12:57:31 -03003775 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
kuuse8b998e42019-07-30 15:22:16 +02003776
tierno59d22d22018-09-25 18:10:19 +02003777 vdu_scaling_info["scaling_direction"] = "OUT"
3778 vdu_scaling_info["vdu-create"] = {}
bravof832f8992020-12-07 12:57:31 -03003779 for delta in deltas:
3780 for vdu_delta in delta["vdu-delta"]:
3781 vdud = get_vdu(db_vnfd, vdu_delta["id"])
aktas13251562021-02-12 22:19:10 +03003782 vdu_index = get_vdur_index(db_vnfr, vdu_delta)
bravof832f8992020-12-07 12:57:31 -03003783 cloud_init_text = self._get_vdu_cloud_init_content(vdud, db_vnfd)
tierno72ef84f2020-10-06 08:22:07 +00003784 if cloud_init_text:
bravof832f8992020-12-07 12:57:31 -03003785 additional_params = self._get_vdu_additional_params(db_vnfr, vdud["id"]) or {}
3786 cloud_init_list = []
3787
3788 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
3789 max_instance_count = 10
3790 if vdu_profile and "max-number-of-instances" in vdu_profile:
3791 max_instance_count = vdu_profile.get("max-number-of-instances", 10)
3792
aktas13251562021-02-12 22:19:10 +03003793 default_instance_num = get_number_of_instances(db_vnfd, vdud["id"])
bravof832f8992020-12-07 12:57:31 -03003794
3795 nb_scale_op += vdu_delta.get("number-of-instances", 1)
3796
aktas13251562021-02-12 22:19:10 +03003797 if nb_scale_op + default_instance_num > max_instance_count:
bravof832f8992020-12-07 12:57:31 -03003798 raise LcmException(
3799 "reached the limit of {} (max-instance-count) "
3800 "scaling-out operations for the "
3801 "scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group)
bravof922c4172020-11-24 21:21:43 -03003802 )
bravof832f8992020-12-07 12:57:31 -03003803 for x in range(vdu_delta.get("number-of-instances", 1)):
3804 if cloud_init_text:
3805 # TODO Information of its own ip is not available because db_vnfr is not updated.
3806 additional_params["OSM"] = get_osm_params(
3807 db_vnfr,
3808 vdu_delta["id"],
3809 vdu_index + x
bravof922c4172020-11-24 21:21:43 -03003810 )
bravof832f8992020-12-07 12:57:31 -03003811 cloud_init_list.append(
3812 self._parse_cloud_init(
3813 cloud_init_text,
3814 additional_params,
3815 db_vnfd["id"],
3816 vdud["id"]
3817 )
3818 )
aktas13251562021-02-12 22:19:10 +03003819 VCA_scaling_info.append(
3820 {
3821 "osm_vdu_id": vdu_delta["id"],
3822 "member-vnf-index": vnf_index,
3823 "type": "create",
3824 "vdu_index": vdu_index + x
3825 }
3826 )
bravof832f8992020-12-07 12:57:31 -03003827 RO_scaling_info.append(
3828 {
3829 "osm_vdu_id": vdu_delta["id"],
3830 "member-vnf-index": vnf_index,
3831 "type": "create",
3832 "count": vdu_delta.get("number-of-instances", 1)
3833 }
3834 )
3835 if cloud_init_list:
3836 RO_scaling_info[-1]["cloud_init"] = cloud_init_list
3837 vdu_scaling_info["vdu-create"][vdu_delta["id"]] = vdu_delta.get("number-of-instances", 1)
ikalyvas02d9e7b2019-05-27 18:16:01 +03003838
tierno59d22d22018-09-25 18:10:19 +02003839 elif scaling_type == "SCALE_IN":
tierno59d22d22018-09-25 18:10:19 +02003840 if "min-instance-count" in scaling_descriptor and scaling_descriptor["min-instance-count"] is not None:
3841 min_instance_count = int(scaling_descriptor["min-instance-count"])
bravof832f8992020-12-07 12:57:31 -03003842
tierno59d22d22018-09-25 18:10:19 +02003843 vdu_scaling_info["scaling_direction"] = "IN"
3844 vdu_scaling_info["vdu-delete"] = {}
bravof832f8992020-12-07 12:57:31 -03003845 deltas = scaling_descriptor.get("aspect-delta-details")["deltas"]
3846 for delta in deltas:
3847 for vdu_delta in delta["vdu-delta"]:
aktas13251562021-02-12 22:19:10 +03003848 vdu_index = get_vdur_index(db_vnfr, vdu_delta)
bravof832f8992020-12-07 12:57:31 -03003849 min_instance_count = 0
3850 vdu_profile = get_vdu_profile(db_vnfd, vdu_delta["id"])
3851 if vdu_profile and "min-number-of-instances" in vdu_profile:
3852 min_instance_count = vdu_profile["min-number-of-instances"]
3853
aktas13251562021-02-12 22:19:10 +03003854 default_instance_num = get_number_of_instances(db_vnfd, vdu_delta["id"])
bravof832f8992020-12-07 12:57:31 -03003855
3856 nb_scale_op -= vdu_delta.get("number-of-instances", 1)
aktas13251562021-02-12 22:19:10 +03003857 if nb_scale_op + default_instance_num < min_instance_count:
bravof832f8992020-12-07 12:57:31 -03003858 raise LcmException(
3859 "reached the limit of {} (min-instance-count) scaling-in operations for the "
3860 "scaling-group-descriptor '{}'".format(nb_scale_op, scaling_group)
3861 )
3862 RO_scaling_info.append({"osm_vdu_id": vdu_delta["id"], "member-vnf-index": vnf_index,
aktas13251562021-02-12 22:19:10 +03003863 "type": "delete", "count": vdu_delta.get("number-of-instances", 1),
3864 "vdu_index": vdu_index - 1})
3865 for x in range(vdu_delta.get("number-of-instances", 1)):
3866 VCA_scaling_info.append(
3867 {
3868 "osm_vdu_id": vdu_delta["id"],
3869 "member-vnf-index": vnf_index,
3870 "type": "delete",
3871 "vdu_index": vdu_index - 1 - x
3872 }
3873 )
bravof832f8992020-12-07 12:57:31 -03003874 vdu_scaling_info["vdu-delete"][vdu_delta["id"]] = vdu_delta.get("number-of-instances", 1)
tierno59d22d22018-09-25 18:10:19 +02003875
3876 # update VDU_SCALING_INFO with the VDUs to delete ip_addresses
tierno27246d82018-09-27 15:59:09 +02003877 vdu_delete = copy(vdu_scaling_info.get("vdu-delete"))
tierno59d22d22018-09-25 18:10:19 +02003878 if vdu_scaling_info["scaling_direction"] == "IN":
3879 for vdur in reversed(db_vnfr["vdur"]):
tierno27246d82018-09-27 15:59:09 +02003880 if vdu_delete.get(vdur["vdu-id-ref"]):
3881 vdu_delete[vdur["vdu-id-ref"]] -= 1
tierno59d22d22018-09-25 18:10:19 +02003882 vdu_scaling_info["vdu"].append({
tierno2357f4e2020-10-19 16:38:59 +00003883 "name": vdur.get("name") or vdur.get("vdu-name"),
tierno59d22d22018-09-25 18:10:19 +02003884 "vdu_id": vdur["vdu-id-ref"],
3885 "interface": []
3886 })
3887 for interface in vdur["interfaces"]:
3888 vdu_scaling_info["vdu"][-1]["interface"].append({
3889 "name": interface["name"],
3890 "ip_address": interface["ip-address"],
3891 "mac_address": interface.get("mac-address"),
3892 })
tierno2357f4e2020-10-19 16:38:59 +00003893 # vdu_delete = vdu_scaling_info.pop("vdu-delete")
tierno59d22d22018-09-25 18:10:19 +02003894
kuuseac3a8882019-10-03 10:48:06 +02003895 # PRE-SCALE BEGIN
tierno59d22d22018-09-25 18:10:19 +02003896 step = "Executing pre-scale vnf-config-primitive"
3897 if scaling_descriptor.get("scaling-config-action"):
3898 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
kuuseac3a8882019-10-03 10:48:06 +02003899 if (scaling_config_action.get("trigger") == "pre-scale-in" and scaling_type == "SCALE_IN") \
3900 or (scaling_config_action.get("trigger") == "pre-scale-out" and scaling_type == "SCALE_OUT"):
tierno59d22d22018-09-25 18:10:19 +02003901 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
3902 step = db_nslcmop_update["detailed-status"] = \
3903 "executing pre-scale scaling-config-action '{}'".format(vnf_config_primitive)
tiernoda964822019-01-14 15:53:47 +00003904
tierno59d22d22018-09-25 18:10:19 +02003905 # look for primitive
bravofe5a31bc2021-02-17 19:09:12 -03003906 for config_primitive in (get_configuration(
3907 db_vnfd, db_vnfd["id"]
3908 ) or {}).get("config-primitive", ()):
tierno59d22d22018-09-25 18:10:19 +02003909 if config_primitive["name"] == vnf_config_primitive:
tierno59d22d22018-09-25 18:10:19 +02003910 break
3911 else:
3912 raise LcmException(
3913 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-action"
tiernoda964822019-01-14 15:53:47 +00003914 "[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:config-"
tiernoa278b842020-07-08 15:33:55 +00003915 "primitive".format(scaling_group, vnf_config_primitive))
tiernoda964822019-01-14 15:53:47 +00003916
tierno16fedf52019-05-24 08:38:26 +00003917 vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
tiernoda964822019-01-14 15:53:47 +00003918 if db_vnfr.get("additionalParamsForVnf"):
3919 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
quilesj7e13aeb2019-10-08 13:34:55 +02003920
tierno9ab95942018-10-10 16:44:22 +02003921 scale_process = "VCA"
tiernod6de1992018-10-11 13:05:52 +02003922 db_nsr_update["config-status"] = "configuring pre-scaling"
kuuseac3a8882019-10-03 10:48:06 +02003923 primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
3924
tierno7c4e24c2020-05-13 08:41:35 +00003925 # Pre-scale retry check: Check if this sub-operation has been executed before
kuuseac3a8882019-10-03 10:48:06 +02003926 op_index = self._check_or_add_scale_suboperation(
3927 db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'PRE-SCALE')
tierno7c4e24c2020-05-13 08:41:35 +00003928 if op_index == self.SUBOPERATION_STATUS_SKIP:
kuuseac3a8882019-10-03 10:48:06 +02003929 # Skip sub-operation
3930 result = 'COMPLETED'
3931 result_detail = 'Done'
3932 self.logger.debug(logging_text +
3933 "vnf_config_primitive={} Skipped sub-operation, result {} {}".format(
3934 vnf_config_primitive, result, result_detail))
3935 else:
tierno7c4e24c2020-05-13 08:41:35 +00003936 if op_index == self.SUBOPERATION_STATUS_NEW:
kuuseac3a8882019-10-03 10:48:06 +02003937 # New sub-operation: Get index of this sub-operation
3938 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
3939 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
3940 format(vnf_config_primitive))
3941 else:
tierno7c4e24c2020-05-13 08:41:35 +00003942 # retry: Get registered params for this existing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02003943 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
3944 vnf_index = op.get('member_vnf_index')
3945 vnf_config_primitive = op.get('primitive')
3946 primitive_params = op.get('primitive_params')
tierno7c4e24c2020-05-13 08:41:35 +00003947 self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
kuuseac3a8882019-10-03 10:48:06 +02003948 format(vnf_config_primitive))
tierno588547c2020-07-01 15:30:20 +00003949 # Execute the primitive, either with new (first-time) or registered (reintent) args
tiernoa278b842020-07-08 15:33:55 +00003950 ee_descriptor_id = config_primitive.get("execution-environment-ref")
3951 primitive_name = config_primitive.get("execution-environment-primitive",
3952 vnf_config_primitive)
tierno588547c2020-07-01 15:30:20 +00003953 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
3954 member_vnf_index=vnf_index,
3955 vdu_id=None,
tiernoa278b842020-07-08 15:33:55 +00003956 vdu_count_index=None,
3957 ee_descriptor_id=ee_descriptor_id)
kuuseac3a8882019-10-03 10:48:06 +02003958 result, result_detail = await self._ns_execute_primitive(
limon625e0252021-03-15 09:39:43 +01003959 ee_id, primitive_name, primitive_params, vca_type=vca_type)
kuuseac3a8882019-10-03 10:48:06 +02003960 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
3961 vnf_config_primitive, result, result_detail))
3962 # Update operationState = COMPLETED | FAILED
3963 self._update_suboperation_status(
3964 db_nslcmop, op_index, result, result_detail)
3965
tierno59d22d22018-09-25 18:10:19 +02003966 if result == "FAILED":
3967 raise LcmException(result_detail)
tiernod6de1992018-10-11 13:05:52 +02003968 db_nsr_update["config-status"] = old_config_status
3969 scale_process = None
kuuseac3a8882019-10-03 10:48:06 +02003970 # PRE-SCALE END
tierno59d22d22018-09-25 18:10:19 +02003971
tierno2357f4e2020-10-19 16:38:59 +00003972 db_nsr_update["_admin.scaling-group.{}.nb-scale-op".format(admin_scale_index)] = nb_scale_op
3973 db_nsr_update["_admin.scaling-group.{}.time".format(admin_scale_index)] = time()
3974
aktas13251562021-02-12 22:19:10 +03003975 # SCALE-IN VCA - BEGIN
3976 if VCA_scaling_info:
3977 step = db_nslcmop_update["detailed-status"] = \
3978 "Deleting the execution environments"
3979 scale_process = "VCA"
3980 for vdu_info in VCA_scaling_info:
3981 if vdu_info["type"] == "delete":
3982 member_vnf_index = str(vdu_info["member-vnf-index"])
3983 self.logger.debug(logging_text + "vdu info: {}".format(vdu_info))
3984 vdu_id = vdu_info["osm_vdu_id"]
3985 vdu_index = int(vdu_info["vdu_index"])
3986 stage[1] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
3987 member_vnf_index, vdu_id, vdu_index)
3988 stage[2] = step = "Scaling in VCA"
3989 self._write_op_status(
3990 op_id=nslcmop_id,
3991 stage=stage
3992 )
3993 vca_update = db_nsr["_admin"]["deployed"]["VCA"]
3994 config_update = db_nsr["configurationStatus"]
3995 for vca_index, vca in enumerate(vca_update):
3996 if (vca or vca.get("ee_id")) and vca["member-vnf-index"] == member_vnf_index and \
3997 vca["vdu_count_index"] == vdu_index:
3998 if vca.get("vdu_id"):
3999 config_descriptor = get_configuration(db_vnfd, vca.get("vdu_id"))
4000 elif vca.get("kdu_name"):
4001 config_descriptor = get_configuration(db_vnfd, vca.get("kdu_name"))
4002 else:
4003 config_descriptor = get_configuration(db_vnfd, db_vnfd["id"])
4004 operation_params = db_nslcmop.get("operationParams") or {}
4005 exec_terminate_primitives = (not operation_params.get("skip_terminate_primitives") and
4006 vca.get("needed_terminate"))
4007 task = asyncio.ensure_future(asyncio.wait_for(
4008 self.destroy_N2VC(logging_text, db_nslcmop, vca, config_descriptor,
4009 vca_index, destroy_ee=True,
4010 exec_primitives=exec_terminate_primitives,
4011 scaling_in=True), timeout=self.timeout_charm_delete))
4012 # wait before next removal
4013 await asyncio.sleep(30)
4014 tasks_dict_info[task] = "Terminating VCA {}".format(vca.get("ee_id"))
4015 del vca_update[vca_index]
4016 del config_update[vca_index]
4017 # wait for pending tasks of terminate primitives
4018 if tasks_dict_info:
4019 self.logger.debug(logging_text +
4020 'Waiting for tasks {}'.format(list(tasks_dict_info.keys())))
4021 error_list = await self._wait_for_tasks(logging_text, tasks_dict_info,
4022 min(self.timeout_charm_delete,
4023 self.timeout_ns_terminate),
4024 stage, nslcmop_id)
4025 tasks_dict_info.clear()
4026 if error_list:
4027 raise LcmException("; ".join(error_list))
4028
4029 db_vca_and_config_update = {
4030 "_admin.deployed.VCA": vca_update,
4031 "configurationStatus": config_update
4032 }
4033 self.update_db_2("nsrs", db_nsr["_id"], db_vca_and_config_update)
4034 scale_process = None
4035 # SCALE-IN VCA - END
4036
kuuseac3a8882019-10-03 10:48:06 +02004037 # SCALE RO - BEGIN
tierno59d22d22018-09-25 18:10:19 +02004038 if RO_scaling_info:
tierno9ab95942018-10-10 16:44:22 +02004039 scale_process = "RO"
tierno2357f4e2020-10-19 16:38:59 +00004040 if self.ro_config.get("ng"):
4041 await self._scale_ng_ro(logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage)
tierno2357f4e2020-10-19 16:38:59 +00004042 vdu_scaling_info.pop("vdu-create", None)
4043 vdu_scaling_info.pop("vdu-delete", None)
tierno59d22d22018-09-25 18:10:19 +02004044
tierno9ab95942018-10-10 16:44:22 +02004045 scale_process = None
tierno59d22d22018-09-25 18:10:19 +02004046 if db_nsr_update:
4047 self.update_db_2("nsrs", nsr_id, db_nsr_update)
aktas13251562021-02-12 22:19:10 +03004048 # SCALE RO - END
4049
4050 # SCALE-UP VCA - BEGIN
4051 if VCA_scaling_info:
4052 step = db_nslcmop_update["detailed-status"] = \
4053 "Creating new execution environments"
4054 scale_process = "VCA"
4055 for vdu_info in VCA_scaling_info:
4056 if vdu_info["type"] == "create":
4057 member_vnf_index = str(vdu_info["member-vnf-index"])
4058 self.logger.debug(logging_text + "vdu info: {}".format(vdu_info))
4059 vnfd_id = db_vnfr["vnfd-ref"]
4060 vdu_index = int(vdu_info["vdu_index"])
4061 deploy_params = {"OSM": get_osm_params(db_vnfr)}
4062 if db_vnfr.get("additionalParamsForVnf"):
4063 deploy_params.update(parse_yaml_strings(db_vnfr["additionalParamsForVnf"].copy()))
4064 descriptor_config = get_configuration(db_vnfd, db_vnfd["id"])
4065 if descriptor_config:
4066 vdu_id = None
4067 vdu_name = None
4068 kdu_name = None
4069 self._deploy_n2vc(
4070 logging_text=logging_text + "member_vnf_index={} ".format(member_vnf_index),
4071 db_nsr=db_nsr,
4072 db_vnfr=db_vnfr,
4073 nslcmop_id=nslcmop_id,
4074 nsr_id=nsr_id,
4075 nsi_id=nsi_id,
4076 vnfd_id=vnfd_id,
4077 vdu_id=vdu_id,
4078 kdu_name=kdu_name,
4079 member_vnf_index=member_vnf_index,
4080 vdu_index=vdu_index,
4081 vdu_name=vdu_name,
4082 deploy_params=deploy_params,
4083 descriptor_config=descriptor_config,
4084 base_folder=base_folder,
4085 task_instantiation_info=tasks_dict_info,
4086 stage=stage
4087 )
4088 vdu_id = vdu_info["osm_vdu_id"]
4089 vdur = find_in_list(db_vnfr["vdur"], lambda vdu: vdu["vdu-id-ref"] == vdu_id)
4090 descriptor_config = get_configuration(db_vnfd, vdu_id)
4091 if vdur.get("additionalParams"):
4092 deploy_params_vdu = parse_yaml_strings(vdur["additionalParams"])
4093 else:
4094 deploy_params_vdu = deploy_params
4095 deploy_params_vdu["OSM"] = get_osm_params(db_vnfr, vdu_id, vdu_count_index=vdu_index)
4096 if descriptor_config:
4097 vdu_name = None
4098 kdu_name = None
4099 stage[1] = "Scaling member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
4100 member_vnf_index, vdu_id, vdu_index)
4101 stage[2] = step = "Scaling out VCA"
4102 self._write_op_status(
4103 op_id=nslcmop_id,
4104 stage=stage
4105 )
4106 self._deploy_n2vc(
4107 logging_text=logging_text + "member_vnf_index={}, vdu_id={}, vdu_index={} ".format(
4108 member_vnf_index, vdu_id, vdu_index),
4109 db_nsr=db_nsr,
4110 db_vnfr=db_vnfr,
4111 nslcmop_id=nslcmop_id,
4112 nsr_id=nsr_id,
4113 nsi_id=nsi_id,
4114 vnfd_id=vnfd_id,
4115 vdu_id=vdu_id,
4116 kdu_name=kdu_name,
4117 member_vnf_index=member_vnf_index,
4118 vdu_index=vdu_index,
4119 vdu_name=vdu_name,
4120 deploy_params=deploy_params_vdu,
4121 descriptor_config=descriptor_config,
4122 base_folder=base_folder,
4123 task_instantiation_info=tasks_dict_info,
4124 stage=stage
4125 )
4126 # TODO: scaling for kdu is not implemented yet.
4127 kdu_name = vdu_info["osm_vdu_id"]
4128 descriptor_config = get_configuration(db_vnfd, kdu_name)
4129 if descriptor_config:
4130 vdu_id = None
4131 vdu_index = vdu_index
4132 vdu_name = None
4133 kdur = next(x for x in db_vnfr["kdur"] if x["kdu-name"] == kdu_name)
4134 deploy_params_kdu = {"OSM": get_osm_params(db_vnfr)}
4135 if kdur.get("additionalParams"):
4136 deploy_params_kdu = parse_yaml_strings(kdur["additionalParams"])
4137
4138 self._deploy_n2vc(
4139 logging_text=logging_text,
4140 db_nsr=db_nsr,
4141 db_vnfr=db_vnfr,
4142 nslcmop_id=nslcmop_id,
4143 nsr_id=nsr_id,
4144 nsi_id=nsi_id,
4145 vnfd_id=vnfd_id,
4146 vdu_id=vdu_id,
4147 kdu_name=kdu_name,
4148 member_vnf_index=member_vnf_index,
4149 vdu_index=vdu_index,
4150 vdu_name=vdu_name,
4151 deploy_params=deploy_params_kdu,
4152 descriptor_config=descriptor_config,
4153 base_folder=base_folder,
4154 task_instantiation_info=tasks_dict_info,
4155 stage=stage
4156 )
4157 # SCALE-UP VCA - END
4158 scale_process = None
tierno59d22d22018-09-25 18:10:19 +02004159
kuuseac3a8882019-10-03 10:48:06 +02004160 # POST-SCALE BEGIN
tierno59d22d22018-09-25 18:10:19 +02004161 # execute primitive service POST-SCALING
4162 step = "Executing post-scale vnf-config-primitive"
4163 if scaling_descriptor.get("scaling-config-action"):
4164 for scaling_config_action in scaling_descriptor["scaling-config-action"]:
kuuseac3a8882019-10-03 10:48:06 +02004165 if (scaling_config_action.get("trigger") == "post-scale-in" and scaling_type == "SCALE_IN") \
4166 or (scaling_config_action.get("trigger") == "post-scale-out" and scaling_type == "SCALE_OUT"):
tierno59d22d22018-09-25 18:10:19 +02004167 vnf_config_primitive = scaling_config_action["vnf-config-primitive-name-ref"]
4168 step = db_nslcmop_update["detailed-status"] = \
4169 "executing post-scale scaling-config-action '{}'".format(vnf_config_primitive)
tiernoda964822019-01-14 15:53:47 +00004170
tierno589befb2019-05-29 07:06:23 +00004171 vnfr_params = {"VDU_SCALE_INFO": vdu_scaling_info}
tiernoda964822019-01-14 15:53:47 +00004172 if db_vnfr.get("additionalParamsForVnf"):
4173 vnfr_params.update(db_vnfr["additionalParamsForVnf"])
4174
tierno59d22d22018-09-25 18:10:19 +02004175 # look for primitive
bravof9a256db2021-02-22 18:02:07 -03004176 for config_primitive in (
4177 get_configuration(db_vnfd, db_vnfd["id"]) or {}
4178 ).get("config-primitive", ()):
tierno59d22d22018-09-25 18:10:19 +02004179 if config_primitive["name"] == vnf_config_primitive:
tierno59d22d22018-09-25 18:10:19 +02004180 break
4181 else:
tiernoa278b842020-07-08 15:33:55 +00004182 raise LcmException(
4183 "Invalid vnfd descriptor at scaling-group-descriptor[name='{}']:scaling-config-"
4184 "action[vnf-config-primitive-name-ref='{}'] does not match any vnf-configuration:"
4185 "config-primitive".format(scaling_group, vnf_config_primitive))
tierno9ab95942018-10-10 16:44:22 +02004186 scale_process = "VCA"
tiernod6de1992018-10-11 13:05:52 +02004187 db_nsr_update["config-status"] = "configuring post-scaling"
kuuseac3a8882019-10-03 10:48:06 +02004188 primitive_params = self._map_primitive_params(config_primitive, {}, vnfr_params)
tiernod6de1992018-10-11 13:05:52 +02004189
tierno7c4e24c2020-05-13 08:41:35 +00004190 # Post-scale retry check: Check if this sub-operation has been executed before
kuuseac3a8882019-10-03 10:48:06 +02004191 op_index = self._check_or_add_scale_suboperation(
4192 db_nslcmop, nslcmop_id, vnf_index, vnf_config_primitive, primitive_params, 'POST-SCALE')
quilesj4cda56b2019-12-05 10:02:20 +00004193 if op_index == self.SUBOPERATION_STATUS_SKIP:
kuuseac3a8882019-10-03 10:48:06 +02004194 # Skip sub-operation
4195 result = 'COMPLETED'
4196 result_detail = 'Done'
4197 self.logger.debug(logging_text +
4198 "vnf_config_primitive={} Skipped sub-operation, result {} {}".
4199 format(vnf_config_primitive, result, result_detail))
4200 else:
quilesj4cda56b2019-12-05 10:02:20 +00004201 if op_index == self.SUBOPERATION_STATUS_NEW:
kuuseac3a8882019-10-03 10:48:06 +02004202 # New sub-operation: Get index of this sub-operation
4203 op_index = len(db_nslcmop.get('_admin', {}).get('operations')) - 1
4204 self.logger.debug(logging_text + "vnf_config_primitive={} New sub-operation".
4205 format(vnf_config_primitive))
4206 else:
tierno7c4e24c2020-05-13 08:41:35 +00004207 # retry: Get registered params for this existing sub-operation
kuuseac3a8882019-10-03 10:48:06 +02004208 op = db_nslcmop.get('_admin', {}).get('operations', [])[op_index]
4209 vnf_index = op.get('member_vnf_index')
4210 vnf_config_primitive = op.get('primitive')
4211 primitive_params = op.get('primitive_params')
tierno7c4e24c2020-05-13 08:41:35 +00004212 self.logger.debug(logging_text + "vnf_config_primitive={} Sub-operation retry".
kuuseac3a8882019-10-03 10:48:06 +02004213 format(vnf_config_primitive))
tierno588547c2020-07-01 15:30:20 +00004214 # Execute the primitive, either with new (first-time) or registered (reintent) args
tiernoa278b842020-07-08 15:33:55 +00004215 ee_descriptor_id = config_primitive.get("execution-environment-ref")
4216 primitive_name = config_primitive.get("execution-environment-primitive",
4217 vnf_config_primitive)
tierno588547c2020-07-01 15:30:20 +00004218 ee_id, vca_type = self._look_for_deployed_vca(nsr_deployed["VCA"],
4219 member_vnf_index=vnf_index,
4220 vdu_id=None,
tiernoa278b842020-07-08 15:33:55 +00004221 vdu_count_index=None,
4222 ee_descriptor_id=ee_descriptor_id)
kuuseac3a8882019-10-03 10:48:06 +02004223 result, result_detail = await self._ns_execute_primitive(
limon625e0252021-03-15 09:39:43 +01004224 ee_id, primitive_name, primitive_params, vca_type=vca_type)
kuuseac3a8882019-10-03 10:48:06 +02004225 self.logger.debug(logging_text + "vnf_config_primitive={} Done with result {} {}".format(
4226 vnf_config_primitive, result, result_detail))
4227 # Update operationState = COMPLETED | FAILED
4228 self._update_suboperation_status(
4229 db_nslcmop, op_index, result, result_detail)
4230
tierno59d22d22018-09-25 18:10:19 +02004231 if result == "FAILED":
4232 raise LcmException(result_detail)
tiernod6de1992018-10-11 13:05:52 +02004233 db_nsr_update["config-status"] = old_config_status
4234 scale_process = None
kuuseac3a8882019-10-03 10:48:06 +02004235 # POST-SCALE END
tierno59d22d22018-09-25 18:10:19 +02004236
tiernod6de1992018-10-11 13:05:52 +02004237 db_nsr_update["detailed-status"] = "" # "scaled {} {}".format(scaling_group, scaling_type)
ikalyvas02d9e7b2019-05-27 18:16:01 +03004238 db_nsr_update["operational-status"] = "running" if old_operational_status == "failed" \
4239 else old_operational_status
tiernod6de1992018-10-11 13:05:52 +02004240 db_nsr_update["config-status"] = old_config_status
tierno59d22d22018-09-25 18:10:19 +02004241 return
tierno2357f4e2020-10-19 16:38:59 +00004242 except (ROclient.ROClientException, DbException, LcmException, NgRoException) as e:
tierno59d22d22018-09-25 18:10:19 +02004243 self.logger.error(logging_text + "Exit Exception {}".format(e))
4244 exc = e
4245 except asyncio.CancelledError:
4246 self.logger.error(logging_text + "Cancelled Exception while '{}'".format(step))
4247 exc = "Operation was cancelled"
4248 except Exception as e:
4249 exc = traceback.format_exc()
4250 self.logger.critical(logging_text + "Exit Exception {} {}".format(type(e).__name__, e), exc_info=True)
4251 finally:
bravof922c4172020-11-24 21:21:43 -03004252 self._write_ns_status(nsr_id=nsr_id, ns_state=None, current_operation="IDLE", current_operation_id=None)
aktas13251562021-02-12 22:19:10 +03004253 if tasks_dict_info:
4254 stage[1] = "Waiting for instantiate pending tasks."
4255 self.logger.debug(logging_text + stage[1])
4256 exc = await self._wait_for_tasks(logging_text, tasks_dict_info, self.timeout_ns_deploy,
4257 stage, nslcmop_id, nsr_id=nsr_id)
tierno59d22d22018-09-25 18:10:19 +02004258 if exc:
tiernoa17d4f42020-04-28 09:59:23 +00004259 db_nslcmop_update["detailed-status"] = error_description_nslcmop = "FAILED {}: {}".format(step, exc)
4260 nslcmop_operation_state = "FAILED"
tierno59d22d22018-09-25 18:10:19 +02004261 if db_nsr:
tiernod6de1992018-10-11 13:05:52 +02004262 db_nsr_update["operational-status"] = old_operational_status
4263 db_nsr_update["config-status"] = old_config_status
4264 db_nsr_update["detailed-status"] = ""
4265 if scale_process:
4266 if "VCA" in scale_process:
4267 db_nsr_update["config-status"] = "failed"
4268 if "RO" in scale_process:
4269 db_nsr_update["operational-status"] = "failed"
4270 db_nsr_update["detailed-status"] = "FAILED scaling nslcmop={} {}: {}".format(nslcmop_id, step,
4271 exc)
tiernoa17d4f42020-04-28 09:59:23 +00004272 else:
4273 error_description_nslcmop = None
4274 nslcmop_operation_state = "COMPLETED"
4275 db_nslcmop_update["detailed-status"] = "Done"
quilesj4cda56b2019-12-05 10:02:20 +00004276
bravof922c4172020-11-24 21:21:43 -03004277 self._write_op_status(op_id=nslcmop_id, stage="", error_message=error_description_nslcmop,
4278 operation_state=nslcmop_operation_state, other_update=db_nslcmop_update)
tiernoa17d4f42020-04-28 09:59:23 +00004279 if db_nsr:
bravof922c4172020-11-24 21:21:43 -03004280 self._write_ns_status(nsr_id=nsr_id, ns_state=None, current_operation="IDLE",
4281 current_operation_id=None, other_update=db_nsr_update)
tiernoa17d4f42020-04-28 09:59:23 +00004282
tierno59d22d22018-09-25 18:10:19 +02004283 if nslcmop_operation_state:
4284 try:
bravof922c4172020-11-24 21:21:43 -03004285 msg = {"nsr_id": nsr_id, "nslcmop_id": nslcmop_id, "operationState": nslcmop_operation_state}
4286 await self.msg.aiowrite("ns", "scaled", msg, loop=self.loop)
tierno59d22d22018-09-25 18:10:19 +02004287 except Exception as e:
4288 self.logger.error(logging_text + "kafka_write notification Exception {}".format(e))
4289 self.logger.debug(logging_text + "Exit")
4290 self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_scale")
tiernob996d942020-07-03 14:52:28 +00004291
tierno2357f4e2020-10-19 16:38:59 +00004292 async def _scale_ng_ro(self, logging_text, db_nsr, db_nslcmop, db_vnfr, vdu_scaling_info, stage):
4293 nsr_id = db_nslcmop["nsInstanceId"]
4294 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
4295 db_vnfrs = {}
4296
4297 # read from db: vnfd's for every vnf
bravof832f8992020-12-07 12:57:31 -03004298 db_vnfds = []
tierno2357f4e2020-10-19 16:38:59 +00004299
4300 # for each vnf in ns, read vnfd
4301 for vnfr in self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id}):
4302 db_vnfrs[vnfr["member-vnf-index-ref"]] = vnfr
4303 vnfd_id = vnfr["vnfd-id"] # vnfd uuid for this vnf
tierno2357f4e2020-10-19 16:38:59 +00004304 # if we haven't this vnfd, read it from db
bravof832f8992020-12-07 12:57:31 -03004305 if not find_in_list(db_vnfds, lambda a_vnfd: a_vnfd["id"] == vnfd_id):
tierno2357f4e2020-10-19 16:38:59 +00004306 # read from db
4307 vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
bravof832f8992020-12-07 12:57:31 -03004308 db_vnfds.append(vnfd)
tierno2357f4e2020-10-19 16:38:59 +00004309 n2vc_key = self.n2vc.get_public_key()
4310 n2vc_key_list = [n2vc_key]
4311 self.scale_vnfr(db_vnfr, vdu_scaling_info.get("vdu-create"), vdu_scaling_info.get("vdu-delete"),
4312 mark_delete=True)
4313 # db_vnfr has been updated, update db_vnfrs to use it
4314 db_vnfrs[db_vnfr["member-vnf-index-ref"]] = db_vnfr
4315 await self._instantiate_ng_ro(logging_text, nsr_id, db_nsd, db_nsr, db_nslcmop, db_vnfrs,
bravof922c4172020-11-24 21:21:43 -03004316 db_vnfds, n2vc_key_list, stage=stage, start_deploy=time(),
tierno2357f4e2020-10-19 16:38:59 +00004317 timeout_ns_deploy=self.timeout_ns_deploy)
4318 if vdu_scaling_info.get("vdu-delete"):
4319 self.scale_vnfr(db_vnfr, None, vdu_scaling_info["vdu-delete"], mark_delete=False)
4320
tiernob996d942020-07-03 14:52:28 +00004321 async def add_prometheus_metrics(self, ee_id, artifact_path, ee_config_descriptor, vnfr_id, nsr_id, target_ip):
4322 if not self.prometheus:
4323 return
4324 # look if exist a file called 'prometheus*.j2' and
4325 artifact_content = self.fs.dir_ls(artifact_path)
4326 job_file = next((f for f in artifact_content if f.startswith("prometheus") and f.endswith(".j2")), None)
4327 if not job_file:
4328 return
4329 with self.fs.file_open((artifact_path, job_file), "r") as f:
4330 job_data = f.read()
4331
4332 # TODO get_service
4333 _, _, service = ee_id.partition(".") # remove prefix "namespace."
4334 host_name = "{}-{}".format(service, ee_config_descriptor["metric-service"])
4335 host_port = "80"
4336 vnfr_id = vnfr_id.replace("-", "")
4337 variables = {
4338 "JOB_NAME": vnfr_id,
4339 "TARGET_IP": target_ip,
4340 "EXPORTER_POD_IP": host_name,
4341 "EXPORTER_POD_PORT": host_port,
4342 }
4343 job_list = self.prometheus.parse_job(job_data, variables)
4344 # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
4345 for job in job_list:
4346 if not isinstance(job.get("job_name"), str) or vnfr_id not in job["job_name"]:
4347 job["job_name"] = vnfr_id + "_" + str(randint(1, 10000))
4348 job["nsr_id"] = nsr_id
4349 job_dict = {jl["job_name"]: jl for jl in job_list}
4350 if await self.prometheus.update(job_dict):
4351 return list(job_dict.keys())
David Garciaaae391f2020-11-09 11:12:54 +01004352
4353 def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
4354 """
4355 Get VCA Cloud and VCA Cloud Credentials for the VIM account
4356
4357 :param: vim_account_id: VIM Account ID
4358
4359 :return: (cloud_name, cloud_credential)
4360 """
bravof922c4172020-11-24 21:21:43 -03004361 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
David Garciaaae391f2020-11-09 11:12:54 +01004362 return config.get("vca_cloud"), config.get("vca_cloud_credential")
4363
4364 def get_vca_k8s_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
4365 """
4366 Get VCA K8s Cloud and VCA K8s Cloud Credentials for the VIM account
4367
4368 :param: vim_account_id: VIM Account ID
4369
4370 :return: (cloud_name, cloud_credential)
4371 """
bravof922c4172020-11-24 21:21:43 -03004372 config = VimAccountDB.get_vim_account_with_id(vim_account_id).get("config", {})
David Garciaaae391f2020-11-09 11:12:54 +01004373 return config.get("vca_k8s_cloud"), config.get("vca_k8s_cloud_credential")