Merge from OSM SO master 35/1535/1
authorPhilip Joseph <philip.joseph@riftio.com>
Fri, 7 Apr 2017 09:53:26 +0000 (09:53 +0000)
committerPhilip Joseph <philip.joseph@riftio.com>
Fri, 7 Apr 2017 09:53:26 +0000 (09:53 +0000)
Signed-off-by: Philip Joseph <philip.joseph@riftio.com>
53 files changed:
BUILD.sh
common/plugins/yang/CMakeLists.txt
common/plugins/yang/rw-sdn.yang
common/python/CMakeLists.txt
common/python/rift/mano/sdn/__init__.py [new file with mode: 0644]
common/python/rift/mano/sdn/accounts.py [new file with mode: 0644]
common/python/rift/mano/sdn/config.py [new file with mode: 0644]
common/python/rift/mano/sdn/operdata.py [new file with mode: 0644]
common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml
common/python/rift/mano/yang_translator/riftiotypes.yaml
models/openmano/python/rift/openmano/openmano_client.py
models/openmano/python/rift/openmano/rift2openmano.py
models/plugins/yang/CMakeLists.txt
models/plugins/yang/mano-types.yang
models/plugins/yang/nsr.yang
models/plugins/yang/pnfd.yang
models/plugins/yang/vlr.yang
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py
rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py
rwcal/plugins/yang/rwcal.yang
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py
rwlaunchpad/plugins/cli/cli_launchpad_schema_listing.txt
rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/package.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py
rwlaunchpad/plugins/rwpkgmgr/CMakeLists.txt
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/downloader/__init__.py
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/downloader/copy.py [new file with mode: 0644]
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/__init__.py
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/copy_status.py [new file with mode: 0644]
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py
rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rwpkgmgr.py
rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py
rwlaunchpad/plugins/rwvns/rift/topmgr/__init__.py
rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py
rwlaunchpad/plugins/rwvns/test/test_sdn_mock.py
rwlaunchpad/plugins/rwvns/test/test_sdn_odl.py [new file with mode: 0644]
rwlaunchpad/plugins/rwvns/test/test_sdn_openstack.py [new file with mode: 0644]
rwlaunchpad/plugins/rwvns/test/test_sdn_sim.py
rwlaunchpad/plugins/rwvns/test/topmgr_module_test.py
rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt
rwlaunchpad/plugins/rwvns/vala/rwsdn.vala
rwlaunchpad/plugins/rwvns/vala/rwsdn_mock/rwsdn_mock.py
rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py
rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py
rwlaunchpad/plugins/rwvns/vala/rwsdn_sim/rwsdn_sim.py
rwlaunchpad/plugins/rwvns/yang/CMakeLists.txt
rwlaunchpad/plugins/rwvns/yang/rwsdn.yang [deleted file]
rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang [new file with mode: 0644]
rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang
rwlaunchpad/plugins/yang/rw-vns.yang
rwlaunchpad/ra/pytest/test_launchpad.py

index 1c90a25..a9c0195 100755 (executable)
--- 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 \
index b17e218..840997a 100644 (file)
 # 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
+  ASSOCIATED_FILES
+    rw-sdn.role.xml
+)
+
 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
@@ -32,12 +44,9 @@ rift_add_yang_target(
     mano-types_yang_gen
   DEPENDS
     rwcal_yang
-    rwsdn_yang
-    rwprojectmano_yang
-    mano-types_yang
+    rwsdnal_yang
   ASSOCIATED_FILES
     rw-cloud.role.xml
-    rw-sdn.role.xml
 )
 
 rift_add_yang_target(
index b4e0d9e..97b0441 100644 (file)
@@ -28,16 +28,16 @@ module rw-sdn
     prefix "rwpb";
   }
 
-  import mano-types {
-    prefix "manotypes";
-  }
-
   import rw-project {
     prefix "rw-project";
   }
 
-  import rwsdn {
-    prefix "rwsdn";
+  import rwsdnal {
+    prefix "rwsdnal";
+  }
+
+  import mano-types {
+    prefix "manotypes";
   }
 
   revision 2017-02-08 {
@@ -50,17 +50,18 @@ module rw-sdn
       "Initial revision.";
   }
 
-  augment "/rw-project:project" {
-    container sdn {
+augment "/rw-project:project" {
+  container sdn {
+      rwpb:msg-new SDNAccountConfig;
       list account {
-        rwpb:msg-new SDNAccountConfig;
+        rwpb:msg-new SDNAccount;
         key "name";
         leaf name {
-          type string;
+         type string;
         }
 
-        uses rwsdn:sdn-provider-auth;
-        uses rwsdn:connection-status;
+        uses rwsdnal:sdn-provider-auth;
+        uses rwsdnal:connection-status;
       }
     }
   }
index 285123b..f6e3baf 100644 (file)
@@ -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 (file)
index 0000000..8f85832
--- /dev/null
@@ -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 (file)
index 0000000..d539ead
--- /dev/null
@@ -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 (file)
index 0000000..20b17cc
--- /dev/null
@@ -0,0 +1,221 @@
+
+#
+#   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 rift.mano.utils.project import get_add_delete_update_cfgs
+
+from . import accounts
+
+
+class SDNAccountNotFound(Exception):
+    pass
+
+
+class SDNAccountError(Exception):
+    pass
+
+
+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 deregister(self):
+        if self._reg:
+            self._reg.deregister()
+            self._reg = None
+
+    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 (file)
index 0000000..094d804
--- /dev/null
@@ -0,0 +1,142 @@
+
+#
+#   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 = {}
+        self._oper = None
+        self._rpc = None
+
+    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)
+
+        self._oper = 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 self._project and not self._project.rpc_check(msg, xact_info=xact_info):
+             return
+
+            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)
+
+        self._rpc = 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()
+
+    def deregister(self):
+        if self._oper:
+            self._oper.deregister()
+            self._oper = None
+
+        if self._rpc:
+            self._rpc.deregister()
+            self._rpc = None
index 6798e2a..4debc76 100644 (file)
@@ -1070,7 +1070,7 @@ capability_types:
         description: >-
           Type of Service Function.
              NOTE- This needs to map with Service Function Type in ODL to
-             support VNFFG. Service Function Type is manadatory param in ODL
+             support VNFFG. Service Function Type is mandatory param in ODL
              SFC.
         required: false
   tosca.capabilities.Compute.Container.Architecture:
index 18a0728..7e4158b 100644 (file)
@@ -1072,7 +1072,7 @@ capability_types:
         description: >-
           Type of Service Function.
              NOTE- This needs to map with Service Function Type in ODL to
-             support VNFFG. Service Function Type is manadatory param in ODL
+             support VNFFG. Service Function Type is mandatory param in ODL
              SFC.
         required: false
   tosca.capabilities.Compute.Container.Architecture:
index 8a89ee1..834eea5 100755 (executable)
@@ -143,8 +143,8 @@ class OpenmanoCliAPI(object):
 
         if proc.returncode != 0:
             self._log.error(
-                    "Openmano command failed (rc=%s) with stdout: %s",
-                    proc.returncode, stdout
+                    "Openmano command %s failed (rc=%s) with stdout: %s",
+                    cmd_args[1], proc.returncode, stdout
                     )
             raise OpenmanoCommandFailed(stdout)
 
index 2b9ec48..92296ff 100755 (executable)
@@ -499,12 +499,11 @@ def rift2openmano_vnfd(rift_vnfd, rift_nsd):
                 if vdu.vm_flavor.has_field("vcpu_count"):
                     vnfc["numas"][0]["cores"] = max(vdu.vm_flavor.vcpu_count, 1)
 
-        else:
-            if vdu.vm_flavor.has_field("vcpu_count") and vdu.vm_flavor.vcpu_count:
-                vnfc["vcpus"] = vdu.vm_flavor.vcpu_count
+        if vdu.vm_flavor.has_field("vcpu_count") and vdu.vm_flavor.vcpu_count:
+            vnfc["vcpus"] = vdu.vm_flavor.vcpu_count
 
-            if vdu.vm_flavor.has_field("memory_mb") and vdu.vm_flavor.memory_mb:
-                vnfc["ram"] = vdu.vm_flavor.memory_mb
+        if vdu.vm_flavor.has_field("memory_mb") and vdu.vm_flavor.memory_mb:
+            vnfc["ram"] = vdu.vm_flavor.memory_mb
 
 
         if vdu.has_field("hypervisor_epa"):
index f22613f..cc2c32e 100644 (file)
@@ -60,6 +60,7 @@ rift_add_yang_target(
     rwprojectmano_yang_gen
   DEPENDS
     rwcloud_yang
+    rwsdn_yang
     rwconfig_agent_yang
     rwprojectmano_yang
   ASSOCIATED_FILES
index 601bdac..01e80d5 100644 (file)
@@ -109,13 +109,13 @@ module mano-types
     }
 
     leaf parameter-pool {
-      description "NSD Parameter pool name to use for this paramter";
+      description "NSD parameter pool name to use for this parameter";
       type string;
     }
 
     leaf read-only {
       description
-        "The value should be greyed out by the UI.
+        "The value should be dimmed by the UI.
         Only applies to parameters with default values.";
       type boolean;
     }
@@ -159,9 +159,9 @@ module mano-types
     leaf image {
       description
             "Image name for the software image.
-             If the image name is found within the VNF packaage it will
-             be uploaded to all cloud accounts during onboarding process.
-             Otherwise, the image must be added to the cloud account with
+             If the image name is found within the VNF package it will
+             be uploaded to all VIM accounts during onboarding process.
+             Otherwise, the image must be added to the VIM account with
              the same name as entered here.
             ";
       type string;
@@ -203,6 +203,7 @@ module mano-types
       }
 
       leaf value {
+        description "Value of the configuration primitive.";
         type string;
       }
     }
