3 # Copyright 2016 RIFT.IO Inc
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
20 from gi
.repository
import (
27 from gi
.repository
.RwTypes
import RwStatus
31 class SdnGetPluginError(Exception):
32 """ Error while fetching SDN plugin """
36 class SdnGetInterfaceError(Exception):
37 """ Error while fetching SDN interface"""
41 class SdnAccountError(Exception):
42 """ Error while creating/deleting/updating SDN Account"""
45 class VnffgrDoesNotExist(Exception):
46 """ Error while fetching SDN interface"""
49 class VnffgrAlreadyExist(Exception):
50 """ Vnffgr already exists Error"""
53 class VnffgrCreationFailed(Exception):
54 """ Error while creating VNFFGR"""
58 class VnffgrUpdateFailed(Exception):
59 """ Error while updating VNFFGR"""
62 class VnffgMgr(object):
63 """ Implements the interface to backend plugins to fetch topology """
64 def __init__(self
, dts
, log
, log_hdl
, loop
):
68 self
._log
_hdl
= log_hdl
71 self
._sdn
_handler
= SDNAccountDtsHandler(self
._dts
,self
._log
,self
)
72 self
._vnffgr
_list
= {}
76 yield from self
._sdn
_handler
.register()
78 def set_sdn_account(self
,account
):
79 if (account
.name
in self
._account
):
80 self
._log
.error("SDN Account is already set")
82 sdn_account
= RwsdnYang
.SDNAccount()
83 sdn_account
.from_dict(account
.as_dict())
84 sdn_account
.name
= account
.name
85 self
._account
[account
.name
] = sdn_account
86 self
._log
.debug("Account set is %s , %s",type(self
._account
), self
._account
)
88 def del_sdn_account(self
, name
):
89 self
._log
.debug("Account deleted is %s , %s", type(self
._account
), name
)
90 del self
._account
[name
]
92 def update_sdn_account(self
,account
):
93 self
._log
.debug("Account updated is %s , %s", type(self
._account
), account
)
94 if account
.name
in self
._account
:
95 sdn_account
= self
._account
[account
.name
]
97 sdn_account
.from_dict(
99 ignore_missing_keys
=True,
101 self
._account
[account
.name
] = sdn_account
103 def get_sdn_account(self
, name
):
105 Creates an object for class RwsdnYang.SdnAccount()
107 if (name
in self
._account
):
108 return self
._account
[name
]
110 self
._log
.error("SDN account is not configured")
113 def get_sdn_plugin(self
,name
):
115 Loads rw.sdn plugin via libpeas
117 if (name
in self
._sdn
):
118 return self
._sdn
[name
]
119 account
= self
.get_sdn_account(name
)
120 plugin_name
= getattr(account
, account
.account_type
).plugin_name
121 self
._log
.debug("SDN plugin being created")
122 plugin
= rw_peas
.PeasPlugin(plugin_name
, 'RwSdn-1.0')
123 engine
, info
, extension
= plugin()
125 self
._sdn
[name
] = plugin
.get_interface("Topology")
127 rc
= self
._sdn
[name
].init(self
._log
_hdl
)
128 assert rc
== RwStatus
.SUCCESS
130 self
._log
.error("ERROR:SDN plugin instantiation failed ")
132 self
._log
.debug("SDN plugin successfully instantiated")
133 return self
._sdn
[name
]
135 def fetch_vnffgr(self
,vnffgr_id
):
136 if vnffgr_id
not in self
._vnffgr
_list
:
137 self
._log
.error("VNFFGR with id %s not present in VNFFGMgr", vnffgr_id
)
138 msg
= "VNFFGR with id {} not present in VNFFGMgr".format(vnffgr_id
)
139 raise VnffgrDoesNotExist(msg
)
140 self
.update_vnffgrs(self
._vnffgr
_list
[vnffgr_id
].sdn_account
)
141 vnffgr
= self
._vnffgr
_list
[vnffgr_id
].deep_copy()
142 self
._log
.debug("VNFFGR for id %s is %s",vnffgr_id
,vnffgr
)
145 def create_vnffgr(self
, vnffgr
,classifier_list
,sff_list
):
148 self
._log
.debug("Received VNFFG chain Create msg %s",vnffgr
)
149 if vnffgr
.id in self
._vnffgr
_list
:
150 self
._log
.error("VNFFGR with id %s already present in VNFFGMgr", vnffgr
.id)
151 vnffgr
.operational_status
= 'failed'
152 msg
= "VNFFGR with id {} already present in VNFFGMgr".format(vnffgr
.id)
153 raise VnffgrAlreadyExist(msg
)
155 self
._vnffgr
_list
[vnffgr
.id] = vnffgr
156 vnffgr
.operational_status
= 'init'
157 if len(self
._account
) == 0:
158 self
._log
.error("SDN Account not configured")
159 vnffgr
.operational_status
= 'failed'
161 if vnffgr
.sdn_account
:
162 sdn_acct_name
= vnffgr
.sdn_account
164 self
._log
.error("SDN Account is not associated to create VNFFGR")
165 # TODO Fail the VNFFGR creation if SDN account is not associated
166 #vnffgr.operational_status = 'failed'
167 #msg = "SDN Account is not associated to create VNFFGR"
168 #raise VnffgrCreationFailed(msg)
169 sdn_account
= [sdn_account
.name
for _
,sdn_account
in self
._account
.items()]
170 sdn_acct_name
= sdn_account
[0]
171 vnffgr
.sdn_account
= sdn_acct_name
172 sdn_plugin
= self
.get_sdn_plugin(sdn_acct_name
)
174 for rsp
in vnffgr
.rsp
:
175 vnffg
= RwsdnYang
.VNFFGChain()
176 vnffg
.name
= rsp
.name
177 vnffg
.classifier_name
= rsp
.classifier_name
180 for index
,cp_ref
in enumerate(rsp
.vnfr_connection_point_ref
):
181 cpath
= vnffg
.vnf_chain_path
.add()
182 cpath
.order
=cp_ref
.hop_number
183 cpath
.service_function_type
= cp_ref
.service_function_type
185 cpath
.transport_type
= 'vxlan-gpe'
187 vnfr
=cpath
.vnfr_ids
.add()
188 vnfr
.vnfr_id
= cp_ref
.vnfr_id_ref
189 vnfr
.vnfr_name
= cp_ref
.vnfr_name_ref
190 vnfr
.mgmt_address
= cp_ref
.connection_point_params
.mgmt_address
191 vnfr
.mgmt_port
= 5000
192 vnfr_list
.append(vnfr
)
194 vdu
= vnfr
.vdu_list
.add()
195 vdu
.name
= cp_ref
.connection_point_params
.name
196 vdu
.port_id
= cp_ref
.connection_point_params
.port_id
197 vdu
.vm_id
= cp_ref
.connection_point_params
.vm_id
198 vdu
.address
= cp_ref
.connection_point_params
.address
199 vdu
.port
= cp_ref
.connection_point_params
.port
201 for sff
in sff_list
.values():
202 _sff
= vnffg
.sff
.add()
203 _sff
.from_dict(sff
.as_dict())
204 if sff
.function_type
== 'SFF':
205 for vnfr
in vnfr_list
:
206 vnfr
.sff_name
= sff
.name
207 self
._log
.debug("Recevied SFF %s, Created SFF is %s",sff
, _sff
)
209 self
._log
.debug("VNFFG chain msg is %s",vnffg
)
210 rc
,rs
= sdn_plugin
.create_vnffg_chain(self
._account
[sdn_acct_name
],vnffg
)
211 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
212 vnffgr
.operational_status
= 'failed'
213 msg
= "Instantiation of VNFFGR with id {} failed".format(vnffgr
.id)
214 raise VnffgrCreationFailed(msg
)
216 self
._log
.info("VNFFG chain created successfully for rsp with id %s",rsp
.id)
220 if(len(classifier_list
) == 2):
221 meta
[vnffgr
.classifier
[0].id] = '0x' + ''.join(str("%0.2X"%int
(i
)) for i
in vnffgr
.classifier
[1].ip_address
.split('.'))
222 meta
[vnffgr
.classifier
[1].id] = '0x' + ''.join(str("%0.2X"%int
(i
)) for i
in vnffgr
.classifier
[0].ip_address
.split('.'))
224 self
._log
.debug("VNFFG Meta VNFFG chain is {}".format(meta
))
226 for classifier
in classifier_list
:
227 vnffgr_cl
= [_classifier
for _classifier
in vnffgr
.classifier
if classifier
.id == _classifier
.id]
228 if len(vnffgr_cl
) > 0:
229 cl_rsp_name
= vnffgr_cl
[0].rsp_name
231 self
._log
.error("No RSP wiht name %s found; Skipping classifier %s creation",classifier
.rsp_id_ref
,classifier
.name
)
233 vnffgcl
= RwsdnYang
.VNFFGClassifier()
234 vnffgcl
.name
= classifier
.name
235 vnffgcl
.rsp_name
= cl_rsp_name
236 vnffgcl
.port_id
= vnffgr_cl
[0].port_id
237 vnffgcl
.vm_id
= vnffgr_cl
[0].vm_id
238 # Get the symmetric classifier endpoint ip and set it in nsh ctx1
240 vnffgcl
.vnffg_metadata
.ctx1
= meta
.get(vnffgr_cl
[0].id,'0')
241 vnffgcl
.vnffg_metadata
.ctx2
= '0'
242 vnffgcl
.vnffg_metadata
.ctx3
= '0'
243 vnffgcl
.vnffg_metadata
.ctx4
= '0'
244 if vnffgr_cl
[0].has_field('sff_name'):
245 vnffgcl
.sff_name
= vnffgr_cl
[0].sff_name
246 for index
,match_rule
in enumerate(classifier
.match_attributes
):
247 acl
= vnffgcl
.match_attributes
.add()
248 #acl.name = vnffgcl.name + str(index)
249 acl
.name
= match_rule
.id
250 acl
.ip_proto
= match_rule
.ip_proto
251 acl
.source_ip_address
= match_rule
.source_ip_address
+ '/32'
252 acl
.source_port
= match_rule
.source_port
253 acl
.destination_ip_address
= match_rule
.destination_ip_address
+ '/32'
254 acl
.destination_port
= match_rule
.destination_port
256 self
._log
.debug(" Creating VNFFG Classifier Classifier %s for RSP: %s",vnffgcl
.name
,vnffgcl
.rsp_name
)
257 rc
,rs
= sdn_plugin
.create_vnffg_classifier(self
._account
[sdn_acct_name
],vnffgcl
)
258 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
259 self
._log
.error("VNFFG Classifier cretaion failed for Classifier %s for RSP ID: %s",classifier
.name
,classifier
.rsp_id_ref
)
260 #vnffgr.operational_status = 'failed'
261 #msg = "Instantiation of VNFFGR with id {} failed".format(vnffgr.id)
262 #raise VnffgrCreationFailed(msg)
264 vnffgr
.operational_status
= 'running'
265 self
.update_vnffgrs(vnffgr
.sdn_account
)
268 def update_vnffgrs(self
,sdn_acct_name
):
270 Update VNFFGR by reading data from SDN Plugin
272 sdn_plugin
= self
.get_sdn_plugin(sdn_acct_name
)
273 rc
,rs
= sdn_plugin
.get_vnffg_rendered_paths(self
._account
[sdn_acct_name
])
274 if rc
!= RwTypes
.RwStatus
.SUCCESS
:
275 msg
= "Reading of VNFFGR from SDN Plugin failed"
276 raise VnffgrUpdateFailed(msg
)
278 vnffgr_list
= [_vnffgr
for _vnffgr
in self
._vnffgr
_list
.values() if _vnffgr
.sdn_account
== sdn_acct_name
and _vnffgr
.operational_status
== 'running']
280 for _vnffgr
in vnffgr_list
:
281 for _vnffgr_rsp
in _vnffgr
.rsp
:
282 vnffg_rsp_list
= [vnffg_rsp
for vnffg_rsp
in rs
.vnffg_rendered_path
if vnffg_rsp
.name
== _vnffgr_rsp
.name
]
283 if vnffg_rsp_list
is not None and len(vnffg_rsp_list
) > 0:
284 vnffg_rsp
= vnffg_rsp_list
[0]
285 if len(vnffg_rsp
.rendered_path_hop
) != len(_vnffgr_rsp
.vnfr_connection_point_ref
):
286 _vnffgr
.operational_status
= 'failed'
287 self
._log
.error("Received hop count %d doesnt match the VNFFGD hop count %d", len(vnffg_rsp
.rendered_path_hop
),
288 len(_vnffgr_rsp
.vnfr_connection_point_ref
))
289 msg
= "Fetching of VNFFGR with id {} failed".format(_vnffgr
.id)
290 raise VnffgrUpdateFailed(msg
)
291 _vnffgr_rsp
.path_id
= vnffg_rsp
.path_id
292 for index
, rendered_hop
in enumerate(vnffg_rsp
.rendered_path_hop
):
293 for vnfr_cp_ref
in _vnffgr_rsp
.vnfr_connection_point_ref
:
294 if rendered_hop
.vnfr_name
== vnfr_cp_ref
.vnfr_name_ref
:
295 vnfr_cp_ref
.hop_number
= rendered_hop
.hop_number
296 vnfr_cp_ref
.service_index
= rendered_hop
.service_index
297 vnfr_cp_ref
.service_function_forwarder
.name
= rendered_hop
.service_function_forwarder
.name
298 vnfr_cp_ref
.service_function_forwarder
.ip_address
= rendered_hop
.service_function_forwarder
.ip_address
299 vnfr_cp_ref
.service_function_forwarder
.port
= rendered_hop
.service_function_forwarder
.port
301 _vnffgr
.operational_status
= 'failed'
302 self
._log
.error("VNFFGR RSP with name %s in VNFFG %s not found",_vnffgr_rsp
.name
, _vnffgr
.id)
303 msg
= "Fetching of VNFFGR with name {} failed".format(_vnffgr_rsp
.name
)
304 raise VnffgrUpdateFailed(msg
)
307 def terminate_vnffgr(self
,vnffgr_id
,sdn_account_name
= None):
309 Deletet the VNFFG chain
311 if vnffgr_id
not in self
._vnffgr
_list
:
312 self
._log
.error("VNFFGR with id %s not present in VNFFGMgr during termination", vnffgr_id
)
313 msg
= "VNFFGR with id {} not present in VNFFGMgr during termination".format(vnffgr_id
)
315 #raise VnffgrDoesNotExist(msg)
316 self
._log
.info("Received VNFFG chain terminate for id %s",vnffgr_id
)
317 if sdn_account_name
is None:
318 sdn_account
= [sdn_account
.name
for _
,sdn_account
in self
._account
.items()]
319 sdn_account_name
= sdn_account
[0]
320 sdn_plugin
= self
.get_sdn_plugin(sdn_account_name
)
321 sdn_plugin
.terminate_vnffg_chain(self
._account
[sdn_account_name
],vnffgr_id
)
322 sdn_plugin
.terminate_vnffg_classifier(self
._account
[sdn_account_name
],vnffgr_id
)
323 del self
._vnffgr
_list
[vnffgr_id
]
325 class SDNAccountDtsHandler(object):
326 XPATH
= "C,/rw-sdn:sdn/rw-sdn:account"
328 def __init__(self
, dts
, log
, parent
):
331 self
._parent
= parent
333 self
._sdn
_account
= {}
335 def _set_sdn_account(self
, account
):
336 self
._log
.info("Setting sdn account: {}".format(account
))
337 if account
.name
in self
._sdn
_account
:
338 self
._log
.error("SDN Account with name %s already exists. Ignoring config", account
.name
);
339 self
._sdn
_account
[account
.name
] = account
340 self
._parent
.set_sdn_account(account
)
342 def _del_sdn_account(self
, account_name
):
343 self
._log
.info("Deleting sdn account: {}".format(account_name
))
344 del self
._sdn
_account
[account_name
]
346 self
._parent
.del_sdn_account(account_name
)
348 def _update_sdn_account(self
, account
):
349 self
._log
.info("Updating sdn account: {}".format(account
))
350 # No need to update locally saved sdn_account's updated fields, as they
351 # are not used anywhere. Call the parent's update callback.
352 self
._parent
.update_sdn_account(account
)
356 def apply_config(dts
, acg
, xact
, action
, _
):
357 self
._log
.debug("Got sdn account apply config (xact: %s) (action: %s)", xact
, action
)
358 if action
== rwdts
.AppconfAction
.INSTALL
and xact
.id is None:
359 self
._log
.debug("No xact handle. Skipping apply config")
360 return RwTypes
.RwStatus
.SUCCESS
362 return RwTypes
.RwStatus
.SUCCESS
365 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
366 """ Prepare callback from DTS for SDN Account config """
368 self
._log
.info("SDN Cloud account config received: %s", msg
)
370 fref
= ProtobufC
.FieldReference
.alloc()
371 fref
.goto_whole_message(msg
.to_pbcm())
373 if fref
.is_field_deleted():
374 # Delete the sdn account record
375 self
._del
_sdn
_account
(msg
.name
)
377 # If the account already exists, then this is an update.
378 if msg
.name
in self
._sdn
_account
:
379 self
._log
.debug("SDN account already exists. Invoking on_prepare update request")
380 if msg
.has_field("account_type"):
381 errmsg
= "Cannot update SDN account's account-type."
382 self
._log
.error(errmsg
)
383 xact_info
.send_error_xpath(RwTypes
.RwStatus
.FAILURE
,
384 SDNAccountDtsHandler
.XPATH
,
386 raise SdnAccountError(errmsg
)
388 # Update the sdn account record
389 self
._update
_sdn
_account
(msg
)
391 self
._log
.debug("SDN account does not already exist. Invoking on_prepare add request")
392 if not msg
.has_field('account_type'):
393 errmsg
= "New SDN account must contain account-type field."
394 self
._log
.error(errmsg
)
395 xact_info
.send_error_xpath(RwTypes
.RwStatus
.FAILURE
,
396 SDNAccountDtsHandler
.XPATH
,
398 raise SdnAccountError(errmsg
)
400 # Set the sdn account record
401 self
._set
_sdn
_account
(msg
)
403 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
406 self
._log
.debug("Registering for Sdn Account config using xpath: %s",
407 SDNAccountDtsHandler
.XPATH
,
410 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
411 on_apply
=apply_config
,
414 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
416 xpath
=SDNAccountDtsHandler
.XPATH
,
417 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY
,
418 on_prepare
=on_prepare