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