* YANG to TOSCA translator
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang_translator.py
1 # Copyright 2016 RIFT.io Inc
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15
16 import os
17 import shutil
18 import subprocess
19 import tarfile
20
21 from rift.mano.yang_translator.common.exception import ValidationError
22 from rift.mano.yang_translator.common.utils import _
23 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
24 import ToscaResource
25 from rift.mano.yang_translator.rwmano.syntax.tosca_template \
26 import ToscaTemplate
27 from rift.mano.yang_translator.rwmano.translate_descriptors \
28 import TranslateDescriptors
29
30 import rift.package.image
31 from rift.package.package import TarPackageArchive
32 import rift.package.cloud_init
33 import rift.package.script
34 import rift.package.store
35
36
37 class YangTranslator(object):
38 '''Invokes translation methods.'''
39
40 def __init__(self, log, yangs=None, files=None, packages=[]):
41 super(YangTranslator, self).__init__()
42 self.log = log
43 self.yangs = {}
44 if yangs is not None:
45 self.yangs = yangs
46 self.files = files
47 self.archive = None
48 self.tosca_template = ToscaTemplate(log)
49 self.node_translator = None
50 self.pkgs = packages
51 self.output_files = {}
52 self.output_files['nsd'] = []
53 self.output_files['vnfd'] = []
54
55 log.info(_('Initialized parameters for translation.'))
56
57 def translate(self):
58 if self.files:
59 self.get_yangs()
60 else:
61 if 'nsd' in self.yangs:
62 self.output_files['nsd'].append(self.yangs['nsd'][0]['short_name'])
63 if 'vnfd' in self.yangs:
64 for yang_vnfd in self.yangs['vnfd']:
65 self.output_files['vnfd'].append(yang_vnfd['short_name'])
66
67 self.node_translator = TranslateDescriptors(self.log,
68 self.yangs,
69 self.tosca_template,
70 self.output_files['vnfd'])
71 self.tosca_template.resources = self.node_translator.translate()
72
73
74 return self.tosca_template.output_to_tosca()
75
76 def get_yangs(self):
77 '''Get the descriptors and convert to yang instances'''
78 for filename in self.files:
79 self.log.debug(_("Load file {0}").format(filename))
80
81 # Only one descriptor per file
82 if tarfile.is_tarfile(filename):
83 tar = open(filename, "r+b")
84 archive = TarPackageArchive(self.log, tar)
85 pkg = archive.create_package()
86 self.pkgs.append(pkg)
87 desc_type = pkg.descriptor_type
88 if desc_type == TranslateDescriptors.NSD:
89 if TranslateDescriptors.NSD not in self.yangs:
90 self.yangs[TranslateDescriptors.NSD] = []
91 self.yangs[TranslateDescriptors.NSD]. \
92 append(pkg.descriptor_msg.as_dict())
93 if 'name' in pkg.descriptor_msg.as_dict() is not None:
94 self.output_files['nsd'].append(pkg.descriptor_msg.as_dict()['name'])
95 else:
96 raise ValidationError(message="NSD Descriptor name attribute is not populated ")
97 elif desc_type == TranslateDescriptors.VNFD:
98 if TranslateDescriptors.VNFD not in self.yangs:
99 self.yangs[TranslateDescriptors.VNFD] = []
100 self.yangs[TranslateDescriptors.VNFD]. \
101 append(pkg.descriptor_msg.as_dict())
102 if 'name' in pkg.descriptor_msg.as_dict() is not None:
103 self.output_files['vnfd'].append(pkg.descriptor_msg.as_dict()['name'])
104 else:
105 raise ValidationError(message="VNFD Descriptor name attribute is not populated ")
106 else:
107 raise ValidationError("Unknown descriptor type: {}".
108 format(desc_type))
109
110 def _create_csar_files(self, output_dir, tmpl_out,
111 archive=False):
112 '''
113 for tmpl in tmpl_out:
114 if ToscaTemplate.TOSCA not in tmpl:
115 self.log.error(_("Did not find TOSCA template for {0}").
116 format(tmpl))
117 return
118 '''
119 # Create sub for each NS template
120 sub_folder_name = None
121 if self.files:
122 if len(self.output_files['nsd']) > 0:
123 if len(self.output_files['nsd']) == 1:
124 sub_folder_name = self.output_files['nsd'][0]
125 else:
126 raise ValidationError(message="Multiple NSD Descriptor uploaded ")
127 elif len(self.output_files['vnfd']) > 0:
128 if len(self.output_files['vnfd']) == 1:
129 sub_folder_name = self.output_files['vnfd'][0]
130 else:
131 raise ValidationError(message="Multiple VNFDs Descriptors uploaded without NSD")
132 else:
133 raise ValidationError(message="No NSD or VNFD uploaded")
134 else:
135 if 'nsd' in self.yangs:
136 sub_folder_name = self.yangs['nsd'][0]['short_name']
137 elif 'vnfd' in self.yangs:
138 sub_folder_name = self.yangs['vnfd'][0]['short_name']
139
140
141 subdir = os.path.join(output_dir, sub_folder_name)
142 if os.path.exists(subdir):
143 shutil.rmtree(subdir)
144 os.makedirs(subdir)
145 riftio_src_file = "{0}{1}".format(os.getenv('RIFT_INSTALL'), "/usr/rift/mano/common/riftiotypes.yaml")
146 # Create the definitions dir
147 def_dir = os.path.join(subdir, 'Definitions')
148 os.makedirs(def_dir)
149 shutil.copy2(riftio_src_file, def_dir + "/riftiotypes.yaml")
150 for tmpl_key in tmpl_out:
151 tmpl = tmpl_out[tmpl_key]
152 entry_file = os.path.join(def_dir, tmpl_key+'.yaml')
153 self.log.debug(_("Writing file {0}").
154 format(entry_file))
155 with open(entry_file, 'w+') as f:
156 f.write(tmpl[ToscaTemplate.TOSCA])
157
158 # Create the Tosca meta
159 meta_dir = os.path.join(subdir, 'TOSCA-Metadata')
160 os.makedirs(meta_dir)
161 meta = '''TOSCA-Meta-File-Version: 1.0
162 CSAR-Version: 1.1
163 Created-By: RIFT.io
164 Entry-Definitions: Definitions/'''
165 meta_data = "{}{}".format(meta, sub_folder_name+'.yaml')
166 meta_file = os.path.join(meta_dir, 'TOSCA.meta')
167 self.log.debug(_("Writing file {0}:\n{1}").
168 format(meta_file, meta_data))
169 with open(meta_file, 'w+') as f:
170 f.write(meta_data)
171
172 # Copy other supporting files
173 for key in tmpl_out:
174 tmpl = tmpl_out[key]
175 if ToscaTemplate.FILES in tmpl:
176 for f in tmpl[ToscaTemplate.FILES]:
177 self.log.debug(_("Copy supporting file {0}").format(f))
178
179 # Search in source packages
180 if len(self.pkgs):
181 for pkg in self.pkgs:
182 # TODO(pjoseph): Need to add support for other file types
183 fname = f[ToscaResource.NAME]
184 dest_path = os.path.join(subdir, f[ToscaResource.DEST])
185 ftype = f[ToscaResource.TYPE]
186
187 if ftype == 'image':
188 image_file_map = rift.package.image.get_package_image_files(pkg)
189
190 if fname in image_file_map:
191 self.log.debug(_("Extracting image {0} to {1}").
192 format(fname, dest_path))
193 pkg.extract_file(image_file_map[fname],
194 dest_path)
195 break
196
197 elif ftype == 'script':
198 script_file_map = \
199 rift.package.script.PackageScriptExtractor.package_script_files(pkg)
200 if fname in script_file_map:
201 self.log.debug(_("Extracting script {0} to {1}").
202 format(fname, dest_path))
203 pkg.extract_file(script_file_map[fname],
204 dest_path)
205 break
206
207 elif ftype == 'cloud_init':
208 script_file_map = \
209 rift.package.cloud_init.PackageCloudInitExtractor.package_script_files(pkg)
210 if fname in script_file_map:
211 self.log.debug(_("Extracting script {0} to {1}").
212 format(fname, dest_path))
213 pkg.extract_file(script_file_map[fname],
214 dest_path)
215 break
216
217 else:
218 self.log.warn(_("Unknown file type {0}: {1}").
219 format(ftype, f))
220
221 #TODO(pjoseph): Search in other locations
222
223 # Create the ZIP archive
224 if archive:
225 prev_dir=os.getcwd()
226 os.chdir(subdir)
227
228 try:
229 zip_file = key + '.zip'
230 zip_path = os.path.join(output_dir, zip_file)
231 self.log.debug(_("Creating zip file {0}").format(zip_path))
232 zip_cmd = "zip -r {}.partial ."
233 subprocess.check_call(zip_cmd.format(zip_path),
234 shell=True,
235 stdout=subprocess.DEVNULL)
236 mv_cmd = "mv {0}.partial {0}"
237 subprocess.check_call(mv_cmd.format(zip_path),
238 shell=True,
239 stdout=subprocess.DEVNULL)
240 shutil.rmtree(subdir)
241 return zip_path
242
243 except subprocess.CalledProcessError as e:
244 self.log.error(_("Creating CSAR archive failed: {0}").
245 format(e))
246
247 except Exception as e:
248 self.log.exception(e)
249
250 finally:
251 os.chdir(prev_dir)
252
253 def write_output(self, output,
254 output_dir=None,
255 archive=False,):
256 if output:
257 zip_files = []
258 #for key in output.keys():
259 if output_dir:
260 zf = self._create_csar_files(output_dir,
261 output,
262 archive=archive,)
263 zip_files.append(zf)
264 else:
265 print(_("There is an issue with TOSCA Template"))
266 return zip_files