From 7b75903dd891795d4426612fd4de76c0f51cf17e Mon Sep 17 00:00:00 2001 From: Chamarty Date: Mon, 3 Apr 2017 17:26:43 -0400 Subject: [PATCH] SDN Accounts refactoring Change-Id: I1e9d8f744dab71a8b4967158f7c086e9731938ac Signed-off-by: Chamarty --- BUILD.sh | 2 + common/plugins/yang/CMakeLists.txt | 13 +- common/plugins/yang/rw-sdn.yang | 11 +- common/python/CMakeLists.txt | 10 + common/python/rift/mano/sdn/__init__.py | 30 +++ common/python/rift/mano/sdn/accounts.py | 157 +++++++++++ common/python/rift/mano/sdn/config.py | 240 +++++++++++++++++ common/python/rift/mano/sdn/operdata.py | 128 +++++++++ rwcal/plugins/yang/rwcal.yang | 56 ---- .../cli/cli_launchpad_schema_listing.txt | 2 +- .../tasklets/rwnsmtasklet/rwnsmtasklet.py | 4 +- .../rift/tasklets/rwnsmtasklet/rwvnffgmgr.py | 10 +- .../tasklets/rwvnstasklet/rwvnstasklet.py | 244 +++--------------- .../plugins/rwvns/rift/topmgr/__init__.py | 1 - .../plugins/rwvns/rift/topmgr/rwtopmgr.py | 169 +----------- .../plugins/rwvns/test/test_sdn_mock.py | 8 +- .../plugins/rwvns/test/test_sdn_odl.py | 116 +++++++++ .../plugins/rwvns/test/test_sdn_openstack.py | 111 ++++++++ .../plugins/rwvns/test/test_sdn_sim.py | 10 +- .../plugins/rwvns/test/topmgr_module_test.py | 2 +- rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt | 4 +- rwlaunchpad/plugins/rwvns/vala/rwsdn.vala | 22 +- .../rwvns/vala/rwsdn_mock/rwsdn_mock.py | 4 +- .../plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py | 9 +- .../vala/rwsdn_openstack/rwsdn_openstack.py | 6 +- .../plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py | 4 +- rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt | 4 +- .../rwvns/yang/{rwsdn.yang => rwsdnal.yang} | 8 +- rwlaunchpad/plugins/yang/rw-vns.yang | 4 +- rwlaunchpad/ra/pytest/test_launchpad.py | 12 +- 30 files changed, 918 insertions(+), 483 deletions(-) create mode 100644 common/python/rift/mano/sdn/__init__.py create mode 100644 common/python/rift/mano/sdn/accounts.py create mode 100644 common/python/rift/mano/sdn/config.py create mode 100644 common/python/rift/mano/sdn/operdata.py create mode 100644 rwlaunchpad/plugins/rwvns/test/test_sdn_odl.py create mode 100644 rwlaunchpad/plugins/rwvns/test/test_sdn_openstack.py rename rwlaunchpad/plugins/rwvns/yang/{rwsdn.yang => rwsdnal.yang} (98%) diff --git a/BUILD.sh b/BUILD.sh index 0e7804c4..40c5990b 100755 --- a/BUILD.sh +++ b/BUILD.sh @@ -210,6 +210,8 @@ if [[ $PLATFORM == ub16 ]]; then rw.core.mano-mano_yang_ylib-1.0 \ rw.core.mano-common-1.0 \ rw.core.mano-rwsdn_yang_ylib-1.0 \ + rw.core.mano-rwsdnal_yang_ylib-1.0 \ + rw.core.mano-rwsdn-1.0 \ rw.core.mano-mano-types_yang_ylib-1.0 \ rw.core.mano-rwcal-cloudsim-1.0 \ rw.core.mano-rwcal-1.0 \ diff --git a/common/plugins/yang/CMakeLists.txt b/common/plugins/yang/CMakeLists.txt index be583ed8..ed7d7b37 100644 --- a/common/plugins/yang/CMakeLists.txt +++ b/common/plugins/yang/CMakeLists.txt @@ -20,15 +20,26 @@ ## # Yang targets ## +rift_add_yang_target( + TARGET rwsdn_yang + YANG_FILES rw-sdn.yang + COMPONENT ${PKG_LONG_NAME} + LIBRARIES + rwsdnal_yang_gen + DEPENDS + rwsdnal_yang +) + rift_add_yang_target( TARGET rwcloud_yang - YANG_FILES rw-cloud.yang rw-sdn.yang + YANG_FILES rw-cloud.yang COMPONENT ${PKG_LONG_NAME} LIBRARIES rwsdn_yang_gen rwcal_yang_gen DEPENDS rwcal_yang + rwsdnal_yang ) rift_add_yang_target( diff --git a/common/plugins/yang/rw-sdn.yang b/common/plugins/yang/rw-sdn.yang index 6475cc52..04414527 100644 --- a/common/plugins/yang/rw-sdn.yang +++ b/common/plugins/yang/rw-sdn.yang @@ -28,8 +28,8 @@ module rw-sdn prefix "rwpb"; } - import rwsdn { - prefix "rwsdn"; + import rwsdnal { + prefix "rwsdnal"; } revision 2015-09-14 { @@ -38,15 +38,16 @@ module rw-sdn } container sdn { + rwpb:msg-new SDNAccountConfig; list account { - rwpb:msg-new SDNAccountConfig; + rwpb:msg-new SDNAccount; key "name"; leaf name { type string; } - uses rwsdn:sdn-provider-auth; - uses rwsdn:connection-status; + uses rwsdnal:sdn-provider-auth; + uses rwsdnal:connection-status; } } diff --git a/common/python/CMakeLists.txt b/common/python/CMakeLists.txt index 50ed1de7..8fba078f 100644 --- a/common/python/CMakeLists.txt +++ b/common/python/CMakeLists.txt @@ -36,6 +36,16 @@ rift_python_install_tree( PYTHON3_ONLY ) +rift_python_install_tree( + FILES + rift/mano/sdn/__init__.py + rift/mano/sdn/accounts.py + rift/mano/sdn/config.py + rift/mano/sdn/operdata.py + COMPONENT ${PKG_LONG_NAME} + PYTHON3_ONLY + ) + rift_python_install_tree( FILES rift/mano/config_agent/operdata.py diff --git a/common/python/rift/mano/sdn/__init__.py b/common/python/rift/mano/sdn/__init__.py new file mode 100644 index 00000000..8f858326 --- /dev/null +++ b/common/python/rift/mano/sdn/__init__.py @@ -0,0 +1,30 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .accounts import ( + SDNAccount, + SDNAccountCalError, + ) + +from .config import ( + SDNAccountConfigSubscriber, + SDNAccountConfigCallbacks + ) + +from .operdata import ( + SDNAccountDtsOperdataHandler, +) diff --git a/common/python/rift/mano/sdn/accounts.py b/common/python/rift/mano/sdn/accounts.py new file mode 100644 index 00000000..d539ead5 --- /dev/null +++ b/common/python/rift/mano/sdn/accounts.py @@ -0,0 +1,157 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import asyncio +from gi import require_version +require_version('RwTypes', '1.0') +require_version('RwsdnalYang', '1.0') +require_version('RwSdnYang', '1.0') + +from gi.repository import ( + RwTypes, + RwsdnalYang, + RwSdnYang, + ) +import rw_peas + +if sys.version_info < (3, 4, 4): + asyncio.ensure_future = asyncio.async + + +class PluginLoadingError(Exception): + pass + + +class SDNAccountCalError(Exception): + pass + + +class SDNAccount(object): + def __init__(self, log, rwlog_hdl, account_msg): + self._log = log + self._account_msg = account_msg.deep_copy() + + self._sdn_plugin = None + self._engine = None + + self._sdn = self.plugin.get_interface("Topology") + self._sdn.init(rwlog_hdl) + + self._status = RwsdnalYang.SDNAccount_ConnectionStatus( + status="unknown", + details="Connection status lookup not started" + ) + + self._validate_task = None + + @property + def plugin(self): + if self._sdn_plugin is None: + try: + self._sdn_plugin = rw_peas.PeasPlugin( + getattr(self._account_msg, self.account_type).plugin_name, + 'RwSdn-1.0', + ) + + except AttributeError as e: + raise PluginLoadingError(str(e)) + + self._engine, _, _ = self._sdn_plugin() + + return self._sdn_plugin + + def _wrap_status_fn(self, fn, *args, **kwargs): + ret = fn(*args, **kwargs) + rw_status = ret[0] + if rw_status != RwTypes.RwStatus.SUCCESS: + msg = "%s returned %s" % (fn.__name__, str(rw_status)) + self._log.error(msg) + raise SDNAccountCalError(msg) + + # If there was only one other return value besides rw_status, then just + # return that element. Otherwise return the rest of the return values + # as a list. + return ret[1] if len(ret) == 2 else ret[1:] + + @property + def sdn(self): + return self._sdn + + @property + def name(self): + return self._account_msg.name + + @property + def account_msg(self): + return self._account_msg + + @property + def sdnal_account_msg(self): + return RwsdnalYang.SDNAccount.from_dict( + self.account_msg.as_dict(), + ignore_missing_keys=True, + ) + + def sdn_account_msg(self, account_dict): + self._account_msg = RwSdnYang.SDNAccount.from_dict(account_dict) + + @property + def account_type(self): + return self._account_msg.account_type + + @property + def connection_status(self): + return self._status + + def update_from_cfg(self, cfg): + self._log.debug("Updating parent SDN Account to %s", cfg) + + raise NotImplementedError("Update SDN account not yet supported") + + + @asyncio.coroutine + def validate_sdn_account_credentials(self, loop): + self._log.debug("Validating SDN Account credentials %s", self._account_msg) + self._status = RwSdnYang.SDNAccount_ConnectionStatus( + status="validating", + details="SDN account connection validation in progress" + ) + rwstatus, status = yield from loop.run_in_executor( + None, + self._sdn.validate_sdn_creds, + self.sdnal_account_msg, + ) + if rwstatus == RwTypes.RwStatus.SUCCESS: + self._status = RwSdnYang.SDNAccount_ConnectionStatus.from_dict(status.as_dict()) + else: + self._status = RwSdnYang.SDNAccount_ConnectionStatus( + status="failure", + details="Error when calling SDNAL validate SDN creds" + ) + + self._log.info("Got SDN account validation response: %s", self._status) + + def start_validate_credentials(self, loop): + if self._validate_task is not None: + self._validate_task.cancel() + self._validate_task = None + + self._validate_task = asyncio.ensure_future( + self.validate_sdn_account_credentials(loop), + loop=loop + ) diff --git a/common/python/rift/mano/sdn/config.py b/common/python/rift/mano/sdn/config.py new file mode 100644 index 00000000..a9de01b5 --- /dev/null +++ b/common/python/rift/mano/sdn/config.py @@ -0,0 +1,240 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import asyncio + +import gi +gi.require_version('RwDts', '1.0') +import rift.tasklets + +from gi.repository import ( + RwDts as rwdts, + ProtobufC, + ) + +from . import accounts + + +class SDNAccountNotFound(Exception): + pass + + +class SDNAccountError(Exception): + pass + + +def get_add_delete_update_cfgs(dts_member_reg, xact, key_name): + # Unforunately, it is currently difficult to figure out what has exactly + # changed in this xact without Pbdelta support (RIFT-4916) + # As a workaround, we can fetch the pre and post xact elements and + # perform a comparison to figure out adds/deletes/updates + xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) + curr_cfgs = list(dts_member_reg.elements) + + xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} + curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} + + # Find Adds + added_keys = set(xact_key_map) - set(curr_key_map) + added_cfgs = [xact_key_map[key] for key in added_keys] + + # Find Deletes + deleted_keys = set(curr_key_map) - set(xact_key_map) + deleted_cfgs = [curr_key_map[key] for key in deleted_keys] + + # Find Updates + updated_keys = set(curr_key_map) & set(xact_key_map) + updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] + + return added_cfgs, deleted_cfgs, updated_cfgs + + +class SDNAccountConfigCallbacks(object): + def __init__(self, + on_add_apply=None, on_add_prepare=None, + on_delete_apply=None, on_delete_prepare=None): + + @asyncio.coroutine + def prepare_noop(*args, **kwargs): + pass + + def apply_noop(*args, **kwargs): + pass + + self.on_add_apply = on_add_apply + self.on_add_prepare = on_add_prepare + self.on_delete_apply = on_delete_apply + self.on_delete_prepare = on_delete_prepare + + for f in ('on_add_apply', 'on_delete_apply'): + ref = getattr(self, f) + if ref is None: + setattr(self, f, apply_noop) + continue + + if asyncio.iscoroutinefunction(ref): + raise ValueError('%s cannot be a coroutine' % (f,)) + + for f in ('on_add_prepare', 'on_delete_prepare'): + ref = getattr(self, f) + if ref is None: + setattr(self, f, prepare_noop) + continue + + if not asyncio.iscoroutinefunction(ref): + raise ValueError("%s must be a coroutine" % f) + + +class SDNAccountConfigSubscriber(object): + XPATH = "C,/rw-sdn:sdn/rw-sdn:account" + + def __init__(self, dts, log, rwlog_hdl, sdn_callbacks, acctstore): + self._dts = dts + self._log = log + self._rwlog_hdl = rwlog_hdl + self._reg = None + + self.accounts = acctstore + + self._sdn_callbacks = sdn_callbacks + + def add_account(self, account_msg): + self._log.info("adding sdn account: {}".format(account_msg)) + + account = accounts.SDNAccount(self._log, self._rwlog_hdl, account_msg) + self.accounts[account.name] = account + + self._sdn_callbacks.on_add_apply(account) + + def delete_account(self, account_name): + self._log.info("deleting sdn account: {}".format(account_name)) + del self.accounts[account_name] + + self._sdn_callbacks.on_delete_apply(account_name) + + def update_account(self, account_msg): + """ Update an existing sdn account + + In order to simplify update, turn an update into a delete followed by + an add. The drawback to this approach is that we will not support + updates of an "in-use" sdn account, but this seems like a + reasonable trade-off. + + + Arguments: + account_msg - The sdn account config message + """ + self._log.info("updating sdn account: {}".format(account_msg)) + + self.delete_account(account_msg.name) + self.add_account(account_msg) + + def register(self): + @asyncio.coroutine + def apply_config(dts, acg, xact, action, _): + self._log.debug("Got sdn account apply config (xact: %s) (action: %s)", xact, action) + + if xact.xact is None: + if action == rwdts.AppconfAction.INSTALL: + curr_cfg = self._reg.elements + for cfg in curr_cfg: + self._log.debug("SDN account being re-added after restart.") + if not cfg.has_field('account_type'): + raise SDNAccountError("New SDN account must contain account_type field.") + self.add_account(cfg) + else: + # When RIFT first comes up, an INSTALL is called with the current config + # Since confd doesn't actally persist data this never has any data so + # skip this for now. + self._log.debug("No xact handle. Skipping apply config") + + return + + add_cfgs, delete_cfgs, update_cfgs = get_add_delete_update_cfgs( + dts_member_reg=self._reg, + xact=xact, + key_name="name", + ) + + # Handle Deletes + for cfg in delete_cfgs: + self.delete_account(cfg.name) + + # Handle Adds + for cfg in add_cfgs: + self.add_account(cfg) + + # Handle Updates + for cfg in update_cfgs: + self.update_account(cfg) + + @asyncio.coroutine + def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch): + """ Prepare callback from DTS for SDN Account """ + + action = xact_info.query_action + self._log.debug("SDN account on_prepare config received (action: %s): %s", + xact_info.query_action, msg) + + if action in [rwdts.QueryAction.CREATE, rwdts.QueryAction.UPDATE]: + if msg.name in self.accounts: + self._log.debug("SDN account already exists. Invoking update request") + + # Since updates are handled by a delete followed by an add, invoke the + # delete prepare callbacks to give clients an opportunity to reject. + yield from self._sdn_callbacks.on_delete_prepare(msg.name) + + else: + self._log.debug("SDN account does not already exist. Invoking on_prepare add request") + if not msg.has_field('account_type'): + raise SDNAccountError("New sdn account must contain account_type field.") + + account = accounts.SDNAccount(self._log, self._rwlog_hdl, msg) + yield from self._sdn_callbacks.on_add_prepare(account) + + elif action == rwdts.QueryAction.DELETE: + # Check if the entire SDN account got deleted + fref = ProtobufC.FieldReference.alloc() + fref.goto_whole_message(msg.to_pbcm()) + if fref.is_field_deleted(): + yield from self._sdn_callbacks.on_delete_prepare(msg.name) + + else: + self._log.error("Deleting individual fields for SDN account not supported") + xact_info.respond_xpath(rwdts.XactRspCode.NACK) + return + + else: + self._log.error("Action (%s) NOT SUPPORTED", action) + xact_info.respond_xpath(rwdts.XactRspCode.NACK) + + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + + self._log.debug("Registering for SDN Account config using xpath: %s", + SDNAccountConfigSubscriber.XPATH, + ) + + acg_handler = rift.tasklets.AppConfGroup.Handler( + on_apply=apply_config, + ) + + with self._dts.appconf_group_create(acg_handler) as acg: + self._reg = acg.register( + xpath=SDNAccountConfigSubscriber.XPATH, + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, + on_prepare=on_prepare, + ) diff --git a/common/python/rift/mano/sdn/operdata.py b/common/python/rift/mano/sdn/operdata.py new file mode 100644 index 00000000..b29f100c --- /dev/null +++ b/common/python/rift/mano/sdn/operdata.py @@ -0,0 +1,128 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import asyncio +import rift.tasklets + +from gi.repository import( + RwSdnYang, + RwDts as rwdts, + ) + + +class SDNAccountNotFound(Exception): + pass + + +class SDNAccountDtsOperdataHandler(object): + def __init__(self, dts, log, loop): + self._dts = dts + self._log = log + self._loop = loop + + self.sdn_accounts = {} + + def add_sdn_account(self, account): + self.sdn_accounts[account.name] = account + account.start_validate_credentials(self._loop) + + def delete_sdn_account(self, account_name): + del self.sdn_accounts[account_name] + + def get_saved_sdn_accounts(self, sdn_account_name): + ''' Get SDN Account corresponding to passed name, or all saved accounts if name is None''' + saved_sdn_accounts = [] + + if sdn_account_name is None or sdn_account_name == "": + sdn_accounts = list(self.sdn_accounts.values()) + saved_sdn_accounts.extend(sdn_accounts) + elif sdn_account_name in self.sdn_accounts: + account = self.sdn_accounts[sdn_account_name] + saved_sdn_accounts.append(account) + else: + errstr = "SDN account {} does not exist".format(sdn_account_name) + raise KeyError(errstr) + + return saved_sdn_accounts + + def _register_show_status(self): + def get_xpath(sdn_name=None): + return "D,/rw-sdn:sdn/account{}/connection-status".format( + "[name='%s']" % sdn_name if sdn_name is not None else '' + ) + + @asyncio.coroutine + def on_prepare(xact_info, action, ks_path, msg): + self._log.debug("Got show SDN connection status request: %s", ks_path.create_string()) + path_entry = RwSdnYang.SDNAccount.schema().keyspec_to_entry(ks_path) + sdn_account_name = path_entry.key00.name + + try: + saved_accounts = self.get_saved_sdn_accounts(sdn_account_name) + for account in saved_accounts: + connection_status = account.connection_status + self._log.debug("Responding to SDN connection status request: %s", connection_status) + xact_info.respond_xpath( + rwdts.XactRspCode.MORE, + xpath=get_xpath(account.name), + msg=account.connection_status, + ) + except KeyError as e: + self._log.warning(str(e)) + xact_info.respond_xpath(rwdts.XactRspCode.NA) + return + + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + + yield from self._dts.register( + xpath=get_xpath(), + handler=rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare), + flags=rwdts.Flag.PUBLISHER, + ) + + def _register_validate_rpc(self): + def get_xpath(): + return "/rw-sdn:update-sdn-status" + + @asyncio.coroutine + def on_prepare(xact_info, action, ks_path, msg): + if not msg.has_field("sdn_account"): + raise SDNAccountNotFound("SDN account name not provided") + + sdn_account_name = msg.sdn_account + try: + account = self.sdn_accounts[sdn_account_name] + except KeyError: + raise SDNAccountNotFound("SDN account name %s not found" % sdn_account_name) + + account.start_validate_credentials(self._loop) + + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + + yield from self._dts.register( + xpath=get_xpath(), + handler=rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare + ), + flags=rwdts.Flag.PUBLISHER, + ) + + @asyncio.coroutine + def register(self): + yield from self._register_show_status() + yield from self._register_validate_rpc() diff --git a/rwcal/plugins/yang/rwcal.yang b/rwcal/plugins/yang/rwcal.yang index 9daefb10..1ed2f7bd 100644 --- a/rwcal/plugins/yang/rwcal.yang +++ b/rwcal/plugins/yang/rwcal.yang @@ -100,62 +100,6 @@ module rwcal uses connection-status; - typedef sdn-account-type { - description "SDN account type"; - type enumeration { - enum odl; - enum mock; - enum sdnsim; - } - } - - grouping sdn-provider-auth { - leaf account-type { - type sdn-account-type; - } - - choice provider-specific-info { - container odl { - leaf username { - type string { - length "1..255"; - } - } - - leaf password { - type string { - length "1..32"; - } - } - - leaf url { - type string { - length "1..255"; - } - } - } - container mock { - leaf username { - type string; - } - leaf plugin-name { - type string; - default "rwsdn_mock"; - } - } - - container sdnsim { - leaf username { - type string; - } - leaf plugin-name { - type string; - default "rwsdn_sim"; - } - } - } - } - grouping provider-auth { leaf account-type { type manotypes:cloud-account-type; diff --git a/rwlaunchpad/plugins/cli/cli_launchpad_schema_listing.txt b/rwlaunchpad/plugins/cli/cli_launchpad_schema_listing.txt index 4a2741dd..f11616ca 100644 --- a/rwlaunchpad/plugins/cli/cli_launchpad_schema_listing.txt +++ b/rwlaunchpad/plugins/cli/cli_launchpad_schema_listing.txt @@ -38,7 +38,7 @@ rw-nsr rw-pb-ext rw-resource-mgr rw-restportforward -rwsdn +rwsdnal rw-sdn rwshell-mgmt rw-sorch diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py index da9807a6..99cb3f7b 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py @@ -51,7 +51,7 @@ from gi.repository import ( VnfrYang, RwVnfrYang, RwNsmYang, - RwsdnYang, + RwsdnalYang, RwDts as rwdts, RwTypes, ProtobufC, @@ -383,7 +383,7 @@ class VnffgRecord(object): vnfr = yield from self._nsr.fetch_vnfr(nsr_vnfr.xpath) self._log.debug("Received VNFR is %s", vnfr) - sff = RwsdnYang.VNFFGSff() + sff = RwsdnalYang.VNFFGSff() sff_list[nsr_vnfr.vnfd.id] = sff sff.name = nsr_vnfr.name sff.function_type = nsr_vnfr.vnfd.service_function_chain diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py index cf836120..4d6cde45 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py @@ -19,7 +19,7 @@ import asyncio from gi.repository import ( RwDts as rwdts, - RwsdnYang, + RwsdnalYang, RwTypes, ProtobufC, ) @@ -79,7 +79,7 @@ class VnffgMgr(object): if (account.name in self._account): self._log.error("SDN Account is already set") else: - sdn_account = RwsdnYang.SDNAccount() + sdn_account = RwsdnalYang.SDNAccount() sdn_account.from_dict(account.as_dict()) sdn_account.name = account.name self._account[account.name] = sdn_account @@ -102,7 +102,7 @@ class VnffgMgr(object): def get_sdn_account(self, name): """ - Creates an object for class RwsdnYang.SdnAccount() + Creates an object for class RwsdnalYang.SdnAccount() """ if (name in self._account): return self._account[name] @@ -172,7 +172,7 @@ class VnffgMgr(object): sdn_plugin = self.get_sdn_plugin(sdn_acct_name) for rsp in vnffgr.rsp: - vnffg = RwsdnYang.VNFFGChain() + vnffg = RwsdnalYang.VNFFGChain() vnffg.name = rsp.name vnffg.classifier_name = rsp.classifier_name @@ -230,7 +230,7 @@ class VnffgMgr(object): else: self._log.error("No RSP wiht name %s found; Skipping classifier %s creation",classifier.rsp_id_ref,classifier.name) continue - vnffgcl = RwsdnYang.VNFFGClassifier() + vnffgcl = RwsdnalYang.VNFFGClassifier() vnffgcl.name = classifier.name vnffgcl.rsp_name = cl_rsp_name vnffgcl.port_id = vnffgr_cl[0].port_id diff --git a/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py b/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py index 0ced574b..6ec2421e 100755 --- a/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py +++ b/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py @@ -16,8 +16,6 @@ # import asyncio -import logging -import os import sys import gi @@ -25,13 +23,12 @@ gi.require_version('RwVnsYang', '1.0') gi.require_version('RwDts', '1.0') from gi.repository import ( RwVnsYang, - RwSdnYang, RwDts as rwdts, RwTypes, - ProtobufC, ) import rift.tasklets +import rift.mano.sdn from rift.vlmgr import ( VlrDtsHandler, @@ -43,20 +40,9 @@ from rift.topmgr import ( NwtopStaticDtsHandler, NwtopDiscoveryDtsHandler, NwtopDataStore, - SdnAccountMgr, ) -class SdnInterfaceError(Exception): - """ SDN interface creation Error """ - pass - - -class SdnPluginError(Exception): - """ SDN plugin creation Error """ - pass - - class VlRecordError(Exception): """ Vlr Record creation Error """ pass @@ -66,186 +52,43 @@ class VlRecordNotFound(Exception): """ Vlr Record not found""" pass -class SdnAccountError(Exception): - """ Error while creating/deleting/updating SDN Account""" - pass -class SdnAccountNotFound(Exception): - pass - -class SDNAccountDtsOperdataHandler(object): - def __init__(self, dts, log, loop, parent): - self._dts = dts +class SDNAccountHandlers(object): + def __init__(self, dts, log, log_hdl, acctstore, loop): self._log = log - self._loop = loop - self._parent = parent - - def _register_show_status(self): - def get_xpath(sdn_name=None): - return "D,/rw-sdn:sdn/rw-sdn:account{}/rw-sdn:connection-status".format( - "[name='%s']" % sdn_name if sdn_name is not None else '' - ) - - @asyncio.coroutine - def on_prepare(xact_info, action, ks_path, msg): - path_entry = RwSdnYang.SDNAccountConfig.schema().keyspec_to_entry(ks_path) - sdn_account_name = path_entry.key00.name - self._log.debug("Got show sdn connection status request: %s", ks_path.create_string()) - - try: - saved_accounts = self._parent._acctmgr.get_saved_sdn_accounts(sdn_account_name) - for account in saved_accounts: - sdn_acct = RwSdnYang.SDNAccountConfig() - sdn_acct.from_dict(account.as_dict()) - - self._log.debug("Responding to sdn connection status request: %s", sdn_acct.connection_status) - xact_info.respond_xpath( - rwdts.XactRspCode.MORE, - xpath=get_xpath(account.name), - msg=sdn_acct.connection_status, - ) - except KeyError as e: - self._log.warning(str(e)) - xact_info.respond_xpath(rwdts.XactRspCode.NA) - return - - xact_info.respond_xpath(rwdts.XactRspCode.ACK) - - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare), - flags=rwdts.Flag.PUBLISHER, - ) - - def _register_validate_rpc(self): - def get_xpath(): - return "/rw-sdn:update-sdn-status" - - @asyncio.coroutine - def on_prepare(xact_info, action, ks_path, msg): - if not msg.has_field("sdn_account"): - raise SdnAccountNotFound("SDN account name not provided") - - sdn_account_name = msg.sdn_account - account = self._parent._acctmgr.get_sdn_account(sdn_account_name) - if account is None: - self._log.warning("SDN account %s does not exist", sdn_account_name) - xact_info.respond_xpath(rwdts.XactRspCode.NA) - return - - self._parent._acctmgr.start_validate_credentials(self._loop, sdn_account_name) - - xact_info.respond_xpath(rwdts.XactRspCode.ACK) - - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare - ), - flags=rwdts.Flag.PUBLISHER, - ) - - @asyncio.coroutine - def register(self): - yield from self._register_show_status() - yield from self._register_validate_rpc() - -class SDNAccountDtsHandler(object): - XPATH = "C,/rw-sdn:sdn/rw-sdn:account" - - def __init__(self, dts, log, parent): + self._log_hdl = log_hdl self._dts = dts - self._log = log - self._parent = parent - - self._sdn_account = {} - - def _set_sdn_account(self, account): - self._log.info("Setting sdn account: {}".format(account)) - if account.name in self._sdn_account: - self._log.error("SDN Account with name %s already exists. Ignoring config", account.name); - self._sdn_account[account.name] = account - self._parent._acctmgr.set_sdn_account(account) - - def _del_sdn_account(self, account_name): - self._log.info("Deleting sdn account: {}".format(account_name)) - del self._sdn_account[account_name] - - self._parent._acctmgr.del_sdn_account(account_name) - - def _update_sdn_account(self, account): - self._log.info("Updating sdn account: {}".format(account)) - # No need to update locally saved sdn_account's updated fields, as they - # are not used anywhere. Call the parent's update callback. - self._parent._acctmgr.update_sdn_account(account) - + self._loop = loop + self._acctstore = acctstore + + self._log.debug("Creating SDN account config handler") + self.sdn_cfg_handler = rift.mano.sdn.SDNAccountConfigSubscriber( + self._dts, self._log, self._log_hdl, + rift.mano.sdn.SDNAccountConfigCallbacks( + on_add_apply=self.on_sdn_account_added, + on_delete_apply=self.on_sdn_account_deleted, + ), + self._acctstore + + ) + + self._log.debug("Creating SDN account opdata handler") + self.sdn_operdata_handler = rift.mano.sdn.SDNAccountDtsOperdataHandler( + self._dts, self._log, self._loop, + ) + + def on_sdn_account_deleted(self, account_name): + self._log.debug("SDN account deleted") + self.sdn_operdata_handler.delete_sdn_account(account_name) + + def on_sdn_account_added(self, account): + self._log.debug("SDN account added") + self.sdn_operdata_handler.add_sdn_account(account) + @asyncio.coroutine def register(self): - def apply_config(dts, acg, xact, action, _): - self._log.debug("Got sdn account apply config (xact: %s) (action: %s)", xact, action) - if action == rwdts.AppconfAction.INSTALL and xact.id is None: - self._log.debug("No xact handle. Skipping apply config") - return RwTypes.RwStatus.SUCCESS - - return RwTypes.RwStatus.SUCCESS - - @asyncio.coroutine - def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch): - """ Prepare callback from DTS for SDN Account config """ - - self._log.info("SDN Cloud account config received: %s", msg) - - fref = ProtobufC.FieldReference.alloc() - fref.goto_whole_message(msg.to_pbcm()) - - if fref.is_field_deleted(): - # Delete the sdn account record - self._del_sdn_account(msg.name) - else: - # If the account already exists, then this is an update. - if msg.name in self._sdn_account: - self._log.debug("SDN account already exists. Invoking on_prepare update request") - if msg.has_field("account_type"): - errmsg = "Cannot update SDN account's account-type." - self._log.error(errmsg) - xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, - errmsg) - raise SdnAccountError(errmsg) - - # Update the sdn account record - self._update_sdn_account(msg) - else: - self._log.debug("SDN account does not already exist. Invoking on_prepare add request") - if not msg.has_field('account_type'): - errmsg = "New SDN account must contain account-type field." - self._log.error(errmsg) - xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, - errmsg) - raise SdnAccountError(errmsg) - - # Set the sdn account record - self._set_sdn_account(msg) - - xact_info.respond_xpath(rwdts.XactRspCode.ACK) - - - self._log.debug("Registering for Sdn Account config using xpath: %s", - SDNAccountDtsHandler.XPATH, - ) - - acg_handler = rift.tasklets.AppConfGroup.Handler( - on_apply=apply_config, - ) - - with self._dts.appconf_group_create(acg_handler) as acg: - acg.register( - xpath=SDNAccountDtsHandler.XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, - on_prepare=on_prepare - ) + self.sdn_cfg_handler.register() + yield from self.sdn_operdata_handler.register() class VnsManager(object): @@ -255,14 +98,13 @@ class VnsManager(object): self._log = log self._log_hdl = log_hdl self._loop = loop + self._acctstore = {} self._vlr_handler = VlrDtsHandler(dts, log, loop, self) self._vld_handler = VldDtsHandler(dts, log, loop, self) - self._sdn_handler = SDNAccountDtsHandler(dts,log,self) - self._sdn_opdata_handler = SDNAccountDtsOperdataHandler(dts,log, loop, self) - self._acctmgr = SdnAccountMgr(self._log, self._log_hdl, self._loop) + self._sdn_handlers = SDNAccountHandlers(dts, log, log_hdl, self._acctstore, loop) self._nwtopdata_store = NwtopDataStore(log) - self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, self._acctmgr, self._nwtopdata_store) - self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, self._acctmgr, self._nwtopdata_store) + self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, self._acctstore, self._nwtopdata_store) + self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, self._acctstore, self._nwtopdata_store) self._vlrs = {} @asyncio.coroutine @@ -278,11 +120,10 @@ class VnsManager(object): yield from self._vld_handler.register() @asyncio.coroutine - def register_sdn_handler(self): - """ Register vlr DTS handler """ - self._log.debug("Registering SDN Account config handler") - yield from self._sdn_handler.register() - yield from self._sdn_opdata_handler.register() + def register_sdn_handlers(self): + """ Register SDN DTS handlers """ + self._log.debug("Registering SDN Account handlers") + yield from self._sdn_handlers.register() @asyncio.coroutine def register_nwtopstatic_handler(self): @@ -299,11 +140,10 @@ class VnsManager(object): @asyncio.coroutine def register(self): """ Register all static DTS handlers""" - yield from self.register_sdn_handler() + yield from self.register_sdn_handlers() yield from self.register_vlr_handler() yield from self.register_vld_handler() yield from self.register_nwtopstatic_handler() - # Not used for now yield from self.register_nwtopdiscovery_handler() def create_vlr(self, msg): diff --git a/rwlaunchpad/plugins/rwvns/rift/topmgr/__init__.py b/rwlaunchpad/plugins/rwvns/rift/topmgr/__init__.py index f570abc9..1f750bd2 100644 --- a/rwlaunchpad/plugins/rwvns/rift/topmgr/__init__.py +++ b/rwlaunchpad/plugins/rwvns/rift/topmgr/__init__.py @@ -20,7 +20,6 @@ from .rwtopmgr import ( NwtopDiscoveryDtsHandler, NwtopStaticDtsHandler, - SdnAccountMgr, ) from .rwtopdatastore import ( diff --git a/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py b/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py index b095fbc0..af4b75bd 100755 --- a/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py +++ b/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py @@ -28,168 +28,23 @@ from gi.repository import ( IetfNetworkTopologyYang, IetfL2TopologyYang, RwTopologyYang, - RwsdnYang, + RwsdnalYang, RwTypes ) from gi.repository.RwTypes import RwStatus -import rw_peas import rift.tasklets -class SdnGetPluginError(Exception): - """ Error while fetching SDN plugin """ - pass - - -class SdnGetInterfaceError(Exception): - """ Error while fetching SDN interface""" - pass - - -class SdnAccountMgr(object): - """ Implements the interface to backend plugins to fetch topology """ - def __init__(self, log, log_hdl, loop): - self._account = {} - self._log = log - self._log_hdl = log_hdl - self._loop = loop - self._sdn = {} - - self._regh = None - - self._status = RwsdnYang.SDNAccount_ConnectionStatus( - status='unknown', - details="Connection status lookup not started" - ) - - self._validate_task = None - - def set_sdn_account(self,account): - if (account.name in self._account): - self._log.error("SDN Account is already set") - else: - sdn_account = RwsdnYang.SDNAccount() - sdn_account.from_dict(account.as_dict()) - sdn_account.name = account.name - self._account[account.name] = sdn_account - self._log.debug("Account set is %s , %s",type(self._account), self._account) - self.start_validate_credentials(self._loop, account.name) - - def del_sdn_account(self, name): - self._log.debug("Account deleted is %s , %s", type(self._account), name) - del self._account[name] - - def update_sdn_account(self,account): - self._log.debug("Account updated is %s , %s", type(self._account), account) - if account.name in self._account: - sdn_account = self._account[account.name] - - sdn_account.from_dict( - account.as_dict(), - ignore_missing_keys=True, - ) - self._account[account.name] = sdn_account - self.start_validate_credentials(self._loop, account.name) - - def get_sdn_account(self, name): - """ - Creates an object for class RwsdnYang.SdnAccount() - """ - if (name in self._account): - return self._account[name] - else: - self._log.error("ERROR : SDN account is not configured") - - def get_saved_sdn_accounts(self, name): - ''' Get SDN Account corresponding to passed name, or all saved accounts if name is None''' - saved_sdn_accounts = [] - - if name is None or name == "": - sdn_accounts = list(self._account.values()) - saved_sdn_accounts.extend(sdn_accounts) - elif name in self._account: - account = self._account[name] - saved_sdn_accounts.append(account) - else: - errstr = "SDN account {} does not exist".format(name) - raise KeyError(errstr) - - return saved_sdn_accounts - - def get_sdn_plugin(self,name): - """ - Loads rw.sdn plugin via libpeas - """ - if (name in self._sdn): - return self._sdn[name] - account = self.get_sdn_account(name) - plugin_name = getattr(account, account.account_type).plugin_name - self._log.info("SDN plugin being created") - plugin = rw_peas.PeasPlugin(plugin_name, 'RwSdn-1.0') - engine, info, extension = plugin() - - self._sdn[name] = plugin.get_interface("Topology") - try: - rc = self._sdn[name].init(self._log_hdl) - assert rc == RwStatus.SUCCESS - except: - self._log.error("ERROR:SDN plugin instantiation failed ") - else: - self._log.info("SDN plugin successfully instantiated") - return self._sdn[name] - - @asyncio.coroutine - def validate_sdn_account_credentials(self, loop, name): - self._log.debug("Validating SDN Account credentials %s", name) - self._status = RwsdnYang.SDNAccount_ConnectionStatus( - status="validating", - details="SDN account connection validation in progress" - ) - - _sdnacct = self.get_sdn_account(name) - if (_sdnacct is None): - raise SdnGetPluginError - _sdnplugin = self.get_sdn_plugin(name) - if (_sdnplugin is None): - raise SdnGetInterfaceError - - rwstatus, status = yield from loop.run_in_executor( - None, - _sdnplugin.validate_sdn_creds, - _sdnacct, - ) - - if rwstatus == RwTypes.RwStatus.SUCCESS: - self._status = RwsdnYang.SDNAccount_ConnectionStatus.from_dict(status.as_dict()) - else: - self._status = RwsdnYang.SDNAccount_ConnectionStatus( - status="failure", - details="Error when calling CAL validate sdn creds" - ) - - self._log.info("Got sdn account validation response: %s", self._status) - _sdnacct.connection_status = self._status - - def start_validate_credentials(self, loop, name): - if self._validate_task is not None: - self._validate_task.cancel() - self._validate_task = None - - self._validate_task = asyncio.ensure_future( - self.validate_sdn_account_credentials(loop, name), - loop=loop - ) - class NwtopDiscoveryDtsHandler(object): """ Handles DTS interactions for the Discovered Topology registration """ DISC_XPATH = "D,/nd:network" - def __init__(self, dts, log, loop, acctmgr, nwdatastore): + def __init__(self, dts, log, loop, acctstore, nwdatastore): self._dts = dts self._log = log self._loop = loop - self._acctmgr = acctmgr + self._acctstore = acctstore self._nwdatastore = nwdatastore self._regh = None @@ -219,16 +74,12 @@ class NwtopDiscoveryDtsHandler(object): if action == rwdts.QueryAction.READ: - for name in self._acctmgr._account: - _sdnacct = self._acctmgr.get_sdn_account(name) - if (_sdnacct is None): - raise SdnGetPluginError - - _sdnplugin = self._acctmgr.get_sdn_plugin(name) - if (_sdnplugin is None): - raise SdnGetInterfaceError + for name, sdnacct in self._acctstore.items(): + if sdnacct.account_type != "odl": + continue + sdnintf = sdnacct.sdn - rc, nwtop = _sdnplugin.get_network_list(_sdnacct) + rc, nwtop = sdnintf.get_network_list(sdnacct.sdnal_account_msg) #assert rc == RwStatus.SUCCESS if rc != RwStatus.SUCCESS: self._log.error("Fetching get network list for SDN Account %s failed", name) @@ -268,11 +119,11 @@ class NwtopStaticDtsHandler(object): """ Handles DTS interactions for the Static Topology registration """ STATIC_XPATH = "C,/nd:network" - def __init__(self, dts, log, loop, acctmgr, nwdatastore): + def __init__(self, dts, log, loop, acctstore, nwdatastore): self._dts = dts self._log = log self._loop = loop - self._acctmgr = acctmgr + self._acctstore = acctstore self._regh = None self.pending = {} diff --git a/rwlaunchpad/plugins/rwvns/test/test_sdn_mock.py b/rwlaunchpad/plugins/rwvns/test/test_sdn_mock.py index 45e2e809..61217475 100644 --- a/rwlaunchpad/plugins/rwvns/test/test_sdn_mock.py +++ b/rwlaunchpad/plugins/rwvns/test/test_sdn_mock.py @@ -22,10 +22,10 @@ import unittest import rw_peas import rwlogger -from gi.repository import RwsdnYang +from gi.repository import RwsdnalYang import gi gi.require_version('RwTypes', '1.0') -gi.require_version('RwSdn', '1.0') +gi.require_version('RwSdnal', '1.0') from gi.repository import RwcalYang from gi.repository import IetfNetworkYang from gi.repository.RwTypes import RwStatus @@ -35,9 +35,9 @@ logger = logging.getLogger('mock') def get_sdn_account(): """ - Creates an object for class RwsdnYang.SdnAccount() + Creates an object for class RwsdnalYang.SdnAccount() """ - account = RwsdnYang.SDNAccount() + account = RwsdnalYang.SDNAccount() account.account_type = "mock" account.mock.username = "rift" account.mock.plugin_name = "rwsdn_mock" diff --git a/rwlaunchpad/plugins/rwvns/test/test_sdn_odl.py b/rwlaunchpad/plugins/rwvns/test/test_sdn_odl.py new file mode 100644 index 00000000..b4dda0e3 --- /dev/null +++ b/rwlaunchpad/plugins/rwvns/test/test_sdn_odl.py @@ -0,0 +1,116 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging +import unittest + +import rw_peas +import rwlogger + +import gi +gi.require_version('RwTypes', '1.0') +from gi.repository import RwsdnalYang +from gi.repository.RwTypes import RwStatus + + +logger = logging.getLogger('sdnodl') + +odl_info = { + 'username' : 'admin', + 'password' : 'admin', + 'url' : 'http://10.66.4.27:8181', +} + + +def get_sdn_account(): + """ + Creates an object for class RwsdnalYang.SdnAccount() + """ + account = RwsdnalYang.SDNAccount() + account.name = "grunt27" + account.account_type = "odl" + account.odl.plugin_name = "rwsdn_odl" + account.odl.username = odl_info['username'] + account.odl.password = odl_info['password'] + account.odl.url = odl_info['url'] + + return account + +def get_sdn_plugin(): + """ + Loads rw.sdn plugin via libpeas + """ + plugin = rw_peas.PeasPlugin('rwsdn_odl', 'RwSdn-1.0') + engine, info, extension = plugin() + + # Get the RwLogger context + rwloggerctx = rwlogger.RwLog.Ctx.new("SDN-Log") + + sdn = plugin.get_interface("Topology") + try: + rc = sdn.init(rwloggerctx) + assert rc == RwStatus.SUCCESS + except: + logger.error("ERROR:SDN ODL plugin instantiation failed. Aborting tests") + else: + logger.info("SDN ODL plugin successfully instantiated") + return sdn + + + +class SdnOdlTest(unittest.TestCase): + def setUp(self): + """ + Initialize test plugins + """ + self._acct = get_sdn_account() + logger.info("SDN-Odl-Test: setUp") + self.sdn = get_sdn_plugin() + logger.info("SDN-Odl-Test: setUpEND") + + def tearDown(self): + logger.info("SDN-Odl-Test: Done with tests") + + def test_validate_sdn_creds(self): + """ + First test case + """ + logger.debug("SDN-Odl-Test: Starting validate creds ") + rc, status = self.sdn.validate_sdn_creds(self._acct) + logger.debug("SDN-Odl-Test: SDN return code %s resp %s", rc, status) + self.assertEqual(rc, RwStatus.SUCCESS) + logger.info("SDN-Odl-Test: Passed validate creds") + + def test_get_network_list(self): + """ + Get-network-list test case + """ + logger.debug("SDN-Odl-Test: Getting network list ") + rc, status = self.sdn.get_network_list(self._acct) + logger.debug("SDN-Odl-Test: SDN return code %s resp %s", rc, status) + self.assertEqual(rc, RwStatus.SUCCESS) + logger.info("SDN-Odl-Test: Passed get network list") + + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + unittest.main() + + + + diff --git a/rwlaunchpad/plugins/rwvns/test/test_sdn_openstack.py b/rwlaunchpad/plugins/rwvns/test/test_sdn_openstack.py new file mode 100644 index 00000000..05fc3f79 --- /dev/null +++ b/rwlaunchpad/plugins/rwvns/test/test_sdn_openstack.py @@ -0,0 +1,111 @@ + +# +# Copyright 2017 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging +import unittest + +import rw_peas +import rwlogger + +import gi +gi.require_version('RwTypes', '1.0') +from gi.repository import RwsdnalYang +from gi.repository.RwTypes import RwStatus + + +logger = logging.getLogger('sdnopenstack') + +openstack_info = { + 'username' : 'pluto', + 'password' : 'mypasswd', + 'auth_url' : 'http://10.66.4.17:5000/v2.0/', + 'project_name' : 'demo', + 'user_domain_name' : 'default', + 'project_domain_name': 'default' +} + + +def get_sdn_account(): + """ + Creates an object for class RwsdnalYang.SdnAccount() + """ + account = RwsdnalYang.SDNAccount() + account.name = "grunt17" + account.account_type = "openstack" + account.openstack.plugin_name = "rwsdn_openstack" + account.openstack.key = openstack_info['username'] + account.openstack.secret = openstack_info['password'] + account.openstack.auth_url = openstack_info['auth_url'] + account.openstack.tenant = openstack_info['project_name'] + account.openstack.user_domain = openstack_info['user_domain_name'] + account.openstack.project_domain = openstack_info['project_domain_name'] + + return account + +def get_sdn_plugin(): + """ + Loads rw.sdn plugin via libpeas + """ + plugin = rw_peas.PeasPlugin('rwsdn_openstack', 'RwSdn-1.0') + engine, info, extension = plugin() + + # Get the RwLogger context + rwloggerctx = rwlogger.RwLog.Ctx.new("SDN-Log") + + sdn = plugin.get_interface("Topology") + try: + rc = sdn.init(rwloggerctx) + assert rc == RwStatus.SUCCESS + except: + logger.error("ERROR:SDN openstack plugin instantiation failed. Aborting tests") + else: + logger.info("SDN openstack plugin successfully instantiated") + return sdn + + + +class SdnOpenstackTest(unittest.TestCase): + def setUp(self): + """ + Initialize test plugins + """ + self._acct = get_sdn_account() + logger.info("SDN-Openstack-Test: setUp") + self.sdn = get_sdn_plugin() + logger.info("SDN-Openstack-Test: setUpEND") + + def tearDown(self): + logger.info("SDN-Openstack-Test: Done with tests") + + def test_validate_sdn_creds(self): + """ + First test case + """ + logger.debug("SDN-Openstack-Test: Starting validate creds ") + rc, status = self.sdn.validate_sdn_creds(self._acct) + logger.debug("SDN-Openstack-Test: SDN return code %s resp %s", rc, status) + self.assertEqual(rc, RwStatus.SUCCESS) + logger.info("SDN-Openstack-Test: Passed validate creds") + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + unittest.main() + + + + diff --git a/rwlaunchpad/plugins/rwvns/test/test_sdn_sim.py b/rwlaunchpad/plugins/rwvns/test/test_sdn_sim.py index d216f0d1..e9cd0b39 100644 --- a/rwlaunchpad/plugins/rwvns/test/test_sdn_sim.py +++ b/rwlaunchpad/plugins/rwvns/test/test_sdn_sim.py @@ -15,7 +15,6 @@ # limitations under the License. # -import datetime import logging import unittest @@ -24,20 +23,17 @@ import rwlogger import gi gi.require_version('RwTypes', '1.0') -gi.require_version('RwSdn', '1.0') -from gi.repository import RwsdnYang -from gi.repository import IetfNetworkYang +from gi.repository import RwsdnalYang from gi.repository.RwTypes import RwStatus -from gi.repository import RwSdn logger = logging.getLogger('sdnsim') def get_sdn_account(): """ - Creates an object for class RwsdnYang.SdnAccount() + Creates an object for class RwsdnalYang.SdnAccount() """ - account = RwsdnYang.SDNAccount() + account = RwsdnalYang.SDNAccount() account.account_type = "sdnsim" account.sdnsim.username = "rift" account.sdnsim.plugin_name = "rwsdn_sim" diff --git a/rwlaunchpad/plugins/rwvns/test/topmgr_module_test.py b/rwlaunchpad/plugins/rwvns/test/topmgr_module_test.py index 44e2f5c3..06b59c37 100755 --- a/rwlaunchpad/plugins/rwvns/test/topmgr_module_test.py +++ b/rwlaunchpad/plugins/rwvns/test/topmgr_module_test.py @@ -43,7 +43,7 @@ import gi.repository.RwManifestYang as rwmanifest import gi.repository.IetfL2TopologyYang as l2Tl import gi.repository.RwTopologyYang as RwTl import gi.repository.RwLaunchpadYang as launchpadyang -from gi.repository import RwsdnYang +from gi.repository import RwsdnalYang from gi.repository.RwTypes import RwStatus from create_stackedl2topology import MyL2Topology diff --git a/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt b/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt index 4865d2e7..8f87f666 100644 --- a/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt +++ b/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt @@ -33,7 +33,7 @@ rift_add_vala( VALA_FILES ${VALA_FILES} VALA_PACKAGES rw_types-1.0 rw_yang-1.0 rw_keyspec-1.0 rw_yang_pb-1.0 rw_schema_proto-1.0 - rw_log_yang-1.0 rw_base_yang-1.0 rwcal_yang-1.0 rwsdn_yang-1.0 rw_manifest_yang-1.0 protobuf_c-1.0 ietf_netconf_yang-1.0 + rw_log_yang-1.0 rw_base_yang-1.0 rwcal_yang-1.0 rwsdnal_yang-1.0 rw_manifest_yang-1.0 protobuf_c-1.0 ietf_netconf_yang-1.0 ietf_network_yang-1.0 ietf_network_topology_yang-1.0 ietf_l2_topology_yang-1.0 rw_topology_yang-1.0 rw_log-1.0 @@ -50,7 +50,7 @@ rift_add_vala( GENERATE_VAPI_FILE ${VALA_LONG_NAME}.vapi GENERATE_GIR_FILE ${VALA_TYPELIB_PREFIX}.gir GENERATE_TYPELIB_FILE ${VALA_TYPELIB_PREFIX}.typelib - DEPENDS rwcal_yang rwsdn_yang mano_yang rwlog_gi rwschema_yang + DEPENDS rwcal_yang rwsdnal_yang mano_yang rwlog_gi rwschema_yang ) rift_install_vala_artifacts( diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn.vala b/rwlaunchpad/plugins/rwvns/vala/rwsdn.vala index ec4ab316..a4d597d8 100644 --- a/rwlaunchpad/plugins/rwvns/vala/rwsdn.vala +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn.vala @@ -10,8 +10,8 @@ namespace RwSdn { * Credential Validation related APIs */ public abstract RwTypes.RwStatus validate_sdn_creds( - Rwsdn.SDNAccount account, - out Rwsdn.SdnConnectionStatus status); + Rwsdnal.SDNAccount account, + out Rwsdnal.SdnConnectionStatus status); /* * Configuring related APIs @@ -22,22 +22,22 @@ namespace RwSdn { * Network related APIs */ public abstract RwTypes.RwStatus get_network_list( - Rwsdn.SDNAccount account, + Rwsdnal.SDNAccount account, out RwTopology.YangData_IetfNetwork network_topology); /* * VNFFG Chain related APIs */ public abstract RwTypes.RwStatus create_vnffg_chain( - Rwsdn.SDNAccount account, - Rwsdn.VNFFGChain vnffg_chain, + Rwsdnal.SDNAccount account, + Rwsdnal.VNFFGChain vnffg_chain, out string vnffg_id); /* * VNFFG Chain Terminate related APIs */ public abstract RwTypes.RwStatus terminate_vnffg_chain( - Rwsdn.SDNAccount account, + Rwsdnal.SDNAccount account, string vnffg_id); @@ -45,22 +45,22 @@ namespace RwSdn { * Network related APIs */ public abstract RwTypes.RwStatus get_vnffg_rendered_paths( - Rwsdn.SDNAccount account, - out Rwsdn.VNFFGRenderedPaths rendered_paths); + Rwsdnal.SDNAccount account, + out Rwsdnal.VNFFGRenderedPaths rendered_paths); /* * Classifier related APIs */ public abstract RwTypes.RwStatus create_vnffg_classifier( - Rwsdn.SDNAccount account, - Rwsdn.VNFFGClassifier vnffg_classifier, + Rwsdnal.SDNAccount account, + Rwsdnal.VNFFGClassifier vnffg_classifier, out string vnffg_classifier_id); /* * Classifier related APIs */ public abstract RwTypes.RwStatus terminate_vnffg_classifier( - Rwsdn.SDNAccount account, + Rwsdnal.SDNAccount account, string vnffg_classifier_id); diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_mock/rwsdn_mock.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_mock/rwsdn_mock.py index 2c0ffcc5..fc0d86ea 100644 --- a/rwlaunchpad/plugins/rwvns/vala/rwsdn_mock/rwsdn_mock.py +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_mock/rwsdn_mock.py @@ -27,7 +27,7 @@ from gi.repository import ( RwSdn, # Vala package RwTypes, RwTopologyYang as RwTl, - RwsdnYang + RwsdnalYang ) import rw_status @@ -144,7 +144,7 @@ class MockPlugin(GObject.Object, RwSdn.Topology): ) ) - account = RwsdnYang.SDNAccount() + account = RwsdnalYang.SDNAccount() account.name = 'mock' account.account_type = 'mock' account.mock.username = 'rift' diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py index 3eb39fcd..2727d8a4 100644 --- a/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py @@ -26,7 +26,7 @@ import time import gi gi.require_version('RwTypes', '1.0') -gi.require_version('RwsdnYang', '1.0') +gi.require_version('RwsdnalYang', '1.0') gi.require_version('RwSdn', '1.0') gi.require_version('RwTopologyYang','1.0') @@ -34,7 +34,7 @@ from gi.repository import ( GObject, RwSdn, # Vala package RwTypes, - RwsdnYang, + RwsdnalYang, RwTopologyYang as RwTl, ) @@ -336,7 +336,7 @@ class SdnOdl(object): """ Validate the SDN account credentials by accessing the rest API using the provided credentials """ - status = RwsdnYang.SdnConnectionStatus() + status = RwsdnalYang.SdnConnectionStatus() url = '{}/{}'.format(account.odl.url,"restconf") try: r=requests.get(url,auth=(account.odl.username,account.odl.password)) @@ -354,7 +354,6 @@ class SdnOdl(object): status.status = "failure" status.details = "Connection Failed (Invlaid URL): %s" % str(e) else: - print("SDN Successfully connected") status.status = "success" status.details = "Connection was successful" @@ -935,7 +934,7 @@ class SdnOdl(object): self.delete_all_sf(account) def _fill_rsp_list(self,sfc_rsp_list,sff_list): - vnffg_rsps = RwsdnYang.VNFFGRenderedPaths() + vnffg_rsps = RwsdnalYang.VNFFGRenderedPaths() for sfc_rsp in sfc_rsp_list['rendered-service-paths']['rendered-service-path']: rsp = vnffg_rsps.vnffg_rendered_path.add() rsp.name = sfc_rsp['name'] diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py index ff5d019c..6adece4e 100644 --- a/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py @@ -41,7 +41,7 @@ from gi.repository import ( GObject, RwCal, RwSdn, # Vala package - RwsdnYang, + RwsdnalYang, RwTypes, RwcalYang) @@ -255,7 +255,7 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology): Returns: Validation Code and Details String """ - status = RwsdnYang.SdnConnectionStatus() + status = RwsdnalYang.SdnConnectionStatus() drv = self._use_driver(account) try: drv.validate_account_creds() @@ -366,7 +366,7 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology): @param account - a SDN account """ self.log.debug('Received get VNFFG rendered path for account %s ', account) - vnffg_rsps = RwsdnYang.VNFFGRenderedPaths() + vnffg_rsps = RwsdnalYang.VNFFGRenderedPaths() drv = self._use_driver(account) port_chain_list = drv.get_port_chain_list() for port_chain in port_chain_list: diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py index 3061265d..164aa03e 100644 --- a/rwlaunchpad/plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py @@ -32,7 +32,7 @@ from gi.repository import ( GObject, RwSdn, # Vala package RwTypes, - RwsdnYang, + RwsdnalYang, #IetfL2TopologyYang as l2Tl, RwTopologyYang as RwTl, ) @@ -91,7 +91,7 @@ class SdnSimPlugin(GObject.Object, RwSdn.Topology): Returns: Validation Code and Details String """ - status = RwsdnYang.SdnConnectionStatus() + status = RwsdnalYang.SdnConnectionStatus() print("SDN Successfully connected") status.status = "success" status.details = "Connection was successful" diff --git a/rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt b/rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt index 00cde0ba..5e7e98a8 100644 --- a/rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt +++ b/rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt @@ -21,10 +21,10 @@ include(rift_yang) -set(source_yang_files rwsdn.yang) +set(source_yang_files rwsdnal.yang) rift_add_yang_target( - TARGET rwsdn_yang + TARGET rwsdnal_yang YANG_FILES ${source_yang_files} COMPONENT ${PKG_LONG_NAME} LIBRARIES diff --git a/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang b/rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang similarity index 98% rename from rwlaunchpad/plugins/rwvns/yang/rwsdn.yang rename to rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang index 8371ab8f..b24952b7 100644 --- a/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang +++ b/rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang @@ -1,7 +1,7 @@ /* * - * Copyright 2016 RIFT.IO Inc + * Copyright 2016-2017 RIFT.IO Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ * */ -module rwsdn +module rwsdnal { - namespace "http://riftio.com/ns/riftware-1.0/rwsdn"; - prefix "rwsdn"; + namespace "http://riftio.com/ns/riftware-1.0/rwsdnal"; + prefix "rwsdnal"; import rw-base { prefix rwbase; diff --git a/rwlaunchpad/plugins/yang/rw-vns.yang b/rwlaunchpad/plugins/yang/rw-vns.yang index 0036e16f..8dc63bb8 100644 --- a/rwlaunchpad/plugins/yang/rw-vns.yang +++ b/rwlaunchpad/plugins/yang/rw-vns.yang @@ -48,8 +48,8 @@ module rw-vns prefix "rwcal"; } - import rwsdn { - prefix "rwsdn"; + import rwsdnal { + prefix "rwsdnal"; } diff --git a/rwlaunchpad/ra/pytest/test_launchpad.py b/rwlaunchpad/ra/pytest/test_launchpad.py index 81f5b548..a6f5ae78 100644 --- a/rwlaunchpad/ra/pytest/test_launchpad.py +++ b/rwlaunchpad/ra/pytest/test_launchpad.py @@ -25,9 +25,9 @@ import pytest import gi -gi.require_version('RwsdnYang', '1.0') +gi.require_version('RwsdnalYang', '1.0') -from gi.repository import RwsdnYang +from gi.repository import RwsdnalYang @pytest.mark.setup('sdn') @pytest.mark.feature('sdn') @@ -39,8 +39,8 @@ class TestSdnSetup: Asserts: SDN name and accout type. ''' - proxy = mgmt_session.proxy(RwsdnYang) - sdn_account = RwsdnYang.SDNAccount( + proxy = mgmt_session.proxy(RwsdnalYang) + sdn_account = RwsdnalYang.SDNAccount( name=sdn_account_name, account_type=sdn_account_type) xpath = "/sdn-accounts/sdn-account-list[name='%s']" % sdn_account_name @@ -57,7 +57,7 @@ class TestSdn: Asserts: sdn_account.account_type is what was configured ''' - proxy = mgmt_session.proxy(RwsdnYang) + proxy = mgmt_session.proxy(RwsdnalYang) xpath = "/sdn-accounts/sdn-account-list[name='%s']" % sdn_account_name sdn_account = proxy.get_config(xpath) assert sdn_account.account_type == sdn_account_type @@ -68,7 +68,7 @@ class TestSdn: class TestSdnTeardown: def test_delete_odl_sdn_account(self, mgmt_session, sdn_account_name): '''Unconfigure sdn account''' - proxy = mgmt_session.proxy(RwsdnYang) + proxy = mgmt_session.proxy(RwsdnalYang) xpath = "/sdn-accounts/sdn-account-list[name='%s']" % sdn_account_name proxy.delete_config(xpath) -- 2.17.1