update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwlaunchpad / plugins / rwlaunchpadtasklet / rift / package / convert.py
1
2 #
3 # Copyright 2016-2017 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import json
19 import logging
20 import os
21 import yaml
22
23 import gi
24 gi.require_version('RwNsdYang', '1.0')
25 gi.require_version('RwProjectNsdYang', '1.0')
26 gi.require_version('RwVnfdYang', '1.0')
27 gi.require_version('RwProjectVnfdYang', '1.0')
28 gi.require_version('RwYang', '1.0')
29 from gi.repository import (
30 RwNsdYang,
31 RwVnfdYang,
32 NsdYang,
33 VnfdYang,
34 RwProjectNsdYang,
35 RwProjectVnfdYang,
36 ProjectNsdYang,
37 ProjectVnfdYang,
38 RwYang,
39 )
40
41 from rift.mano.utils.project import NS_PROJECT
42 from rift.rwlib.translation.json2xml import InvalidSchemaException
43
44 class UnknownExtensionError(Exception):
45 pass
46
47
48 class SerializationError(Exception):
49 pass
50
51
52 def decode(desc_data):
53 if isinstance(desc_data, bytes):
54 desc_data = desc_data.decode()
55
56 return desc_data
57
58
59 class ProtoMessageSerializer(object):
60 """(De)Serializer/deserializer fo a specific protobuf message into various formats"""
61 libyang_model = None
62
63 def __init__(self, yang_ns, yang_pb_cls,
64 yang_ns_project, yang_pb_project_cls):
65 """ Create a serializer for a specific protobuf message """
66 self._yang_ns = yang_ns
67 self._yang_pb_cls = yang_pb_cls
68 self._yang_ns_project = yang_ns_project
69 self._yang_pb_project_cls = yang_pb_project_cls
70
71 self._log = logging.getLogger('rw-maon-log')
72
73 @classmethod
74 def _deserialize_extension_method_map(cls):
75 return {
76 ".xml": cls._from_xml_file_hdl,
77 ".yml": cls._from_yaml_file_hdl,
78 ".yaml": cls._from_yaml_file_hdl,
79 ".json": cls._from_json_file_hdl,
80 }
81
82 @classmethod
83 def _serialize_extension_method_map(cls):
84 return {
85 ".xml": cls.to_xml_string,
86 ".yml": cls.to_yaml_string,
87 ".yaml": cls.to_yaml_string,
88 ".json": cls.to_json_string,
89 }
90
91 @classmethod
92 def is_supported_file(cls, filename):
93 """Returns whether a file has a supported file extension
94
95 Arguments:
96 filename - A descriptor file
97
98 Returns:
99 True if file extension is supported, False otherwise
100
101 """
102 _, extension = os.path.splitext(filename)
103 extension_lc = extension.lower()
104
105 return extension_lc in cls._deserialize_extension_method_map()
106
107 @property
108 def yang_namespace(self):
109 """ The Protobuf's GI namespace class (e.g. RwVnfdYang) """
110 return self._yang_ns
111
112 @property
113 def yang_class(self):
114 """ The Protobuf's GI class (e.g. RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd) """
115 return self._yang_pb_cls
116
117 @property
118 def yang_ns_project(self):
119 """ The Protobuf's GI namespace class (e.g. RwProjectVnfdYang) """
120 return self._yang_ns_project
121
122 @property
123 def yang_class_project(self):
124 """ The Protobuf's GI class (e.g. RwProjectVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd) """
125 return self._yang_pb_project_cls
126
127 @property
128 def model(self):
129 cls = self.__class__
130
131 # Cache the libyang model for the serializer class
132 if cls.libyang_model is None:
133 cls.libyang_model = RwYang.model_create_libyang()
134 cls.libyang_model.load_schema_ypbc(self.yang_namespace.get_schema())
135 cls.libyang_model.load_schema_ypbc(self.yang_ns_project.get_schema())
136
137 return cls.libyang_model
138
139 def _from_xml_file_hdl(self, file_hdl, project=None):
140 xml = file_hdl.read()
141
142 return self.yang_class.from_xml_v2(self.model, decode(xml), strict=False) \
143 if not project else self._yang_pb_project_cls.from_xml_v2(self.model, decode(xml), strict=False)
144
145 def _from_json_file_hdl(self, file_hdl, project=None):
146 jstr = file_hdl.read()
147 self._log.debug("Convert from json file: {}".format(jstr))
148
149 try:
150 if not project:
151 desc_msg = self.yang_class.from_json(self.model, decode(jstr), strict=False)
152 else:
153 desc_msg = self._yang_pb_project_cls.from_json(self.model, decode(jstr), strict=False)
154
155 self._log.debug("desc_msg: {}".format(desc_msg.as_dict()))
156 return self.yang_class_project.from_dict(desc_msg.as_dict())
157 except Exception as e:
158 self._log.exception(e)
159 raise e
160
161 def _from_yaml_file_hdl(self, file_hdl, project=None):
162 yml = file_hdl.read()
163
164 try:
165 desc_msg = self.yang_class.from_yaml(self.model, decode(yml), strict=True)
166 except InvalidSchemaException as invalid_scheme_exception:
167 self._log.error("Exception raised during schema translation, %s. Launchpad will" \
168 "continue to process the remaining elements ", str(invalid_scheme_exception))
169 desc_msg = self.yang_class.from_yaml(self.model, decode(yml), strict=False)
170 except Exception as e:
171 self._log.exception(e)
172 raise e
173
174 return self.yang_class_project.from_dict(desc_msg.as_dict())
175
176 def to_desc_msg(self, pb_msg, project_rooted=True):
177 """Convert to and from project rooted pb msg descriptor to catalog
178 rooted pb msg
179 project_rooted: if pb_msg is project rooted or not
180 """
181 if project_rooted:
182 if isinstance(pb_msg, self._yang_pb_project_cls):
183 return self._yang_pb_cls.from_dict(pb_msg.as_dict())
184 elif isinstance(pb_msg, self._yang_pb_cls):
185 return pb_msg
186
187 else:
188 if isinstance(pb_msg, self._yang_pb_cls):
189 return self._yang_pb_project_cls.from_dict(pb_msg.as_dict())
190 elif isinstance(pb_msg, self._yang_pb_project_cls):
191 return pb_msg
192
193 raise TypeError("Invalid protobuf message type provided: {}".format(type(pb_msg)))
194
195
196 def to_json_string(self, pb_msg, project_ns=False):
197 """ Serialize a protobuf message into JSON
198
199 Arguments:
200 pb_msg - A GI-protobuf object of type provided into constructor
201 project_ns - Need the desc in project namespace, required for
202 posting to Restconf as part of onboarding
203
204 Returns:
205 A JSON string representing the protobuf message
206
207 Raises:
208 SerializationError - Message could not be serialized
209 TypeError - Incorrect protobuf type provided
210 """
211 self._log.debug("Convert desc to json (ns:{}): {}".format(project_ns, pb_msg.as_dict()))
212 try:
213 # json_str = pb_msg.to_json(self.model)
214
215 desc_msg = self.to_desc_msg(pb_msg, not project_ns)
216 json_str = desc_msg.to_json(self.model)
217 if project_ns:
218 # Remove rw-project:project top level element
219 dic = json.loads(json_str)
220 jstr = json.dumps(dic[NS_PROJECT][0])
221 else:
222 jstr = json_str
223
224 except Exception as e:
225 raise SerializationError(e)
226
227 self._log.debug("Convert desc to json: {}".format(jstr))
228 return jstr
229
230 def to_yaml_string(self, pb_msg, project_ns=False):
231 """ Serialize a protobuf message into YAML
232
233 Arguments:
234 pb_msg - A GI-protobuf object of type provided into constructor
235 project_ns - Need the desc in project namespace, required for
236 posting to Restconf as part of onboarding
237
238 Returns:
239 A YAML string representing the protobuf message
240
241 Raises:
242 SerializationError - Message could not be serialized
243 TypeError - Incorrect protobuf type provided
244 """
245 self._log.debug("Convert desc to yaml (ns:{}): {}".format(project_ns, pb_msg.as_dict()))
246 try:
247 desc_msg = self.to_desc_msg(pb_msg, not project_ns)
248 yaml_str = desc_msg.to_yaml(self.model)
249 if project_ns:
250 # Remove rw-project:project top level element
251 dic = yaml.loads(yaml_str)
252 ystr = yaml.dump(dic[NS_PROJECT][0])
253 else:
254 ystr = yaml_str
255
256
257 except Exception as e:
258 self._log.exception("Exception converting to yaml: {}".format(e))
259 raise SerializationError(e)
260
261 return ystr
262
263 def to_xml_string(self, pb_msg):
264 """ Serialize a protobuf message into XML
265
266 Arguments:
267 pb_msg - A GI-protobuf object of type provided into constructor
268
269 Returns:
270 A XML string representing the protobuf message
271
272 Raises:
273 SerializationError - Message could not be serialized
274 TypeError - Incorrect protobuf type provided
275 """
276 try:
277 desc_msg = self.to_desc_msg(pb_msg)
278 xml_str = desc_msg.to_xml_v2(self.model)
279
280 except Exception as e:
281 self._log.exception("Exception converting to xml: {}".format(e))
282 raise SerializationError(e)
283
284 return xml_str
285
286 def from_file_hdl(self, file_hdl, extension, project=None):
287 """ Returns the deserialized protobuf message from file contents
288
289 This function determines the serialization format based on file extension
290
291 Arguments:
292 file_hdl - The file hdl to deserialize (set at pos 0)
293 extension - Extension of the file format (second item of os.path.splitext())
294
295 Returns:
296 A GI-Proto message of type that was provided into the constructor
297
298 Raises:
299 UnknownExtensionError - File extension is not of a known serialization format
300 SerializationError - File failed to be deserialized into the protobuf message
301 """
302
303 extension_lc = extension.lower()
304 extension_map = self._deserialize_extension_method_map()
305
306 if extension_lc not in extension_map:
307 raise UnknownExtensionError("Cannot detect message format for %s extension" % extension_lc)
308
309 try:
310 self._log.debug("Converting from json..project = {}".format(project))
311 msg = extension_map[extension_lc](self, file_hdl, project)
312 except Exception as e:
313 raise SerializationError(e)
314
315 return msg
316
317 def to_string(self, pb_msg, extension):
318 """ Returns the serialized protobuf message for a particular file extension
319
320 This function determines the serialization format based on file extension
321
322 Arguments:
323 pb_msg - A GI-protobuf object of type provided into constructor
324 extension - Extension of the file format (second item of os.path.splitext())
325
326 Returns:
327 A GI-Proto message of type that was provided into the constructor
328
329 Raises:
330 UnknownExtensionError - File extension is not of a known serialization format
331 SerializationError - File failed to be deserialized into the protobuf message
332 """
333
334 extension_lc = extension.lower()
335 extension_map = self._serialize_extension_method_map()
336
337 if extension_lc not in extension_map:
338 raise UnknownExtensionError("Cannot detect message format for %s extension" % extension_lc)
339
340 try:
341 msg = extension_map[extension_lc](self, pb_msg)
342 except Exception as e:
343 raise SerializationError(e)
344
345 return msg
346
347
348 class VnfdSerializer(ProtoMessageSerializer):
349 """ Creates a serializer for the VNFD descriptor"""
350 def __init__(self):
351 super().__init__(VnfdYang, VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd,
352 ProjectVnfdYang, ProjectVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd)
353
354
355 class NsdSerializer(ProtoMessageSerializer):
356 """ Creates a serializer for the NSD descriptor"""
357 def __init__(self):
358 super().__init__(NsdYang, NsdYang.YangData_Nsd_NsdCatalog_Nsd,
359 ProjectNsdYang, ProjectNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd)
360
361
362 class RwVnfdSerializer(ProtoMessageSerializer):
363 """ Creates a serializer for the VNFD descriptor"""
364 def __init__(self):
365 super().__init__(RwVnfdYang, RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd,
366 RwProjectVnfdYang, RwProjectVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd)
367
368
369 class RwNsdSerializer(ProtoMessageSerializer):
370 """ Creates a serializer for the NSD descriptor"""
371 def __init__(self):
372 super().__init__(RwNsdYang, RwNsdYang.YangData_Nsd_NsdCatalog_Nsd,
373 RwProjectNsdYang, RwProjectNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd)