a62bff371d73da1b3305e8b59157568119f835a6
[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 import rift.package.icon
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'].replace(' ','_'))
63 if 'vnfd' in self.yangs:
64 for yang_vnfd in self.yangs['vnfd']:
65 self.output_files['vnfd'].append(yang_vnfd['short_name'].replace(' ','_'))
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'].replace(' ','_')
137 elif 'vnfd' in self.yangs:
138 sub_folder_name = self.yangs['vnfd'][0]['short_name'].replace(' ','_')
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 tosca_meta_entry_file = None
151 for tmpl_key in tmpl_out:
152 tmpl = tmpl_out[tmpl_key]
153 file_name = tmpl_key.replace(' ','_')
154 entry_file = os.path.join(def_dir, file_name+'.yaml')
155 if file_name.endswith('nsd'):
156 tosca_meta_entry_file = file_name
157 self.log.debug(_("Writing file {0}").
158 format(entry_file))
159 with open(entry_file, 'w+') as f:
160 f.write(tmpl[ToscaTemplate.TOSCA])
161
162 if tosca_meta_entry_file is None:
163 tosca_meta_entry_file = sub_folder_name
164 # Create the Tosca meta
165 meta_dir = os.path.join(subdir, 'TOSCA-Metadata')
166 os.makedirs(meta_dir)
167 meta = '''TOSCA-Meta-File-Version: 1.0
168 CSAR-Version: 1.1
169 Created-By: RIFT.io
170 Entry-Definitions: Definitions/'''
171 meta_data = "{}{}".format(meta, tosca_meta_entry_file+'.yaml')
172 meta_file = os.path.join(meta_dir, 'TOSCA.meta')
173 self.log.debug(_("Writing file {0}:\n{1}").
174 format(meta_file, meta_data))
175 with open(meta_file, 'w+') as f:
176 f.write(meta_data)
177
178 # Copy other supporting files
179 for key in tmpl_out:
180 tmpl = tmpl_out[key]
181 if ToscaTemplate.FILES in tmpl:
182 for f in tmpl[ToscaTemplate.FILES]:
183 self.log.debug(_("Copy supporting file {0}").format(f))
184
185 # Search in source packages
186 if len(self.pkgs):
187 for pkg in self.pkgs:
188 # TODO(pjoseph): Need to add support for other file types
189 fname = f[ToscaResource.NAME]
190 dest_path = os.path.join(subdir, f[ToscaResource.DEST])
191 ftype = f[ToscaResource.TYPE]
192
193 if ftype == 'image':
194 image_file_map = rift.package.image.get_package_image_files(pkg)
195
196 if fname in image_file_map:
197 self.log.debug(_("Extracting image {0} to {1}").
198 format(fname, dest_path))
199 pkg.extract_file(image_file_map[fname],
200 dest_path)
201 break
202
203 elif ftype == 'script':
204 script_file_map = \
205 rift.package.script.PackageScriptExtractor.package_script_files(pkg)
206 if fname in script_file_map:
207 self.log.debug(_("Extracting script {0} to {1}").
208 format(fname, dest_path))
209 pkg.extract_file(script_file_map[fname],
210 dest_path)
211 break
212
213 elif ftype == 'cloud_init':
214 script_file_map = \
215 rift.package.cloud_init.PackageCloudInitExtractor.package_script_files(pkg)
216 if fname in script_file_map:
217 self.log.debug(_("Extracting script {0} to {1}").
218 format(fname, dest_path))
219 pkg.extract_file(script_file_map[fname],
220 dest_path)
221 break
222 elif ftype == 'icons':
223 icon_file_map = \
224 rift.package.icon.PackageIconExtractor.package_icon_files(pkg)
225 if fname in icon_file_map:
226 self.log.debug(_("Extracting script {0} to {1}").
227 format(fname, dest_path))
228 pkg.extract_file(icon_file_map[fname],
229 dest_path)
230 break
231
232 else:
233 self.log.warn(_("Unknown file type {0}: {1}").
234 format(ftype, f))
235
236 #TODO(pjoseph): Search in other locations
237
238 # Create the ZIP archive
239 if archive:
240 prev_dir=os.getcwd()
241 os.chdir(subdir)
242
243 try:
244 zip_file = sub_folder_name + '.zip'
245 zip_path = os.path.join(output_dir, zip_file)
246 self.log.debug(_("Creating zip file {0}").format(zip_path))
247 zip_cmd = "zip -r {}.partial ."
248 subprocess.check_call(zip_cmd.format(zip_path),
249 shell=True,
250 stdout=subprocess.DEVNULL)
251 mv_cmd = "mv {0}.partial {0}"
252 subprocess.check_call(mv_cmd.format(zip_path),
253 shell=True,
254 stdout=subprocess.DEVNULL)
255 shutil.rmtree(subdir)
256 return zip_path
257
258 except subprocess.CalledProcessError as e:
259 self.log.error(_("Creating CSAR archive failed: {0}").
260 format(e))
261
262 except Exception as e:
263 self.log.exception(e)
264
265 finally:
266 os.chdir(prev_dir)
267
268 def write_output(self, output,
269 output_dir=None,
270 archive=False,):
271 if output:
272 zip_files = []
273 #for key in output.keys():
274 if output_dir:
275 zf = self._create_csar_files(output_dir,
276 output,
277 archive=archive,)
278 zip_files.append(zf)
279 else:
280 print(_("There is an issue with TOSCA Template"))
281 return zip_files