Bug 356 - NS-Scale. Still see Exception from the RO
[osm/SO.git] / models / openmano / python / rift / openmano / rift2openmano.py
1 #!/usr/bin/env python3
2
3 #
4 # Copyright 2016 RIFT.IO Inc
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 implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18
19 import argparse
20 import collections
21 import logging
22 import math
23 import os
24 import sys
25 import tempfile
26 import yaml
27 import ast
28 import json
29
30 import gi
31 gi.require_version('RwYang', '1.0')
32 gi.require_version('RwProjectVnfdYang', '1.0')
33 gi.require_version('RwProjectNsdYang', '1.0')
34 gi.require_version('NsdYang', '1.0')
35 gi.require_version('VnfdYang', '1.0')
36
37 from gi.repository import (
38 RwYang,
39 RwProjectVnfdYang as RwVnfdYang,
40 RwProjectNsdYang as RwNsdYang,
41 NsdYang as NsdYang,
42 VnfdYang as VnfdYang
43 )
44
45 import rift.package.store
46 import rift.package.cloud_init
47
48 logger = logging.getLogger("rift2openmano.py")
49
50
51 class VNFNotFoundError(Exception):
52 pass
53
54
55 class RiftNSD(object):
56 model = RwYang.Model.create_libyang()
57 model.load_module('nsd')
58
59 def __init__(self, descriptor):
60 self._nsd = descriptor
61
62 def __str__(self):
63 return str(self._nsd)
64
65 @property
66 def name(self):
67 return self._nsd.name
68
69 @property
70 def id(self):
71 return self._nsd.id
72
73 @property
74 def vnfd_ids(self):
75 return [c.vnfd_id_ref for c in self._nsd.constituent_vnfd]
76
77 @property
78 def constituent_vnfds(self):
79 return self._nsd.constituent_vnfd
80
81 @property
82 def scaling_group_descriptor(self):
83 return self._nsd.scaling_group_descriptor
84
85 @property
86 def vlds(self):
87 return self._nsd.vld
88
89 @property
90 def cps(self):
91 return self._nsd.connection_point
92
93 @property
94 def description(self):
95 return self._nsd.description
96
97 @classmethod
98 def from_xml_file_hdl(cls, hdl):
99 hdl.seek(0)
100 descriptor = NsdYang.YangData_Nsd_NsdCatalog_Nsd()
101 descriptor.from_xml_v2(RiftNSD.model, hdl.read())
102 return cls(descriptor)
103
104 @classmethod
105 def from_yaml_file_hdl(cls, hdl):
106 hdl.seek(0)
107 descriptor = NsdYang.YangData_Nsd_NsdCatalog_Nsd()
108 descriptor.from_yaml(RiftNSD.model, hdl.read())
109 return cls(descriptor)
110
111 def from_dict(self):
112 descriptor = NsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict(self._nsd.as_dict(), ignore_missing_keys=True).to_json_without_namespace(RiftNSD.model)
113 return descriptor
114
115
116 class RiftVNFD(object):
117 model = RwYang.Model.create_libyang()
118 model.load_module('vnfd')
119
120 def __init__(self, descriptor):
121 self._vnfd = descriptor
122
123 def __str__(self):
124 return str(self._vnfd)
125
126 @property
127 def id(self):
128 return self._vnfd.id
129
130 @property
131 def name(self):
132 return self._vnfd.name
133
134 @property
135 def description(self):
136 return self._vnfd.description
137
138 @property
139 def cps(self):
140 return self._vnfd.connection_point
141
142 @property
143 def vdus(self):
144 return self._vnfd.vdu
145
146 @property
147 def internal_vlds(self):
148 return self._vnfd.internal_vld
149
150 @classmethod
151 def from_xml_file_hdl(cls, hdl):
152 hdl.seek(0)
153 descriptor = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd()
154 descriptor.from_xml_v2(RiftVNFD.model, hdl.read())
155 return cls(descriptor)
156
157 @classmethod
158 def from_yaml_file_hdl(cls, hdl):
159 hdl.seek(0)
160 descriptor = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd()
161 descriptor.from_yaml(RiftVNFD.model, hdl.read())
162 return cls(descriptor)
163
164 def from_dict(self):
165 descriptor = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict(self._vnfd.as_dict(), ignore_missing_keys=True).to_json_without_namespace(RiftVNFD.model)
166 return descriptor
167
168
169 def is_writable_directory(dir_path):
170 """ Returns True if dir_path is writable, False otherwise
171
172 Arguments:
173 dir_path - A directory path
174 """
175 if not os.path.exists(dir_path):
176 raise ValueError("Directory does not exist: %s", dir_path)
177
178 try:
179 testfile = tempfile.TemporaryFile(dir=dir_path)
180 testfile.close()
181 except OSError:
182 return False
183
184 return True
185
186
187 def create_vnfd_from_files(vnfd_file_hdls):
188 """ Create a list of RiftVNFD instances from xml/yaml file handles
189
190 Arguments:
191 vnfd_file_hdls - Rift VNFD XML/YAML file handles
192
193 Returns:
194 A list of RiftVNFD instances
195 """
196 vnfd_dict = {}
197 for vnfd_file_hdl in vnfd_file_hdls:
198 if vnfd_file_hdl.name.endswith("yaml") or vnfd_file_hdl.name.endswith("yaml"):
199 vnfd = RiftVNFD.from_yaml_file_hdl(vnfd_file_hdl)
200 else:
201 vnfd = RiftVNFD.from_xml_file_hdl(vnfd_file_hdl)
202 vnfd_dict[vnfd.id] = vnfd
203
204 return vnfd_dict
205
206
207 def create_nsd_from_file(nsd_file_hdl):
208 """ Create a list of RiftNSD instances from yaml/xml file handles
209
210 Arguments:
211 nsd_file_hdls - Rift NSD XML/yaml file handles
212
213 Returns:
214 A list of RiftNSD instances
215 """
216 if nsd_file_hdl.name.endswith("yaml") or nsd_file_hdl.name.endswith("yaml"):
217 nsd = RiftNSD.from_yaml_file_hdl(nsd_file_hdl)
218 else:
219 nsd = RiftNSD.from_xml_file_hdl(nsd_file_hdl)
220 return nsd
221
222
223 def ddict():
224 return collections.defaultdict(dict)
225
226 def convert_vnfd_name(vnfd_name, member_idx):
227 return vnfd_name + "__" + str(member_idx)
228
229
230 def rift2openmano_nsd(rift_nsd, rift_vnfds, openmano_vnfd_ids, http_api, rift_vnfd_id=None):
231 try:
232 if rift_vnfd_id is None:
233 for vnfd_id in rift_nsd.vnfd_ids:
234 if vnfd_id not in rift_vnfds:
235 raise VNFNotFoundError("VNF id %s not provided" % vnfd_id)
236
237 openmano_nsd_im_body = json.loads(rift_nsd.from_dict())
238 openmano_nsd_api_format = {
239 "nsd:nsd-catalog": {
240 "nsd": [openmano_nsd_im_body['nsd-catalog']['nsd'][0]]
241 }
242 }
243
244 openmano_nsd = http_api.post_nsd_v3(openmano_nsd_api_format)
245
246 return openmano_nsd
247
248 except Exception as e:
249 logger.error(e)
250 raise e
251
252 def rift2openmano_vnfd_nsd(rift_nsd, rift_vnfds, openmano_vnfd_ids, http_api, rift_vnfd_id=None):
253 try:
254 if rift_vnfd_id not in rift_vnfds:
255 raise VNFNotFoundError("VNF id %s not provided" % rift_vnfd_id)
256
257 # This is the scaling NSD Descriptor. Can use the NSD IM Model.
258 openmano_nsd_im_body = json.loads(rift_nsd.from_dict())
259
260 openmano_nsd_api_format = {
261 "nsd:nsd-catalog": {
262 "nsd": [openmano_nsd_im_body['nsd-catalog']['nsd'][0]]
263 }
264 }
265
266 openmano_nsd = http_api.post_nsd_v3(openmano_nsd_api_format)
267
268 return openmano_nsd
269
270 except Exception as e:
271 logger.error(e)
272 raise e
273
274
275 def cloud_init(rift_vnfd_id, vdu, project_name='default'):
276 """ Populate cloud_init with script from
277 either the inline contents or from the file provided
278 """
279 vnfd_package_store = rift.package.store.VnfdPackageFilesystemStore(logger, project=project_name)
280
281 cloud_init_msg = None
282 if 'cloud_init' in vdu:
283 logger.debug("cloud_init script provided inline %s", vdu['cloud_init'])
284 cloud_init_msg = vdu['cloud_init']
285 elif 'cloud_init_file' in vdu:
286 # Get cloud-init script contents from the file provided in the cloud_init_file param
287 logger.debug("cloud_init script provided in file %s", vdu['cloud_init_file'])
288 filename = vdu['cloud_init_file']
289 vnfd_package_store.refresh()
290 stored_package = vnfd_package_store.get_package(rift_vnfd_id)
291 cloud_init_extractor = rift.package.cloud_init.PackageCloudInitExtractor(logger)
292 try:
293 cloud_init_msg = cloud_init_extractor.read_script(stored_package, filename)
294 except rift.package.cloud_init.CloudInitExtractionError as e:
295 raise ValueError(e)
296 else:
297 logger.debug("VDU translation: cloud-init script not provided")
298 return
299
300 logger.debug("Current cloud init msg is {}".format(cloud_init_msg))
301 return cloud_init_msg
302
303 def config_file_init(rift_vnfd_id, vdu, cfg_file, project_name='default'):
304 """ Populate config file init with file provided
305 """
306 vnfd_package_store = rift.package.store.VnfdPackageFilesystemStore(logger, project=project_name)
307
308 # Get script contents from the file provided in the cloud_init directory
309 logger.debug("config file script provided in file {}".format(cfg_file))
310 filename = cfg_file
311 vnfd_package_store.refresh()
312 stored_package = vnfd_package_store.get_package(rift_vnfd_id)
313 cloud_init_extractor = rift.package.cloud_init.PackageCloudInitExtractor(logger)
314 try:
315 cfg_file_msg = cloud_init_extractor.read_script(stored_package, filename)
316 except rift.package.cloud_init.CloudInitExtractionError as e:
317 raise ValueError(e)
318
319 logger.debug("Current config file msg is {}".format(cfg_file_msg))
320 return cfg_file_msg
321
322 def rift2openmano_vnfd(rift_vnfd, rift_nsd, http_api):
323 try:
324 openmano_vnfd_im_body = json.loads(rift_vnfd.from_dict())
325
326 # All type_yang leafs renamed to type
327
328
329 vnfd_dict = openmano_vnfd_im_body['vnfd-catalog']['vnfd'][0]
330
331 if 'vdu' in vnfd_dict:
332 for vdu in vnfd_dict['vdu']:
333 if 'cloud_init_file' in vdu:
334 # Replacing the leaf with the actual contents of the file.
335 # The RO does not have the ability to read files yet.
336 vdu['cloud_init_file'] = cloud_init(openmano_vnfd_im_body.id, vdu)
337 elif 'cloud_init' in vdu:
338 vdu['cloud_init'] = cloud_init(openmano_vnfd_im_body.id, vdu)
339
340 if 'supplemental_boot_data' in vdu:
341 if 'config_file' in vdu['supplemental_boot_data']:
342 for config_file in vdu['supplemental_boot_data']['config_file']:
343 # Replacing the leaf with the actual contents of the file.
344 # The RO does not have the ability to read files yet.
345 config_file['source'] = config_file_init(openmano_vnfd_im_body.id, vdu, config_file['source'])
346
347 openmano_vnfd_api_format = {
348 "vnfd:vnfd-catalog": {
349 "vnfd": [vnfd_dict]
350 }
351 }
352
353 openmano_vnfd = http_api.post_vnfd_v3(openmano_vnfd_api_format)
354
355 return openmano_vnfd
356
357 except Exception as e:
358 logger.error(e)
359 raise e
360
361
362
363 def parse_args(argv=sys.argv[1:]):
364 """ Parse the command line arguments
365
366 Arguments:
367 arv - The list of arguments to parse
368
369 Returns:
370 Argparse Namespace instance
371 """
372 parser = argparse.ArgumentParser()
373 parser.add_argument(
374 '-o', '--outdir',
375 default='-',
376 help="Directory to output converted descriptors. Default is stdout",
377 )
378
379 parser.add_argument(
380 '-n', '--nsd-file-hdl',
381 metavar="nsd_file",
382 type=argparse.FileType('r'),
383 help="Rift NSD Descriptor File",
384 )
385
386 parser.add_argument(
387 '-v', '--vnfd-file-hdls',
388 metavar="vnfd_file",
389 action='append',
390 type=argparse.FileType('r'),
391 help="Rift VNFD Descriptor File",
392 )
393
394 args = parser.parse_args(argv)
395
396 if not os.path.exists(args.outdir):
397 os.makedirs(args.outdir)
398
399 if not is_writable_directory(args.outdir):
400 logging.error("Directory %s is not writable", args.outdir)
401 sys.exit(1)
402
403 return args
404
405
406 def write_yaml_to_file(name, outdir, desc_dict):
407 file_name = "%s.yaml" % name
408 yaml_str = yaml.dump(desc_dict)
409 if outdir == "-":
410 sys.stdout.write(yaml_str)
411 return
412
413 file_path = os.path.join(outdir, file_name)
414 dir_path = os.path.dirname(file_path)
415 if not os.path.exists(dir_path):
416 os.makedirs(dir_path)
417
418 with open(file_path, "w") as hdl:
419 hdl.write(yaml_str)
420
421 logger.info("Wrote descriptor to %s", file_path)
422
423
424 def main(argv=sys.argv[1:]):
425 args = parse_args(argv)
426 nsd = None
427 openmano_vnfr_ids = dict()
428 vnf_dict = None
429 if args.vnfd_file_hdls is not None:
430 vnf_dict = create_vnfd_from_files(args.vnfd_file_hdls)
431
432 for vnfd in vnf_dict:
433 openmano_vnfr_ids[vnfd] = vnfd
434
435 if args.nsd_file_hdl is not None:
436 nsd = create_nsd_from_file(args.nsd_file_hdl)
437
438 openmano_nsd = rift2openmano_nsd(nsd, vnf_dict, openmano_vnfr_ids)
439 vnfd_nsd = rift2openmano_vnfd_nsd(nsd, vnf_dict, openmano_vnfr_ids)
440 write_yaml_to_file(openmano_nsd["name"], args.outdir, openmano_nsd)
441 write_yaml_to_file(vnfd_nsd["name"], args.outdir, vnfd_nsd)
442
443 for vnf in vnf_dict.values():
444 openmano_vnf = rift2openmano_vnfd(vnf, nsd)
445 write_yaml_to_file(openmano_vnf["vnf"]["name"], args.outdir, openmano_vnf)
446
447
448 if __name__ == "__main__":
449 logging.basicConfig(level=logging.WARNING)
450 main()