@@ -212,10 +213,10 @@ module mano-types
     container vnf-configuration {
       rwpb:msg-new VnfConfiguration;
       description
-          "Information regarding the VNF configuration 
-           is captured here. Note that if the NS contains
-           multiple instances of the same VNF, each instance
-           of the VNF may have different configuration";
+          "Information about the VNF configuration. Note: 
+           If the NS contains multiple instances of the
+           same VNF, each instance could have a different
+           configuration.";
 
       choice config-method {
         description
@@ -235,7 +236,7 @@ module mano-types
 
             leaf protocol {
               description
-                  "Protocol to use for netconf (e.g. ssh)";
+                  "Protocol to use for NETCONF such as ssh";
               type enumeration {
                 enum None;
                 enum ssh;
@@ -244,7 +245,7 @@ module mano-types
 
             leaf port {
               description
-                  "Port for the netconf server.";
+                  "Port for the NETCONF server.";
               type inet:port-number;
             }
           }
@@ -266,7 +267,8 @@ module mano-types
           description
               "Use custom script for configuring the VNF.
                This script is executed in the context of 
-               Orchestrator.";
+               Orchestrator (The same system and environment
+               as the Launchpad).";
           container script {
             leaf script-type {
               description
@@ -301,7 +303,7 @@ module mano-types
 
         leaf username {
           description 
-              "username for configuration.";
+              "User name for configuration.";
           type string;
         }
 
@@ -314,14 +316,14 @@ module mano-types
 
       container config-attributes {
         description
-            "Miscelaneous input parameters to be considered
+            "Miscellaneous input parameters to be considered
              while processing the NSD to apply configuration";
 
         leaf config-priority {
           description
-              "Configuration priority - order of confgiration
-               to be applied to each VNF in this NS,
-               low number gets precedence over high number";
+              "Configuration priority - order of configuration
+               to be applied to each VNF in this NS ,A low
+               number takes precedence over a high number";
           type uint64;
         }
 
@@ -411,7 +413,7 @@ module mano-types
 
   typedef api-type {
     description
-      "Type of API to fetch monitoring params";
+      "Type of API to fetch monitoring parameters";
 
     type enumeration {
       enum HTTP;
@@ -425,8 +427,8 @@ module mano-types
       "The method to extract a value from a JSON response
 
        NAMEKEY - Use the name as the key for a non-nested value.
-       JSONPATH - Use jsonpath-rw implemenation to extract a value.
-       OBJECTPATH - Use objectpath implemenation to extract a value.";
+       JSONPATH - Use jsonpath-rw implementation to extract a value.
+       OBJECTPATH - Use objectpath implementation to extract a value.";
       type enumeration {
         enum NAMEKEY;
         enum JSONPATH;
@@ -450,7 +452,7 @@ module mano-types
         VPORT: Virtual Port
         // VNIC_ADDR: Virtual NIC Address
         // PNIC_ADDR: Physical NIC Address
-        // PPORT: Phsical Port.";
+        // PPORT: Physical Port.";
 
     type enumeration {
       enum VPORT;
@@ -656,7 +658,7 @@ module mano-types
     container vm-flavor {
       leaf vcpu-count {
         description
-            "Number of vcpus for the VM.";
+            "Number of VCPUs for the VM.";
         type uint16;
       }
 
@@ -803,13 +805,13 @@ module mano-types
 
       
       leaf om-cpu-model-string {
-        description "Openmano CPU model string";
+        description "OpenMANO CPU model string";
         type string;
       }
 
       list om-cpu-feature {
         key "feature";
-        description "List of openmano CPU features";
+        description "List of OpenMANO CPU features";
         leaf feature {
           description "CPU feature";
           type string;
@@ -831,12 +833,12 @@ module mano-types
             "Memory page allocation size. If a VM requires
              hugepages, it should choose LARGE or SIZE_2MB
              or SIZE_1GB. If the VM prefers hugepages it
-             should chose PREFER_LARGE.
+             should choose PREFER_LARGE.
              LARGE        : Require hugepages (either 2MB or 1GB)
              SMALL        : Doesn't require hugepages
              SIZE_2MB     : Requires 2MB hugepages
              SIZE_1GB     : Requires 1GB hugepages
-             PREFER_LARGE : Application perfers hugepages";
+             PREFER_LARGE : Application prefers hugepages";
         type enumeration {
           enum LARGE;
           enum SMALL;
@@ -913,20 +915,21 @@ module mano-types
         case numa-aware {
           container numa-node-policy {
             description
-                "This policy defines numa topology of the
+                "This policy defines NUMA topology of the
                  guest. Specifically identifies if the guest
-                 should be run on a host with one numa
-                 node or multiple numa nodes. As an example
-                 a guest may want 8 vcpus and 4 GB of
-                 memory. But may want the vcpus and memory
-                 distributed across multiple numa nodes.
-                 The NUMA node 1 may run with 6 vcpus and
-                 3GB, and NUMA node 2 may run with 2 vcpus
-                 and 1GB.";
+                 should be run on a host with one NUMA
+                 node or multiple NUMA nodes. As an example
+                 a guest might need 8 VCPUs and 4 GB of
+                 memory. However, it might need the VCPUs
+                 and memory distributed across multiple
+                 NUMA nodes. In this scenario, NUMA node
+                 1 could run with 6 VCPUs and 3GB, and
+                 NUMA node 2 could run with 2 VCPUs and
+                 1GB.";
 
             leaf node-cnt {
               description
-                  "The number of numa nodes to expose to the VM.";
+                  "The number of NUMA nodes to expose to the VM.";
               type uint16;
             }
 
@@ -938,7 +941,7 @@ module mano-types
                                strictly from the memory attached
                                to the NUMA node.
                    PREFERRED : The memory should be allocated
-                               perferentially from the memory
+                               preferentially from the memory
                                attached to the NUMA node";
               type enumeration {
                 enum STRICT;
@@ -958,12 +961,12 @@ module mano-types
               list vcpu {
                 key "id";
                 description
-                    "List of vcpus to allocate on
-                     this numa node.";
+                    "List of VCPUs to allocate on
+                     this NUMA node.";
                 leaf id {
                   type uint64;
-                  description "List of vcpus ids to allocate on
-                               this numa node";
+                  description "List of VCPUs ids to allocate on
+                               this NUMA node";
                 }
               }
 
@@ -976,7 +979,7 @@ module mano-types
 
               choice om-numa-type {
                 description
-                    "Openmano Numa type selection";
+                    "OpenMANO Numa type selection";
 
                 case cores {
                   leaf num-cores {
@@ -992,7 +995,7 @@ module mano-types
 
                     list paired-thread-ids {
                       description
-                          "List of thread pairs to use in case of paired-thread numa";
+                          "List of thread pairs to use in case of paired-thread NUMA";
                       max-elements 16;
                       key thread-a;
 
@@ -1025,14 +1028,19 @@ module mano-types
       description "Container for the provider network.";
       leaf physical-network {
         description
-            "Name of the phsyical network on which the provider
+            "Name of the physical network on which the provider
              network is built.";
         type string;
       }
 
       leaf overlay-type {
         description
-            "Type of the overlay network.";
+            "Type of the overlay network.
+             LOCAL - Provider network implemented in a single compute node
+             FLAT - Provider network shared by all tenants 
+             VLAN - Provider network implemented using 802.1Q tagging
+             VXLAN - Provider networks implemented using RFC 7348
+             GRE - Provider networks implemented using GRE tunnels";
         type enumeration {
           enum LOCAL;
           enum FLAT;
@@ -1043,7 +1051,7 @@ module mano-types
       }
       leaf segmentation_id {
         description
-            "Segmentation ID";
+            "ID of segregated virtual networks";
             type uint32;
       }
     }
@@ -1089,8 +1097,8 @@ module mano-types
 
       leaf method {
         description
-          "This is the method to be performed at the uri.
-           GET by default for action";
+          "Method that the URI should perform.
+           Deafult action is GET.";
 
         type manotypes:http-method;
         default "GET";
@@ -1113,7 +1121,7 @@ module mano-types
 
     list monitoring-param {
       description
-          "List of monitoring parameters at the NS level";
+          "List of monitoring parameters at the network service level";
       key id;
       leaf id {
         type string;
@@ -1178,16 +1186,18 @@ module mano-types
       }
 
       leaf group-tag {
-        description "A simple tag to group monitoring parameters";
+        description "A tag to group monitoring parameters";
         type string;
       }
 
 
       leaf widget-type {
+        description "Defines the UI Display variant of measured counters.";
         type manotypes:widget-type;
       }
 
       leaf units {
+        description "Measured Counter Units (e.g., Packets, Kbps, Mbps, etc.)";
         type string;
       }
   }
@@ -1206,7 +1216,7 @@ module mano-types
         }
         leaf max-value {
           description
-              "Maxium value for the parameter";
+              "Maximum value for the parameter";
           type uint64;
         }
       }
@@ -1265,7 +1275,7 @@ module mano-types
       }
 
       leaf group-tag {
-        description "A simple tag to group control parameters";
+        description "A tag to group control parameters";
         type string;
       }
 
@@ -1277,7 +1287,7 @@ module mano-types
 
       leaf max-value {
         description
-            "Maxium value for the parameter";
+            "Maximum value for the parameter";
         type uint64;
       }
 
@@ -1303,15 +1313,15 @@ module mano-types
 
       leaf url {
         description
-          "This is the URL where to perform the operation";
+          "This is the URL where the operation should be performed.";
 
         type inet:uri;
       }
 
       leaf method {
         description
-          "This is the method to be performed at the uri.
-           POST by default for action";
+          "Method that the URI should perform.
+           Default Action is POST";
 
         type manotypes:http-method;
         default "POST";
@@ -1347,7 +1357,7 @@ module mano-types
       }
 
       leaf group-tag {
-        description "A simple tag to group monitoring parameter";
+        description "A tag to group monitoring parameter";
         type string;
       }
 
@@ -1377,7 +1387,7 @@ module mano-types
   }
 
   grouping input-parameter {
-    description "";
+    description "List of input parameters that can be specified when instantiating a network service.";
 
     list input-parameter {
       description
@@ -1388,7 +1398,7 @@ module mano-types
 
       leaf xpath {
         description
-          "A an xpath that specfies which element in a descriptor is to be
+          "An xpath that specfies which element in a descriptor is to be
           modified.";
         type string;
       }
@@ -1421,7 +1431,7 @@ module mano-types
       }
 
       leaf default-value {
-        description " A default value for this input parameter";
+        description "/nsd:nsd-catalog/nsd:nsd/nsd:vendor";
         type string;
       }
     }
@@ -1627,7 +1637,7 @@ module mano-types
   }
 
   typedef alarm-severity-type {
-    description "An indication of the importance or ugency of the alarm";
+    description "An indication of the importance or urgency of the alarm";
     type enumeration {
       enum LOW;
       enum MODERATE;
@@ -1646,8 +1656,8 @@ module mano-types
 
   typedef alarm-statistic-type {
     description
-        "The type of statistic to used to measure a metric to determine
-        threshold crossing for an alarm.";
+        "Statistic type to use to determine threshold crossing
+         for an alarm.";
     type enumeration {
       enum AVERAGE;
       enum MINIMUM;
@@ -1659,9 +1669,9 @@ module mano-types
 
   typedef alarm-operation-type {
     description
-        "The relational operator used to define whether an alarm should be
-        triggered when, say, the metric statistic goes above or below a
-        specified value.";
+        "The relational operator used to define whether an alarm
+        should be triggered in certain scenarios, such as if the
+        metric statistic goes above or below a specified value.";
     type enumeration {
       enum GE; // greater than or equal
       enum LE; // less than or equal
@@ -1674,8 +1684,7 @@ module mano-types
   grouping alarm {
     leaf alarm-id {
       description
-          "This field is reserved for the identifier assigned by the cloud
-          provider";
+          "This field is reserved for the identifier assigned by the VIM provider";
 
       type string;
     }
@@ -1686,7 +1695,7 @@ module mano-types
     }
 
     leaf description {
-      description "A string containing a description of this alarm";
+      description "A description of this alarm";
       type string;
     }
 
@@ -1738,7 +1747,7 @@ module mano-types
     }
 
     leaf severity {
-      description "A measure of the important or urgency of the alarm";
+      description "A measure of the importance or urgency of the alarm";
       type alarm-severity-type;
     }
 
@@ -1754,9 +1763,9 @@ module mano-types
 
     leaf operation {
       description
-          "The relational operator that defines whether the alarm should be
-          triggered when the metric statistic is, say, above or below the
-          specified threshold value.";
+          "The relational operator used to define whether an alarm should be
+           triggered in certain scenarios, such as if the metric statistic 
+           goes above or below a specified value.";
       type alarm-operation-type;
     }
 
@@ -1779,17 +1788,14 @@ module mano-types
 
     leaf evaluations {
       description
-          "This is the number of samples of the metric statistic used to
-          evaluate threshold crossing. Each sample or evaluation is equal to
-          the metric statistic obtained for a given period. This can be used
-          to mitigate spikes in the metric that may skew the statistic of
-          interest.";
+          "Defines the length of time (seconds) in which metric data are
+           collected in order to evaluate the chosen statistic.";
       type uint32;
     }
   }
 
   typedef cloud-account-type {
-    description "cloud account type";
+    description "VIM account type";
     type enumeration {
       enum aws;
       enum cloudsim;
@@ -1808,9 +1814,13 @@ module mano-types
       key "metadata-key";
       
       leaf metadata-key {
+        description
+            "Name of the additional information attached to the host-aggregate";
         type string;
       }
       leaf metadata-value {
+        description
+            "Value of the corresponding metadata-key";
         type string;
       }
     }
index 419b05b..50bbc0d 100644 (file)
@@ -101,6 +101,64 @@ module nsr
     }
   }
 
+  grouping cloud-config {
+    description "List of cloud config parameters";
+
+    list ssh-authorized-key {
+      key "key-pair-ref";
+
+      description "List of authorized ssh keys as part of cloud-config";
+
+      leaf key-pair-ref {
+        description "A reference to the key pair entry in the global key pair table";
+        type leafref {
+          path "/nsr:key-pair/nsr:name";
+        }
+      }
+    }
+    list user {
+      key "name";
+
+      description "List of users to be added through cloud-config";
+      leaf name {
+        description "Name of the user ";
+        type string;
+      }
+      leaf user-info {
+        description "The user name's real name";
+        type string;
+      }
+      list ssh-authorized-key {
+        key "key-pair-ref";
+
+        description "Used to configure the list of public keys to be injected as part 
+                        of ns instantiation";
+
+        leaf key-pair-ref {
+          description "A reference to the key pair entry in the global key pair table";
+          type leafref {
+            path "/nsr:key-pair/nsr:name";
+          }
+        }
+      }
+    }
+  }
+
+  list key-pair {
+    key "name";
+    description "Used to configure the list of public keys to be injected as part
+                 of ns instantiation";
+    leaf name {
+      description "Name of this key pair";
+      type string;
+    }
+
+    leaf key {
+      description "Key associated with this key pair";
+      type string;
+    }
+  }
+
   augment "/rw-project:project" {
     list key-pair {
       key "name";
@@ -237,7 +295,7 @@ module nsr
           NOTE: An issue with confd is preventing the
           use of xpath. Seems to be an issue with leafref
           to leafref, whose target is in a different module.
-          Once that is resovled this will switched to use
+          Once that is resolved this will switched to use
           leafref";
         type yang:uuid;
       }
@@ -254,46 +312,7 @@ module nsr
       uses manotypes:placement-group-input;
     }
 
-    list ssh-authorized-key {
-      key "key-pair-ref";
-
-      description "List of authorized ssh keys as part of cloud-config";
-
-      leaf key-pair-ref {
-        description "A reference to the key pair entry in the global key pair table";
-        type leafref {
-          path "../../../../key-pair/name";
-        }
-      }
-    }
-    list user {
-      key "name";
-
-      description "List of users to be added through cloud-config";
-      leaf name {
-        description "Name of the user ";
-        type string;
-      }
-
-      leaf user-info {
-        description "The user name's real name";
-        type string;
-      }
-
-      list ssh-authorized-key {
-        key "key-pair-ref";
-
-        description "Used to configure the list of public keys to be injected as part
-                        of ns instantiation";
-
-        leaf key-pair-ref {
-          description "A reference to the key pair entry in the global key pair table";
-          type leafref {
-            path "../../../../../key-pair/name";
-          }
-        }
-      }
-    }
+    uses cloud-config;
   }
 
   grouping vnffgr {
@@ -422,7 +441,7 @@ module nsr
             description
                 "Type of Service Function.
                 NOTE: This needs to map with Service Function Type in ODL to
-                support VNFFG. Service Function Type is manadatory param in ODL
+                support VNFFG. Service Function Type is mandatory param in ODL
                 SFC. This is temporarily set to string for ease of use";
             type string;
           }
@@ -869,7 +888,7 @@ module nsr
 
            list vnf-primitive-group {
               description
-                  "List of service primitives grouped by VNF.";
+                 "Reference to member-vnf within constituent-vnfds";
 
               key "member-vnf-index-ref";
               leaf member-vnf-index-ref {
@@ -917,7 +936,7 @@ module nsr
 
            leaf user-defined-script {
              description
-                 "A user defined script.";
+               "A user defined script.";
              type string;
            }
         }
@@ -1207,7 +1226,7 @@ module nsr
                NOTE: An issue with confd is preventing the
                use of xpath. Seems to be an issue with leafref
                to leafref, whose target is in a different module.
-               Once that is resovled this will switched to use
+               Once that is resolved this will switched to use
                leafref";
 
           type string;
@@ -1569,5 +1588,4 @@ module nsr
       }
     }
   }
-
 }
index f48f75d..fffa8ab 100644 (file)
@@ -73,7 +73,7 @@ module pnfd
         }
 
         leaf short-name {
-          description "PNFD short name.";
+          description "Short name to appear as label in the UI";
           type string;
         }
 
index ca98671..394f2ec 100644 (file)
@@ -99,7 +99,7 @@ module vlr
         }
 
         leaf short-name {
-          description "Short name for VLR for UI";
+          description "Short name to appear as label in the UI";
           type string;
         }
 
index 6ee2cb6..d6831a7 100644 (file)
@@ -29,6 +29,7 @@ from . import utils as drv_utils
 
 # Exceptions
 import keystoneclient.exceptions as KeystoneExceptions
+import neutronclient.common.exceptions as NeutronException
 
 
 class ValidationError(Exception):
@@ -137,7 +138,7 @@ class OpenstackDriver(object):
            self.cinder_drv = ci_drv.CinderDriver(self.sess_drv,
                                               region_name = region,
                                               logger = self.log)
-        except Exception as e:
+        except Exception:
            self.cinder_drv = None
         
         self.ceilo_drv = ce_drv.CeilometerDriver(self.sess_drv,
@@ -171,7 +172,10 @@ class OpenstackDriver(object):
         return self._cache['cinder']
     
     def build_resource_cache(self):
-        self.build_network_resource_cache()
+        try:
+            self.build_network_resource_cache()
+        except KeyError:
+            raise
         self.build_nova_resource_cache()
         self.build_cinder_resource_cache()
         self.build_glance_resource_cache()
@@ -215,11 +219,13 @@ class OpenstackDriver(object):
             self.log.info("Discovering management network %s", self._mgmt_network)
             network_list = self._cache_populate(self.neutron_drv.network_get,
                                                 None,
-                                                **{'network_name':self._mgmt_network})
+                                                **{'network_name': self._mgmt_network})
             if network_list:
                 self.neutron_cache['mgmt_net'] = network_list['id']
             else:
-                raise KeyError("Error")
+                msg = "Could not find management network %s" % self._mgmt_network
+                self.log.error(msg)
+                raise KeyError(msg)
 
             
     def _build_glance_image_list(self):
@@ -231,7 +237,6 @@ class OpenstackDriver(object):
     
     def _build_cinder_volume_list(self):
         self.log.info("Discovering volumes")
-        vollist = self.cinder_volume_list()
         self.cinder_cache['volumes'] = self._cache_populate(self.cinder_volume_list,
                                                            list())
         return self.cinder_cache['volumes']
@@ -244,7 +249,10 @@ class OpenstackDriver(object):
             
     def build_network_resource_cache(self):
         self.log.info("Building network resource cache")
-        self._get_neutron_mgmt_network()
+        try:
+            self._get_neutron_mgmt_network()
+        except KeyError:
+            raise
         self._build_neutron_security_group_list()
         self._build_neutron_subnet_prefix_list()
 
@@ -316,22 +324,28 @@ class OpenstackDriver(object):
             self.sess_drv.invalidate_auth_token()
             self.sess_drv.auth_token
             self.build_resource_cache()
+        except KeystoneExceptions.Unauthorized as e:
+            self.log.error("Invalid credentials ")
+            raise ValidationError("Invalid Credentials: "+ str(e))
         except KeystoneExceptions.AuthorizationFailure as e:
             self.log.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e))
             raise ValidationError("Invalid Credentials: "+ str(e))
+        except NeutronException.NotFound as e:
+            self.log.error("Given management network could not be found for Openstack account ")
+            raise ValidationError("Neutron network not found "+ str(e))
         except Exception as e:
             self.log.error("Could not connect to Openstack. Exception: %s", str(e))
             raise ValidationError("Connection Error: "+ str(e))
 
-    
+
     def glance_image_create(self, **kwargs):
-        if not 'disk_format' in kwargs:
+        if 'disk_format' not in kwargs:
             kwargs['disk_format'] = 'qcow2'
-        if not 'container_format' in kwargs:
+        if 'container_format' not in kwargs:
             kwargs['container_format'] = 'bare'
-        if not 'min_disk' in kwargs:
+        if 'min_disk' not in kwargs:
             kwargs['min_disk'] = 0
-        if not 'min_ram' in kwargs:
+        if 'min_ram' not in kwargs:
             kwargs['min_ram'] = 0
         return self.glance_drv.image_create(**kwargs)
 
@@ -374,7 +388,7 @@ class OpenstackDriver(object):
 
     def nova_server_create(self, **kwargs):
         if 'security_groups' not in kwargs:
-            kwargs['security_groups'] = [ s['name'] for s in self._nova_security_groups ]
+            kwargs['security_groups'] = [s['name'] for s in self._nova_security_groups]
         return self.nova_drv.server_create(**kwargs)
 
     def nova_server_add_port(self, server_id, port_id):
@@ -428,6 +442,9 @@ class OpenstackDriver(object):
     def nova_volume_list(self, server_id):
         return self.nova_drv.volume_list(server_id)
 
+    def neutron_extensions_list(self):
+        return self.neutron_drv.extensions_list()
+
     def neutron_network_list(self):
         return self.neutron_drv.network_list()
 
@@ -592,7 +609,7 @@ class OpenstackDriver(object):
     def cinder_volume_list(self):
         return self.cinder_drv.volume_list()
   
-    def cinder_volume_get(self,vol_id):
+    def cinder_volume_get(self, vol_id):
         return self.cinder_drv.volume_get(vol_id)
   
     def cinder_volume_set_metadata(self, volumeid, metadata):
index eac0d6c..68702bb 100644 (file)
@@ -15,7 +15,6 @@
 #   limitations under the License.
 #
 
-import contextlib
 import logging
 import os
 import subprocess
@@ -32,8 +31,6 @@ import rift.rwcal.openstack as openstack_drv
 import rw_status
 import rift.cal.rwcal_status as rwcal_status
 import rwlogger
-import neutronclient.common.exceptions as NeutronException
-import keystoneclient.exceptions as KeystoneExceptions
 
 
 from gi.repository import (
@@ -44,9 +41,9 @@ from gi.repository import (
 
 PREPARE_VM_CMD = "prepare_vm.py --auth_url {auth_url} --username {username} --password {password} --tenant_name {tenant_name} --region {region} --user_domain {user_domain} --project_domain {project_domain} --mgmt_network {mgmt_network} --server_id {server_id} --port_metadata "
 
-rwstatus_exception_map = { IndexError: RwTypes.RwStatus.NOTFOUND,
-                           KeyError: RwTypes.RwStatus.NOTFOUND,
-                           NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED,}
+rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
+                          KeyError: RwTypes.RwStatus.NOTFOUND,
+                          NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED, }
 
 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
@@ -75,9 +72,6 @@ class RwcalAccountDriver(object):
         self.log = logger
         try:
             self._driver = openstack_drv.OpenstackDriver(logger = self.log, **kwargs)
-        except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,
-                NeutronException.NotFound) as e:
-            raise
         except Exception as e:
             self.log.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e)))
             raise
@@ -154,34 +148,11 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             Validation Code and Details String
         """
         status = RwcalYang.CloudConnectionStatus()
-        drv = self._use_driver(account) 
         try:
+            drv = self._use_driver(account) 
             drv.validate_account_creds()
-        except KeystoneExceptions.Unauthorized as e:
-            self.log.error("Invalid credentials given for VIM account %s", account.name)
-            status.status = "failure"
-            status.details = "Invalid Credentials: %s" % str(e)
-
-        except KeystoneExceptions.AuthorizationFailure as e:
-            self.log.error("Bad authentication URL given for VIM account %s. Given auth url: %s",
-                           account.name, account.openstack.auth_url)
-            status.status = "failure"
-            status.details = "Invalid auth url: %s" % str(e)
-
-        except NeutronException.NotFound as e:
-            self.log.error("Given management network %s could not be found for VIM account %s",
-                           account.openstack.mgmt_network,
-                           account.name)
-            status.status = "failure"
-            status.details = "mgmt network does not exist: %s" % str(e)
-
-        except openstack_drv.ValidationError as e:
-            self.log.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
-            status.status = "failure"
-            status.details = "Invalid Credentials: %s" % str(e)
-
         except Exception as e:
-            msg = "RwcalOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
+            msg = "RwcalOpenstackPlugin: Exception: %s" %(str(e))
             self.log.error(msg)
             status.status = "failure"
             status.details = msg
@@ -400,11 +371,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             kwargs['image_id']  = vminfo.image_id
 
         ### If floating_ip is required and we don't have one, better fail before any further allocation
-        pool_name = None
         floating_ip = False
         if vminfo.has_field('allocate_public_address') and vminfo.allocate_public_address:
-            if account.openstack.has_field('floating_ip_pool'):
-                pool_name = account.openstack.floating_ip_pool
             floating_ip = True
 
         if vminfo.has_field('cloud_init') and vminfo.cloud_init.has_field('userdata'):
@@ -441,7 +409,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             kwargs['availability_zone'] = None
 
         if vminfo.has_field('server_group'):
-            kwargs['scheduler_hints'] = {'group': vminfo.server_group }
+            kwargs['scheduler_hints'] = {'group': vminfo.server_group}
         else:
             kwargs['scheduler_hints'] = None
 
@@ -540,10 +508,10 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             if key in vm.user_tags.fields:
                 setattr(vm.user_tags, key, value)
         if 'OS-EXT-SRV-ATTR:host' in vm_info:
-            if vm_info['OS-EXT-SRV-ATTR:host'] != None:
+            if vm_info['OS-EXT-SRV-ATTR:host'] is not None:
                 vm.host_name = vm_info['OS-EXT-SRV-ATTR:host']
         if 'OS-EXT-AZ:availability_zone' in vm_info:
-            if vm_info['OS-EXT-AZ:availability_zone'] != None:
+            if vm_info['OS-EXT-AZ:availability_zone'] is not None:
                 vm.availability_zone = vm_info['OS-EXT-AZ:availability_zone']
         return vm
 
@@ -680,7 +648,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         network                  = RwcalYang.NetworkInfoItem()
         network.network_name     = network_info['name']
         network.network_id       = network_info['id']
-        if ('provider:network_type' in network_info) and (network_info['provider:network_type'] != None):
+        if ('provider:network_type' in network_info) and (network_info['provider:network_type'] is not None):
             network.provider_network.overlay_type = network_info['provider:network_type'].upper()
         if ('provider:segmentation_id' in network_info) and (network_info['provider:segmentation_id']):
             network.provider_network.segmentation_id = network_info['provider:segmentation_id']
@@ -1068,7 +1036,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                 cmd += (" --vol_metadata {}").format(tmp_file.name)
             
         exec_path = 'python3 ' + os.path.dirname(openstack_drv.__file__)
-        exec_cmd = exec_path+'/'+cmd
+        exec_cmd = exec_path + '/' + cmd
         self.log.info("Running command: %s" %(exec_cmd))
         subprocess.call(exec_cmd, shell=True)
 
index f2d5b08..dcd653e 100644 (file)
@@ -109,62 +109,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;
index fbbc695..d74f695 100644 (file)
@@ -753,7 +753,7 @@ class ConfigManagerConfig(object):
                 vdu_data = []
                 for vdu in vnfr['vdur']:
                     d = {}
-                    for k in ['name','management_ip', 'vm_management_ip', 'id']:
+                    for k in ['name','management_ip', 'vm_management_ip', 'id', 'vdu_id_ref']:
                         if k in vdu:
                             d[k] = vdu[k]
                     vdu_data.append(d)
index 6bebc9d..fefbd3e 100644 (file)
@@ -44,7 +44,7 @@ rw-project-nsd
 rw-project-vnfd
 rw-resource-mgr
 rw-restportforward
-rwsdn
+rwsdnal
 rw-sdn
 rwshell-mgmt
 rw-sorch
index 3ce3500..dc31b68 100644 (file)
@@ -161,6 +161,16 @@ class DescriptorPackage(object):
 
         return self.descriptor_msg.id
 
+    @property
+    def descriptor_name(self):
+        """  The descriptor name of this descriptor in the system """
+        if not self.descriptor_msg.has_field("name"):
+            msg = "Descriptor name not present"
+            self._log.error(msg)
+            raise PackageError(msg)
+
+        return self.descriptor_msg.name
+
     @classmethod
     def get_descriptor_patterns(cls):
         """ Returns a tuple of descriptor regex and Package Types  """
@@ -342,9 +352,14 @@ class DescriptorPackage(object):
 
         try:
             # Copy the contents of the file to the correct path
+            # For folder creation (or nested folders), dest_file appears w/ trailing "/" like: dir1/ or dir1/dir2/
+            # For regular file upload, dest_file appears as dir1/abc.txt
             dest_dir_path = os.path.dirname(dest_file)
             if not os.path.isdir(dest_dir_path):
                 os.makedirs(dest_dir_path)
+                if not os.path.basename(dest_file): 
+                    self._log.debug("Created dir path, no filename to insert in {}, skipping..".format(dest_dir_path))
+                    return
 
             with open(dest_file, 'wb') as dst_hdl:
                 with open(new_file, 'rb') as src_hdl:
index 0fcd905..2bb4d18 100644 (file)
@@ -225,7 +225,6 @@ class OpenmanoVnfr(object):
 
         self._created = True
 
-    @asyncio.coroutine
     def delete(self):
         if not self._created:
             return
@@ -235,11 +234,7 @@ class OpenmanoVnfr(object):
             self._log.warning("Openmano vnf id not set.  Cannot delete.")
             return
 
-        yield from self._loop.run_in_executor(
-                None,
-                self._cli_api.vnf_delete,
-                self._vnf_id,
-                )
+        self._cli_api.vnf_delete(self._vnf_id)
 
 
 class OpenmanoNSRecordState(Enum):
@@ -260,6 +255,7 @@ class OpenmanoNSRecordState(Enum):
 
 class OpenmanoNsr(object):
     TIMEOUT_SECS = 300
+    INSTANCE_TERMINATE_TIMEOUT = 60
 
     def __init__(self, dts, log, loop, publisher, cli_api, http_api, nsd_msg, nsr_config_msg,key_pairs):
         self._dts = dts
@@ -446,23 +442,20 @@ class OpenmanoNsr(object):
         yield from vnfr.create()
         self._vnfrs.append(vnfr)
 
-    @asyncio.coroutine
     def delete(self):
         if not self._created:
             self._log.debug("NSD wasn't created.  Skipping delete.")
             return
 
         self._log.debug("Deleting openmano nsr")
-
-        yield from self._loop.run_in_executor(
-               None,
-               self._cli_api.ns_delete,
-               self._nsd_uuid,
-               )
+        self._cli_api.ns_delete(self._nsd_uuid)
 
         self._log.debug("Deleting openmano vnfrs")
+        deleted_vnf_id_list = []
         for vnfr in self._vnfrs:
-            yield from vnfr.delete()
+            if vnfr.vnfr.vnfd.id not in deleted_vnf_id_list:
+                vnfr.delete()
+                deleted_vnf_id_list.append(vnfr.vnfr.vnfd.id)
 
 
     @asyncio.coroutine
@@ -678,7 +671,6 @@ class OpenmanoNsr(object):
                         yield from self._publisher.publish_vnfr(None, vnfr_msg)
                         active_vnfs.append(vnfr)
 
-
                 except Exception as e:
                     vnfr_msg.operational_status = "failed"
                     self._state = OpenmanoNSRecordState.FAILED
@@ -725,26 +717,22 @@ class OpenmanoNsr(object):
                 self.instance_monitor_task(), loop=self._loop
                 )
 
-    @asyncio.coroutine
     def terminate(self):
-
-        for _,handler in  self._vdur_console_handler.items():
-            handler._regh.deregister()
-
         if self._nsr_uuid is None:
-            self._log.warning("Cannot terminate an un-instantiated nsr")
-            return
+            start_time = time.time()
+            while ((time.time() - start_time) < OpenmanoNsr.INSTANCE_TERMINATE_TIMEOUT) and (self._nsr_uuid is None):
+                time.sleep(5)
+                self._log.warning("Waiting for nsr to get instatiated")
+            if self._nsr_uuid is None:
+                self._log.warning("Cannot terminate an un-instantiated nsr")
+                return
 
         if self._monitor_task is not None:
             self._monitor_task.cancel()
             self._monitor_task = None
 
         self._log.debug("Terminating openmano nsr")
-        yield from self._loop.run_in_executor(
-               None,
-               self._cli_api.ns_terminate,
-               self._nsr_uuid,
-               )
+        self._cli_api.ns_terminate(self._nsr_uuid)
 
     @asyncio.coroutine
     def create_vlr(self,vlr):
@@ -914,8 +902,14 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase):
         nsr_id = nsr.id
         openmano_nsr = self._openmano_nsrs[nsr_id]
 
-        yield from openmano_nsr.terminate()
-        yield from openmano_nsr.delete()
+        for _,handler in openmano_nsr._vdur_console_handler.items():
+            handler._regh.deregister()
+
+        yield from self._loop.run_in_executor(
+               None,
+               self.terminate,
+               openmano_nsr,
+               )
 
         with self._dts.transaction() as xact:
             for vnfr in openmano_nsr.vnfrs:
@@ -924,6 +918,10 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase):
 
         del self._openmano_nsrs[nsr_id]
 
+    def terminate(self, openmano_nsr):
+        openmano_nsr.terminate()
+        openmano_nsr.delete()
+
     @asyncio.coroutine
     def terminate_vnf(self, vnfr):
         """
index 4aed527..ea17e60 100755 (executable)
@@ -51,7 +51,7 @@ from gi.repository import (
     VnfrYang,
     RwVnfrYang,
     RwNsmYang,
-    RwsdnYang,
+    RwsdnalYang,
     RwDts as rwdts,
     RwTypes,
     ProtobufC,
@@ -395,7 +395,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
@@ -2041,7 +2041,7 @@ class NetworkServiceRecord(object):
                    (member_vnfd.member_vnf_index_ref == str(const_vnfd.member_vnf_index)):
                     group_info = self.resolve_placement_group_cloud_construct(group)
                     if group_info is None:
-                        self._log.error("Could not resolve cloud-construct for placement group: %s", group.name)
+                        self._log.info("Could not resolve cloud-construct for placement group: %s", group.name)
                         ### raise PlacementGroupError("Could not resolve cloud-construct for placement group: {}".format(group.name))
                     else:
                         self._log.info("Successfully resolved cloud construct for placement group: %s for VNF: %s (Member Index: %s)",
index 81f22e9..0b04efd 100755 (executable)
@@ -19,7 +19,7 @@ import asyncio
 
 from gi.repository import (
     RwDts as rwdts,
-    RwsdnYang,
+    RwsdnalYang,
     RwTypes,
     ProtobufC,
 )
@@ -85,7 +85,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
@@ -108,7 +108,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]
@@ -178,7 +178,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
 
@@ -236,7 +236,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
index 4be45d0..eca40c2 100644 (file)
@@ -37,14 +37,16 @@ rift_python_install_tree(
     rift/tasklets/${TASKLET_NAME}/rpc.py
     rift/tasklets/${TASKLET_NAME}/downloader/__init__.py
     rift/tasklets/${TASKLET_NAME}/downloader/url.py
+    rift/tasklets/${TASKLET_NAME}/downloader/copy.py
     rift/tasklets/${TASKLET_NAME}/proxy/__init__.py
     rift/tasklets/${TASKLET_NAME}/proxy/base.py
     rift/tasklets/${TASKLET_NAME}/proxy/filesystem.py
     rift/tasklets/${TASKLET_NAME}/publisher/__init__.py
     rift/tasklets/${TASKLET_NAME}/publisher/download_status.py
+    rift/tasklets/${TASKLET_NAME}/publisher/copy_status.py
     rift/tasklets/${TASKLET_NAME}/subscriber/__init__.py
     rift/tasklets/${TASKLET_NAME}/subscriber/download_status.py
   COMPONENT ${PKG_LONG_NAME}
   PYTHON3_ONLY)
 
-rift_add_subdirs(test)
\ No newline at end of file
+rift_add_subdirs(test)
diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/downloader/copy.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/downloader/copy.py
new file mode 100644 (file)
index 0000000..fa3dd3e
--- /dev/null
@@ -0,0 +1,199 @@
+# 
+#   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.
+#
+#   Author(s): Nandan Sinha
+#
+
+import os
+import uuid
+import shutil 
+import enum
+
+import gi
+gi.require_version('RwVnfdYang', '1.0')
+gi.require_version('RwNsdYang', '1.0')
+from gi.repository import (
+    RwYang, 
+    NsdYang,
+    VnfdYang,
+    RwVnfdYang,
+    RwNsdYang,
+    RwPkgMgmtYang
+)
+
+class PackageCopyError(Exception): 
+    pass
+
+class CopyStatus(enum.Enum):
+    UNINITIATED = 0
+    STARTED = 1
+    IN_PROGRESS = 2
+    COMPLETED = 3
+    FAILED = 4
+    CANCELLED = 5
+
+TaskStatus = RwPkgMgmtYang.TaskStatus
+
+class CopyMeta:
+    STATUS_MAP = {
+        CopyStatus.STARTED: TaskStatus.QUEUED.value_nick.upper(),
+        CopyStatus.UNINITIATED: TaskStatus.QUEUED.value_nick.upper(),
+        CopyStatus.IN_PROGRESS: TaskStatus.IN_PROGRESS.value_nick.upper(),
+        CopyStatus.COMPLETED: TaskStatus.COMPLETED.value_nick.upper(),
+        CopyStatus.FAILED: TaskStatus.FAILED.value_nick.upper(),
+        CopyStatus.CANCELLED: TaskStatus.CANCELLED.value_nick.upper()
+        }
+
+    def __init__(self, transaction_id):
+        self.transaction_id = transaction_id
+        self.state = CopyStatus.UNINITIATED
+
+    def set_state(self, state):
+        self.state = state
+
+    def as_dict(self): 
+        return self.__dict__
+
+    def to_yang(self):
+        job = RwPkgMgmtYang.CopyJob.from_dict({
+            "transaction_id": self.transaction_id, 
+            "status": CopyMeta.STATUS_MAP[self.state]
+            })
+        return job
+
+class PackageFileCopier:
+    DESCRIPTOR_MAP = {
+            "vnfd": (RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd, 'vnfd rw-vnfd'), 
+            "nsd" : (RwNsdYang.YangData_Nsd_NsdCatalog_Nsd, 'nsd rw-nsd') 
+            }
+
+    @classmethod
+    def from_rpc_input(cls, rpc_input, proxy, log=None): 
+        return cls(
+                rpc_input.package_id,
+                rpc_input.package_type, 
+                rpc_input.package_name,
+                proxy = proxy,
+                log=log)
+
+    def __init__(self, 
+            pkg_id, 
+            pkg_type, 
+            pkg_name, 
+            proxy, 
+            log):
+        self.src_package_id = pkg_id
+        self.package_type = pkg_type.lower()
+        self.dest_package_name = pkg_name
+        self.dest_package_id = str(uuid.uuid4())
+        self.transaction_id = str(uuid.uuid4())
+        self.proxy = proxy
+        self.log = log
+        self.meta = CopyMeta(self.transaction_id)
+        self.src_package = None
+        self.dest_desc_msg = None
+
+    # Start of delegate calls
+    def call_delegate(self, event):
+        if not self.delegate:
+            return
+        
+        # Send out the descriptor message to be posted on success
+        # Otherwise send out the CopyJob yang conversion from meta object.
+        if event == "on_download_succeeded":
+            getattr(self.delegate, event)(self.dest_desc_msg)
+        else:
+            getattr(self.delegate, event)(self.meta.to_yang())
+
+    def _copy_tree(self):
+        """
+        Locate directory tree of the source descriptor folder. 
+        Copy directory tree to destination descriptor folder.  
+
+        """
+        store = self.proxy._get_store(self.package_type)
+        src_path = store._get_package_dir(self.src_package_id)
+        self.src_package = store.get_package(self.src_package_id) 
+        src_desc_name = self.src_package.descriptor_name
+        src_copy_path = os.path.join(src_path, src_desc_name)
+
+        self.dest_copy_path = os.path.join(store.DEFAULT_ROOT_DIR, 
+                self.dest_package_id, 
+                self.dest_package_name)
+        self.log.debug("Copying contents from {src} to {dest}".
+                format(src=src_copy_path, dest=self.dest_copy_path))
+
+        shutil.copytree(src_copy_path, self.dest_copy_path)
+
+    def _create_descriptor_file(self):
+        """ Update descriptor file for the newly copied descriptor catalog.
+        Use the existing descriptor file to create a descriptor proto gi object,
+        change some identifiers, and create a new descriptor yaml file from it.
+
+        """
+        src_desc_file = self.src_package.descriptor_file
+        src_desc_contents = self.src_package.descriptor_msg.as_dict()
+        src_desc_contents.update(
+                id =self.dest_package_id, 
+                name = self.dest_package_name,
+                short_name = self.dest_package_name
+                )
+
+        desc_cls, modules = PackageFileCopier.DESCRIPTOR_MAP[self.package_type]
+        self.dest_desc_msg = desc_cls.from_dict(src_desc_contents)
+        dest_desc_path = os.path.join(self.dest_copy_path, 
+                "{pkg_name}_{pkg_type}.yaml".format(pkg_name=self.dest_package_name, pkg_type=self.package_type))
+        model = RwYang.Model.create_libncx()
+        for module in modules.split():
+            model.load_module(module) 
+
+        with open(dest_desc_path, "w") as fh:
+            fh.write(self.dest_desc_msg.to_yaml(model))
+
+        copied_desc_file = os.path.join(self.dest_copy_path, os.path.basename(src_desc_file))
+        if os.path.exists(copied_desc_file):
+            self.log.debug("Deleting copied yaml from old source %s" % (copied_desc_file))
+            os.remove(copied_desc_file)
+
+    def copy(self):
+        try:
+            if self.package_type not in PackageFileCopier.DESCRIPTOR_MAP: 
+                raise PackageCopyError("Package type {} not currently supported for copy operations".format(self.package_type))
+
+            self._copy_tree()
+            self._create_descriptor_file()
+            self.copy_succeeded()
+
+        except Exception as e: 
+            self.log.exception(str(e))
+            self.copy_failed()
+
+        self.copy_finished()
+
+    def copy_failed(self):
+        self.meta.set_state(CopyStatus.FAILED)
+        self.call_delegate("on_download_failed")
+
+    def copy_progress(self): 
+        self.meta.set_state(CopyStatus.IN_PROGRESS)
+        self.call_delegate("on_download_progress")
+
+    def copy_succeeded(self):
+        self.meta.set_state(CopyStatus.COMPLETED)
+        self.call_delegate("on_download_succeeded")
+
+    def copy_finished(self): 
+        self.call_delegate("on_download_finished") 
+
diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/copy_status.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/copy_status.py
new file mode 100644 (file)
index 0000000..927331c
--- /dev/null
@@ -0,0 +1,124 @@
+# 
+#   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.
+#
+#   Author(s): Nandan Sinha
+#
+
+import sys
+import asyncio
+import uuid
+import abc
+import functools 
+from concurrent.futures import Future
+
+from gi.repository import (RwDts as rwdts)
+import rift.mano.dts as mano_dts
+import rift.downloader as url_downloader
+import rift.tasklets.rwlaunchpad.onboard as onboard 
+
+if sys.version_info < (3, 4, 4): 
+    asyncio.ensure_future = asyncio.async
+
+
+class CopyStatusPublisher(mano_dts.DtsHandler, url_downloader.DownloaderProtocol): 
+
+    def __init__(self, log, dts, loop, tasklet_info):
+        super().__init__(log, dts, loop) 
+        self.tasks = {} 
+        self.tasklet_info = tasklet_info
+
+    def xpath(self, transaction_id=None):
+        return ("D,/rw-pkg-mgmt:copy-jobs/rw-pkg-mgmt:job" +
+            ("[transaction-id='{}']".format(transaction_id) if transaction_id else ""))
+        pass
+    
+    @asyncio.coroutine
+    def register(self):
+        self.reg = yield from self.dts.register(xpath=self.xpath(),
+                  flags=rwdts.Flag.PUBLISHER|rwdts.Flag.CACHE|rwdts.Flag.NO_PREP_READ)
+
+        assert self.reg is not None
+
+    @asyncio.coroutine
+    def register_copier(self, copier):
+        copier.delegate = self
+        future = self.loop.run_in_executor(None, copier.copy)
+        self.tasks[copier.transaction_id] = (copier, future)
+
+        return (copier.transaction_id, copier.dest_package_id)
+
+    @asyncio.coroutine
+    def _dts_publisher(self, job_msg): 
+        # Publish the download state 
+        self.reg.update_element(
+                self.xpath(transaction_id=job_msg.transaction_id), job_msg) 
+
+    @staticmethod
+    def _async_add(func, fut): 
+        try: 
+            ret = func()
+            fut.set_result(ret) 
+        except Exception as e: 
+            fut.set_exception(e) 
+
+    def _schedule_dts_work(self, job_msg): 
+        f = functools.partial( 
+                asyncio.ensure_future, 
+                self._dts_publisher(job_msg), 
+                loop = self.loop)
+        fut = Future()
+        self.loop.call_soon_threadsafe(CopyStatusPublisher._async_add, f, fut) 
+        xx = fut.result()
+        if fut.exception() is not None:
+            self.log.error("Caught future exception during download: %s type %s", str(fut.exception()), type(fut.exception()))
+            raise fut.exception()
+        return xx
+
+    def on_download_progress(self, job_msg):
+        """callback that triggers update.
+        """
+        return self._schedule_dts_work(job_msg) 
+
+    def on_download_finished(self, job_msg):
+        """callback that triggers update.
+        """
+        # clean up the local cache
+        key = job_msg.transaction_id
+        if key in self.tasks:
+            del self.tasks[key]
+
+        return self._schedule_dts_work(job_msg)
+
+    def on_download_succeeded(self, job_msg): 
+        """Post the catalog descriptor object to the http endpoint.
+        Argument: job_msg (proto-gi descriptor_msg of the copied descriptor)
+
+        """
+        manifest = self.tasklet_info.get_pb_manifest()
+        use_ssl = manifest.bootstrap_phase.rwsecurity.use_ssl
+        ssl_cert, ssl_key = None, None 
+        if use_ssl:
+            ssl_cert = manifest.bootstrap_phase.rwsecurity.cert
+            ssl_key = manifest.bootstrap_phase.rwsecurity.key
+
+        onboarder = onboard.DescriptorOnboarder(self.log, 
+                "127.0.0.1", 8008, use_ssl, ssl_cert, ssl_key)
+        try:
+            onboarder.onboard(job_msg)
+        except onboard.OnboardError as e: 
+            self.log.error("Onboard exception triggered while posting copied catalog descriptor %s", e)
+            raise 
+
+
index c207b43..d360640 100644 (file)
@@ -34,6 +34,7 @@ RPC_PKG_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_GetPackageEndpoint
 RPC_SCHEMA_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_GetPackageSchema
 RPC_PACKAGE_ADD_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageFileAdd
 RPC_PACKAGE_DELETE_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageFileDelete
+RPC_PACKAGE_COPY_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageCopy
 
 
 class EndpointDiscoveryRpcHandler(mano_dts.AbstractRpcHandler):
@@ -67,12 +68,12 @@ class EndpointDiscoveryRpcHandler(mano_dts.AbstractRpcHandler):
 class SchemaRpcHandler(mano_dts.AbstractRpcHandler):
     """RPC handler to generate the schema for the packages.
     """
-    def __init__(self, log, dts, loop, proxy):
+    def __init__(self, log, dts, loop, project, proxy):
         """
         Args:
             proxy: Any impl of .proxy.AbstractPackageManagerProxy
         """
-        super().__init__(log, dts, loop)
+        super().__init__(log, dts, loop, project)
         self.proxy = proxy
 
     @property
@@ -158,6 +159,33 @@ class PackageOperationsRpcHandler(mano_dts.AbstractRpcHandler):
 
         return rpc_op
 
+class PackageCopyOperationsRpcHandler(mano_dts.AbstractRpcHandler):
+    def __init__(self, log, dts, loop, project, proxy, publisher):
+        """
+        Args:
+            proxy: Any impl of .proxy.AbstractPackageManagerProxy
+            publisher: CopyStatusPublisher object
+        """
+        super().__init__(log, dts, loop, project)
+        self.proxy = proxy
+        self.publisher = publisher
+
+    @property
+    def xpath(self):
+        return "/rw-pkg-mgmt:package-copy"
+
+    @asyncio.coroutine
+    def callback(self, ks_path, msg):
+        import uuid 
+        copier = pkg_downloader.PackageFileCopier.from_rpc_input(msg, proxy=self.proxy, log=self.log)
+
+        transaction_id, dest_package_id = yield from self.publisher.register_copier(copier)
+        rpc_op = RPC_PACKAGE_COPY_ENDPOINT.from_dict({
+            "transaction_id":transaction_id,
+            "package_id":dest_package_id, 
+            "package_type":msg.package_type})
+
+        return rpc_op
 
 class PackageDeleteOperationsRpcHandler(mano_dts.AbstractRpcHandler):
     def __init__(self, log, dts, loop, proxy):
index 0a93ade..0fcabe3 100644 (file)
@@ -47,22 +47,31 @@ class PackageManagerProject(ManoProject):
     def __init__(self, name, tasklet, **kw):
         super(PackageManagerProject, self).__init__(tasklet.log, name)
         self.update(tasklet)
+        proxy = kw["proxy"]
 
         args = [self.log, self.dts, self.loop, self]
         self.job_handler = pkg_publisher.DownloadStatusPublisher(*args)
+        self.copy_publisher = pkg_publisher.CopyStatusPublisher(*args + [self.tasklet.tasklet_info])
+
         # create catalog subscribers 
         self.vnfd_catalog_sub = subscriber.VnfdStatusSubscriber(*args)
         self.nsd_catalog_sub = subscriber.NsdStatusSubscriber(*args)
-
+        
+        args.append(proxy)
+        self.copy_rpc = rpc.PackageCopyOperationsRpcHandler(*(args + [self.copy_publisher]))
 
     @asyncio.coroutine
     def register (self):
         yield from self.vnfd_catalog_sub.register()
         yield from self.nsd_catalog_sub.register()
+        yield from self.copy_rpc.register()
+        yield from self.copy_publisher.register()
         yield from self.job_handler.register()
 
     def deregister (self):
         yield from self.job_handler.deregister()
+        yield from self.copy_rpc.deregister()
+        yield from self.copy_publisher.deregister()
         yield from self.vnfd_catalog_sub.deregister()
         yield from self.nsd_catalog_sub.deregister()
 
@@ -82,27 +91,35 @@ class PackageManagerTasklet(rift.tasklets.Tasklet):
             self.log.exception(e)
 
     def start(self):
-        super().start()
 
         self.log.debug("Registering with dts")
 
-        self.dts = rift.tasklets.DTS(
+        try:
+            super().start()
+            self.dts = rift.tasklets.DTS(
                 self.tasklet_info,
                 RwPkgMgmtYang.get_schema(),
                 self.loop,
                 self.on_dts_state_change
                 )
 
-        proxy = filesystem.FileSystemProxy(self.loop, self.log)
+            proxy = filesystem.FileSystemProxy(self.loop, self.log)
+            args = [self.log, self.dts, self.loop]
 
-        args = [self.log, self.dts, self.loop]
-        args.append(proxy)
-        self.endpoint_rpc = rpc.EndpointDiscoveryRpcHandler(*args)
-        self.schema_rpc = rpc.SchemaRpcHandler(*args)
-        self.delete_rpc = rpc.PackageDeleteOperationsRpcHandler(*args)
+            args.append(proxy)
+            self.endpoint_rpc = rpc.EndpointDiscoveryRpcHandler(*args)
+            self.schema_rpc = rpc.SchemaRpcHandler(*args)
+            self.delete_rpc = rpc.PackageDeleteOperationsRpcHandler(*args)
+
+            args.append(self)
+            self.pkg_op = rpc.PackageOperationsRpcHandler(*args)
 
-        args.append(self)
-        self.pkg_op = rpc.PackageOperationsRpcHandler(*args)
+            self.project_handler = ProjectHandler(self, PackageManagerProject,
+                                                  proxy=proxy,)
+        except Exception as e:
+            self.log.exception("Exception caught rwpkgmgr start: %s", str(e))
+        else:
+            self.log.debug("rwpkgmgr started successfully!")
 
     def stop(self):
         try:
index 1720e39..ba3c5a2 100755 (executable)
@@ -16,8 +16,6 @@
 #
 
 import asyncio
-import logging
-import os
 import sys
 
 import gi
@@ -25,10 +23,8 @@ 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
@@ -36,6 +32,7 @@ from rift.mano.utils.project import (
     ManoProject,
     ProjectHandler,
 )
+import rift.mano.sdn
 
 from rift.vlmgr import (
     VlrDtsHandler,
@@ -47,20 +44,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
@@ -70,218 +56,48 @@ 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, project):
         self._log = log
+        self._log_hdl = log_hdl
+        self._dts = dts
         self._loop = loop
-        self._parent = parent
-        self._project = self._parent._project
-        self._regh = None
-        self._rpch = None
-
-    def _register_show_status(self):
-        def get_xpath(sdn_name=None):
-            return self._project.add_project("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)
-
-        self._regh = 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 self._project.rpc_check(msg, xact_info=xact_info):
-                return
-
-            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)
+        self._acctstore = acctstore
+        self._project = project
+  
+        self._log.debug("Creating SDN account config handler")
+        self.sdn_cfg_handler = rift.mano.sdn.SDNAccountConfigSubscriber(
+              self._dts, self._log, project, 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._rpch = 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()
-
-    def deregister(self):
-        self._log.debug("De-register SDN opdata handler for project {}".
-                        format(self._project.name))
-        if self._regh:
-            self._regh.deregister()
-            self._regh = None
-
-        if self._rpch:
-            self._rpch.deregister()
-            self._rpch = None
-
-
-class SDNAccountDtsHandler(object):
-    XPATH = "C,/rw-sdn:sdn/rw-sdn:account"
-
-    def __init__(self, dts, log, parent):
-        self._dts = dts
-        self._log = log
-        self._parent = parent
-        self._project = parent._project
-
-        self._sdn_account = {}
-        self._regh = None
-
-    @property
-    def _xpath(self):
-        return self._project.add_project(SDNAccountDtsHandler.XPATH)
-
-    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._log.debug("Creating SDN account opdata handler")
+        self.sdn_operdata_handler = rift.mano.sdn.SDNAccountDtsOperdataHandler(
+              self._dts, self._log, self._loop, project,
+        )
+  
+    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,
-                                                   self._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,
-                                                   self._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: {}".
-                        format(self._xpath))
-
-        acg_handler = rift.tasklets.AppConfGroup.Handler(
-                        on_apply=apply_config,
-                        )
-
-        with self._dts.appconf_group_create(acg_handler) as acg:
-            self._regh = acg.register(
-                xpath=self._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()
 
     def deregister(self):
-        self._log.debug("De-register VLR handler for project {}".
-                        format(self._project.name))
-        if self._regh:
-            self._regh.deregister()
-            self._regh = None
+        self.sdn_cfg_handler.deregister()
+        self.sdn_operdata_handler.deregister()
 
 
 class VnsManager(object):
@@ -292,17 +108,15 @@ class VnsManager(object):
         self._log_hdl = log_hdl
         self._loop = loop
         self._project = project
-
+        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._project)
+        self._sdn_handlers = SDNAccountHandlers(dts, log, log_hdl, self._acctstore, loop, project)
         self._nwtopdata_store = NwtopDataStore(log)
-        self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, self._project,
-                                                                self._acctmgr, self._nwtopdata_store)
-        self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, self._project,
-                                                          self._acctmgr, self._nwtopdata_store)
+        self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, project,
+                                                                self._acctstore, self._nwtopdata_store)
+        self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, project,
+                                                          self._acctstore, self._nwtopdata_store)
         self._vlrs = {}
 
     @asyncio.coroutine
@@ -318,11 +132,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):
@@ -339,11 +152,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 deregister(self):
@@ -351,8 +163,7 @@ class VnsManager(object):
         self._nwtopstatic_handler.deregister()
         self._vld_handler.deregister()
         self._vlr_handler.deregister()
-        self._sdn_opdata_handler.deregister()
-        self._sdn_handler.deregister()
+        self._sdn_handlers.deregister()
 
     def create_vlr(self, msg):
         """ Create VLR """
index f570abc..1f750bd 100644 (file)
@@ -20,7 +20,6 @@
 from .rwtopmgr import (
     NwtopDiscoveryDtsHandler,
     NwtopStaticDtsHandler,
-    SdnAccountMgr,
 )
 
 from .rwtopdatastore import (
index e1c7ea1..7f6b4dd 100755 (executable)
@@ -28,170 +28,24 @@ 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, project):
-        self._account = {}
-        self._log = log
-        self._log_hdl = log_hdl
-        self._loop = loop
-        self._project = project
-        self._sdn = {}
-
-        self._regh = None
-
-        self._status = RwsdnYang.SdnConnectionStatus(
-                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.SdnConnectionStatus.from_dict(status.as_dict())
-        else:
-            self._status = RwsdnYang.SdnConnectionStatus(
-                    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, project, acctmgr, nwdatastore):
+    def __init__(self, dts, log, loop, project, acctstore, nwdatastore):
         self._dts = dts
         self._log = log
         self._loop = loop
         self._project = project
-        self._acctmgr = acctmgr
+        self._acctstore = acctstore
         self._nwdatastore = nwdatastore
 
         self._regh = None
@@ -228,16 +82,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)
@@ -277,12 +127,12 @@ class NwtopStaticDtsHandler(object):
     """ Handles DTS interactions for the Static Topology registration """
     STATIC_XPATH = "C,/nd:network"
 
-    def __init__(self, dts, log, loop, project, acctmgr, nwdatastore):
+    def __init__(self, dts, log, loop, project, acctstore, nwdatastore):
         self._dts = dts
         self._log = log
         self._loop = loop
         self._project = project
-        self._acctmgr = acctmgr
+        self._acctstore = acctstore
 
         self._regh = None
         self.pending = {}
index 45e2e80..6121747 100644 (file)
@@ -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 (file)
index 0000000..b4dda0e
--- /dev/null
@@ -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 (file)
index 0000000..05fc3f7
--- /dev/null
@@ -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()
+
+
+
+
index d216f0d..e9cd0b3 100644 (file)
@@ -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"
index 44e2f5c..06b59c3 100755 (executable)
@@ -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
index 7e7ee2f..d3aa299 100644 (file)
@@ -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 rw_project_yang-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 rwproject_yang
+  DEPENDS rwcal_yang rwsdnal_yang mano_yang rwlog_gi rwschema_yang rwproject_yang
   )
 
 rift_install_vala_artifacts(
index ec4ab31..a4d597d 100644 (file)
@@ -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);
 
 
index 2c0ffcc..fc0d86e 100644 (file)
@@ -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'
index 3eb39fc..2727d8a 100644 (file)
@@ -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']
index ff5d019..a9ff983 100644 (file)
 #   limitations under the License.
 #
 
-import contextlib
 import logging
 
 import gi
 gi.require_version('RwSdn', '1.0')
-gi.require_version('RwCal', '1.0')
-gi.require_version('RwcalYang', '1.0')
 
+import rift.rwcal.openstack as openstack_drv
 from rift.rwcal.openstack import session as sess_drv
 from rift.rwcal.openstack import keystone as ks_drv
 from rift.rwcal.openstack import neutron as nt_drv
@@ -39,15 +37,13 @@ import keystoneclient.exceptions as KeystoneExceptions
 
 from gi.repository import (
     GObject,
-    RwCal,
-    RwSdn, # Vala package
-    RwsdnYang,
-    RwTypes,
-    RwcalYang)
+    RwSdn,  # Vala package
+    RwsdnalYang,
+    RwTypes)
 
-rwstatus_exception_map = { IndexError: RwTypes.RwStatus.NOTFOUND,
+rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
                            KeyError: RwTypes.RwStatus.NOTFOUND,
-                           NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED,}
+                           NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED, }
 
 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
@@ -94,6 +90,7 @@ class OpenstackL2PortChainingDriver(object):
                      project_domain_name = kwargs['project_domain'] if 'project_domain' in kwargs else None,
                      user_domain_name    = kwargs['user_domain'] if 'user_domain' in kwargs else None,)
 
+        self.auth_url = kwargs['auth_url']
         cert_validate = kwargs['cert_validate'] if 'cert_validate' in kwargs else False
         region = kwargs['region_name'] if 'region_name' in kwargs else False
 
@@ -115,17 +112,43 @@ class OpenstackL2PortChainingDriver(object):
                                                         logger = self.log)
 
     def validate_account_creds(self):
+        status = RwsdnalYang.SdnConnectionStatus()
         try:
             self.sess_drv.invalidate_auth_token()
             self.sess_drv.auth_token
+        except KeystoneExceptions.Unauthorized as e:
+            self.log.error("Invalid credentials given for SDN account ")
+            status.status = "failure"
+            status.details = "Invalid Credentials: %s" % str(e)
+  
         except KeystoneExceptions.AuthorizationFailure as e:
-            self.log.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e))
-            raise ValidationError("Invalid Credentials: "+ str(e))
+            self.log.error("Bad authentication URL given for SDN account. Given auth url: %s",
+                                   self.auth_url)
+            status.status = "failure"
+            status.details = "Invalid auth url: %s" % str(e)
+  
+        except NeutronException.NotFound as e:
+            status.status = "failure"
+            status.details = "Neutron exception  %s" % str(e)
+  
+        except openstack_drv.ValidationError as e:
+            self.log.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
+            status.status = "failure"
+            status.details = "Invalid Credentials: %s" % str(e)
+  
         except Exception as e:
