blob: 9e91a10b4eb1f12964cc9bf9648f6576a55570db [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
23
24import abc
25import asyncio
quilesj29114342019-10-29 09:30:44 +010026from http import HTTPStatus
beierlmf52cb7c2020-04-21 16:36:35 -040027import os
28import shlex
29import subprocess
30import time
31
quilesj29114342019-10-29 09:30:44 +010032from n2vc.exceptions import N2VCBadArgumentsException
beierlmf52cb7c2020-04-21 16:36:35 -040033from osm_common.dbmongo import DbException
quilesj776ab392019-12-12 16:10:54 +000034import yaml
quilesj29114342019-10-29 09:30:44 +010035
beierlmf52cb7c2020-04-21 16:36:35 -040036from n2vc.loggable import Loggable
David Garciac38a6962020-09-16 13:31:33 +020037from n2vc.utils import JujuStatusToOSM, N2VCDeploymentStatus
quilesj29114342019-10-29 09:30:44 +010038
39
40class N2VCConnector(abc.ABC, Loggable):
41 """Generic N2VC connector
42
43 Abstract class
44 """
45
46 """
beierlmf52cb7c2020-04-21 16:36:35 -040047 ####################################################################################
48 ################################### P U B L I C ####################################
49 ####################################################################################
quilesj29114342019-10-29 09:30:44 +010050 """
51
52 def __init__(
beierlmf52cb7c2020-04-21 16:36:35 -040053 self,
54 db: object,
55 fs: object,
56 log: object,
beierlmf52cb7c2020-04-21 16:36:35 -040057 on_update_db=None,
David Garcia6331b042021-04-26 15:36:24 +020058 **kwargs,
quilesj29114342019-10-29 09:30:44 +010059 ):
60 """Initialize N2VC abstract connector. It defines de API for VCA connectors
61
62 :param object db: Mongo object managing the MongoDB (repo common DbBase)
beierlmf52cb7c2020-04-21 16:36:35 -040063 :param object fs: FileSystem object managing the package artifacts (repo common
64 FsBase)
quilesj29114342019-10-29 09:30:44 +010065 :param object log: the logging object to log to
beierlmf52cb7c2020-04-21 16:36:35 -040066 :param on_update_db: callback called when n2vc connector updates database.
67 Received arguments:
quilesj29114342019-10-29 09:30:44 +010068 table: e.g. "nsrs"
69 filter: e.g. {_id: <nsd-id> }
70 path: e.g. "_admin.deployed.VCA.3."
71 updated_data: e.g. , "{ _admin.deployed.VCA.3.status: 'xxx', etc }"
72 """
73
74 # parent class
beierlmf52cb7c2020-04-21 16:36:35 -040075 Loggable.__init__(self, log=log, log_to_console=True, prefix="\nN2VC")
quilesj29114342019-10-29 09:30:44 +010076
77 # check arguments
78 if db is None:
beierlmf52cb7c2020-04-21 16:36:35 -040079 raise N2VCBadArgumentsException("Argument db is mandatory", ["db"])
quilesj29114342019-10-29 09:30:44 +010080 if fs is None:
beierlmf52cb7c2020-04-21 16:36:35 -040081 raise N2VCBadArgumentsException("Argument fs is mandatory", ["fs"])
quilesj29114342019-10-29 09:30:44 +010082
quilesj29114342019-10-29 09:30:44 +010083 # store arguments into self
84 self.db = db
85 self.fs = fs
quilesj29114342019-10-29 09:30:44 +010086 self.on_update_db = on_update_db
87
88 # generate private/public key-pair
quilesjac4e0de2019-11-27 16:12:02 +000089 self.private_key_path = None
90 self.public_key_path = None
quilesj29114342019-10-29 09:30:44 +010091
92 @abc.abstractmethod
quilesj776ab392019-12-12 16:10:54 +000093 async def get_status(self, namespace: str, yaml_format: bool = True):
quilesj29114342019-10-29 09:30:44 +010094 """Get namespace status
95
96 :param namespace: we obtain ns from namespace
quilesj776ab392019-12-12 16:10:54 +000097 :param yaml_format: returns a yaml string
quilesj29114342019-10-29 09:30:44 +010098 """
99
100 # TODO: review which public key
quilesjac4e0de2019-11-27 16:12:02 +0000101 def get_public_key(self) -> str:
quilesj29114342019-10-29 09:30:44 +0100102 """Get the VCA ssh-public-key
103
beierlmf52cb7c2020-04-21 16:36:35 -0400104 Returns the SSH public key from local mahine, to be injected into virtual
105 machines to be managed by the VCA.
quilesj29114342019-10-29 09:30:44 +0100106 First run, a ssh keypair will be created.
107 The public key is injected into a VM so that we can provision the
beierlmf52cb7c2020-04-21 16:36:35 -0400108 machine with Juju, after which Juju will communicate with the VM
quilesj29114342019-10-29 09:30:44 +0100109 directly via the juju agent.
110 """
111
quilesj29114342019-10-29 09:30:44 +0100112 # Find the path where we expect our key lives (~/.ssh)
beierlmf52cb7c2020-04-21 16:36:35 -0400113 homedir = os.environ.get("HOME")
quilesj776ab392019-12-12 16:10:54 +0000114 if not homedir:
Dominik Fleischmann0c478252020-07-15 14:44:45 +0200115 self.log.warning("No HOME environment variable, using /tmp")
beierlmf52cb7c2020-04-21 16:36:35 -0400116 homedir = "/tmp"
quilesj29114342019-10-29 09:30:44 +0100117 sshdir = "{}/.ssh".format(homedir)
selvi.jafde3be2023-04-28 06:17:26 +0000118 sshdir = os.path.realpath(os.path.normpath(os.path.abspath(sshdir)))
quilesj29114342019-10-29 09:30:44 +0100119 if not os.path.exists(sshdir):
120 os.mkdir(sshdir)
121
122 self.private_key_path = "{}/id_n2vc_rsa".format(sshdir)
selvi.jafde3be2023-04-28 06:17:26 +0000123 self.private_key_path = os.path.realpath(
124 os.path.normpath(os.path.abspath(self.private_key_path))
125 )
quilesj29114342019-10-29 09:30:44 +0100126 self.public_key_path = "{}.pub".format(self.private_key_path)
selvi.jafde3be2023-04-28 06:17:26 +0000127 self.public_key_path = os.path.realpath(
128 os.path.normpath(os.path.abspath(self.public_key_path))
129 )
quilesj29114342019-10-29 09:30:44 +0100130
131 # If we don't have a key generated, then we have to generate it using ssh-keygen
132 if not os.path.exists(self.private_key_path):
selvi.jafde3be2023-04-28 06:17:26 +0000133 command = "ssh-keygen -t {} -b {} -N '' -f {}".format(
beierlmf52cb7c2020-04-21 16:36:35 -0400134 "rsa", "4096", self.private_key_path
quilesj29114342019-10-29 09:30:44 +0100135 )
136 # run command with arguments
selvi.jafde3be2023-04-28 06:17:26 +0000137 args = shlex.split(command)
138 subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
quilesj29114342019-10-29 09:30:44 +0100139
140 # Read the public key. Only one public key (one line) in the file
141 with open(self.public_key_path, "r") as file:
142 public_key = file.readline()
143
144 return public_key
145
146 @abc.abstractmethod
147 async def create_execution_environment(
148 self,
149 namespace: str,
150 db_dict: dict,
151 reuse_ee_id: str = None,
152 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400153 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100154 ) -> (str, dict):
beierlmf52cb7c2020-04-21 16:36:35 -0400155 """Create an Execution Environment. Returns when it is created or raises an
156 exception on failing
quilesj29114342019-10-29 09:30:44 +0100157
158 :param str namespace: Contains a dot separate string.
159 LCM will use: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
160 :param dict db_dict: where to write to database when the status changes.
161 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400162 e.g. {collection: "nsrs", filter: {_id: <nsd-id>, path:
163 "_admin.deployed.VCA.3"}
164 :param str reuse_ee_id: ee id from an older execution. It allows us to reuse an
165 older environment
quilesj29114342019-10-29 09:30:44 +0100166 :param float progress_timeout:
167 :param float total_timeout:
168 :returns str, dict: id of the new execution environment and credentials for it
beierlmf52cb7c2020-04-21 16:36:35 -0400169 (credentials can contains hostname, username, etc depending on
170 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100171 """
172
173 @abc.abstractmethod
174 async def register_execution_environment(
175 self,
176 namespace: str,
177 credentials: dict,
178 db_dict: dict,
179 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400180 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100181 ) -> str:
182 """
183 Register an existing execution environment at the VCA
184
185 :param str namespace: same as create_execution_environment method
beierlmf52cb7c2020-04-21 16:36:35 -0400186 :param dict credentials: credentials to access the existing execution
187 environment
188 (it can contains hostname, username, path to private key, etc depending on
189 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100190 :param dict db_dict: where to write to database when the status changes.
191 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400192 e.g. {collection: "nsrs", filter:
193 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100194 :param float progress_timeout:
195 :param float total_timeout:
196 :returns str: id of the execution environment
197 """
198
199 @abc.abstractmethod
200 async def install_configuration_sw(
201 self,
202 ee_id: str,
203 artifact_path: str,
204 db_dict: dict,
205 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400206 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100207 ):
208 """
209 Install the software inside the execution environment identified by ee_id
210
beierlmf52cb7c2020-04-21 16:36:35 -0400211 :param str ee_id: the id of the execution environment returned by
212 create_execution_environment or register_execution_environment
213 :param str artifact_path: where to locate the artifacts (parent folder) using
214 the self.fs
215 the final artifact path will be a combination of this artifact_path and
216 additional string from the config_dict (e.g. charm name)
quilesj29114342019-10-29 09:30:44 +0100217 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400218 It contains a dict with
219 {collection: <str>, filter: {}, path: <str>},
220 e.g. {collection: "nsrs", filter:
221 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100222 :param float progress_timeout:
223 :param float total_timeout:
224 """
225
226 @abc.abstractmethod
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200227 async def install_k8s_proxy_charm(
228 self,
229 charm_name: str,
230 namespace: str,
231 artifact_path: str,
232 db_dict: dict,
233 progress_timeout: float = None,
234 total_timeout: float = None,
235 config: dict = None,
236 ) -> str:
237 """
238 Install a k8s proxy charm
239
240 :param charm_name: Name of the charm being deployed
241 :param namespace: collection of all the uuids related to the charm.
242 :param str artifact_path: where to locate the artifacts (parent folder) using
243 the self.fs
244 the final artifact path will be a combination of this artifact_path and
245 additional string from the config_dict (e.g. charm name)
246 :param dict db_dict: where to write into database when the status changes.
247 It contains a dict with
248 {collection: <str>, filter: {}, path: <str>},
249 e.g. {collection: "nsrs", filter:
250 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
251 :param float progress_timeout:
252 :param float total_timeout:
253 :param config: Dictionary with additional configuration
254
255 :returns ee_id: execution environment id.
256 """
257
258 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100259 async def get_ee_ssh_public__key(
260 self,
261 ee_id: str,
262 db_dict: dict,
263 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400264 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100265 ) -> str:
266 """
beierlmf52cb7c2020-04-21 16:36:35 -0400267 Generate a priv/pub key pair in the execution environment and return the public
268 key
quilesj29114342019-10-29 09:30:44 +0100269
beierlmf52cb7c2020-04-21 16:36:35 -0400270 :param str ee_id: the id of the execution environment returned by
271 create_execution_environment or register_execution_environment
quilesj29114342019-10-29 09:30:44 +0100272 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400273 It contains a dict with
274 {collection: <str>, filter: {}, path: <str>},
275 e.g. {collection: "nsrs", filter:
276 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100277 :param float progress_timeout:
278 :param float total_timeout:
279 :returns: public key of the execution environment
beierlmf52cb7c2020-04-21 16:36:35 -0400280 For the case of juju proxy charm ssh-layered, it is the one
281 returned by 'get-ssh-public-key' primitive.
quilesj29114342019-10-29 09:30:44 +0100282 It raises a N2VC exception if fails
283 """
284
285 @abc.abstractmethod
286 async def add_relation(
beierlmf52cb7c2020-04-21 16:36:35 -0400287 self, ee_id_1: str, ee_id_2: str, endpoint_1: str, endpoint_2: str
quilesj29114342019-10-29 09:30:44 +0100288 ):
289 """
beierlmf52cb7c2020-04-21 16:36:35 -0400290 Add a relation between two Execution Environments (using their associated
291 endpoints).
quilesj29114342019-10-29 09:30:44 +0100292
293 :param str ee_id_1: The id of the first execution environment
294 :param str ee_id_2: The id of the second execution environment
295 :param str endpoint_1: The endpoint in the first execution environment
296 :param str endpoint_2: The endpoint in the second execution environment
297 """
298
299 # TODO
300 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400301 async def remove_relation(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100302 """ """
quilesj29114342019-10-29 09:30:44 +0100303
304 # TODO
305 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400306 async def deregister_execution_environments(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100307 """ """
quilesj29114342019-10-29 09:30:44 +0100308
309 @abc.abstractmethod
310 async def delete_namespace(
beierlmf52cb7c2020-04-21 16:36:35 -0400311 self, namespace: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100312 ):
313 """
314 Remove a network scenario and its execution environments
315 :param namespace: [<nsi-id>].<ns-id>
316 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400317 It contains a dict with
318 {collection: <str>, filter: {}, path: <str>},
319 e.g. {collection: "nsrs", filter:
320 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100321 :param float total_timeout:
322 """
323
324 @abc.abstractmethod
325 async def delete_execution_environment(
beierlmf52cb7c2020-04-21 16:36:35 -0400326 self, ee_id: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100327 ):
328 """
329 Delete an execution environment
330 :param str ee_id: id of the execution environment to delete
331 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400332 It contains a dict with
333 {collection: <str>, filter: {}, path: <str>},
334 e.g. {collection: "nsrs", filter:
335 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100336 :param float total_timeout:
337 """
338
339 @abc.abstractmethod
aticig8070c3c2022-04-18 00:31:42 +0300340 async def upgrade_charm(
341 self,
342 ee_id: str = None,
343 path: str = None,
344 charm_id: str = None,
345 charm_type: str = None,
346 timeout: float = None,
347 ) -> str:
348 """This method upgrade charms in VNFs
349
350 Args:
351 ee_id: Execution environment id
352 path: Local path to the charm
353 charm_id: charm-id
354 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
355 timeout: (Float) Timeout for the ns update operation
356
357 Returns:
358 The output of the update operation if status equals to "completed"
359 """
360
361 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100362 async def exec_primitive(
363 self,
364 ee_id: str,
365 primitive_name: str,
366 params_dict: dict,
367 db_dict: dict = None,
368 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400369 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100370 ) -> str:
371 """
372 Execute a primitive in the execution environment
373
beierlmf52cb7c2020-04-21 16:36:35 -0400374 :param str ee_id: the one returned by create_execution_environment or
375 register_execution_environment
376 :param str primitive_name: must be one defined in the software. There is one
377 called 'config', where, for the proxy case, the 'credentials' of VM are
378 provided
quilesj29114342019-10-29 09:30:44 +0100379 :param dict params_dict: parameters of the action
380 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400381 It contains a dict with
382 {collection: <str>, filter: {}, path: <str>},
383 e.g. {collection: "nsrs", filter:
384 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100385 :param float progress_timeout:
386 :param float total_timeout:
387 :returns str: primitive result, if ok. It raises exceptions in case of fail
388 """
389
390 async def disconnect(self):
391 """
392 Disconnect from VCA
393 """
394
395 """
beierlmf52cb7c2020-04-21 16:36:35 -0400396 ####################################################################################
397 ################################### P R I V A T E ##################################
398 ####################################################################################
quilesj29114342019-10-29 09:30:44 +0100399 """
400
401 def _get_namespace_components(self, namespace: str) -> (str, str, str, str, str):
402 """
403 Split namespace components
404
405 :param namespace: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
406 :return: nsi_id, ns_id, vnf_id, vdu_id, vdu_count
407 """
408
409 # check parameters
410 if namespace is None or len(namespace) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400411 raise N2VCBadArgumentsException(
412 "Argument namespace is mandatory", ["namespace"]
413 )
quilesj29114342019-10-29 09:30:44 +0100414
415 # split namespace components
beierlmf52cb7c2020-04-21 16:36:35 -0400416 parts = namespace.split(".")
quilesj29114342019-10-29 09:30:44 +0100417 nsi_id = None
418 ns_id = None
419 vnf_id = None
420 vdu_id = None
421 vdu_count = None
422 if len(parts) > 0 and len(parts[0]) > 0:
423 nsi_id = parts[0]
424 if len(parts) > 1 and len(parts[1]) > 0:
425 ns_id = parts[1]
426 if len(parts) > 2 and len(parts[2]) > 0:
427 vnf_id = parts[2]
428 if len(parts) > 3 and len(parts[3]) > 0:
429 vdu_id = parts[3]
beierlmf52cb7c2020-04-21 16:36:35 -0400430 vdu_parts = parts[3].split("-")
quilesj29114342019-10-29 09:30:44 +0100431 if len(vdu_parts) > 1:
432 vdu_id = vdu_parts[0]
433 vdu_count = vdu_parts[1]
434
435 return nsi_id, ns_id, vnf_id, vdu_id, vdu_count
436
437 async def write_app_status_to_db(
beierlmf52cb7c2020-04-21 16:36:35 -0400438 self,
439 db_dict: dict,
440 status: N2VCDeploymentStatus,
441 detailed_status: str,
442 vca_status: str,
443 entity_type: str,
David Garciaeb8943a2021-04-12 12:07:37 +0200444 vca_id: str = None,
quilesj29114342019-10-29 09:30:44 +0100445 ):
David Garciaeb8943a2021-04-12 12:07:37 +0200446 """
447 Write application status to database
448
449 :param: db_dict: DB dictionary
450 :param: status: Status of the application
451 :param: detailed_status: Detailed status
452 :param: vca_status: VCA status
453 :param: entity_type: Entity type ("application", "machine, and "action")
454 :param: vca_id: Id of the VCA. If None, the default VCA will be used.
455 """
quilesj29114342019-10-29 09:30:44 +0100456 if not db_dict:
beierlmf52cb7c2020-04-21 16:36:35 -0400457 self.log.debug("No db_dict => No database write")
quilesj29114342019-10-29 09:30:44 +0100458 return
459
beierlmf52cb7c2020-04-21 16:36:35 -0400460 # self.log.debug('status={} / detailed-status={} / VCA-status={}/entity_type={}'
461 # .format(str(status.value), detailed_status, vca_status, entity_type))
quilesj29114342019-10-29 09:30:44 +0100462
463 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400464 the_table = db_dict["collection"]
465 the_filter = db_dict["filter"]
466 the_path = db_dict["path"]
467 if not the_path[-1] == ".":
468 the_path = the_path + "."
quilesj29114342019-10-29 09:30:44 +0100469 update_dict = {
beierlmf52cb7c2020-04-21 16:36:35 -0400470 the_path + "status": str(status.value),
471 the_path + "detailed-status": detailed_status,
472 the_path + "VCA-status": vca_status,
473 the_path + "entity-type": entity_type,
474 the_path + "status-time": str(time.time()),
quilesj29114342019-10-29 09:30:44 +0100475 }
476
477 self.db.set_one(
478 table=the_table,
479 q_filter=the_filter,
480 update_dict=update_dict,
beierlmf52cb7c2020-04-21 16:36:35 -0400481 fail_on_empty=True,
quilesj29114342019-10-29 09:30:44 +0100482 )
483
484 # database callback
485 if self.on_update_db:
486 if asyncio.iscoroutinefunction(self.on_update_db):
beierlmf52cb7c2020-04-21 16:36:35 -0400487 await self.on_update_db(
David Garciaeb8943a2021-04-12 12:07:37 +0200488 the_table, the_filter, the_path, update_dict, vca_id=vca_id
beierlmf52cb7c2020-04-21 16:36:35 -0400489 )
quilesj29114342019-10-29 09:30:44 +0100490 else:
garciadeblas82b591c2021-03-24 09:22:13 +0100491 self.on_update_db(
492 the_table, the_filter, the_path, update_dict, vca_id=vca_id
493 )
quilesj29114342019-10-29 09:30:44 +0100494
495 except DbException as e:
496 if e.http_code == HTTPStatus.NOT_FOUND:
beierlmf52cb7c2020-04-21 16:36:35 -0400497 self.log.error(
498 "NOT_FOUND error: Exception writing status to database: {}".format(
499 e
500 )
501 )
quilesj29114342019-10-29 09:30:44 +0100502 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400503 self.log.info("Exception writing status to database: {}".format(e))
quilesj29114342019-10-29 09:30:44 +0100504
David Garciac38a6962020-09-16 13:31:33 +0200505 def osm_status(self, entity_type: str, status: str) -> N2VCDeploymentStatus:
David Garcia4fee80e2020-05-13 12:18:38 +0200506 if status not in JujuStatusToOSM[entity_type]:
David Garcia2f66c4d2020-06-19 11:40:18 +0200507 self.log.warning("Status {} not found in JujuStatusToOSM.".format(status))
David Garcia4fee80e2020-05-13 12:18:38 +0200508 return N2VCDeploymentStatus.UNKNOWN
509 return JujuStatusToOSM[entity_type][status]
quilesj29114342019-10-29 09:30:44 +0100510
David Garcia4fee80e2020-05-13 12:18:38 +0200511
quilesj776ab392019-12-12 16:10:54 +0000512def obj_to_yaml(obj: object) -> str:
513 # dump to yaml
514 dump_text = yaml.dump(obj, default_flow_style=False, indent=2)
515 # split lines
516 lines = dump_text.splitlines()
517 # remove !!python/object tags
beierlmf52cb7c2020-04-21 16:36:35 -0400518 yaml_text = ""
quilesj776ab392019-12-12 16:10:54 +0000519 for line in lines:
beierlmf52cb7c2020-04-21 16:36:35 -0400520 index = line.find("!!python/object")
quilesj776ab392019-12-12 16:10:54 +0000521 if index >= 0:
522 line = line[:index]
beierlmf52cb7c2020-04-21 16:36:35 -0400523 yaml_text += line + "\n"
quilesj776ab392019-12-12 16:10:54 +0000524 return yaml_text
525
526
527def obj_to_dict(obj: object) -> dict:
528 # convert obj to yaml
529 yaml_text = obj_to_yaml(obj)
530 # parse to dict
garciadeblas2e69dc62022-07-05 18:05:04 +0200531 return yaml.load(yaml_text, Loader=yaml.SafeLoader)