blob: 2566b0c0717e9c53dc3d813bbf02774916bd11b1 [file] [log] [blame]
quilesj29114342019-10-29 09:30:44 +01001##
2# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
3# This file is part of OSM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain 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,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# For those usages not covered by the Apache License, Version 2.0 please
20# contact with: nfvlabs@tid.es
21##
22
quilesj29114342019-10-29 09:30:44 +010023import asyncio
beierlmf52cb7c2020-04-21 16:36:35 -040024import logging
25import os
quilesj29114342019-10-29 09:30:44 +010026
beierlmf52cb7c2020-04-21 16:36:35 -040027from n2vc.exceptions import (
28 N2VCBadArgumentsException,
29 N2VCException,
30 N2VCConnectionException,
31 N2VCExecutionException,
almagiaba6e5322020-09-16 09:44:40 +020032 # N2VCNotFound,
beierlmf52cb7c2020-04-21 16:36:35 -040033 MethodNotImplemented,
Dominik Fleischmannb9513342020-06-09 11:57:14 +020034 JujuK8sProxycharmNotSupported,
beierlmf52cb7c2020-04-21 16:36:35 -040035)
quilesj29114342019-10-29 09:30:44 +010036from n2vc.n2vc_conn import N2VCConnector
quilesj776ab392019-12-12 16:10:54 +000037from n2vc.n2vc_conn import obj_to_dict, obj_to_yaml
David Garcia4fee80e2020-05-13 12:18:38 +020038from n2vc.libjuju import Libjuju
David Garcia475a7222020-09-21 16:19:15 +020039from n2vc.utils import base64_to_cacert
quilesj29114342019-10-29 09:30:44 +010040
41
42class N2VCJujuConnector(N2VCConnector):
43
44 """
David Garcia4fee80e2020-05-13 12:18:38 +020045####################################################################################
46################################### P U B L I C ####################################
47####################################################################################
quilesj29114342019-10-29 09:30:44 +010048 """
49
David Garcia347aae62020-04-29 12:34:23 +020050 BUILT_IN_CLOUDS = ["localhost", "microk8s"]
51
quilesj29114342019-10-29 09:30:44 +010052 def __init__(
53 self,
54 db: object,
55 fs: object,
56 log: object = None,
57 loop: object = None,
beierlmf52cb7c2020-04-21 16:36:35 -040058 url: str = "127.0.0.1:17070",
59 username: str = "admin",
quilesj29114342019-10-29 09:30:44 +010060 vca_config: dict = None,
beierlmf52cb7c2020-04-21 16:36:35 -040061 on_update_db=None,
quilesj29114342019-10-29 09:30:44 +010062 ):
63 """Initialize juju N2VC connector
64 """
65
66 # parent class constructor
67 N2VCConnector.__init__(
68 self,
69 db=db,
70 fs=fs,
71 log=log,
72 loop=loop,
73 url=url,
74 username=username,
75 vca_config=vca_config,
beierlmf52cb7c2020-04-21 16:36:35 -040076 on_update_db=on_update_db,
quilesj29114342019-10-29 09:30:44 +010077 )
78
79 # silence websocket traffic log
beierlmf52cb7c2020-04-21 16:36:35 -040080 logging.getLogger("websockets.protocol").setLevel(logging.INFO)
81 logging.getLogger("juju.client.connection").setLevel(logging.WARN)
82 logging.getLogger("model").setLevel(logging.WARN)
quilesj29114342019-10-29 09:30:44 +010083
beierlmf52cb7c2020-04-21 16:36:35 -040084 self.log.info("Initializing N2VC juju connector...")
quilesj29114342019-10-29 09:30:44 +010085
86 """
87 ##############################################################
88 # check arguments
89 ##############################################################
90 """
91
92 # juju URL
93 if url is None:
beierlmf52cb7c2020-04-21 16:36:35 -040094 raise N2VCBadArgumentsException("Argument url is mandatory", ["url"])
95 url_parts = url.split(":")
quilesj29114342019-10-29 09:30:44 +010096 if len(url_parts) != 2:
beierlmf52cb7c2020-04-21 16:36:35 -040097 raise N2VCBadArgumentsException(
98 "Argument url: bad format (localhost:port) -> {}".format(url), ["url"]
99 )
quilesj29114342019-10-29 09:30:44 +0100100 self.hostname = url_parts[0]
101 try:
102 self.port = int(url_parts[1])
103 except ValueError:
beierlmf52cb7c2020-04-21 16:36:35 -0400104 raise N2VCBadArgumentsException(
105 "url port must be a number -> {}".format(url), ["url"]
106 )
quilesj29114342019-10-29 09:30:44 +0100107
108 # juju USERNAME
109 if username is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400110 raise N2VCBadArgumentsException(
111 "Argument username is mandatory", ["username"]
112 )
quilesj29114342019-10-29 09:30:44 +0100113
114 # juju CONFIGURATION
115 if vca_config is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400116 raise N2VCBadArgumentsException(
117 "Argument vca_config is mandatory", ["vca_config"]
118 )
quilesj29114342019-10-29 09:30:44 +0100119
beierlmf52cb7c2020-04-21 16:36:35 -0400120 if "secret" in vca_config:
121 self.secret = vca_config["secret"]
quilesj29114342019-10-29 09:30:44 +0100122 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400123 raise N2VCBadArgumentsException(
124 "Argument vca_config.secret is mandatory", ["vca_config.secret"]
125 )
quilesj29114342019-10-29 09:30:44 +0100126
127 # pubkey of juju client in osm machine: ~/.local/share/juju/ssh/juju_id_rsa.pub
128 # if exists, it will be written in lcm container: _create_juju_public_key()
beierlmf52cb7c2020-04-21 16:36:35 -0400129 if "public_key" in vca_config:
130 self.public_key = vca_config["public_key"]
quilesj29114342019-10-29 09:30:44 +0100131 else:
132 self.public_key = None
133
134 # TODO: Verify ca_cert is valid before using. VCA will crash
135 # if the ca_cert isn't formatted correctly.
quilesj29114342019-10-29 09:30:44 +0100136
beierlmf52cb7c2020-04-21 16:36:35 -0400137 self.ca_cert = vca_config.get("ca_cert")
quilesj29114342019-10-29 09:30:44 +0100138 if self.ca_cert:
beierlmf52cb7c2020-04-21 16:36:35 -0400139 self.ca_cert = base64_to_cacert(vca_config["ca_cert"])
quilesj29114342019-10-29 09:30:44 +0100140
David Garcia81045962020-07-16 12:37:13 +0200141 if "api_proxy" in vca_config and vca_config["api_proxy"] != "":
beierlmf52cb7c2020-04-21 16:36:35 -0400142 self.api_proxy = vca_config["api_proxy"]
143 self.log.debug(
144 "api_proxy for native charms configured: {}".format(self.api_proxy)
145 )
quilesj29114342019-10-29 09:30:44 +0100146 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400147 self.warning(
David Garcia81045962020-07-16 12:37:13 +0200148 "api_proxy is not configured"
beierlmf52cb7c2020-04-21 16:36:35 -0400149 )
tiernoec8a5042020-06-24 13:57:10 +0000150 self.api_proxy = None
quilesj29114342019-10-29 09:30:44 +0100151
beierlmf52cb7c2020-04-21 16:36:35 -0400152 if "enable_os_upgrade" in vca_config:
153 self.enable_os_upgrade = vca_config["enable_os_upgrade"]
garciadeblas923510c2019-12-17 15:02:11 +0100154 else:
155 self.enable_os_upgrade = True
156
beierlmf52cb7c2020-04-21 16:36:35 -0400157 if "apt_mirror" in vca_config:
158 self.apt_mirror = vca_config["apt_mirror"]
garciadeblas923510c2019-12-17 15:02:11 +0100159 else:
160 self.apt_mirror = None
161
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200162 self.cloud = vca_config.get('cloud')
163 self.k8s_cloud = None
164 if "k8s_cloud" in vca_config:
165 self.k8s_cloud = vca_config.get("k8s_cloud")
166 self.log.debug('Arguments have been checked')
quilesj29114342019-10-29 09:30:44 +0100167
168 # juju data
beierlmf52cb7c2020-04-21 16:36:35 -0400169 self.controller = None # it will be filled when connect to juju
170 self.juju_models = {} # model objects for every model_name
171 self.juju_observers = {} # model observers for every model_name
172 self._connecting = (
173 False # while connecting to juju (to avoid duplicate connections)
174 )
175 self._authenticated = (
176 False # it will be True when juju connection be stablished
177 )
178 self._creating_model = False # True during model creation
David Garcia4fee80e2020-05-13 12:18:38 +0200179 self.libjuju = Libjuju(
180 endpoint=self.url,
181 api_proxy=self.api_proxy,
182 enable_os_upgrade=self.enable_os_upgrade,
183 apt_mirror=self.apt_mirror,
184 username=self.username,
185 password=self.secret,
186 cacert=self.ca_cert,
187 loop=self.loop,
188 log=self.log,
189 db=self.db,
190 n2vc=self,
191 )
quilesj29114342019-10-29 09:30:44 +0100192
beierlmf52cb7c2020-04-21 16:36:35 -0400193 # create juju pub key file in lcm container at
194 # ./local/share/juju/ssh/juju_id_rsa.pub
quilesj29114342019-10-29 09:30:44 +0100195 self._create_juju_public_key()
196
beierlmf52cb7c2020-04-21 16:36:35 -0400197 self.log.info("N2VC juju connector initialized")
quilesj29114342019-10-29 09:30:44 +0100198
quilesj776ab392019-12-12 16:10:54 +0000199 async def get_status(self, namespace: str, yaml_format: bool = True):
200
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100201 # self.log.info('Getting NS status. namespace: {}'.format(namespace))
quilesj29114342019-10-29 09:30:44 +0100202
beierlmf52cb7c2020-04-21 16:36:35 -0400203 _nsi_id, ns_id, _vnf_id, _vdu_id, _vdu_count = self._get_namespace_components(
204 namespace=namespace
205 )
quilesj29114342019-10-29 09:30:44 +0100206 # model name is ns_id
207 model_name = ns_id
208 if model_name is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400209 msg = "Namespace {} not valid".format(namespace)
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100210 self.log.error(msg)
beierlmf52cb7c2020-04-21 16:36:35 -0400211 raise N2VCBadArgumentsException(msg, ["namespace"])
quilesj29114342019-10-29 09:30:44 +0100212
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200213 status = {}
214 models = await self.libjuju.list_models(contains=ns_id)
215
216 for m in models:
David Garciad745e222020-06-30 08:39:26 +0200217 status[m] = await self.libjuju.get_model_status(m)
quilesj29114342019-10-29 09:30:44 +0100218
quilesj776ab392019-12-12 16:10:54 +0000219 if yaml_format:
220 return obj_to_yaml(status)
221 else:
222 return obj_to_dict(status)
quilesj29114342019-10-29 09:30:44 +0100223
224 async def create_execution_environment(
225 self,
226 namespace: str,
227 db_dict: dict,
228 reuse_ee_id: str = None,
229 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400230 total_timeout: float = None,
David Garcia9a63e8d2020-11-03 20:37:06 +0100231 cloud_name: str = None,
232 credential_name: str = None,
quilesj29114342019-10-29 09:30:44 +0100233 ) -> (str, dict):
234
beierlmf52cb7c2020-04-21 16:36:35 -0400235 self.log.info(
236 "Creating execution environment. namespace: {}, reuse_ee_id: {}".format(
237 namespace, reuse_ee_id
238 )
239 )
quilesj29114342019-10-29 09:30:44 +0100240
quilesj29114342019-10-29 09:30:44 +0100241 machine_id = None
242 if reuse_ee_id:
beierlmf52cb7c2020-04-21 16:36:35 -0400243 model_name, application_name, machine_id = self._get_ee_id_components(
244 ee_id=reuse_ee_id
245 )
quilesj29114342019-10-29 09:30:44 +0100246 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400247 (
248 _nsi_id,
249 ns_id,
250 _vnf_id,
251 _vdu_id,
252 _vdu_count,
253 ) = self._get_namespace_components(namespace=namespace)
quilesj29114342019-10-29 09:30:44 +0100254 # model name is ns_id
255 model_name = ns_id
256 # application name
257 application_name = self._get_application_name(namespace=namespace)
258
beierlmf52cb7c2020-04-21 16:36:35 -0400259 self.log.debug(
260 "model name: {}, application name: {}, machine_id: {}".format(
261 model_name, application_name, machine_id
262 )
263 )
quilesj29114342019-10-29 09:30:44 +0100264
265 # create or reuse a new juju machine
266 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200267 if not await self.libjuju.model_exists(model_name):
David Garcia9a63e8d2020-11-03 20:37:06 +0100268 cloud = cloud_name or self.cloud
269 credential = credential_name or cloud_name if cloud_name else self.cloud
270 await self.libjuju.add_model(
271 model_name,
272 cloud_name=cloud,
273 credential_name=credential
274 )
David Garcia4fee80e2020-05-13 12:18:38 +0200275 machine, new = await self.libjuju.create_machine(
quilesj29114342019-10-29 09:30:44 +0100276 model_name=model_name,
quilesj29114342019-10-29 09:30:44 +0100277 machine_id=machine_id,
278 db_dict=db_dict,
279 progress_timeout=progress_timeout,
beierlmf52cb7c2020-04-21 16:36:35 -0400280 total_timeout=total_timeout,
quilesj29114342019-10-29 09:30:44 +0100281 )
David Garcia4fee80e2020-05-13 12:18:38 +0200282 # id for the execution environment
283 ee_id = N2VCJujuConnector._build_ee_id(
284 model_name=model_name,
285 application_name=application_name,
286 machine_id=str(machine.entity_id),
287 )
288 self.log.debug("ee_id: {}".format(ee_id))
289
290 if new:
291 # write ee_id in database
292 self._write_ee_id_db(db_dict=db_dict, ee_id=ee_id)
293
quilesj29114342019-10-29 09:30:44 +0100294 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400295 message = "Error creating machine on juju: {}".format(e)
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100296 self.log.error(message)
quilesj29114342019-10-29 09:30:44 +0100297 raise N2VCException(message=message)
298
quilesj29114342019-10-29 09:30:44 +0100299 # new machine credentials
David Garcia4fee80e2020-05-13 12:18:38 +0200300 credentials = {
301 "hostname": machine.dns_name,
302 }
quilesj29114342019-10-29 09:30:44 +0100303
beierlmf52cb7c2020-04-21 16:36:35 -0400304 self.log.info(
305 "Execution environment created. ee_id: {}, credentials: {}".format(
306 ee_id, credentials
307 )
308 )
quilesj29114342019-10-29 09:30:44 +0100309
310 return ee_id, credentials
311
312 async def register_execution_environment(
313 self,
314 namespace: str,
315 credentials: dict,
316 db_dict: dict,
317 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400318 total_timeout: float = None,
David Garcia9a63e8d2020-11-03 20:37:06 +0100319 cloud_name: str = None,
320 credential_name: str = None,
quilesj29114342019-10-29 09:30:44 +0100321 ) -> str:
322
beierlmf52cb7c2020-04-21 16:36:35 -0400323 self.log.info(
324 "Registering execution environment. namespace={}, credentials={}".format(
325 namespace, credentials
326 )
327 )
quilesj29114342019-10-29 09:30:44 +0100328
329 if credentials is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400330 raise N2VCBadArgumentsException(
331 message="credentials are mandatory", bad_args=["credentials"]
332 )
333 if credentials.get("hostname"):
334 hostname = credentials["hostname"]
quilesj29114342019-10-29 09:30:44 +0100335 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400336 raise N2VCBadArgumentsException(
337 message="hostname is mandatory", bad_args=["credentials.hostname"]
338 )
339 if credentials.get("username"):
340 username = credentials["username"]
quilesj29114342019-10-29 09:30:44 +0100341 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400342 raise N2VCBadArgumentsException(
343 message="username is mandatory", bad_args=["credentials.username"]
344 )
345 if "private_key_path" in credentials:
346 private_key_path = credentials["private_key_path"]
quilesj29114342019-10-29 09:30:44 +0100347 else:
348 # if not passed as argument, use generated private key path
349 private_key_path = self.private_key_path
350
beierlmf52cb7c2020-04-21 16:36:35 -0400351 _nsi_id, ns_id, _vnf_id, _vdu_id, _vdu_count = self._get_namespace_components(
352 namespace=namespace
353 )
quilesj29114342019-10-29 09:30:44 +0100354
355 # model name
356 model_name = ns_id
357 # application name
358 application_name = self._get_application_name(namespace=namespace)
359
360 # register machine on juju
361 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200362 if not await self.libjuju.model_exists(model_name):
David Garcia9a63e8d2020-11-03 20:37:06 +0100363 cloud = cloud_name or self.cloud
364 credential = credential_name or cloud_name if cloud_name else self.cloud
365 await self.libjuju.add_model(
366 model_name,
367 cloud_name=cloud,
368 credential_name=credential
369 )
David Garcia4fee80e2020-05-13 12:18:38 +0200370 machine_id = await self.libjuju.provision_machine(
quilesj29114342019-10-29 09:30:44 +0100371 model_name=model_name,
372 hostname=hostname,
373 username=username,
374 private_key_path=private_key_path,
375 db_dict=db_dict,
376 progress_timeout=progress_timeout,
beierlmf52cb7c2020-04-21 16:36:35 -0400377 total_timeout=total_timeout,
quilesj29114342019-10-29 09:30:44 +0100378 )
379 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400380 self.log.error("Error registering machine: {}".format(e))
381 raise N2VCException(
382 message="Error registering machine on juju: {}".format(e)
383 )
quilesjac4e0de2019-11-27 16:12:02 +0000384
beierlmf52cb7c2020-04-21 16:36:35 -0400385 self.log.info("Machine registered: {}".format(machine_id))
quilesj29114342019-10-29 09:30:44 +0100386
387 # id for the execution environment
388 ee_id = N2VCJujuConnector._build_ee_id(
389 model_name=model_name,
390 application_name=application_name,
beierlmf52cb7c2020-04-21 16:36:35 -0400391 machine_id=str(machine_id),
quilesj29114342019-10-29 09:30:44 +0100392 )
393
beierlmf52cb7c2020-04-21 16:36:35 -0400394 self.log.info("Execution environment registered. ee_id: {}".format(ee_id))
quilesj29114342019-10-29 09:30:44 +0100395
396 return ee_id
397
398 async def install_configuration_sw(
399 self,
400 ee_id: str,
401 artifact_path: str,
402 db_dict: dict,
403 progress_timeout: float = None,
David Garciadfaa6e82020-04-01 16:06:39 +0200404 total_timeout: float = None,
405 config: dict = None,
David Garciaf8a9d462020-03-25 18:19:02 +0100406 num_units: int = 1,
quilesj29114342019-10-29 09:30:44 +0100407 ):
408
beierlmf52cb7c2020-04-21 16:36:35 -0400409 self.log.info(
410 (
411 "Installing configuration sw on ee_id: {}, "
412 "artifact path: {}, db_dict: {}"
413 ).format(ee_id, artifact_path, db_dict)
414 )
quilesj29114342019-10-29 09:30:44 +0100415
quilesj29114342019-10-29 09:30:44 +0100416 # check arguments
417 if ee_id is None or len(ee_id) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400418 raise N2VCBadArgumentsException(
419 message="ee_id is mandatory", bad_args=["ee_id"]
420 )
quilesj29114342019-10-29 09:30:44 +0100421 if artifact_path is None or len(artifact_path) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400422 raise N2VCBadArgumentsException(
423 message="artifact_path is mandatory", bad_args=["artifact_path"]
424 )
quilesj29114342019-10-29 09:30:44 +0100425 if db_dict is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400426 raise N2VCBadArgumentsException(
427 message="db_dict is mandatory", bad_args=["db_dict"]
428 )
quilesj29114342019-10-29 09:30:44 +0100429
430 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400431 (
432 model_name,
433 application_name,
434 machine_id,
435 ) = N2VCJujuConnector._get_ee_id_components(ee_id=ee_id)
436 self.log.debug(
437 "model: {}, application: {}, machine: {}".format(
438 model_name, application_name, machine_id
439 )
440 )
441 except Exception:
quilesj29114342019-10-29 09:30:44 +0100442 raise N2VCBadArgumentsException(
beierlmf52cb7c2020-04-21 16:36:35 -0400443 message="ee_id={} is not a valid execution environment id".format(
444 ee_id
445 ),
446 bad_args=["ee_id"],
quilesj29114342019-10-29 09:30:44 +0100447 )
448
449 # remove // in charm path
beierlmf52cb7c2020-04-21 16:36:35 -0400450 while artifact_path.find("//") >= 0:
451 artifact_path = artifact_path.replace("//", "/")
quilesj29114342019-10-29 09:30:44 +0100452
453 # check charm path
454 if not self.fs.file_exists(artifact_path, mode="dir"):
beierlmf52cb7c2020-04-21 16:36:35 -0400455 msg = "artifact path does not exist: {}".format(artifact_path)
456 raise N2VCBadArgumentsException(message=msg, bad_args=["artifact_path"])
quilesj29114342019-10-29 09:30:44 +0100457
beierlmf52cb7c2020-04-21 16:36:35 -0400458 if artifact_path.startswith("/"):
quilesj29114342019-10-29 09:30:44 +0100459 full_path = self.fs.path + artifact_path
460 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400461 full_path = self.fs.path + "/" + artifact_path
quilesj29114342019-10-29 09:30:44 +0100462
463 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200464 await self.libjuju.deploy_charm(
quilesj29114342019-10-29 09:30:44 +0100465 model_name=model_name,
466 application_name=application_name,
David Garcia4fee80e2020-05-13 12:18:38 +0200467 path=full_path,
quilesj29114342019-10-29 09:30:44 +0100468 machine_id=machine_id,
469 db_dict=db_dict,
470 progress_timeout=progress_timeout,
David Garciadfaa6e82020-04-01 16:06:39 +0200471 total_timeout=total_timeout,
beierlmf52cb7c2020-04-21 16:36:35 -0400472 config=config,
David Garciaf8a9d462020-03-25 18:19:02 +0100473 num_units=num_units,
quilesj29114342019-10-29 09:30:44 +0100474 )
475 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400476 raise N2VCException(
477 message="Error desploying charm into ee={} : {}".format(ee_id, e)
478 )
quilesj29114342019-10-29 09:30:44 +0100479
beierlmf52cb7c2020-04-21 16:36:35 -0400480 self.log.info("Configuration sw installed")
quilesj29114342019-10-29 09:30:44 +0100481
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200482 async def install_k8s_proxy_charm(
483 self,
484 charm_name: str,
485 namespace: str,
486 artifact_path: str,
487 db_dict: dict,
488 progress_timeout: float = None,
489 total_timeout: float = None,
490 config: dict = None,
David Garcia9a63e8d2020-11-03 20:37:06 +0100491 cloud_name: str = None,
492 credential_name: str = None,
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200493 ) -> str:
494 """
495 Install a k8s proxy charm
496
497 :param charm_name: Name of the charm being deployed
498 :param namespace: collection of all the uuids related to the charm.
499 :param str artifact_path: where to locate the artifacts (parent folder) using
500 the self.fs
501 the final artifact path will be a combination of this artifact_path and
502 additional string from the config_dict (e.g. charm name)
503 :param dict db_dict: where to write into database when the status changes.
504 It contains a dict with
505 {collection: <str>, filter: {}, path: <str>},
506 e.g. {collection: "nsrs", filter:
507 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
508 :param float progress_timeout:
509 :param float total_timeout:
510 :param config: Dictionary with additional configuration
David Garcia9a63e8d2020-11-03 20:37:06 +0100511 :param cloud_name: Cloud Name in which the charms will be deployed
512 :param credential_name: Credential Name to use in the cloud_name.
513 If not set, cloud_name will be used as credential_name
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200514
515 :returns ee_id: execution environment id.
516 """
517 self.log.info('Installing k8s proxy charm: {}, artifact path: {}, db_dict: {}'
518 .format(charm_name, artifact_path, db_dict))
519
520 if not self.k8s_cloud:
521 raise JujuK8sProxycharmNotSupported("There is not k8s_cloud available")
522
523 if artifact_path is None or len(artifact_path) == 0:
524 raise N2VCBadArgumentsException(
525 message="artifact_path is mandatory", bad_args=["artifact_path"]
526 )
527 if db_dict is None:
528 raise N2VCBadArgumentsException(message='db_dict is mandatory', bad_args=['db_dict'])
529
530 # remove // in charm path
531 while artifact_path.find('//') >= 0:
532 artifact_path = artifact_path.replace('//', '/')
533
534 # check charm path
535 if not self.fs.file_exists(artifact_path, mode="dir"):
536 msg = 'artifact path does not exist: {}'.format(artifact_path)
537 raise N2VCBadArgumentsException(message=msg, bad_args=['artifact_path'])
538
539 if artifact_path.startswith('/'):
540 full_path = self.fs.path + artifact_path
541 else:
542 full_path = self.fs.path + '/' + artifact_path
543
544 _, ns_id, _, _, _ = self._get_namespace_components(namespace=namespace)
545 model_name = '{}-k8s'.format(ns_id)
David Garcia9a63e8d2020-11-03 20:37:06 +0100546 cloud = cloud_name or self.k8s_cloud
547 credential = credential_name or cloud_name if cloud_name else self.k8s_cloud
548 await self.libjuju.add_model(
549 model_name,
550 cloud_name=cloud,
551 credential_name=credential
552 )
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200553 application_name = self._get_application_name(namespace)
554
555 try:
556 await self.libjuju.deploy_charm(
557 model_name=model_name,
558 application_name=application_name,
559 path=full_path,
560 machine_id=None,
561 db_dict=db_dict,
562 progress_timeout=progress_timeout,
563 total_timeout=total_timeout,
564 config=config
565 )
566 except Exception as e:
567 raise N2VCException(message='Error deploying charm: {}'.format(e))
568
569 self.log.info('K8s proxy charm installed')
570 ee_id = N2VCJujuConnector._build_ee_id(
571 model_name=model_name,
572 application_name=application_name,
573 machine_id="k8s",
574 )
Dominik Fleischmann7ace6fa2020-06-29 16:16:28 +0200575
576 self._write_ee_id_db(db_dict=db_dict, ee_id=ee_id)
577
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200578 return ee_id
579
quilesj29114342019-10-29 09:30:44 +0100580 async def get_ee_ssh_public__key(
581 self,
582 ee_id: str,
583 db_dict: dict,
584 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400585 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100586 ) -> str:
587
beierlmf52cb7c2020-04-21 16:36:35 -0400588 self.log.info(
589 (
590 "Generating priv/pub key pair and get pub key on ee_id: {}, db_dict: {}"
591 ).format(ee_id, db_dict)
592 )
quilesj29114342019-10-29 09:30:44 +0100593
quilesj29114342019-10-29 09:30:44 +0100594 # check arguments
595 if ee_id is None or len(ee_id) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400596 raise N2VCBadArgumentsException(
597 message="ee_id is mandatory", bad_args=["ee_id"]
598 )
quilesj29114342019-10-29 09:30:44 +0100599 if db_dict is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400600 raise N2VCBadArgumentsException(
601 message="db_dict is mandatory", bad_args=["db_dict"]
602 )
quilesj29114342019-10-29 09:30:44 +0100603
604 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400605 (
606 model_name,
607 application_name,
608 machine_id,
609 ) = N2VCJujuConnector._get_ee_id_components(ee_id=ee_id)
610 self.log.debug(
611 "model: {}, application: {}, machine: {}".format(
612 model_name, application_name, machine_id
613 )
614 )
615 except Exception:
quilesj29114342019-10-29 09:30:44 +0100616 raise N2VCBadArgumentsException(
beierlmf52cb7c2020-04-21 16:36:35 -0400617 message="ee_id={} is not a valid execution environment id".format(
618 ee_id
619 ),
620 bad_args=["ee_id"],
quilesj29114342019-10-29 09:30:44 +0100621 )
622
623 # try to execute ssh layer primitives (if exist):
624 # generate-ssh-key
625 # get-ssh-public-key
626
627 output = None
628
David Garcia4fee80e2020-05-13 12:18:38 +0200629 application_name = N2VCJujuConnector._format_app_name(application_name)
630
quilesj29114342019-10-29 09:30:44 +0100631 # execute action: generate-ssh-key
632 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200633 output, _status = await self.libjuju.execute_action(
quilesj29114342019-10-29 09:30:44 +0100634 model_name=model_name,
635 application_name=application_name,
beierlmf52cb7c2020-04-21 16:36:35 -0400636 action_name="generate-ssh-key",
quilesj29114342019-10-29 09:30:44 +0100637 db_dict=db_dict,
638 progress_timeout=progress_timeout,
beierlmf52cb7c2020-04-21 16:36:35 -0400639 total_timeout=total_timeout,
quilesj29114342019-10-29 09:30:44 +0100640 )
641 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400642 self.log.info(
643 "Skipping exception while executing action generate-ssh-key: {}".format(
644 e
645 )
646 )
quilesj29114342019-10-29 09:30:44 +0100647
648 # execute action: get-ssh-public-key
649 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200650 output, _status = await self.libjuju.execute_action(
quilesj29114342019-10-29 09:30:44 +0100651 model_name=model_name,
652 application_name=application_name,
beierlmf52cb7c2020-04-21 16:36:35 -0400653 action_name="get-ssh-public-key",
quilesj29114342019-10-29 09:30:44 +0100654 db_dict=db_dict,
655 progress_timeout=progress_timeout,
beierlmf52cb7c2020-04-21 16:36:35 -0400656 total_timeout=total_timeout,
quilesj29114342019-10-29 09:30:44 +0100657 )
658 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400659 msg = "Cannot execute action get-ssh-public-key: {}\n".format(e)
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100660 self.log.info(msg)
David Garcia4fee80e2020-05-13 12:18:38 +0200661 raise N2VCExecutionException(e, primitive_name="get-ssh-public-key")
quilesj29114342019-10-29 09:30:44 +0100662
663 # return public key if exists
David Garcia9ae8fa52019-12-09 18:50:03 +0100664 return output["pubkey"] if "pubkey" in output else output
quilesj29114342019-10-29 09:30:44 +0100665
David Garcia85755d12020-09-21 19:51:23 +0200666 async def get_metrics(self, model_name: str, application_name: str) -> dict:
667 return await self.libjuju.get_metrics(model_name, application_name)
668
quilesj29114342019-10-29 09:30:44 +0100669 async def add_relation(
beierlmf52cb7c2020-04-21 16:36:35 -0400670 self, ee_id_1: str, ee_id_2: str, endpoint_1: str, endpoint_2: str
quilesj29114342019-10-29 09:30:44 +0100671 ):
672
beierlmf52cb7c2020-04-21 16:36:35 -0400673 self.log.debug(
674 "adding new relation between {} and {}, endpoints: {}, {}".format(
675 ee_id_1, ee_id_2, endpoint_1, endpoint_2
676 )
677 )
quilesj29114342019-10-29 09:30:44 +0100678
quilesjaae10b42020-01-09 08:49:10 +0000679 # check arguments
680 if not ee_id_1:
beierlmf52cb7c2020-04-21 16:36:35 -0400681 message = "EE 1 is mandatory"
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100682 self.log.error(message)
beierlmf52cb7c2020-04-21 16:36:35 -0400683 raise N2VCBadArgumentsException(message=message, bad_args=["ee_id_1"])
quilesjaae10b42020-01-09 08:49:10 +0000684 if not ee_id_2:
beierlmf52cb7c2020-04-21 16:36:35 -0400685 message = "EE 2 is mandatory"
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100686 self.log.error(message)
beierlmf52cb7c2020-04-21 16:36:35 -0400687 raise N2VCBadArgumentsException(message=message, bad_args=["ee_id_2"])
quilesjaae10b42020-01-09 08:49:10 +0000688 if not endpoint_1:
beierlmf52cb7c2020-04-21 16:36:35 -0400689 message = "endpoint 1 is mandatory"
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100690 self.log.error(message)
beierlmf52cb7c2020-04-21 16:36:35 -0400691 raise N2VCBadArgumentsException(message=message, bad_args=["endpoint_1"])
quilesjaae10b42020-01-09 08:49:10 +0000692 if not endpoint_2:
beierlmf52cb7c2020-04-21 16:36:35 -0400693 message = "endpoint 2 is mandatory"
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100694 self.log.error(message)
beierlmf52cb7c2020-04-21 16:36:35 -0400695 raise N2VCBadArgumentsException(message=message, bad_args=["endpoint_2"])
quilesjaae10b42020-01-09 08:49:10 +0000696
quilesjaae10b42020-01-09 08:49:10 +0000697 # get the model, the applications and the machines from the ee_id's
beierlmf52cb7c2020-04-21 16:36:35 -0400698 model_1, app_1, _machine_1 = self._get_ee_id_components(ee_id_1)
699 model_2, app_2, _machine_2 = self._get_ee_id_components(ee_id_2)
quilesj29114342019-10-29 09:30:44 +0100700
701 # model must be the same
702 if model_1 != model_2:
beierlmf52cb7c2020-04-21 16:36:35 -0400703 message = "EE models are not the same: {} vs {}".format(ee_id_1, ee_id_2)
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100704 self.log.error(message)
beierlmf52cb7c2020-04-21 16:36:35 -0400705 raise N2VCBadArgumentsException(
706 message=message, bad_args=["ee_id_1", "ee_id_2"]
707 )
quilesj29114342019-10-29 09:30:44 +0100708
709 # add juju relations between two applications
710 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200711 await self.libjuju.add_relation(
quilesjaae10b42020-01-09 08:49:10 +0000712 model_name=model_1,
David Garcia8331f7c2020-08-25 16:10:07 +0200713 endpoint_1="{}:{}".format(app_1, endpoint_1),
714 endpoint_2="{}:{}".format(app_2, endpoint_2),
quilesjaae10b42020-01-09 08:49:10 +0000715 )
quilesj29114342019-10-29 09:30:44 +0100716 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400717 message = "Error adding relation between {} and {}: {}".format(
718 ee_id_1, ee_id_2, e
719 )
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100720 self.log.error(message)
quilesj29114342019-10-29 09:30:44 +0100721 raise N2VCException(message=message)
722
beierlmf52cb7c2020-04-21 16:36:35 -0400723 async def remove_relation(self):
quilesj29114342019-10-29 09:30:44 +0100724 # TODO
beierlmf52cb7c2020-04-21 16:36:35 -0400725 self.log.info("Method not implemented yet")
726 raise MethodNotImplemented()
quilesj29114342019-10-29 09:30:44 +0100727
beierlmf52cb7c2020-04-21 16:36:35 -0400728 async def deregister_execution_environments(self):
beierlmf52cb7c2020-04-21 16:36:35 -0400729 self.log.info("Method not implemented yet")
730 raise MethodNotImplemented()
quilesj29114342019-10-29 09:30:44 +0100731
732 async def delete_namespace(
beierlmf52cb7c2020-04-21 16:36:35 -0400733 self, namespace: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100734 ):
beierlmf52cb7c2020-04-21 16:36:35 -0400735 self.log.info("Deleting namespace={}".format(namespace))
quilesj29114342019-10-29 09:30:44 +0100736
quilesj29114342019-10-29 09:30:44 +0100737 # check arguments
738 if namespace is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400739 raise N2VCBadArgumentsException(
740 message="namespace is mandatory", bad_args=["namespace"]
741 )
quilesj29114342019-10-29 09:30:44 +0100742
beierlmf52cb7c2020-04-21 16:36:35 -0400743 _nsi_id, ns_id, _vnf_id, _vdu_id, _vdu_count = self._get_namespace_components(
744 namespace=namespace
745 )
quilesj29114342019-10-29 09:30:44 +0100746 if ns_id is not None:
quilesj29114342019-10-29 09:30:44 +0100747 try:
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200748 models = await self.libjuju.list_models(contains=ns_id)
749 for model in models:
750 await self.libjuju.destroy_model(
751 model_name=model, total_timeout=total_timeout
752 )
quilesj29114342019-10-29 09:30:44 +0100753 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400754 raise N2VCException(
755 message="Error deleting namespace {} : {}".format(namespace, e)
756 )
quilesj29114342019-10-29 09:30:44 +0100757 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400758 raise N2VCBadArgumentsException(
759 message="only ns_id is permitted to delete yet", bad_args=["namespace"]
760 )
quilesj29114342019-10-29 09:30:44 +0100761
beierlmf52cb7c2020-04-21 16:36:35 -0400762 self.log.info("Namespace {} deleted".format(namespace))
quilesj29114342019-10-29 09:30:44 +0100763
764 async def delete_execution_environment(
beierlmf52cb7c2020-04-21 16:36:35 -0400765 self, ee_id: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100766 ):
beierlmf52cb7c2020-04-21 16:36:35 -0400767 self.log.info("Deleting execution environment ee_id={}".format(ee_id))
quilesj29114342019-10-29 09:30:44 +0100768
quilesj29114342019-10-29 09:30:44 +0100769 # check arguments
770 if ee_id is None:
beierlmf52cb7c2020-04-21 16:36:35 -0400771 raise N2VCBadArgumentsException(
772 message="ee_id is mandatory", bad_args=["ee_id"]
773 )
quilesj29114342019-10-29 09:30:44 +0100774
beierlmf52cb7c2020-04-21 16:36:35 -0400775 model_name, application_name, _machine_id = self._get_ee_id_components(
776 ee_id=ee_id
777 )
quilesj29114342019-10-29 09:30:44 +0100778
779 # destroy the application
780 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200781 await self.libjuju.destroy_model(
782 model_name=model_name, total_timeout=total_timeout
beierlmf52cb7c2020-04-21 16:36:35 -0400783 )
quilesj29114342019-10-29 09:30:44 +0100784 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400785 raise N2VCException(
786 message=(
787 "Error deleting execution environment {} (application {}) : {}"
788 ).format(ee_id, application_name, e)
789 )
quilesj29114342019-10-29 09:30:44 +0100790
791 # destroy the machine
beierlmf52cb7c2020-04-21 16:36:35 -0400792 # try:
calvinosanch41395222020-02-21 09:25:21 +0100793 # await self._juju_destroy_machine(
794 # model_name=model_name,
795 # machine_id=machine_id,
796 # total_timeout=total_timeout
797 # )
798 # except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400799 # raise N2VCException(
800 # message='Error deleting execution environment {} (machine {}) : {}'
801 # .format(ee_id, machine_id, e))
quilesj29114342019-10-29 09:30:44 +0100802
beierlmf52cb7c2020-04-21 16:36:35 -0400803 self.log.info("Execution environment {} deleted".format(ee_id))
quilesj29114342019-10-29 09:30:44 +0100804
805 async def exec_primitive(
beierlmf52cb7c2020-04-21 16:36:35 -0400806 self,
807 ee_id: str,
808 primitive_name: str,
809 params_dict: dict,
810 db_dict: dict = None,
811 progress_timeout: float = None,
812 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100813 ) -> str:
814
beierlmf52cb7c2020-04-21 16:36:35 -0400815 self.log.info(
816 "Executing primitive: {} on ee: {}, params: {}".format(
817 primitive_name, ee_id, params_dict
818 )
819 )
quilesj29114342019-10-29 09:30:44 +0100820
quilesj29114342019-10-29 09:30:44 +0100821 # check arguments
822 if ee_id is None or len(ee_id) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400823 raise N2VCBadArgumentsException(
824 message="ee_id is mandatory", bad_args=["ee_id"]
825 )
quilesj29114342019-10-29 09:30:44 +0100826 if primitive_name is None or len(primitive_name) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400827 raise N2VCBadArgumentsException(
828 message="action_name is mandatory", bad_args=["action_name"]
829 )
quilesj29114342019-10-29 09:30:44 +0100830 if params_dict is None:
831 params_dict = dict()
832
833 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400834 (
835 model_name,
836 application_name,
837 _machine_id,
838 ) = N2VCJujuConnector._get_ee_id_components(ee_id=ee_id)
quilesj29114342019-10-29 09:30:44 +0100839 except Exception:
840 raise N2VCBadArgumentsException(
beierlmf52cb7c2020-04-21 16:36:35 -0400841 message="ee_id={} is not a valid execution environment id".format(
842 ee_id
843 ),
844 bad_args=["ee_id"],
quilesj29114342019-10-29 09:30:44 +0100845 )
846
beierlmf52cb7c2020-04-21 16:36:35 -0400847 if primitive_name == "config":
quilesj29114342019-10-29 09:30:44 +0100848 # Special case: config primitive
849 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200850 await self.libjuju.configure_application(
quilesj29114342019-10-29 09:30:44 +0100851 model_name=model_name,
852 application_name=application_name,
853 config=params_dict,
quilesj29114342019-10-29 09:30:44 +0100854 )
David Garcia4fee80e2020-05-13 12:18:38 +0200855 actions = await self.libjuju.get_actions(
856 application_name=application_name, model_name=model_name,
857 )
858 self.log.debug(
859 "Application {} has these actions: {}".format(
860 application_name, actions
861 )
862 )
863 if "verify-ssh-credentials" in actions:
864 # execute verify-credentials
865 num_retries = 20
866 retry_timeout = 15.0
867 for _ in range(num_retries):
868 try:
869 self.log.debug("Executing action verify-ssh-credentials...")
870 output, ok = await self.libjuju.execute_action(
871 model_name=model_name,
872 application_name=application_name,
873 action_name="verify-ssh-credentials",
874 db_dict=db_dict,
875 progress_timeout=progress_timeout,
876 total_timeout=total_timeout,
877 )
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200878
879 if ok == "failed":
880 self.log.debug(
881 "Error executing verify-ssh-credentials: {}. Retrying..."
882 )
883 await asyncio.sleep(retry_timeout)
884
885 continue
David Garcia4fee80e2020-05-13 12:18:38 +0200886 self.log.debug("Result: {}, output: {}".format(ok, output))
887 break
888 except asyncio.CancelledError:
889 raise
David Garcia4fee80e2020-05-13 12:18:38 +0200890 else:
891 self.log.error(
892 "Error executing verify-ssh-credentials after {} retries. ".format(
893 num_retries
894 )
895 )
896 else:
897 msg = "Action verify-ssh-credentials does not exist in application {}".format(
898 application_name
899 )
900 self.log.debug(msg=msg)
quilesj29114342019-10-29 09:30:44 +0100901 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400902 self.log.error("Error configuring juju application: {}".format(e))
quilesj29114342019-10-29 09:30:44 +0100903 raise N2VCExecutionException(
beierlmf52cb7c2020-04-21 16:36:35 -0400904 message="Error configuring application into ee={} : {}".format(
905 ee_id, e
906 ),
907 primitive_name=primitive_name,
quilesj29114342019-10-29 09:30:44 +0100908 )
beierlmf52cb7c2020-04-21 16:36:35 -0400909 return "CONFIG OK"
quilesj29114342019-10-29 09:30:44 +0100910 else:
911 try:
David Garcia4fee80e2020-05-13 12:18:38 +0200912 output, status = await self.libjuju.execute_action(
quilesj29114342019-10-29 09:30:44 +0100913 model_name=model_name,
914 application_name=application_name,
915 action_name=primitive_name,
916 db_dict=db_dict,
917 progress_timeout=progress_timeout,
918 total_timeout=total_timeout,
919 **params_dict
920 )
beierlmf52cb7c2020-04-21 16:36:35 -0400921 if status == "completed":
quilesj29114342019-10-29 09:30:44 +0100922 return output
923 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400924 raise Exception("status is not completed: {}".format(status))
quilesj29114342019-10-29 09:30:44 +0100925 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400926 self.log.error(
927 "Error executing primitive {}: {}".format(primitive_name, e)
928 )
quilesj29114342019-10-29 09:30:44 +0100929 raise N2VCExecutionException(
beierlmf52cb7c2020-04-21 16:36:35 -0400930 message="Error executing primitive {} into ee={} : {}".format(
931 primitive_name, ee_id, e
932 ),
933 primitive_name=primitive_name,
quilesj29114342019-10-29 09:30:44 +0100934 )
935
936 async def disconnect(self):
beierlmf52cb7c2020-04-21 16:36:35 -0400937 self.log.info("closing juju N2VC...")
David Garcia4fee80e2020-05-13 12:18:38 +0200938 try:
939 await self.libjuju.disconnect()
940 except Exception as e:
941 raise N2VCConnectionException(
942 message="Error disconnecting controller: {}".format(e), url=self.url
943 )
quilesj29114342019-10-29 09:30:44 +0100944
945 """
David Garcia4fee80e2020-05-13 12:18:38 +0200946####################################################################################
947################################### P R I V A T E ##################################
948####################################################################################
quilesj29114342019-10-29 09:30:44 +0100949 """
950
beierlmf52cb7c2020-04-21 16:36:35 -0400951 def _write_ee_id_db(self, db_dict: dict, ee_id: str):
quilesj29114342019-10-29 09:30:44 +0100952
953 # write ee_id to database: _admin.deployed.VCA.x
954 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400955 the_table = db_dict["collection"]
956 the_filter = db_dict["filter"]
957 the_path = db_dict["path"]
958 if not the_path[-1] == ".":
959 the_path = the_path + "."
960 update_dict = {the_path + "ee_id": ee_id}
Dominik Fleischmannf9bed352020-02-27 10:04:34 +0100961 # self.log.debug('Writing ee_id to database: {}'.format(the_path))
quilesj29114342019-10-29 09:30:44 +0100962 self.db.set_one(
963 table=the_table,
964 q_filter=the_filter,
965 update_dict=update_dict,
beierlmf52cb7c2020-04-21 16:36:35 -0400966 fail_on_empty=True,
quilesj29114342019-10-29 09:30:44 +0100967 )
tierno8ff11992020-03-26 09:51:11 +0000968 except asyncio.CancelledError:
969 raise
quilesj29114342019-10-29 09:30:44 +0100970 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400971 self.log.error("Error writing ee_id to database: {}".format(e))
quilesj29114342019-10-29 09:30:44 +0100972
973 @staticmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400974 def _build_ee_id(model_name: str, application_name: str, machine_id: str):
quilesj29114342019-10-29 09:30:44 +0100975 """
976 Build an execution environment id form model, application and machine
977 :param model_name:
978 :param application_name:
979 :param machine_id:
980 :return:
981 """
982 # id for the execution environment
beierlmf52cb7c2020-04-21 16:36:35 -0400983 return "{}.{}.{}".format(model_name, application_name, machine_id)
quilesj29114342019-10-29 09:30:44 +0100984
985 @staticmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400986 def _get_ee_id_components(ee_id: str) -> (str, str, str):
quilesj29114342019-10-29 09:30:44 +0100987 """
988 Get model, application and machine components from an execution environment id
989 :param ee_id:
990 :return: model_name, application_name, machine_id
991 """
992
993 if ee_id is None:
994 return None, None, None
995
996 # split components of id
beierlmf52cb7c2020-04-21 16:36:35 -0400997 parts = ee_id.split(".")
quilesj29114342019-10-29 09:30:44 +0100998 model_name = parts[0]
999 application_name = parts[1]
1000 machine_id = parts[2]
1001 return model_name, application_name, machine_id
1002
1003 def _get_application_name(self, namespace: str) -> str:
1004 """
1005 Build application name from namespace
1006 :param namespace:
1007 :return: app-vnf-<vnf id>-vdu-<vdu-id>-cnt-<vdu-count>
1008 """
1009
Adam Israel18046072019-12-08 21:44:29 -05001010 # TODO: Enforce the Juju 50-character application limit
1011
quilesj29114342019-10-29 09:30:44 +01001012 # split namespace components
beierlmf52cb7c2020-04-21 16:36:35 -04001013 _, _, vnf_id, vdu_id, vdu_count = self._get_namespace_components(
1014 namespace=namespace
1015 )
quilesj29114342019-10-29 09:30:44 +01001016
1017 if vnf_id is None or len(vnf_id) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -04001018 vnf_id = ""
quilesj29114342019-10-29 09:30:44 +01001019 else:
Adam Israel18046072019-12-08 21:44:29 -05001020 # Shorten the vnf_id to its last twelve characters
beierlmf52cb7c2020-04-21 16:36:35 -04001021 vnf_id = "vnf-" + vnf_id[-12:]
quilesj29114342019-10-29 09:30:44 +01001022
1023 if vdu_id is None or len(vdu_id) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -04001024 vdu_id = ""
quilesj29114342019-10-29 09:30:44 +01001025 else:
Adam Israel18046072019-12-08 21:44:29 -05001026 # Shorten the vdu_id to its last twelve characters
beierlmf52cb7c2020-04-21 16:36:35 -04001027 vdu_id = "-vdu-" + vdu_id[-12:]
quilesj29114342019-10-29 09:30:44 +01001028
1029 if vdu_count is None or len(vdu_count) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -04001030 vdu_count = ""
quilesj29114342019-10-29 09:30:44 +01001031 else:
beierlmf52cb7c2020-04-21 16:36:35 -04001032 vdu_count = "-cnt-" + vdu_count
quilesj29114342019-10-29 09:30:44 +01001033
beierlmf52cb7c2020-04-21 16:36:35 -04001034 application_name = "app-{}{}{}".format(vnf_id, vdu_id, vdu_count)
quilesj29114342019-10-29 09:30:44 +01001035
1036 return N2VCJujuConnector._format_app_name(application_name)
1037
quilesj29114342019-10-29 09:30:44 +01001038 def _create_juju_public_key(self):
1039 """Recreate the Juju public key on lcm container, if needed
1040 Certain libjuju commands expect to be run from the same machine as Juju
1041 is bootstrapped to. This method will write the public key to disk in
1042 that location: ~/.local/share/juju/ssh/juju_id_rsa.pub
1043 """
1044
1045 # Make sure that we have a public key before writing to disk
1046 if self.public_key is None or len(self.public_key) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -04001047 if "OSMLCM_VCA_PUBKEY" in os.environ:
1048 self.public_key = os.getenv("OSMLCM_VCA_PUBKEY", "")
quilesj29114342019-10-29 09:30:44 +01001049 if len(self.public_key) == 0:
1050 return
1051 else:
1052 return
1053
beierlmf52cb7c2020-04-21 16:36:35 -04001054 pk_path = "{}/.local/share/juju/ssh".format(os.path.expanduser("~"))
quilesj29114342019-10-29 09:30:44 +01001055 file_path = "{}/juju_id_rsa.pub".format(pk_path)
beierlmf52cb7c2020-04-21 16:36:35 -04001056 self.log.debug(
1057 "writing juju public key to file:\n{}\npublic key: {}".format(
1058 file_path, self.public_key
1059 )
1060 )
quilesj29114342019-10-29 09:30:44 +01001061 if not os.path.exists(pk_path):
1062 # create path and write file
1063 os.makedirs(pk_path)
beierlmf52cb7c2020-04-21 16:36:35 -04001064 with open(file_path, "w") as f:
1065 self.log.debug("Creating juju public key file: {}".format(file_path))
quilesj29114342019-10-29 09:30:44 +01001066 f.write(self.public_key)
1067 else:
beierlmf52cb7c2020-04-21 16:36:35 -04001068 self.log.debug("juju public key file already exists: {}".format(file_path))
quilesj29114342019-10-29 09:30:44 +01001069
1070 @staticmethod
1071 def _format_model_name(name: str) -> str:
1072 """Format the name of the model.
1073
1074 Model names may only contain lowercase letters, digits and hyphens
1075 """
1076
beierlmf52cb7c2020-04-21 16:36:35 -04001077 return name.replace("_", "-").replace(" ", "-").lower()
quilesj29114342019-10-29 09:30:44 +01001078
1079 @staticmethod
1080 def _format_app_name(name: str) -> str:
1081 """Format the name of the application (in order to assure valid application name).
1082
1083 Application names have restrictions (run juju deploy --help):
1084 - contains lowercase letters 'a'-'z'
1085 - contains numbers '0'-'9'
1086 - contains hyphens '-'
1087 - starts with a lowercase letter
1088 - not two or more consecutive hyphens
1089 - after a hyphen, not a group with all numbers
1090 """
1091
1092 def all_numbers(s: str) -> bool:
1093 for c in s:
1094 if not c.isdigit():
1095 return False
1096 return True
1097
beierlmf52cb7c2020-04-21 16:36:35 -04001098 new_name = name.replace("_", "-")
1099 new_name = new_name.replace(" ", "-")
quilesj29114342019-10-29 09:30:44 +01001100 new_name = new_name.lower()
beierlmf52cb7c2020-04-21 16:36:35 -04001101 while new_name.find("--") >= 0:
1102 new_name = new_name.replace("--", "-")
1103 groups = new_name.split("-")
quilesj29114342019-10-29 09:30:44 +01001104
1105 # find 'all numbers' groups and prefix them with a letter
beierlmf52cb7c2020-04-21 16:36:35 -04001106 app_name = ""
quilesj29114342019-10-29 09:30:44 +01001107 for i in range(len(groups)):
1108 group = groups[i]
1109 if all_numbers(group):
beierlmf52cb7c2020-04-21 16:36:35 -04001110 group = "z" + group
quilesj29114342019-10-29 09:30:44 +01001111 if i > 0:
beierlmf52cb7c2020-04-21 16:36:35 -04001112 app_name += "-"
quilesj29114342019-10-29 09:30:44 +01001113 app_name += group
1114
1115 if app_name[0].isdigit():
beierlmf52cb7c2020-04-21 16:36:35 -04001116 app_name = "z" + app_name
quilesj29114342019-10-29 09:30:44 +01001117
1118 return app_name