-            self.log.error("Could not connect to Openstack. Exception: %s", str(e))
-            raise ValidationError("Connection Error: "+ str(e))
+            msg = "RwsdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
+            self.log.error(msg)
+            status.status = "failure"
+            status.details = msg
+  
+        else:
+            status.status = "success"
+            status.details = "Connection was successful"
+  
+        return status 
 
-    def delete_port_chain(self,port_chain_id):
+    def delete_port_chain(self, port_chain_id):
         "Delete port chain"
         try:
             result = self.portchain_drv.get_port_chain(port_chain_id)
@@ -144,15 +167,15 @@ class OpenstackL2PortChainingDriver(object):
                 port_pairs.extend(port_pair_group["port_pair_group"]["port_pairs"])
                 self.portchain_drv.delete_port_pair_group(port_pair_group_id)
 
-            self.log.debug("Port pairs during delete is %s",port_pairs)
+            self.log.debug("Port pairs during delete is %s", port_pairs)
 
             for port_pair_id in port_pairs:
                 self.portchain_drv.delete_port_pair(port_pair_id)
                 pass
         except Exception as e:
-            self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id,str(e))
+            self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id, str(e))
 
-    def update_port_chain(self,port_chain_id,flow_classifier_list):
+    def update_port_chain(self, port_chain_id, flow_classifier_list):
         result = self.portchain_drv.get_port_chain(port_chain_id)
         result.raise_for_status()
         port_chain = result.json()['port_chain']
