1 # Copyright 2016 RIFT.io Inc
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 from copy
import deepcopy
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 \
22 from rift
.mano
.yang_translator
.rwmano
.yang
.yang_vdu
import YangVdu
24 TARGET_CLASS_NAME
= 'YangVnfd'
27 class YangVnfd(ToscaResource
):
28 '''Class for RIFT.io YANG VNF descriptor translation to TOSCA type.'''
32 CONFIG_TYPES
= ['script', 'netconf', 'rest', 'juju']
34 OTHER_KEYS
= (MGMT_INTF
, HTTP_EP
, MON_PARAM
) = \
35 ('mgmt_interface', 'http_endpoint', 'monitoring_param')
43 super(YangVnfd
, self
).__init
__(log
,
53 def handle_yang(self
):
54 self
.log
.debug(_("Process VNFD desc {0}: {1}").format(self
.name
,
57 def process_vnf_config(conf
):
59 if self
.CONFIG_ATTR
in conf
:
60 for key
, value
in conf
.pop(self
.CONFIG_ATTR
).items():
63 if self
.CONFIG_TMPL
in conf
:
64 vnf_conf
[self
.CONFIG_TMPL
] = conf
.pop(self
.CONFIG_TMPL
)
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
70 for key
in self
.CONFIG_TYPES
:
72 copy_config_details(key
, conf
.pop(key
))
76 self
.log
.warn(_("{0}, Did not process all in VNF "
79 self
.log
.debug(_("{0}, vnf config: {1}").format(self
, vnf_conf
))
80 self
.props
[self
.VNF_CONFIG
] = vnf_conf
82 def process_mgmt_intf(intf
):
83 if len(self
.mgmt_intf
) > 0:
84 err_msg(_("{0}, Already processed another mgmt intf {1}, "
86 format(self
, self
.msmg_intf
, intf
))
87 self
.log
.error(err_msg
)
88 raise ValidationError(message
=err_msg
)
90 self
.mgmt_intf
['protocol'] = 'tcp'
93 self
.mgmt_intf
[self
.PORT
] = intf
.pop(self
.PORT
)
94 self
.props
[self
.PORT
] = self
.mgmt_intf
[self
.PORT
]
98 if intf
['vdu_id'] == vdu
.id:
99 self
.mgmt_intf
[self
.VDU
] = vdu
.get_name(self
.name
)
103 if self
.DASHBOARD_PARAMS
in intf
:
104 self
.mgmt_intf
[self
.DASHBOARD_PARAMS
] = \
105 intf
.pop(self
.DASHBOARD_PARAMS
)
108 self
.log
.warn(_("{0}, Did not process all in mgmt "
111 self
.log
.debug(_("{0}, Management interface: {1}").
112 format(self
, self
.mgmt_intf
))
114 def process_http_ep(eps
):
115 self
.log
.debug("{}, HTTP EP: {}".format(self
, 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
)
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
)
128 def process_mon_param(params
):
131 fields
= [self
.NAME
, self
.ID
, 'value_type', 'units', 'group_tag',
132 'json_query_method', 'http_endpoint_ref', 'widget_type',
136 monp
[key
] = param
.pop(key
)
139 self
.log
.warn(_("{0}, Did not process the following for "
140 "monitporing-param {1}").
142 self
.log
.debug(_("{0}, Monitoring param: {1}").format(self
, monp
))
143 self
.mon_param
.append(monp
)
147 self
.log
.debug("{}, CP: {}".format(self
, cp_dic
))
148 name
= cp_dic
.pop(self
.NAME
)
149 for vdu
in self
.vdus
:
151 vdu
.set_cp_type(name
, cp_dic
.pop(self
.TYPE_Y
))
154 self
.log
.warn(_("{0}, Did not process the following for "
155 "connection-point {1}: {2}").
156 format(self
, name
, cp_dic
))
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
165 dic
= deepcopy(self
.yang
)
167 for key
in self
.REQUIRED_FIELDS
:
168 self
.props
[key
] = dic
.pop(key
)
170 self
.id = self
.props
[self
.ID
]
172 # Process VDUs before CPs so as to update the CP struct in VDU
173 # when we process CP later
175 for vdu_dic
in dic
.pop(self
.VDU
):
176 vdu
= YangVdu(self
.log
, vdu_dic
.pop(self
.NAME
),
179 self
.vdus
.append(vdu
)
181 for key
in ENDPOINTS_MAP
.keys():
183 ENDPOINTS_MAP
[key
](dic
.pop(key
))
185 if self
.VNF_CONFIG
in dic
:
186 process_vnf_config(dic
.pop(self
.VNF_CONFIG
))
188 self
.remove_ignored_fields(dic
)
190 self
.log
.warn(_("{0}, Did not process the following for "
193 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.props
))
194 except Exception as e
:
195 err_msg
= _("Exception processing VNFD {0} : {1}"). \
197 self
.log
.error(err_msg
)
198 raise ValidationError(message
=err_msg
)
200 def update_cp_vld(self
, cp_name
, vld_name
):
201 for vdu
in self
.vdus
:
202 cp
= vdu
.get_cp(cp_name
)
204 vdu
.set_vld(cp_name
, vld_name
)
207 def generate_tosca_type(self
, tosca
):
208 self
.log
.debug(_("{0} Generate tosa types").
211 for vdu
in self
.vdus
:
212 tosca
= vdu
.generate_tosca_type(tosca
)
215 if self
.T_VNF_CONFIG
not in tosca
[self
.DATA_TYPES
]:
216 tosca
[self
.DATA_TYPES
][self
.T_VNF_CONFIG
] = {
219 {self
.TYPE
: self
.STRING
},
221 {self
.TYPE
: self
.INTEGER
,
223 self
.REQUIRED
: self
.NO
,
225 [{'greater_or_equal': 0}]},
227 {self
.TYPE
: self
.INTEGER
,
229 [{'greater_than': 0}]},
231 {self
.TYPE
: self
.MAP
},
233 {self
.TYPE
: self
.STRING
,
234 self
.REQUIRED
: self
.NO
},
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',
246 {self
.TYPE
: self
.INTEGER
},
248 {self
.TYPE
: self
.STRING
},
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',
256 self
.DASHBOARD_PARAMS
:
257 {self
.TYPE
: self
.MAP
},
259 {self
.TYPE
: self
.STRING
},
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',
268 {self
.TYPE
: self
.INTEGER
},
270 {self
.TYPE
: self
.STRING
},
272 {self
.TYPE
: self
.STRING
,
273 self
.DEFAULT
: 'INT'},
275 {self
.TYPE
: self
.STRING
,
276 self
.DEFAULT
: 'Group1'},
278 {self
.TYPE
: self
.STRING
},
280 {self
.TYPE
: self
.STRING
},
282 {self
.TYPE
: self
.STRING
,
283 self
.DEFAULT
: 'NAMEKEY'},
285 {self
.TYPE
: self
.STRING
},
287 {self
.TYPE
: self
.STRING
,
288 self
.DEFAULT
: 'COUNTER'},
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',
298 {self
.TYPE
: self
.T_VNF_CONFIG
},
300 {self
.TYPE
: self
.INTEGER
,
302 [{'in_range': '[1, 65535]'}]},
304 {self
.TYPE
: self
.BOOL
,
305 self
.DEFAULT
: self
.TRUE
},
309 {self
.TYPE
: self
.T_MGMT_INTF
},
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
},
319 {self
.TYPE
: 'tosca.capabilities.nfv.VirtualLinkable',
321 'tosca.relationships.nfv.VirtualLinksTo',
322 self
.NODE
: self
.T_VDU1
,
323 self
.OCCURENCES
: '[1, UNBOUND]'}}
329 def generate_vnf_template(self
, tosca
, index
):
330 self
.log
.debug(_("{0}, Generate tosca template for VNF {1}").
331 format(self
, index
, tosca
))
333 for vdu
in self
.vdus
:
334 tosca
= vdu
.generate_vdu_template(tosca
, self
.name
)
337 node
[self
.TYPE
] = self
.T_VNF1
339 # Remove fields not required in TOSCA
340 self
.props
.pop(self
.DESC
)
342 # Update index to the member-vnf-index
343 self
.props
[self
.ID
] = index
344 node
[self
.PROPERTIES
] = self
.props
347 if len(self
.mgmt_intf
):
348 caps
[self
.MGMT_INTF
] = {
349 self
.PROPERTIES
: self
.mgmt_intf
352 if len(self
.http_ep
):
353 caps
[self
.HTTP_EP
] = {
354 self
.PROPERTIES
: self
.http_ep
[0]
356 if len(self
.http_ep
) > 1:
357 self
.log
.warn(_("{0}: Currently only one HTTP endpoint "
359 format(self
, self
.http_ep
))
361 if len(self
.mon_param
):
363 for monp
in self
.mon_param
:
364 name
= "{}_{}".format(self
.MON_PARAM
, count
)
365 caps
[name
] = {self
.PROPERTIES
: monp
}
368 node
[self
.CAPABILITIES
] = caps
372 for vdu
in self
.vdus
:
373 reqs
.append({'vdus': {self
.NODE
: vdu
.get_name(self
.name
)}})
375 node
[self
.REQUIREMENTS
] = reqs
377 self
.log
.warn(_("{0}, Did not find any VDUS with this VNF").
380 self
.log
.debug(_("{0}, VNF node: {1}").format(self
, node
))
382 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][self
.name
] = node
386 def get_supporting_files(self
):
389 for vdu
in self
.vdus
:
390 f
= vdu
.get_supporting_files()