blob: 4fa7e36ef7dff2281c5489205115f167c6db99ce [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)
118 if not os.path.exists(sshdir):
119 os.mkdir(sshdir)
120
121 self.private_key_path = "{}/id_n2vc_rsa".format(sshdir)
122 self.public_key_path = "{}.pub".format(self.private_key_path)
123
124 # If we don't have a key generated, then we have to generate it using ssh-keygen
125 if not os.path.exists(self.private_key_path):
126 cmd = "ssh-keygen -t {} -b {} -N '' -f {}".format(
beierlmf52cb7c2020-04-21 16:36:35 -0400127 "rsa", "4096", self.private_key_path
quilesj29114342019-10-29 09:30:44 +0100128 )
129 # run command with arguments
130 subprocess.check_output(shlex.split(cmd))
131
132 # Read the public key. Only one public key (one line) in the file
133 with open(self.public_key_path, "r") as file:
134 public_key = file.readline()
135
136 return public_key
137
138 @abc.abstractmethod
139 async def create_execution_environment(
140 self,
141 namespace: str,
142 db_dict: dict,
143 reuse_ee_id: str = None,
144 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400145 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100146 ) -> (str, dict):
beierlmf52cb7c2020-04-21 16:36:35 -0400147 """Create an Execution Environment. Returns when it is created or raises an
148 exception on failing
quilesj29114342019-10-29 09:30:44 +0100149
150 :param str namespace: Contains a dot separate string.
151 LCM will use: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
152 :param dict db_dict: where to write to database when the status changes.
153 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400154 e.g. {collection: "nsrs", filter: {_id: <nsd-id>, path:
155 "_admin.deployed.VCA.3"}
156 :param str reuse_ee_id: ee id from an older execution. It allows us to reuse an
157 older environment
quilesj29114342019-10-29 09:30:44 +0100158 :param float progress_timeout:
159 :param float total_timeout:
160 :returns str, dict: id of the new execution environment and credentials for it
beierlmf52cb7c2020-04-21 16:36:35 -0400161 (credentials can contains hostname, username, etc depending on
162 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100163 """
164
165 @abc.abstractmethod
166 async def register_execution_environment(
167 self,
168 namespace: str,
169 credentials: dict,
170 db_dict: dict,
171 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400172 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100173 ) -> str:
174 """
175 Register an existing execution environment at the VCA
176
177 :param str namespace: same as create_execution_environment method
beierlmf52cb7c2020-04-21 16:36:35 -0400178 :param dict credentials: credentials to access the existing execution
179 environment
180 (it can contains hostname, username, path to private key, etc depending on
181 underlying cloud)
quilesj29114342019-10-29 09:30:44 +0100182 :param dict db_dict: where to write to database when the status changes.
183 It contains a dictionary with {collection: str, filter: {}, path: str},
beierlmf52cb7c2020-04-21 16:36:35 -0400184 e.g. {collection: "nsrs", filter:
185 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100186 :param float progress_timeout:
187 :param float total_timeout:
188 :returns str: id of the execution environment
189 """
190
191 @abc.abstractmethod
192 async def install_configuration_sw(
193 self,
194 ee_id: str,
195 artifact_path: str,
196 db_dict: dict,
197 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400198 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100199 ):
200 """
201 Install the software inside the execution environment identified by ee_id
202
beierlmf52cb7c2020-04-21 16:36:35 -0400203 :param str ee_id: the id of the execution environment returned by
204 create_execution_environment or register_execution_environment
205 :param str artifact_path: where to locate the artifacts (parent folder) using
206 the self.fs
207 the final artifact path will be a combination of this artifact_path and
208 additional string from the config_dict (e.g. charm name)
quilesj29114342019-10-29 09:30:44 +0100209 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400210 It contains a dict with
211 {collection: <str>, filter: {}, path: <str>},
212 e.g. {collection: "nsrs", filter:
213 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100214 :param float progress_timeout:
215 :param float total_timeout:
216 """
217
218 @abc.abstractmethod
Dominik Fleischmannb9513342020-06-09 11:57:14 +0200219 async def install_k8s_proxy_charm(
220 self,
221 charm_name: str,
222 namespace: str,
223 artifact_path: str,
224 db_dict: dict,
225 progress_timeout: float = None,
226 total_timeout: float = None,
227 config: dict = None,
228 ) -> str:
229 """
230 Install a k8s proxy charm
231
232 :param charm_name: Name of the charm being deployed
233 :param namespace: collection of all the uuids related to the charm.
234 :param str artifact_path: where to locate the artifacts (parent folder) using
235 the self.fs
236 the final artifact path will be a combination of this artifact_path and
237 additional string from the config_dict (e.g. charm name)
238 :param dict db_dict: where to write into database when the status changes.
239 It contains a dict with
240 {collection: <str>, filter: {}, path: <str>},
241 e.g. {collection: "nsrs", filter:
242 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
243 :param float progress_timeout:
244 :param float total_timeout:
245 :param config: Dictionary with additional configuration
246
247 :returns ee_id: execution environment id.
248 """
249
250 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100251 async def get_ee_ssh_public__key(
252 self,
253 ee_id: str,
254 db_dict: dict,
255 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400256 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100257 ) -> str:
258 """
beierlmf52cb7c2020-04-21 16:36:35 -0400259 Generate a priv/pub key pair in the execution environment and return the public
260 key
quilesj29114342019-10-29 09:30:44 +0100261
beierlmf52cb7c2020-04-21 16:36:35 -0400262 :param str ee_id: the id of the execution environment returned by
263 create_execution_environment or register_execution_environment
quilesj29114342019-10-29 09:30:44 +0100264 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400265 It contains a dict with
266 {collection: <str>, filter: {}, path: <str>},
267 e.g. {collection: "nsrs", filter:
268 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100269 :param float progress_timeout:
270 :param float total_timeout:
271 :returns: public key of the execution environment
beierlmf52cb7c2020-04-21 16:36:35 -0400272 For the case of juju proxy charm ssh-layered, it is the one
273 returned by 'get-ssh-public-key' primitive.
quilesj29114342019-10-29 09:30:44 +0100274 It raises a N2VC exception if fails
275 """
276
277 @abc.abstractmethod
278 async def add_relation(
beierlmf52cb7c2020-04-21 16:36:35 -0400279 self, ee_id_1: str, ee_id_2: str, endpoint_1: str, endpoint_2: str
quilesj29114342019-10-29 09:30:44 +0100280 ):
281 """
beierlmf52cb7c2020-04-21 16:36:35 -0400282 Add a relation between two Execution Environments (using their associated
283 endpoints).
quilesj29114342019-10-29 09:30:44 +0100284
285 :param str ee_id_1: The id of the first execution environment
286 :param str ee_id_2: The id of the second execution environment
287 :param str endpoint_1: The endpoint in the first execution environment
288 :param str endpoint_2: The endpoint in the second execution environment
289 """
290
291 # TODO
292 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400293 async def remove_relation(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100294 """ """
quilesj29114342019-10-29 09:30:44 +0100295
296 # TODO
297 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400298 async def deregister_execution_environments(self):
garciadeblas82b591c2021-03-24 09:22:13 +0100299 """ """
quilesj29114342019-10-29 09:30:44 +0100300
301 @abc.abstractmethod
302 async def delete_namespace(
beierlmf52cb7c2020-04-21 16:36:35 -0400303 self, namespace: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100304 ):
305 """
306 Remove a network scenario and its execution environments
307 :param namespace: [<nsi-id>].<ns-id>
308 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400309 It contains a dict with
310 {collection: <str>, filter: {}, path: <str>},
311 e.g. {collection: "nsrs", filter:
312 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100313 :param float total_timeout:
314 """
315
316 @abc.abstractmethod
317 async def delete_execution_environment(
beierlmf52cb7c2020-04-21 16:36:35 -0400318 self, ee_id: str, db_dict: dict = None, total_timeout: float = None
quilesj29114342019-10-29 09:30:44 +0100319 ):
320 """
321 Delete an execution environment
322 :param str ee_id: id of the execution environment to delete
323 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400324 It contains a dict with
325 {collection: <str>, filter: {}, path: <str>},
326 e.g. {collection: "nsrs", filter:
327 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100328 :param float total_timeout:
329 """
330
331 @abc.abstractmethod
aticig8070c3c2022-04-18 00:31:42 +0300332 async def upgrade_charm(
333 self,
334 ee_id: str = None,
335 path: str = None,
336 charm_id: str = None,
337 charm_type: str = None,
338 timeout: float = None,
339 ) -> str:
340 """This method upgrade charms in VNFs
341
342 Args:
343 ee_id: Execution environment id
344 path: Local path to the charm
345 charm_id: charm-id
346 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
347 timeout: (Float) Timeout for the ns update operation
348
349 Returns:
350 The output of the update operation if status equals to "completed"
351 """
352
353 @abc.abstractmethod
quilesj29114342019-10-29 09:30:44 +0100354 async def exec_primitive(
355 self,
356 ee_id: str,
357 primitive_name: str,
358 params_dict: dict,
359 db_dict: dict = None,
360 progress_timeout: float = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400361 total_timeout: float = None,
quilesj29114342019-10-29 09:30:44 +0100362 ) -> str:
363 """
364 Execute a primitive in the execution environment
365
beierlmf52cb7c2020-04-21 16:36:35 -0400366 :param str ee_id: the one returned by create_execution_environment or
367 register_execution_environment
368 :param str primitive_name: must be one defined in the software. There is one
369 called 'config', where, for the proxy case, the 'credentials' of VM are
370 provided
quilesj29114342019-10-29 09:30:44 +0100371 :param dict params_dict: parameters of the action
372 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400373 It contains a dict with
374 {collection: <str>, filter: {}, path: <str>},
375 e.g. {collection: "nsrs", filter:
376 {_id: <nsd-id>, path: "_admin.deployed.VCA.3"}
quilesj29114342019-10-29 09:30:44 +0100377 :param float progress_timeout:
378 :param float total_timeout:
379 :returns str: primitive result, if ok. It raises exceptions in case of fail
380 """
381
382 async def disconnect(self):
383 """
384 Disconnect from VCA
385 """
386
387 """
beierlmf52cb7c2020-04-21 16:36:35 -0400388 ####################################################################################
389 ################################### P R I V A T E ##################################
390 ####################################################################################
quilesj29114342019-10-29 09:30:44 +0100391 """
392
393 def _get_namespace_components(self, namespace: str) -> (str, str, str, str, str):
394 """
395 Split namespace components
396
397 :param namespace: [<nsi-id>].<ns-id>.<vnf-id>.<vdu-id>[-<count>]
398 :return: nsi_id, ns_id, vnf_id, vdu_id, vdu_count
399 """
400
401 # check parameters
402 if namespace is None or len(namespace) == 0:
beierlmf52cb7c2020-04-21 16:36:35 -0400403 raise N2VCBadArgumentsException(
404 "Argument namespace is mandatory", ["namespace"]
405 )
quilesj29114342019-10-29 09:30:44 +0100406
407 # split namespace components
beierlmf52cb7c2020-04-21 16:36:35 -0400408 parts = namespace.split(".")
quilesj29114342019-10-29 09:30:44 +0100409 nsi_id = None
410 ns_id = None
411 vnf_id = None
412 vdu_id = None
413 vdu_count = None
414 if len(parts) > 0 and len(parts[0]) > 0:
415 nsi_id = parts[0]
416 if len(parts) > 1 and len(parts[1]) > 0:
417 ns_id = parts[1]
418 if len(parts) > 2 and len(parts[2]) > 0:
419 vnf_id = parts[2]
420 if len(parts) > 3 and len(parts[3]) > 0:
421 vdu_id = parts[3]
beierlmf52cb7c2020-04-21 16:36:35 -0400422 vdu_parts = parts[3].split("-")
quilesj29114342019-10-29 09:30:44 +0100423 if len(vdu_parts) > 1:
424 vdu_id = vdu_parts[0]
425 vdu_count = vdu_parts[1]
426
427 return nsi_id, ns_id, vnf_id, vdu_id, vdu_count
428
429 async def write_app_status_to_db(
beierlmf52cb7c2020-04-21 16:36:35 -0400430 self,
431 db_dict: dict,
432 status: N2VCDeploymentStatus,
433 detailed_status: str,
434 vca_status: str,
435 entity_type: str,
David Garciaeb8943a2021-04-12 12:07:37 +0200436 vca_id: str = None,
quilesj29114342019-10-29 09:30:44 +0100437 ):
David Garciaeb8943a2021-04-12 12:07:37 +0200438 """
439 Write application status to database
440
441 :param: db_dict: DB dictionary
442 :param: status: Status of the application
443 :param: detailed_status: Detailed status
444 :param: vca_status: VCA status
445 :param: entity_type: Entity type ("application", "machine, and "action")
446 :param: vca_id: Id of the VCA. If None, the default VCA will be used.
447 """
quilesj29114342019-10-29 09:30:44 +0100448 if not db_dict:
beierlmf52cb7c2020-04-21 16:36:35 -0400449 self.log.debug("No db_dict => No database write")
quilesj29114342019-10-29 09:30:44 +0100450 return
451
beierlmf52cb7c2020-04-21 16:36:35 -0400452 # self.log.debug('status={} / detailed-status={} / VCA-status={}/entity_type={}'
453 # .format(str(status.value), detailed_status, vca_status, entity_type))
quilesj29114342019-10-29 09:30:44 +0100454
455 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400456 the_table = db_dict["collection"]
457 the_filter = db_dict["filter"]
458 the_path = db_dict["path"]
459 if not the_path[-1] == ".":
460 the_path = the_path + "."
quilesj29114342019-10-29 09:30:44 +0100461 update_dict = {
beierlmf52cb7c2020-04-21 16:36:35 -0400462 the_path + "status": str(status.value),
463 the_path + "detailed-status": detailed_status,
464 the_path + "VCA-status": vca_status,
465 the_path + "entity-type": entity_type,
466 the_path + "status-time": str(time.time()),
quilesj29114342019-10-29 09:30:44 +0100467 }
468
469 self.db.set_one(
470 table=the_table,
471 q_filter=the_filter,
472 update_dict=update_dict,
beierlmf52cb7c2020-04-21 16:36:35 -0400473 fail_on_empty=True,
quilesj29114342019-10-29 09:30:44 +0100474 )
475
476 # database callback
477 if self.on_update_db:
478 if asyncio.iscoroutinefunction(self.on_update_db):
beierlmf52cb7c2020-04-21 16:36:35 -0400479 await self.on_update_db(
David Garciaeb8943a2021-04-12 12:07:37 +0200480 the_table, the_filter, the_path, update_dict, vca_id=vca_id
beierlmf52cb7c2020-04-21 16:36:35 -0400481 )
quilesj29114342019-10-29 09:30:44 +0100482 else:
garciadeblas82b591c2021-03-24 09:22:13 +0100483 self.on_update_db(
484 the_table, the_filter, the_path, update_dict, vca_id=vca_id
485 )
quilesj29114342019-10-29 09:30:44 +0100486
487 except DbException as e:
488 if e.http_code == HTTPStatus.NOT_FOUND:
beierlmf52cb7c2020-04-21 16:36:35 -0400489 self.log.error(
490 "NOT_FOUND error: Exception writing status to database: {}".format(
491 e
492 )
493 )
quilesj29114342019-10-29 09:30:44 +0100494 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400495 self.log.info("Exception writing status to database: {}".format(e))
quilesj29114342019-10-29 09:30:44 +0100496
David Garciac38a6962020-09-16 13:31:33 +0200497 def osm_status(self, entity_type: str, status: str) -> N2VCDeploymentStatus:
David Garcia4fee80e2020-05-13 12:18:38 +0200498 if status not in JujuStatusToOSM[entity_type]:
David Garcia2f66c4d2020-06-19 11:40:18 +0200499 self.log.warning("Status {} not found in JujuStatusToOSM.".format(status))
David Garcia4fee80e2020-05-13 12:18:38 +0200500 return N2VCDeploymentStatus.UNKNOWN
501 return JujuStatusToOSM[entity_type][status]
quilesj29114342019-10-29 09:30:44 +0100502
David Garcia4fee80e2020-05-13 12:18:38 +0200503
quilesj776ab392019-12-12 16:10:54 +0000504def obj_to_yaml(obj: object) -> str:
505 # dump to yaml
506 dump_text = yaml.dump(obj, default_flow_style=False, indent=2)
507 # split lines
508 lines = dump_text.splitlines()
509 # remove !!python/object tags
beierlmf52cb7c2020-04-21 16:36:35 -0400510 yaml_text = ""
quilesj776ab392019-12-12 16:10:54 +0000511 for line in lines:
beierlmf52cb7c2020-04-21 16:36:35 -0400512 index = line.find("!!python/object")
quilesj776ab392019-12-12 16:10:54 +0000513 if index >= 0:
514 line = line[:index]
beierlmf52cb7c2020-04-21 16:36:35 -0400515 yaml_text += line + "\n"
quilesj776ab392019-12-12 16:10:54 +0000516 return yaml_text
517
518
519def obj_to_dict(obj: object) -> dict:
520 # convert obj to yaml
521 yaml_text = obj_to_yaml(obj)
522 # parse to dict
garciadeblas2e69dc62022-07-05 18:05:04 +0200523 return yaml.load(yaml_text, Loader=yaml.SafeLoader)