@@ -160,20 +183,20 @@ class OpenstackL2PortChainingDriver(object):
         if port_chain and port_chain['flow_classifiers']:
            new_flow_classifier_list.extend(port_chain['flow_classifiers'])
         new_flow_classifier_list.extend(flow_classifier_list)
-        port_chain_id = self.portchain_drv.update_port_chain(port_chain['id'],flow_classifiers=new_flow_classifier_list)
+        port_chain_id = self.portchain_drv.update_port_chain(port_chain['id'], flow_classifiers=new_flow_classifier_list)
         return port_chain_id
 
-    def create_flow_classifer(self,classifier_name,classifier_dict):
+    def create_flow_classifer(self, classifier_name, classifier_dict):
         "Create flow classifier"
-        flow_classifier_id = self.portchain_drv.create_flow_classifier(classifier_name,classifier_dict)
+        flow_classifier_id = self.portchain_drv.create_flow_classifier(classifier_name, classifier_dict)
         return flow_classifier_id
 
-    def delete_flow_classifier(self,classifier_id):
+    def delete_flow_classifier(self, classifier_id):
         "Create flow classifier"
         try:
             self.portchain_drv.delete_flow_classifier(classifier_id)
         except Exception as e:
-            self.log.error("Error while deleting flow classifier with id %s, exception %s", classifier_id,str(e))
+            self.log.error("Error while deleting flow classifier with id %s, exception %s", classifier_id, str(e))
 
     def get_port_chain_list(self):
         result = self.portchain_drv.get_port_chain_list()
