9ff53e2f66e0998899cb9d7f947162ee1d025264
[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 if self.POLL_INTVL in http_ep:
121 http_ep[self.POLL_INTVL] = ep.pop(self.POLL_INTVL_SECS)
122 if len(ep):
123 self.log.warn(_("{0}, Did not process the following for "
124 "http ep {1}").format(self, ep))
125 self.log.debug(_("{0}, http endpoint: {1}").format(self, http_ep))
126 self.http_ep.append(http_ep)
127
128 def process_mon_param(params):
129 for param in params:
130 monp = {}
131 fields = [self.NAME, self.ID, 'value_type', 'units', 'group_tag',
132 'json_query_method', 'http_endpoint_ref', 'widget_type',
133 self.DESC]
134 for key in fields:
135 if key in param:
136 monp[key] = param.pop(key)
137
138 if len(param):
139 self.log.warn(_("{0}, Did not process the following for "
140 "monitporing-param {1}").
141 format(self, param))
142 self.log.debug(_("{0}, Monitoring param: {1}").format(self, monp))
143 self.mon_param.append(monp)
144
145 def process_cp(cps):
146 for cp_dic in cps:
147 self.log.debug("{}, CP: {}".format(self, cp_dic))
148 name = cp_dic.pop(self.NAME)
149 for vdu in self.vdus:
150 if vdu.has_cp(name):
151 vdu.set_cp_type(name, cp_dic.pop(self.TYPE_Y))
152 break
153 if len(cp_dic):
154 self.log.warn(_("{0}, Did not process the following for "
155 "connection-point {1}: {2}").
156 format(self, name, cp_dic))
157
158 ENDPOINTS_MAP = {
159 self.MGMT_INTF: process_mgmt_intf,
160 self.HTTP_EP: process_http_ep,
161 self.MON_PARAM: process_mon_param,
162 'connection_point': process_cp
163 }
164
165 dic = deepcopy(self.yang)
166 try:
167 for key in self.REQUIRED_FIELDS:
168 self.props[key] = dic.pop(key)
169
170 self.id = self.props[self.ID]
171
172 # Process VDUs before CPs so as to update the CP struct in VDU
173 # when we process CP later
174 if self.VDU in dic:
175 for vdu_dic in dic.pop(self.VDU):
176 vdu = YangVdu(self.log, vdu_dic.pop(self.NAME),
177 self.VDU, vdu_dic)
178 vdu.process_vdu()
179 self.vdus.append(vdu)
180
181 for key in ENDPOINTS_MAP.keys():
182 if key in dic:
183 ENDPOINTS_MAP[key](dic.pop(key))
184
185 if self.VNF_CONFIG in dic:
186 process_vnf_config(dic.pop(self.VNF_CONFIG))
187
188 self.remove_ignored_fields(dic)
189 if len(dic):
190 self.log.warn(_("{0}, Did not process the following for "
191 "VNFD: {1}").
192 format(self, dic))
193 self.log.debug(_("{0}, VNFD: {1}").format(self, self.props))
194 except Exception as e:
195 err_msg = _("Exception processing VNFD {0} : {1}"). \
196 format(self.name, e)
197 self.log.error(err_msg)
198 raise ValidationError(message=err_msg)
199
200 def update_cp_vld(self, cp_name, vld_name):
201 for vdu in self.vdus:
202 cp = vdu.get_cp(cp_name)
203 if cp:
204 vdu.set_vld(cp_name, vld_name)
205 break
206
207 def generate_tosca_type(self, tosca):
208 self.log.debug(_("{0} Generate tosa types").
209 format(self))
210
211 for vdu in self.vdus:
212 tosca = vdu.generate_tosca_type(tosca)
213
214 # Add data_types
215 if self.T_VNF_CONFIG not in tosca[self.DATA_TYPES]:
216 tosca[self.DATA_TYPES][self.T_VNF_CONFIG] = {
217 self.PROPERTIES:
218 {self.CONFIG_TYPE:
219 {self.TYPE: self.STRING},
220 'config_delay':
221 {self.TYPE: self.INTEGER,
222 self.DEFAULT: 0,
223 self.REQUIRED: self.NO,
224 self.CONSTRAINTS:
225 [{'greater_or_equal': 0}]},
226 'config_priority':
227 {self.TYPE: self.INTEGER,
228 self.CONSTRAINTS:
229 [{'greater_than': 0}]},
230 self.CONFIG_DETAILS:
231 {self.TYPE: self.MAP},
232 self.CONFIG_TMPL:
233 {self.TYPE: self.STRING,
234 self.REQUIRED: self.NO},
235 }
236 }
237
238 # Add capability types
239 if self.CAPABILITY_TYPES not in tosca:
240 tosca[self.CAPABILITY_TYPES] = {}
241 if self.T_HTTP_EP not in tosca[self.CAPABILITY_TYPES]:
242 tosca[self.CAPABILITY_TYPES][self.T_HTTP_EP] = {
243 self.DERIVED_FROM: 'tosca.capabilities.Endpoint',
244 self.PROPERTIES: {
245 'polling_interval':
246 {self.TYPE: self.INTEGER},
247 'path':
248 {self.TYPE: self.STRING},
249 },
250 }
251
252 if self.T_MGMT_INTF not in tosca[self.CAPABILITY_TYPES]:
253 tosca[self.CAPABILITY_TYPES][self.T_MGMT_INTF] = {
254 self.DERIVED_FROM: 'tosca.capabilities.Endpoint',
255 self.PROPERTIES: {
256 self.DASHBOARD_PARAMS:
257 {self.TYPE: self.MAP},
258 self.VDU:
259 {self.TYPE: self.STRING},
260 },
261 }
262
263 if self.T_MON_PARAM not in tosca[self.CAPABILITY_TYPES]:
264 tosca[self.CAPABILITY_TYPES][self.T_MON_PARAM] = {
265 self.DERIVED_FROM: 'tosca.capabilities.nfv.Metric',
266 self.PROPERTIES: {
267 'id':
268 {self.TYPE: self.INTEGER},
269 'name':
270 {self.TYPE: self.STRING},
271 'value_type':
272 {self.TYPE: self.STRING,
273 self.DEFAULT: 'INT'},
274 'group_tag':
275 {self.TYPE: self.STRING,
276 self.DEFAULT: 'Group1'},
277 'units':
278 {self.TYPE: self.STRING},
279 'description':
280 {self.TYPE: self.STRING},
281 'json_query_method':
282 {self.TYPE: self.STRING,
283 self.DEFAULT: 'NAMEKEY'},
284 'http_endpoint_ref':
285 {self.TYPE: self.STRING},
286 'widget_type':
287 {self.TYPE: self.STRING,
288 self.DEFAULT: 'COUNTER'},
289 }
290 }
291
292 # Define the VNF type
293 if self.T_VNF1 not in tosca[self.NODE_TYPES]:
294 tosca[self.NODE_TYPES][self.T_VNF1] = {
295 self.DERIVED_FROM: 'tosca.nodes.nfv.VNF',
296 self.PROPERTIES: {
297 'vnf_configuration':
298 {self.TYPE: self.T_VNF_CONFIG},
299 'port':
300 {self.TYPE: self.INTEGER,
301 self.CONSTRAINTS:
302 [{'in_range': '[1, 65535]'}]},
303 self.START_BY_DFLT:
304 {self.TYPE: self.BOOL,
305 self.DEFAULT: self.TRUE},
306 },
307 self.CAPABILITIES: {
308 'mgmt_interface':
309 {self.TYPE: self.T_MGMT_INTF},
310 'http_endpoint':
311 {self.TYPE: self.T_HTTP_EP},
312 'monitoring_param_0':
313 {self.TYPE: self.T_MON_PARAM},
314 'monitoring_param_1':
315 {self.TYPE: self.T_MON_PARAM},
316 },
317 self.REQUIREMENTS: [
318 {'vdus':
319 {self.TYPE: 'tosca.capabilities.nfv.VirtualLinkable',
320 self.RELATIONSHIP:
321 'tosca.relationships.nfv.VirtualLinksTo',
322 self.NODE: self.T_VDU1,
323 self.OCCURENCES: '[1, UNBOUND]'}}
324 ],
325 }
326
327 return tosca
328
329 def generate_vnf_template(self, tosca, index):
330 self.log.debug(_("{0}, Generate tosca template for VNF {1}").
331 format(self, index, tosca))
332
333 for vdu in self.vdus:
334 tosca = vdu.generate_vdu_template(tosca, self.name)
335
336 node = {}
337 node[self.TYPE] = self.T_VNF1
338
339 # Remove fields not required in TOSCA
340 self.props.pop(self.DESC)
341
342 # Update index to the member-vnf-index
343 self.props[self.ID] = index
344 node[self.PROPERTIES] = self.props
345
346 caps = {}
347 if len(self.mgmt_intf):
348 caps[self.MGMT_INTF] = {
349 self.PROPERTIES: self.mgmt_intf
350 }
351
352 if len(self.http_ep):
353 caps[self.HTTP_EP] = {
354 self.PROPERTIES: self.http_ep[0]
355 }
356 if len(self.http_ep) > 1:
357 self.log.warn(_("{0}: Currently only one HTTP endpoint "
358 "supported: {1}").
359 format(self, self.http_ep))
360
361 if len(self.mon_param):
362 count = 0
363 for monp in self.mon_param:
364 name = "{}_{}".format(self.MON_PARAM, count)
365 caps[name] = {self.PROPERTIES: monp}
366 count += 1
367
368 node[self.CAPABILITIES] = caps
369
370 if len(self.vdus):
371 reqs = []
372 for vdu in self.vdus:
373 reqs.append({'vdus': {self.NODE: vdu.get_name(self.name)}})
374
375 node[self.REQUIREMENTS] = reqs
376 else:
377 self.log.warn(_("{0}, Did not find any VDUS with this VNF").
378 format(self))
379
380 self.log.debug(_("{0}, VNF node: {1}").format(self, node))
381
382 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
383
384 return tosca
385
386 def get_supporting_files(self):
387 files = []
388
389 for vdu in self.vdus:
390 f = vdu.get_supporting_files()
391 if f and len(f):
392 files.extend(f)
393
394 return files