d263e6f01cb1302a47517e08d092ad80e30a91fa
[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 except Exception as e:
73 self.log.warning(_("Unable to use YANG GI to generate "
74 "descriptors, falling back to alternate "
75 "method: {}").format(e))
76 self.log.exception(e)
77 use_gi = False
78
79 if not use_gi:
80 nsd = {
81 'id': nsd_id,
82 'name': self.metadata['name'],
83 'description': self.description,
84 'vendor': self.metadata['vendor'],
85 'short-name': self.metadata['name'],
86 'version': self.metadata['version'],
87 }
88
89 for resource in self.resources:
90 # Do the vlds first
91 if resource.type == 'vld':
92 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
93
94 for resource in self.resources:
95 # Do the vnfds next
96 if resource.type == 'vnfd':
97 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
98
99 for resource in self.resources:
100 # Do the other nodes
101 if resource.type != 'vnfd' and resource.type != 'vld':
102 resource.generate_yang_model(nsd, vnfds, use_gi=use_gi)
103
104 for group in self.groups:
105 group.generate_yang_model(nsd, vnfds, use_gi=use_gi)
106
107 for policy in self.policies:
108 policy.generate_yang_model(nsd, vnfds, use_gi=use_gi)
109
110 # Add input params to nsd
111 if use_gi:
112 for param in self.parameters:
113 nsd.input_parameter_xpath.append(
114 NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath(
115 xpath=param.get_xpath(),
116 )
117 )
118 else:
119 nsd['input-parameter-xpath'] = []
120 for param in self.parameters:
121 nsd['input-parameter-xpath'].append(
122 {'xpath': param.get_xpath()})
123
124 # Get list of supporting files referred in template
125 # Returned format is {desc_id: [{type: type, name: filename}]}
126 # TODO (pjoseph): Currently only images and scripts are retrieved.
127 # Need to add support to get script names, charms, etc.
128 other_files = {}
129 for resource in self.resources:
130 resource.get_supporting_files(other_files)
131
132 for policy in self.policies:
133 policy.get_supporting_files(other_files, desc_id=nsd_id)
134
135 self.log.debug(_("List of other files: {}".format(other_files)))
136
137 # Do the final processing and convert each descriptor to yaml string
138 tpl = {}
139
140 # Add the NSD
141 if use_gi:
142 nsd_pf = self.get_yaml(['nsd', 'rw-nsd'], nsd_cat)
143 nsd_id = nsd_cat.nsd[0].id
144 nsd_name = nsd_cat.nsd[0].name
145 else:
146 nsd_id = nsd['id']
147 nsd_name = nsd['name']
148
149 # In case of non gi proecssing,
150 # - convert all values to string
151 # - enclose in a catalog dict
152 # - prefix all keys with nsd or vnfd
153 # - Convert to YAML string
154 nsd_pf = yaml.dump(
155 self.prefix_dict(
156 self.add_cat(dict_convert_values_to_str(nsd),
157 self.NSD),
158 self.NSD),
159 default_flow_style=False)
160
161 nsd_out = {
162 self.NAME: nsd_name,
163 self.ID: nsd_id,
164 self.YANG: nsd_pf,
165 }
166
167 if nsd_id in other_files:
168 nsd_out[self.FILES] = other_files[nsd_id]
169
170 tpl[self.NSD] = [nsd_out]
171
172 # Add the VNFDs
173 tpl[self.VNFD] = []
174
175 for vnfd in vnfds:
176 if use_gi:
177 vnfd_pf = self.get_yaml(['vnfd', 'rw-vnfd'], vnfd)
178 vnfd_id = vnfd.vnfd[0].id
179 vnfd_name = vnfd.vnfd[0].name
180
181 else:
182 vnfd_id = vnfd['id']
183 vnfd_name = vnfd['name']
184
185 # In case of non gi proecssing,
186 # - convert all values to string
187 # - enclose in a catalog dict
188 # - prefix all keys with nsd or vnfd
189 # - Convert to YAML string
190 vnfd_pf = yaml.dump(
191 self.prefix_dict(
192 self.add_cat(dict_convert_values_to_str(vnfd),
193 self.VNFD),
194 self.VNFD),
195 default_flow_style=False)
196
197 vnfd_out = {
198 self.NAME: vnfd_name,
199 self.ID: vnfd_id,
200 self.YANG: vnfd_pf,
201 }
202
203 if vnfd_id in other_files:
204 vnfd_out[self.FILES] = other_files[vnfd_id]
205
206 tpl[self.VNFD].append(vnfd_out)
207
208 self.log.debug(_("NSD: {0}").format(tpl[self.NSD]))
209 self.log.debug(_("VNFDs:"))
210 for vnfd in tpl[self.VNFD]:
211 self.log.debug(_("{0}").format(vnfd))
212
213 return tpl
214
215 def _get_field(self, d, pf, field='name'):
216 '''Get the name given for the descriptor'''
217 # Search within the desc for a key pf:name
218 key = pf+':'+field
219 if isinstance(d, dict):
220 # If it is a dict, search for pf:name
221 if key in d:
222 return d[key]
223 else:
224 for k, v in d.items():
225 result = self._get_field(v, pf, field)
226 if result:
227 return result
228 elif isinstance(d, list):
229 for memb in d:
230 result = self._get_field(memb, pf, field)
231 if result:
232 return result
233
234 def prefix_dict(self, d, pf):
235 '''Prefix all keys of a dict with a specific prefix:'''
236 if isinstance(d, dict):
237 dic = {}
238 for key in d.keys():
239 # Only prefix keys without any prefix
240 # so later we can do custom prefixing
241 # which will not get overwritten here
242 if ':' not in key:
243 dic[pf+':'+key] = self.prefix_dict(d[key], pf)
244 else:
245 dic[key] = self.prefix_dict(d[key], pf)
246 return dic
247 elif isinstance(d, list):
248 arr = []
249 for memb in d:
250 arr.append(self.prefix_dict(memb, pf))
251 return arr
252 else:
253 return d
254
255 def add_cat(self, desc, pf):
256 return {pf+'-catalog': {pf: [desc]}}
257
258 def get_yaml(self, module_list, desc):
259 model = RwYang.Model.create_libncx()
260 for module in module_list:
261 model.load_module(module)
262 return desc.to_yaml(model)