@@ -204,6 +227,7 @@ class RwsdnAccountDriver(object):
     
 class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
     instance_num = 1
+
     def __init__(self):
         GObject.Object.__init__(self)
         self.log = logging.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin.instance_num)
@@ -255,9 +279,9 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
         Returns:
             Validation Code and Details String
         """
-        status = RwsdnYang.SdnConnectionStatus()
-        drv = self._use_driver(account)
+        status = RwsdnalYang.SdnConnectionStatus()
         try:
+            drv = self._use_driver(account)
             drv.validate_account_creds()
 
         except openstack_drv.ValidationError as e:
@@ -278,14 +302,14 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
         return status
 
     @rwstatus(ret_on_failure=[""])
-    def do_create_vnffg_chain(self, account,vnffg):
+    def do_create_vnffg_chain(self, account, vnffg):
         """
         Creates Service Function chain in ODL
 
         @param account - a SDN account
 
         """
-        self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account,vnffg))
+        self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account, vnffg))
         drv = self._use_driver(account)
         port_list = list()
         vnf_chain_list = sorted(vnffg.vnf_chain_path, key = lambda x: x.order)
@@ -293,16 +317,16 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
         for path in vnf_chain_list:
             if prev_vm_id and path.vnfr_ids[0].vdu_list[0].vm_id == prev_vm_id:
                 prev_entry = port_list.pop()
-                port_list.append((prev_entry[0],path.vnfr_ids[0].vdu_list[0].port_id))
+                port_list.append((prev_entry[0], path.vnfr_ids[0].vdu_list[0].port_id))
                 prev_vm_id = None
             else:
                 prev_vm_id = path.vnfr_ids[0].vdu_list[0].vm_id
-                port_list.append((path.vnfr_ids[0].vdu_list[0].port_id,path.vnfr_ids[0].vdu_list[0].port_id))
-        vnffg_id = drv.create_port_chain(vnffg.name,port_list)
+                port_list.append((path.vnfr_ids[0].vdu_list[0].port_id, path.vnfr_ids[0].vdu_list[0].port_id))
+        vnffg_id = drv.create_port_chain(vnffg.name, port_list)
         return vnffg_id
 
     @rwstatus
-    def do_terminate_vnffg_chain(self, account,vnffg_id):
+    def do_terminate_vnffg_chain(self, account, vnffg_id):
         """
         Terminate Service Function chain in ODL
 
