blob: c77d779a559ca1a525a69ca14a6ba58cb906a265 [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,
57 loop: object,
beierlmf52cb7c2020-04-21 16:36:35 -040058 on_update_db=None,
David Garcia6331b042021-04-26 15:36:24 +020059 **kwargs,
quilesj29114342019-10-29 09:30:44 +010060 ):
61 """Initialize N2VC abstract connector. It defines de API for VCA connectors
62
63 :param object db: Mongo object managing the MongoDB (repo common DbBase)
beierlmf52cb7c2020-04-21 16:36:35 -040064 :param object fs: FileSystem object managing the package artifacts (repo common
65 FsBase)
quilesj29114342019-10-29 09:30:44 +010066 :param object log: the logging object to log to
67 :param object loop: the loop to use for asyncio (default current thread loop)
beierlmf52cb7c2020-04-21 16:36:35 -040068 :param on_update_db: callback called when n2vc connector updates database.
69 Received arguments:
quilesj29114342019-10-29 09:30:44 +010070 table: e.g. "nsrs"
71 filter: e.g. {_id: <nsd-id> }
72 path: e.g. "_admin.deployed.VCA.3."
73 updated_data: e.g. , "{ _admin.deployed.VCA.3.status: 'xxx', etc }"
74 """
75
76 # parent class
beierlmf52cb7c2020-04-21 16:36:35 -040077 Loggable.__init__(self, log=log, log_to_console=True, prefix="\nN2VC")
quilesj29114342019-10-29 09:30:44 +010078
79 # check arguments
80 if db is None:
beierlmf52cb7c2020-04-21 16:36:35 -040081 raise N2VCBadArgumentsException("Argument db is mandatory", ["db"])
quilesj29114342019-10-29 09:30:44 +010082 if fs is None:
beierlmf52cb7c2020-04-21 16:36:35 -040083 raise N2VCBadArgumentsException("Argument fs is mandatory", ["fs"])
quilesj29114342019-10-29 09:30:44 +010084
quilesj29114342019-10-29 09:30:44 +010085 # store arguments into self
86 self.db = db
87 self.fs = fs
88 self.loop = loop or asyncio.get_event_loop()
quilesj29114342019-10-29 09:30:44 +010089 self.on_update_db = on_update_db
90
91 # generate private/public key-pair
quilesjac4e0de2019-11-27 16:12:02 +000092 self.private_key_path = None
93 self.public_key_path = None
quilesj29114342019-10-29 09:30:44 +010094
95 @abc.abstractmethod
quilesj776ab392019-12-12 16:10:54 +000096 async def get_status(self, namespace: str, yaml_format: bool = True):
quilesj29114342019-10-29 09:30:44 +010097 """Get namespace status
98
99 :param namespace: we obtain ns from namespace
quilesj776ab392019-12-12 16:10:54 +0000100 :param yaml_format: returns a yaml string
quilesj29114342019-10-29 09:30:44 +0100101 """
102
103 # TODO: review which public key
quilesjac4e0de2019-11-27 16:12:02 +0000104 def get_public_key(self) -> str:
quilesj29114342019-10-29 09:30:44 +0100105 """Get the VCA ssh-public-key
106
beierlmf52cb7c2020-04-21 16:36:35 -0400107 Returns the SSH public key from local mahine, to be injected into virtual
108 machines to be managed by the VCA.
quilesj29114342019-10-29 09:30:44 +0100109 First run, a ssh keypair will be created.
110 The public key is injected into a VM so that we can provision the
beierlmf52cb7c2020-04-21 16:36:35 -0400111 machine with Juju, after which Juju will communicate with the VM
quilesj29114342019-10-29 09:30:44 +0100112 directly via the juju agent.
113 """
114
quilesj29114342019-10-29 09:30:44 +0100115 # Find the path where we expect our key lives (~/.ssh)
beierlmf52cb7c2020-04-21 16:36:35 -0400116 homedir = os.environ.get("HOME")
quilesj776ab392019-12-12 16:10:54 +0000117 if not homedir:
Dominik Fleischmann0c478252020-07-15 14:44:45 +0200118 self.log.warning("No HOME environment variable, using /tmp")
beierlmf52cb7c2020-04-21 16:36:35 -0400119 homedir = "/tmp"
quilesj29114342019-10-29 09:30:44 +0100120 sshdir = "{}/.ssh".format(homedir)
121 if not os.path.exists(sshdir):
122 os.mkdir(sshdir)
123
124 self.private_key_path = "{}/id_n2vc_rsa".format(sshdir)
125 self.public_key_path = "{}.pub".format(self.private_key_path)
126
127 # If we don't have a key generated, then we have to generate it using ssh-keygen
128 if not os.path.exists(self.private_key_path):
129 cmd = "ssh-keygen -t {} -b {} -N '' -f {}".format(
beierlmf52cb7c2020-04-21 16:36:35 -0400130 "rsa", "4096", self.private_key_path
quilesj29114342019-10-29 09:30:44 +0100131 )
132 # run command with arguments
133 subprocess.check_output(shlex.split(cmd))
134
135 # Read the public key. Only one public key (one line) in the file
136 with open(self.public_key_path, "r") as file:
137 public_key = file.readline()
138
139 return public_key
140
141 @abc.abstractmethod
142 async def create_execution_environment(
143 self,
144 namespace: str,
145 db_dict: dict,
146 reuse_ee_id: str = None,
147 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400148 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100149 ) -> (str, dict):
beierlmf52cb7c2020-04-21 16:36:35 -0400150 """Create an Execution Environment. Returns when it is created or raises an
151 exception on failing
quilesj29114342019-10-29 09:30:44 +0100152
153 :param str namespace: Contains a dot separate string.
154 LCM will use: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
155 :param dict db_dict: where to write to database when the status changes.
156 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400157 e.g. {collection: "nsrs", filter: {_id: <nsd-id>, path:
158 "_admin.deployed.VCA.3"}
159 :param str reuse_ee_id: ee id from an older execution. It allows us to reuse an
160 older environment
quilesj29114342019-10-29 09:30:44 +0100161 :param float progress_timeout:
162 :param float total_timeout:
163 :returns str, dict: id of the new execution environment and credentials for it
beierlmf52cb7c2020-04-21 16:36:35 -0400164 (credentials can contains hostname, username, etc depending on
165 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100166 """
167
168 @abc.abstractmethod
169 async def register_execution_environment(
170 self,
171 namespace: str,
172 credentials: dict,
173 db_dict: dict,
174 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400175 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100176 ) -> str:
177 """
178 Register an existing execution environment at the VCA
179
180 :param str namespace: same as create_execution_environment method
beierlmf52cb7c2020-04-21 16:36:35 -0400181 :param dict credentials: credentials to access the existing execution
182 environment
183 (it can contains hostname, username, path to private key, etc depending on
184 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100185 :param dict db_dict: where to write to database when the status changes.
186 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400187 e.g. {collection: "nsrs", filter:
188 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100189 :param float progress_timeout:
190 :param float total_timeout:
191 :returns str: id of the execution environment
192 """
193
194 @abc.abstractmethod
195 async def install_configuration_sw(
196 self,
197 ee_id: str,
198 artifact_path: str,
199 db_dict: dict,
200 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400201 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100202 ):
203 """
204 Install the software inside the execution environment identified by ee_id
205
beierlmf52cb7c2020-04-21 16:36:35 -0400206 :param str ee_id: the id of the execution environment returned by
207 create_execution_environment or register_execution_environment
208 :param str artifact_path: where to locate the artifacts (parent folder) using
209 the self.fs
210 the final artifact path will be a combination of this artifact_path and
211 additional string from the config_dict (e.g. charm name)
quilesj29114342019-10-29 09:30:44 +0100212 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400213 It contains a dict with
214 {collection: <str>, filter: {}, path: <str>},
215 e.g. {collection: "nsrs", filter:
216 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100217 :param float progress_timeout:
218 :param float total_timeout:
219 """
220
221 @abc.abstractmethod
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200222 async def install_k8s_proxy_charm(
223 self,
224 charm_name: str,
225 namespace: str,
226 artifact_path: str,
227 db_dict: dict,
228 progress_timeout: float = None,
229 total_timeout: float = None,
230 config: dict = None,
231 ) -> str:
232 """
233 Install a k8s proxy charm
234
235 :param charm_name: Name of the charm being deployed
236 :param namespace: collection of all the uuids related to the charm.
237 :param str artifact_path: where to locate the artifacts (parent folder) using
238 the self.fs
239 the final artifact path will be a combination of this artifact_path and
240 additional string from the config_dict (e.g. charm name)
241 :param dict db_dict: where to write into database when the status changes.
242 It contains a dict with
243 {collection: <str>, filter: {}, path: <str>},
244 e.g. {collection: "nsrs", filter:
245 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
246 :param float progress_timeout:
247 :param float total_timeout:
248 :param config: Dictionary with additional configuration
249
250 :returns ee_id: execution environment id.
251 """
252
253 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100254 async def get_ee_ssh_public__key(
255 self,
256 ee_id: str,
257 db_dict: dict,
258 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400259 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100260 ) -> str:
261 """
beierlmf52cb7c2020-04-21 16:36:35 -0400262 Generate a priv/pub key pair in the execution environment and return the public
263 key
quilesj29114342019-10-29 09:30:44 +0100264
beierlmf52cb7c2020-04-21 16:36:35 -0400265 :param str ee_id: the id of the execution environment returned by
266 create_execution_environment or register_execution_environment
quilesj29114342019-10-29 09:30:44 +0100267 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400268 It contains a dict with
269 {collection: <str>, filter: {}, path: <str>},
270 e.g. {collection: "nsrs", filter:
271 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100272 :param float progress_timeout:
273 :param float total_timeout:
274 :returns: public key of the execution environment
beierlmf52cb7c2020-04-21 16:36:35 -0400275 For the case of juju proxy charm ssh-layered, it is the one
276 returned by 'get-ssh-public-key' primitive.
quilesj29114342019-10-29 09:30:44 +0100277 It raises a N2VC exception if fails
278 """
279
280 @abc.abstractmethod
281 async def add_relation(
beierlmf52cb7c2020-04-21 16:36:35 -0400282 self, ee_id_1: str, ee_id_2: str, endpoint_1: str, endpoint_2: str
quilesj29114342019-10-29 09:30:44 +0100283 ):
284 """
beierlmf52cb7c2020-04-21 16:36:35 -0400285 Add a relation between two Execution Environments (using their associated
286 endpoints).
quilesj29114342019-10-29 09:30:44 +0100287
288 :param str ee_id_1: The id of the first execution environment
289 :param str ee_id_2: The id of the second execution environment
290 :param str endpoint_1: The endpoint in the first execution environment
291 :param str endpoint_2: The endpoint in the second execution environment
292 """
293
294 # TODO
295 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400296 async def remove_relation(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100297 """ """
quilesj29114342019-10-29 09:30:44 +0100298
299 # TODO
300 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400301 async def deregister_execution_environments(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100302 """ """
quilesj29114342019-10-29 09:30:44 +0100303
304 @abc.abstractmethod
305 async def delete_namespace(
beierlmf52cb7c2020-04-21 16:36:35 -0400306 self, namespace: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100307 ):
308 """
309 Remove a network scenario and its execution environments
310 :param namespace: [<nsi-id>].<ns-id>
311 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400312 It contains a dict with
313 {collection: <str>, filter: {}, path: <str>},
314 e.g. {collection: "nsrs", filter:
315 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100316 :param float total_timeout:
317 """
318
319 @abc.abstractmethod
320 async def delete_execution_environment(
beierlmf52cb7c2020-04-21 16:36:35 -0400321 self, ee_id: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100322 ):
323 """
324 Delete an execution environment
325 :param str ee_id: id of the execution environment to delete
326 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400327 It contains a dict with
328 {collection: <str>, filter: {}, path: <str>},
329 e.g. {collection: "nsrs", filter:
330 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100331 :param float total_timeout:
332 """
333
334 @abc.abstractmethod
aticig8070c3c2022-04-18 00:31:42 +0300335 async def upgrade_charm(
336 self,
337 ee_id: str = None,
338 path: str = None,
339 charm_id: str = None,
340 charm_type: str = None,
341 timeout: float = None,
342 ) -> str:
343 """This method upgrade charms in VNFs
344
345 Args:
346 ee_id: Execution environment id
347 path: Local path to the charm
348 charm_id: charm-id
349 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
350 timeout: (Float) Timeout for the ns update operation
351
352 Returns:
353 The output of the update operation if status equals to "completed"
354 """
355
356 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100357 async def exec_primitive(
358 self,
359 ee_id: str,
360 primitive_name: str,
361 params_dict: dict,
362 db_dict: dict = None,
363 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400364 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100365 ) -> str:
366 """
367 Execute a primitive in the execution environment
368
beierlmf52cb7c2020-04-21 16:36:35 -0400369 :param str ee_id: the one returned by create_execution_environment or
370 register_execution_environment
371 :param str primitive_name: must be one defined in the software. There is one
372 called 'config', where, for the proxy case, the 'credentials' of VM are
373 provided
quilesj29114342019-10-29 09:30:44 +0100374 :param dict params_dict: parameters of the action
375 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400376 It contains a dict with
377 {collection: <str>, filter: {}, path: <str>},
378 e.g. {collection: "nsrs", filter:
379 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100380 :param float progress_timeout:
381 :param float total_timeout:
382 :returns str: primitive result, if ok. It raises exceptions in case of fail
383 """
384
385 async def disconnect(self):
386 """
387 Disconnect from VCA
388 """
389
390 """
beierlmf52cb7c2020-04-21 16:36:35 -0400391 ####################################################################################
392 ################################### P R I V A T E ##################################
393 ####################################################################################
quilesj29114342019-10-29 09:30:44 +0100394 """
395
396 def _get_namespace_components(self, namespace: str) -> (str, str, str, str, str):
397 """
398 Split namespace components
399
400 :param namespace: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
401 :return: nsi_id, ns_id, vnf_id, vdu_id, vdu_count
402 """
403
404 # check parameters
405 if namespace is None or len(namespace) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400406 raise N2VCBadArgumentsException(
407 "Argument namespace is mandatory", ["namespace"]
408 )
quilesj29114342019-10-29 09:30:44 +0100409
410 # split namespace components
beierlmf52cb7c2020-04-21 16:36:35 -0400411 parts = namespace.split(".")
quilesj29114342019-10-29 09:30:44 +0100412 nsi_id = None
413 ns_id = None
414 vnf_id = None
415 vdu_id = None
416 vdu_count = None
417 if len(parts) > 0 and len(parts[0]) > 0:
418 nsi_id = parts[0]
419 if len(parts) > 1 and len(parts[1]) > 0:
420 ns_id = parts[1]
421 if len(parts) > 2 and len(parts[2]) > 0:
422 vnf_id = parts[2]
423 if len(parts) > 3 and len(parts[3]) > 0:
424 vdu_id = parts[3]
beierlmf52cb7c2020-04-21 16:36:35 -0400425 vdu_parts = parts[3].split("-")
quilesj29114342019-10-29 09:30:44 +0100426 if len(vdu_parts) > 1:
427 vdu_id = vdu_parts[0]
428 vdu_count = vdu_parts[1]
429
430 return nsi_id, ns_id, vnf_id, vdu_id, vdu_count
431
432 async def write_app_status_to_db(
beierlmf52cb7c2020-04-21 16:36:35 -0400433 self,
434 db_dict: dict,
435 status: N2VCDeploymentStatus,
436 detailed_status: str,
437 vca_status: str,
438 entity_type: str,
David Garciaeb8943a2021-04-12 12:07:37 +0200439 vca_id: str = None,
quilesj29114342019-10-29 09:30:44 +0100440 ):
David Garciaeb8943a2021-04-12 12:07:37 +0200441 """
442 Write application status to database
443
444 :param: db_dict: DB dictionary
445 :param: status: Status of the application
446 :param: detailed_status: Detailed status
447 :param: vca_status: VCA status
448 :param: entity_type: Entity type ("application", "machine, and "action")
449 :param: vca_id: Id of the VCA. If None, the default VCA will be used.
450 """
quilesj29114342019-10-29 09:30:44 +0100451 if not db_dict:
beierlmf52cb7c2020-04-21 16:36:35 -0400452 self.log.debug("No db_dict => No database write")
quilesj29114342019-10-29 09:30:44 +0100453 return
454
beierlmf52cb7c2020-04-21 16:36:35 -0400455 # self.log.debug('status={} / detailed-status={} / VCA-status={}/entity_type={}'
456 # .format(str(status.value), detailed_status, vca_status, entity_type))
quilesj29114342019-10-29 09:30:44 +0100457
458 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400459 the_table = db_dict["collection"]
460 the_filter = db_dict["filter"]
461 the_path = db_dict["path"]
462 if not the_path[-1] == ".":
463 the_path = the_path + "."
quilesj29114342019-10-29 09:30:44 +0100464 update_dict = {
beierlmf52cb7c2020-04-21 16:36:35 -0400465 the_path + "status": str(status.value),
466 the_path + "detailed-status": detailed_status,
467 the_path + "VCA-status": vca_status,
468 the_path + "entity-type": entity_type,
469 the_path + "status-time": str(time.time()),
quilesj29114342019-10-29 09:30:44 +0100470 }
471
472 self.db.set_one(
473 table=the_table,
474 q_filter=the_filter,
475 update_dict=update_dict,
beierlmf52cb7c2020-04-21 16:36:35 -0400476 fail_on_empty=True,
quilesj29114342019-10-29 09:30:44 +0100477 )
478
479 # database callback
480 if self.on_update_db:
481 if asyncio.iscoroutinefunction(self.on_update_db):
beierlmf52cb7c2020-04-21 16:36:35 -0400482 await self.on_update_db(
David Garciaeb8943a2021-04-12 12:07:37 +0200483 the_table, the_filter, the_path, update_dict, vca_id=vca_id
beierlmf52cb7c2020-04-21 16:36:35 -0400484 )
quilesj29114342019-10-29 09:30:44 +0100485 else:
garciadeblas82b591c2021-03-24 09:22:13 +0100486 self.on_update_db(
487 the_table, the_filter, the_path, update_dict, vca_id=vca_id
488 )
quilesj29114342019-10-29 09:30:44 +0100489
490 except DbException as e:
491 if e.http_code == HTTPStatus.NOT_FOUND:
beierlmf52cb7c2020-04-21 16:36:35 -0400492 self.log.error(
493 "NOT_FOUND error: Exception writing status to database: {}".format(
494 e
495 )
496 )
quilesj29114342019-10-29 09:30:44 +0100497 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400498 self.log.info("Exception writing status to database: {}".format(e))
quilesj29114342019-10-29 09:30:44 +0100499
David Garciac38a6962020-09-16 13:31:33 +0200500 def osm_status(self, entity_type: str, status: str) -> N2VCDeploymentStatus:
David Garcia4fee80e2020-05-13 12:18:38 +0200501 if status not in JujuStatusToOSM[entity_type]:
David Garcia2f66c4d2020-06-19 11:40:18 +0200502 self.log.warning("Status {} not found in JujuStatusToOSM.".format(status))
David Garcia4fee80e2020-05-13 12:18:38 +0200503 return N2VCDeploymentStatus.UNKNOWN
504 return JujuStatusToOSM[entity_type][status]
quilesj29114342019-10-29 09:30:44 +0100505
David Garcia4fee80e2020-05-13 12:18:38 +0200506
quilesj776ab392019-12-12 16:10:54 +0000507def obj_to_yaml(obj: object) -> str:
508 # dump to yaml
509 dump_text = yaml.dump(obj, default_flow_style=False, indent=2)
510 # split lines
511 lines = dump_text.splitlines()
512 # remove !!python/object tags
beierlmf52cb7c2020-04-21 16:36:35 -0400513 yaml_text = ""
quilesj776ab392019-12-12 16:10:54 +0000514 for line in lines:
beierlmf52cb7c2020-04-21 16:36:35 -0400515 index = line.find("!!python/object")
quilesj776ab392019-12-12 16:10:54 +0000516 if index >= 0:
517 line = line[:index]
beierlmf52cb7c2020-04-21 16:36:35 -0400518 yaml_text += line + "\n"
quilesj776ab392019-12-12 16:10:54 +0000519 return yaml_text
520
521
522def obj_to_dict(obj: object) -> dict:
523 # convert obj to yaml
524 yaml_text = obj_to_yaml(obj)
525 # parse to dict
526 return yaml.load(yaml_text, Loader=yaml.Loader)