Merge "Revert "Functional spec for cloud-init support""
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang / yang_vnfd.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 from copy import deepcopy
17
18 from rift.mano.yang_translator.common.exception import ValidationError
19 from rift.mano.yang_translator.common.utils import _
20 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
21 import ToscaResource
22 from rift.mano.yang_translator.rwmano.yang.yang_vdu import YangVdu
23
24 TARGET_CLASS_NAME = 'YangVnfd'
25
26
27 class YangVnfd(ToscaResource):
28 '''Class for RIFT.io YANG VNF descriptor translation to TOSCA type.'''
29
30 yangtype = 'vnfd'
31
32 CONFIG_TYPES = ['script', 'netconf', 'rest', 'juju']
33
34 OTHER_KEYS = (MGMT_INTF, HTTP_EP, MON_PARAM) = \
35 ('mgmt_interface', 'http_endpoint', 'monitoring_param')
36
37
38 def __init__(self,
39 log,
40 name,
41 type_,
42 yang):
43 super(YangVnfd, self).__init__(log,
44 name,
45 type_,
46 yang)
47 self.props = {}
48 self.vdus = []
49 self.mgmt_intf = {}
50 self.mon_param = []
51 self.http_ep = []
52
53 def handle_yang(self):
54 self.log.debug(_("Process VNFD desc {0}: {1}").format(self.name,
55 self.yang))
56
57 def process_vnf_config(conf):
58 vnf_conf = {}
59 if self.CONFIG_ATTR in conf:
60 for key, value in conf.pop(self.CONFIG_ATTR).items():
61 vnf_conf[key] = value
62
63 if self.CONFIG_TMPL in conf:
64 vnf_conf[self.CONFIG_TMPL] = conf.pop(self.CONFIG_TMPL)
65
66 def copy_config_details(conf_type, conf_details):
67 vnf_conf[self.CONFIG_TYPE] = conf_type
68 vnf_conf[self.CONFIG_DETAILS] = conf_details
69
70 for key in self.CONFIG_TYPES:
71 if key in conf:
72 copy_config_details(key, conf.pop(key))
73 break
74
75 if len(conf):
76 self.log.warn(_("{0}, Did not process all in VNF "
77 "configuration {1}").
78 format(self, conf))
79 self.log.debug(_("{0}, vnf config: {1}").format(self, vnf_conf))
80 self.props[self.VNF_CONFIG] = vnf_conf
81
82 def process_mgmt_intf(intf):
83 if len(self.mgmt_intf) > 0:
84 err_msg(_("{0}, Already processed another mgmt intf {1}, "
85 "got another {2}").
86 format(self, self.msmg_intf, intf))
87 self.log.error(err_msg)
88 raise ValidationError(message=err_msg)
89
90 self.mgmt_intf['protocol'] = 'tcp'
91
92 if self.PORT in intf:
93 self.mgmt_intf[self.PORT] = intf.pop(self.PORT)
94 self.props[self.PORT] = self.mgmt_intf[self.PORT]
95
96 if 'vdu_id' in intf:
97 for vdu in self.vdus:
98 if intf['vdu_id'] == vdu.id:
99 self.mgmt_intf[self.VDU] = vdu.get_name(self.name)
100 intf.pop('vdu_id')
101 break
102
103 if self.DASHBOARD_PARAMS in intf:
104 self.mgmt_intf[self.DASHBOARD_PARAMS] = \
105 intf.pop(self.DASHBOARD_PARAMS)
106
107 if len(intf):
108 self.log.warn(_("{0}, Did not process all in mgmt "
109 "interface {1}").
110 format(self, intf))
111 self.log.debug(_("{0}, Management interface: {1}").
112 format(self, self.mgmt_intf))
113
114 def process_http_ep(eps):
115 self.log.debug("{}, HTTP EP: {}".format(self, eps))
116 for ep in eps:
117 http_ep = {'protocol': 'http'} # Required for TOSCA
118 http_ep[self.PATH] = ep.pop(self.PATH)
119 http_ep[self.PORT] = ep.pop(self.PORT)
120 http_ep[self.POLL_INTVL] = ep.pop(self.POLL_INTVL_SECS)
121 if len(ep):
122 self.log.warn(_("{0}, Did not process the following for "
123 "http ep {1}").format(self, ep))
124 self.log.debug(_("{0}, http endpoint: {1}").format(self, http_ep))
125 self.http_ep.append(http_ep)
126
127 def process_mon_param(params):
128 for param in params:
129 monp = {}
130 fields = [self.NAME, self.ID, 'value_type', 'units', 'group_tag',
131 'json_query_method', 'http_endpoint_ref', 'widget_type',
132 self.DESC]
133 for key in fields:
134 if key in param:
135 monp[key] = param.pop(key)
136
137 if len(param):
138 self.log.warn(_("{0}, Did not process the following for "
139 "monitporing-param {1}").
140 format(self, param))
141 self.log.debug(_("{0}, Monitoring param: {1}").format(self, monp))
142 self.mon_param.append(monp)
143
144 def process_cp(cps):
145 for cp_dic in cps:
146 self.log.debug("{}, CP: {}".format(self, cp_dic))
147 name = cp_dic.pop(self.NAME)
148 for vdu in self.vdus:
149 if vdu.has_cp(name):
150 vdu.set_cp_type(name, cp_dic.pop(self.TYPE_Y))
151 break
152 if len(cp_dic):
153 self.log.warn(_("{0}, Did not process the following for "
154 "connection-point {1}: {2}").
155 format(self, name, cp_dic))
156
157 ENDPOINTS_MAP = {
158 self.MGMT_INTF: process_mgmt_intf,
159 self.HTTP_EP: process_http_ep,
160 self.MON_PARAM: process_mon_param,
161 'connection_point': process_cp
162 }
163
164 dic = deepcopy(self.yang)
165 try:
166 for key in self.REQUIRED_FIELDS:
167 self.props[key] = dic.pop(key)
168
169 self.id = self.props[self.ID]
170
171 # Process VDUs before CPs so as to update the CP struct in VDU
172 # when we process CP later
173 if self.VDU in dic:
174 for vdu_dic in dic.pop(self.VDU):
175 vdu = YangVdu(self.log, vdu_dic.pop(self.NAME),
176 self.VDU, vdu_dic)
177 vdu.process_vdu()
178 self.vdus.append(vdu)
179
180 for key in ENDPOINTS_MAP.keys():
181 if key in dic:
182 ENDPOINTS_MAP[key](dic.pop(key))
183
184 if self.VNF_CONFIG in dic:
185 process_vnf_config(dic.pop(self.VNF_CONFIG))
186
187 self.remove_ignored_fields(dic)
188 if len(dic):
189 self.log.warn(_("{0}, Did not process the following for "
190 "VNFD: {1}").
191 format(self, dic))
192 self.log.debug(_("{0}, VNFD: {1}").format(self, self.props))
193 except Exception as e:
194 err_msg = _("Exception processing VNFD {0} : {1}"). \
195 format(self.name, e)
196 self.log.error(err_msg)
197 raise ValidationError(message=err_msg)
198
199 def update_cp_vld(self, cp_name, vld_name):
200 for vdu in self.vdus:
201 cp = vdu.get_cp(cp_name)
202 if cp:
203 vdu.set_vld(cp_name, vld_name)
204 break
205
206 def generate_tosca_type(self, tosca):
207 self.log.debug(_("{0} Generate tosa types").
208 format(self))
209
210 for vdu in self.vdus:
211 tosca = vdu.generate_tosca_type(tosca)
212
213 # Add data_types
214 if self.T_VNF_CONFIG not in tosca[self.DATA_TYPES]:
215 tosca[self.DATA_TYPES][self.T_VNF_CONFIG] = {
216 self.PROPERTIES:
217 {self.CONFIG_TYPE:
218 {self.TYPE: self.STRING},
219 'config_delay':
220 {self.TYPE: self.INTEGER,
221 self.DEFAULT: 0,
222 self.REQUIRED: self.NO,
223 self.CONSTRAINTS:
224 [{'greater_or_equal': 0}]},
225 'config_priority':
226 {self.TYPE: self.INTEGER,
227 self.CONSTRAINTS:
228 [{'greater_than': 0}]},
229 self.CONFIG_DETAILS:
230 {self.TYPE: self.MAP},
231 self.CONFIG_TMPL:
232 {self.TYPE: self.STRING,
233 self.REQUIRED: self.NO},
234 }
235 }
236
237 # Add capability types
238 if self.CAPABILITY_TYPES not in tosca:
239 tosca[self.CAPABILITY_TYPES] = {}
240 if self.T_HTTP_EP not in tosca[self.CAPABILITY_TYPES]:
241 tosca[self.CAPABILITY_TYPES][self.T_HTTP_EP] = {
242 self.DERIVED_FROM: 'tosca.capabilities.Endpoint',
243 self.PROPERTIES: {
244 'polling_interval':
245 {self.TYPE: self.INTEGER},
246 'path':
247 {self.TYPE: self.STRING},
248 },
249 }
250
251 if self.T_MGMT_INTF not in tosca[self.CAPABILITY_TYPES]:
252 tosca[self.CAPABILITY_TYPES][self.T_MGMT_INTF] = {
253 self.DERIVED_FROM: 'tosca.capabilities.Endpoint',
254 self.PROPERTIES: {
255 self.DASHBOARD_PARAMS:
256 {self.TYPE: self.MAP},
257 self.VDU:
258 {self.TYPE: self.STRING},
259 },
260 }
261
262 if self.T_MON_PARAM not in tosca[self.CAPABILITY_TYPES]:
263 tosca[self.CAPABILITY_TYPES][self.T_MON_PARAM] = {
264 self.DERIVED_FROM: 'tosca.capabilities.nfv.Metric',
265 self.PROPERTIES: {
266 'id':
267 {self.TYPE: self.INTEGER},
268 'name':
269 {self.TYPE: self.STRING},
270 'value_type':
271 {self.TYPE: self.STRING,
272 self.DEFAULT: 'INT'},
273 'group_tag':
274 {self.TYPE: self.STRING,
275 self.DEFAULT: 'Group1'},
276 'units':
277 {self.TYPE: self.STRING},
278 'description':
279 {self.TYPE: self.STRING},
280 'json_query_method':
281 {self.TYPE: self.STRING,
282 self.DEFAULT: 'NAMEKEY'},
283 'http_endpoint_ref':
284 {self.TYPE: self.STRING},
285 'widget_type':
286 {self.TYPE: self.STRING,
287 self.DEFAULT: 'COUNTER'},
288 }
289 }
290
291 # Define the VNF type
292 if self.T_VNF1 not in tosca[self.NODE_TYPES]:
293 tosca[self.NODE_TYPES][self.T_VNF1] = {
294 self.DERIVED_FROM: 'tosca.nodes.nfv.VNF',
295 self.PROPERTIES: {
296 'vnf_configuration':
297 {self.TYPE: self.T_VNF_CONFIG},
298 'port':
299 {self.TYPE: self.INTEGER,
300 self.CONSTRAINTS:
301 [{'in_range': '[1, 65535]'}]},
302 self.START_BY_DFLT:
303 {self.TYPE: self.BOOL,
304 self.DEFAULT: self.TRUE},
305 },
306 self.CAPABILITIES: {
307 'mgmt_interface':
308 {self.TYPE: self.T_MGMT_INTF},
309 'http_endpoint':
310 {self.TYPE: self.T_HTTP_EP},
311 'monitoring_param_0':
312 {self.TYPE: self.T_MON_PARAM},
313 'monitoring_param_1':
314 {self.TYPE: self.T_MON_PARAM},
315 },
316 self.REQUIREMENTS: [
317 {'vdus':
318 {self.TYPE: 'tosca.capabilities.nfv.VirtualLinkable',
319 self.RELATIONSHIP:
320 'tosca.relationships.nfv.VirtualLinksTo',
321 self.NODE: self.T_VDU1,
322 self.OCCURENCES: '[1, UNBOUND]'}}
323 ],
324 }
325
326 return tosca
327
328 def generate_vnf_template(self, tosca, index):
329 self.log.debug(_("{0}, Generate tosca template for VNF {1}").
330 format(self, index, tosca))
331
332 for vdu in self.vdus:
333 tosca = vdu.generate_vdu_template(tosca, self.name)
334
335 node = {}
336 node[self.TYPE] = self.T_VNF1
337
338 # Remove fields not required in TOSCA
339 self.props.pop(self.DESC)
340
341 # Update index to the member-vnf-index
342 self.props[self.ID] = index
343 node[self.PROPERTIES] = self.props
344
345 caps = {}
346 if len(self.mgmt_intf):
347 caps[self.MGMT_INTF] = {
348 self.PROPERTIES: self.mgmt_intf
349 }
350
351 if len(self.http_ep):
352 caps[self.HTTP_EP] = {
353 self.PROPERTIES: self.http_ep[0]
354 }
355 if len(self.http_ep) > 1:
356 self.log.warn(_("{0}: Currently only one HTTP endpoint "
357 "supported: {1}").
358 format(self, self.http_ep))
359
360 if len(self.mon_param):
361 count = 0
362 for monp in self.mon_param:
363 name = "{}_{}".format(self.MON_PARAM, count)
364 caps[name] = {self.PROPERTIES: monp}
365 count += 1
366
367 node[self.CAPABILITIES] = caps
368
369 if len(self.vdus):
370 reqs = []
371 for vdu in self.vdus:
372 reqs.append({'vdus': {self.NODE: vdu.get_name(self.name)}})
373
374 node[self.REQUIREMENTS] = reqs
375 else:
376 self.log.warn(_("{0}, Did not find any VDUS with this VNF").
377 format(self))
378
379 self.log.debug(_("{0}, VNF node: {1}").format(self, node))
380
381 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
382
383 return tosca
384
385 def get_supporting_files(self):
386 files = []
387
388 for vdu in self.vdus:
389 f = vdu.get_supporting_files()
390 if f and len(f):
391 files.extend(f)
392
393 return files