@@ -319,8 +343,8 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
 
            @param account - a SDN account
         """
-        self.log.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account,vnffg_classifier))
-        protocol_map = {1:'ICMP',6:'TCP',17:'UDP'}
+        self.log.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account, vnffg_classifier))
+        protocol_map = {1: 'ICMP', 6: 'TCP', 17: 'UDP'}
         flow_classifier_list = list()
         drv =  self._use_driver(account)
         for rule in vnffg_classifier.match_attributes:
@@ -328,7 +352,7 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
             flow_dict = {} 
             for field, value in rule.as_dict().items():
                  if field == 'ip_proto':
-                     flow_dict['protocol'] = protocol_map.get(value,None)
+                     flow_dict['protocol'] = protocol_map.get(value, None)
                  elif field == 'source_ip_address':
                      flow_dict['source_ip_prefix'] = value
                  elif field == 'destination_ip_address':
@@ -343,7 +367,7 @@ class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
                     flow_dict['logical_source_port'] = vnffg_classifier.port_id 
             flow_classifier_id = drv.create_flow_classifer(classifier_name, flow_dict)
             flow_classifier_list.append(flow_classifier_id)
-        drv.update_port_chain(vnffg_classifier.rsp_id,flow_classifier_list)
+        drv.update_port_chain(vnffg_classifier.rsp_id, flow_classifier_list)
         return flow_classifier_list
 
     @rwstatus(ret_on_failure=[None])
@@ -366,7 +390,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:
index 3061265..164aa03 100644 (file)
@@ -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"
index ecc180c..c7c015d 100644 (file)
 
 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/rwsdn.yang
deleted file mode 100644 (file)
index fba317f..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-
-/*
- * 
- *   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.
- *   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.
- *
- *
- */
-
-module rwsdn
-{
-  namespace "http://riftio.com/ns/riftware-1.0/rwsdn";
-  prefix "rwsdn";
-
-  import rw-base {
-    prefix rwbase;
-  }
-
-  import rw-pb-ext {
-    prefix "rwpb";
-  }
-
-  import rw-yang-types {
-    prefix "rwt";
-  }
-
-  import rw-log {
-    prefix "rwlog";
-  }
-
-  import mano-types {
-    prefix "manotypes";
-  }
-
-  import ietf-inet-types {
-    prefix "inet";
-  }
-
-  import ietf-yang-types {
-    prefix "yang";
-  }
-
-  import rw-project {
-    prefix "rw-project";
-  }
-
-  revision 2017-02-08 {
-    description
-      "Update model to support projects.";
-  }
-
-  revision 2014-12-30 {
-    description
-        "Initial revision.";
-    reference
-        "RIFT RWSDN cloud data";
-  }
-
-  typedef sdn-connection-status-enum {
-    description "Connection status for the sdn account";
-    type enumeration {
-      enum unknown;
-      enum validating;
-      enum success;
-      enum failure;
-    }
-  }
-
-  grouping connection-status {
-    container connection-status {
-      config false;
-      rwpb:msg-new SdnConnectionStatus;
-      leaf status {
-        type sdn-connection-status-enum;
-      }
-      leaf details {
-        type string;
-      }
-    }
-  }
-
-  // uses connection-status;
-
-  typedef sdn-account-type {
-    description "SDN account type";
-    type enumeration {
-      enum odl;
-      enum mock;
-      enum sdnsim;
-      enum openstack;
-    }
-  }
-
-  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";
-          }
-        }
-        leaf plugin-name {
-          type string;
-          default "rwsdn_odl";
-        }
-      }
-      container mock {
-        leaf username {
-          type string;
-        }
-        leaf plugin-name {
-          type string;
-          default "rwsdn_mock";
-        }
-      }
-
-      container sdnsim {
-        leaf username {
-          type string;
-        }
-        leaf topology-source {
-          type string;
-        }
-        leaf plugin-name {
-          type string;
-          default "rwsdn_sim";
-        }
-      }
-
-      container openstack {
-        leaf key {
-          type string;
-          mandatory true;
-        }
-
-        leaf secret {
-          type string;
-          mandatory true;
-        }
-
-        leaf auth_url {
-          type string;
-          mandatory true;
-        }
-
-        leaf tenant {
-          type string;
-          mandatory true;
-        }
-
-        leaf admin {
-          type boolean;
-          default false;
-        }
-
-        leaf user-domain {
-          type string;
-          default "Default";
-          description "Domain of the OpenStack user";
-        }
-
-        leaf project-domain {
-          type string;
-          default "Default";
-          description "Domain of the OpenStack project";
-        }
-
-        leaf region {
-          type string;
-          default "RegionOne";
-        }
-
-        leaf plugin-name {
-          type string;
-          default "rwsdn_openstack";
-        }
-
-        leaf cert-validate {
-          type boolean;
-          default false;
-          description "Certificate validatation policy in case of SSL/TLS connection";
-        }
-      }
-
-    }
-  }
-
-  augment "/rw-project:project" {
-    container sdn-accounts {
-      list sdn-account-list {
-        rwpb:msg-new SDNAccount;
-        key "name";
-
-        leaf name {
-          type string;
-        }
-
-        uses sdn-provider-auth;
-        uses connection-status;
-      }
-    }
-  }
-
-  augment "/rw-project:project" {
-    container vnffgs {
-      list vnffg-chain {
-        key "name";
-        rwpb:msg-new VNFFGChain;
-
-        leaf name {
-          type string;
-        }
-
-        list vnf-chain-path {
-          key "order";
-          leaf order {
-            type uint32;
-            description " Order of the VNF in VNFFG chain";
-          }
-          leaf service-function-type {
-            type string;
-          }
-          leaf nsh-aware {
-            type boolean;
-          }
-          leaf transport-type {
-            type string;
-          }
-          list vnfr-ids {
-            key "vnfr-id";
-            leaf vnfr-id {
-              type yang:uuid;
-            }
-            leaf vnfr-name {
-              type string;
-            }
-            leaf mgmt-address {
-              type inet:ip-address;
-            }
-            leaf mgmt-port {
-              type inet:port-number;
-            }
-            list vdu-list {
-              key "vm-id port-id";
-              leaf port-id {
-                rwpb:field-inline "true";
-                rwpb:field-string-max 64;
-                type string;
-              }
-              leaf vm-id {
-                rwpb:field-inline "true";
-                rwpb:field-string-max 64;
-                type string;
-              }
-              leaf name {
-                type string;
-              }
-              leaf address {
-                type inet:ip-address;
-              }
-              leaf port {
-                type inet:port-number;
-              }
-            }
-            leaf sff-name {
-              description "SFF name useful for non OVS based SFF";
-              type string;
-            }
-          }
-        }
-        list sff {
-          rwpb:msg-new VNFFGSff;
-          key "name";
-          leaf name {
-            type string;
-          }
-          leaf function-type {
-            type string;
-          }
-          leaf mgmt-address {
-            type inet:ip-address;
-          }
-          leaf mgmt-port {
-            type inet:port-number;
-          }
-          list dp-endpoints {
-            key "name";
-            leaf name {
-              type string;
-            }
-            leaf address {
-              type inet:ip-address;
-            }
-            leaf port {
-              type inet:port-number;
-            }
-          }
-          list vnfr-list {
-            key "vnfr-name";
-            leaf vnfr-name {
-              type string;
-            }
-          }
-        }
-        leaf classifier-name {
-          type string;
-        }
-      }
-    }
-  }
-
-  augment "/rw-project:project" {
-    container vnffg-rendered-paths {
-      rwpb:msg-new VNFFGRenderedPaths;
-      list vnffg-rendered-path {
-        key "name";
-        rwpb:msg-new VNFFGRenderedPath;
-        config false;
-        leaf name {
-          type string;
-        }
-        leaf path-id {
-          description
-            "Unique Identifier for the service path";
-          type uint32;
-        }
-        list rendered-path-hop {
-          key "hop-number";
-          leaf hop-number {
-            type uint8;
-          }
-          leaf service-index {
-            description
-              "Location within the service path";
-            type uint8;
-          }
-          leaf vnfr-name {
-            type string;
-          }
-          container service-function-forwarder {
-            leaf name {
-              description
-                "Service Function Forwarder name";
-              type string;
-            }
-            leaf ip-address {
-              description
-                "Service Function Forwarder Data Plane IP address";
-              type inet:ip-address;
-            }
-            leaf port {
-              description
-                "Service Function Forwarder Data Plane port";
-              type inet:port-number;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  augment "/rw-project:project" {
-    container vnffg-classifiers {
-      list vnffg-classifier {
-        key "name";
-        rwpb:msg-new VNFFGClassifier;
-
-        leaf name {
-          type string;
-        }
-        leaf rsp-name {
-          type string;
-        }
-        leaf rsp-id {
-          type yang:uuid;
-        }
-        leaf port-id {
-          rwpb:field-inline "true";
-          rwpb:field-string-max 64;
-          type string;
-        }
-        leaf vm-id {
-          rwpb:field-inline "true";
-          rwpb:field-string-max 64;
-          type string;
-        }
-        leaf sff-name {
-          type string;
-        }
-        container vnffg-metadata {
-          leaf ctx1 {
-            type string;
-          }
-          leaf ctx2 {
-            type string;
-          }
-          leaf ctx3 {
-            type string;
-          }
-          leaf ctx4 {
-            type string;
-          }
-        }
-        list match-attributes {
-          description
-            "List of match attributes.";
-          key "name";
-          leaf name {
-            description
-              "Name for the Access list";
-            type string;
-          }
-
-          leaf ip-proto {
-            description
-              "IP Protocol.";
-            type uint8;
-          }
-
-          leaf source-ip-address {
-            description
-              "Source IP address.";
-            type inet:ip-prefix;
-          }
-
-          leaf destination-ip-address {
-            description
-              "Destination IP address.";
-            type inet:ip-prefix;
-          }
-
-          leaf source-port {
-            description
-              "Source port number.";
-            type inet:port-number;
-          }
-
-          leaf destination-port {
-            description
-              "Destination port number.";
-            type inet:port-number;
-          }
-        } //match-attributes
-      }
-    }
-  }
-
-}
-
-/* vim: set ts=2:sw=2: */
diff --git a/rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang b/rwlaunchpad/plugins/rwvns/yang/rwsdnal.yang
new file mode 100644 (file)
index 0000000..5cd1563
--- /dev/null
@@ -0,0 +1,477 @@
+
+/*
+ * 
+ *   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.
+ *   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.
+ *
+ *
+ */
+
+module rwsdnal
+{
+  namespace "http://riftio.com/ns/riftware-1.0/rwsdnal";
+  prefix "rwsdnal";
+
+  import rw-base {
+    prefix rwbase;
+  }
+
+  import rw-pb-ext {
+    prefix "rwpb";
+  }
+
+  import rw-yang-types {
+    prefix "rwt";
+  }
+
+  import rw-log {
+    prefix "rwlog";
+  }
+
+  import mano-types {
+    prefix "manotypes";
+  }
+
+  import ietf-inet-types {
+    prefix "inet";
+  }
+
+  import ietf-yang-types {
+    prefix "yang";
+  }
+
+  import rw-project {
+    prefix "rw-project";
+  }
+
+  revision 2017-02-08 {
+    description
+      "Update model to support projects.";
+  }
+
+  revision 2014-12-30 {
+    description
+        "Initial revision.";
+    reference
+        "RIFT RWSDN cloud data";
+  }
+
+  typedef sdn-connection-status-enum {
+    description "Connection status for the sdn account";
+    type enumeration {
+      enum unknown;
+      enum validating;
+      enum success;
+      enum failure;
+    }
+  }
+
+  grouping connection-status {
+    container connection-status {
+      config false;
+      rwpb:msg-new SdnConnectionStatus;
+      leaf status {
+        type sdn-connection-status-enum;
+      }
+      leaf details {
+        type string;
+      }
+    }
+  }
+
+  // uses connection-status;
+
+  typedef sdn-account-type {
+    description "SDN account type";
+    type enumeration {
+      enum odl;
+      enum mock;
+      enum sdnsim;
+      enum openstack;
+    }
+  }
+
+  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";
+          }
+        }
+        leaf plugin-name {
+          type string;
+          default "rwsdn_odl";
+        }
+      }
+      container mock {
+        leaf username {
+          type string;
+        }
+        leaf plugin-name {
+          type string;
+          default "rwsdn_mock";
+        }
+      }
+
+      container sdnsim {
+        leaf username {
+          type string;
+        }
+        leaf topology-source {
+          type string;
+        }
+        leaf plugin-name {
+          type string;
+          default "rwsdn_sim";
+        }
+      }
+
+      container openstack {
+        leaf key {
+          type string;
+          mandatory true;
+        }
+
+        leaf secret {
+          type string;
+          mandatory true;
+        }
+
+        leaf auth_url {
+          type string;
+          mandatory true;
+        }
+
+        leaf tenant {
+          type string;
+          mandatory true;
+        }
+
+        leaf admin {
+          type boolean;
+          default false;
+        }
+
+        leaf user-domain {
+          type string;
+          default "Default";
+          description "Domain of the OpenStack user";
+        }
+
+        leaf project-domain {
+          type string;
+          default "Default";
+          description "Domain of the OpenStack project";
+        }
+
+        leaf region {
+          type string;
+          default "RegionOne";
+        }
+
+        leaf plugin-name {
+          type string;
+          default "rwsdn_openstack";
+        }
+
+        leaf cert-validate {
+          type boolean;
+          default false;
+          description "Certificate validatation policy in case of SSL/TLS connection";
+        }
+      }
+
+    }
+  }
+
+  augment "/rw-project:project" {
+    container sdn-accounts {
+      list sdn-account-list {
+        rwpb:msg-new SDNAccount;
+        key "name";
+
+        leaf name {
+          type string;
+        }
+
+        uses sdn-provider-auth;
+        uses connection-status;
+      }
+    }
+  }
+
+  augment "/rw-project:project" {
+    container vnffgs {
+      list vnffg-chain {
+        key "name";
+        rwpb:msg-new VNFFGChain;
+
+        leaf name {
+          type string;
+        }
+
+        list vnf-chain-path {
+          key "order";
+          leaf order {
+            type uint32;
+            description " Order of the VNF in VNFFG chain";
+          }
+          leaf service-function-type {
+            type string;
+          }
+          leaf nsh-aware {
+            type boolean;
+          }
+          leaf transport-type {
+            type string;
+          }
+          list vnfr-ids {
+            key "vnfr-id";
+            leaf vnfr-id {
+              type yang:uuid;
+            }
+            leaf vnfr-name {
+              type string;
+            }
+            leaf mgmt-address {
+              type inet:ip-address;
+            }
+            leaf mgmt-port {
+              type inet:port-number;
+            }
+            list vdu-list {
+              key "vm-id port-id";
+              leaf port-id {
+                rwpb:field-inline "true";
+                rwpb:field-string-max 64;
+                type string;
+              }
+              leaf vm-id {
+                rwpb:field-inline "true";
+                rwpb:field-string-max 64;
+                type string;
+              }
+              leaf name {
+                type string;
+              }
+              leaf address {
+                type inet:ip-address;
+              }
+              leaf port {
+                type inet:port-number;
+              }
+            }
+            leaf sff-name {
+              description "SFF name useful for non OVS based SFF";
+              type string;
+            }
+          }
+        }
+        list sff {
+          rwpb:msg-new VNFFGSff;
+          key "name";
+          leaf name {
+            type string;
+          }
+          leaf function-type {
+            type string;
+          }
+          leaf mgmt-address {
+            type inet:ip-address;
+          }
+          leaf mgmt-port {
+            type inet:port-number;
+          }
+          list dp-endpoints {
+            key "name";
+            leaf name {
+              type string;
+            }
+            leaf address {
+              type inet:ip-address;
+            }
+            leaf port {
+              type inet:port-number;
+            }
+          }
+          list vnfr-list {
+            key "vnfr-name";
+            leaf vnfr-name {
+              type string;
+            }
+          }
+        }
+        leaf classifier-name {
+          type string;
+        }
+      }
+    }
+  }
+
+  augment "/rw-project:project" {
+    container vnffg-rendered-paths {
+      rwpb:msg-new VNFFGRenderedPaths;
+      list vnffg-rendered-path {
+        key "name";
+        rwpb:msg-new VNFFGRenderedPath;
+        config false;
+        leaf name {
+          type string;
+        }
+        leaf path-id {
+          description
+            "Unique Identifier for the service path";
+          type uint32;
+        }
+        list rendered-path-hop {
+          key "hop-number";
+          leaf hop-number {
+            type uint8;
+          }
+          leaf service-index {
+            description
+              "Location within the service path";
+            type uint8;
+          }
+          leaf vnfr-name {
+            type string;
+          }
+          container service-function-forwarder {
+            leaf name {
+              description
+                "Service Function Forwarder name";
+              type string;
+            }
+            leaf ip-address {
+              description
+                "Service Function Forwarder Data Plane IP address";
+              type inet:ip-address;
+            }
+            leaf port {
+              description
+                "Service Function Forwarder Data Plane port";
+              type inet:port-number;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  augment "/rw-project:project" {
+    container vnffg-classifiers {
+      list vnffg-classifier {
+        key "name";
+        rwpb:msg-new VNFFGClassifier;
+
+        leaf name {
+          type string;
+        }
+        leaf rsp-name {
+          type string;
+        }
+        leaf rsp-id {
+          type yang:uuid;
+        }
+        leaf port-id {
+          rwpb:field-inline "true";
+          rwpb:field-string-max 64;
+          type string;
+        }
+        leaf vm-id {
+          rwpb:field-inline "true";
+          rwpb:field-string-max 64;
+          type string;
+        }
+        leaf sff-name {
+          type string;
+        }
+        container vnffg-metadata {
+          leaf ctx1 {
+            type string;
+          }
+          leaf ctx2 {
+            type string;
+          }
+          leaf ctx3 {
+            type string;
+          }
+          leaf ctx4 {
+            type string;
+          }
+        }
+        list match-attributes {
+          description
+            "List of match attributes.";
+          key "name";
+          leaf name {
+            description
+              "Name for the Access list";
+            type string;
+          }
+
+          leaf ip-proto {
+            description
+              "IP Protocol.";
+            type uint8;
+          }
+
+          leaf source-ip-address {
+            description
+              "Source IP address.";
+            type inet:ip-prefix;
+          }
+
+          leaf destination-ip-address {
+            description
+              "Destination IP address.";
+            type inet:ip-prefix;
+          }
+
+          leaf source-port {
+            description
+              "Source port number.";
+            type inet:port-number;
+          }
+
+          leaf destination-port {
+            description
+              "Destination port number.";
+            type inet:port-number;
+          }
+        } //match-attributes
+      }
+    }
+  }
+
+}
+
+/* vim: set ts=2:sw=2: */
index c568990..ee3b7d1 100644 (file)
@@ -196,6 +196,14 @@ module rw-pkg-mgmt
     }
   }
 
+  grouping copy-task-status {
+    leaf status {
+      description "The status of the copy task";
+      type task-status;
+      default QUEUED;
+    }
+  }
+
   augment "/rw-project:project" {
     container download-jobs {
       rwpb:msg-new DownloadJobs;
@@ -220,6 +228,24 @@ module rw-pkg-mgmt
         uses download-task-status;
       }
     }
+
+    container copy-jobs {
+      rwpb:msg-new CopyJobs;
+      description "Copy jobs";
+      config false;
+
+      list job {
+        rwpb:msg-new CopyJob;
+        key "transaction-id";
+
+        leaf transaction-id {
+          description "Unique UUID";
+          type string;
+        }
+
+        uses copy-task-status;
+      }
+    }
   }
 
   rpc get-package-endpoint {
@@ -238,6 +264,30 @@ module rw-pkg-mgmt
     }
   }
 
+  rpc package-copy {
+    description "Copies the package specified in input and returns the copied package id";
+
+    input {
+      uses package-identifer;
+
+      leaf package-name {
+        description "Name of destination package";
+        type string;
+      }
+
+      uses manotypes:rpc-project-name;
+    }
+
+    output {
+     leaf transaction-id {
+        description "Valid ID to track the status of the task";
+        type string;
+      }
+
+     uses package-identifer;
+    }
+  }
+
   rpc get-package-schema {
     description "Retrieves the schema for the package type";
 
index d0c9cc1..6ea19eb 100644 (file)
@@ -48,8 +48,8 @@ module rw-vns
     prefix "rwcal";
   }
 
-  import rwsdn {
-    prefix "rwsdn";
+  import rwsdnal {
+    prefix "rwsdnal";
   }
 
 
index 81f5b54..a6f5ae7 100644 (file)
@@ -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)