3 # Copyright 2016-2017 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.
24 gi
.require_version('RwVnsYang', '1.0')
25 gi
.require_version('RwDts', '1.0')
26 from gi
.repository
import (
35 from rift
.mano
.utils
.project
import (
40 from rift
.vlmgr
import (
46 from rift
.topmgr
import (
47 NwtopStaticDtsHandler
,
48 NwtopDiscoveryDtsHandler
,
54 class SdnInterfaceError(Exception):
55 """ SDN interface creation Error """
59 class SdnPluginError(Exception):
60 """ SDN plugin creation Error """
64 class VlRecordError(Exception):
65 """ Vlr Record creation Error """
69 class VlRecordNotFound(Exception):
70 """ Vlr Record not found"""
73 class SdnAccountError(Exception):
74 """ Error while creating/deleting/updating SDN Account"""
77 class SdnAccountNotFound(Exception):
80 class SDNAccountDtsOperdataHandler(object):
81 def __init__(self
, dts
, log
, loop
, parent
):
86 self
._project
= self
._parent
._project
90 def _register_show_status(self
):
91 def get_xpath(sdn_name
=None):
92 return self
._project
.add_project("D,/rw-sdn:sdn/rw-sdn:account{}" \
93 "/rw-sdn:connection-status".format(
94 "[name='%s']" % sdn_name
if sdn_name
is not None else ''
98 def on_prepare(xact_info
, action
, ks_path
, msg
):
99 path_entry
= RwSdnYang
.SDNAccountConfig
.schema().keyspec_to_entry(ks_path
)
100 sdn_account_name
= path_entry
.key00
.name
101 self
._log
.debug("Got show sdn connection status request: %s", ks_path
.create_string())
104 saved_accounts
= self
._parent
._acctmgr
.get_saved_sdn_accounts(sdn_account_name
)
105 for account
in saved_accounts
:
106 sdn_acct
= RwSdnYang
.SDNAccountConfig()
107 sdn_acct
.from_dict(account
.as_dict())
109 self
._log
.debug("Responding to sdn connection status request: %s", sdn_acct
.connection_status
)
110 xact_info
.respond_xpath(
111 rwdts
.XactRspCode
.MORE
,
112 xpath
=get_xpath(account
.name
),
113 msg
=sdn_acct
.connection_status
,
115 except KeyError as e
:
116 self
._log
.warning(str(e
))
117 xact_info
.respond_xpath(rwdts
.XactRspCode
.NA
)
120 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
122 self
._regh
= yield from self
._dts
.register(
124 handler
=rift
.tasklets
.DTS
.RegistrationHandler(
125 on_prepare
=on_prepare
),
126 flags
=rwdts
.Flag
.PUBLISHER
,
129 def _register_validate_rpc(self
):
131 return "/rw-sdn:update-sdn-status"
134 def on_prepare(xact_info
, action
, ks_path
, msg
):
136 if not self
._project
.rpc_check(msg
, xact_info
=xact_info
):
139 if not msg
.has_field("sdn_account"):
140 raise SdnAccountNotFound("SDN account name not provided")
142 sdn_account_name
= msg
.sdn_account
143 account
= self
._parent
._acctmgr
.get_sdn_account(sdn_account_name
)
145 self
._log
.warning("SDN account %s does not exist", sdn_account_name
)
146 xact_info
.respond_xpath(rwdts
.XactRspCode
.NA
)
149 self
._parent
._acctmgr
.start_validate_credentials(self
._loop
, sdn_account_name
)
151 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
153 self
._rpch
= yield from self
._dts
.register(
155 handler
=rift
.tasklets
.DTS
.RegistrationHandler(
156 on_prepare
=on_prepare
158 flags
=rwdts
.Flag
.PUBLISHER
,
163 yield from self
._register
_show
_status
()
164 yield from self
._register
_validate
_rpc
()
166 def deregister(self
):
167 self
._log
.debug("De-register SDN opdata handler for project {}".
168 format(self
._project
.name
))
170 self
._regh
.deregister()
174 self
._rpch
.deregister()
178 class SDNAccountDtsHandler(object):
179 XPATH
= "C,/rw-sdn:sdn/rw-sdn:account"
181 def __init__(self
, dts
, log
, parent
):
184 self
._parent
= parent
185 self
._project
= parent
._project
187 self
._sdn
_account
= {}
192 return self
._project
.add_project(SDNAccountDtsHandler
.XPATH
)
194 def _set_sdn_account(self
, account
):
195 self
._log
.info("Setting sdn account: {}".format(account
))
196 if account
.name
in self
._sdn
_account
:
197 self
._log
.error("SDN Account with name %s already exists. Ignoring config", account
.name
);
198 self
._sdn
_account
[account
.name
] = account
199 self
._parent
._acctmgr
.set_sdn_account(account
)
201 def _del_sdn_account(self
, account_name
):
202 self
._log
.info("Deleting sdn account: {}".format(account_name
))
203 del self
._sdn
_account
[account_name
]
205 self
._parent
._acctmgr
.del_sdn_account(account_name
)
207 def _update_sdn_account(self
, account
):
208 self
._log
.info("Updating sdn account: {}".format(account
))
209 # No need to update locally saved sdn_account's updated fields, as they
210 # are not used anywhere. Call the parent's update callback.
211 self
._parent
._acctmgr
.update_sdn_account(account
)
215 def apply_config(dts
, acg
, xact
, action
, _
):
216 self
._log
.debug("Got sdn account apply config (xact: %s) (action: %s)", xact
, action
)
217 if action
== rwdts
.AppconfAction
.INSTALL
and xact
.id is None:
218 self
._log
.debug("No xact handle. Skipping apply config")
219 return RwTypes
.RwStatus
.SUCCESS
221 return RwTypes
.RwStatus
.SUCCESS
224 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
225 """ Prepare callback from DTS for SDN Account config """
227 self
._log
.info("SDN Cloud account config received: %s", msg
)
229 fref
= ProtobufC
.FieldReference
.alloc()
230 fref
.goto_whole_message(msg
.to_pbcm())
232 if fref
.is_field_deleted():
233 # Delete the sdn account record
234 self
._del
_sdn
_account
(msg
.name
)
236 # If the account already exists, then this is an update.
237 if msg
.name
in self
._sdn
_account
:
238 self
._log
.debug("SDN account already exists. Invoking on_prepare update request")
239 if msg
.has_field("account_type"):
240 errmsg
= "Cannot update SDN account's account-type."
241 self
._log
.error(errmsg
)
242 xact_info
.send_error_xpath(RwTypes
.RwStatus
.FAILURE
,
245 raise SdnAccountError(errmsg
)
247 # Update the sdn account record
248 self
._update
_sdn
_account
(msg
)
250 self
._log
.debug("SDN account does not already exist. Invoking on_prepare add request")
251 if not msg
.has_field('account_type'):
252 errmsg
= "New SDN account must contain account-type field."
253 self
._log
.error(errmsg
)
254 xact_info
.send_error_xpath(RwTypes
.RwStatus
.FAILURE
,
257 raise SdnAccountError(errmsg
)
259 # Set the sdn account record
260 self
._set
_sdn
_account
(msg
)
262 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
265 self
._log
.debug("Registering for Sdn Account config using xpath: {}".
268 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
269 on_apply
=apply_config
,
272 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
273 self
._regh
= acg
.register(
275 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY
,
276 on_prepare
=on_prepare
279 def deregister(self
):
280 self
._log
.debug("De-register VLR handler for project {}".
281 format(self
._project
.name
))
283 self
._regh
.deregister()
287 class VnsManager(object):
288 """ The Virtual Network Service Manager """
289 def __init__(self
, dts
, log
, log_hdl
, loop
, project
):
292 self
._log
_hdl
= log_hdl
294 self
._project
= project
296 self
._vlr
_handler
= VlrDtsHandler(dts
, log
, loop
, self
)
297 self
._vld
_handler
= VldDtsHandler(dts
, log
, loop
, self
)
298 self
._sdn
_handler
= SDNAccountDtsHandler(dts
,log
,self
)
299 self
._sdn
_opdata
_handler
= SDNAccountDtsOperdataHandler(dts
,log
, loop
, self
)
300 self
._acctmgr
= SdnAccountMgr(self
._log
, self
._log
_hdl
, self
._loop
, self
._project
)
301 self
._nwtopdata
_store
= NwtopDataStore(log
)
302 self
._nwtopdiscovery
_handler
= NwtopDiscoveryDtsHandler(dts
, log
, loop
, self
._project
,
303 self
._acctmgr
, self
._nwtopdata
_store
)
304 self
._nwtopstatic
_handler
= NwtopStaticDtsHandler(dts
, log
, loop
, self
._project
,
305 self
._acctmgr
, self
._nwtopdata
_store
)
309 def register_vlr_handler(self
):
310 """ Register vlr DTS handler """
311 self
._log
.debug("Registering DTS VLR handler")
312 yield from self
._vlr
_handler
.register()
315 def register_vld_handler(self
):
316 """ Register vlr DTS handler """
317 self
._log
.debug("Registering DTS VLD handler")
318 yield from self
._vld
_handler
.register()
321 def register_sdn_handler(self
):
322 """ Register vlr DTS handler """
323 self
._log
.debug("Registering SDN Account config handler")
324 yield from self
._sdn
_handler
.register()
325 yield from self
._sdn
_opdata
_handler
.register()
328 def register_nwtopstatic_handler(self
):
329 """ Register static NW topology DTS handler """
330 self
._log
.debug("Registering static DTS NW topology handler")
331 yield from self
._nwtopstatic
_handler
.register()
334 def register_nwtopdiscovery_handler(self
):
335 """ Register discovery-based NW topology DTS handler """
336 self
._log
.debug("Registering discovery-based DTS NW topology handler")
337 yield from self
._nwtopdiscovery
_handler
.register()
341 """ Register all static DTS handlers"""
342 yield from self
.register_sdn_handler()
343 yield from self
.register_vlr_handler()
344 yield from self
.register_vld_handler()
345 yield from self
.register_nwtopstatic_handler()
347 yield from self
.register_nwtopdiscovery_handler()
349 def deregister(self
):
350 self
._nwtopdiscovery
_handler
.deregister()
351 self
._nwtopstatic
_handler
.deregister()
352 self
._vld
_handler
.deregister()
353 self
._vlr
_handler
.deregister()
354 self
._sdn
_opdata
_handler
.deregister()
355 self
._sdn
_handler
.deregister()
357 def create_vlr(self
, msg
):
359 if msg
.id in self
._vlrs
:
360 err
= "Vlr id %s already exists" % msg
.id
362 # raise VlRecordError(err)
363 return self
._vlrs
[msg
.id]
365 self
._log
.info("Creating VirtualLinkRecord %s", msg
.id)
366 self
._vlrs
[msg
.id] = VirtualLinkRecord(self
._dts
,
373 return self
._vlrs
[msg
.id]
375 def get_vlr(self
, vlr_id
):
376 """ Get VLR by vlr id """
377 return self
._vlrs
[vlr_id
]
380 def delete_vlr(self
, vlr_id
, xact
):
381 """ Delete VLR with the passed id"""
382 if vlr_id
not in self
._vlrs
:
383 err
= "Delete Failed - Vlr id %s not found" % vlr_id
385 raise VlRecordNotFound(err
)
387 self
._log
.info("Deleting virtual link id %s", vlr_id
)
388 yield from self
._vlrs
[vlr_id
].terminate(xact
)
389 del self
._vlrs
[vlr_id
]
390 self
._log
.info("Deleted virtual link id %s", vlr_id
)
392 def find_vlr_by_vld_id(self
, vld_id
):
393 """ Find a VLR matching the VLD Id """
394 for vlr
in self
._vlrs
.values():
395 if vlr
.vld_id
== vld_id
:
401 """ Run this VNSM instance """
402 self
._log
.debug("Run VNSManager - registering static DTS handlers")
403 yield from self
.register()
405 def vld_in_use(self
, vld_id
):
406 """ Is this VLD in use """
410 def publish_vlr(self
, xact
, xpath
, msg
):
411 """ Publish a VLR """
412 path
= self
._project
.add_project(xpath
)
413 self
._log
.debug("Publish vlr called with path %s, msg %s",
415 yield from self
._vlr
_handler
.update(xact
, path
, msg
)
418 def unpublish_vlr(self
, xact
, xpath
):
419 """ Publish a VLR """
420 path
= self
._project
.add_project(xpath
)
421 self
._log
.debug("Unpublish vlr called with path %s", path
)
422 yield from self
._vlr
_handler
.delete(xact
, path
)
425 class VnsProject(ManoProject
):
427 def __init__(self
, name
, tasklet
, **kw
):
428 super(VnsProject
, self
).__init
__(tasklet
.log
, name
)
431 self
._vlr
_handler
= None
433 # A mapping of instantiated vlr_id's to VirtualLinkRecord objects
438 self
._vnsm
= VnsManager(dts
=self
._dts
,
440 log_hdl
=self
._log
_hdl
,
443 yield from self
._vnsm
.run()
445 # NSM needs to detect VLD deletion that has active VLR
446 # self._vld_handler = VldDescriptorConfigDtsHandler(
447 # self._dts, self.log, self.loop, self._vlrs,
449 # yield from self._vld_handler.register()
451 def deregister(self
):
452 self
._log
.debug("De-register project {}".format(self
.name
))
453 self
._vnsm
.deregister()
456 class VnsTasklet(rift
.tasklets
.Tasklet
):
457 """ The VNS tasklet class """
458 def __init__(self
, *args
, **kwargs
):
459 super(VnsTasklet
, self
).__init
__(*args
, **kwargs
)
460 self
.rwlog
.set_category("rw-mano-log")
461 self
.rwlog
.set_subcategory("vns")
464 self
._project
_handler
= None
472 super(VnsTasklet
, self
).start()
473 self
.log
.info("Starting VnsTasklet")
475 self
.log
.debug("Registering with dts")
476 self
._dts
= rift
.tasklets
.DTS(self
.tasklet_info
,
477 RwVnsYang
.get_schema(),
479 self
.on_dts_state_change
)
481 self
.log
.debug("Created DTS Api GI Object: %s", self
._dts
)
483 def on_instance_started(self
):
484 """ The task instance started callback"""
485 self
.log
.debug("Got instance started callback")
491 print("Caught Exception in VNS stop:", sys
.exc_info()[0])
496 """ task init callback"""
497 self
.log
.debug("creating project handler")
498 self
.project_handler
= ProjectHandler(self
, VnsProject
)
499 self
.project_handler
.register()
503 """ tasklet run callback """
507 def on_dts_state_change(self
, state
):
508 """Take action according to current dts state to transition
509 application into the corresponding application state
512 state - current dts state
515 rwdts
.State
.INIT
: rwdts
.State
.REGN_COMPLETE
,
516 rwdts
.State
.CONFIG
: rwdts
.State
.RUN
,
520 rwdts
.State
.INIT
: self
.init
,
521 rwdts
.State
.RUN
: self
.run
,
524 # Transition application to next state
525 handler
= handlers
.get(state
, None)
526 if handler
is not None:
529 # Transition dts to next state
530 next_state
= switch
.get(state
, None)
531 if next_state
is not None:
532 self
._dts
.handle
.set_state(next_state
)