update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / syntax / mano_template.py
1 #
2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
5 #
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
12 # under the License.
13 #
14 # Copyright 2016 RIFT.io Inc
15
16 import uuid
17
18 import yaml
19
20 from rift.mano.tosca_translator.common.utils import _
21
22 from rift.mano.tosca_translator.common.utils import dict_convert_values_to_str
23
24 try:
25 import gi
26 gi.require_version('RwYang', '1.0')
27 gi.require_version('RwNsdYang', '1.0')
28 gi.require_version('NsdYang', '1.0')
29
30 from gi.repository import NsdYang
31 from gi.repository import RwNsdYang
32 from gi.repository import RwYang
33 except ImportError:
34 pass
35 except ValueError as e:
36 pass
37
38
39 class ManoTemplate(object):
40 '''Container for full RIFT.io MANO template.'''
41
42 YANG_NS = (NSD, VNFD) = ('nsd', 'vnfd')
43 OUTPUT_FIELDS = (NAME, ID, YANG, FILES) = ('name', 'id', 'yang', 'files')
44
45 def __init__(self, log):
46 self.log = log
47 self.resources = []
48 self.outputs = []
49 self.parameters = []
50 self.description = "Translated from TOSCA"
51 self.metadata = None
52 self.policies = []
53 self.groups = []
54
55 def output_to_yang(self, use_gi=False, indent=4):
56 self.log.debug(_('Converting translated output to yang model.'))
57
58 nsd_cat = None
59 nsd_id = str(uuid.uuid1())
60 vnfds = []
61
62 if use_gi:
63 try:
64 nsd_cat = RwNsdYang.YangData_Nsd_NsdCatalog()
65 nsd = nsd_cat.nsd.add()
66 nsd.id = nsd_id
67 nsd.name = self.metadata['name']
68 nsd.description = self.description
69 nsd.vendor = self.metadata['vendor']
70 nsd.short_name = self.metadata['name']
71 nsd.version = self.metadata['version']
72 if 'logo' in self.metadata:
73 nsd.logo = self.metadata['logo']
74 except Exception as e:
75 self.log.warning(_("Unable to use YANG GI to generate "
76 "descriptors, falling back to alternate "
77 "method: {}").format(e))
78 self.log.exception(e)
79 use_gi = False
80
81 if not use_gi:
82 nsd = {
83 'id': nsd_id,
84 'name': self.metadata['name'],
85 'description': self.description,
86 'vendor': self.metadata['vendor'],
87 'short-name': self.metadata['name'],
88 'version': self.metadata['version'],
89 }
90
91 for resource in self.resources:
92 # Do the vlds first
93 if resource.type == 'vld':
94 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
95
96 vnf_type_duplicate = []
97 vnfd_resources = []
98 vnfd_duplicate_resource_list = []
99 for resource in self.resources:
100 if resource.type == 'vnfd':
101 vnfd_resources.append(resource)
102
103 vnfd_resources.sort(key=lambda x: x.member_vnf_id, reverse=False)
104 vnf_type_to_vnf_id = {}
105 for resource in vnfd_resources:
106 if resource.vnf_type not in vnf_type_duplicate:
107 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
108 vnf_type_to_vnf_id[resource.vnf_type] = resource.id
109 vnf_type_duplicate.append(resource.vnf_type)
110 else:
111 vnfd_duplicate_resource_list.append(resource)
112
113 for resource in vnfd_duplicate_resource_list:
114 resource.generate_nsd_constiuent(nsd, vnf_type_to_vnf_id[resource.vnf_type])
115
116 for resource in self.resources:
117 # Do the other nodes
118 if resource.type != 'vnfd' and resource.type != 'vld':
119 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
120
121 for group in self.groups:
122 group.generate_yang_model(nsd, vnfds, use_gi=use_gi)
123
124 for policy in self.policies:
125 policy.generate_yang_model(nsd, vnfds, use_gi=use_gi)
126
127 # Add input params to nsd
128 if use_gi:
129 for param in self.parameters:
130 nsd.input_parameter_xpath.append(
131 NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath(
132 xpath=param.get_xpath(),
133 )
134 )
135 else:
136 nsd['input-parameter-xpath'] = []
137 for param in self.parameters:
138 nsd['input-parameter-xpath'].append(
139 {'xpath': param.get_xpath()})
140
141 # Get list of supporting files referred in template
142 # Returned format is {desc_id: [{type: type, name: filename}]}
143 # TODO (pjoseph): Currently only images and scripts are retrieved.
144 # Need to add support to get script names, charms, etc.
145 other_files = {}
146 for resource in self.resources:
147 resource.get_supporting_files(other_files, desc_id=nsd_id)
148
149 for policy in self.policies:
150 policy.get_supporting_files(other_files, desc_id=nsd_id)
151
152 self.log.debug(_("List of other files: {}".format(other_files)))
153
154 # Do the final processing and convert each descriptor to yaml string
155 tpl = {}
156
157 # Add the NSD
158 if use_gi:
159 nsd_pf = self.get_yaml(['nsd', 'rw-nsd'], nsd_cat)
160 nsd_id = nsd_cat.nsd[0].id
161 nsd_name = nsd_cat.nsd[0].name
162 else:
163 nsd_id = nsd['id']
164 nsd_name = nsd['name']
165
166 # In case of non gi proecssing,
167 # - convert all values to string
168 # - enclose in a catalog dict
169 # - prefix all keys with nsd or vnfd
170 # - Convert to YAML string
171 nsd_pf = yaml.dump(
172 self.prefix_dict(
173 self.add_cat(dict_convert_values_to_str(nsd),
174 self.NSD),
175 self.NSD),
176 default_flow_style=False)
177
178 nsd_out = {
179 self.NAME: nsd_name,
180 self.ID: nsd_id,
181 self.YANG: nsd_pf,
182 }
183
184 if nsd_id in other_files:
185 nsd_out[self.FILES] = other_files[nsd_id]
186
187 tpl[self.NSD] = [nsd_out]
188
189 # Add the VNFDs
190 tpl[self.VNFD] = []
191
192 for vnfd in vnfds:
193 if use_gi:
194 vnfd_pf = self.get_yaml(['vnfd', 'rw-vnfd'], vnfd)
195 vnfd_id = vnfd.vnfd[0].id
196 vnfd_name = vnfd.vnfd[0].name
197
198 else:
199 vnfd_id = vnfd['id']
200 vnfd_name = vnfd['name']
201
202 # In case of non gi proecssing,
203 # - convert all values to string
204 # - enclose in a catalog dict
205 # - prefix all keys with nsd or vnfd
206 # - Convert to YAML string
207 vnfd_pf = yaml.dump(
208 self.prefix_dict(
209 self.add_cat(dict_convert_values_to_str(vnfd),
210 self.VNFD),
211 self.VNFD),
212 default_flow_style=False)
213
214 vnfd_out = {
215 self.NAME: vnfd_name,
216 self.ID: vnfd_id,
217 self.YANG: vnfd_pf,
218 }
219
220
221 if vnfd_id in other_files:
222 vnfd_out[self.FILES] = other_files[vnfd_id]
223
224 tpl[self.VNFD].append(vnfd_out)
225
226 self.log.debug(_("NSD: {0}").format(tpl[self.NSD]))
227 self.log.debug(_("VNFDs:"))
228 for vnfd in tpl[self.VNFD]:
229 self.log.debug(_("{0}").format(vnfd))
230
231 return tpl
232
233 def _get_field(self, d, pf, field='name'):
234 '''Get the name given for the descriptor'''
235 # Search within the desc for a key pf:name
236 key = pf+':'+field
237 if isinstance(d, dict):
238 # If it is a dict, search for pf:name
239 if key in d:
240 return d[key]
241 else:
242 for k, v in d.items():
243 result = self._get_field(v, pf, field)
244 if result:
245 return result
246 elif isinstance(d, list):
247 for memb in d:
248 result = self._get_field(memb, pf, field)
249 if result:
250 return result
251
252 def prefix_dict(self, d, pf):
253 '''Prefix all keys of a dict with a specific prefix:'''
254 if isinstance(d, dict):
255 dic = {}
256 for key in d.keys():
257 # Only prefix keys without any prefix
258 # so later we can do custom prefixing
259 # which will not get overwritten here
260 if ':' not in key:
261 dic[pf+':'+key] = self.prefix_dict(d[key], pf)
262 else:
263 dic[key] = self.prefix_dict(d[key], pf)
264 return dic
265 elif isinstance(d, list):
266 arr = []
267 for memb in d:
268 arr.append(self.prefix_dict(memb, pf))
269 return arr
270 else:
271 return d
272
273 def add_cat(self, desc, pf):
274 return {pf+'-catalog': {pf: [desc]}}
275
276 def get_yaml(self, module_list, desc):
277 model = RwYang.Model.create_libyang()
278
279 for module in module_list:
280 model.load_module(module)
281 return desc.to_yaml(model)