From f49375710db1acf3cd74c8651d098b7a08e8d0b2 Mon Sep 17 00:00:00 2001 From: Philip Joseph Date: Fri, 3 Mar 2017 01:55:37 +0530 Subject: [PATCH] New feature: Code changes for project support Signed-off-by: Philip Joseph --- common/python/CMakeLists.txt | 1 + common/python/rift/downloader/local_file.py | 84 +++ common/python/rift/downloader/url.py | 2 + common/python/rift/mano/cloud/accounts.py | 8 +- common/python/rift/mano/cloud/config.py | 51 +- common/python/rift/mano/cloud/operdata.py | 40 +- .../python/rift/mano/config_agent/config.py | 45 +- .../python/rift/mano/config_agent/operdata.py | 103 +-- .../mano/config_data/test/test_converter.py | 8 +- common/python/rift/mano/dts/core.py | 42 +- common/python/rift/mano/dts/rpc/core.py | 16 +- .../python/rift/mano/dts/subscriber/core.py | 71 +-- .../rift/mano/dts/subscriber/ns_subscriber.py | 6 +- .../rift/mano/dts/subscriber/ro_account.py | 2 +- .../python/rift/mano/dts/subscriber/store.py | 14 +- .../subscriber/test/utest_subscriber_dts.py | 24 +- .../mano/dts/subscriber/vnf_subscriber.py | 4 +- .../rwmano/syntax/mano_parameter.py | 2 +- .../rwmano/syntax/mano_template.py | 4 +- .../rwmano/tosca/tosca_nfv_vnf.py | 2 +- common/python/rift/mano/utils/project.py | 547 +++++++++++++++- .../yang_translator/rwmano/yang/yang_nsd.py | 2 +- common/python/test/utest_project.py | 221 +++++++ common/rw_gen_package.py | 4 +- examples/ping_pong_ns/config_desc.py | 6 +- .../rift/mano/examples/ping_pong_nsd.py | 8 +- .../python/rift/openmano/rift2openmano.py | 12 +- models/plugins/yang/mano-types.yang | 8 +- models/plugins/yang/nsd.yang | 44 +- models/plugins/yang/rw-project.yang | 11 + .../rift/tasklets/rwconmantasklet/RiftCA.py | 5 +- .../tasklets/rwconmantasklet/RiftCM_rpc.py | 24 +- .../rift/tasklets/rwconmantasklet/jujuconf.py | 5 +- .../rwconmantasklet/riftcm_config_plugin.py | 14 +- .../rwconmantasklet/rwconman_conagent.py | 20 +- .../rwconmantasklet/rwconman_config.py | 142 +++-- .../rwconmantasklet/rwconman_events.py | 10 +- .../rwconmantasklet/rwconmantasklet.py | 196 +++--- rwcm/test/rwso_test.py | 8 +- .../rift/tasklets/rwautoscaler/engine.py | 6 + .../tasklets/rwautoscaler/rwautoscaler.py | 181 +++--- .../rift/tasklets/rwautoscaler/subscribers.py | 17 +- .../rwautoscaler/test/utest_autoscaler_dts.py | 18 +- .../rwimagemgr/rift/imagemgr/client.py | 21 +- .../rift/tasklets/rwimagemgr/tasklet.py | 221 +++++-- .../rift/tasklets/rwimagemgr/upload.py | 3 +- .../rwimagemgr/test/utest_dts_handlers.py | 2 +- .../rift/package/convert.py | 32 +- .../rwlaunchpadtasklet/rift/package/store.py | 2 +- .../rift/tasklets/rwlaunchpad/datacenters.py | 18 +- .../rift/tasklets/rwlaunchpad/export.py | 13 +- .../rift/tasklets/rwlaunchpad/image.py | 4 +- .../rift/tasklets/rwlaunchpad/onboard.py | 36 +- .../rift/tasklets/rwlaunchpad/tasklet.py | 317 ++++++---- .../rift/tasklets/rwlaunchpad/uploader.py | 100 +-- .../rwlaunchpadtasklet/scripts/onboard_pkg | 66 +- .../rwlaunchpadtasklet/test/utest_export.py | 6 +- .../rwlaunchpadtasklet/test/utest_onboard.py | 10 +- .../test/utest_uploader_app_dts.py | 2 +- .../rwmonitor/rift/tasklets/rwmonitor/core.py | 18 +- .../rift/tasklets/rwmonitor/tasklet.py | 286 +++++---- .../rift/tasklets/rwmonparam/nsr_core.py | 32 +- .../rift/tasklets/rwmonparam/rwmonparam.py | 170 ++--- .../rift/tasklets/rwmonparam/vnfr_core.py | 16 +- .../rwmonparam/test/utest_mon_params.py | 8 +- .../rwmonparam/test/utest_mon_params_dts.py | 14 +- .../rwnsm/rift/tasklets/rwnsmtasklet/cloud.py | 17 +- .../tasklets/rwnsmtasklet/openmano_nsm.py | 8 +- .../rift/tasklets/rwnsmtasklet/publisher.py | 58 +- .../tasklets/rwnsmtasklet/rwnsm_conman.py | 3 +- .../tasklets/rwnsmtasklet/rwnsmtasklet.py | 592 ++++++++++++------ .../rift/tasklets/rwnsmtasklet/rwvnffgmgr.py | 55 +- .../rift/tasklets/rwnsmtasklet/scale_group.py | 4 +- .../rwpkgmgr/publisher/download_status.py | 15 +- .../rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py | 22 +- .../rift/tasklets/rwpkgmgr/rwpkgmgr.py | 35 +- .../rwpkgmgr/subscriber/download_status.py | 7 +- .../rwresmgrtasklet/rwresmgr_config.py | 37 +- .../rwresmgrtasklet/rwresmgr_events.py | 93 ++- .../rwresmgrtasklet/rwresmgrtasklet.py | 54 +- .../plugins/rwresmgr/test/rmmgr_test.py | 10 +- .../rwstagingmgr/publisher/staging_status.py | 14 +- .../tasklets/rwstagingmgr/rwstagingmgr.py | 42 +- .../tasklets/rwstagingmgr/store/file_store.py | 33 +- .../tasklets/rwvnfmtasklet/rwvnfmtasklet.py | 262 +++++--- .../tasklets/rwvnstasklet/rwvnstasklet.py | 167 +++-- .../plugins/rwvns/rift/topmgr/rwtopmgr.py | 44 +- .../plugins/rwvns/rift/vlmgr/rwvlmgr.py | 52 +- rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang | 7 + .../multivm_vnf/test_multi_vm_vnf_slb.py | 6 +- .../multivm_vnf/test_multi_vm_vnf_trafgen.py | 6 +- .../ra/pytest/ns/haproxy/test_scaling.py | 4 +- .../ra/pytest/ns/pingpong/test_pingpong.py | 10 +- rwlaunchpad/ra/pytest/ns/test_onboard.py | 6 +- rwlaunchpad/test/mano_error_ut.py | 4 +- rwlaunchpad/test/mano_ut.py | 398 +++++++++--- rwlaunchpad/test/pytest/lp_kt_utm_test.py | 6 +- .../test/pytest/lp_kt_utm_wims_test.py | 6 +- rwlaunchpad/test/pytest/lp_test.py | 6 +- .../test/pytest/lp_tg_2vrouter_ts_epa_test.py | 6 +- .../test/pytest/lp_tg_2vrouter_ts_test.py | 6 +- .../pytest/lp_tg_vrouter_ts_epa_sriov_test.py | 6 +- rwlaunchpad/test/utest_nsr_handler.py | 24 +- rwlaunchpad/test/utest_ro_account.py | 8 +- rwlaunchpad/test/utest_rwmonitor.py | 24 +- rwlaunchpad/test/utest_rwnsm.py | 60 +- 106 files changed, 3938 insertions(+), 1714 deletions(-) create mode 100644 common/python/rift/downloader/local_file.py create mode 100644 common/python/test/utest_project.py diff --git a/common/python/CMakeLists.txt b/common/python/CMakeLists.txt index dcef461f..de83df3b 100644 --- a/common/python/CMakeLists.txt +++ b/common/python/CMakeLists.txt @@ -75,6 +75,7 @@ rift_python_install_tree( FILES rift/downloader/__init__.py rift/downloader/base.py + rift/downloader/local_file.py rift/downloader/url.py COMPONENT ${PKG_LONG_NAME} PYTHON3_ONLY diff --git a/common/python/rift/downloader/local_file.py b/common/python/rift/downloader/local_file.py new file mode 100644 index 00000000..c6e9c5e3 --- /dev/null +++ b/common/python/rift/downloader/local_file.py @@ -0,0 +1,84 @@ +# +# Copyright 2016 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. +# +# Taken from http://stackoverflow.com/a/27786580 + + +import logging +import requests +import os +from urllib.parse import urlparse + + +class LocalFileAdapter(requests.adapters.BaseAdapter): + """Protocol Adapter to allow Requests to GET file:// URLs + + @todo: Properly handle non-empty hostname portions. + """ + + @staticmethod + def _chkpath(method, path): + """Return an HTTP status for the given filesystem path.""" + if method.lower() in ('put', 'delete'): + return 501, "Not Implemented" # TODO + elif method.lower() not in ('get', 'head'): + return 405, "Method Not Allowed" + elif os.path.isdir(path): + return 400, "Path Not A File" + elif not os.path.isfile(path): + return 404, "File Not Found" + elif not os.access(path, os.R_OK): + return 403, "Access Denied" + else: + return 200, "OK" + + def send(self, req, **kwargs): # pylint: disable=unused-argument + """Return the file specified by the given request + + @type req: C{PreparedRequest} + @todo: Should I bother filling `response.headers` and processing + If-Modified-Since and friends using `os.stat`? + """ + + log = logging.getLogger('rw-mano-log') + log.debug("Request: {}".format(req)) + + url = urlparse(req.path_url) + path = os.path.normcase(os.path.normpath(url.path)) + response = requests.Response() + + response.status_code, response.reason = self._chkpath(req.method, path) + log.debug("Response {}: {}".format(response.status_code, response.reason)) + if response.status_code == 200 and req.method.lower() != 'head': + try: + response.raw = open(path, 'rb') + except (OSError, IOError) as err: + response.status_code = 500 + response.reason = str(err) + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + response.request = req + response.connection = self + + + log.debug("Response {}: {}".format(response.status_code, response)) + return response + + def close(self): + pass diff --git a/common/python/rift/downloader/url.py b/common/python/rift/downloader/url.py index 27688945..0cdd8d4a 100644 --- a/common/python/rift/downloader/url.py +++ b/common/python/rift/downloader/url.py @@ -39,6 +39,7 @@ gi.require_version("RwPkgMgmtYang", "1.0") from gi.repository import RwPkgMgmtYang from . import base +from .local_file import LocalFileAdapter as LocalFileAdapter class UrlDownloader(base.AbstractDownloader): @@ -109,6 +110,7 @@ class UrlDownloader(base.AbstractDownloader): retries = Retry(total=5, backoff_factor=1) session.mount("http://", HTTPAdapter(max_retries=retries)) session.mount("https://", HTTPAdapter(max_retries=retries)) + session.mount("file://", LocalFileAdapter()) return session diff --git a/common/python/rift/mano/cloud/accounts.py b/common/python/rift/mano/cloud/accounts.py index d3aa860e..65635127 100644 --- a/common/python/rift/mano/cloud/accounts.py +++ b/common/python/rift/mano/cloud/accounts.py @@ -53,7 +53,7 @@ class CloudAccount(object): self._cal = self.plugin.get_interface("Cloud") self._cal.init(rwlog_hdl) - self._status = RwCloudYang.CloudAccount_ConnectionStatus( + self._status = RwCloudYang.CloudAcc_ConnectionStatus( status="unknown", details="Connection status lookup not started" ) @@ -151,7 +151,7 @@ class CloudAccount(object): @asyncio.coroutine def validate_cloud_account_credentials(self, loop): self._log.debug("Validating Cloud Account credentials %s", self._account_msg) - self._status = RwCloudYang.CloudAccount_ConnectionStatus( + self._status = RwCloudYang.CloudAcc_ConnectionStatus( status="validating", details="Cloud account connection validation in progress" ) @@ -161,9 +161,9 @@ class CloudAccount(object): self.cal_account_msg, ) if rwstatus == RwTypes.RwStatus.SUCCESS: - self._status = RwCloudYang.CloudAccount_ConnectionStatus.from_dict(status.as_dict()) + self._status = RwCloudYang.CloudAcc_ConnectionStatus.from_dict(status.as_dict()) else: - self._status = RwCloudYang.CloudAccount_ConnectionStatus( + self._status = RwCloudYang.CloudAcc_ConnectionStatus( status="failure", details="Error when calling CAL validate cloud creds" ) diff --git a/common/python/rift/mano/cloud/config.py b/common/python/rift/mano/cloud/config.py index c62f129d..f7906fdb 100644 --- a/common/python/rift/mano/cloud/config.py +++ b/common/python/rift/mano/cloud/config.py @@ -21,11 +21,13 @@ import rw_peas import gi gi.require_version('RwDts', '1.0') import rift.tasklets +from rift.mano.utils.project import get_add_delete_update_cfgs from gi.repository import ( RwcalYang as rwcal, RwDts as rwdts, ProtobufC, + RwCloudYang, ) from . import accounts @@ -38,32 +40,6 @@ class CloudAccountError(Exception): pass -def get_add_delete_update_cfgs(dts_member_reg, xact, key_name): - # Unforunately, it is currently difficult to figure out what has exactly - # changed in this xact without Pbdelta support (RIFT-4916) - # As a workaround, we can fetch the pre and post xact elements and - # perform a comparison to figure out adds/deletes/updates - xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) - curr_cfgs = list(dts_member_reg.elements) - - xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} - curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} - - # Find Adds - added_keys = set(xact_key_map) - set(curr_key_map) - added_cfgs = [xact_key_map[key] for key in added_keys] - - # Find Deletes - deleted_keys = set(curr_key_map) - set(xact_key_map) - deleted_cfgs = [curr_key_map[key] for key in deleted_keys] - - # Find Updates - updated_keys = set(curr_key_map) & set(xact_key_map) - updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] - - return added_cfgs, deleted_cfgs, updated_cfgs - - class CloudAccountConfigCallbacks(object): def __init__(self, on_add_apply=None, on_add_prepare=None, @@ -101,12 +77,13 @@ class CloudAccountConfigCallbacks(object): class CloudAccountConfigSubscriber(object): - XPATH = "C,/rw-project:project/rw-cloud:cloud/rw-cloud:account" + XPATH = "C,/rw-cloud:cloud/rw-cloud:account" - def __init__(self, dts, log, rwlog_hdl, cloud_callbacks): + def __init__(self, dts, log, rwlog_hdl, project, cloud_callbacks): self._dts = dts self._log = log self._rwlog_hdl = rwlog_hdl + self._project = project self._reg = None self.accounts = {} @@ -144,9 +121,16 @@ class CloudAccountConfigSubscriber(object): self.delete_account(account_msg.name) self.add_account(account_msg) + def deregister(self): + self._log.debug("Project {}: De-register cloud account handler". + format(self._project)) + if self._reg: + self._reg.deregister() + self._reg = None + def register(self): @asyncio.coroutine - def apply_config(dts, acg, xact, action, _): + def apply_config(dts, acg, xact, action, scratch): self._log.debug("Got cloud account apply config (xact: %s) (action: %s)", xact, action) if xact.xact is None: @@ -188,12 +172,16 @@ class CloudAccountConfigSubscriber(object): """ Prepare callback from DTS for Cloud Account """ action = xact_info.query_action + + xpath = ks_path.to_xpath(RwCloudYang.get_schema()) + self._log.debug("Cloud 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("Cloud account already exists. Invoking update request") + self._log.debug("Cloud account {} already exists. " \ + "Invoking update request".format(msg.name)) # Since updates are handled by a delete followed by an add, invoke the # delete prepare callbacks to give clients an opportunity to reject. @@ -241,9 +229,10 @@ class CloudAccountConfigSubscriber(object): on_apply=apply_config, ) + xpath = self._project.add_project(CloudAccountConfigSubscriber.XPATH) with self._dts.appconf_group_create(acg_handler) as acg: self._reg = acg.register( - xpath=CloudAccountConfigSubscriber.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, on_prepare=on_prepare, ) diff --git a/common/python/rift/mano/cloud/operdata.py b/common/python/rift/mano/cloud/operdata.py index 12ab8012..a86f6385 100644 --- a/common/python/rift/mano/cloud/operdata.py +++ b/common/python/rift/mano/cloud/operdata.py @@ -21,6 +21,7 @@ import rift.tasklets from gi.repository import( RwCloudYang, RwDts as rwdts, + RwTypes, ) class CloudAccountNotFound(Exception): @@ -28,11 +29,14 @@ class CloudAccountNotFound(Exception): class CloudAccountDtsOperdataHandler(object): - def __init__(self, dts, log, loop): + def __init__(self, dts, log, loop, project): self._dts = dts self._log = log self._loop = loop + self._project = project + self._regh = None + self._rpc = None self.cloud_accounts = {} def add_cloud_account(self, account): @@ -71,13 +75,13 @@ class CloudAccountDtsOperdataHandler(object): def _register_show_status(self): def get_xpath(cloud_name=None): - return "D,/rw-project:project/rw-cloud:cloud/account{}/connection-status".format( - "[name='%s']" % cloud_name if cloud_name is not None else '' - ) + return "D,/rw-cloud:cloud/account{}/connection-status".format( + "[name='%s']" % cloud_name if cloud_name is not None else '' + ) @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): - path_entry = RwCloudYang.CloudAccount.schema().keyspec_to_entry(ks_path) + path_entry = RwCloudYang.CloudAcc.schema().keyspec_to_entry(ks_path) cloud_account_name = path_entry.key00.name self._log.debug("Got show cloud connection status request: %s", ks_path.create_string()) @@ -86,9 +90,10 @@ class CloudAccountDtsOperdataHandler(object): for account in saved_accounts: connection_status = account.connection_status self._log.debug("Responding to cloud connection status request: %s", connection_status) + xpath = self._project.add_project(get_xpath(account.name)) xact_info.respond_xpath( rwdts.XactRspCode.MORE, - xpath=get_xpath(account.name), + xpath=xpath, msg=account.connection_status, ) except KeyError as e: @@ -98,8 +103,9 @@ class CloudAccountDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( - xpath=get_xpath(), + xpath = self._project.add_project(get_xpath()) + self._regh = yield from self._dts.register( + xpath=xpath, handler=rift.tasklets.DTS.RegistrationHandler( on_prepare=on_prepare), flags=rwdts.Flag.PUBLISHER, @@ -113,12 +119,20 @@ class CloudAccountDtsOperdataHandler(object): def on_prepare(xact_info, action, ks_path, msg): if not msg.has_field("cloud_account"): raise CloudAccountNotFound("Cloud account name not provided") - cloud_account_name = msg.cloud_account + + if not self._project.rpc_check(msg, xact_info=xact_info): + return + try: account = self.cloud_accounts[cloud_account_name] except KeyError: - raise CloudAccountNotFound("Cloud account name %s not found" % cloud_account_name) + errmsg = "Cloud account name {} not found in project {}". \ + format(cloud_account_name, self._project.name) + xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, + get_xpath(), + errmsg) + raise CloudAccountNotFound(errmsg) account.start_validate_credentials(self._loop) @@ -126,7 +140,7 @@ class CloudAccountDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( + self._rpc = yield from self._dts.register( xpath=get_xpath(), handler=rift.tasklets.DTS.RegistrationHandler( on_prepare=on_prepare @@ -138,3 +152,7 @@ class CloudAccountDtsOperdataHandler(object): def register(self): yield from self._register_show_status() yield from self._register_validate_rpc() + + def deregister(self): + yield from self._rpc.deregister() + yield from self._regh.deregister() diff --git a/common/python/rift/mano/config_agent/config.py b/common/python/rift/mano/config_agent/config.py index 7500bac7..daee792e 100644 --- a/common/python/rift/mano/config_agent/config.py +++ b/common/python/rift/mano/config_agent/config.py @@ -21,6 +21,7 @@ import rw_peas import gi gi.require_version('RwDts', '1.0') import rift.tasklets +from rift.mano.utils.project import get_add_delete_update_cfgs from gi.repository import ( RwcalYang as rwcal, @@ -36,32 +37,6 @@ class ConfigAccountError(Exception): pass -def get_add_delete_update_cfgs(dts_member_reg, xact, key_name): - # Unforunately, it is currently difficult to figure out what has exactly - # changed in this xact without Pbdelta support (RIFT-4916) - # As a workaround, we can fetch the pre and post xact elements and - # perform a comparison to figure out adds/deletes/updates - xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) - curr_cfgs = list(dts_member_reg.elements) - - xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} - curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} - - # Find Adds - added_keys = set(xact_key_map) - set(curr_key_map) - added_cfgs = [xact_key_map[key] for key in added_keys] - - # Find Deletes - deleted_keys = set(curr_key_map) - set(xact_key_map) - deleted_cfgs = [curr_key_map[key] for key in deleted_keys] - - # Find Updates - updated_keys = set(curr_key_map) & set(xact_key_map) - updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] - - return added_cfgs, deleted_cfgs, updated_cfgs - - class ConfigAgentCallbacks(object): def __init__(self, on_add_apply=None, on_add_prepare=None, @@ -101,9 +76,10 @@ class ConfigAgentCallbacks(object): class ConfigAgentSubscriber(object): XPATH = "C,/rw-config-agent:config-agent/account" - def __init__(self, dts, log, config_callbacks): + def __init__(self, dts, log, project, config_callbacks): self._dts = dts self._log = log + self._project = project self._reg = None self.accounts = {} @@ -139,6 +115,13 @@ class ConfigAgentSubscriber(object): self.delete_account(account_msg) self.add_account(account_msg) + def deregister(self): + self._log.debug("De-register config agent handler for project {}". + format(self._project.name)) + if self._reg: + self._reg.deregister() + self._reg = None + def register(self): def apply_config(dts, acg, xact, action, _): self._log.debug("Got config account apply config (xact: %s) (action: %s)", xact, action) @@ -212,17 +195,17 @@ class ConfigAgentSubscriber(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - self._log.debug("Registering for Config Account config using xpath: %s", - ConfigAgentSubscriber.XPATH, - ) acg_handler = rift.tasklets.AppConfGroup.Handler( on_apply=apply_config, ) with self._dts.appconf_group_create(acg_handler) as acg: + xpath = self._project.add_project(ConfigAgentSubscriber.XPATH) + self._log.debug("Registering for Config Account config using xpath: %s", + xpath) self._reg = acg.register( - xpath=ConfigAgentSubscriber.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER, on_prepare=on_prepare, ) diff --git a/common/python/rift/mano/config_agent/operdata.py b/common/python/rift/mano/config_agent/operdata.py index 729a1f1c..61ae5f8e 100644 --- a/common/python/rift/mano/config_agent/operdata.py +++ b/common/python/rift/mano/config_agent/operdata.py @@ -27,7 +27,6 @@ from gi.repository import ( RwDts as rwdts) import rift.tasklets - import rift.mano.utils.juju_api as juju @@ -153,12 +152,15 @@ class ConfigAgentAccount(object): ) class CfgAgentDtsOperdataHandler(object): - def __init__(self, dts, log, loop): + def __init__(self, dts, log, loop, project): self._dts = dts self._log = log self._loop = loop + self._project = project self.cfg_agent_accounts = {} + self._show_reg = None + self._rpc_reg = None def add_cfg_agent_account(self, account_msg): account = ConfigAgentAccount(self._log, account_msg) @@ -205,9 +207,10 @@ class CfgAgentDtsOperdataHandler(object): for account in saved_accounts: connection_status = account.connection_status self._log.debug("Responding to config agent connection status request: %s", connection_status) + xpath = self._project.add_project(get_xpath(account.name)) xact_info.respond_xpath( rwdts.XactRspCode.MORE, - xpath=get_xpath(account.name), + xpath=xpath, msg=account.connection_status, ) except KeyError as e: @@ -217,12 +220,13 @@ class CfgAgentDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare), - flags=rwdts.Flag.PUBLISHER, - ) + xpath = self._project.add_project(get_xpath()) + self._show_reg = yield from self._dts.register( + xpath=xpath, + handler=rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare), + flags=rwdts.Flag.PUBLISHER, + ) def _register_validate_rpc(self): def get_xpath(): @@ -234,6 +238,10 @@ class CfgAgentDtsOperdataHandler(object): raise ConfigAgentAccountNotFound("Config Agent account name not provided") cfg_agent_account_name = msg.cfg_agent_account + + if not self._project.rpc_check(msg, xact_info=xact_info): + return + try: account = self.cfg_agent_accounts[cfg_agent_account_name] except KeyError: @@ -243,24 +251,29 @@ class CfgAgentDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare - ), - flags=rwdts.Flag.PUBLISHER, - ) + self._rpc_reg = 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._show_reg.deregister() + self._rpc_reg.deregister() + + class ConfigAgentJob(object): """A wrapper over the config agent job object, providing some convenience functions. - YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob contains + YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob contains || ==> VNFRS || @@ -274,17 +287,19 @@ class ConfigAgentJob(object): "running" : "pending", "failed" : "failure"} - def __init__(self, nsr_id, job, tasks=None): + def __init__(self, nsr_id, job, project, tasks=None): """ Args: nsr_id (uuid): ID of NSR record - job (YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob): Gi object + job (YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob): Gi object tasks: List of asyncio.tasks. If provided the job monitor will use it to monitor the tasks instead of the execution IDs """ self._job = job self.nsr_id = nsr_id self.tasks = tasks + self._project = project + self._regh = None @property @@ -315,10 +330,10 @@ class ConfigAgentJob(object): @property def xpath(self): """Xpath of the job""" - return ("D,/nsr:ns-instance-opdata" + + return self._project.add_project(("D,/nsr:ns-instance-opdata" + "/nsr:nsr[nsr:ns-instance-config-ref='{}']" + "/nsr:config-agent-job[nsr:job-id='{}']" - ).format(self.nsr_id, self.id) + ).format(self.nsr_id, self.id)) @property def regh(self): @@ -333,7 +348,7 @@ class ConfigAgentJob(object): @staticmethod def convert_rpc_input_to_job(nsr_id, rpc_output, tasks): """A helper function to convert the YangOutput_Nsr_ExecNsConfigPrimitive - to YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob (NsrYang) + to YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob (NsrYang) Args: nsr_id (uuid): NSR ID @@ -344,10 +359,10 @@ class ConfigAgentJob(object): ConfigAgentJob """ # Shortcuts to prevent the HUUGE names. - CfgAgentJob = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob - CfgAgentVnfr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr - CfgAgentPrimitive = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr_Primitive - CfgAgentPrimitiveParam = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr_Primitive_Parameter + CfgAgentJob = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob + CfgAgentVnfr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr + CfgAgentPrimitive = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr_Primitive + CfgAgentPrimitiveParam = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConfigAgentJob_Vnfr_Primitive_Parameter job = CfgAgentJob.from_dict({ "job_id": rpc_output.job_id, @@ -385,7 +400,7 @@ class ConfigAgentJob(object): job.vnfr.append(vnfr_job) - return ConfigAgentJob(nsr_id, job, tasks) + return ConfigAgentJob(nsr_id, job, project, tasks) class ConfigAgentJobMonitor(object): @@ -666,6 +681,7 @@ class CfgAgentJobDtsHandler(object): self._regh = None self._nsr_regh = None + self._project = cfgm.project @property def regh(self): @@ -684,9 +700,9 @@ class CfgAgentJobDtsHandler(object): @staticmethod def cfg_job_xpath(nsr_id, job_id): - return ("D,/nsr:ns-instance-opdata" + + return self._project.add_project(("D,/nsr:ns-instance-opdata" + "/nsr:nsr[nsr:ns-instance-config-ref = '{}']" + - "/nsr:config-agent-job[nsr:job-id='{}']").format(nsr_id, job_id) + "/nsr:config-agent-job[nsr:job-id='{}']").format(nsr_id, job_id)) @asyncio.coroutine def register(self): @@ -697,7 +713,7 @@ class CfgAgentJobDtsHandler(object): """ prepare callback from dts """ xpath = ks_path.to_xpath(RwNsrYang.get_schema()) if action == rwdts.QueryAction.READ: - schema = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.schema() + schema = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr.schema() path_entry = schema.keyspec_to_entry(ks_path) try: nsr_id = path_entry.key00.ns_instance_config_ref @@ -728,7 +744,8 @@ class CfgAgentJobDtsHandler(object): hdl = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare,) with self._dts.group_create() as group: - self._regh = group.register(xpath=CfgAgentJobDtsHandler.XPATH, + self._regh = group.register(xpath=self._project.add_project( + CfgAgentJobDtsHandler.XPATH), handler=hdl, flags=rwdts.Flag.PUBLISHER, ) @@ -749,7 +766,7 @@ class CfgAgentJobDtsHandler(object): @property def nsr_xpath(self): - return "D,/nsr:ns-instance-opdata/nsr:nsr" + return self._project.add_project("D,/nsr:ns-instance-opdata/nsr:nsr") @asyncio.coroutine def register_for_nsr(self): @@ -784,6 +801,17 @@ class CfgAgentJobDtsHandler(object): except Exception as e: self._log.error("Failed to register for NSR changes as %s", str(e)) + def deregister(self): + self._log.debug("De-register config agent job for project". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + + if self._nsr_regh: + self._nsr_regh.deregister() + self._nsr_regh = None + class ConfigAgentJobManager(object): """A central class that manager all the Config Agent related data, @@ -791,7 +819,7 @@ class ConfigAgentJobManager(object): TODO: Needs to support multiple config agents. """ - def __init__(self, dts, log, loop, nsm): + def __init__(self, dts, log, loop, project, nsm): """ Args: dts : Dts handle @@ -804,11 +832,12 @@ class ConfigAgentJobManager(object): self.log = log self.loop = loop self.nsm = nsm + self.project = project self.handler = CfgAgentJobDtsHandler(dts, log, loop, nsm, self) self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) def add_job(self, rpc_output, tasks=None): - """Once an RPC is trigger add a now job + """Once an RPC is triggered, add a new job Args: rpc_output (YangOutput_Nsr_ExecNsConfigPrimitive): Rpc output @@ -818,7 +847,8 @@ class ConfigAgentJobManager(object): """ nsr_id = rpc_output.nsr_id_ref - job = ConfigAgentJob.convert_rpc_input_to_job(nsr_id, rpc_output, tasks) + job = ConfigAgentJob.convert_rpc_input_to_job(nsr_id, rpc_output, + tasks, self.project) self.log.debug("Creating a job monitor for Job id: {}".format( rpc_output.job_id)) @@ -864,3 +894,6 @@ class ConfigAgentJobManager(object): def register(self): yield from self.handler.register() yield from self.handler.register_for_nsr() + + def deregister(self): + yield from self.handler.deregister() diff --git a/common/python/rift/mano/config_data/test/test_converter.py b/common/python/rift/mano/config_data/test/test_converter.py index 1bfd7d76..6169d739 100644 --- a/common/python/rift/mano/config_data/test/test_converter.py +++ b/common/python/rift/mano/config_data/test/test_converter.py @@ -23,14 +23,14 @@ import yaml @pytest.fixture(scope="function") def nsd(): - catalog = NsdYang.YangData_Nsd_NsdCatalog() + catalog = NsdYang.YangData_RwProject_Project_NsdCatalog() nsd = catalog.nsd.add() nsd.id = str(uuid.uuid1()) return nsd @pytest.fixture(scope="function") def vnfd(): - catalog = VnfdYang.YangData_Vnfd_VnfdCatalog() + catalog = VnfdYang.YangData_RwProject_Project_VnfdCatalog() vnfd = catalog.vnfd.add() vnfd.id = str(uuid.uuid1()) return vnfd @@ -287,7 +287,7 @@ initial_config_primitive: cidr: 10.10.10.2/30 """ - catalog = VnfdYang.YangData_Vnfd_VnfdCatalog() + catalog = VnfdYang.YangData_RwProject_Project_VnfdCatalog() expected_vnfd = catalog.vnfd.add() vnf_config = expected_vnfd.vnf_configuration expected_vnfd.id = vnfd.id @@ -374,7 +374,7 @@ def test_nsd_merge(nsd, convertor): Vlan ID: '3000' """ - catalog = NsdYang.YangData_Nsd_NsdCatalog() + catalog = NsdYang.YangData_RwProject_Project_NsdCatalog() expected_nsd = catalog.nsd.add() expected_nsd.id = nsd.id expected_nsd.service_primitive.add().from_dict( diff --git a/common/python/rift/mano/dts/core.py b/common/python/rift/mano/dts/core.py index 4894e168..3a049459 100644 --- a/common/python/rift/mano/dts/core.py +++ b/common/python/rift/mano/dts/core.py @@ -25,7 +25,7 @@ class DtsHandler(object): """A common class to hold the barebone objects to build a publisher or subscriber """ - def __init__(self, log, dts, loop): + def __init__(self, log, dts, loop, project): """Constructor Args: @@ -34,7 +34,39 @@ class DtsHandler(object): loop : Asyncio event loop. """ # Reg handle - self.reg = None - self.log = log - self.dts = dts - self.loop = loop + self._reg = None + self._log = log + self._dts = dts + self._loop = loop + self._project = project + + @property + def reg(self): + return self._reg + + @reg.setter + def reg(self, val): + self._reg = val + + @property + def log(self): + return self._log + + @property + def dts(self): + return self._dts + + @property + def loop(self): + return self._loop + + @property + def project(self): + return self._project + + def deregister(self): + self._log.debug("De-registering DTS handler ({}) for project {}". + format(self.__class__.__name__, self._project)) + if self._reg: + self._reg.deregister() + self._reg = None diff --git a/common/python/rift/mano/dts/rpc/core.py b/common/python/rift/mano/dts/rpc/core.py index dfa08bb6..72016f19 100644 --- a/common/python/rift/mano/dts/rpc/core.py +++ b/common/python/rift/mano/dts/rpc/core.py @@ -36,8 +36,8 @@ from ..core import DtsHandler class AbstractRpcHandler(DtsHandler): """Base class to simplify RPC implementation """ - def __init__(self, log, dts, loop): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project=None): + super().__init__(log, dts, loop, project) if not asyncio.iscoroutinefunction(self.callback): raise ValueError('%s has to be a coroutine' % (self.callback)) @@ -61,6 +61,9 @@ class AbstractRpcHandler(DtsHandler): def on_prepare(self, xact_info, action, ks_path, msg): assert action == rwdts.QueryAction.RPC + if self.project and not self.project.rpc_check(msg, xact_info=xact_info): + return + try: rpc_op = yield from self.callback(ks_path, msg) xact_info.respond_xpath( @@ -76,6 +79,11 @@ class AbstractRpcHandler(DtsHandler): @asyncio.coroutine def register(self): + if self.reg: + self._log.warning("RPC already registered for project {}". + format(self._project.name)) + return + reg_event = asyncio.Event(loop=self.loop) @asyncio.coroutine @@ -94,6 +102,10 @@ class AbstractRpcHandler(DtsHandler): yield from reg_event.wait() + def deregister(self): + self.reg.deregister() + self.reg = None + @abc.abstractmethod @asyncio.coroutine def callback(self, ks_path, msg): diff --git a/common/python/rift/mano/dts/subscriber/core.py b/common/python/rift/mano/dts/subscriber/core.py index dd2513e8..ebd38f97 100644 --- a/common/python/rift/mano/dts/subscriber/core.py +++ b/common/python/rift/mano/dts/subscriber/core.py @@ -1,6 +1,6 @@ """ -# -# Copyright 2016 RIFT.IO Inc +# +# Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ import asyncio from gi.repository import (RwDts as rwdts, ProtobufC) import rift.tasklets +from rift.mano.utils.project import ( + get_add_delete_update_cfgs, + ) from ..core import DtsHandler @@ -35,11 +38,11 @@ class SubscriberDtsHandler(DtsHandler): """A common class for all subscribers. """ @classmethod - def from_tasklet(cls, tasklet, callback=None): + def from_project(cls, proj, callback=None): """Convenience method to build the object from tasklet Args: - tasklet (rift.tasklets.Tasklet): Tasklet + proj (rift.mano.utils.project.ManoProject): Project callback (None, optional): Callable, which will be invoked on subscriber changes. @@ -48,15 +51,15 @@ class SubscriberDtsHandler(DtsHandler): msg: The Gi Object msg from DTS action(rwdts.QueryAction): Action type """ - return cls(tasklet.log, tasklet.dts, tasklet.loop, callback=callback) + return cls(proj.log, proj.dts, proj.loop, proj, callback=callback) - def __init__(self, log, dts, loop, callback=None): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project, callback=None): + super().__init__(log, dts, loop, project) self.callback = callback def get_reg_flags(self): """Default set of REG flags, can be over-ridden by sub classes. - + Returns: Set of rwdts.Flag types. """ @@ -70,7 +73,7 @@ class AbstractOpdataSubscriber(SubscriberDtsHandler): Opdata subscriber can be created in one step by subclassing and implementing the MANDATORY get_xpath() method - + """ @abc.abstractmethod def get_xpath(self): @@ -84,6 +87,12 @@ class AbstractOpdataSubscriber(SubscriberDtsHandler): def register(self): """Triggers the registration """ + + if self.reg: + self._log.warning("RPC already registered for project {}". + format(self._project.name)) + return + xacts = {} def on_commit(xact_info): @@ -121,7 +130,7 @@ class AbstractOpdataSubscriber(SubscriberDtsHandler): ) self.reg = yield from self.dts.register( - xpath=self.get_xpath(), + xpath=self.project.add_project(self.get_xpath()), flags=self.get_reg_flags(), handler=handler) @@ -129,9 +138,6 @@ class AbstractOpdataSubscriber(SubscriberDtsHandler): assert self.reg is not None - def deregister(self): - self.reg.deregister() - class AbstractConfigSubscriber(SubscriberDtsHandler): """Abstract class that simplifies the process of creating subscribers @@ -139,7 +145,7 @@ class AbstractConfigSubscriber(SubscriberDtsHandler): Config subscriber can be created in one step by subclassing and implementing the MANDATORY get_xpath() method - + """ KEY = "msgs" @@ -151,41 +157,21 @@ class AbstractConfigSubscriber(SubscriberDtsHandler): def key_name(self): pass - def get_add_delete_update_cfgs(self, dts_member_reg, xact, key_name): - # Unforunately, it is currently difficult to figure out what has exactly - # changed in this xact without Pbdelta support (RIFT-4916) - # As a workaround, we can fetch the pre and post xact elements and - # perform a comparison to figure out adds/deletes/updates - xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) - curr_cfgs = list(dts_member_reg.elements) - - xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} - curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} - - # Find Adds - added_keys = set(xact_key_map) - set(curr_key_map) - added_cfgs = [xact_key_map[key] for key in added_keys] - - # Find Deletes - deleted_keys = set(curr_key_map) - set(xact_key_map) - deleted_cfgs = [curr_key_map[key] for key in deleted_keys] - - # Find Updates - updated_keys = set(curr_key_map) & set(xact_key_map) - updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] - - return added_cfgs, deleted_cfgs, updated_cfgs - @asyncio.coroutine def register(self): """ Register for VNFD configuration""" + if self.reg: + self._log.warning("RPC already registered for project {}". + format(self._project.name)) + return + def on_apply(dts, acg, xact, action, scratch): """Apply the configuration""" is_recovery = xact.xact is None and action == rwdts.AppconfAction.INSTALL - add_cfgs, delete_cfgs, update_cfgs = self.get_add_delete_update_cfgs( + add_cfgs, delete_cfgs, update_cfgs = get_add_delete_update_cfgs( dts_member_reg=self.reg, xact=xact, key_name=self.key_name()) @@ -207,9 +193,6 @@ class AbstractConfigSubscriber(SubscriberDtsHandler): acg_hdl = rift.tasklets.AppConfGroup.Handler(on_apply=on_apply) with self.dts.appconf_group_create(handler=acg_hdl) as acg: self.reg = acg.register( - xpath=self.get_xpath(), + xpath=self.project.add_project(self.get_xpath()), flags=self.get_reg_flags(), on_prepare=on_prepare) - - def deregister(self): - self.reg.deregister() diff --git a/common/python/rift/mano/dts/subscriber/ns_subscriber.py b/common/python/rift/mano/dts/subscriber/ns_subscriber.py index c16f7716..a379b9a3 100644 --- a/common/python/rift/mano/dts/subscriber/ns_subscriber.py +++ b/common/python/rift/mano/dts/subscriber/ns_subscriber.py @@ -39,7 +39,7 @@ class NsrCatalogSubscriber(core.AbstractOpdataSubscriber): return rwdts.Flag.SUBSCRIBER|rwdts.Flag.DELTA_READY def get_xpath(self): - return "D,/nsr:ns-instance-opdata/nsr:nsr" + return self._project.add_project("D,/nsr:ns-instance-opdata/nsr:nsr") class NsdCatalogSubscriber(core.AbstractConfigSubscriber): @@ -49,7 +49,7 @@ class NsdCatalogSubscriber(core.AbstractConfigSubscriber): return "id" def get_xpath(self): - return "C,/nsd:nsd-catalog/nsd:nsd" + return self._project.add_project("C,/nsd:nsd-catalog/nsd:nsd") class NsInstanceConfigSubscriber(core.AbstractConfigSubscriber): @@ -59,4 +59,4 @@ class NsInstanceConfigSubscriber(core.AbstractConfigSubscriber): return "id" def get_xpath(self): - return "C,/nsr:ns-instance-config/nsr:nsr" + return self._project.add_project("C,/nsr:ns-instance-config/nsr:nsr") diff --git a/common/python/rift/mano/dts/subscriber/ro_account.py b/common/python/rift/mano/dts/subscriber/ro_account.py index 575d649c..3091d155 100644 --- a/common/python/rift/mano/dts/subscriber/ro_account.py +++ b/common/python/rift/mano/dts/subscriber/ro_account.py @@ -33,4 +33,4 @@ class ROAccountConfigSubscriber(core.AbstractConfigSubscriber): return "name" def get_xpath(self): - return("C,/rw-launchpad:resource-orchestrator") \ No newline at end of file + return self._project.add_project("C,/rw-launchpad:resource-orchestrator") diff --git a/common/python/rift/mano/dts/subscriber/store.py b/common/python/rift/mano/dts/subscriber/store.py index 88cb79a3..222d444d 100644 --- a/common/python/rift/mano/dts/subscriber/store.py +++ b/common/python/rift/mano/dts/subscriber/store.py @@ -33,10 +33,10 @@ class SubscriberStore(core.SubscriberDtsHandler): """ KEY = enum.Enum('KEY', 'NSR NSD VNFD VNFR') - def __init__(self, log, dts, loop, callback=None): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project, callback=None): + super().__init__(log, dts, loop, project) - params = (self.log, self.dts, self.loop) + params = (self.log, self.dts, self.loop, self.project) self._nsr_sub = ns_subscriber.NsrCatalogSubscriber(*params, callback=self.on_nsr_change) self._nsrs = {} @@ -92,6 +92,14 @@ class SubscriberStore(core.SubscriberDtsHandler): yield from self._vnfr_sub.register() yield from self._nsr_sub.register() + def deregister(self): + self._log.debug("De-register store for project {}". + format(self._project)) + self._vnfd_sub.deregister() + self._nsd_sub.deregister() + self._vnfr_sub.deregister() + self._nsr_sub.deregister() + @asyncio.coroutine def refresh_store(self, subsriber, store): itr = yield from self.dts.query_read(subsriber.get_xpath()) diff --git a/common/python/rift/mano/dts/subscriber/test/utest_subscriber_dts.py b/common/python/rift/mano/dts/subscriber/test/utest_subscriber_dts.py index a69a00f5..bf7ae680 100644 --- a/common/python/rift/mano/dts/subscriber/test/utest_subscriber_dts.py +++ b/common/python/rift/mano/dts/subscriber/test/utest_subscriber_dts.py @@ -107,10 +107,10 @@ class SubscriberStoreDtsTestCase(rift.test.dts.AbstractDTSTest): def test_vnfd_handler(self): yield from self.store.register() - mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + mock_vnfd = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() mock_vnfd.id = str(uuid.uuid1()) - w_xpath = "C,/vnfd:vnfd-catalog/vnfd:vnfd" + w_xpath = "C,/rw-project:project/vnfd:vnfd-catalog/vnfd:vnfd" xpath = "{}[vnfd:id='{}']".format(w_xpath, mock_vnfd.id) yield from self.publisher.publish(w_xpath, xpath, mock_vnfd) @@ -128,10 +128,10 @@ class SubscriberStoreDtsTestCase(rift.test.dts.AbstractDTSTest): def test_vnfr_handler(self): yield from self.store.register() - mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + mock_vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() mock_vnfr.id = str(uuid.uuid1()) - w_xpath = "D,/vnfr:vnfr-catalog/vnfr:vnfr" + w_xpath = "D,/rw-project:project/vnfr:vnfr-catalog/vnfr:vnfr" xpath = "{}[vnfr:id='{}']".format(w_xpath, mock_vnfr.id) yield from self.publisher.publish(w_xpath, xpath, mock_vnfr) @@ -151,11 +151,11 @@ class SubscriberStoreDtsTestCase(rift.test.dts.AbstractDTSTest): def test_nsr_handler(self): yield from self.store.register() - mock_nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr() + mock_nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr() mock_nsr.ns_instance_config_ref = str(uuid.uuid1()) mock_nsr.name_ref = "Foo" - w_xpath = "D,/nsr:ns-instance-opdata/nsr:nsr" + w_xpath = "D,/rw-project:project/nsr:ns-instance-opdata/nsr:nsr" xpath = "{}[nsr:ns-instance-config-ref='{}']".format(w_xpath, mock_nsr.ns_instance_config_ref) yield from self.publisher.publish(w_xpath, xpath, mock_nsr) @@ -175,10 +175,10 @@ class SubscriberStoreDtsTestCase(rift.test.dts.AbstractDTSTest): def test_nsd_handler(self): yield from self.store.register() - mock_nsd = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd() + mock_nsd = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() mock_nsd.id = str(uuid.uuid1()) - w_xpath = "C,/nsd:nsd-catalog/nsd:nsd" + w_xpath = "C,/rw-project:project/nsd:nsd-catalog/nsd:nsd" xpath = "{}[nsd:id='{}']".format(w_xpath, mock_nsd.id) yield from self.publisher.publish(w_xpath, xpath, mock_nsd) @@ -206,22 +206,22 @@ class SubscriberStoreDtsTestCase(rift.test.dts.AbstractDTSTest): # publish yield from vnf_handler.register() - mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + mock_vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() mock_vnfr.id = str(uuid.uuid1()) def mon_xpath(param_id=None): """ Monitoring params xpath """ - return("D,/vnfr:vnfr-catalog" + + return("D,/rw-project:project/vnfr:vnfr-catalog" + "/vnfr:vnfr[vnfr:id='{}']".format(mock_vnfr.id) + "/vnfr:monitoring-param" + ("[vnfr:id='{}']".format(param_id) if param_id else "")) - w_xpath = "D,/vnfr:vnfr-catalog/vnfr:vnfr" + w_xpath = "D,/rw-project:project/vnfr:vnfr-catalog/vnfr:vnfr" xpath = "{}[vnfr:id='{}']".format(w_xpath, mock_vnfr.id) yield from self.publisher.publish(w_xpath, xpath, mock_vnfr) - mock_param = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam.from_dict({ + mock_param = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam.from_dict({ "id": "1" }) mock_vnfr.monitoring_param.append(mock_param) diff --git a/common/python/rift/mano/dts/subscriber/vnf_subscriber.py b/common/python/rift/mano/dts/subscriber/vnf_subscriber.py index 76a58aba..85ec5894 100644 --- a/common/python/rift/mano/dts/subscriber/vnf_subscriber.py +++ b/common/python/rift/mano/dts/subscriber/vnf_subscriber.py @@ -38,7 +38,7 @@ class VnfrCatalogSubscriber(core.AbstractOpdataSubscriber): return rwdts.Flag.SUBSCRIBER|rwdts.Flag.DELTA_READY def get_xpath(self): - return "D,/vnfr:vnfr-catalog/vnfr:vnfr" + return self.project.add_project("D,/vnfr:vnfr-catalog/vnfr:vnfr") class VnfdCatalogSubscriber(core.AbstractConfigSubscriber): @@ -48,4 +48,4 @@ class VnfdCatalogSubscriber(core.AbstractConfigSubscriber): return "id" def get_xpath(self): - return "C,/vnfd:vnfd-catalog/vnfd:vnfd" + return self.project.add_project("C,/vnfd:vnfd-catalog/vnfd:vnfd") diff --git a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_parameter.py b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_parameter.py index aa6b83b5..7df584f0 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_parameter.py +++ b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_parameter.py @@ -42,7 +42,7 @@ class ManoParameter(object): # TODO(Philip): Harcoding for now, need to make this generic def get_xpath(self): - xpath = '/nsd:nsd-catalog/nsd:nsd/nsd:' + self.name + xpath = '/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:' + self.name return xpath def get_dict_output(self): diff --git a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py index d263e6f0..edcbef6d 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py +++ b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_template.py @@ -61,7 +61,7 @@ class ManoTemplate(object): if use_gi: try: - nsd_cat = RwNsdYang.YangData_Nsd_NsdCatalog() + nsd_cat = RwNsdYang.YangData_RwProject_Project_NsdCatalog() nsd = nsd_cat.nsd.add() nsd.id = nsd_id nsd.name = self.metadata['name'] @@ -111,7 +111,7 @@ class ManoTemplate(object): if use_gi: for param in self.parameters: nsd.input_parameter_xpath.append( - NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath( + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath( xpath=param.get_xpath(), ) ) diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py index 3e529671..6356601c 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py @@ -222,7 +222,7 @@ class ToscaNfvVnf(ManoResource): format(self.name, self.properties)) def generate_yang_model_gi(self, nsd, vnfds): - vnfd_cat = RwVnfdYang.YangData_Vnfd_VnfdCatalog() + vnfd_cat = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog() vnfd = vnfd_cat.vnfd.add() props = convert_keys_to_python(self.properties) try: diff --git a/common/python/rift/mano/utils/project.py b/common/python/rift/mano/utils/project.py index 9c303f97..637c0caf 100644 --- a/common/python/rift/mano/utils/project.py +++ b/common/python/rift/mano/utils/project.py @@ -16,6 +16,21 @@ # limitations under the License. # +import abc +import asyncio +import logging + +import gi +gi.require_version('RwProjectYang', '1.0') +gi.require_version('RwDtsYang', '1.0') +from gi.repository import ( + RwProjectYang, + RwDts as rwdts, + ProtobufC, +) + +import rift.tasklets + class ManoProjectError(Exception): pass @@ -33,19 +48,37 @@ class ManoProjXpathKeyErr(ManoProjectError): pass +class ManoProjXpathNotRootErr(ManoProjectError): + pass + + +class ManoProjXpathPresentErr(ManoProjectError): + pass + + +NS = 'rw-project' +PROJECT = 'project' +NS_PROJECT = '{}:{}'.format(NS, PROJECT) +XPATH = '/{}'.format(NS_PROJECT) +XPATH_LEN = len(XPATH) + +NAME = 'name' +NAME_LEN = len(NAME) +NS_NAME = '{}:{}'.format(NS, NAME) + +DEFAULT_PROJECT = 'default' +DEFAULT_PREFIX = "{}[{}='{}']".format(XPATH, + NS_NAME, + DEFAULT_PROJECT) + + class ManoProject(object): '''Class to handle the project name''' - NS = 'rw-project' - XPATH = '/{}:project'.format(NS) - XPATH_LEN = len(XPATH) - - NAME = 'name' - NAME_LEN = len(NAME) - NS_NAME = '{}:{}'.format(NS, NAME) + log = None @classmethod - def create_from_xpath(cls, xpath, log): + def instance_from_xpath(cls, xpath, log): name = cls.from_xpath(xpath, log) if name is None: return None @@ -57,24 +90,23 @@ class ManoProject(object): def from_xpath(cls, xpath, log): log.debug("Get project name from {}".format(xpath)); - if cls.XPATH in xpath: - idx = xpath.find(cls.XPATH) + cls.XPATH_LEN + if XPATH in xpath: + idx = xpath.find(XPATH) if idx == -1: msg = "Project not found in XPATH: {}".format(xpath) log.error(msg) raise ManoProjXpathNoProjErr(msg) - sub = xpath[idx:] - sub = sub.strip() - if (len(sub) < cls.NAME_LEN) or (sub[0] != '['): + sub = xpath[idx+XPATH_LEN:].strip() + if (len(sub) < NAME_LEN) or (sub[0] != '['): msg = "Project name not found in XPath: {}".format(xpath) log.error(msg) raise ManoProjXpathKeyErr(msg) sub = sub[1:].strip() - idx = sub.find(cls.NS_NAME) + idx = sub.find(NS_NAME) if idx == -1: - idx = sub.find(cls.NAME) + idx = sub.find(NAME) if idx != 0: msg = "Project name not found in XPath: {}".format(xpath) log.error(msg) @@ -86,10 +118,11 @@ class ManoProject(object): log.error(msg) raise ManoProjXpathKeyErr(msg) - sub = sub[:idx-1].strip() + sub = sub[:idx].strip() try: + log.debug("Key and value found: {}".format(sub)) k, n = sub.split("=", 2) - name = n.strip() + name = n.strip(' \'"') if name is None: msg = "Project name is empty in XPath".format(xpath) log.error(msg) @@ -104,23 +137,497 @@ class ManoProject(object): .format(xpath, e) log.exception(msg) raise ManoProjXpathKeyErr(msg) + else: + msg = "Project not found in XPATH: {}".format(xpath) + log.error(msg) + raise ManoProjXpathNoProjErr(msg) + + @classmethod + def get_log(cls): + if not cls.log: + cls.log = logging.getLogger('rw-mano-log.rw-project') + cls.log.setLevel(logging.ERROR) + + @classmethod + def prefix_project(cls, xpath, project=None, log=None): + if log is None: + log = cls.get_log() + + if project is None: + project = DEFAULT_PROJECT + proj_prefix = DEFAULT_PREFIX + else: + proj_prefix = "{}[{}='{}']".format(XPATH, + NS_NAME, + project) + + log.debug("Add project {} to {}".format(project, xpath)) + + prefix = '' + suffix = xpath + idx = xpath.find('C,/') + if idx == -1: + idx = xpath.find('D,/') + + suffix = xpath + if idx != -1: + prefix = xpath[:2] + suffix = xpath[2:] + + if suffix[0] != '/': + msg = "Non-rooted xpath provided: {}".format(xpath) + log.error(msg) + raise ManoProjXpathNotRootErr(msg) + + idx = suffix.find(XPATH) + if idx == 0: + name = cls.from_xpath(xpath, log) + if name == project: + log.warning("Project already in the XPATH: {}".format(xpath)) + return xpath + + else: + msg = "Different project {} already in XPATH {}". \ + format(name, xpath) + log.error(msg) + raise ManoProjXpathPresentErr(msg) + + ret = prefix + proj_prefix + suffix + log.debug("XPath with project: {}".format(ret)) + return ret + - def __init__(self, log, name=None): + def __init__(self, log, name=None, tasklet=None): self._log = log - self._name = name + self._name = None + self._prefix = None + self._pbcm = None + self._tasklet = None + self._dts = None + self._loop = None + self._log_hdl = None + + # Track if the apply config was received + self._apply = False + + if name: + self.name = name + + def update(self, tasklet): + # Store the commonly used properties from a tasklet + self._tasklet = tasklet + self._log_hdl = tasklet.log_hdl + self._dts = tasklet.dts + self._loop = tasklet.loop @property def name(self): return self._name + @property + def log(self): + return self._log + + @property + def prefix(self): + return self._prefix + + @property + def pbcm(self): + return self._pbcm + + @property + def config(self): + return self._pbcm.project_config + + @property + def tasklet(self): + return self._tasklet + + @property + def log_hdl(self): + return self._log_hdl + + @property + def dts(self): + return self._dts + + @property + def loop(self): + return self._loop + @name.setter def name(self, value): if self._name is None: self._name = value + self._prefix = "{}[{}='{}']".format(XPATH, + NS_NAME, + self._name) + self._pbcm = RwProjectYang.YangData_RwProject_Project( + name=self._name) + + elif self._name == value: + self._log.debug("Setting the same name again for project {}". + format(value)) else: msg = "Project name already set to {}".format(self._name) self._log.error(msg) raise ManoProjNameSetErr(msg) def set_from_xpath(self, xpath): - self.name = ManoProject.get_from_xpath(xpath, self._log) + self.name = ManoProject.from_xpath(xpath, self._log) + + def add_project(self, xpath): + return ManoProject.prefix_project(xpath, log=self._log, project=self._name) + + @abc.abstractmethod + @asyncio.coroutine + def delete_prepare(self): + self._log.debug("Delete prepare for project {}".format(self._name)) + return True + + @abc.abstractmethod + @asyncio.coroutine + def register(self): + msg = "Register not implemented for project type {}". \ + format(self.__class__.__name__) + self._log.error(msg) + raise NotImplementedError(msg) + + @abc.abstractmethod + def deregister(self): + msg = "De-register not implemented for project type {}". \ + format(self.__class__.__name__) + self._log.error(msg) + raise NotImplementedError(msg) + + def rpc_check(self, msg, xact_info=None): + '''Check if the rpc is for this project''' + try: + project = msg.project_name + except AttributeError as e: + project = DEFAULT_PROJECT + + if project != self.name: + self._log.debug("Project {}: RPC is for different project {}". + format(self.name, project)) + if xact_info: + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + return False + + return True + + @asyncio.coroutine + def create_project(self, dts): + proj_xpath = "C,{}/project-config".format(self.prefix) + self._log.info("Creating project: {} with {}". + format(proj_xpath, self.config.as_dict())) + + yield from dts.query_create(proj_xpath, + rwdts.XactFlag.ADVISE, + self.config) + + +def get_add_delete_update_cfgs(dts_member_reg, xact, key_name): + #TODO: Check why this is getting called during project delete + if not dts_member_reg: + return [], [], [] + + # Unforunately, it is currently difficult to figure out what has exactly + # changed in this xact without Pbdelta support (RIFT-4916) + # As a workaround, we can fetch the pre and post xact elements and + # perform a comparison to figure out adds/deletes/updates + xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) + curr_cfgs = list(dts_member_reg.elements) + + xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} + curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} + + # Find Adds + added_keys = set(xact_key_map) - set(curr_key_map) + added_cfgs = [xact_key_map[key] for key in added_keys] + + # Find Deletes + deleted_keys = set(curr_key_map) - set(xact_key_map) + deleted_cfgs = [curr_key_map[key] for key in deleted_keys] + + # Find Updates + updated_keys = set(curr_key_map) & set(xact_key_map) + updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] + + return added_cfgs, deleted_cfgs, updated_cfgs + + +class ProjectConfigCallbacks(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 ProjectDtsHandler(object): + XPATH = "C,{}/project-config".format(XPATH) + + def __init__(self, dts, log, callbacks): + self._dts = dts + self._log = log + self._callbacks = callbacks + + self.reg = None + self.projects = [] + + @property + def log(self): + return self._log + + @property + def dts(self): + return self._dts + + def add_project(self, name): + self.log.info("Adding project: {}".format(name)) + + if name not in self.projects: + self._callbacks.on_add_apply(name) + self.projects.append(name) + else: + self.log.error("Project already present: {}". + format(name)) + + def delete_project(self, name): + self._log.info("Deleting project: {}".format(name)) + if name in self.projects: + self._callbacks.on_delete_apply(name) + self.projects.remove(name) + else: + self.log.error("Unrecognized project: {}". + format(name)) + + def update_project(self, name): + """ Update an existing project + + Currently, we do not take any action on MANO for this, + so no callbacks are defined + + Arguments: + msg - The project config message + """ + self._log.info("Updating project: {}".format(name)) + if name in self.projects: + pass + else: + self.log.error("Unrecognized project: {}". + format(name)) + + def register(self): + @asyncio.coroutine + def apply_config(dts, acg, xact, action, scratch): + self._log.debug("Got project 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("Project being re-added after restart.") + self.add_project(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_ref", + ) + + # Handle Deletes + for cfg in delete_cfgs: + self.delete_project(cfg.name_ref) + + # Handle Adds + for cfg in add_cfgs: + self.add_project(cfg.name_ref) + + # Handle Updates + for cfg in update_cfgs: + self.update_project(cfg.name_ref) + + @asyncio.coroutine + def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch): + """ Prepare callback from DTS for Project """ + + # xpath = ks_path.to_xpath(RwProjectYang.get_schema()) + # name = ManoProject.from_xpath(xpath, self._log) + # if not name: + # self._log.error("Did not find the project name in ks: {}". + # format(xpath)) + # xact_info.respond_xpath(rwdts.XactRspCode.NACK) + # return + + action = xact_info.query_action + name = msg.name_ref + self._log.debug("Project %s on_prepare config received (action: %s): %s", + name, xact_info.query_action, msg) + + if action in [rwdts.QueryAction.CREATE, rwdts.QueryAction.UPDATE]: + if name in self.projects: + self._log.debug("Project {} already exists. Ignore request". + format(name)) + + else: + self._log.debug("Project {}: Invoking on_prepare add request". + format(name)) + yield from self._callbacks.on_add_prepare(name) + + elif action == rwdts.QueryAction.DELETE: + # Check if the entire project got deleted + fref = ProtobufC.FieldReference.alloc() + fref.goto_whole_message(msg.to_pbcm()) + if fref.is_field_deleted(): + if name in self.projects: + rc = yield from self._callbacks.on_delete_prepare(name) + if not rc: + self._log.error("Project {} should not be deleted". + format(name)) + xact_info.respond_xpath(rwdts.XactRspCode.NACK) + else: + self._log.warning("Delete on unknown project: {}". + format(name)) + + else: + self._log.error("Action (%s) NOT SUPPORTED", action) + xact_info.respond_xpath(rwdts.XactRspCode.NACK) + return + + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + + self._log.debug("Registering for project config using xpath: %s", + ProjectDtsHandler.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=ProjectDtsHandler.XPATH, + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, + on_prepare=on_prepare, + ) + +class ProjectHandler(object): + def __init__(self, tasklet, project_class, **kw): + self._tasklet = tasklet + self._log = tasklet.log + self._log_hdl = tasklet.log_hdl + self._dts = tasklet.dts + self._loop = tasklet.loop + self._class = project_class + self._kw = kw + + self._log.debug("Creating project config handler") + self.project_cfg_handler = ProjectDtsHandler( + self._dts, self._log, + ProjectConfigCallbacks( + on_add_apply=self.on_project_added, + on_add_prepare=self.on_add_prepare, + on_delete_apply=self.on_project_deleted, + on_delete_prepare=self.on_delete_prepare, + ) + ) + + def _get_tasklet_name(self): + return self._tasklet.tasklet_info.instance_name + + def _get_project(self, name): + try: + proj = self._tasklet.projects[name] + except Exception as e: + self._log.exception("Project {} ({})not found for tasklet {}: {}". + format(name, list(self._tasklet.projects.keys()), + self._get_tasklet_name(), e)) + raise e + + return proj + + def on_project_deleted(self, name): + self._log.debug("Project {} deleted".format(name)) + try: + self._get_project(name).deregister() + except Exception as e: + self._log.exception("Project {} deregister for {} failed: {}". + format(name, self._get_tasklet_name(), e)) + + try: + proj = self._tasklet.projects.pop(name) + del proj + except Exception as e: + self._log.exception("Project {} delete for {} failed: {}". + format(name, self._get_tasklet_name(), e)) + + def on_project_added(self, name): + self._log.debug("Project {} added to tasklet {}". + format(name, self._get_tasklet_name())) + self._get_project(name)._apply = True + + @asyncio.coroutine + def on_add_prepare(self, name): + self._log.debug("Project {} to be added to {}". + format(name, self._get_tasklet_name())) + + try: + self._tasklet.projects[name] = \ + self._class(name, self._tasklet, **(self._kw)) + except Exception as e: + self._log.exception("Project {} create for {} failed: {}". + formatname, self._get_tasklet_name(), e()) + + try: + yield from self._get_project(name).register() + except Exception as e: + self._log.exception("Project {} register for tasklet {} failed: {}". + format(name, self._get_tasklet_name(), e)) + + @asyncio.coroutine + def on_delete_prepare(self, name): + self._log.debug("Project {} being deleted for tasklet {}". + format(name, self._get_tasklet_name())) + rc = yield from self._get_project(name).delete_prepare() + return rc + + def register(self): + self.project_cfg_handler.register() diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py index 491bd86d..41845dca 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py @@ -73,7 +73,7 @@ class YangNsd(ToscaResource): self.inputs.append({ self.NAME: self.map_yang_name_to_tosca( - val.replace('/nsd:nsd-catalog/nsd:nsd/nsd:', ''))}) + val.replace('/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:', ''))}) if len(param): self.log.warn(_("{0}, Did not process the following for " "input param {1}: {2}"). diff --git a/common/python/test/utest_project.py b/common/python/test/utest_project.py new file mode 100644 index 00000000..1bc531ec --- /dev/null +++ b/common/python/test/utest_project.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 + +# +# 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 argparse +import asyncio +import logging +import os +import sys +import unittest +import xmlrunner + +from rift.mano.utils import project + +NAME = 'test' +XPATH = "/rw-project:project[rw-project:name='{}']".format(NAME) + +class TestCase(unittest.TestCase): + log = None + + @classmethod + def set_logger(cls, log): + cls.log = log + + def setUp(self): + if not TestCase.log: + log = logging.getLogger() + log.setLevel( logging.ERROR) + + def test_create_from_xpath(self): + """ + Asserts: + 1. Instance of project from xpath + 2. project name in instance is correct + """ + proj = project.ManoProject.create_from_xpath(XPATH, TestCase.log) + assert proj + assert NAME == proj.name + assert XPATH == proj.prefix + + obj = project.ManoProject.create_from_xpath(proj.prefix, TestCase.log) + assert obj + assert NAME == obj.name + assert XPATH == obj.prefix + + def test_create(self): + """ + Asserts: + 1. Instance of project + 2. project name in instance is correct + """ + proj = project.ManoProject(TestCase.log, name=NAME) + assert proj + assert NAME == proj.name + assert XPATH == proj.prefix + + obj = project.ManoProject.create_from_xpath(proj.prefix, TestCase.log) + assert obj + assert NAME == obj.name + assert XPATH == obj.prefix + + def test_create_update(self): + """ + Asserts: + 1. Instance of project + 2. Set project name later + 3. project name in instance is correct + """ + proj = project.ManoProject(TestCase.log) + assert proj + assert None == proj.name + + proj.name = NAME + assert NAME == proj.name + assert XPATH == proj.prefix + + try: + proj.name = 'testing' + except project.ManoProjNameSetErr as e: + TestCase.log.debug("Expected exception: {}".format(e)) + else: + assert False + + obj = project.ManoProject.create_from_xpath(proj.prefix, TestCase.log) + assert obj + assert NAME == obj.name + assert XPATH == obj.prefix + + def test_update_from_xpath(self): + """ + Asserts: + 1. Instance of project + 2. Update from XPATH + 2. project name in instance is correct + """ + proj = project.ManoProject(TestCase.log) + assert proj + assert proj.name is None + + proj.set_from_xpath(XPATH) + assert NAME == proj.name + assert XPATH == proj.prefix + + try: + proj.set_from_xpath(XPATH) + except project.ManoProjNameSetErr as e: + TestCase.log.debug("Expected exception: {}".format(e)) + else: + assert False + + obj = project.ManoProject.create_from_xpath(proj.prefix, TestCase.log) + assert obj + assert NAME == obj.name + assert XPATH == obj.prefix + + def test_create_from_xpath1(self): + """ + Asserts: + 1. Instance of project from xpath + 2. project name in instance is correct + """ + xpath = XPATH + '/rw:project/rw-project:project/rw-project:project/rw-project:project/rw-project:project/nsd:nsd-catalog/nsd:nsd[id=\'1232334\']' + proj = project.ManoProject.create_from_xpath(xpath, TestCase.log) + assert proj + assert NAME == proj.name + assert XPATH == proj.prefix + + def test_create_from_xpath2(self): + """ + Asserts: + 1. Instance of project from xpath + 2. project name in instance is correct + """ + xpath = '/rw-project:project [ name = "{}" ]'.format(NAME) + proj = project.ManoProject.create_from_xpath(xpath, TestCase.log) + assert proj + assert NAME == proj.name + assert XPATH == proj.prefix + + def test_create_from_xpath_invalid(self): + """ + Asserts: + 1. Exception due to invalid XPATH format for extracting project + """ + xpath = '/' + try: + proj = project.ManoProject.create_from_xpath(xpath, TestCase.log) + except project.ManoProjXpathNoProjErr as e: + TestCase.log.debug("Expected exception: {}".format(e)) + else: + assert False + + def test_create_from_xpath_invalid1(self): + """ + Asserts: + 1. Exception due to invalid XPATH format for extracting project + """ + xpath = '/rw-project:project/{}'.format(NAME) + try: + proj = project.ManoProject.create_from_xpath(xpath, TestCase.log) + except project.ManoProjXpathKeyErr as e: + TestCase.log.debug("Expected exception: {}".format(e)) + else: + assert False + + def test_create_from_xpath_invalid2(self): + """ + Asserts: + 1. Exception due to invalid XPATH format for extracting project + """ + xpath = '/rw-project:project[id=test]' + try: + proj = project.ManoProject.create_from_xpath(xpath, TestCase.log) + except project.ManoProjXpathKeyErr as e: + TestCase.log.debug("Expected exception: {}".format(e)) + else: + assert False + + def tearDown(self): + pass + + +def main(argv=sys.argv[1:]): + logging.basicConfig(format='TEST %(message)s') + + runner = xmlrunner.XMLTestRunner(output=os.environ["RIFT_MODULE_TEST"]) + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--verbose', action='store_true') + parser.add_argument('-n', '--no-runner', action='store_true') + + args, unknown = parser.parse_known_args(argv) + if args.no_runner: + runner = None + + # Set the global logging level + log = logging.getLogger() + log.setLevel(logging.DEBUG if args.verbose else logging.ERROR) + TestCase.set_logger(log) + + # The unittest framework requires a program name, so use the name of this + # file instead (we do not want to have to pass a fake program name to main + # when this is called from the interpreter). + unittest.main(argv=[__file__] + unknown + ["-v"], testRunner=runner) + +if __name__ == '__main__': + main() diff --git a/common/rw_gen_package.py b/common/rw_gen_package.py index 427e7175..7c052f0d 100755 --- a/common/rw_gen_package.py +++ b/common/rw_gen_package.py @@ -40,9 +40,9 @@ def read_from_file(module_list, infile, input_format, descr_type): descr = None if descr_type == "nsd": - descr = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd() + descr = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() else: - descr = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + descr = VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() if input_format == 'json': json_str = open(infile).read() diff --git a/examples/ping_pong_ns/config_desc.py b/examples/ping_pong_ns/config_desc.py index fcd14000..50204f9b 100755 --- a/examples/ping_pong_ns/config_desc.py +++ b/examples/ping_pong_ns/config_desc.py @@ -39,7 +39,7 @@ model.load_schema_ypbc(VnfdYang.get_schema()) def configure_vld(proxy, vld_xml_hdl): vld_xml = vld_xml_hdl.read() logger.debug("Attempting to deserialize XML into VLD protobuf: %s", vld_xml) - vld = VldYang.YangData_Vld_VldCatalog_Vld() + vld = VldYang.YangData_RwProject_Project_VldCatalog_Vld() vld.from_xml_v2(model, vld_xml) logger.debug("Sending VLD to netconf: %s", vld) @@ -49,7 +49,7 @@ def configure_vld(proxy, vld_xml_hdl): def configure_vnfd(proxy, vnfd_xml_hdl): vnfd_xml = vnfd_xml_hdl.read() logger.debug("Attempting to deserialize XML into VNFD protobuf: %s", vnfd_xml) - vnfd = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + vnfd = VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() vnfd.from_xml_v2(model, vnfd_xml) logger.debug("Sending VNFD to netconf: %s", vnfd) @@ -59,7 +59,7 @@ def configure_vnfd(proxy, vnfd_xml_hdl): def configure_nsd(proxy, nsd_xml_hdl): nsd_xml = nsd_xml_hdl.read() logger.debug("Attempting to deserialize XML into NSD protobuf: %s", nsd_xml) - nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd() + nsd = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() nsd.from_xml_v2(model, nsd_xml) logger.debug("Sending NSD to netconf: %s", nsd) diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py index b18deb7c..e852b21e 100755 --- a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py +++ b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py @@ -112,10 +112,10 @@ class ManoDescriptor(object): if output_format == 'json': with open('%s/%s.json' % (outdir, self.name), "w") as fh: fh.write(simplejson.dumps(desc, indent=4)) - elif output_format.strip() == 'yaml': + elif output_format == 'yaml': with open('%s/%s.yaml' % (outdir, self.name), "w") as fh: fh.write(yaml.dump(desc, default_flow_style=False)) - elif output_format.strip() == 'xml': + elif output_format == 'xml': # Converting from dict to xml does not provide the # required output. So using the PBCM to_xml and then # printing only from the catalog tag. @@ -283,7 +283,7 @@ class VirtualNetworkFunction(ManoDescriptor): internal_cp.name = cp_name + "/icp{}".format(i) internal_cp.id = cp_name + "/icp{}".format(i) internal_cp.type_yang = 'VPORT' - ivld_cp = internal_vlds[i].internal_connection_point_ref.add() + ivld_cp = internal_vlds[i].internal_connection_point.add() ivld_cp.id_ref = internal_cp.id internal_interface = vdu.internal_interface.add() @@ -761,7 +761,7 @@ exit 0 group_desc.min_instance_count = scale_group.min_count for vnfd, count in scale_group.vnfd_count_map.items(): member = group_desc.vnfd_member.add() - member.member_vnf_index_ref = vnfd_index_map[vnfd] + member.member_vnf_index_ref = str(vnfd_index_map[vnfd]) member.count = count for trigger in scale_group.config_action: diff --git a/models/openmano/python/rift/openmano/rift2openmano.py b/models/openmano/python/rift/openmano/rift2openmano.py index 2ce17c08..8a308e61 100755 --- a/models/openmano/python/rift/openmano/rift2openmano.py +++ b/models/openmano/python/rift/openmano/rift2openmano.py @@ -88,20 +88,20 @@ class RiftNSD(object): @classmethod def from_xml_file_hdl(cls, hdl): hdl.seek(0) - descriptor = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd() + descriptor = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() descriptor.from_xml_v2(RiftNSD.model, hdl.read()) return cls(descriptor) @classmethod def from_yaml_file_hdl(cls, hdl): hdl.seek(0) - descriptor = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd() + descriptor = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() descriptor.from_yaml(RiftNSD.model, hdl.read()) return cls(descriptor) @classmethod def from_dict(cls, nsd_dict): - descriptor = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict(nsd_dict) + descriptor = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd.from_dict(nsd_dict) return cls(descriptor) @@ -143,20 +143,20 @@ class RiftVNFD(object): @classmethod def from_xml_file_hdl(cls, hdl): hdl.seek(0) - descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + descriptor = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() descriptor.from_xml_v2(RiftVNFD.model, hdl.read()) return cls(descriptor) @classmethod def from_yaml_file_hdl(cls, hdl): hdl.seek(0) - descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + descriptor = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() descriptor.from_yaml(RiftVNFD.model, hdl.read()) return cls(descriptor) @classmethod def from_dict(cls, vnfd_dict): - descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict(vnfd_dict) + descriptor = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd.from_dict(vnfd_dict) return cls(descriptor) diff --git a/models/plugins/yang/mano-types.yang b/models/plugins/yang/mano-types.yang index 3fd0da21..89fbca01 100644 --- a/models/plugins/yang/mano-types.yang +++ b/models/plugins/yang/mano-types.yang @@ -1200,9 +1200,11 @@ module mano-types } leaf http-endpoint-ref { - type leafref { - path "../../http-endpoint/path"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../http-endpoint/path"; + // } + type string; } leaf json-query-method { diff --git a/models/plugins/yang/nsd.yang b/models/plugins/yang/nsd.yang index 49bb9e6c..2b48ee7d 100644 --- a/models/plugins/yang/nsd.yang +++ b/models/plugins/yang/nsd.yang @@ -843,9 +843,11 @@ module nsd leaf member-vnf-index-ref { description "Reference to member-vnf within constituent-vnfds"; - type leafref { - path "../../../constituent-vnfd/member-vnf-index"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../../constituent-vnfd/member-vnf-index"; + // } + type uint64; } leaf vnfd-id-ref { @@ -865,11 +867,13 @@ module nsd leaf vnfd-connection-point-ref { description "A reference to a connection point name"; - type leafref { - path "../../../../../../vnfd:vnfd-catalog/vnfd:vnfd" + - "[vnfd:id = current()/../vnfd-id-ref]/" + - "vnfd:connection-point/vnfd:name"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../../../../../vnfd:vnfd-catalog/vnfd:vnfd" + + // "[vnfd:id = current()/../vnfd-id-ref]/" + + // "vnfd:connection-point/vnfd:name"; + // } + type string; } } } @@ -927,9 +931,11 @@ module nsd leaf vnfd-id-ref { description "Identifier for the VNFD."; - type leafref { - path "../../../../../vnfd:vnfd-catalog/vnfd:vnfd/vnfd:id"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../../../../vnfd:vnfd-catalog/vnfd:vnfd/vnfd:id"; + // } + type string; } leaf start-by-default { @@ -987,17 +993,21 @@ module nsd leaf member-vnf-index-ref { description "member VNF index of this member VNF"; - type leafref { - path "../../../constituent-vnfd/member-vnf-index"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../../constituent-vnfd/member-vnf-index"; + // } + type uint64; } leaf vnfd-id-ref { description "Identifier for the VNFD."; - type leafref { - path "../../../../../../vnfd:vnfd-catalog/vnfd:vnfd/vnfd:id"; - } + // TODO (Philip): Fix this + // type leafref { + // path "../../../../../../vnfd:vnfd-catalog/vnfd:vnfd/vnfd:id"; + // } + type string; } } } diff --git a/models/plugins/yang/rw-project.yang b/models/plugins/yang/rw-project.yang index 2f34c923..32030d1c 100644 --- a/models/plugins/yang/rw-project.yang +++ b/models/plugins/yang/rw-project.yang @@ -21,6 +21,10 @@ module rw-project namespace "http://riftio.com/ns/riftware-1.0/rw-project"; prefix "rw-project"; + import rw-pb-ext { + prefix "rw-pb-ext"; + } + revision 2017-02-08 { description "Initial revision. This YANG file defines the @@ -60,6 +64,13 @@ module rw-project description "Project management related configuration."; + leaf name-ref { + type leafref { + path "../../name"; + } + mandatory true; + } + // list user { // description // "The list of Users who have been assigned Roles within this diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py index 0400a54a..9d434245 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py @@ -32,8 +32,9 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): """ Implementation of the riftcm_config_plugin.RiftCMConfigPluginBase """ - def __init__(self, dts, log, loop, account): - riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop, account) + def __init__(self, dts, log, loop, project, account): + riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, + loop, project, account) self._name = account.name self._type = riftcm_config_plugin.DEFAULT_CAP_TYPE self._rift_install_dir = os.environ['RIFT_INSTALL'] diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py index 5f330249..f622d5fb 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py @@ -42,17 +42,19 @@ class RiftCMRPCHandler(object): GET_NS_CONF_XPATH = "I,/nsr:get-ns-service-primitive-values" GET_NS_CONF_O_XPATH = "O,/nsr:get-ns-service-primitive-values" - def __init__(self, dts, log, loop, nsm): + def __init__(self, dts, log, loop, project, nsm): self._dts = dts self._log = log self._loop = loop + self._project = project self._nsm = nsm self._ns_regh = None self._vnf_regh = None self._get_ns_conf_regh = None - self.job_manager = rift.mano.config_agent.ConfigAgentJobManager(dts, log, loop, nsm) + self.job_manager = rift.mano.config_agent.ConfigAgentJobManager(dts, log, loop, + project, nsm) self._rift_install_dir = os.environ['RIFT_INSTALL'] self._rift_artif_dir = os.environ['RIFT_ARTIFACTS'] @@ -67,6 +69,16 @@ class RiftCMRPCHandler(object): """ Return the NS manager instance """ return self._nsm + def deregister(self): + self._log.debug("De-register conman rpc handlers for project {}". + format(self._project)) + for reg in self.reghs: + if reg: + reg.deregister() + reg = None + + self.job_manager.deregister() + def prepare_meta(self, rpc_ip): try: @@ -264,6 +276,10 @@ class RiftCMRPCHandler(object): def on_ns_config_prepare(xact_info, action, ks_path, msg): """ prepare callback from dts exec-ns-service-primitive""" assert action == rwdts.QueryAction.RPC + + if not self._project.rpc_check(msg, xact_info): + return + rpc_ip = msg rpc_op = NsrYang.YangOutput_Nsr_ExecNsServicePrimitive.from_dict({ "triggered_by": rpc_ip.triggered_by, @@ -389,6 +405,10 @@ class RiftCMRPCHandler(object): @asyncio.coroutine def on_get_ns_config_values_prepare(xact_info, action, ks_path, msg): assert action == rwdts.QueryAction.RPC + + if not self._project.rpc_check(msg, xact_info): + return + nsr_id = msg.nsr_id_ref cfg_prim_name = msg.name try: diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py index add6a29d..d6425827 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py @@ -42,8 +42,9 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): """ Juju implementation of the riftcm_config_plugin.RiftCMConfigPluginBase """ - def __init__(self, dts, log, loop, account): - riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop, account) + def __init__(self, dts, log, loop, project, account): + riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop, + project, account) self._name = account.name self._type = 'juju' self._ip_address = account.juju.ip_address diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py index 640e4b59..d7716905 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py @@ -25,9 +25,10 @@ class RiftCMnsr(object): Agent class for NSR created for Agents to use objects from NSR ''' - def __init__(self, nsr_dict, cfg): + def __init__(self, nsr_dict, cfg, project): self._nsr = nsr_dict self._cfg = cfg + self._project = project self._vnfrs = [] self._vnfrs_msg = [] self._vnfr_ids = {} @@ -75,7 +76,7 @@ class RiftCMnsr(object): if vnfr['id'] in self._vnfr_ids.keys(): agent_vnfr = self._vnfr_ids[vnfr['id']] else: - agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg) + agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg, self._project) self._vnfrs.append(agent_vnfr) self._vnfrs_msg.append(vnfr_msg) self._vnfr_ids[agent_vnfr.id] = agent_vnfr @@ -89,11 +90,12 @@ class RiftCMvnfr(object): ''' Agent base class for VNFR processing ''' - def __init__(self, nsr_name, vnfr_dict, vnfr_msg): + def __init__(self, nsr_name, vnfr_dict, vnfr_msg, project): self._vnfr = vnfr_dict self._vnfr_msg = vnfr_msg self._nsr_name = nsr_name self._configurable = False + self._project = project @property def nsr_name(self): @@ -133,7 +135,8 @@ class RiftCMvnfr(object): @property def xpath(self): """ VNFR xpath """ - return "D,/vnfr:vnfr-catalog/vnfr:vnfr[vnfr:id = '{}']".format(self.id) + return self._project.add_project("D,/vnfr:vnfr-catalog/vnfr:vnfr[vnfr:id = '{}']". + format(self.id)) def set_to_configurable(self): self._configurable = True @@ -152,10 +155,11 @@ class RiftCMConfigPluginBase(object): There will be single instance of this plugin for each plugin type. """ - def __init__(self, dts, log, loop, config_agent): + def __init__(self, dts, log, loop, project, config_agent): self._dts = dts self._log = log self._loop = loop + self._project = project self._config_agent = config_agent @property diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py index 5578a357..61f3a176 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py @@ -48,16 +48,17 @@ class ConfigAgentVnfrTypeError(Exception): class ConfigAccountHandler(object): - def __init__(self, dts, log, loop, on_add_config_agent, on_delete_config_agent): + def __init__(self, dts, log, loop, project, on_add_config_agent, on_delete_config_agent): self._log = log self._dts = dts self._loop = loop + self._project = project self._on_add_config_agent = on_add_config_agent self._on_delete_config_agent = on_delete_config_agent self._log.debug("creating config account handler") self.cloud_cfg_handler = rift.mano.config_agent.ConfigAgentSubscriber( - self._dts, self._log, + self._dts, self._log, self._project, rift.mano.config_agent.ConfigAgentCallbacks( on_add_apply=self.on_config_account_added, on_delete_apply=self.on_config_account_deleted, @@ -77,6 +78,10 @@ class ConfigAccountHandler(object): def register(self): self.cloud_cfg_handler.register() + def deregister(self): + self.cloud_cfg_handler.deregister() + + class RiftCMConfigPlugins(object): """ NSM Config Agent Plugins """ def __init__(self): @@ -117,7 +122,8 @@ class RiftCMConfigAgent(object): self._config_plugins = RiftCMConfigPlugins() self._config_handler = ConfigAccountHandler( - self._dts, self._log, self._loop, self._on_config_agent, self._on_config_agent_delete) + self._dts, self._log, self._loop, parent._project, + self._on_config_agent, self._on_config_agent_delete) self._plugin_instances = {} self._default_account_added = False @@ -179,7 +185,8 @@ class RiftCMConfigAgent(object): else: # Otherwise, instantiate a new plugin using the config agent account self._log.debug("Instantiting new config agent using class: %s", cap_inst) - new_instance = cap_inst(self._dts, self._log, self._loop, config_agent) + new_instance = cap_inst(self._dts, self._log, self._loop, + self._ConfigManagerConfig._project, config_agent) self._plugin_instances[cap_name] = new_instance # TODO (pjoseph): See why this was added, as this deletes the @@ -216,6 +223,11 @@ class RiftCMConfigAgent(object): for account in config_agents: self._on_config_agent(account) + def deregister(self): + self._log.debug("De-registering config agent nsm plugin manager". + format(self._ConfigManagerConfig._project)) + self._config_handler.deregister() + def set_config_agent(self, nsr, vnfr, method): if method == 'juju': agent_type = 'juju' diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py index 7a24d702..6f76cb4f 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py @@ -127,13 +127,16 @@ class ConfigManagerConfig(object): self._log = log self._loop = loop self._parent = parent + self._project = parent._project + self._nsr_dict = {} self.pending_cfg = {} self.terminate_cfg = {} self.pending_tasks = [] # User for NSRid get retry # (mainly excercised at restart case) - self._config_xpath = "C,/cm-config" - self._opdata_xpath = "D,/rw-conman:cm-state" + + self._config_xpath = self._project.add_project("C,/rw-conman:cm-config") + self._opdata_xpath = self._project.add_project("D,/rw-conman:cm-state") self.cm_config = conmanY.SoConfig() # RO specific configuration @@ -147,7 +150,7 @@ class ConfigManagerConfig(object): self.cm_state['states'] = "Initialized" # Initialize objects to register - self.cmdts_obj = ConfigManagerDTS(self._log, self._loop, self, self._dts) + self.cmdts_obj = ConfigManagerDTS(self._log, self._loop, self, self._dts, self._project) self._config_agent_mgr = conagent.RiftCMConfigAgent( self._dts, self._log, @@ -157,10 +160,11 @@ class ConfigManagerConfig(object): self.reg_handles = [ self.cmdts_obj, self._config_agent_mgr, - RiftCM_rpc.RiftCMRPCHandler(self._dts, self._log, self._loop, + RiftCM_rpc.RiftCMRPCHandler(self._dts, self._log, self._loop, self._project, PretendNsm( self._dts, self._log, self._loop, self)), ] + self._op_reg = None def is_nsr_valid(self, nsr_id): if nsr_id in self._nsr_dict: @@ -241,7 +245,19 @@ class ConfigManagerConfig(object): # Initialize all handles that needs to be registered for reg in self.reg_handles: yield from reg.register() - + + def deregister(self): + # De-register all reg handles + self._log.debug("De-register ConfigManagerConfig for project {}". + format(self._project)) + + for reg in self.reg_handles: + reg.deregister() + reg = None + + self._op_reg.delete_element(self._opdata_xpath) + self._op_reg.deregister() + @asyncio.coroutine def register_cm_state_opdata(self): @@ -285,9 +301,9 @@ class ConfigManagerConfig(object): try: handler=rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare) - yield from self._dts.register(xpath=self._opdata_xpath, - handler=handler, - flags=rwdts.Flag.PUBLISHER) + self._op_reg = yield from self._dts.register(xpath=self._opdata_xpath, + handler=handler, + flags=rwdts.Flag.PUBLISHER) self._log.info("Successfully registered for opdata(%s)", self._opdata_xpath) except Exception as e: self._log.error("Failed to register for opdata as (%s)", e) @@ -301,7 +317,7 @@ class ConfigManagerConfig(object): if method in vnf_config: return method return None - + def get_cfg_file_extension(method, configuration_options): ext_dict = { "netconf" : "xml", @@ -473,7 +489,7 @@ class ConfigManagerConfig(object): try: if id not in nsr_dict: - nsr_obj = ConfigManagerNSR(self._log, self._loop, self, id) + nsr_obj = ConfigManagerNSR(self._log, self._loop, self, self._project, id) nsr_dict[id] = nsr_obj else: self._log.info("NSR(%s) is already initialized!", id) @@ -502,7 +518,7 @@ class ConfigManagerConfig(object): # Create Agent NSR class nsr_config = yield from cmdts_obj.get_nsr_config(id) self._log.debug("NSR {} config: {}".format(id, nsr_config)) - nsr_obj.agent_nsr = riftcm_config_plugin.RiftCMnsr(nsr, nsr_config) + nsr_obj.agent_nsr = riftcm_config_plugin.RiftCMnsr(nsr, nsr_config, self._project) try: yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.RECEIVED) @@ -519,7 +535,7 @@ class ConfigManagerConfig(object): # return nsr_obj.set_config_dir(self) - + for const_vnfr in nsr['constituent_vnfr_ref']: self._log.debug("Fetching VNFR (%s)", const_vnfr['vnfr_id']) vnfr_msg = yield from cmdts_obj.get_vnfr(const_vnfr['vnfr_id']) @@ -624,45 +640,50 @@ class ConfigManagerConfig(object): def terminate_NSR(self, id): nsr_dict = self._nsr_dict if id not in nsr_dict: - self._log.error("NSR(%s) does not exist!", id) + self._log.debug("NSR(%s) does not exist!", id) return else: - # Remove this NSR if we have it on pending task list - for task in self.pending_tasks: - if task['nsrid'] == id: - self.del_from_pending_tasks(task) + try: + # Remove this NSR if we have it on pending task list + for task in self.pending_tasks: + if task['nsrid'] == id: + self.del_from_pending_tasks(task) - # Remove this object from global list - nsr_obj = nsr_dict.pop(id, None) + # Remove this object from global list + nsr_obj = nsr_dict.pop(id, None) - # Remove this NS cm-state from global status list - self.cm_state['cm_nsr'].remove(nsr_obj.cm_nsr) + # Remove this NS cm-state from global status list + self.cm_state['cm_nsr'].remove(nsr_obj.cm_nsr) - # Also remove any scheduled configuration event - for nsr_obj_p in self._parent.pending_cfg: - if nsr_obj_p == nsr_obj: - assert id == nsr_obj_p._nsr_id - #self._parent.pending_cfg.remove(nsr_obj_p) - # Mark this as being deleted so we do not try to configure it if we are in cfg_delay (will wake up and continue to process otherwise) - nsr_obj_p.being_deleted = True - self._log.info("Removed scheduled configuration for NSR(%s)", nsr_obj.nsr_name) + # Also remove any scheduled configuration event + for nsr_obj_p in self._parent.pending_cfg: + if nsr_obj_p == nsr_obj: + assert id == nsr_obj_p._nsr_id + #self._parent.pending_cfg.remove(nsr_obj_p) + # Mark this as being deleted so we do not try to configure + # it if we are in cfg_delay (will wake up and continue to process otherwise) + nsr_obj_p.being_deleted = True + self._log.info("Removed scheduled configuration for NSR(%s)", nsr_obj.nsr_name) - self._parent.remove_nsr_obj(id) + self._parent.remove_nsr_obj(id) - # Call Config Agent to clean up for each VNF - for agent_vnfr in nsr_obj.agent_nsr.vnfrs: - yield from self._config_agent_mgr.invoke_config_agent_plugins( - 'notify_terminate_vnfr', - nsr_obj.agent_nsr, - agent_vnfr) + # Call Config Agent to clean up for each VNF + for agent_vnfr in nsr_obj.agent_nsr.vnfrs: + yield from self._config_agent_mgr.invoke_config_agent_plugins( + 'notify_terminate_vnfr', + nsr_obj.agent_nsr, + agent_vnfr) - # publish delete cm-state (cm-nsr) - yield from nsr_obj.delete_cm_nsr() + # publish delete cm-state (cm-nsr) + yield from nsr_obj.delete_cm_nsr() - #####################TBD########################### - # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_terminate_ns', self.id) + #####################TBD########################### + # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_terminate_ns', self.id) - self._log.info("NSR(%s/%s) is deleted", nsr_obj.nsr_name, id) + self._log.info("NSR(%s/%s) is deleted", nsr_obj.nsr_name, id) + + except Exception as e: + self._log.exception("Terminate NSR exception: {}".format(e)) @asyncio.coroutine def process_initial_config(self, nsr_obj, conf, script, vnfr_name=None): @@ -874,7 +895,7 @@ class ConfigManagerConfig(object): class ConfigManagerNSR(object): - def __init__(self, log, loop, parent, id): + def __init__(self, log, loop, parent, project, id): self._log = log self._loop = loop self._rwcal = None @@ -882,6 +903,7 @@ class ConfigManagerNSR(object): self._cp_dict = {} self._nsr_id = id self._parent = parent + self._project = project self._log.info("Instantiated NSR entry for id=%s", id) self.nsr_cfg_config_attributes_dict = {} self.vnf_config_attributes_dict = {} @@ -910,10 +932,9 @@ class ConfigManagerNSR(object): @property def nsr_opdata_xpath(self): ''' Returns full xpath for this NSR cm-state opdata ''' - return( - "D,/rw-conman:cm-state" + - "/rw-conman:cm-nsr[rw-conman:id='{}']" - ).format(self._nsr_id) + return self._project.add_project(( + "D,/rw-conman:cm-state/rw-conman:cm-nsr[rw-conman:id='{}']" + ).format(self._nsr_id)) @property def vnfrs(self): @@ -1369,14 +1390,16 @@ class XPaths(object): class ConfigManagerDTS(object): ''' This class either reads from DTS or publishes to DTS ''' - def __init__(self, log, loop, parent, dts): + def __init__(self, log, loop, parent, dts, project): self._log = log self._loop = loop self._parent = parent self._dts = dts + self._project = project @asyncio.coroutine - def _read_dts(self, xpath, do_trace=False): + def _read_dts(self, path, do_trace=False): + xpath = self._project.add_project(path) self._log.debug("_read_dts path = %s", xpath) flags = rwdts.XactFlag.MERGE res_iter = yield from self._dts.query_read( @@ -1463,19 +1486,21 @@ class ConfigManagerDTS(object): return cfgagentl @asyncio.coroutine - def update(self, path, msg, flags=rwdts.XactFlag.REPLACE): + def update(self, xpath, msg, flags=rwdts.XactFlag.REPLACE): """ Update a cm-state (cm-nsr) record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Updating cm-state %s:%s dts_pub_hdl = %s", path, msg, self.dts_pub_hdl) self.dts_pub_hdl.update_element(path, msg, flags) self._log.debug("Updated cm-state, %s:%s", path, msg) @asyncio.coroutine - def delete(self, path): + def delete(self, xpath): """ Delete cm-nsr record in DTS with the path only """ + path = self._project.add_project(xpath) self._log.debug("Deleting cm-nsr %s dts_pub_hdl = %s", path, self.dts_pub_hdl) self.dts_pub_hdl.delete_element(path) self._log.debug("Deleted cm-nsr, %s", path) @@ -1484,12 +1509,23 @@ class ConfigManagerDTS(object): def register(self): yield from self.register_to_publish() yield from self.register_for_nsr() - + + def deregister(self): + self._log.debug("De-registering conman config for project {}". + format(self._project.name)) + if self.dts_reg_hdl: + self.dts_reg_hdl.deregister() + self.dts_reg_hdl = None + + if self.dts_pub_hdl: + self.dts_pub_hdl.deregister() + self.dts_pub_hdl = None + @asyncio.coroutine def register_to_publish(self): ''' Register to DTS for publishing cm-state opdata ''' - xpath = "D,/rw-conman:cm-state/rw-conman:cm-nsr" + xpath = self._project.add_project("D,/rw-conman:cm-state/rw-conman:cm-nsr") self._log.debug("Registering to publish cm-state @ %s", xpath) hdl = rift.tasklets.DTS.RegistrationHandler() with self._dts.group_create() as group: @@ -1499,7 +1535,7 @@ class ConfigManagerDTS(object): @property def nsr_xpath(self): - return "D,/nsr:ns-instance-opdata/nsr:nsr" + return self._project.add_project("D,/nsr:ns-instance-opdata/nsr:nsr") @asyncio.coroutine def register_for_nsr(self): diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py index f292a689..e4f043e2 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py @@ -55,9 +55,12 @@ def log_this_vnf(vnf_cfg): else: log_vnf += "{}/".format(vnf_cfg[item]) return log_vnf - + + class ConfigManagerROifConnectionError(Exception): pass + + class ScriptError(Exception): pass @@ -68,12 +71,15 @@ class ConfigManagerEvents(object): self._log = log self._loop = loop self._parent = parent - self._nsr_xpath = "/cm-state/cm-nsr" + self._nsr_xpath = parent._project.add_project("/cm-state/cm-nsr") @asyncio.coroutine def register(self): pass + def deregister(self): + pass + @asyncio.coroutine def update_vnf_state(self, vnf_cfg, state): nsr_obj = vnf_cfg['nsr_obj'] diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py index 4e92b6c1..e260b245 100755 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py @@ -1,6 +1,6 @@ -# -# Copyright 2016 RIFT.IO Inc +# +# Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,10 @@ ''' This file - ConfigManagerTasklet() | ++ +| +ConfigManagerProject() +| +--|--> ConfigurationManager() | +--> rwconman_config.py - ConfigManagerConfig() @@ -44,6 +48,10 @@ from gi.repository import ( ) import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + ) from . import rwconman_config as Config from . import rwconman_events as Event @@ -60,10 +68,12 @@ def log_this_vnf(vnf_cfg): return log_vnf class ConfigurationManager(object): - def __init__(self, log, loop, dts): + def __init__(self, log, loop, dts, project): self._log = log self._loop = loop self._dts = dts + self._project = project + self.cfg_sleep = True self.cfg_dir = os.path.join(os.environ["RIFT_INSTALL"], "etc/conman") self._config = Config.ConfigManagerConfig(self._dts, self._log, self._loop, self) @@ -71,6 +81,7 @@ class ConfigurationManager(object): self.pending_cfg = [] self.pending_tasks = {} self._nsr_objs = {} + self._task = None # The configuration_handler task self._handlers = [ self._config, @@ -228,71 +239,74 @@ class ConfigurationManager(object): yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_FAILED) return ret_status - # Basically, this loop will never end. - while True: - # Check the pending tasks are complete - # Store a list of tasks that are completed and - # remove from the pending_tasks list outside loop - ids = [] - for nsr_id, task in self.pending_tasks.items(): - if task.done(): - ids.append(nsr_id) - e = task.exception() - if e: - self._log.error("Exception in configuring nsr {}: {}". - format(nsr_id, e)) - nsr_obj = self.get_nsr_obj(nsr_id) - if nsr_obj: - yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_FAILED, str(e)) - + try: + # Basically, this loop will never end. + while True: + # Check the pending tasks are complete + # Store a list of tasks that are completed and + # remove from the pending_tasks list outside loop + ids = [] + for nsr_id, task in self.pending_tasks.items(): + if task.done(): + ids.append(nsr_id) + e = task.exception() + if e: + self._log.error("Exception in configuring nsr {}: {}". + format(nsr_id, e)) + nsr_obj = self.get_nsr_obj(nsr_id) + if nsr_obj: + yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_FAILED, str(e)) + + else: + rc = task.result() + self._log.debug("NSR {} configured: {}".format(nsr_id, rc)) else: - rc = task.result() - self._log.debug("NSR {} configured: {}".format(nsr_id, rc)) - else: - self._log.debug("NSR {} still configuring".format(nsr_id)) - - # Remove the completed tasks - for nsr_id in ids: - self.pending_tasks.pop(nsr_id) - - # TODO (pjoseph): Fix this - # Sleep before processing any NS (Why are we getting multiple NSR running DTS updates?) - # If the sleep is not 10 seconds it does not quite work, NSM is marking it 'running' - # wrongfully 10 seconds in advance? - yield from asyncio.sleep(10, loop=self._loop) - - if self.pending_cfg: - # get first NS, pending_cfg is nsr_obj list - nsr_obj = self.pending_cfg[0] - nsr_done = False - if nsr_obj.being_deleted is False: - # Process this NS, returns back same obj is successfull or exceeded retries - try: - self._log.info("Processing NSR:{}".format(nsr_obj.nsr_name)) - - # Check if we already have a task running for this NSR - # Case where we are still configuring and terminate is called - if nsr_obj.nsr_id in self.pending_tasks: - self._log.error("NSR {} in state {} has a configure task running.". - format(nsr_obj.nsr_name, nsr_obj.get_ns_cm_state())) - # Terminate the task for this NSR - self.pending_tasks[nsr_obj.nsr_id].cancel() - - yield from self.update_ns_state(nsr_obj, conmanY.RecordState.CFG_PROCESS) - - # Call in a separate thread - self.pending_tasks[nsr_obj.nsr_id] = \ - self._loop.create_task( - process_nsr_obj(nsr_obj) - ) - - # Remove this nsr_obj - self.pending_cfg.remove(nsr_obj) - - except Exception as e: - self._log.error("Failed to process NSR as %s", str(e)) - self._log.exception(e) - + self._log.debug("NSR {} still configuring".format(nsr_id)) + + # Remove the completed tasks + for nsr_id in ids: + self.pending_tasks.pop(nsr_id) + + # TODO (pjoseph): Fix this + # Sleep before processing any NS (Why are we getting multiple NSR running DTS updates?) + # If the sleep is not 10 seconds it does not quite work, NSM is marking it 'running' + # wrongfully 10 seconds in advance? + yield from asyncio.sleep(10, loop=self._loop) + + if self.pending_cfg: + # get first NS, pending_cfg is nsr_obj list + nsr_obj = self.pending_cfg[0] + nsr_done = False + if nsr_obj.being_deleted is False: + # Process this NS, returns back same obj is successfull or exceeded retries + try: + self._log.info("Processing NSR:{}".format(nsr_obj.nsr_name)) + + # Check if we already have a task running for this NSR + # Case where we are still configuring and terminate is called + if nsr_obj.nsr_id in self.pending_tasks: + self._log.error("NSR {} in state {} has a configure task running.". + format(nsr_obj.nsr_name, nsr_obj.get_ns_cm_state())) + # Terminate the task for this NSR + self.pending_tasks[nsr_obj.nsr_id].cancel() + + yield from self.update_ns_state(nsr_obj, conmanY.RecordState.CFG_PROCESS) + + # Call in a separate thread + self.pending_tasks[nsr_obj.nsr_id] = \ + self._loop.create_task( + process_nsr_obj(nsr_obj) + ) + + # Remove this nsr_obj + self.pending_cfg.remove(nsr_obj) + + except Exception as e: + self._log.error("Failed to process NSR as %s", str(e)) + self._log.exception(e) + + except asyncio.CancelledError as e: + self._log.debug("Stopped configuration handler for project {}".format(self._project)) @asyncio.coroutine def register(self): @@ -300,7 +314,37 @@ class ConfigurationManager(object): for reg in self._handlers: yield from reg.register() - asyncio.ensure_future(self.configuration_handler(), loop=self._loop) + self._task = asyncio.ensure_future(self.configuration_handler(), loop=self._loop) + + def deregister(self): + self._log.debug("De-register conman for project {}".format(self._project.name)) + self._task.cancel() + + for reg in self._handlers: + reg.deregister() + + +class ConfigManagerProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(ConfigManagerProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self._con_man = None + + @asyncio.coroutine + def register (self): + self._log.info("Initializing the Configuration-Manager tasklet") + self._con_man = ConfigurationManager(self.log, + self.loop, + self._dts, + self,) + yield from self._con_man.register() + + def deregister(self): + self._log.debug("De-register project {}".format(self.name)) + self._con_man.deregister() + class ConfigManagerTasklet(rift.tasklets.Tasklet): def __init__(self, *args, **kwargs): @@ -308,7 +352,13 @@ class ConfigManagerTasklet(rift.tasklets.Tasklet): self.rwlog.set_category("rw-conman-log") self._dts = None - self._con_man = None + + self.project_handler = None + self.projects = {} + + @property + def dts(self): + return self._dts def start(self): super(ConfigManagerTasklet, self).start() @@ -327,11 +377,9 @@ class ConfigManagerTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): - self._log.info("Initializing the Configuration-Manager tasklet") - self._con_man = ConfigurationManager(self.log, - self.loop, - self._dts) - yield from self._con_man.register() + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, ConfigManagerProject) + self.project_handler.register() @asyncio.coroutine def run(self): diff --git a/rwcm/test/rwso_test.py b/rwcm/test/rwso_test.py index e0c5011b..7d2a4a85 100755 --- a/rwcm/test/rwso_test.py +++ b/rwcm/test/rwso_test.py @@ -240,11 +240,11 @@ class RWSOTestCase(unittest.TestCase): self.log.debug("STARTING - test_create_resource_pools") tinfo = self.new_tinfo('poolconfig') dts = rift.tasklets.DTS(tinfo, self.schema, self.loop) - pool_xpath = "C,/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" - pool_records_xpath = "D,/rw-resource-mgr:resource-pool-records" + pool_xpath = "C,/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" + pool_records_xpath = "D,/rw-project:project/rw-resource-mgr:resource-pool-records" account_xpath = "C,/rw-launchpad:cloud-account" - compute_xpath = "D,/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data[event-id='{}']" - network_xpath = "D,/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data[event-id='{}']" + compute_xpath = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data[event-id='{}']" + network_xpath = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data[event-id='{}']" @asyncio.coroutine def configure_cloud_account(): diff --git a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/engine.py b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/engine.py index d71aefcf..d43a3d64 100644 --- a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/engine.py +++ b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/engine.py @@ -106,6 +106,7 @@ class ScalingCriteria: log, dts, loop, + project, nsr_id, monp_id, scaling_criteria, @@ -143,6 +144,7 @@ class ScalingCriteria: self.log, self.dts, self.loop, + project, self.nsr_id, self.monp_id, callback=self.add_value) @@ -236,6 +238,7 @@ class ScalingPolicy(ScalingCriteria.Delegate): log, dts, loop, + project, nsr_id, nsd_id, scaling_group_name, @@ -258,6 +261,7 @@ class ScalingPolicy(ScalingCriteria.Delegate): self.loop = loop self.log = log self.dts = dts + self.project = project self.nsd_id = nsd_id self.nsr_id = nsr_id self.scaling_group_name = scaling_group_name @@ -270,6 +274,7 @@ class ScalingPolicy(ScalingCriteria.Delegate): self.log, self.dts, self.loop, + self.project, self.nsr_id, callback=self.handle_nsr_monp) @@ -311,6 +316,7 @@ class ScalingPolicy(ScalingCriteria.Delegate): self.log, self.dts, self.loop, + self.project, self.nsr_id, monp.id, cri, diff --git a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/rwautoscaler.py b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/rwautoscaler.py index affa579b..98e37e63 100644 --- a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/rwautoscaler.py +++ b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/rwautoscaler.py @@ -1,6 +1,6 @@ """ -# -# Copyright 2016 RIFT.IO Inc +# +# Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -39,91 +39,40 @@ from gi.repository import ( import rift.mano.cloud import rift.mano.dts as subscriber import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + ) +class AutoScalerProject(ManoProject, engine.ScalingPolicy.Delegate): -class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): - """The main task of this Tasklet is to listen for NSR changes and once the - NSR is configured, ScalingPolicy is created. - """ - def __init__(self, *args, **kwargs): + def __init__(self, name, tasklet, **kw): + super(AutoScalerProject, self).__init__(tasklet.log, name) + self.update(tasklet) - try: - super().__init__(*args, **kwargs) - self.store = None - self.monparam_store = None + self.store = None + self.monparam_store = None + self.nsr_sub = None + self.nsr_monp_subscribers = {} + self.instance_id_store = collections.defaultdict(list) - self.nsr_sub = None - self.nsr_monp_subscribers = {} - self.instance_id_store = collections.defaultdict(list) - - except Exception as e: - self.log.exception(e) + self.store = subscriber.SubscriberStore.from_project(self) + self.nsr_sub = subscriber.NsrCatalogSubscriber(self.log, self.dts, self.loop, + self, self.handle_nsr) - def start(self): - super().start() + def deregister(self): + self.log.debug("De-register project {}".format(self.name)) + self.nsr_sub.deregister() + self.store.deregister() - self.log.debug("Registering with dts") - - self.dts = rift.tasklets.DTS( - self.tasklet_info, - RwLaunchpadYang.get_schema(), - self.loop, - self.on_dts_state_change - ) - - self.store = subscriber.SubscriberStore.from_tasklet(self) - self.nsr_sub = subscriber.NsrCatalogSubscriber(self.log, self.dts, self.loop, self.handle_nsr) - - self.log.debug("Created DTS Api GI Object: %s", self.dts) - - def stop(self): - try: - self.dts.deinit() - except Exception as e: - self.log.exception(e) @asyncio.coroutine - def init(self): + def register (self): self.log.debug("creating vnfr subscriber") yield from self.store.register() yield from self.nsr_sub.register() - @asyncio.coroutine - def run(self): - pass - - @asyncio.coroutine - def on_dts_state_change(self, state): - """Handle DTS state change - - Take action according to current DTS state to transition application - into the corresponding application state - - Arguments - state - current dts state - - """ - switch = { - rwdts.State.INIT: rwdts.State.REGN_COMPLETE, - rwdts.State.CONFIG: rwdts.State.RUN, - } - - handlers = { - rwdts.State.INIT: self.init, - rwdts.State.RUN: self.run, - } - - # Transition application to next state - handler = handlers.get(state, None) - if handler is not None: - yield from handler() - - # Transition dts to next state - next_state = switch.get(state, None) - if next_state is not None: - self.dts.handle.set_state(next_state) - def scale_in(self, scaling_group_name, nsr_id): """Delegate callback @@ -142,6 +91,7 @@ class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): # Trigger an rpc rpc_ip = NsrYang.YangInput_Nsr_ExecScaleIn.from_dict({ + 'project_name': self.name, 'nsr_id_ref': nsr_id, 'instance_id': instance_id, 'scaling_group_name_ref': scaling_group_name}) @@ -168,6 +118,7 @@ class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): def _scale_out(): # Trigger an rpc rpc_ip = NsrYang.YangInput_Nsr_ExecScaleOut.from_dict({ + 'project_name': self.name, 'nsr_id_ref': nsr_id , 'scaling_group_name_ref': scaling_group_name}) @@ -191,7 +142,7 @@ class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): NS that moves to config state. Args: - nsr (RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr): Ns Opdata + nsr (RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr): Ns Opdata action (rwdts.QueryAction): Action type of the change. """ def nsr_create(): @@ -204,7 +155,7 @@ class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): for scaling_group in nsd.scaling_group_descriptor: for policy_cfg in scaling_group.scaling_policy: policy = engine.ScalingPolicy( - self.log, self.dts, self.loop, + self.log, self.dts, self.loop, self, nsr.ns_instance_config_ref, nsr.nsd_ref, scaling_group.name, @@ -228,3 +179,81 @@ class AutoScalerTasklet(rift.tasklets.Tasklet, engine.ScalingPolicy.Delegate): nsr_create() elif action == rwdts.QueryAction.DELETE: nsr_delete() + + +class AutoScalerTasklet(rift.tasklets.Tasklet): + """The main task of this Tasklet is to listen for NSR changes and once the + NSR is configured, ScalingPolicy is created. + """ + def __init__(self, *args, **kwargs): + + try: + super().__init__(*args, **kwargs) + + self._project_handler = None + self.projects = {} + + except Exception as e: + self.log.exception(e) + + def start(self): + super().start() + + self.log.debug("Registering with dts") + + self.dts = rift.tasklets.DTS( + self.tasklet_info, + RwLaunchpadYang.get_schema(), + self.loop, + self.on_dts_state_change + ) + + self.log.debug("Created DTS Api GI Object: %s", self.dts) + + def stop(self): + try: + self.dts.deinit() + except Exception as e: + self.log.exception(e) + + @asyncio.coroutine + def init(self): + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, AutoScalerProject) + self.project_handler.register() + + @asyncio.coroutine + def run(self): + pass + + @asyncio.coroutine + def on_dts_state_change(self, state): + """Handle DTS state change + + Take action according to current DTS state to transition application + into the corresponding application state + + Arguments + state - current dts state + + """ + switch = { + rwdts.State.INIT: rwdts.State.REGN_COMPLETE, + rwdts.State.CONFIG: rwdts.State.RUN, + } + + handlers = { + rwdts.State.INIT: self.init, + rwdts.State.RUN: self.run, + } + + # Transition application to next state + handler = handlers.get(state, None) + if handler is not None: + yield from handler() + + # Transition dts to next state + next_state = switch.get(state, None) + if next_state is not None: + self.dts.handle.set_state(next_state) + diff --git a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/subscribers.py b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/subscribers.py index 04185b68..f96a832b 100644 --- a/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/subscribers.py +++ b/rwlaunchpad/plugins/rwautoscaler/rift/tasklets/rwautoscaler/subscribers.py @@ -1,6 +1,6 @@ -# -# Copyright 2016 RIFT.IO Inc +# +# Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,21 +20,18 @@ import rift.mano.dts as mano_dts class NsrMonParamSubscriber(mano_dts.AbstractOpdataSubscriber): """Registers for NSR monitoring parameter changes. - + Attributes: monp_id (str): Monitoring Param ID nsr_id (str): NSR ID """ - def __init__(self, log, dts, loop, nsr_id, monp_id=None, callback=None): - super().__init__(log, dts, loop, callback) + def __init__(self, log, dts, loop, project, nsr_id, monp_id=None, callback=None): + super().__init__(log, dts, loop, project, callback) self.nsr_id = nsr_id self.monp_id = monp_id def get_xpath(self): - return ("D,/nsr:ns-instance-opdata/nsr:nsr" + + return self.project.add_project(("D,/nsr:ns-instance-opdata/nsr:nsr" + "[nsr:ns-instance-config-ref='{}']".format(self.nsr_id) + "/nsr:monitoring-param" + - ("[nsr:id='{}']".format(self.monp_id) if self.monp_id else "")) - - - + ("[nsr:id='{}']".format(self.monp_id) if self.monp_id else ""))) diff --git a/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py b/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py index c00ca11f..edc7a9e4 100644 --- a/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py +++ b/rwlaunchpad/plugins/rwautoscaler/test/utest_autoscaler_dts.py @@ -42,8 +42,8 @@ from gi.repository import ( ) -ScalingCriteria = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria -ScalingPolicy = NsdYang.YangData_Nsd_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy +ScalingCriteria = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy_ScalingCriteria +ScalingPolicy = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_ScalingGroupDescriptor_ScalingPolicy class MockDelegate(engine.ScalingCriteria.Delegate): @@ -73,7 +73,7 @@ class MockStore(): def __call__(self): store = mock.MagicMock() - mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict({ + mock_vnfd = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd.from_dict({ 'id': "1", 'monitoring_param': [ {'description': 'no of ping requests', @@ -98,12 +98,12 @@ class MockStore(): store.get_vnfd = mock.MagicMock(return_value=mock_vnfd) - mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({'id': '1'}) - mock_vnfr.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict({'id': '1'}) + mock_vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.from_dict({'id': '1'}) + mock_vnfr.vnfd = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vnfd.from_dict({'id': '1'}) store.get_vnfr = mock.MagicMock(return_value=mock_vnfr) - mock_nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.from_dict({ + mock_nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr.from_dict({ 'ns_instance_config_ref': "1", 'name_ref': "Foo", 'nsd_ref': '1', @@ -138,7 +138,7 @@ class MockStore(): scale_in_val = 100 scale_out_val = 200 - mock_nsd = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict({ + mock_nsd = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd.from_dict({ 'id': '1', 'monitoring_param': (monp_cfg if not self.legacy else []), 'constituent_vnfd': [{'member_vnf_index': 1, @@ -206,13 +206,13 @@ class AutoscalarDtsTestCase(rift.test.dts.AbstractDTSTest): def _populate_mock_values(self, criterias, nsr_id, floor, ceil): # Mock publish # Verify Scale in AND operator - NsMonParam = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam + NsMonParam = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_MonitoringParam publisher = rift.test.dts.DescriptorPublisher(self.log, self.dts, self.loop) for criteria in criterias: monp_id = criteria.ns_monitoring_param_ref - w_xpath = "D,/nsr:ns-instance-opdata/nsr:nsr" + w_xpath = "D,/rw-project:project/nsr:ns-instance-opdata/nsr:nsr" w_xpath = w_xpath + "[nsr:ns-instance-config-ref='{}']/nsr:monitoring-param".format(nsr_id) xpath = w_xpath + "[nsr:id ='{}']".format(monp_id) diff --git a/rwlaunchpad/plugins/rwimagemgr/rift/imagemgr/client.py b/rwlaunchpad/plugins/rwimagemgr/rift/imagemgr/client.py index 10df45b9..42185ae2 100644 --- a/rwlaunchpad/plugins/rwimagemgr/rift/imagemgr/client.py +++ b/rwlaunchpad/plugins/rwimagemgr/rift/imagemgr/client.py @@ -48,7 +48,7 @@ class UploadJobClient(object): self._loop = loop self._dts = dts - def create_job(self, image_name, image_checksum, cloud_account_names=None): + def create_job(self, image_name, image_checksum, project, cloud_account_names=None): """ Create an image upload_job and return an UploadJob instance Arguments: @@ -62,6 +62,7 @@ class UploadJobClient(object): """ create_job_msg = RwImageMgmtYang.CreateUploadJob.from_dict({ "onboarded_image": { + "project_name": project.name, "image_name": image_name, "image_checksum": image_checksum, } @@ -81,9 +82,9 @@ class UploadJobClient(object): job_id = rpc_result.job_id - return UploadJob(self._log, self._loop, self._dts, job_id) + return UploadJob(self._log, self._loop, self._dts, job_id, project) - def create_job_threadsafe(self, image_name, image_checksum, cloud_account_names=None): + def create_job_threadsafe(self, image_name, image_checksum, project, cloud_account_names=None): """ A thread-safe, syncronous wrapper for create_job """ future = concurrent.futures.Future() @@ -96,7 +97,7 @@ class UploadJobClient(object): def add_task(): task = self._loop.create_task( - self.create_job(image_name, image_checksum, cloud_account_names) + self.create_job(image_name, image_checksum, project, cloud_account_names) ) task.add_done_callback(on_done) @@ -106,11 +107,12 @@ class UploadJobClient(object): class UploadJob(object): """ A handle for a image upload job """ - def __init__(self, log, loop, dts, job_id): + def __init__(self, log, loop, dts, job_id, project): self._log = log self._loop = loop self._dts = dts self._job_id = job_id + self._project = project @asyncio.coroutine def wait_until_complete(self): @@ -122,12 +124,11 @@ class UploadJob(object): UploadJobCancelled: The upload job was cancelled """ self._log.debug("waiting for upload job %s to complete", self._job_id) + xpath = self._project.add_project("D,/rw-image-mgmt:upload-jobs/" + + "rw-image-mgmt:job[rw-image-mgmt:id='{}']". + format(self._job_id)) while True: - query_iter = yield from self._dts.query_read( - "D,/rw-image-mgmt:upload-jobs/rw-image-mgmt:job[rw-image-mgmt:id='{}']".format( - self._job_id - ) - ) + query_iter = yield from self._dts.query_read(xpath) job_status_msg = None for fut_resp in query_iter: job_status_msg = (yield from fut_resp).result diff --git a/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/tasklet.py b/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/tasklet.py index 027e5821..f3ba2ed8 100644 --- a/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/tasklet.py +++ b/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/tasklet.py @@ -22,6 +22,13 @@ import time import rift.tasklets import rift.mano.cloud +from rift.mano.utils.project import ( + ManoProject, + ProjectConfigCallbacks, + ProjectHandler, + get_add_delete_update_cfgs, + DEFAULT_PROJECT, + ) from . import glance_proxy_server from . import glance_client @@ -53,16 +60,18 @@ class ImageNotFoundError(ImageRequestError): class CloudAccountDtsHandler(object): - def __init__(self, log, dts, log_hdl): + def __init__(self, log, dts, log_hdl, project): self._dts = dts self._log = log self._log_hdl = log_hdl self._cloud_cfg_subscriber = None + self._project = project def register(self, on_add_apply, on_delete_apply): - self._log.debug("creating cloud account config handler") + self._log.debug("Project {}: creating cloud account config handler". + format(self._project.name)) self._cloud_cfg_subscriber = rift.mano.cloud.CloudAccountConfigSubscriber( - self._dts, self._log, self._log_hdl, + self._dts, self._log, self._log_hdl, self._project, rift.mano.cloud.CloudAccountConfigCallbacks( on_add_apply=on_add_apply, on_delete_apply=on_delete_apply, @@ -70,6 +79,11 @@ class CloudAccountDtsHandler(object): ) self._cloud_cfg_subscriber.register() + def deregister(self): + self._log.debug("Project {}: Removing cloud account config handler". + format(self._project.name)) + self._cloud_cfg_subscriber.deregister() + def openstack_image_to_image_info(openstack_image): """Convert the OpenstackImage to a ImageInfo protobuf message @@ -95,19 +109,21 @@ def openstack_image_to_image_info(openstack_image): class ImageDTSShowHandler(object): """ A DTS publisher for the upload-jobs data container """ - def __init__(self, log, loop, dts, job_controller): + def __init__(self, log, loop, dts, job_controller, project): self._log = log self._loop = loop self._dts = dts self._job_controller = job_controller + self._project = project self._subscriber = None + def get_xpath(self): + return self._project.add_project("D,/rw-image-mgmt:upload-jobs") + @asyncio.coroutine def register(self): """ Register as a publisher and wait for reg_ready to complete """ - def get_xpath(): - return "D,/rw-image-mgmt:upload-jobs" @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): @@ -119,7 +135,7 @@ class ImageDTSShowHandler(object): xact_info.respond_xpath( rwdts.XactRspCode.ACK, - xpath=get_xpath(), + xpath=self.get_xpath(), msg=jobs_pb_msg, ) @@ -130,7 +146,7 @@ class ImageDTSShowHandler(object): reg_event.set() self._subscriber = yield from self._dts.register( - xpath=get_xpath(), + xpath=self.get_xpath(), handler=rift.tasklets.DTS.RegistrationHandler( on_prepare=on_prepare, on_ready=on_ready, @@ -141,9 +157,18 @@ class ImageDTSShowHandler(object): yield from reg_event.wait() + def deregister(self): + self._log.debug("Project {}: De-register show image handler". + format(self._project.name)) + if self._subscriber: + self._subscriber.delete_element(self.get_xpath()) + self._subscriber.deregister() + self._subscriber = None + class ImageDTSRPCHandler(object): """ A DTS publisher for the upload-job RPC's """ - def __init__(self, log, loop, dts, accounts, glance_client, upload_task_creator, job_controller): + def __init__(self, log, loop, dts, accounts, glance_client, + upload_task_creator, job_controller, project): self._log = log self._loop = loop self._dts = dts @@ -151,8 +176,10 @@ class ImageDTSRPCHandler(object): self._glance_client = glance_client self._upload_task_creator = upload_task_creator self._job_controller = job_controller + self._project = project - self._subscriber = None + self._create = None + self._cancel = None @asyncio.coroutine def _register_create_upload_job(self): @@ -164,6 +191,10 @@ class ImageDTSRPCHandler(object): create_msg = msg account_names = create_msg.cloud_account + + if not self._project.rpc_check(msg, xact_info): + return + # If cloud accounts were not specified, upload image to all cloud account if not account_names: account_names = list(self._accounts.keys()) @@ -217,14 +248,14 @@ class ImageDTSRPCHandler(object): def on_ready(_, status): reg_event.set() - self._subscriber = yield from self._dts.register( - xpath="I," + get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare, - on_ready=on_ready, - ), - flags=rwdts.Flag.PUBLISHER, - ) + self._create = yield from self._dts.register( + xpath="I," + get_xpath(), + handler=rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare, + on_ready=on_ready, + ), + flags=rwdts.Flag.PUBLISHER, + ) yield from reg_event.wait() @@ -235,6 +266,9 @@ class ImageDTSRPCHandler(object): @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): + if not self._project.rpc_check(msg, xact_info): + return + if not msg.has_field("job_id"): self._log.error("cancel-upload-job missing job-id field.") xact_info.respond_xpath(rwdts.XactRspCode.NACK) @@ -256,14 +290,14 @@ class ImageDTSRPCHandler(object): def on_ready(_, status): reg_event.set() - self._subscriber = yield from self._dts.register( - xpath="I," + get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare, - on_ready=on_ready, - ), - flags=rwdts.Flag.PUBLISHER, - ) + self._cancel = yield from self._dts.register( + xpath="I," + get_xpath(), + handler=rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare, + on_ready=on_ready, + ), + flags=rwdts.Flag.PUBLISHER, + ) yield from reg_event.wait() @@ -273,16 +307,28 @@ class ImageDTSRPCHandler(object): yield from self._register_create_upload_job() yield from self._register_cancel_upload_job() + def deregister(self): + self._log.debug("Project {}: Deregister image rpc handlers". + format(self._project.name)) + if self._create: + self._create.deregister() + self._create = None + + if self._cancel: + self._cancel.deregister() + self._cancel = None + class GlanceClientUploadTaskCreator(object): """ This class creates upload tasks using configured cloud accounts and configured image catalog glance client """ - def __init__(self, log, loop, accounts, glance_client): + def __init__(self, log, loop, accounts, glance_client, project): self._log = log self._loop = loop self._accounts = accounts self._glance_client = glance_client + self._project = project @asyncio.coroutine def create_tasks(self, account_names, image_id=None, image_name=None, image_checksum=None): @@ -397,6 +443,78 @@ class GlanceClientUploadTaskCreator(object): create_msg.image_checksum if "image_checksum" in create_msg else None) ) +class ImageMgrProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(ImageMgrProject, self).__init__(tasklet.log, name) + self.update(tasklet) + try: + self.glance_client = kw['client'] + except KeyError as e: + self._log.exception("kw {}: {}".format(kw, e)) + + self.cloud_cfg_subscriber = None + self.job_controller = None + self.task_creator = None + self.rpc_handler = None + self.show_handler = None + + self.cloud_accounts = {} + + @asyncio.coroutine + def register(self): + try: + self.log.debug("creating cloud account handler") + self.cloud_cfg_subscriber = CloudAccountDtsHandler(self._log, + self._dts, + self._log_hdl, + self) + self.cloud_cfg_subscriber.register( + self.on_cloud_account_create, + self.on_cloud_account_delete + ) + + self.job_controller = upload.ImageUploadJobController( + self.log, self.loop, self + ) + + self.task_creator = GlanceClientUploadTaskCreator( + self.log, self.loop, self.cloud_accounts, + self.glance_client, self + ) + + self.rpc_handler = ImageDTSRPCHandler( + self.log, self.loop, self.dts, self.cloud_accounts, + self.glance_client, self.task_creator, + self.job_controller, self + ) + yield from self.rpc_handler.register() + + self.show_handler = ImageDTSShowHandler( + self.log, self.loop, self.dts, self.job_controller, self + ) + yield from self.show_handler.register() + except Exception as e: + self.log.exception("Error during project {} register: e". + format(self.name, e)) + + def deregister(self): + self.log.debug("De-register handlers for project: {}".format(self.name)) + self.rpc_handler.deregister() + self.show_handler.deregister() + self.cloud_cfg_subscriber.deregister() + + def on_cloud_account_create(self, account): + self.log.debug("adding cloud account: %s", account.name) + self.cloud_accounts[account.name] = account + + def on_cloud_account_delete(self, account_name): + self.log.debug("deleting cloud account: %s", account_name) + if account_name not in self.cloud_accounts: + self.log.warning("cloud account not found: %s", account_name) + else: + del self.cloud_accounts[account_name] + class ImageManagerTasklet(rift.tasklets.Tasklet): """ @@ -409,16 +527,13 @@ class ImageManagerTasklet(rift.tasklets.Tasklet): super().__init__(*args, **kwargs) self.rwlog.set_category("rw-mano-log") - self.cloud_cfg_subscriber = None self.http_proxy = None self.proxy_server = None self.dts = None - self.job_controller = None - self.cloud_accounts = {} self.glance_client = None - self.task_creator = None - self.rpc_handler = None - self.show_handler = None + self.project_handler = None + + self.projects = {} def start(self): super().start() @@ -443,13 +558,6 @@ class ImageManagerTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): try: - self.log.debug("creating cloud account handler") - self.cloud_cfg_subscriber = CloudAccountDtsHandler(self.log, self.dts, self.log_hdl) - self.cloud_cfg_subscriber.register( - self.on_cloud_account_create, - self.on_cloud_account_delete - ) - self.log.debug("creating http proxy server") self.http_proxy = glance_proxy_server.QuickProxyServer(self.log, self.loop) @@ -459,43 +567,18 @@ class ImageManagerTasklet(rift.tasklets.Tasklet): ) self.proxy_server.start() - self.job_controller = upload.ImageUploadJobController( - self.log, self.loop - ) - self.glance_client = glance_client.OpenstackGlanceClient.from_token( self.log, "127.0.0.1", "9292", "test" ) - self.task_creator = GlanceClientUploadTaskCreator( - self.log, self.loop, self.cloud_accounts, self.glance_client - ) - - self.rpc_handler = ImageDTSRPCHandler( - self.log, self.loop, self.dts, self.cloud_accounts, self.glance_client, self.task_creator, - self.job_controller - ) - yield from self.rpc_handler.register() - - self.show_handler = ImageDTSShowHandler( - self.log, self.loop, self.dts, self.job_controller - ) - yield from self.show_handler.register() + self.log.debug("Creating project handler") + self.project_handler = ProjectHandler(self, ImageMgrProject, + client=self.glance_client) + self.project_handler.register() except Exception as e: self.log.exception("error during init") - def on_cloud_account_create(self, account): - self.log.debug("adding cloud account: %s", account.name) - self.cloud_accounts[account.name] = account - - def on_cloud_account_delete(self, account_name): - self.log.debug("deleting cloud account: %s", account_name) - if account_name not in self.cloud_accounts: - self.log.warning("cloud account not found: %s", account_name) - - del self.cloud_accounts[account_name] - @asyncio.coroutine def run(self): pass diff --git a/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/upload.py b/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/upload.py index 7ce74b22..3f1fb821 100644 --- a/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/upload.py +++ b/rwlaunchpad/plugins/rwimagemgr/rift/tasklets/rwimagemgr/upload.py @@ -51,9 +51,10 @@ class ImageUploadJobController(object): """ This class starts and manages ImageUploadJobs """ MAX_COMPLETED_JOBS = 20 - def __init__(self, log, loop, max_completed_jobs=MAX_COMPLETED_JOBS): + def __init__(self, log, loop, project, max_completed_jobs=MAX_COMPLETED_JOBS): self._log = log self._loop = loop + self._project = project self._job_id_gen = itertools.count(1) self._max_completed_jobs = max_completed_jobs diff --git a/rwlaunchpad/plugins/rwimagemgr/test/utest_dts_handlers.py b/rwlaunchpad/plugins/rwimagemgr/test/utest_dts_handlers.py index 7ba4f76a..88205bda 100755 --- a/rwlaunchpad/plugins/rwimagemgr/test/utest_dts_handlers.py +++ b/rwlaunchpad/plugins/rwimagemgr/test/utest_dts_handlers.py @@ -138,7 +138,7 @@ class RwImageRPCTestCase(rift.test.dts.AbstractDTSTest): ) query_iter = yield from self.dts_c.query_read( - "D,/rw-image-mgmt:upload-jobs", + "D,/rw-project:project/rw-image-mgmt:upload-jobs", ) for fut_resp in query_iter: diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/convert.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/convert.py index 7571c570..2e8e3b23 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/convert.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/convert.py @@ -16,8 +16,10 @@ # import json +import logging import os import tempfile +import yaml import gi gi.require_version('RwNsdYang', '1.0') @@ -31,6 +33,8 @@ from gi.repository import ( RwYang, ) +from rift.mano.utils.project import NS_PROJECT + class UnknownExtensionError(Exception): pass @@ -97,7 +101,7 @@ class ProtoMessageSerializer(object): @property def yang_class(self): - """ The Protobuf's GI class (e.g. RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd) """ + """ The Protobuf's GI class (e.g. RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd) """ return self._yang_pb_cls @property @@ -122,9 +126,16 @@ class ProtoMessageSerializer(object): return self.yang_class.from_json(self.model, decode(json), strict=False) def _from_yaml_file_hdl(self, file_hdl): - yaml = file_hdl.read() + yml = file_hdl.read() - return self.yang_class.from_yaml(self.model, decode(yaml), strict=False) + # Need to prefix project on to the descriptor and then + # convert to yang pb + # TODO: See if there is a better way to do this + desc = {NS_PROJECT: []} + desc[NS_PROJECT].append(yaml.load(decode(yml))) + # log = logging.getLogger('rw-mano-log') + # log.error("Desc from yaml: {}".format(desc)) + return self.yang_class.from_yaml(self.model, yaml.dump(desc), strict=False) def to_json_string(self, pb_msg): """ Serialize a protobuf message into JSON @@ -145,10 +156,15 @@ class ProtoMessageSerializer(object): try: json_str = pb_msg.to_json(self.model) + # Remove rw-project:project top level element + dic = json.loads(json_str) + jstr = json.dumps(dic[NS_PROJECT][0]) except Exception as e: raise SerializationError(e) - return json_str + log = logging.getLogger('rw-mano-log') + log.error("Desc to json: {}".format(jstr)) + return jstr def to_yaml_string(self, pb_msg): """ Serialize a protobuf message into YAML @@ -262,22 +278,22 @@ class ProtoMessageSerializer(object): class VnfdSerializer(ProtoMessageSerializer): """ Creates a serializer for the VNFD descriptor""" def __init__(self): - super().__init__(VnfdYang, VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd) + super().__init__(VnfdYang, VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd) class NsdSerializer(ProtoMessageSerializer): """ Creates a serializer for the NSD descriptor""" def __init__(self): - super().__init__(NsdYang, NsdYang.YangData_Nsd_NsdCatalog_Nsd) + super().__init__(NsdYang, NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd) class RwVnfdSerializer(ProtoMessageSerializer): """ Creates a serializer for the VNFD descriptor""" def __init__(self): - super().__init__(RwVnfdYang, RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd) + super().__init__(RwVnfdYang, RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd) class RwNsdSerializer(ProtoMessageSerializer): """ Creates a serializer for the NSD descriptor""" def __init__(self): - super().__init__(RwNsdYang, RwNsdYang.YangData_Nsd_NsdCatalog_Nsd) + super().__init__(RwNsdYang, RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd) diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/store.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/store.py index 34fff1cd..62b517ae 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/store.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/package/store.py @@ -52,9 +52,9 @@ class PackageFilesystemStore(object): @property def root_dir(self): return self._root_dir - def _get_package_dir(self, package_id): + self._log.debug("Package dir {}, {}".format(self._root_dir, package_id)) return os.path.join(self._root_dir, package_id) def _get_package_files(self, package_id): diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/datacenters.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/datacenters.py index 05731a61..a481a11b 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/datacenters.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/datacenters.py @@ -35,19 +35,20 @@ class DataCenterPublisher(mano_dts.DtsHandler): XPATH = "D,/rw-launchpad:datacenters" - def __init__(self, log, dts, loop): + def __init__(self, log, dts, loop, project): """Creates an instance of a DataCenterPublisher Arguments: tasklet - the tasklet that this publisher is registered for """ - super().__init__(log, dts, loop) + super().__init__(log, dts, loop, project) self._ro_sub = mano_dts.ROAccountConfigSubscriber( self.log, self.dts, self.loop, + self.project, callback=self.on_ro_account_change ) self.ro_accounts = {} @@ -58,6 +59,15 @@ class DataCenterPublisher(mano_dts.DtsHandler): elif action == RwDts.QueryAction.DELETE and ro_account.name in self.ro_accounts: del self.ro_accounts[ro_account.name] + def deregister(self): + self._log.debug("De-register datacenter handler for project {}". + format(self.project.name)) + if self.reg: + self.reg.deregister() + self.reg = None + + self._ro_sub.deregister() + @asyncio.coroutine def register(self): """Registers the publisher with DTS""" @@ -105,7 +115,7 @@ class DataCenterPublisher(mano_dts.DtsHandler): xact_info.respond_xpath( RwDts.XactRspCode.MORE, - 'D,/rw-launchpad:datacenters', + self.project.add_project(DataCenterPublisher.XPATH), datacenters, ) @@ -119,7 +129,7 @@ class DataCenterPublisher(mano_dts.DtsHandler): with self.dts.group_create() as group: self.reg = group.register( - xpath=DataCenterPublisher.XPATH, + xpath=self.project.add_project(DataCenterPublisher.XPATH), handler=handler, flags=RwDts.Flag.PUBLISHER, ) diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py index 7fa61301..1e017b20 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py @@ -195,21 +195,18 @@ class DescriptorPackageArchiveExporter(object): class ExportRpcHandler(mano_dts.AbstractRpcHandler): - def __init__(self, log, dts, loop, application, store_map, exporter, catalog_map): + def __init__(self, application, catalog_map): """ Args: application: UploaderApplication - store_map: dict containing VnfdStore & NsdStore - exporter : DescriptorPackageArchiveExporter calalog_map: Dict containing Vnfds and Nsd onboarding. """ - super().__init__(log, dts, loop) + super().__init__(application.log, application.dts, application.loop) self.application = application - self.store_map = store_map - self.exporter = exporter + self.store_map = application.package_store_map + self.exporter = application.exporter self.catalog_map = catalog_map - self.log = log @property def xpath(self): @@ -240,7 +237,7 @@ class ExportRpcHandler(mano_dts.AbstractRpcHandler): # Parse the IDs desc_id = msg.package_id - catalog = self.catalog_map[desc_type] + catalog = self.catalog_map[desc_type](project=msg.project_name) if desc_id not in catalog: raise ValueError("Unable to find package ID: {}".format(desc_id)) diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/image.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/image.py index b18e304d..a025b374 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/image.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/image.py @@ -65,9 +65,9 @@ class ImageUploader(object): except Exception as e: raise ImageUploadError("Failed to upload image to catalog: %s" % str(e)) from e - def upload_image_to_cloud_accounts(self, image_name, image_checksum, cloud_accounts=None): + def upload_image_to_cloud_accounts(self, image_name, image_checksum, project, cloud_accounts=None): self._log.debug("uploading image %s to all cloud accounts", image_name) - upload_job = self._client.create_job_threadsafe(image_name, image_checksum, cloud_accounts) + upload_job = self._client.create_job_threadsafe(image_name, image_checksum, project, cloud_accounts) try: upload_job.wait_until_complete_threadsafe() except client.UploadJobError as e: diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/onboard.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/onboard.py index b12e192f..db7737fa 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/onboard.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/onboard.py @@ -17,6 +17,7 @@ import requests +from rift.mano.utils.project import DEFAULT_PROJECT from rift.package import convert from gi.repository import ( NsdYang, @@ -37,17 +38,17 @@ class UpdateError(Exception): class DescriptorOnboarder(object): """ This class is responsible for onboarding descriptors using Restconf""" DESC_ENDPOINT_MAP = { - NsdYang.YangData_Nsd_NsdCatalog_Nsd: "nsd-catalog/nsd", - RwNsdYang.YangData_Nsd_NsdCatalog_Nsd: "nsd-catalog/nsd", - VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd: "vnfd-catalog/vnfd", - RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd: "vnfd-catalog/vnfd", + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd: "nsd-catalog/nsd", + RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd: "nsd-catalog/nsd", + VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd: "vnfd-catalog/vnfd", + RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd: "vnfd-catalog/vnfd", } DESC_SERIALIZER_MAP = { - NsdYang.YangData_Nsd_NsdCatalog_Nsd: convert.NsdSerializer(), - RwNsdYang.YangData_Nsd_NsdCatalog_Nsd: convert.RwNsdSerializer(), - VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd: convert.VnfdSerializer(), - RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd: convert.RwVnfdSerializer(), + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd: convert.NsdSerializer(), + RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd: convert.RwNsdSerializer(), + VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd: convert.VnfdSerializer(), + RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd: convert.RwVnfdSerializer(), } HEADERS = {"content-type": "application/vnd.yang.data+json"} @@ -72,28 +73,32 @@ class DescriptorOnboarder(object): return headers - def _get_url(self, descriptor_msg): + def _get_url(self, descriptor_msg, project=None): if type(descriptor_msg) not in DescriptorOnboarder.DESC_SERIALIZER_MAP: raise TypeError("Invalid descriptor message type") + if project is None: + project = DEFAULT_PROJECT + endpoint = DescriptorOnboarder.DESC_ENDPOINT_MAP[type(descriptor_msg)] + ep = "project/{}/{}".format(project, endpoint) url = "{}://{}:{}/api/config/{}".format( "https" if self._use_ssl else "http", self._host, self.port, - endpoint, + ep, ) return url - def _make_request_args(self, descriptor_msg, auth=None): + def _make_request_args(self, descriptor_msg, auth=None, project=None): if type(descriptor_msg) not in DescriptorOnboarder.DESC_SERIALIZER_MAP: raise TypeError("Invalid descriptor message type") serializer = DescriptorOnboarder.DESC_SERIALIZER_MAP[type(descriptor_msg)] json_data = serializer.to_json_string(descriptor_msg) - url = self._get_url(descriptor_msg) + url = self._get_url(descriptor_msg, project=project) request_args = dict( url=url, @@ -134,7 +139,7 @@ class DescriptorOnboarder(object): self._log.error(msg) raise UpdateError(msg) from e - def onboard(self, descriptor_msg, auth=None): + def onboard(self, descriptor_msg, auth=None, project=None): """ Onboard the descriptor config Arguments: @@ -145,20 +150,23 @@ class DescriptorOnboarder(object): OnboardError - The descriptor config update failed """ - request_args = self._make_request_args(descriptor_msg, auth) + request_args = self._make_request_args(descriptor_msg, auth, project) try: response = requests.post(**request_args) response.raise_for_status() except requests.exceptions.ConnectionError as e: msg = "Could not connect to restconf endpoint: %s" % str(e) self._log.error(msg) + self._log.exception(msg) raise OnboardError(msg) from e except requests.exceptions.HTTPError as e: msg = "POST request to %s error: %s" % (request_args["url"], response.text) self._log.error(msg) + self._log.exception(msg) raise OnboardError(msg) from e except requests.exceptions.Timeout as e: msg = "Timed out connecting to restconf endpoint: %s", str(e) self._log.error(msg) + self._log.exception(msg) raise OnboardError(msg) from e diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tasklet.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tasklet.py index 0eff6160..76dc781c 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tasklet.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tasklet.py @@ -40,6 +40,12 @@ from gi.repository import ( import rift.tasklets import rift.mano.cloud import rift.mano.config_agent +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + get_add_delete_update_cfgs, + DEFAULT_PROJECT, + ) from rift.package import store from . import uploader @@ -53,45 +59,25 @@ MAX_BUFFER_SIZE = 1 * MB # Max. size loaded into memory! MAX_BODY_SIZE = 1 * MB # Max. size loaded into memory! -def get_add_delete_update_cfgs(dts_member_reg, xact, key_name): - # Unforunately, it is currently difficult to figure out what has exactly - # changed in this xact without Pbdelta support (RIFT-4916) - # As a workaround, we can fetch the pre and post xact elements and - # perform a comparison to figure out adds/deletes/updates - xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) - curr_cfgs = list(dts_member_reg.elements) - - xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} - curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} - - # Find Adds - added_keys = set(xact_key_map) - set(curr_key_map) - added_cfgs = [xact_key_map[key] for key in added_keys] - - # Find Deletes - deleted_keys = set(curr_key_map) - set(xact_key_map) - deleted_cfgs = [curr_key_map[key] for key in deleted_keys] - - # Find Updates - updated_keys = set(curr_key_map) & set(xact_key_map) - updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]] - - return added_cfgs, deleted_cfgs, updated_cfgs +class LaunchpadError(Exception): + pass +class LpProjectNotFound(Exception): + pass class CatalogDtsHandler(object): - def __init__(self, tasklet, app): + def __init__(self, project, app): self.app = app self.reg = None - self.tasklet = tasklet + self.project = project @property def log(self): - return self.tasklet.log + return self.project.log @property def dts(self): - return self.tasklet.dts + return self.project.dts class NsdCatalogDtsHandler(CatalogDtsHandler): @@ -99,27 +85,27 @@ class NsdCatalogDtsHandler(CatalogDtsHandler): def add_nsd(self, nsd): self.log.debug('nsd-catalog-handler:add:{}'.format(nsd.id)) - if nsd.id not in self.tasklet.nsd_catalog: - self.tasklet.nsd_catalog[nsd.id] = nsd + if nsd.id not in self.project.nsd_catalog: + self.project.nsd_catalog[nsd.id] = nsd else: self.log.error("nsd already in catalog: {}".format(nsd.id)) def update_nsd(self, nsd): self.log.debug('nsd-catalog-handler:update:{}'.format(nsd.id)) - if nsd.id in self.tasklet.nsd_catalog: - self.tasklet.nsd_catalog[nsd.id] = nsd + if nsd.id in self.project.nsd_catalog: + self.project.nsd_catalog[nsd.id] = nsd else: self.log.error("unrecognized NSD: {}".format(nsd.id)) def delete_nsd(self, nsd_id): self.log.debug('nsd-catalog-handler:delete:{}'.format(nsd_id)) - if nsd_id in self.tasklet.nsd_catalog: - del self.tasklet.nsd_catalog[nsd_id] + if nsd_id in self.project.nsd_catalog: + del self.project.nsd_catalog[nsd_id] else: self.log.error("unrecognized NSD: {}".format(nsd_id)) try: - self.tasklet.nsd_package_store.delete_package(nsd_id) + self.project.tasklet.nsd_package_store.delete_package(nsd_id) except store.PackageStoreError as e: self.log.warning("could not delete package from store: %s", str(e)) @@ -151,47 +137,54 @@ class NsdCatalogDtsHandler(CatalogDtsHandler): for cfg in update_cfgs: self.update_nsd(cfg) - self.log.debug("Registering for NSD catalog") + self.log.debug("Registering for NSD catalog in project". + format(self.project.name)) acg_handler = rift.tasklets.AppConfGroup.Handler( on_apply=apply_config, ) with self.dts.appconf_group_create(acg_handler) as acg: + xpath = self.project.add_project(NsdCatalogDtsHandler.XPATH) self.reg = acg.register( - xpath=NsdCatalogDtsHandler.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER, ) + def deregister(self): + if self.reg: + self.reg.deregister() + self.reg = None + class VnfdCatalogDtsHandler(CatalogDtsHandler): XPATH = "C,/vnfd:vnfd-catalog/vnfd:vnfd" def add_vnfd(self, vnfd): self.log.debug('vnfd-catalog-handler:add:{}'.format(vnfd.id)) - if vnfd.id not in self.tasklet.vnfd_catalog: - self.tasklet.vnfd_catalog[vnfd.id] = vnfd + if vnfd.id not in self.project.vnfd_catalog: + self.project.vnfd_catalog[vnfd.id] = vnfd else: self.log.error("VNFD already in catalog: {}".format(vnfd.id)) def update_vnfd(self, vnfd): self.log.debug('vnfd-catalog-handler:update:{}'.format(vnfd.id)) - if vnfd.id in self.tasklet.vnfd_catalog: - self.tasklet.vnfd_catalog[vnfd.id] = vnfd + if vnfd.id in self.project.vnfd_catalog: + self.project.vnfd_catalog[vnfd.id] = vnfd else: self.log.error("unrecognized VNFD: {}".format(vnfd.id)) def delete_vnfd(self, vnfd_id): self.log.debug('vnfd-catalog-handler:delete:{}'.format(vnfd_id)) - if vnfd_id in self.tasklet.vnfd_catalog: - del self.tasklet.vnfd_catalog[vnfd_id] + if vnfd_id in self.project.vnfd_catalog: + del self.project.vnfd_catalog[vnfd_id] else: self.log.error("unrecognized VNFD: {}".format(vnfd_id)) try: - self.tasklet.vnfd_package_store.delete_package(vnfd_id) + self.project.tasklet.vnfd_package_store.delete_package(vnfd_id) except store.PackageStoreError as e: self.log.warning("could not delete package from store: %s", str(e)) @@ -223,28 +216,36 @@ class VnfdCatalogDtsHandler(CatalogDtsHandler): for cfg in update_cfgs: self.update_vnfd(cfg) - self.log.debug("Registering for VNFD catalog") + self.log.debug("Registering for VNFD catalog in project {}". + format(self.project.name)) acg_handler = rift.tasklets.AppConfGroup.Handler( on_apply=apply_config, ) with self.dts.appconf_group_create(acg_handler) as acg: + xpath = self.project.add_project(VnfdCatalogDtsHandler.XPATH) self.reg = acg.register( - xpath=VnfdCatalogDtsHandler.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER, ) + def deregister(self): + if self.reg: + self.reg.deregister() + self.reg = None + class CfgAgentAccountHandlers(object): - def __init__(self, dts, log, log_hdl, loop): + def __init__(self, dts, log, log_hdl, loop, project): self._dts = dts self._log = log self._log_hdl = log_hdl self._loop = loop + self._project = project self._log.debug("creating config agent account config handler") self.cfg_agent_cfg_handler = rift.mano.config_agent.ConfigAgentSubscriber( - self._dts, self._log, + self._dts, self._log, self._project, rift.mano.config_agent.ConfigAgentCallbacks( on_add_apply=self.on_cfg_agent_account_added, on_delete_apply=self.on_cfg_agent_account_deleted, @@ -253,7 +254,7 @@ class CfgAgentAccountHandlers(object): self._log.debug("creating config agent account opdata handler") self.cfg_agent_operdata_handler = rift.mano.config_agent.CfgAgentDtsOperdataHandler( - self._dts, self._log, self._loop, + self._dts, self._log, self._loop, self._project ) def on_cfg_agent_account_deleted(self, account): @@ -269,38 +270,45 @@ class CfgAgentAccountHandlers(object): self.cfg_agent_cfg_handler.register() yield from self.cfg_agent_operdata_handler.register() + def deregister(self): + self.cfg_agent_operdata_handler.deregister() + self.cfg_agent_cfg_handler.deregister() + + class CloudAccountHandlers(object): - def __init__(self, dts, log, log_hdl, loop, app): + def __init__(self, dts, log, log_hdl, loop, app, project): self._log = log self._log_hdl = log_hdl self._dts = dts self._loop = loop self._app = app + self._project = project - self._log.debug("creating cloud account config handler") + self._log.debug("Creating cloud account config handler for project {}". + format(project.name)) self.cloud_cfg_handler = rift.mano.cloud.CloudAccountConfigSubscriber( - self._dts, self._log, self._log_hdl, + self._dts, self._log, self._log_hdl, self._project, rift.mano.cloud.CloudAccountConfigCallbacks( on_add_apply=self.on_cloud_account_added, on_delete_apply=self.on_cloud_account_deleted, - ) + ), ) self._log.debug("creating cloud account opdata handler") self.cloud_operdata_handler = rift.mano.cloud.CloudAccountDtsOperdataHandler( - self._dts, self._log, self._loop, + self._dts, self._log, self._loop, self._project, ) def on_cloud_account_deleted(self, account_name): self._log.debug("cloud account deleted") - self._app.accounts.clear() - self._app.accounts.extend(list(self.cloud_cfg_handler.accounts.values())) + self._app.accounts[self._project.name] = \ + list(self.cloud_cfg_handler.accounts.values()) self.cloud_operdata_handler.delete_cloud_account(account_name) def on_cloud_account_added(self, account): self._log.debug("cloud account added") - self._app.accounts.clear() - self._app.accounts.extend(list(self.cloud_cfg_handler.accounts.values())) + self._app.accounts[self._project.name] = \ + list(self.cloud_cfg_handler.accounts.values()) self._log.debug("accounts: %s", self._app.accounts) self.cloud_operdata_handler.add_cloud_account(account) @@ -309,21 +317,18 @@ class CloudAccountHandlers(object): self.cloud_cfg_handler.register() yield from self.cloud_operdata_handler.register() + def deregister(self): + self.cloud_cfg_handler.deregister() + yield from self.cloud_operdata_handler.deregister() -class LaunchpadTasklet(rift.tasklets.Tasklet): - UPLOAD_MAX_BODY_SIZE = MAX_BODY_SIZE - UPLOAD_MAX_BUFFER_SIZE = MAX_BUFFER_SIZE - UPLOAD_PORT = "4567" - def __init__(self, *args, **kwargs): - super(LaunchpadTasklet, self).__init__(*args, **kwargs) - self.rwlog.set_category("rw-mano-log") - self.rwlog.set_subcategory("launchpad") +class LaunchpadProject(ManoProject): - self.app = None - self.server = None + def __init__(self, name, tasklet, **kw): + super(LaunchpadProject, self).__init__(tasklet.log, name) + self.update(tasklet) + self._app = kw['app'] - self.account_handler = None self.config_handler = None self.nsd_catalog_handler = None self.vld_catalog_handler = None @@ -331,14 +336,59 @@ class LaunchpadTasklet(rift.tasklets.Tasklet): self.cloud_handler = None self.datacenter_handler = None self.lp_config_handler = None - - self.vnfd_package_store = store.VnfdPackageFilesystemStore(self.log) - self.nsd_package_store = store.NsdPackageFilesystemStore(self.log) + self.account_handler = None self.nsd_catalog = dict() self.vld_catalog = dict() self.vnfd_catalog = dict() + @property + def dts(self): + return self._dts + + @property + def loop(self): + return self._loop + + @asyncio.coroutine + def register(self): + self.log.debug("creating NSD catalog handler for project {}".format(self.name)) + self.nsd_catalog_handler = NsdCatalogDtsHandler(self, self._app) + yield from self.nsd_catalog_handler.register() + + self.log.debug("creating VNFD catalog handler for project {}".format(self.name)) + self.vnfd_catalog_handler = VnfdCatalogDtsHandler(self, self._app) + yield from self.vnfd_catalog_handler.register() + + self.log.debug("creating datacenter handler for project {}".format(self.name)) + self.datacenter_handler = datacenters.DataCenterPublisher(self.log, self.dts, + self.loop, self) + yield from self.datacenter_handler.register() + + self.log.debug("creating cloud account handler for project {}".format(self.name)) + self.cloud_handler = CloudAccountHandlers(self.dts, self.log, self.log_hdl, + self.loop, self._app, self) + yield from self.cloud_handler.register() + + self.log.debug("creating config agent handler for project {}".format(self.name)) + self.config_handler = CfgAgentAccountHandlers(self.dts, self.log, self.log_hdl, + self.loop, self) + yield from self.config_handler.register() + + def deregister(self): + self.log.debug("De-register handlers for project: {}".format(self.name)) + self.config_handler.deregister() + self.cloud_handler.deregister() + self.datacenter_handler.deregister() + self.vnfd_catalog_handler.deregister() + self.nsd_catalog_handler.deregister() + + @asyncio.coroutine + def delete_prepare(self): + if self.nsd_catalog or self.vnfd_catalog or self.vld_catalog: + return False + return True + @property def cloud_accounts(self): if self.cloud_handler is None: @@ -346,6 +396,48 @@ class LaunchpadTasklet(rift.tasklets.Tasklet): return list(self.cloud_handler.cloud_cfg_handler.accounts.values()) + +class LaunchpadTasklet(rift.tasklets.Tasklet): + UPLOAD_MAX_BODY_SIZE = MAX_BODY_SIZE + UPLOAD_MAX_BUFFER_SIZE = MAX_BUFFER_SIZE + UPLOAD_PORT = "4567" + + def __init__(self, *args, **kwargs): + super(LaunchpadTasklet, self).__init__(*args, **kwargs) + self.rwlog.set_category("rw-mano-log") + self.rwlog.set_subcategory("launchpad") + + self.dts = None + self.project_handler = None + + self.vnfd_package_store = store.VnfdPackageFilesystemStore(self.log) + self.nsd_package_store = store.NsdPackageFilesystemStore(self.log) + + self.app = None + self.server = None + self.projects = {} + print("LP Tasklet init") + + def _get_project(project=None): + if project is None: + project = DEFAULT_PROJECT + + if project in self.projects: + return self.projects[project] + + msg = "Project {} not found".format(project) + self._log.error(msg) + raise LpProjectNotFound(msg) + + def nsd_catalog_get(self, project=None): + return self._get_project(project=project).nsd_catalog + + def vnfd_catalog_get(self, project=None): + return self._get_project(project=project).vnfd_catalog + + def get_cloud_accounts(self, project=None): + return self._get_project(project=project).cloud_accounts + def start(self): super(LaunchpadTasklet, self).start() self.log.info("Starting LaunchpadTasklet") @@ -368,56 +460,51 @@ class LaunchpadTasklet(rift.tasklets.Tasklet): self.log.exception("Caught Exception in LP stop") raise + def get_vnfd_catalog(self, project): + return self.projects[project].vnfd_catalog + + def get_nsd_catalog(self, project): + return self.projects[project].nsd_catalog + @asyncio.coroutine def init(self): - io_loop = rift.tasklets.tornado.TaskletAsyncIOLoop(asyncio_loop=self.loop) - self.app = uploader.UploaderApplication.from_tasklet(self) - yield from self.app.register() - - manifest = self.tasklet_info.get_pb_manifest() - ssl_cert = manifest.bootstrap_phase.rwsecurity.cert - ssl_key = manifest.bootstrap_phase.rwsecurity.key - ssl_options = { + try: + io_loop = rift.tasklets.tornado.TaskletAsyncIOLoop(asyncio_loop=self.loop) + self.app = uploader.UploaderApplication.from_tasklet(self) + yield from self.app.register() + + manifest = self.tasklet_info.get_pb_manifest() + ssl_cert = manifest.bootstrap_phase.rwsecurity.cert + ssl_key = manifest.bootstrap_phase.rwsecurity.key + ssl_options = { "certfile": ssl_cert, "keyfile": ssl_key, - } - - if manifest.bootstrap_phase.rwsecurity.use_ssl: - self.server = tornado.httpserver.HTTPServer( - self.app, - max_body_size=LaunchpadTasklet.UPLOAD_MAX_BODY_SIZE, - io_loop=io_loop, - ssl_options=ssl_options, - ) - - else: - self.server = tornado.httpserver.HTTPServer( - self.app, - max_body_size=LaunchpadTasklet.UPLOAD_MAX_BODY_SIZE, - io_loop=io_loop, - ) - - self.log.debug("creating NSD catalog handler") - self.nsd_catalog_handler = NsdCatalogDtsHandler(self, self.app) - yield from self.nsd_catalog_handler.register() - - self.log.debug("creating VNFD catalog handler") - self.vnfd_catalog_handler = VnfdCatalogDtsHandler(self, self.app) - yield from self.vnfd_catalog_handler.register() - - self.log.debug("creating datacenter handler") - self.datacenter_handler = datacenters.DataCenterPublisher(self.log, self.dts, self.loop) - yield from self.datacenter_handler.register() + } + + if manifest.bootstrap_phase.rwsecurity.use_ssl: + self.server = tornado.httpserver.HTTPServer( + self.app, + max_body_size=LaunchpadTasklet.UPLOAD_MAX_BODY_SIZE, + io_loop=io_loop, + ssl_options=ssl_options, + ) - self.log.debug("creating cloud account handler") - self.cloud_handler = CloudAccountHandlers( - self.dts, self.log, self.log_hdl, self.loop, self.app + else: + self.server = tornado.httpserver.HTTPServer( + self.app, + max_body_size=LaunchpadTasklet.UPLOAD_MAX_BODY_SIZE, + io_loop=io_loop, ) - yield from self.cloud_handler.register() - self.log.debug("creating config agent handler") - self.config_handler = CfgAgentAccountHandlers(self.dts, self.log, self.log_hdl, self.loop) - yield from self.config_handler.register() + self.log.debug("Registering project handler") + print("PJ: Registering project handler") + self.project_handler = ProjectHandler(self, LaunchpadProject, + app=self.app) + self.project_handler.register() + + except Exception as e: + self.log.error("Exception : {}".format(e)) + self.log.exception(e) @asyncio.coroutine def run(self): diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/uploader.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/uploader.py index c908bb3b..1406a890 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/uploader.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/uploader.py @@ -146,12 +146,12 @@ class HttpMessageError(Exception): class UploadRpcHandler(mano_dts.AbstractRpcHandler): - def __init__(self, log, dts, loop, application): + def __init__(self, application): """ Args: application: UploaderApplication """ - super().__init__(log, dts, loop) + super().__init__(application.log, application.dts, application.loop) self.application = application @property @@ -164,30 +164,41 @@ class UploadRpcHandler(mano_dts.AbstractRpcHandler): log = self.application.get_logger(transaction_id) log.message(OnboardStart()) + self.log.debug("Package create RPC: {}".format(msg)) auth = None if msg.username is not None: auth = (msg.username, msg.password) + try: + project = msg.project_name + except AttributeError as e: + self._log.warning("Did not get project name in RPC: {}". + format(msg.as_dict())) + project = rift.mano.utils.project.DEFAULT_PROJECT + self.application.onboard( msg.external_url, transaction_id, - auth=auth + auth=auth, + project=project, ) rpc_op = RPC_PACKAGE_CREATE_ENDPOINT.from_dict({ - "transaction_id": transaction_id}) + "transaction_id": transaction_id, + "project_name": project, + }) return rpc_op class UpdateRpcHandler(mano_dts.AbstractRpcHandler): - def __init__(self, log, dts, loop, application): + def __init__(self, application): """ Args: application: UploaderApplication """ - super().__init__(log, dts, loop) + super().__init__(application.log, application.dts, application.loop) self.application = application @property @@ -208,11 +219,14 @@ class UpdateRpcHandler(mano_dts.AbstractRpcHandler): self.application.update( msg.external_url, transaction_id, - auth=auth + auth=auth, + project=msg.project_name, ) rpc_op = RPC_PACKAGE_UPDATE_ENDPOINT.from_dict({ - "transaction_id": transaction_id}) + "transaction_id": transaction_id, + "project_name": msg.project_name, + }) return rpc_op @@ -231,11 +245,12 @@ class UpdateStateHandler(state.StateHandler): class UpdatePackage(downloader.DownloaderProtocol): - def __init__(self, log, loop, url, auth, + def __init__(self, log, loop, project, url, auth, onboarder, uploader, package_store_map): super().__init__() self.log = log self.loop = loop + self.project = project self.url = url self.auth = auth self.onboarder = onboarder @@ -355,7 +370,7 @@ class UpdatePackage(downloader.DownloaderProtocol): ) try: self.uploader.upload_image(image_name, image_checksum, image_hdl) - self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum) + self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum, self.project) except image.ImageUploadError as e: self.log.exception("Failed to upload image: %s", image_name) @@ -427,22 +442,24 @@ class UpdatePackage(downloader.DownloaderProtocol): self.log.message(UpdateDescriptorUpdate()) try: - self.onboarder.update(descriptor_msg) + self.onboarder.update(descriptor_msg, project=self.project) except onboard.UpdateError as e: raise MessageException(UpdateDescriptorError(package.descriptor_file)) from e class OnboardPackage(downloader.DownloaderProtocol): - def __init__(self, log, loop, url, auth, + def __init__(self, log, loop, project, url, auth, onboarder, uploader, package_store_map): self.log = log self.loop = loop + self.project = project self.url = url self.auth = auth self.onboarder = onboarder self.uploader = uploader self.package_store_map = package_store_map + self.project = project def _onboard_package(self, packages): # Extract package could return multiple packages if @@ -623,7 +640,7 @@ class OnboardPackage(downloader.DownloaderProtocol): self.log.message(OnboardDescriptorOnboard()) try: - self.onboarder.onboard(descriptor_msg) + self.onboarder.onboard(descriptor_msg, project=self.project) except onboard.OnboardError as e: raise MessageException(OnboardDescriptorError(package.descriptor_file)) from e @@ -637,29 +654,23 @@ class UploaderApplication(tornado.web.Application): ssl_cert = manifest.bootstrap_phase.rwsecurity.cert ssl_key = manifest.bootstrap_phase.rwsecurity.key return cls( - tasklet.log, - tasklet.dts, - tasklet.loop, - ssl=(ssl_cert, ssl_key), - vnfd_store=tasklet.vnfd_package_store, - nsd_store=tasklet.nsd_package_store, - vnfd_catalog=tasklet.vnfd_catalog, - nsd_catalog=tasklet.nsd_catalog) + tasklet, + ssl=(ssl_cert, ssl_key), + vnfd_store=tasklet.vnfd_package_store, + nsd_store=tasklet.nsd_package_store) def __init__( self, - log, - dts, - loop, + tasklet, ssl=None, vnfd_store=None, - nsd_store=None, - vnfd_catalog=None, - nsd_catalog=None): + nsd_store=None): - self.log = log - self.loop = loop - self.dts = dts + self.log = tasklet.log + self.loop = tasklet.loop + self.dts = tasklet.dts + + self.accounts = {} self.use_ssl = False self.ssl_cert, self.ssl_key = None, None @@ -673,7 +684,6 @@ class UploaderApplication(tornado.web.Application): if not nsd_store: nsd_store = rift.package.store.NsdPackageFilesystemStore(self.log) - self.accounts = [] self.messages = collections.defaultdict(list) self.export_dir = os.path.join(os.environ['RIFT_ARTIFACTS'], 'launchpad/exports') @@ -689,24 +699,16 @@ class UploaderApplication(tornado.web.Application): self.exporter = export.DescriptorPackageArchiveExporter(self.log) self.loop.create_task(export.periodic_export_cleanup(self.log, self.loop, self.export_dir)) - self.vnfd_catalog = vnfd_catalog - self.nsd_catalog = nsd_catalog + self.get_vnfd_catalog = tasklet.get_vnfd_catalog + self.get_nsd_catalog = tasklet.get_nsd_catalog catalog_map = { - "vnfd": self.vnfd_catalog, - "nsd": self.nsd_catalog + "vnfd": self.get_vnfd_catalog, + "nsd": self.get_nsd_catalog } - self.upload_handler = UploadRpcHandler(self.log, self.dts, self.loop, self) - self.update_handler = UpdateRpcHandler(self.log, self.dts, self.loop, self) - self.export_handler = export.ExportRpcHandler( - self.log, - self.dts, - self.loop, - self, - store_map=self.package_store_map, - exporter=self.exporter, - catalog_map=catalog_map - ) + self.upload_handler = UploadRpcHandler(self) + self.update_handler = UpdateRpcHandler(self) + self.export_handler = export.ExportRpcHandler(self, catalog_map) attrs = dict(log=self.log, loop=self.loop) @@ -737,12 +739,13 @@ class UploaderApplication(tornado.web.Application): def get_logger(self, transaction_id): return message.Logger(self.log, self.messages[transaction_id]) - def onboard(self, url, transaction_id, auth=None): + def onboard(self, url, transaction_id, auth=None, project=None): log = message.Logger(self.log, self.messages[transaction_id]) onboard_package = OnboardPackage( log, self.loop, + project, url, auth, self.onboarder, @@ -752,12 +755,13 @@ class UploaderApplication(tornado.web.Application): self.loop.run_in_executor(None, onboard_package.download_package) - def update(self, url, transaction_id, auth=None): + def update(self, url, transaction_id, auth=None, project=None): log = message.Logger(self.log, self.messages[transaction_id]) update_package = UpdatePackage( log, self.loop, + project, url, auth, self.onboarder, diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/scripts/onboard_pkg b/rwlaunchpad/plugins/rwlaunchpadtasklet/scripts/onboard_pkg index 2b2eb911..d5bb4e50 100755 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/scripts/onboard_pkg +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/scripts/onboard_pkg @@ -100,6 +100,8 @@ class OnboardPackage: self._log = log self._args = args + self._project = args.project + self._pkgs = None self._service_name = None @@ -121,30 +123,36 @@ class OnboardPackage: user=self._user, passwd=self._password, api_server_ip=self._api_server_ip) + self._upload_url = "curl -k https://{ip}:{port}/api/upload". \ format(ip=self._ip, port=self._uport) self._headers = '-H "accept: application/json"' + \ ' -H "content-type: application/json"' - self._conf_url = "curl -k {header} --user \"{user}:{passwd}\" https://{ip}:{port}/api/config". \ + + self._conf_url = "curl -k {header} --user \"{user}:{passwd}\" https://{ip}:{port}/api/config/project/{project}". \ format(header=self._headers, user=self._user, passwd=self._password, ip=self._ip, - port=self._rport) - self._oper_url = "curl -k {header} --user \"{user}:{passwd}\" https://{ip}:{port}/api/operational". \ + port=self._rport, + project=self._project) + + self._oper_url = "curl -k {header} --user \"{user}:{passwd}\" https://{ip}:{port}/api/operational/project/{project}". \ format(header=self._headers, user=self._user, passwd=self._password, ip=self._ip, - port=self._rport) + port=self._rport, + project=self._project) @property def log(self): return self._log def validate_args(self): + args = self._args if args.upload_pkg is not None: self._pkgs = args.upload_pkg self.log.debug("Packages to upload: {}".format(self._pkgs)) @@ -165,8 +173,8 @@ class OnboardPackage: uuid.UUID(args.datacenter) self._dc = args.datacenter except ValueError as e: - raise OnboardPkgInvalidDescId("Invalid UUID for datacenter: {}". - format(args.datacenter)) + raise OnboardPkgInvalidDescId("Invalid UUID for datacenter {}: {}". + format(args.datacenter, e)) elif args.vim_account: self._account = args.vim_account @@ -181,8 +189,8 @@ class OnboardPackage: self._service_name, self._account)) - if (self._pkgs is None) and (self._nsd_id is None): - raise OnboardPkgInputError("Need to specify either upload-pkg or instantiate options") + if (self._pkgs is None) and (self._nsd_id is None) and (not args.list_nsds): + raise OnboardPkgInputError("Need to specify either upload-pkg or instantiate or list options") # Validate the port numbers are correct def valid_port(port): @@ -334,7 +342,12 @@ class OnboardPackage: format(self._nsd_id, js['error'])) - nsd = js['nsd:nsd'] + try: + nsd = js['nsd:nsd'] + except KeyError as e: + raise OnboardPkgNsdError("NSD ID {} provided is not valid". + format(self._nsd_id)) + self.log.debug("NSD to instantiate: {}".format(nsd)) # Generate a UUID for NS @@ -381,11 +394,41 @@ class OnboardPackage: self.log.info("Successfully initiated instantiation of NS as {} ({})". format(self._service_name, ns_id)) + def list_nsds(self): + if self._args.list_nsds: + self.log.debug("Check NSDS at {}:{}, with credentials {}:{}". + format(self._ip, self._rport, self._user, self._password)) + + rest_url = self._conf_url+"/nsd-catalog/nsd" + try: + output = self._exec_cmd(rest_url) + self.log.debug("Output of NSD list: {}". + format(output)) + if output: + js = json.loads(output) + if "error" in js: + raise OnboardPkgRcConnError("SO Restconf connect error: {}". + format(js["error"])) + else: + print("No NSDs found on SO") + return + + self.log.debug("NSD list: {}".format(js)) + print('List of NSDs on SO:\nName\tID') + for nsd in js['nsd:nsd']: + print('{}\t{}'.format(nsd['name'], nsd['id'])) + + except OnboardPkgCmdError as e: + self.log.error("SO restconf connect failed: {}".format(e)) + raise OnboardPkgRcConnError("SO Restconf connect error: {}". + format(e)) + def process(self): self.validate_args() self.validate_connectivity() self.upload_packages() self.instantiate() + self.list_nsds() if __name__ == "__main__": @@ -397,6 +440,9 @@ if __name__ == "__main__": help="Descriptor packages to upload. " + \ "If multiple descriptors are provided, they are uploaded in the same sequence.") + parser.add_argument("-l", "--list-nsds", action='store_true', + help="List available network service descriptors") + parser.add_argument("-i", "--instantiate", help="Instantiate a network service with the name") parser.add_argument("-d", "--nsd-id", @@ -406,6 +452,8 @@ if __name__ == "__main__": parser.add_argument("-c", "--vim-account", help="Cloud/VIM account to instantiate on") + parser.add_argument("--project", default='default', + help="Project to use, default 'default'") parser.add_argument("-o", "--onboard-port", default=8443, type=int, help="Onboarding port number - node port number, default 8443") parser.add_argument("-p", "--upload-port", default=4567, type=int, diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_export.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_export.py index 7a787c7b..522c1a80 100755 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_export.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_export.py @@ -59,7 +59,7 @@ class TestExport(utest_package.PackageTestCase): self._vnfd_serializer = rift.package.convert.VnfdSerializer() def test_create_archive(self): - rw_vnfd_msg = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd( + rw_vnfd_msg = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd( id="new_id", name="new_name", description="new_description" ) json_desc_str = self._rw_vnfd_serializer.to_json_string(rw_vnfd_msg) @@ -80,11 +80,11 @@ class TestExport(utest_package.PackageTestCase): self.assertEqual(package.descriptor_msg, rw_vnfd_msg) def test_export_package(self): - rw_vnfd_msg = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd( + rw_vnfd_msg = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd( id="new_id", name="new_name", description="new_description", meta="THIS FIELD IS NOT IN REGULAR VNFD" ) - vnfd_msg = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd() + vnfd_msg = VnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd() vnfd_msg.from_dict(rw_vnfd_msg.as_dict(), ignore_missing_keys=True) self.assertNotEqual(rw_vnfd_msg, vnfd_msg) diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_onboard.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_onboard.py index 871132f5..b3a7458e 100755 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_onboard.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_onboard.py @@ -218,7 +218,7 @@ class OnboardTestCase(tornado.testing.AsyncHTTPTestCase): @rift.test.dts.async_test def test_onboard_nsd(self): - nsd_msg = NsdYang.YangData_Nsd_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") + nsd_msg = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") yield from self._loop.run_in_executor(None, self._onboarder.onboard, nsd_msg) self.assertEqual(self._handler_info.last_request_message, nsd_msg) self.assertEqual(self._handler_info.last_descriptor_type, "nsd") @@ -226,7 +226,7 @@ class OnboardTestCase(tornado.testing.AsyncHTTPTestCase): @rift.test.dts.async_test def test_update_nsd(self): - nsd_msg = NsdYang.YangData_Nsd_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") + nsd_msg = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") yield from self._loop.run_in_executor(None, self._onboarder.update, nsd_msg) self.assertEqual(self._handler_info.last_request_message, nsd_msg) self.assertEqual(self._handler_info.last_descriptor_type, "nsd") @@ -234,7 +234,7 @@ class OnboardTestCase(tornado.testing.AsyncHTTPTestCase): @rift.test.dts.async_test def test_bad_descriptor_type(self): - nsd_msg = NsdYang.YangData_Nsd_NsdCatalog() + nsd_msg = NsdYang.YangData_RwProject_Project_NsdCatalog() with self.assertRaises(TypeError): yield from self._loop.run_in_executor(None, self._onboarder.update, nsd_msg) @@ -246,7 +246,7 @@ class OnboardTestCase(tornado.testing.AsyncHTTPTestCase): # Use a port not used by the instantiated server new_port = self._port - 1 self._onboarder.port = new_port - nsd_msg = NsdYang.YangData_Nsd_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") + nsd_msg = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") with self.assertRaises(onboard.OnboardError): yield from self._loop.run_in_executor(None, self._onboarder.onboard, nsd_msg) @@ -259,7 +259,7 @@ class OnboardTestCase(tornado.testing.AsyncHTTPTestCase): # Set the timeout to something minimal to speed up test self._onboarder.timeout = .1 - nsd_msg = NsdYang.YangData_Nsd_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") + nsd_msg = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd(id=str(uuid.uuid4()), name="nsd_name") # Force the request to timeout by running the call synchronously so the with self.assertRaises(onboard.OnboardError): diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_uploader_app_dts.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_uploader_app_dts.py index fdc2e22c..18b53d5f 100755 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_uploader_app_dts.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/test/utest_uploader_app_dts.py @@ -74,7 +74,7 @@ class TestCase(rift.test.dts.AbstractDTSTest): mock_vnfd_catalog = mock.MagicMock() self.uid, path = self.create_mock_package() - mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict({ + mock_vnfd = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd.from_dict({ "id": self.uid }) mock_vnfd_catalog = {self.uid: mock_vnfd} diff --git a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py index b8abea74..f43da6fc 100644 --- a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py +++ b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py @@ -231,7 +231,7 @@ class NfviMetrics(object): self._account = account self._plugin = plugin self._timestamp = 0 - self._metrics = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_NfviMetrics() + self._metrics = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics() self._vdur = vdur self._vim_id = vdur.vim_id self._updating = None @@ -305,7 +305,7 @@ class NfviMetrics(object): try: # Create uninitialized metric structure - vdu_metrics = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_NfviMetrics() + vdu_metrics = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics() # VCPU vdu_metrics.vcpu.total = self.vdur.vm_flavor.vcpu_count @@ -549,17 +549,19 @@ class Monitor(object): different sub-systems that are used to monitor the NFVI. """ - def __init__(self, loop, log, config): + def __init__(self, loop, log, config, project): """Create a Monitor object Arguments: - loop - an event loop - log - the logger used by this object - config - an instance of InstanceConfiguration + loop - an event loop + log - the logger used by this object + config - an instance of InstanceConfiguration + project - an instance of the project """ self._loop = loop self._log = log + self._project = project self._cloud_accounts = dict() self._nfvi_plugins = NfviMetricsPluginManager(log) @@ -580,6 +582,10 @@ class Monitor(object): """The event log used by this object""" return self._log + @property + def project(self): + return self._project + @property def cache(self): """The NFVI metrics cache""" diff --git a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py index 4ab351e7..b9173e64 100644 --- a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py +++ b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py @@ -90,30 +90,34 @@ from gi.repository import ( import rift.tasklets import rift.mano.cloud +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + ) from . import core class DtsHandler(object): - def __init__(self, tasklet): + def __init__(self, project): self.reg = None - self.tasklet = tasklet + self.project = project @property def log(self): - return self.tasklet.log + return self.project._log @property def log_hdl(self): - return self.tasklet.log_hdl + return self.project._log_hdl @property def dts(self): - return self.tasklet.dts + return self.project._dts @property def loop(self): - return self.tasklet.loop + return self.project._loop @property def classname(self): @@ -151,7 +155,7 @@ class VnfrCatalogSubscriber(DtsHandler): with self.dts.group_create() as group: group.register( - xpath=VnfrCatalogSubscriber.XPATH, + xpath=self.project.add_project(VnfrCatalogSubscriber.XPATH), flags=rwdts.Flag.SUBSCRIBER, handler=handler, ) @@ -173,20 +177,20 @@ class NsInstanceConfigSubscriber(DtsHandler): with self.dts.appconf_group_create(acg_handler) as acg: self.reg = acg.register( - xpath=NsInstanceConfigSubscriber.XPATH, + xpath=self.project.add_project(NsInstanceConfigSubscriber.XPATH), flags=rwdts.Flag.SUBSCRIBER, ) class CloudAccountDtsHandler(DtsHandler): - def __init__(self, tasklet): - super().__init__(tasklet) + def __init__(self, project): + super().__init__(project) self._cloud_cfg_subscriber = None def register(self): self.log.debug("creating cloud account config handler") self._cloud_cfg_subscriber = rift.mano.cloud.CloudAccountConfigSubscriber( - self.dts, self.log, self.log_hdl, + self.dts, self.log, self.log_hdl, self.project, rift.mano.cloud.CloudAccountConfigCallbacks( on_add_apply=self.tasklet.on_cloud_account_create, on_delete_apply=self.tasklet.on_cloud_account_delete, @@ -208,7 +212,7 @@ class VdurNfviMetricsPublisher(DtsHandler): # before timing out, the current data will be published instead. TIMEOUT = 2.0 - def __init__(self, tasklet, vnfr, vdur): + def __init__(self, project, vnfr, vdur): """Create an instance of VdurNvfiPublisher Arguments: @@ -217,12 +221,12 @@ class VdurNfviMetricsPublisher(DtsHandler): vdur - the VDUR of the VDU whose metrics are published """ - super().__init__(tasklet) + super().__init__(project) self._vnfr = vnfr self._vdur = vdur self._handle = None - self._xpath = VdurNfviMetricsPublisher.XPATH.format(vnfr.id, vdur.id) + self._xpath = project.add_project(VdurNfviMetricsPublisher.XPATH.format(vnfr.id, vdur.id)) self._deregistered = asyncio.Event(loop=self.loop) @@ -321,7 +325,7 @@ class LaunchpadConfigDtsSubscriber(DtsHandler): with self.dts.appconf_group_create(acg_handler) as acg: self.reg = acg.register( - xpath="C,/rw-launchpad:launchpad-config", + xpath=self.project.add_project("C,/rw-launchpad:launchpad-config"), flags=rwdts.Flag.SUBSCRIBER, ) @@ -335,8 +339,8 @@ class CreateAlarmRPC(DtsHandler): them on to the tasklet. """ - def __init__(self, tasklet): - super().__init__(tasklet) + def __init__(self, project): + super().__init__(project) self._handle = None @asyncio.coroutine @@ -345,6 +349,10 @@ class CreateAlarmRPC(DtsHandler): @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): try: + + if not self.project.rpc_check(msg, xact_info=xact_info): + return + response = VnfrYang.YangOutput_Vnfr_CreateAlarm() response.alarm_id = yield from self.tasklet.on_create_alarm( msg.cloud_account, @@ -382,8 +390,8 @@ class DestroyAlarmRPC(DtsHandler): them on to the tasklet. """ - def __init__(self, tasklet): - super().__init__(tasklet) + def __init__(self, project): + super().__init__(project) self._handle = None @asyncio.coroutine @@ -392,6 +400,9 @@ class DestroyAlarmRPC(DtsHandler): @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): try: + if not self.project.rpc_check(msg, xact_info=xact_info): + return + yield from self.tasklet.on_destroy_alarm( msg.cloud_account, msg.alarm_id, @@ -473,70 +484,31 @@ class WebhookApplication(tornado.web.Application): ]) -class MonitorTasklet(rift.tasklets.Tasklet): - """ - The MonitorTasklet provides a interface for DTS to interact with an - instance of the Monitor class. This allows the Monitor class to remain - independent of DTS. - """ - - DEFAULT_POLLING_PERIOD = 1.0 - - def __init__(self, *args, **kwargs): - try: - super().__init__(*args, **kwargs) - self.rwlog.set_category("rw-monitor-log") - - self.vnfr_subscriber = VnfrCatalogSubscriber(self) - self.cloud_cfg_subscriber = CloudAccountDtsHandler(self) - self.ns_instance_config_subscriber = NsInstanceConfigSubscriber(self) - self.launchpad_cfg_subscriber = LaunchpadConfigDtsSubscriber(self) +class MonitorProject(ManoProject): - self.config = core.InstanceConfiguration() - self.config.polling_period = MonitorTasklet.DEFAULT_POLLING_PERIOD + def __init__(self, name, tasklet, **kw): + super(MonitorProject, self).__init__(log, name) + self._tasklet = tasklet + self._log_hdl = tasklet.log_hdl + self._dts = tasklet.dts + self._loop = tasklet.loop - self.monitor = core.Monitor(self.loop, self.log, self.config) - self.vdur_handlers = dict() + self.vnfr_subscriber = VnfrCatalogSubscriber(self) + self.cloud_cfg_subscriber = CloudAccountDtsHandler(self) + self.ns_instance_config_subscriber = NsInstanceConfigSubscriber(self) + self.launchpad_cfg_subscriber = LaunchpadConfigDtsSubscriber(self) - self.webhooks = None - self.create_alarm_rpc = CreateAlarmRPC(self) - self.destroy_alarm_rpc = DestroyAlarmRPC(self) + self.config = core.InstanceConfiguration() + self.config.polling_period = MonitorTasklet.DEFAULT_POLLING_PERIOD + self.monitor = core.Monitor(self.loop, self.log, self.config, self) + self.vdur_handlers = dict() - except Exception as e: - self.log.exception(e) - - @property - def polling_period(self): - return self.config.polling_period - - @property - def public_ip(self): - """The public IP of the launchpad""" - return self.config.public_ip - - def start(self): - super().start() - self.log.info("Starting MonitoringTasklet") - - self.log.debug("Registering with dts") - self.dts = rift.tasklets.DTS( - self.tasklet_info, - RwLaunchpadYang.get_schema(), - self.loop, - self.on_dts_state_change - ) - - self.log.debug("Created DTS Api GI Object: %s", self.dts) - - def stop(self): - try: - self.dts.deinit() - except Exception as e: - self.log.exception(e) + self.create_alarm_rpc = CreateAlarmRPC(self) + self.destroy_alarm_rpc = DestroyAlarmRPC(self) @asyncio.coroutine - def init(self): + def register (self): self.log.debug("creating cloud account handler") self.cloud_cfg_subscriber.register() @@ -555,23 +527,15 @@ class MonitorTasklet(rift.tasklets.Tasklet): self.log.debug("creating destroy-alarm rpc handler") yield from self.destroy_alarm_rpc.register() - self.log.debug("creating webhook server") - loop = rift.tasklets.tornado.TaskletAsyncIOLoop(asyncio_loop=self.loop) - self.webhooks = WebhookApplication(self) - self.server = tornado.httpserver.HTTPServer( - self.webhooks, - io_loop=loop, - ) - - @asyncio.coroutine - def on_public_ip(self, ip): - """Store the public IP of the launchpad - Arguments: - ip - a string containing the public IP address of the launchpad + @property + def polling_period(self): + return self.config.polling_period - """ - self.config.public_ip = ip + @property + def public_ip(self): + """The public IP of the launchpad""" + return self.config.public_ip def on_ns_instance_config_update(self, config): """Update configuration information @@ -589,44 +553,6 @@ class MonitorTasklet(rift.tasklets.Tasklet): def on_cloud_account_delete(self, account_name): self.monitor.remove_cloud_account(account_name) - @asyncio.coroutine - def run(self): - self.webhooks.listen(WebhookApplication.DEFAULT_WEBHOOK_PORT) - - def on_instance_started(self): - self.log.debug("Got instance started callback") - - @asyncio.coroutine - def on_dts_state_change(self, state): - """Handle DTS state change - - Take action according to current DTS state to transition application - into the corresponding application state - - Arguments - state - current dts state - - """ - switch = { - rwdts.State.INIT: rwdts.State.REGN_COMPLETE, - rwdts.State.CONFIG: rwdts.State.RUN, - } - - handlers = { - rwdts.State.INIT: self.init, - rwdts.State.RUN: self.run, - } - - # Transition application to next state - handler = handlers.get(state, None) - if handler is not None: - yield from handler() - - # Transition dts to next state - next_state = switch.get(state, None) - if next_state is not None: - self.dts.handle.set_state(next_state) - def on_vnfr_create(self, vnfr): if not self.monitor.nfvi_metrics_available(vnfr.cloud_account): msg = "NFVI metrics unavailable for {}" @@ -712,3 +638,105 @@ class MonitorTasklet(rift.tasklets.Tasklet): """ yield from self.monitor.destroy_alarm(account, alarm_id) + + +class MonitorTasklet(rift.tasklets.Tasklet): + """ + The MonitorTasklet provides a interface for DTS to interact with an + instance of the Monitor class. This allows the Monitor class to remain + independent of DTS. + """ + + DEFAULT_POLLING_PERIOD = 1.0 + + def __init__(self, *args, **kwargs): + try: + super().__init__(*args, **kwargs) + self.rwlog.set_category("rw-monitor-log") + + self._project_handler = None + self.projects = {} + + self.webhooks = None + + except Exception as e: + self.log.exception(e) + + def start(self): + super().start() + self.log.info("Starting MonitoringTasklet") + + self.log.debug("Registering with dts") + self.dts = rift.tasklets.DTS( + self.tasklet_info, + RwLaunchpadYang.get_schema(), + self.loop, + self.on_dts_state_change + ) + + self.log.debug("Created DTS Api GI Object: %s", self.dts) + + def stop(self): + try: + self.dts.deinit() + except Exception as e: + self.log.exception(e) + + @asyncio.coroutine + def init(self): + self.log.debug("creating webhook server") + loop = rift.tasklets.tornado.TaskletAsyncIOLoop(asyncio_loop=self.loop) + self.webhooks = WebhookApplication(self) + self.server = tornado.httpserver.HTTPServer( + self.webhooks, + io_loop=loop, + ) + + @asyncio.coroutine + def on_public_ip(self, ip): + """Store the public IP of the launchpad + + Arguments: + ip - a string containing the public IP address of the launchpad + + """ + self.config.public_ip = ip + + @asyncio.coroutine + def run(self): + self.webhooks.listen(WebhookApplication.DEFAULT_WEBHOOK_PORT) + + def on_instance_started(self): + self.log.debug("Got instance started callback") + + @asyncio.coroutine + def on_dts_state_change(self, state): + """Handle DTS state change + + Take action according to current DTS state to transition application + into the corresponding application state + + Arguments + state - current dts state + + """ + switch = { + rwdts.State.INIT: rwdts.State.REGN_COMPLETE, + rwdts.State.CONFIG: rwdts.State.RUN, + } + + handlers = { + rwdts.State.INIT: self.init, + rwdts.State.RUN: self.run, + } + + # Transition application to next state + handler = handlers.get(state, None) + if handler is not None: + yield from handler() + + # Transition dts to next state + next_state = switch.get(state, None) + if next_state is not None: + self.dts.handle.set_state(next_state) + diff --git a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/nsr_core.py b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/nsr_core.py index 78a3c8fc..ce195803 100644 --- a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/nsr_core.py +++ b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/nsr_core.py @@ -42,22 +42,22 @@ class VnfrMonitoringParamSubscriber(mano_dts.AbstractOpdataSubscriber): monp_id (str): Monitoring Param ID vnfr_id (str): VNFR ID """ - def __init__(self, log, dts, loop, vnfr_id, monp_id, callback=None): - super().__init__(log, dts, loop, callback) + def __init__(self, log, dts, loop, project, vnfr_id, monp_id, callback=None): + super().__init__(log, dts, loop, project, callback) self.vnfr_id = vnfr_id self.monp_id = monp_id def get_xpath(self): - return("D,/vnfr:vnfr-catalog" + + return self.project.add_project(("D,/vnfr:vnfr-catalog" + "/vnfr:vnfr[vnfr:id='{}']".format(self.vnfr_id) + "/vnfr:monitoring-param" + - "[vnfr:id='{}']".format(self.monp_id)) + "[vnfr:id='{}']".format(self.monp_id))) class NsrMonitoringParam(): """Class that handles NS Mon-param data. """ - MonParamMsg = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam + MonParamMsg = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_MonitoringParam MISSING = None DEFAULT_AGGREGATION_TYPE = "AVERAGE" @@ -66,7 +66,7 @@ class NsrMonitoringParam(): """Convenience class that constructs NSMonitoringParam objects Args: - nsd (RwNsdYang.YangData_Nsd_NsdCatalog_Nsd): Nsd object + nsd (RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd): Nsd object constituent_vnfrs (list): List of constituent vnfr objects of NSR store (SubscriberStore): Store object instance @@ -76,8 +76,6 @@ class NsrMonitoringParam(): Also handles legacy NSD descriptor which has no mon-param defines. In such cases the mon-params are created from VNFD's mon-param config. """ - MonParamMsg = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_MonitoringParam - mon_params = [] for mon_param_msg in nsd.monitoring_param: mon_params.append(NsrMonitoringParam( @@ -278,16 +276,17 @@ class NsrMonitoringParamPoller(mano_dts.DtsHandler): def from_handler(cls, handler, monp, callback): """Convenience class to build NsrMonitoringParamPoller object. """ - return cls(handler.log, handler.dts, handler.loop, monp, callback) + return cls(handler.log, handler.dts, handler.loop, handler.project, + monp, callback) - def __init__(self, log, dts, loop, monp, callback=None): + def __init__(self, log, dts, loop, project, monp, callback=None): """ Args: monp (NsrMonitoringParam): Param object callback (None, optional): Callback to be triggered after value has been aggregated. """ - super().__init__(log, dts, loop) + super().__init__(log, dts, loop, project) self.monp = monp self.subscribers = [] @@ -341,7 +340,8 @@ class NsrMonitoringParamPoller(mano_dts.DtsHandler): for vnfr_id, monp_id in self.monp.vnfr_ids: callback = functools.partial(self.update_value, vnfr_id=vnfr_id) self.subscribers.append(VnfrMonitoringParamSubscriber( - self.loop, self.dts, self.loop, vnfr_id, monp_id, callback=callback)) + self.loop, self.dts, self.loop, self.project, + vnfr_id, monp_id, callback=callback)) @asyncio.coroutine def start(self): @@ -356,14 +356,14 @@ class NsrMonitoringParamPoller(mano_dts.DtsHandler): class NsrMonitorDtsHandler(mano_dts.DtsHandler): """ NSR monitoring class """ - def __init__(self, log, dts, loop, nsr, constituent_vnfrs, store): + def __init__(self, log, dts, loop, project, nsr, constituent_vnfrs, store): """ Args: - nsr (RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr): NSR object + nsr (RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr): NSR object constituent_vnfrs (list): list of VNFRs in NSR store (SubscriberStore): Store instance """ - super().__init__(log, dts, loop) + super().__init__(log, dts, loop, project) self.nsr = nsr self.store = store @@ -371,7 +371,7 @@ class NsrMonitorDtsHandler(mano_dts.DtsHandler): self.mon_params_pollers = [] def xpath(self, param_id=None): - return ("D,/nsr:ns-instance-opdata/nsr:nsr" + + return self.project.add_project("D,/nsr:ns-instance-opdata/nsr:nsr" + "[nsr:ns-instance-config-ref='{}']".format(self.nsr.ns_instance_config_ref) + "/nsr:monitoring-param" + ("[nsr:id='{}']".format(param_id) if param_id else "")) diff --git a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/rwmonparam.py b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/rwmonparam.py index 04e03061..2bfad6d1 100644 --- a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/rwmonparam.py +++ b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/rwmonparam.py @@ -34,21 +34,20 @@ from gi.repository import ( import rift.mano.cloud import rift.mano.dts as subscriber import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + ) from . import vnfr_core from . import nsr_core -class MonitoringParameterTasklet(rift.tasklets.Tasklet): - """The main task of this Tasklet is to listen for VNFR changes and once the - VNFR hits the running state, triggers the monitor. - """ - def __init__(self, *args, **kwargs): - try: - super().__init__(*args, **kwargs) - self.rwlog.set_category("rw-monitor-log") - except Exception as e: - self.log.exception(e) +class MonParamProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(MonParamProject, self).__init__(tasklet.log, name) + self.update(tasklet) self.vnfr_subscriber = None self.store = None @@ -59,77 +58,27 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): # Needs to be moved to store once the DTS bug is resolved self.vnfrs = {} - def start(self): - super().start() - - self.log.info("Starting MonitoringParameterTasklet") - self.log.debug("Registering with dts") - - self.dts = rift.tasklets.DTS( - self.tasklet_info, - RwLaunchpadYang.get_schema(), - self.loop, - self.on_dts_state_change - ) - - self.vnfr_subscriber = subscriber.VnfrCatalogSubscriber.from_tasklet( + self.vnfr_subscriber = subscriber.VnfrCatalogSubscriber.from_project( self, callback=self.handle_vnfr) - self.nsr_subsriber = subscriber.NsrCatalogSubscriber.from_tasklet( + self.nsr_subsriber = subscriber.NsrCatalogSubscriber.from_project( self, callback=self.handle_nsr) - self.store = subscriber.SubscriberStore.from_tasklet(self) + self.store = subscriber.SubscriberStore.from_project(self) self.log.debug("Created DTS Api GI Object: %s", self.dts) - def stop(self): - try: - self.dts.deinit() - except Exception as e: - self.log.exception(e) - @asyncio.coroutine - def init(self): + def register (self): self.log.debug("creating vnfr subscriber") yield from self.store.register() yield from self.vnfr_subscriber.register() yield from self.nsr_subsriber.register() - @asyncio.coroutine - def run(self): - pass - - @asyncio.coroutine - def on_dts_state_change(self, state): - """Handle DTS state change - - Take action according to current DTS state to transition application - into the corresponding application state - - Arguments - state - current dts state - - """ - switch = { - rwdts.State.INIT: rwdts.State.REGN_COMPLETE, - rwdts.State.CONFIG: rwdts.State.RUN, - } - - handlers = { - rwdts.State.INIT: self.init, - rwdts.State.RUN: self.run, - } - - # Transition application to next state - handler = handlers.get(state, None) - if handler is not None: - yield from handler() - - # Transition dts to next state - next_state = switch.get(state, None) - if next_state is not None: - self.dts.handle.set_state(next_state) + def deregister(self): + self.log.debug("De-register vnfr project {}".format(self.name)) + #TODO: def handle_vnfr(self, vnfr, action): """Starts a monitoring parameter job for every VNFR that reaches @@ -141,7 +90,6 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): """ def vnfr_create(): - # if vnfr.operational_status == "running" and vnfr.id not in self.vnfr_monitors: if vnfr.config_status == "configured" and vnfr.id not in self.vnfr_monitors: vnf_mon = vnfr_core.VnfMonitorDtsHandler.from_vnf_data( @@ -178,7 +126,7 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): NS that moves to config state. Args: - nsr (RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr): Ns Opdata + nsr (RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr): Ns Opdata action (rwdts.QueryAction): Action type of the change. """ def nsr_create(): @@ -188,6 +136,7 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): self.log, self.dts, self.loop, + self, nsr, list(self.vnfrs.values()), self.store @@ -197,8 +146,12 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def task(): - yield from nsr_mon.register() - yield from nsr_mon.start() + try: + yield from nsr_mon.register() + yield from nsr_mon.start() + except Exception as e: + self.log.exception("NSR {} monparam task failed: {}". + format(nsr.name_ref, e)) self.loop.create_task(task()) @@ -214,3 +167,78 @@ class MonitoringParameterTasklet(rift.tasklets.Tasklet): nsr_create() elif action == rwdts.QueryAction.DELETE: nsr_delete() + + +class MonitoringParameterTasklet(rift.tasklets.Tasklet): + """The main task of this Tasklet is to listen for VNFR changes and once the + VNFR hits the running state, triggers the monitor. + """ + def __init__(self, *args, **kwargs): + try: + super().__init__(*args, **kwargs) + self.rwlog.set_category("rw-monitor-log") + except Exception as e: + self.log.exception(e) + + self._project_handler = None + self.projects = {} + + def start(self): + super().start() + + self.log.info("Starting MonitoringParameterTasklet") + self.log.debug("Registering with dts") + + self.dts = rift.tasklets.DTS( + self.tasklet_info, + RwLaunchpadYang.get_schema(), + self.loop, + self.on_dts_state_change + ) + + def stop(self): + try: + self.dts.deinit() + except Exception as e: + self.log.exception(e) + + @asyncio.coroutine + def init(self): + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, MonParamProject) + self.project_handler.register() + + @asyncio.coroutine + def run(self): + pass + + @asyncio.coroutine + def on_dts_state_change(self, state): + """Handle DTS state change + + Take action according to current DTS state to transition application + into the corresponding application state + + Arguments + state - current dts state + + """ + switch = { + rwdts.State.INIT: rwdts.State.REGN_COMPLETE, + rwdts.State.CONFIG: rwdts.State.RUN, + } + + handlers = { + rwdts.State.INIT: self.init, + rwdts.State.RUN: self.run, + } + + # Transition application to next state + handler = handlers.get(state, None) + if handler is not None: + yield from handler() + + # Transition dts to next state + next_state = switch.get(state, None) + if next_state is not None: + self.dts.handle.set_state(next_state) diff --git a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/vnfr_core.py b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/vnfr_core.py index 6dc3a25b..335e957d 100644 --- a/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/vnfr_core.py +++ b/rwlaunchpad/plugins/rwmonparam/rift/tasklets/rwmonparam/vnfr_core.py @@ -616,29 +616,29 @@ class VnfMonitorDtsHandler(mano_dts.DtsHandler): XPATH = "D,/vnfr:vnfr-catalog/vnfr:vnfr/vnfr:monitoring-param" @classmethod - def from_vnf_data(cls, tasklet, vnfr_msg, vnfd_msg): - handler = cls(tasklet.log, tasklet.dts, tasklet.loop, + def from_vnf_data(cls, project, vnfr_msg, vnfd_msg): + handler = cls(project.log, project.dts, project.loop, project, vnfr_msg.id, vnfr_msg.mgmt_interface.ip_address, vnfd_msg.monitoring_param, vnfd_msg.http_endpoint) return handler - def __init__(self, log, dts, loop, vnfr_id, mgmt_ip, params, endpoints): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project, vnfr_id, mgmt_ip, params, endpoints): + super().__init__(log, dts, loop, project) self._mgmt_ip = mgmt_ip self._vnfr_id = vnfr_id mon_params = [] for mon_param in params: - param = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam.from_dict( + param = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam.from_dict( mon_param.as_dict() ) mon_params.append(param) http_endpoints = [] for endpoint in endpoints: - endpoint = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint.from_dict( + endpoint = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_HttpEndpoint.from_dict( endpoint.as_dict() ) http_endpoints.append(endpoint) @@ -674,10 +674,10 @@ class VnfMonitorDtsHandler(mano_dts.DtsHandler): def xpath(self, param_id=None): """ Monitoring params xpath """ - return("D,/vnfr:vnfr-catalog" + + return self.project.add_project(("D,/vnfr:vnfr-catalog" + "/vnfr:vnfr[vnfr:id='{}']".format(self._vnfr_id) + "/vnfr:monitoring-param" + - ("[vnfr:id='{}']".format(param_id) if param_id else "")) + ("[vnfr:id='{}']".format(param_id) if param_id else ""))) @property def msg(self): diff --git a/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py b/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py index fd48952d..c99fc007 100755 --- a/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py +++ b/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params.py @@ -54,7 +54,7 @@ class MonParamsPingStatsTest(AsyncioTornadoTest): 'ping-response-rx-count': 10 } - mon_param_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam() + mon_param_msg = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam() mon_param_msg.from_dict({ 'id': '1', 'name': 'ping-request-tx-count', @@ -67,7 +67,7 @@ class MonParamsPingStatsTest(AsyncioTornadoTest): 'units': 'packets' }) - endpoint_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint() + endpoint_msg = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_HttpEndpoint() endpoint_msg.from_dict({ 'path': ping_path, 'polling_interval_secs': 1, @@ -231,7 +231,7 @@ class MonParamsPingStatsHttpsTest(AsyncioTornadoHttpsTest): 'ping-response-rx-count': 10 } - mon_param_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam() + mon_param_msg = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam() mon_param_msg.from_dict({ 'id': '1', 'name': 'ping-request-tx-count', @@ -244,7 +244,7 @@ class MonParamsPingStatsHttpsTest(AsyncioTornadoHttpsTest): 'units': 'packets' }) - endpoint_msg = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint() + endpoint_msg = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_HttpEndpoint() endpoint_msg.from_dict({ 'path': ping_path, 'https': 'true', diff --git a/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params_dts.py b/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params_dts.py index fb0b0393..80d9387f 100644 --- a/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params_dts.py +++ b/rwlaunchpad/plugins/rwmonparam/test/utest_mon_params_dts.py @@ -50,7 +50,7 @@ import utest_mon_params class MonParamMsgGenerator(object): def __init__(self, num_messages=1): ping_path = r"/api/v1/ping/stats" - self._endpoint_msg = vnfryang.YangData_Vnfr_VnfrCatalog_Vnfr_HttpEndpoint.from_dict({ + self._endpoint_msg = vnfryang.YangData_RwProject_Project_VnfrCatalog_Vnfr_HttpEndpoint.from_dict({ 'path': ping_path, 'https': 'true', 'polling_interval_secs': 1, @@ -61,7 +61,7 @@ class MonParamMsgGenerator(object): self._mon_param_msgs = [] for i in range(1, num_messages): - self._mon_param_msgs.append(vnfryang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam.from_dict({ + self._mon_param_msgs.append(vnfryang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam.from_dict({ 'id': '%s' % i, 'name': 'param_num_%s' % i, 'json_query_method': "NAMEKEY", @@ -127,7 +127,7 @@ class MonParamsDtsTestCase(rift.test.dts.AbstractDTSTest): def setup_mock_store(self, aggregation_type, monps, legacy=False): store = mock.MagicMock() - mock_vnfd = RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd.from_dict({ + mock_vnfd = RwVnfdYang.YangData_RwProject_Project_VnfdCatalog_Vnfd.from_dict({ 'id': "1", 'monitoring_param': [ {'description': 'no of ping requests', @@ -151,14 +151,14 @@ class MonParamsDtsTestCase(rift.test.dts.AbstractDTSTest): }) store.get_vnfd = mock.MagicMock(return_value=mock_vnfd) - mock_vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({ + mock_vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.from_dict({ 'id': '1', 'monitoring_param': ([monp.as_dict() for monp in monps] if not legacy else []) }) - mock_vnfr.vnfd = vnfryang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict({'id': '1'}) + mock_vnfr.vnfd = vnfryang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vnfd.from_dict({'id': '1'}) store.get_vnfr = mock.MagicMock(return_value=mock_vnfr) - mock_nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.from_dict({ + mock_nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr.from_dict({ 'ns_instance_config_ref': "1", 'name_ref': "Foo", 'constituent_vnfr_ref': [{'vnfr_id': mock_vnfr.id}], @@ -182,7 +182,7 @@ class MonParamsDtsTestCase(rift.test.dts.AbstractDTSTest): 'vnfd_monitoring_param_ref': '2'}] }] - mock_nsd = RwNsdYang.YangData_Nsd_NsdCatalog_Nsd.from_dict({ + mock_nsd = RwNsdYang.YangData_RwProject_Project_NsdCatalog_Nsd.from_dict({ 'id': str(uuid.uuid1()), 'monitoring_param': (monp if not legacy else []) }) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/cloud.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/cloud.py index 007e62ca..ef7856d1 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/cloud.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/cloud.py @@ -128,15 +128,17 @@ class NsmPlugins(object): class CloudAccountConfigSubscriber: - def __init__(self, log, dts, log_hdl): + def __init__(self, log, dts, log_hdl, project): self._dts = dts self._log = log self._log_hdl = log_hdl + self._project = project self._cloud_sub = rift.mano.cloud.CloudAccountConfigSubscriber( self._dts, self._log, self._log_hdl, + self._project, rift.mano.cloud.CloudAccountConfigCallbacks()) def get_cloud_account_sdn_name(self, account_name): @@ -154,6 +156,9 @@ class CloudAccountConfigSubscriber: def register(self): self._cloud_sub.register() + def deregister(self): + self._cloud_sub.deregister() + class ROAccountPluginSelector(object): """ @@ -166,10 +171,11 @@ class ROAccountPluginSelector(object): """ DEFAULT_PLUGIN = RwNsPlugin - def __init__(self, dts, log, loop, records_publisher): + def __init__(self, dts, log, loop, project, records_publisher): self._dts = dts self._log = log self._loop = loop + self._project = project self._records_publisher = records_publisher self._nsm_plugins = NsmPlugins() @@ -178,12 +184,14 @@ class ROAccountPluginSelector(object): self._log, self._dts, self._loop, + self._project, callback=self.on_ro_account_change ) self._nsr_sub = mano_dts.NsrCatalogSubscriber( self._log, self._dts, self._loop, + self._project, self.handle_nsr) # The default plugin will be RwNsPlugin @@ -240,3 +248,8 @@ class ROAccountPluginSelector(object): def register(self): yield from self._ro_sub.register() yield from self._nsr_sub.register() + + def deregister(self): + self._log.debug("Project {} de-register".format(self._project.name)) + self._ro_sub.deregister() + self._nsr_sub.deregister() diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py index d53d701d..0fcd905e 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py @@ -102,7 +102,7 @@ class VnfrConsoleOperdataDtsHandler(object): ) if action == rwdts.QueryAction.READ: - schema = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur.schema() + schema = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur.schema() path_entry = schema.keyspec_to_entry(ks_path) try: @@ -118,7 +118,7 @@ class VnfrConsoleOperdataDtsHandler(object): self._nsr._nsr_uuid, self._vdur_id ) - vdur_console = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur() + vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur() vdur_console.id = self._vdur_id if console_url: vdur_console.console_url = console_url @@ -128,7 +128,7 @@ class VnfrConsoleOperdataDtsHandler(object): except openmano_client.InstanceStatusError as e: self._log.error("Could not get NS instance console URL: %s", str(e)) - vdur_console = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur() + vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur() vdur_console.id = self._vdur_id vdur_console.console_url = 'none' @@ -885,7 +885,7 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase): def vnfr_uptime_update(self, vnfr): try: - vnfr_ = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({'id': vnfr.id}) + vnfr_ = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.from_dict({'id': vnfr.id}) while True: vnfr_.uptime = int(time.time()) - vnfr._create_time yield from self._publisher.publish_vnfr(None, vnfr_) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/publisher.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/publisher.py index 6c4b1237..e3ae5cf1 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/publisher.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/publisher.py @@ -33,10 +33,11 @@ class NsrOpDataDtsHandler(object): """ The network service op data DTS handler """ XPATH = "D,/nsr:ns-instance-opdata/nsr:nsr" - def __init__(self, dts, log, loop): + def __init__(self, dts, log, loop, project): self._dts = dts self._log = log self._loop = loop + self._project = project self._regh = None @property @@ -47,38 +48,45 @@ class NsrOpDataDtsHandler(object): @asyncio.coroutine def register(self): """ Register for Nsr op data publisher registration""" - self._log.debug("Registering Nsr op data path %s as publisher", - NsrOpDataDtsHandler.XPATH) + if self._regh: + return + + xpath = self._project.add_project(NsrOpDataDtsHandler.XPATH) + self._log.debug("Registering Nsr op data path {} as publisher". + format(xpath)) hdl = rift.tasklets.DTS.RegistrationHandler() with self._dts.group_create() as group: - self._regh = group.register(xpath=NsrOpDataDtsHandler.XPATH, + self._regh = group.register(xpath=xpath, handler=hdl, flags=rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ) @asyncio.coroutine - def create(self, xact, path, msg): + def create(self, xact, xpath, msg): """ Create an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Creating NSR xact = %s, %s:%s", xact, path, msg) self.regh.create_element(path, msg) self._log.debug("Created NSR xact = %s, %s:%s", xact, path, msg) @asyncio.coroutine - def update(self, xact, path, msg, flags=rwdts.XactFlag.REPLACE): + def update(self, xact, xpath, msg, flags=rwdts.XactFlag.REPLACE): """ Update an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Updating NSR xact = %s, %s:%s regh = %s", xact, path, msg, self.regh) self.regh.update_element(path, msg, flags) self._log.debug("Updated NSR xact = %s, %s:%s", xact, path, msg) @asyncio.coroutine - def delete(self, xact, path): + def delete(self, xact, xpath): """ Update an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Deleting NSR xact:%s, path:%s", xact, path) self.regh.delete_element(path) self._log.debug("Deleted NSR xact:%s, path:%s", xact, path) @@ -86,13 +94,14 @@ class NsrOpDataDtsHandler(object): class VnfrPublisherDtsHandler(object): - """ Registers 'D,/vnfr:vnfr-catalog/vnfr:vnfr' DTS""" + """ Registers 'D,/rw-project:project/vnfr:vnfr-catalog/vnfr:vnfr' DTS""" XPATH = "D,/vnfr:vnfr-catalog/vnfr:vnfr" - def __init__(self, dts, log, loop): + def __init__(self, dts, log, loop, project): self._dts = dts self._log = log self._loop = loop + self._project = project self._regh = None @@ -104,6 +113,8 @@ class VnfrPublisherDtsHandler(object): @asyncio.coroutine def register(self): """ Register for Vvnfr create/update/delete/read requests from dts """ + if self._regh: + return @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): @@ -116,12 +127,13 @@ class VnfrPublisherDtsHandler(object): "%s action on VirtualNetworkFunctionRecord not supported", action) - self._log.debug("Registering for VNFR using xpath: %s", - VnfrPublisherDtsHandler.XPATH,) + xpath = self._project.add_project(VnfrPublisherDtsHandler.XPATH) + self._log.debug("Registering for VNFR using xpath: {}". + format(xpath)) hdl = rift.tasklets.DTS.RegistrationHandler() with self._dts.group_create() as group: - self._regh = group.register(xpath=VnfrPublisherDtsHandler.XPATH, + self._regh = group.register(xpath=xpath, handler=hdl, flags=(rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | @@ -160,13 +172,14 @@ class VnfrPublisherDtsHandler(object): class VlrPublisherDtsHandler(object): - """ registers 'D,/vlr:vlr-catalog/vlr:vlr """ + """ registers 'D,/rw-project:project/vlr:vlr-catalog/vlr:vlr """ XPATH = "D,/vlr:vlr-catalog/vlr:vlr" - def __init__(self, dts, log, loop): + def __init__(self, dts, log, loop, project): self._dts = dts self._log = log self._loop = loop + self._project = project self._regh = None @@ -179,6 +192,9 @@ class VlrPublisherDtsHandler(object): def register(self): """ Register for vlr create/update/delete/read requests from dts """ + if self._regh: + return + @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): """ prepare callback from dts """ @@ -190,12 +206,13 @@ class VlrPublisherDtsHandler(object): "%s action on VirtualLinkRecord not supported", action) - self._log.debug("Registering for VLR using xpath: %s", - VlrPublisherDtsHandler.XPATH,) + xpath = self._project.add_project(VlrPublisherDtsHandler.XPATH) + self._log.debug("Registering for VLR using xpath: {}". + format(xpath)) hdl = rift.tasklets.DTS.RegistrationHandler() with self._dts.group_create() as group: - self._regh = group.register(xpath=VlrPublisherDtsHandler.XPATH, + self._regh = group.register(xpath=xpath, handler=hdl, flags=(rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | @@ -238,10 +255,11 @@ class VnfdPublisher(object): HEADERS = {"content-type": "application/vnd.yang.data+json"} - def __init__(self, use_ssl, ssl_cert, ssl_key, loop): + def __init__(self, use_ssl, ssl_cert, ssl_key, loop, project): self.use_ssl = use_ssl self.ssl_cert = ssl_cert self.ssl_key = ssl_key + self._project = project self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) self.loop = loop @@ -255,7 +273,7 @@ class VnfdPublisher(object): scheme = "https" if self.use_ssl else "http" - url = "{}://127.0.0.1:8008/api/config/vnfd-catalog/vnfd/{}" + url = "{}://127.0.0.1:8008/api/config/project/{}/vnfd-catalog/vnfd/{}" model = RwYang.Model.create_libncx() model.load_module("rw-vnfd") @@ -277,7 +295,7 @@ class VnfdPublisher(object): options["cert"] = (self.ssl_cert, self.ssl_key) response = requests.put( - url.format(scheme, vnfd.id), + url.format(scheme, self._project.name, vnfd.id), **options ) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py index 23ab7b6e..1988cc4a 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py @@ -46,6 +46,7 @@ class ROConfigManager(object): self._loop = loop self._dts = dts self.nsm = parent + self.project = parent._project self._log.debug("Initialized ROConfigManager") def is_ready(self): @@ -53,7 +54,7 @@ class ROConfigManager(object): @property def cm_state_xpath(self): - return ("/rw-conman:cm-state/rw-conman:cm-nsr") + return self.project.add_project("/rw-conman:cm-state/rw-conman:cm-nsr") @classmethod def map_config_status(cls, status): diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py index 3153a48c..0797b558 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py @@ -61,6 +61,12 @@ import rift.tasklets import rift.mano.ncclient import rift.mano.config_data.config import rift.mano.dts as mano_dts +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + get_add_delete_update_cfgs, + DEFAULT_PROJECT, + ) from . import rwnsm_conman as conman from . import cloud @@ -225,7 +231,7 @@ class VnffgRecord(object): "sdn_account": self._sdn_account_name, "operational_status": 'init', } - vnffgr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) + vnffgr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) elif self._vnffgr_state == VnffgRecordState.TERMINATED: vnffgr_dict = {"id": self._vnffgr_id, "vnffgd_id_ref": self._vnffgd_msg.id, @@ -233,7 +239,7 @@ class VnffgRecord(object): "sdn_account": self._sdn_account_name, "operational_status": 'terminated', } - vnffgr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) + vnffgr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) else: try: vnffgr = self._vnffgmgr.fetch_vnffgr(self._vnffgr_id) @@ -246,7 +252,7 @@ class VnffgRecord(object): "sdn_account": self._sdn_account_name, "operational_status": 'failed', } - vnffgr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) + vnffgr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) return vnffgr @@ -258,7 +264,7 @@ class VnffgRecord(object): "vnffgd_name_ref": self._vnffgd_msg.name, "sdn_account": self._sdn_account_name, } - vnffgr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) + vnffgr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_Vnffgr.from_dict(vnffgr_dict) for rsp in self._vnffgd_msg.rsp: vnffgr_rsp = vnffgr.rsp.add() vnffgr_rsp.id = str(uuid.uuid4()) @@ -270,9 +276,11 @@ class VnffgRecord(object): vnfd = [vnfr.vnfd for vnfr in self._nsr.vnfrs.values() if vnfr.vnfd.id == rsp_cp_ref.vnfd_id_ref] self._log.debug("VNFD message during VNFFG instantiation is %s",vnfd) if len(vnfd) > 0 and vnfd[0].has_field('service_function_type'): - self._log.debug("Service Function Type for VNFD ID %s is %s",rsp_cp_ref.vnfd_id_ref, vnfd[0].service_function_type) + self._log.debug("Service Function Type for VNFD ID %s is %s", + rsp_cp_ref.vnfd_id_ref, vnfd[0].service_function_type) else: - self._log.error("Service Function Type not available for VNFD ID %s; Skipping in chain",rsp_cp_ref.vnfd_id_ref) + self._log.error("Service Function Type not available for VNFD ID %s; Skipping in chain", + rsp_cp_ref.vnfd_id_ref) continue vnfr_cp_ref = vnffgr_rsp.vnfr_connection_point_ref.add() @@ -293,7 +301,8 @@ class VnffgRecord(object): self._log.info("Received vnf op status is %s; retrying",vnfr.operational_status) if vnfr.operational_status == 'failed': self._log.error("Fetching VNFR for %s failed", vnfr.id) - raise NsrInstantiationFailed("Failed NS %s instantiation due to VNFR %s failure" % (self.id, vnfr.id)) + raise NsrInstantiationFailed("Failed NS %s instantiation due to VNFR %s failure" % + (self.id, vnfr.id)) yield from asyncio.sleep(2, loop=self._loop) vnfr = yield from self._nsr.fetch_vnfr(nsr_vnfr.xpath) self._log.debug("Received VNFR is %s", vnfr) @@ -320,7 +329,8 @@ class VnffgRecord(object): rsp_id_ref = _rsp[0].id rsp_name = _rsp[0].name else: - self._log.error("RSP with ID %s not found during classifier creation for classifier id %s",vnffgd_classifier.rsp_id_ref,vnffgd_classifier.id) + self._log.error("RSP with ID %s not found during classifier creation for classifier id %s", + vnffgd_classifier.rsp_id_ref,vnffgd_classifier.id) continue vnffgr_classifier = vnffgr.classifier.add() vnffgr_classifier.id = vnffgd_classifier.id @@ -344,7 +354,8 @@ class VnffgRecord(object): self._log.info("Received vnf op status is %s; retrying",vnfr.operational_status) if vnfr.operational_status == 'failed': self._log.error("Fetching VNFR for %s failed", vnfr.id) - raise NsrInstantiationFailed("Failed NS %s instantiation due to VNFR %s failure" % (self.id, vnfr.id)) + raise NsrInstantiationFailed("Failed NS %s instantiation due to VNFR %s failure" % + (self.id, vnfr.id)) yield from asyncio.sleep(2, loop=self._loop) vnfr = yield from self._nsr.fetch_vnfr(nsr_vnfr.xpath) self._log.debug("Received VNFR is %s", vnfr) @@ -357,8 +368,9 @@ class VnffgRecord(object): for ext_intf in vdu.external_interface: if ext_intf.name == vnffgr_classifier.vnfr_connection_point_ref: vnffgr_classifier.vm_id = vdu.vim_id - self._log.debug("VIM ID for CP %s in VNFR %s is %s",cp.name,nsr_vnfr.id, - vnfr_cp_ref.connection_point_params.vm_id) + self._log.debug("VIM ID for CP %s in VNFR %s is %s", + cp.name,nsr_vnfr.id, + vnfr_cp_ref.connection_point_params.vm_id) break self._log.info("VNFFGR msg to be sent is %s", vnffgr) @@ -459,7 +471,9 @@ class VirtualLinkRecord(object): XPATH = "D,/vlr:vlr-catalog/vlr:vlr" @staticmethod @asyncio.coroutine - def create_record(dts, log, loop, nsr_name, vld_msg, cloud_account_name, om_datacenter, ip_profile, nsr_id, restart_mode=False): + def create_record(dts, log, loop, project, nsr_name, vld_msg, + cloud_account_name, om_datacenter, ip_profile, + nsr_id, restart_mode=False): """Creates a new VLR object based on the given data. If restart mode is enabled, then we look for existing records in the @@ -472,6 +486,7 @@ class VirtualLinkRecord(object): dts, log, loop, + project, nsr_name, vld_msg, cloud_account_name, @@ -482,7 +497,7 @@ class VirtualLinkRecord(object): if restart_mode: res_iter = yield from dts.query_read( - "D,/vlr:vlr-catalog/vlr:vlr", + project.add_project("D,/vlr:vlr-catalog/vlr:vlr"), rwdts.XactFlag.MERGE) for fut in res_iter: @@ -498,10 +513,12 @@ class VirtualLinkRecord(object): return vlr_obj - def __init__(self, dts, log, loop, nsr_name, vld_msg, cloud_account_name, om_datacenter, ip_profile, nsr_id): + def __init__(self, dts, log, loop, project, nsr_name, vld_msg, + cloud_account_name, om_datacenter, ip_profile, nsr_id): self._dts = dts self._log = log self._loop = loop + self._project = project self._nsr_name = nsr_name self._vld_msg = vld_msg self._cloud_account_name = cloud_account_name @@ -517,7 +534,8 @@ class VirtualLinkRecord(object): @property def xpath(self): """ path for this object """ - return "D,/vlr:vlr-catalog/vlr:vlr[vlr:id = '{}']".format(self._vlr_id) + return self._project.add_project("D,/vlr:vlr-catalog/vlr:vlr[vlr:id = '{}']". + format(self._vlr_id)) @property def id(self): @@ -615,7 +633,7 @@ class VirtualLinkRecord(object): vlr_dict['ip_profile_params' ] = self._ip_profile.ip_profile_params.as_dict() vlr_dict.update(vld_copy_dict) - vlr = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr.from_dict(vlr_dict) + vlr = RwVlrYang.YangData_RwProject_Project_VlrCatalog_Vlr.from_dict(vlr_dict) return vlr def reset_id(self, vlr_id): @@ -623,7 +641,7 @@ class VirtualLinkRecord(object): def create_nsr_vlr_msg(self, vnfrs): """ The VLR message""" - nsr_vlr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_Vlr() + nsr_vlr = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_Vlr() nsr_vlr.vlr_ref = self._vlr_id nsr_vlr.assigned_subnet = self.assigned_subnet nsr_vlr.cloud_account = self.cloud_account_name @@ -721,7 +739,7 @@ class VirtualNetworkFunctionRecord(object): @staticmethod @asyncio.coroutine - def create_record(dts, log, loop, vnfd, const_vnfd_msg, nsd_id, nsr_name, + def create_record(dts, log, loop, project, vnfd, const_vnfd_msg, nsd_id, nsr_name, cloud_account_name, om_datacenter_name, nsr_id, group_name, group_instance_id, placement_groups, restart_mode=False): """Creates a new VNFR object based on the given data. @@ -736,6 +754,7 @@ class VirtualNetworkFunctionRecord(object): dts, log, loop, + project, vnfd, const_vnfd_msg, nsd_id, @@ -750,7 +769,7 @@ class VirtualNetworkFunctionRecord(object): if restart_mode: res_iter = yield from dts.query_read( - "D,/vnfr:vnfr-catalog/vnfr:vnfr", + project.add_project("D,/vnfr:vnfr-catalog/vnfr:vnfr"), rwdts.XactFlag.MERGE) for fut in res_iter: @@ -767,6 +786,7 @@ class VirtualNetworkFunctionRecord(object): dts, log, loop, + project, vnfd, const_vnfd_msg, nsd_id, @@ -781,6 +801,7 @@ class VirtualNetworkFunctionRecord(object): self._dts = dts self._log = log self._loop = loop + self._project = project self._vnfd = vnfd self._const_vnfd_msg = const_vnfd_msg self._nsd_id = nsd_id @@ -820,7 +841,8 @@ class VirtualNetworkFunctionRecord(object): @property def xpath(self): """ VNFR xpath """ - return "D,/vnfr:vnfr-catalog/vnfr:vnfr[vnfr:id = '{}']".format(self.id) + return self._project.add_project("D,/vnfr:vnfr-catalog/vnfr:vnfr[vnfr:id = '{}']" + .format(self.id)) @property def vnfr_msg(self): @@ -830,7 +852,9 @@ class VirtualNetworkFunctionRecord(object): @property def const_vnfr_msg(self): """ VNFR message """ - return RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConstituentVnfrRef(vnfr_id=self.id,cloud_account=self.cloud_account_name,om_datacenter=self._om_datacenter_name) + return RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ConstituentVnfrRef( + vnfr_id=self.id, cloud_account=self.cloud_account_name, + om_datacenter=self._om_datacenter_name) @property def vnfd(self): @@ -896,7 +920,8 @@ class VirtualNetworkFunctionRecord(object): @staticmethod def vnfr_xpath(vnfr): """ Get the VNFR path from VNFR """ - return (VirtualNetworkFunctionRecord.XPATH + "[vnfr:id = '{}']").format(vnfr.id) + return (self._project.add_project(VirtualNetworkFunctionRecord.XPATH) + + "[vnfr:id = '{}']").format(vnfr.id) @property def config_type(self): @@ -956,10 +981,11 @@ class VirtualNetworkFunctionRecord(object): } vnfr_dict.update(vnfd_copy_dict) - vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict) - vnfr.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict(), - ignore_missing_keys=True) - vnfr.member_vnf_index_ref = self.member_vnf_index + vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.from_dict(vnfr_dict) + vnfr.vnfd = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vnfd. \ + from_dict(self.vnfd.as_dict(), + ignore_missing_keys=True) + vnfr.member_vnf_index_ref = str(self.member_vnf_index) vnfr.vnf_configuration.from_dict(self._vnfd.vnf_configuration.as_dict()) if self._vnfd.mgmt_interface.has_field("port"): @@ -981,7 +1007,7 @@ class VirtualNetworkFunctionRecord(object): format(self.name, self.vnfr_msg)) yield from self._dts.query_update( self.xpath, - rwdts.XactFlag.TRACE, + 0, #rwdts.XactFlag.TRACE, self.vnfr_msg ) @@ -1069,7 +1095,7 @@ class VirtualNetworkFunctionRecord(object): # For every connection point in the VNFD fill in the identifier for conn_p in self._vnfd.connection_point: - cpr = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_ConnectionPoint() + cpr = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_ConnectionPoint() cpr.name = conn_p.name cpr.type_yang = conn_p.type_yang if conn_p.has_field('port_security_enabled'): @@ -1222,7 +1248,7 @@ class NetworkServiceStatus(object): event_list = [] idx = 1 for entry in self._events: - event = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_OperationalEvents() + event = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_OperationalEvents() event.id = idx idx += 1 event.timestamp, event.event, event.description, event.details = entry @@ -1234,7 +1260,8 @@ class NetworkServiceRecord(object): """ Network service record """ XPATH = "D,/nsr:ns-instance-opdata/nsr:nsr" - def __init__(self, dts, log, loop, nsm, nsm_plugin, nsr_cfg_msg, sdn_account_name, key_pairs, restart_mode=False, + def __init__(self, dts, log, loop, nsm, nsm_plugin, nsr_cfg_msg, + sdn_account_name, key_pairs, project, restart_mode=False, vlr_handler=None): self._dts = dts self._log = log @@ -1244,6 +1271,7 @@ class NetworkServiceRecord(object): self._nsm_plugin = nsm_plugin self._sdn_account_name = sdn_account_name self._vlr_handler = vlr_handler + self._project = project self._nsd = None self._nsr_msg = None @@ -1277,7 +1305,7 @@ class NetworkServiceRecord(object): self.set_state(NetworkServiceRecordState.INIT) - self.substitute_input_parameters = InputParameterSubstitution(self._log) + self.substitute_input_parameters = InputParameterSubstitution(self._log, self._project) @property def nsm_plugin(self): @@ -1393,7 +1421,7 @@ class NetworkServiceRecord(object): for group_info in self._nsr_cfg_msg.nsd_placement_group_maps: if group_info.placement_group_ref == input_group.name: - group = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_PlacementGroupsInfo() + group = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_PlacementGroupsInfo() group_dict = {k:v for k,v in group_info.as_dict().items() if k != 'placement_group_ref'} for param in copy_dict: @@ -1465,14 +1493,16 @@ class NetworkServiceRecord(object): def vlr_uptime_update(self, vlr): try: - vlr_ = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr.from_dict({'id': vlr.id}) + vlr_ = RwVlrYang.YangData_RwProject_Project_VlrCatalog_Vlr.from_dict({'id': vlr.id}) while True: vlr_.uptime = int(time.time()) - vlr._create_time - yield from self._vlr_handler.update(None, VirtualLinkRecord.vlr_xpath(vlr), vlr_) + xpath = self._project.add_project(VirtualLinkRecord.vlr_xpath(vlr)) + yield from self._vlr_handler.update(None, xpath, vlr_) yield from asyncio.sleep(2, loop=self._loop) except asyncio.CancelledError: self._log.debug("Received cancellation request for vlr_uptime_update task") - yield from self._vlr_handler.delete(None, VirtualLinkRecord.vlr_xpath(vlr)) + xpath = self._project.add_project(VirtualLinkRecord.vlr_xpath(vlr)) + yield from self._vlr_handler.delete(None, xpath) @asyncio.coroutine @@ -1850,6 +1880,7 @@ class NetworkServiceRecord(object): self._dts, self._log, self._loop, + self._project, self.name, vld, cloud_account, @@ -2033,6 +2064,7 @@ class NetworkServiceRecord(object): vnfr = yield from VirtualNetworkFunctionRecord.create_record(self._dts, self._log, self._loop, + self._project, vnfd_msg, const_vnfd, self.nsd_id, @@ -2188,23 +2220,23 @@ class NetworkServiceRecord(object): @property def nsr_xpath(self): """ Returns the xpath associated with this NSR """ - return( + return self._project.add_project(( "D,/nsr:ns-instance-opdata" + "/nsr:nsr[nsr:ns-instance-config-ref = '{}']" - ).format(self.id) + ).format(self.id)) @staticmethod def xpath_from_nsr(nsr): """ Returns the xpath associated with this NSR op data""" - return (NetworkServiceRecord.XPATH + - "[nsr:ns-instance-config-ref = '{}']").format(nsr.id) + return self._project.add_project((NetworkServiceRecord.XPATH + + "[nsr:ns-instance-config-ref = '{}']").format(nsr.id)) @property def nsd_xpath(self): """ Return NSD config xpath.""" - return( + return self._project.add_project(( "C,/nsd:nsd-catalog/nsd:nsd[nsd:id = '{}']" - ).format(self.nsd_id) + ).format(self.nsd_id)) @asyncio.coroutine def instantiate(self, config_xact): @@ -2486,7 +2518,7 @@ class NetworkServiceRecord(object): def create_msg(self): """ The network serice record as a message """ nsr_dict = {"ns_instance_config_ref": self.id} - nsr = RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr.from_dict(nsr_dict) + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr.from_dict(nsr_dict) #nsr.cloud_account = self.cloud_account_name nsr.sdn_account = self._sdn_account_name nsr.name_ref = self.name @@ -2500,7 +2532,7 @@ class NetworkServiceRecord(object): nsr.uptime = int(time.time()) - self._create_time for cfg_prim in self.nsd_msg.service_primitive: - cfg_prim = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ServicePrimitive.from_dict( + cfg_prim = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ServicePrimitive.from_dict( cfg_prim.as_dict()) nsr.service_primitive.append(cfg_prim) @@ -2640,7 +2672,7 @@ class InputParameterSubstitution(object): This class is responsible for substituting input parameters into an NSD. """ - def __init__(self, log): + def __init__(self, log, project): """Create an instance of InputParameterSubstitution Arguments: @@ -2648,6 +2680,7 @@ class InputParameterSubstitution(object): """ self.log = log + self.project = project def __call__(self, nsd, nsr_config): """Substitutes input parameters from the NSR config into the NSD @@ -2667,7 +2700,7 @@ class InputParameterSubstitution(object): # to be modified optional_input_parameters = set() for input_parameter in nsd.input_parameter_xpath: - optional_input_parameters.add(input_parameter.xpath) + optional_input_parameters.add(self.project.add_project(input_parameter.xpath)) # Apply the input parameters to the descriptor if nsr_config.input_parameter: @@ -2746,7 +2779,9 @@ class NetworkServiceDescriptor(object): @staticmethod def path_for_id(nsd_id): """ Return path for the passed nsd_id""" - return "C,/nsd:nsd-catalog/nsd:nsd[nsd:id = '{}'".format(nsd_id) + return self._nsm._project.add_project( + "C,/nsd:nsd-catalog/nsd:nsd[nsd:id = '{}'". + format(nsd_id)) def path(self): """ Return the message associated with this NetworkServiceDescriptor""" @@ -2768,6 +2803,7 @@ class NsdDtsHandler(object): self._nsm = nsm self._regh = None + self._project = nsm._project @property def regh(self): @@ -2778,6 +2814,11 @@ class NsdDtsHandler(object): def register(self): """ Register for Nsd create/update/delete/read requests from dts """ + if self._regh: + self._log.warning("DTS handler already registered for project {}". + format(self._project.name)) + return + def on_apply(dts, acg, xact, action, scratch): """Apply the configuration""" is_recovery = xact.xact is None and action == rwdts.AppconfAction.INSTALL @@ -2805,7 +2846,7 @@ class NsdDtsHandler(object): except Exception as e: self._log.error("Exception in cleaning up NSD libs {}: {}". format(nsd_id, e)) - self._log.excpetion(e) + self._log.exception(e) @asyncio.coroutine def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch): @@ -2845,10 +2886,17 @@ class NsdDtsHandler(object): # Need a list in scratch to store NSDs to create/update later # acg._scratch['nsds'] = list() self._regh = acg.register( - xpath=NsdDtsHandler.XPATH, + xpath=self._project.add_project(NsdDtsHandler.XPATH), flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, on_prepare=on_prepare) + def deregister(self): + self._log.debug("De-register NSD handler for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + class VnfdDtsHandler(object): """ DTS handler for VNFD config changes """ @@ -2860,6 +2908,7 @@ class VnfdDtsHandler(object): self._loop = loop self._nsm = nsm self._regh = None + self._project = nsm._project @property def regh(self): @@ -2870,6 +2919,11 @@ class VnfdDtsHandler(object): def register(self): """ Register for VNFD configuration""" + if self._regh: + self._log.warning("DTS handler already registered for project {}". + format(self._project.name)) + return + @asyncio.coroutine def on_apply(dts, acg, xact, action, scratch): """Apply the configuration""" @@ -2910,20 +2964,28 @@ class VnfdDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) + xpath = self._project.add_project(VnfdDtsHandler.XPATH) self._log.debug( - "Registering for VNFD config using xpath: %s", - VnfdDtsHandler.XPATH, - ) + "Registering for VNFD config using xpath {} for project {}" + .format(xpath, self._project)) acg_hdl = rift.tasklets.AppConfGroup.Handler(on_apply=on_apply) with self._dts.appconf_group_create(handler=acg_hdl) as acg: # Need a list in scratch to store VNFDs to create/update later # acg._scratch['vnfds'] = list() # acg._scratch['deleted_vnfds'] = list() self._regh = acg.register( - xpath=VnfdDtsHandler.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, on_prepare=on_prepare) + def deregister(self): + self._log.debug("De-register VNFD handler for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + + class NsrRpcDtsHandler(object): """ The network service instantiation RPC DTS handler """ EXEC_NSR_CONF_XPATH = "I,/nsr:start-network-service" @@ -2945,7 +3007,9 @@ class NsrRpcDtsHandler(object): self._ns_regh = None self._manager = None - self._nsr_config_url = NsrRpcDtsHandler.REST_BASE_V2_URL + 'config/ns-instance-config' + self._nsr_config_url = NsrRpcDtsHandler.REST_BASE_V2_URL + \ + 'config/project/{}/ns-instance-config'. \ + format(self._nsm._project.name) self._model = RwYang.Model.create_libncx() self._model.load_schema_ypbc(RwNsrYang.get_schema()) @@ -2993,24 +3057,39 @@ class NsrRpcDtsHandler(object): def _apply_ns_instance_config(self,payload_dict): #self._log.debug("At apply NS instance config with payload %s",payload_dict) - req_hdr= {'accept':'application/vnd.yang.data+json','content-type':'application/vnd.yang.data+json'} - response=requests.post(self._nsr_config_url, headers=req_hdr, auth=('admin', 'admin'),data=payload_dict,verify=False) + req_hdr= {'accept':'application/vnd.yang.data+json', + 'content-type':'application/vnd.yang.data+json'} + response=requests.post(self._nsr_config_url, headers=req_hdr, + auth=('admin', 'admin'),data=payload_dict,verify=False) return response @asyncio.coroutine def register(self): """ Register for NS monitoring read from dts """ + if self._ns_regh: + self._log.warning("RPC already registered for project {}". + format(self._project.name)) + return + @asyncio.coroutine def on_ns_config_prepare(xact_info, action, ks_path, msg): """ prepare callback from dts start-network-service""" assert action == rwdts.QueryAction.RPC rpc_ip = msg + + if not self._nsm._project.rpc_check(msg, xact_info=xact_info): + return + rpc_op = NsrYang.YangOutput_Nsr_StartNetworkService.from_dict({ - "nsr_id":str(uuid.uuid4()) - }) + "nsr_id":str(uuid.uuid4()), + "project_name": msg.prject_name, + }) - if not ('name' in rpc_ip and 'nsd_ref' in rpc_ip and ('cloud_account' in rpc_ip or 'om_datacenter' in rpc_ip)): - self._log.error("Mandatory parameters name or nsd_ref or cloud account not found in start-network-service {}".format(rpc_ip)) + if not ('name' in rpc_ip and 'nsd_ref' in rpc_ip and + ('cloud_account' in rpc_ip or 'om_datacenter' in rpc_ip)): + self._log.error("Mandatory parameters name or nsd_ref or " + + "cloud account not found in start-network-service {}". + format(rpc_ip)) self._log.debug("start-network-service RPC input: {}".format(rpc_ip)) @@ -3029,11 +3108,11 @@ class NsrRpcDtsHandler(object): ns_instance_config_dict = {"id":rpc_op.nsr_id, "admin_status":"ENABLED"} ns_instance_config_copy_dict = {k:v for k, v in rpc_ip.as_dict().items() - if k in RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr().fields} + if k in RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr().fields} ns_instance_config_dict.update(ns_instance_config_copy_dict) - ns_instance_config = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr.from_dict(ns_instance_config_dict) - ns_instance_config.nsd = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_Nsd() + ns_instance_config = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr.from_dict(ns_instance_config_dict) + ns_instance_config.nsd = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_Nsd() ns_instance_config.nsd.from_dict(nsd_copy.msg.as_dict()) payload_dict = ns_instance_config.to_json(self._model) @@ -3076,6 +3155,13 @@ class NsrRpcDtsHandler(object): flags=rwdts.Flag.PUBLISHER, ) + def deregister(self): + self._log.debug("De-register NSR RPC for project {}". + format(self._nsm._project.name)) + if self._ns_regh: + self._ns_regh.deregister() + self._ns_regh = None + class NsrDtsHandler(object): """ The network service DTS handler """ @@ -3088,6 +3174,7 @@ class NsrDtsHandler(object): self._log = log self._loop = loop self._nsm = nsm + self._project = self._nsm._project self._nsr_regh = None self._scale_regh = None @@ -3102,13 +3189,18 @@ class NsrDtsHandler(object): def register(self): """ Register for Nsr create/update/delete/read requests from dts """ + if self._nsr_regh: + self._log.warning("DTS handler already registered for project {}". + format(self._project.name)) + return + def nsr_id_from_keyspec(ks): - nsr_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) + nsr_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) nsr_id = nsr_path_entry.key00.id return nsr_id def group_name_from_keyspec(ks): - group_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) + group_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) group_name = group_path_entry.key00.scaling_group_name_ref return group_name @@ -3199,32 +3291,6 @@ class NsrDtsHandler(object): for vld in vl_delta["deleted"]: yield from self._nsm.nsr_terminate_vl(nsr_id, vld) - def get_add_delete_update_cfgs(dts_member_reg, xact, key_name, scratch): - # Unfortunately, it is currently difficult to figure out what has exactly - # changed in this xact without Pbdelta support (RIFT-4916) - # As a workaround, we can fetch the pre and post xact elements and - # perform a comparison to figure out adds/deletes/updates - xact_cfgs = list(dts_member_reg.get_xact_elements(xact)) - curr_cfgs = list(dts_member_reg.elements) - - xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs} - curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs} - - # Find Adds - added_keys = set(xact_key_map) - set(curr_key_map) - added_cfgs = [xact_key_map[key] for key in added_keys] - - # Find Deletes - deleted_keys = set(curr_key_map) - set(xact_key_map) - deleted_cfgs = [curr_key_map[key] for key in deleted_keys] - - # Find Updates - updated_keys = set(curr_key_map) & set(xact_key_map) - updated_cfgs = [xact_key_map[key] for key in updated_keys - if xact_key_map[key] != curr_key_map[key]] - - return added_cfgs, deleted_cfgs, updated_cfgs - def get_nsr_key_pairs(dts_member_reg, xact): key_pairs = {} for instance_cfg, keyspec in dts_member_reg.get_xact_elements(xact, include_keyspec=True): @@ -3273,7 +3339,11 @@ class NsrDtsHandler(object): def begin_instantiation(nsr): # Begin instantiation self._log.info("Beginning NS instantiation: %s", nsr.id) - yield from self._nsm.instantiate_ns(nsr.id, xact) + try: + yield from self._nsm.instantiate_ns(nsr.id, xact) + except Exception as e: + self._log.exception("NS instantiation: {}".format(e)) + raise e self._log.debug("Got nsr apply (xact: %s) (action: %s)(scr: %s)", xact, action, scratch) @@ -3289,8 +3359,7 @@ class NsrDtsHandler(object): (added_msgs, deleted_msgs, updated_msgs) = get_add_delete_update_cfgs(self._nsr_regh, xact, - "id", - scratch) + "id") self._log.debug("Added: %s, Deleted: %s, Updated: %s", added_msgs, deleted_msgs, updated_msgs) @@ -3401,24 +3470,40 @@ class NsrDtsHandler(object): acg.handle.prepare_complete_ok(xact_info.handle) - self._log.debug("Registering for NSR config using xpath: %s", - NsrDtsHandler.NSR_XPATH) + xpath = self._project.add_project(NsrDtsHandler.NSR_XPATH) + self._log.debug("Registering for NSR config using xpath: {}". + format(xpath)) acg_hdl = rift.tasklets.AppConfGroup.Handler(on_apply=on_apply) with self._dts.appconf_group_create(handler=acg_hdl) as acg: - self._nsr_regh = acg.register(xpath=NsrDtsHandler.NSR_XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, - on_prepare=on_prepare) + self._nsr_regh = acg.register( + xpath=xpath, + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, + on_prepare=on_prepare + ) self._scale_regh = acg.register( - xpath=NsrDtsHandler.SCALE_INSTANCE_XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY| rwdts.Flag.CACHE, - ) + xpath=self._project.add_project(NsrDtsHandler.SCALE_INSTANCE_XPATH), + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY| rwdts.Flag.CACHE, + ) self._key_pair_regh = acg.register( - xpath=NsrDtsHandler.KEY_PAIR_XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, - ) + xpath=self._project.add_project(NsrDtsHandler.KEY_PAIR_XPATH), + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE, + ) + + def deregister(self): + self._log.debug("De-register NSR config for project {}". + format(self._project.name)) + if self._nsr_regh: + self._nsr_regh.deregister() + self._nsr_regh = None + if self._scale_regh: + self._scale_regh.deregister() + self._scale_regh = None + if self._key_pair_regh: + self._key_pair_regh.deregister() + self._key_pair_regh = None class NsrOpDataDtsHandler(object): @@ -3430,6 +3515,8 @@ class NsrOpDataDtsHandler(object): self._log = log self._loop = loop self._nsm = nsm + + self._project = nsm._project self._regh = None @property @@ -3445,39 +3532,55 @@ class NsrOpDataDtsHandler(object): @asyncio.coroutine def register(self): """ Register for Nsr op data publisher registration""" - self._log.debug("Registering Nsr op data path %s as publisher", - NsrOpDataDtsHandler.XPATH) + if self._regh: + self._log.warning("NSR op data handler already registered for project {}". + format(self._project.name)) + return + + xpath = self._project.add_project(NsrOpDataDtsHandler.XPATH) + self._log.debug("Registering Nsr op data path {} as publisher". + format(xpath)) hdl = rift.tasklets.DTS.RegistrationHandler() handlers = rift.tasklets.Group.Handler() with self._dts.group_create(handler=handlers) as group: - self._regh = group.register(xpath=NsrOpDataDtsHandler.XPATH, + self._regh = group.register(xpath=xpath, handler=hdl, flags=rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | rwdts.Flag.DATASTORE) + def deregister(self): + self._log.debug("De-register NSR opdata for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine - def create(self, path, msg): + def create(self, xpath, msg): """ Create an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Creating NSR %s:%s", path, msg) self.regh.create_element(path, msg) self._log.debug("Created NSR, %s:%s", path, msg) @asyncio.coroutine - def update(self, path, msg, flags=rwdts.XactFlag.REPLACE): + def update(self, xpath, msg, flags=rwdts.XactFlag.REPLACE): """ Update an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Updating NSR, %s:%s regh = %s", path, msg, self.regh) self.regh.update_element(path, msg, flags) self._log.debug("Updated NSR, %s:%s", path, msg) @asyncio.coroutine - def delete(self, path): + def delete(self, xpath): """ Update an NS record in DTS with the path and message """ + path = self._project.add_project(xpath) self._log.debug("Deleting NSR path:%s", path) self.regh.delete_element(path) self._log.debug("Deleted NSR path:%s", path) @@ -3508,6 +3611,11 @@ class VnfrDtsHandler(object): @asyncio.coroutine def register(self): """ Register for vnfr create/update/delete/ advises from dts """ + if self._regh: + self._log.warning("VNFR DTS handler already registered for project {}". + format(self._project.name)) + return + def on_commit(xact_info): """ The transaction has been committed """ @@ -3523,16 +3631,17 @@ class VnfrDtsHandler(object): xact_info, action, ks_path, msg ) - schema = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.schema() + schema = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.schema() path_entry = schema.keyspec_to_entry(ks_path) if path_entry.key00.id not in self._nsm._vnfrs: - self._log.error("%s request for non existent record path %s", - action, xpath) + # Check if this is a monitoring param xpath + if 'vnfr:monitoring-param' not in xpath: + self._log.error("%s request for non existent record path %s", + action, xpath) xact_info.respond_xpath(rwdts.XactRspCode.NA, xpath) return - self._log.debug("Deleting VNFR with id %s", path_entry.key00.id) if action == rwdts.QueryAction.CREATE or action == rwdts.QueryAction.UPDATE: yield from self._nsm.update_vnfr(msg) elif action == rwdts.QueryAction.DELETE: @@ -3547,10 +3656,17 @@ class VnfrDtsHandler(object): hdl = rift.tasklets.DTS.RegistrationHandler(on_commit=on_commit, on_prepare=on_prepare,) with self._dts.group_create() as group: - self._regh = group.register(xpath=VnfrDtsHandler.XPATH, + self._regh = group.register(xpath=self._nsm._project.add_project( + VnfrDtsHandler.XPATH), handler=hdl, flags=(rwdts.Flag.SUBSCRIBER),) + def deregister(self): + self._log.debug("De-register VNFR for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None class NsdRefCountDtsHandler(object): """ The NSD Ref Count DTS handler """ @@ -3577,6 +3693,11 @@ class NsdRefCountDtsHandler(object): @asyncio.coroutine def register(self): """ Register for NSD ref count read from dts """ + if self._regh: + self._log.warning("NSD ref DTS handler already registered for project {}". + format(self._project.name)) + return + @asyncio.coroutine def on_prepare(xact_info, action, ks_path, msg): @@ -3584,7 +3705,7 @@ class NsdRefCountDtsHandler(object): xpath = ks_path.to_xpath(RwNsrYang.get_schema()) if action == rwdts.QueryAction.READ: - schema = RwNsrYang.YangData_Nsr_NsInstanceOpdata_NsdRefCount.schema() + schema = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_NsdRefCount.schema() path_entry = schema.keyspec_to_entry(ks_path) nsd_list = yield from self._nsm.get_nsd_refcount(path_entry.key00.nsd_id_ref) for xpath, msg in nsd_list: @@ -3597,19 +3718,28 @@ class NsdRefCountDtsHandler(object): hdl = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare,) with self._dts.group_create() as group: - self._regh = group.register(xpath=NsdRefCountDtsHandler.XPATH, + self._regh = group.register(xpath=self._nsm._project.add_project( + NsdRefCountDtsHandler.XPATH), handler=hdl, flags=rwdts.Flag.PUBLISHER,) + def deregister(self): + self._log.debug("De-register NSD Ref count for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + class NsManager(object): """ The Network Service Manager class""" - def __init__(self, dts, log, loop, + def __init__(self, dts, log, loop, project, nsr_handler, vnfr_handler, vlr_handler, ro_plugin_selector, vnffgmgr, vnfd_pub_handler, cloud_account_handler): self._dts = dts self._log = log self._loop = loop + self._project = project self._nsr_handler = nsr_handler self._vnfr_pub_handler = vnfr_handler self._vlr_pub_handler = vlr_handler @@ -3640,8 +3770,9 @@ class NsManager(object): VnfrDtsHandler(dts, log, loop, self), NsdRefCountDtsHandler(dts, log, loop, self), NsrDtsHandler(dts, log, loop, self), - ScalingRpcHandler(log, dts, loop, self.scale_rpc_callback), - NsrRpcDtsHandler(dts,log,loop,self), + ScalingRpcHandler(log, dts, loop, self._project, + self.scale_rpc_callback), + NsrRpcDtsHandler(dts, log, loop, self), self._vnfd_dts_handler, self.cfgmgr_obj, ] @@ -3717,6 +3848,11 @@ class NsManager(object): for dts_handle in self._dts_handlers: yield from dts_handle.register() + def deregister(self): + """ Register all static DTS handlers """ + for dts_handle in self._dts_handlers: + yield from dts_handle.deregister() + def get_ns_by_nsr_id(self, nsr_id): """ get NSR by nsr id """ @@ -3756,12 +3892,16 @@ class NsManager(object): msg : RPC input action : Scaling Action """ - ScalingGroupInstance = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup_Instance - ScalingGroup = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup + ScalingGroupInstance = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup_Instance + ScalingGroup = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup + + xpath = self._project.add_project( + ('C,/nsr:ns-instance-config/nsr:nsr[nsr:id="{}"]'). + format(msg.nsr_id_ref)) - xpath = ('C,/nsr:ns-instance-config/nsr:nsr[nsr:id="{}"]').format( - msg.nsr_id_ref) - instance = ScalingGroupInstance.from_dict({"id": msg.instance_id}) + instance = ScalingGroupInstance.from_dict({ + "id": msg.instance_id, + "project_name": self._project.name,}) @asyncio.coroutine def get_nsr_scaling_group(): @@ -3861,6 +4001,7 @@ class NsManager(object): nsr_msg, sdn_account_name, key_pairs, + self._project, restart_mode=restart_mode, vlr_handler=self._ro_plugin_selector._records_publisher._vlr_pub_hdlr ) @@ -3919,7 +4060,7 @@ class NsManager(object): @asyncio.coroutine def get_nsr_config(self, nsd_id): - xpath = "C,/nsr:ns-instance-config" + xpath = self._project.add_project("C,/nsr:ns-instance-config") results = yield from self._dts.query_read(xpath, rwdts.XactFlag.MERGE) for result in results: @@ -4098,18 +4239,18 @@ class NsManager(object): def nsd_refcount_xpath(nsd_id): """ xpath for ref count entry """ - return (NsdRefCountDtsHandler.XPATH + + return (self._project.add_project(NsdRefCountDtsHandler.XPATH) + "[rw-nsr:nsd-id-ref = '{}']").format(nsd_id) nsd_list = [] if nsd_id is None or nsd_id == "": for nsd in self._nsds.values(): - nsd_msg = RwNsrYang.YangData_Nsr_NsInstanceOpdata_NsdRefCount() + nsd_msg = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_NsdRefCount() nsd_msg.nsd_id_ref = nsd.id nsd_msg.instance_ref_count = nsd.ref_count nsd_list.append((nsd_refcount_xpath(nsd.id), nsd_msg)) elif nsd_id in self._nsds: - nsd_msg = RwNsrYang.YangData_Nsr_NsInstanceOpdata_NsdRefCount() + nsd_msg = RwNsrYang.YangData_RwProject_Project_NsInstanceOpdata_NsdRefCount() nsd_msg.nsd_id_ref = self._nsds[nsd_id].id nsd_msg.instance_ref_count = self._nsds[nsd_id].ref_count nsd_list.append((nsd_refcount_xpath(nsd_id), nsd_msg)) @@ -4145,10 +4286,12 @@ class NsmRecordsPublisherProxy(object): """ This class provides a publisher interface that allows plugin objects to publish NSR/VNFR/VLR""" - def __init__(self, dts, log, loop, nsr_pub_hdlr, vnfr_pub_hdlr, vlr_pub_hdlr): + def __init__(self, dts, log, loop, project, nsr_pub_hdlr, + vnfr_pub_hdlr, vlr_pub_hdlr,): self._dts = dts self._log = log self._loop = loop + self._project = project self._nsr_pub_hdlr = nsr_pub_hdlr self._vlr_pub_hdlr = vlr_pub_hdlr self._vnfr_pub_hdlr = vnfr_pub_hdlr @@ -4180,13 +4323,13 @@ class NsmRecordsPublisherProxy(object): @asyncio.coroutine def publish_vlr(self, xact, vlr): """ Publish a VLR """ - path = VirtualLinkRecord.vlr_xpath(vlr) + path = self._project.add_project(VirtualLinkRecord.vlr_xpath(vlr)) return (yield from self._vlr_pub_hdlr.update(xact, path, vlr)) @asyncio.coroutine def unpublish_vlr(self, xact, vlr): """ Unpublish a VLR """ - path = VirtualLinkRecord.vlr_xpath(vlr) + path = self._project.add_project(VirtualLinkRecord.vlr_xpath(vlr)) return (yield from self._vlr_pub_hdlr.delete(xact, path)) @@ -4200,24 +4343,35 @@ class ScalingRpcHandler(mano_dts.DtsHandler): ACTION = Enum('ACTION', 'SCALE_IN SCALE_OUT') - def __init__(self, log, dts, loop, callback=None): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project, callback=None): + super().__init__(log, dts, loop, project) self.callback = callback self.last_instance_id = defaultdict(int) + self._regh_in = None + self._regh_out = None @asyncio.coroutine def register(self): + if self._regh_in: + self._log.warning("RPC already registered for project {}". + format(self._project.name)) + return + @asyncio.coroutine def on_scale_in_prepare(xact_info, action, ks_path, msg): assert action == rwdts.QueryAction.RPC try: + if not self._project.rpc_check(msg, xact_info=xact_info): + return + if self.callback: self.callback(xact_info.xact, msg, self.ACTION.SCALE_IN) rpc_op = NsrYang.YangOutput_Nsr_ExecScaleIn.from_dict({ - "instance_id": msg.instance_id}) + "instance_id": msg.instance_id, + "project_name": self._project.name,}) xact_info.respond_xpath( rwdts.XactRspCode.ACK, @@ -4235,6 +4389,9 @@ class ScalingRpcHandler(mano_dts.DtsHandler): assert action == rwdts.QueryAction.RPC try: + if not self._project.rpc_check(msg, xact_info=xact_info): + return + scaling_group = msg.scaling_group_name_ref if not msg.instance_id: last_instance_id = self.last_instance_id[scale_group] @@ -4245,7 +4402,8 @@ class ScalingRpcHandler(mano_dts.DtsHandler): self.callback(xact_info.xact, msg, self.ACTION.SCALE_OUT) rpc_op = NsrYang.YangOutput_Nsr_ExecScaleOut.from_dict({ - "instance_id": msg.instance_id}) + "instance_id": msg.instance_id, + "project_name": self._project.name,}) xact_info.respond_xpath( rwdts.XactRspCode.ACK, @@ -4264,32 +4422,38 @@ class ScalingRpcHandler(mano_dts.DtsHandler): on_prepare=on_scale_out_prepare) with self.dts.group_create() as group: - group.register( - xpath=self.__class__.SCALE_IN_INPUT_XPATH, - handler=scale_in_hdl, - flags=rwdts.Flag.PUBLISHER) - group.register( - xpath=self.__class__.SCALE_OUT_INPUT_XPATH, - handler=scale_out_hdl, - flags=rwdts.Flag.PUBLISHER) - + self._regh_in = group.register( + xpath=self.__class__.SCALE_IN_INPUT_XPATH, + handler=scale_in_hdl, + flags=rwdts.Flag.PUBLISHER) + self._regh_out = group.register( + xpath=self.__class__.SCALE_OUT_INPUT_XPATH, + handler=scale_out_hdl, + flags=rwdts.Flag.PUBLISHER) + + def deregister(self): + self._log.debug("De-register scale RPCs for project {}". + format(self._project.name)) + if self._regh_in: + self._regh_in.deregister() + self._regh_in = None + if self._regh_out: + self._regh_out.deregister() + self._regh_out = None + + +class NsmProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(NsmProject, self).__init__(tasklet.log, name) + self.update(tasklet) -class NsmTasklet(rift.tasklets.Tasklet): - """ - The network service manager tasklet - """ - def __init__(self, *args, **kwargs): - super(NsmTasklet, self).__init__(*args, **kwargs) - self.rwlog.set_category("rw-mano-log") - self.rwlog.set_subcategory("nsm") - - self._dts = None self._nsm = None self._ro_plugin_selector = None self._vnffgmgr = None - self._nsr_handler = None + self._nsr_pub_handler = None self._vnfr_pub_handler = None self._vlr_pub_handler = None self._vnfd_pub_handler = None @@ -4297,57 +4461,33 @@ class NsmTasklet(rift.tasklets.Tasklet): self._records_publisher_proxy = None - def start(self): - """ The task start callback """ - super(NsmTasklet, self).start() - self.log.info("Starting NsmTasklet") - - self.log.debug("Registering with dts") - self._dts = rift.tasklets.DTS(self.tasklet_info, - RwNsmYang.get_schema(), - self.loop, - self.on_dts_state_change) - - self.log.debug("Created DTS Api GI Object: %s", self._dts) - - def stop(self): - try: - self._dts.deinit() - except Exception: - print("Caught Exception in NSM stop:", sys.exc_info()[0]) - raise - - def on_instance_started(self): - """ Task instance started callback """ - self.log.debug("Got instance started callback") - @asyncio.coroutine - def init(self): - """ Task init callback """ - self.log.debug("Got instance started callback") - - self.log.debug("creating config account handler") - - self._nsr_pub_handler = publisher.NsrOpDataDtsHandler(self._dts, self.log, self.loop) + def register(self): + self._nsr_pub_handler = publisher.NsrOpDataDtsHandler( + self._dts, self.log, self.loop, self) yield from self._nsr_pub_handler.register() - self._vnfr_pub_handler = publisher.VnfrPublisherDtsHandler(self._dts, self.log, self.loop) + self._vnfr_pub_handler = publisher.VnfrPublisherDtsHandler( + self._dts, self.log, self.loop, self) yield from self._vnfr_pub_handler.register() - self._vlr_pub_handler = publisher.VlrPublisherDtsHandler(self._dts, self.log, self.loop) + self._vlr_pub_handler = publisher.VlrPublisherDtsHandler( + self._dts, self.log, self.loop, self) yield from self._vlr_pub_handler.register() - manifest = self.tasklet_info.get_pb_manifest() + manifest = self._tasklet.tasklet_info.get_pb_manifest() use_ssl = manifest.bootstrap_phase.rwsecurity.use_ssl ssl_cert = manifest.bootstrap_phase.rwsecurity.cert ssl_key = manifest.bootstrap_phase.rwsecurity.key - self._vnfd_pub_handler = publisher.VnfdPublisher(use_ssl, ssl_cert, ssl_key, self.loop) + self._vnfd_pub_handler = publisher.VnfdPublisher( + use_ssl, ssl_cert, ssl_key, self.loop, self) self._records_publisher_proxy = NsmRecordsPublisherProxy( self._dts, self.log, self.loop, + self, self._nsr_pub_handler, self._vnfr_pub_handler, self._vlr_pub_handler, @@ -4359,6 +4499,7 @@ class NsmTasklet(rift.tasklets.Tasklet): self._dts, self.log, self.loop, + self, self._records_publisher_proxy, ) yield from self._ro_plugin_selector.register() @@ -4366,28 +4507,91 @@ class NsmTasklet(rift.tasklets.Tasklet): self._cloud_account_handler = cloud.CloudAccountConfigSubscriber( self._log, self._dts, - self.log_hdl) + self.log_hdl, + self, + ) yield from self._cloud_account_handler.register() - self._vnffgmgr = rwvnffgmgr.VnffgMgr(self._dts, self.log, self.log_hdl, self.loop) + self._vnffgmgr = rwvnffgmgr.VnffgMgr(self._dts, self.log, self.log_hdl, self.loop, self) yield from self._vnffgmgr.register() self._nsm = NsManager( self._dts, self.log, self.loop, + self, self._nsr_pub_handler, self._vnfr_pub_handler, self._vlr_pub_handler, self._ro_plugin_selector, self._vnffgmgr, self._vnfd_pub_handler, - self._cloud_account_handler + self._cloud_account_handler, ) yield from self._nsm.register() + def deregister(self): + self._log.debug("Project {} de-register".format(self.name)) + self._nsm.deregister() + self._vnffgmgr.deregister() + self._cloud_account_handler.deregister() + self._ro_plugin_selector.deregister() + + +class NsmTasklet(rift.tasklets.Tasklet): + """ + The network service manager tasklet + """ + def __init__(self, *args, **kwargs): + super(NsmTasklet, self).__init__(*args, **kwargs) + self.rwlog.set_category("rw-mano-log") + self.rwlog.set_subcategory("nsm") + + self._dts = None + self.project_handler = None + self.projects = {} + + @property + def dts(self): + return self._dts + + def start(self): + """ The task start callback """ + super(NsmTasklet, self).start() + self.log.info("Starting NsmTasklet") + + self.log.debug("Registering with dts") + self._dts = rift.tasklets.DTS(self.tasklet_info, + RwNsmYang.get_schema(), + self.loop, + self.on_dts_state_change) + + self.log.debug("Created DTS Api GI Object: %s", self._dts) + + def stop(self): + try: + self._dts.deinit() + except Exception: + print("Caught Exception in NSM stop:", sys.exc_info()[0]) + raise + + def on_instance_started(self): + """ Task instance started callback """ + self.log.debug("Got instance started callback") + + @asyncio.coroutine + def init(self): + """ Task init callback """ + self.log.debug("Got instance started callback") + + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, NsmProject) + self.project_handler.register() + + + @asyncio.coroutine def run(self): """ Task run callback """ diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py index 1f4f4bc4..81f22e9f 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwvnffgmgr.py @@ -61,20 +61,26 @@ class VnffgrUpdateFailed(Exception): class VnffgMgr(object): """ Implements the interface to backend plugins to fetch topology """ - def __init__(self, dts, log, log_hdl, loop): + def __init__(self, dts, log, log_hdl, loop, project): self._account = {} self._dts = dts self._log = log self._log_hdl = log_hdl self._loop = loop + self._project = project self._sdn = {} - self._sdn_handler = SDNAccountDtsHandler(self._dts,self._log,self) + self._sdn_handler = SDNAccountDtsHandler(self._dts, self._log, self) self._vnffgr_list = {} @asyncio.coroutine def register(self): yield from self._sdn_handler.register() + def deregister(self): + self._log.debug("Project {} de-register vnffgmgr". + format(self._project.name)) + self._sdn_handler.deregister() + def set_sdn_account(self,account): if (account.name in self._account): self._log.error("SDN Account is already set") @@ -323,14 +329,16 @@ class VnffgMgr(object): del self._vnffgr_list[vnffgr_id] class SDNAccountDtsHandler(object): - XPATH = "C,/rw-project:project/rw-sdn:sdn/rw-sdn:account" + 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 = self._parent._project self._sdn_account = {} + self._reg = None def _set_sdn_account(self, account): self._log.info("Setting sdn account: {}".format(account)) @@ -380,9 +388,11 @@ class SDNAccountDtsHandler(object): if msg.has_field("account_type"): errmsg = "Cannot update SDN account's account-type." self._log.error(errmsg) - xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, - errmsg) + xact_info.send_error_xpath( + RwTypes.RwStatus.FAILURE, + self._project.add_project(SDNAccountDtsHandler.XPATH), + errmsg + ) raise SdnAccountError(errmsg) # Update the sdn account record @@ -392,9 +402,11 @@ class SDNAccountDtsHandler(object): if not msg.has_field('account_type'): errmsg = "New SDN account must contain account-type field." self._log.error(errmsg) - xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, - errmsg) + xact_info.send_error_xpath( + RwTypes.RwStatus.FAILURE, + self._project.add_project(SDNAccountDtsHandler.XPATH), + errmsg + ) raise SdnAccountError(errmsg) # Set the sdn account record @@ -403,20 +415,23 @@ class SDNAccountDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - self._log.debug("Registering for Sdn Account config using xpath: %s", - SDNAccountDtsHandler.XPATH, - ) + xpath = self._project.add_project(SDNAccountDtsHandler.XPATH) + self._log.debug("Registering for Sdn Account config using xpath: {}". + format(xpath)) acg_handler = rift.tasklets.AppConfGroup.Handler( on_apply=apply_config, ) with self._dts.appconf_group_create(acg_handler) as acg: - acg.register( - xpath=SDNAccountDtsHandler.XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, - on_prepare=on_prepare - ) - - - + self._reg = acg.register( + xpath=xpath, + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, + on_prepare=on_prepare + ) + + def deregister(self): + self._log.debug("De-register SDN Account handler in vnffg for project". + format(self._project.name)) + self._reg.deregister() + self._reg = None diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/scale_group.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/scale_group.py index 8bbf894f..e3b7f1f0 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/scale_group.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/scale_group.py @@ -104,7 +104,7 @@ class ScalingGroup(object): def create_record_msg(self): """ Returns a NSR Scaling group record """ - msg = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ScalingGroupRecord( + msg = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ScalingGroupRecord( scaling_group_name_ref=self.name, ) @@ -259,7 +259,7 @@ class ScalingGroupInstance(object): return self._vnfrs.values() def create_record_msg(self): - msg = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ScalingGroupRecord_Instance( + msg = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr_ScalingGroupRecord_Instance( instance_id=self._instance_id, create_time=self._create_time, op_status=self._op_status, diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/download_status.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/download_status.py index 6890241e..9c768a61 100644 --- a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/download_status.py +++ b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/publisher/download_status.py @@ -28,13 +28,14 @@ import rift.downloader as url_downloader class DownloadStatusPublisher(mano_dts.DtsHandler, url_downloader.DownloaderProtocol): - def __init__(self, log, dts, loop): - super().__init__(log, dts, loop) + def __init__(self, log, dts, loop, project): + super().__init__(log, dts, loop, project) self.tasks = {} def xpath(self, download_id=None): - return ("D,/rw-pkg-mgmt:download-jobs/rw-pkg-mgmt:job" + - ("[download-id='{}']".format(download_id) if download_id else "")) + return self._project.add_project("D,/rw-pkg-mgmt:download-jobs/rw-pkg-mgmt:job" + + ("[download-id='{}']". + format(download_id) if download_id else "")) @asyncio.coroutine def register(self): @@ -43,6 +44,12 @@ class DownloadStatusPublisher(mano_dts.DtsHandler, url_downloader.DownloaderProt assert self.reg is not None + def dergister(self): + self._log.debug("De-registering download status for project {}". + format(self.project.name)) + if self.reg: + self.reg.deregister() + self.reg = None def on_download_progress(self, download_job_msg): """callback that triggers update. diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py index f55a8fdf..c207b437 100644 --- a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py +++ b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rpc.py @@ -102,22 +102,36 @@ class PackageOperationsRpcHandler(mano_dts.AbstractRpcHandler): 3. Return a tracking ID for the client to monitor the entire status """ - def __init__(self, log, dts, loop, proxy, publisher): + def __init__(self, log, dts, loop, proxy, tasklet): """ Args: proxy: Any impl of .proxy.AbstractPackageManagerProxy - publisher: Instance of DownloadStatusPublisher + publisher: Instance of tasklet to find the DownloadStatusPublisher + for a specific project """ super().__init__(log, dts, loop) self.proxy = proxy - self.publisher = publisher + self.tasklet = tasklet @property def xpath(self): return "/rw-pkg-mgmt:package-file-add" + def get_publisher(self, msg): + try: + proj = self.tasklet.projects[msg.project_name] + except Exception as e: + err = "Project or project name not found {}: {}". \ + format(msg.as_dict(), e) + self.log.error (err) + raise Exception (err) + + return proj.job_handler + @asyncio.coroutine def callback(self, ks_path, msg): + publisher = self.get_publisher(msg) + if not msg.external_url: # For now we will only support External URL download raise Exception ("No download URL provided") @@ -138,7 +152,7 @@ class PackageOperationsRpcHandler(mano_dts.AbstractRpcHandler): proxy=self.proxy, log=self.log) - download_id = yield from self.publisher.register_downloader(url_downloader) + download_id = yield from publisher.register_downloader(url_downloader) rpc_op = RPC_PACKAGE_ADD_ENDPOINT.from_dict({"task_id": download_id}) diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rwpkgmgr.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rwpkgmgr.py index d4e57367..18acc4d4 100644 --- a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rwpkgmgr.py +++ b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/rwpkgmgr.py @@ -32,13 +32,33 @@ from gi.repository import ( RwDts as rwdts, RwPkgMgmtYang) import rift.tasklets - +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, +) from . import rpc from .proxy import filesystem from . import publisher as pkg_publisher +class PackageManagerProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(PackageManagerProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self.job_handler = pkg_publisher.DownloadStatusPublisher( + self._log, self._dts, self._loop, self) + + @asyncio.coroutine + def register (self): + yield from self.job_handler.register() + + def deregister (self): + yield from self.job_handler.deregister() + + class PackageManagerTasklet(rift.tasklets.Tasklet): def __init__(self, *args, **kwargs): try: @@ -46,6 +66,10 @@ class PackageManagerTasklet(rift.tasklets.Tasklet): self.rwlog.set_category("rw-mano-log") self.endpoint_rpc = None self.schema_rpc = None + + self._project_handler = None + self.projects = {} + except Exception as e: self.log.exception(e) @@ -64,14 +88,12 @@ class PackageManagerTasklet(rift.tasklets.Tasklet): proxy = filesystem.FileSystemProxy(self.loop, self.log) args = [self.log, self.dts, self.loop] - self.job_handler = pkg_publisher.DownloadStatusPublisher(*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.job_handler) + args.append(self) self.pkg_op = rpc.PackageOperationsRpcHandler(*args) def stop(self): @@ -85,9 +107,12 @@ class PackageManagerTasklet(rift.tasklets.Tasklet): yield from self.endpoint_rpc.register() yield from self.schema_rpc.register() yield from self.pkg_op.register() - yield from self.job_handler.register() yield from self.delete_rpc.register() + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, PackageManagerProject) + self.project_handler.register() + @asyncio.coroutine def run(self): pass diff --git a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/subscriber/download_status.py b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/subscriber/download_status.py index c4a5a531..ea4b5e89 100644 --- a/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/subscriber/download_status.py +++ b/rwlaunchpad/plugins/rwpkgmgr/rift/tasklets/rwpkgmgr/subscriber/download_status.py @@ -21,8 +21,9 @@ import rift.mano.dts as mano_dts class DownloadStatusSubscriber(mano_dts.AbstractOpdataSubscriber): - def __init__(self, log, dts, loop, callback=None): - super().__init__(log, dts, loop, callback) + def __init__(self, log, dts, loop, project, callback=None): + super().__init__(log, dts, loop, project, callback) def get_xpath(self): - return ("D,/rw-pkg-mgmt:download-jobs/rw-pkg-mgmt:job") + return self._project.add_project( + "D,/rw-pkg-mgmt:download-jobs/rw-pkg-mgmt:job") diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_config.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_config.py index 5035b186..1b1c9bff 100644 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_config.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_config.py @@ -1,6 +1,6 @@ -# -# Copyright 2016 RIFT.IO Inc +# +# Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -51,12 +51,26 @@ class ResourceMgrConfig(object): self._parent = parent self._cloud_sub = None + self._res_sub = None + self._project = parent._project @asyncio.coroutine def register(self): yield from self.register_resource_pool_operational_data() self.register_cloud_account_config() + def deregister(self): + self._log.debug("De-register for project {}".format(self._project.name)) + if self._cloud_sub: + self._cloud_sub.deregister() + self._cloud_sub = None + + if self._res_sub: + self._res_sub.delete_element( + self._project.add_project(ResourceMgrConfig.XPATH_POOL_OPER_DATA)) + self._res_sub.deregister() + self._res_sub = None + def register_cloud_account_config(self): def on_add_cloud_account_apply(account): self._log.debug("Received on_add_cloud_account: %s", account) @@ -78,8 +92,9 @@ class ResourceMgrConfig(object): ) self._cloud_sub = rift.mano.cloud.CloudAccountConfigSubscriber( - self._dts, self._log, self._rwlog_hdl, cloud_callbacks - ) + self._dts, self._log, self._rwlog_hdl, + self._project, cloud_callbacks + ) self._cloud_sub.register() @asyncio.coroutine @@ -102,14 +117,14 @@ class ResourceMgrConfig(object): cloud_account_msg.records.append(pool_info) xact_info.respond_xpath(rwdts.XactRspCode.ACK, - ResourceMgrConfig.XPATH_POOL_OPER_DATA, + self._project.add_project(ResourceMgrConfig.XPATH_POOL_OPER_DATA), msg=msg,) - self._log.debug("Registering for Resource Mgr resource-pool-record using xpath: %s", - ResourceMgrConfig.XPATH_POOL_OPER_DATA) + xpath = self._project.add_project(ResourceMgrConfig.XPATH_POOL_OPER_DATA) + self._log.debug("Registering for Resource Mgr resource-pool-record using xpath: {}". + format(xpath)) handler=rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare) - response = yield from self._dts.register(xpath=ResourceMgrConfig.XPATH_POOL_OPER_DATA, - handler=handler, - flags=rwdts.Flag.PUBLISHER) - + self._res_sub = yield from self._dts.register(xpath=xpath, + handler=handler, + flags=rwdts.Flag.PUBLISHER) diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py index c80925c6..360390b0 100755 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py @@ -48,6 +48,7 @@ class ResourceMgrEvent(object): self._dts = dts self._loop = loop self._parent = parent + self._project = parent._project self._vdu_reg = None self._link_reg = None @@ -60,22 +61,34 @@ class ResourceMgrEvent(object): yield from asyncio.wait([self._link_reg_event.wait(), self._vdu_reg_event.wait()], timeout=timeout, loop=self._loop) - def create_record_dts(self, regh, xact, path, msg): + def _add_config_flag(self, xpath, config=False): + if xpath[0] == '/': + if config: + return 'C,' + xpath + else: + return 'D,' + xpath + + return xpath + + def create_record_dts(self, regh, xact, xpath, msg): """ Create a record in DTS with path and message """ + path = self._add_config_flag(self._project.add_project(xpath)) self._log.debug("Creating Resource Record xact = %s, %s:%s", xact, path, msg) regh.create_element(path, msg) - def delete_record_dts(self, regh, xact, path): + def delete_record_dts(self, regh, xact, xpath): """ Delete a VNFR record in DTS with path and message """ + path = self._add_config_flag(self._project.add_project(xpath)) self._log.debug("Deleting Resource Record xact = %s, %s", xact, path) regh.delete_element(path) + @asyncio.coroutine def register(self): @asyncio.coroutine @@ -161,12 +174,17 @@ class ResourceMgrEvent(object): yield from self._parent.release_virtual_network(pathentry.key00.event_id) self.delete_record_dts(self._link_reg, None, ks_path.to_xpath(RwResourceMgrYang.get_schema())) elif action == rwdts.QueryAction.READ: - response_info = yield from self._parent.read_virtual_network_info(pathentry.key00.event_id) + # TODO: Check why we are getting null event id request + if pathentry.key00.event_id: + response_info = yield from self._parent.read_virtual_network_info(pathentry.key00.event_id) + else: + xact_info.respond_xpath(rwdts.XactRspCode.NA) + return else: raise ValueError("Only read/create/delete actions available. Received action: %s" %(action)) - self._log.debug("Responding with VirtualLinkInfo at xpath %s: %s.", - response_xpath, response_info) + self._log.info("Responding with VirtualLinkInfo at xpath %s: %s.", + response_xpath, response_info) xact_info.respond_xpath(rwdts.XactRspCode.ACK, response_xpath, response_info) @@ -177,7 +195,7 @@ class ResourceMgrEvent(object): return rwdts.MemberRspCode.ACTION_OK def monitor_vdu_state(response_xpath, pathentry): - self._log.info("Initiating VDU state monitoring for xpath: %s ", response_xpath) + self._log.debug("Initiating VDU state monitoring for xpath: %s ", response_xpath) time_to_wait = 300 sleep_time = 2 loop_cnt = int(time_to_wait/sleep_time) @@ -197,7 +215,8 @@ class ResourceMgrEvent(object): response_info) else: if response_info.resource_state == 'active' or response_info.resource_state == 'failed': - self._log.info("VDU state monitoring: VDU reached terminal state. Publishing VDU info: %s at path: %s", + self._log.info("VDU state monitoring: VDU reached terminal state. " + + "Publishing VDU info: %s at path: %s", response_info, response_xpath) yield from self._dts.query_update(response_xpath, rwdts.XactFlag.ADVISE, @@ -205,7 +224,9 @@ class ResourceMgrEvent(object): return else: ### End of loop. This is only possible if VDU did not reach active state - err_msg = "VDU state monitoring: VDU at xpath :{} did not reached active state in {} seconds. Aborting monitoring".format(response_xpath, time_to_wait) + err_msg = ("VDU state monitoring: VDU at xpath :{} did not reached active " + + "state in {} seconds. Aborting monitoring". + format(response_xpath, time_to_wait)) self._log.info(err_msg) response_info = RwResourceMgrYang.VDUEventData_ResourceInfo() response_info.resource_state = 'failed' @@ -217,6 +238,7 @@ class ResourceMgrEvent(object): def allocate_vdu_task(ks_path, event_id, cloud_account, request_msg): response_xpath = ks_path.to_xpath(RwResourceMgrYang.get_schema()) + "/resource-info" + response_xpath = self._add_config_flag(response_xpath) schema = RwResourceMgrYang.VDUEventData().schema() pathentry = schema.keyspec_to_entry(ks_path) try: @@ -233,21 +255,22 @@ class ResourceMgrEvent(object): response_info) else: if response_info.resource_state == 'failed' or response_info.resource_state == 'active' : - self._log.info("Virtual compute create task completed. Publishing VDU info: %s at path: %s", - response_info, response_xpath) + self._log.debug("Virtual compute create task completed. Publishing VDU info: %s at path: %s", + response_info, response_xpath) yield from self._dts.query_update(response_xpath, rwdts.XactFlag.ADVISE, response_info) else: + self._log.debug("VDU create monitor at {}".format(response_xpath)) asyncio.ensure_future(monitor_vdu_state(response_xpath, pathentry), loop = self._loop) - @asyncio.coroutine def on_vdu_request_prepare(xact_info, action, ks_path, request_msg): self._log.debug("Received vdu on_prepare callback (xact_info: %s, action: %s): %s", xact_info, action, request_msg) response_xpath = ks_path.to_xpath(RwResourceMgrYang.get_schema()) + "/resource-info" + response_xpath = self._add_config_flag(response_xpath) schema = RwResourceMgrYang.VDUEventData().schema() pathentry = schema.keyspec_to_entry(ks_path) @@ -269,7 +292,12 @@ class ResourceMgrEvent(object): yield from self._parent.release_virtual_compute(pathentry.key00.event_id) self.delete_record_dts(self._vdu_reg, None, ks_path.to_xpath(RwResourceMgrYang.get_schema())) elif action == rwdts.QueryAction.READ: - response_info = yield from self._parent.read_virtual_compute_info(pathentry.key00.event_id) + # TODO: Check why we are getting null event id request + if pathentry.key00.event_id: + response_info = yield from self._parent.read_virtual_compute_info(pathentry.key00.event_id) + else: + xact_info.respond_xpath(rwdts.XactRspCode.NA) + return else: raise ValueError("Only create/delete actions available. Received action: %s" %(action)) @@ -293,24 +321,37 @@ class ResourceMgrEvent(object): link_handlers = rift.tasklets.Group.Handler(on_event=onlink_event,) with self._dts.group_create(handler=link_handlers) as link_group: - self._log.debug("Registering for Link Resource Request using xpath: %s", - ResourceMgrEvent.VLINK_REQUEST_XPATH) + xpath = self._project.add_project(ResourceMgrEvent.VLINK_REQUEST_XPATH) + self._log.debug("Registering for Link Resource Request using xpath: {}". + format(xpath)) - self._link_reg = link_group.register(xpath=ResourceMgrEvent.VLINK_REQUEST_XPATH, - handler=rift.tasklets.DTS.RegistrationHandler(on_ready=on_request_ready, - on_commit=on_link_request_commit, - on_prepare=on_link_request_prepare), - flags=rwdts.Flag.PUBLISHER | rwdts.Flag.DATASTORE,) + self._link_reg = link_group.register(xpath=xpath, + handler=rift.tasklets.DTS.RegistrationHandler(on_ready=on_request_ready, + on_commit=on_link_request_commit, + on_prepare=on_link_request_prepare), + flags=rwdts.Flag.PUBLISHER | rwdts.Flag.DATASTORE,) vdu_handlers = rift.tasklets.Group.Handler(on_event=onvdu_event, ) with self._dts.group_create(handler=vdu_handlers) as vdu_group: - self._log.debug("Registering for VDU Resource Request using xpath: %s", - ResourceMgrEvent.VDU_REQUEST_XPATH) + xpath = self._project.add_project(ResourceMgrEvent.VDU_REQUEST_XPATH) + self._log.debug("Registering for VDU Resource Request using xpath: {}". + format(xpath)) + + self._vdu_reg = vdu_group.register(xpath=xpath, + handler=rift.tasklets.DTS.RegistrationHandler(on_ready=on_request_ready, + on_commit=on_vdu_request_commit, + on_prepare=on_vdu_request_prepare), + flags=rwdts.Flag.PUBLISHER | rwdts.Flag.DATASTORE,) + + + def deregister(self): + self._log.debug("De-register for project {}".format(self._project.name)) - self._vdu_reg = vdu_group.register(xpath=ResourceMgrEvent.VDU_REQUEST_XPATH, - handler=rift.tasklets.DTS.RegistrationHandler(on_ready=on_request_ready, - on_commit=on_vdu_request_commit, - on_prepare=on_vdu_request_prepare), - flags=rwdts.Flag.PUBLISHER | rwdts.Flag.DATASTORE,) + if self._vdu_reg: + self._vdu_reg.deregister() + self._vdu_reg = None + if self._link_reg: + self._link_reg.deregister() + self._link_reg = None diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py index cdcadc73..d2359154 100755 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py @@ -34,6 +34,10 @@ from gi.repository import ( ) import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, +) from . import rwresmgr_core as Core from . import rwresmgr_config as Config @@ -41,11 +45,13 @@ from . import rwresmgr_events as Event class ResourceManager(object): - def __init__(self, log, log_hdl, loop, dts): + def __init__(self, log, log_hdl, loop, dts, project): self._log = log self._log_hdl = log_hdl self._loop = loop self._dts = dts + self._project = project + self.config_handler = Config.ResourceMgrConfig(self._dts, self._log, self._log_hdl, self._loop, self) self.event_handler = Event.ResourceMgrEvent(self._dts, self._log, self._loop, self) self.core = Core.ResourceMgrCore(self._dts, self._log, self._log_hdl, self._loop, self) @@ -55,6 +61,10 @@ class ResourceManager(object): yield from self.config_handler.register() yield from self.event_handler.register() + def deregister(self): + self.event_handler.deregister() + self.config_handler.deregister() + def add_cloud_account_config(self, account): self._log.debug("Received Cloud-Account add config event for account: %s", account.name) self.core.add_cloud_account(account) @@ -160,16 +170,45 @@ class ResourceManager(object): return info +class ResMgrProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(ResMgrProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self._resource_manager = None + + @asyncio.coroutine + def register (self): + self._log.debug("Initializing the Resource Manager tasklet for project {}". + format(self.name)) + self._resource_manager = ResourceManager(self._log, + self._log_hdl, + self._loop, + self._dts, + self,) + yield from self._resource_manager.register() + + def deregister(self): + self._log.debug("De-registering project {}".format(self.name)) + self._resource_manager.deregister() + + class ResMgrTasklet(rift.tasklets.Tasklet): def __init__(self, *args, **kwargs): super(ResMgrTasklet, self).__init__(*args, **kwargs) self.rwlog.set_category("rw-resource-mgr-log") self._dts = None - self._resource_manager = None + self._project_handler = None + self.projects = {} + + @property + def dts(self): + return self._dts def start(self): super(ResMgrTasklet, self).start() - self.log.info("Starting ResMgrTasklet") + self.log.debug("Starting ResMgrTasklet") self.log.debug("Registering with dts") @@ -192,12 +231,9 @@ class ResMgrTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): - self._log.info("Initializing the Resource Manager tasklet") - self._resource_manager = ResourceManager(self.log, - self.log_hdl, - self.loop, - self._dts) - yield from self._resource_manager.register() + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, ResMgrProject) + self.project_handler.register() @asyncio.coroutine def run(self): diff --git a/rwlaunchpad/plugins/rwresmgr/test/rmmgr_test.py b/rwlaunchpad/plugins/rwresmgr/test/rmmgr_test.py index 17c5bf0a..c0b4788a 100755 --- a/rwlaunchpad/plugins/rwresmgr/test/rmmgr_test.py +++ b/rwlaunchpad/plugins/rwresmgr/test/rmmgr_test.py @@ -441,7 +441,7 @@ class RMMgrTestCase(rift.test.dts.AbstractDTSTest): @asyncio.coroutine def configure_compute_resource_pools(self, dts, resource_type, cloud_type): - pool_xpath = "C,/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" + pool_xpath = "C,/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" msg = self.get_compute_pool_msg("virtual-compute", resource_type, cloud_type) self.log.info("Configuring compute-resource-pool: %s",msg) yield from dts.query_create(pool_xpath, @@ -451,7 +451,7 @@ class RMMgrTestCase(rift.test.dts.AbstractDTSTest): @asyncio.coroutine def configure_network_resource_pools(self, dts, resource_type, cloud_type): - pool_xpath = "C,/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" + pool_xpath = "C,/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools" msg = self.get_network_pool_msg("virtual-network", resource_type, cloud_type) self.log.info("Configuring network-resource-pool: %s",msg) yield from dts.query_create(pool_xpath, @@ -460,7 +460,7 @@ class RMMgrTestCase(rift.test.dts.AbstractDTSTest): @asyncio.coroutine def verify_resource_pools_config(self, dts): - pool_records_xpath = "D,/rw-resource-mgr:resource-pool-records" + pool_records_xpath = "D,/rw-project:project/rw-resource-mgr:resource-pool-records" self.log.debug("Verifying test_create_resource_pools results") res_iter = yield from dts.query_read(pool_records_xpath,) for result in res_iter: @@ -491,7 +491,7 @@ class RMMgrTestCase(rift.test.dts.AbstractDTSTest): @asyncio.coroutine def reserve_network_resources(self, name, dts, cloud_type): - network_xpath = "D,/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data[event-id='{}']" + network_xpath = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data[event-id='{}']" msg,xpath = self.get_network_reserve_msg(name, cloud_type, network_xpath) self.log.debug("Sending create event to network-event xpath %s with msg: %s" % (xpath, msg)) yield from dts.query_create(xpath, 0, msg) @@ -500,7 +500,7 @@ class RMMgrTestCase(rift.test.dts.AbstractDTSTest): @asyncio.coroutine def reserve_compute_resources(self, name, dts, cloud_type, vlinks = []): - compute_xpath = "D,/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data[event-id='{}']" + compute_xpath = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data[event-id='{}']" msg,xpath = self.get_compute_reserve_msg(name, cloud_type, compute_xpath, vlinks) self.log.debug("Sending create event to compute-event xpath %s with msg: %s" % (xpath, msg)) yield from dts.query_create(xpath, 0, msg) diff --git a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/publisher/staging_status.py b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/publisher/staging_status.py index 82e2da57..36c36f65 100644 --- a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/publisher/staging_status.py +++ b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/publisher/staging_status.py @@ -28,13 +28,13 @@ from ..protocol import StagingStoreProtocol class StagingStorePublisher(mano_dts.DtsHandler, StagingStoreProtocol): - def __init__(self, log, dts, loop): - super().__init__(log, dts, loop) + def __init__(self, project): + super().__init__(project.log, project.dts, project.loop, project) self.delegate = None def xpath(self, area_id=None): - return ("D,/rw-staging-mgmt:staging-areas/rw-staging-mgmt:staging-area" + - ("[area-id='{}']".format(area_id) if area_id else "")) + return self.project.add_project("D,/rw-staging-mgmt:staging-areas/rw-staging-mgmt:staging-area" + + ("[area-id='{}']".format(area_id) if area_id else "")) @asyncio.coroutine def register(self): @@ -59,6 +59,12 @@ class StagingStorePublisher(mano_dts.DtsHandler, StagingStoreProtocol): assert self.reg is not None + def deregister(self): + self._log.debug("Project {}: de-register staging store handler". + format(self._project.name)) + if self.reg: + self.reg.deregister() + def on_staging_area_create(self, store): self.reg.update_element(self.xpath(store.area_id), store) diff --git a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/rwstagingmgr.py b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/rwstagingmgr.py index 04a7cae3..4cb6553b 100644 --- a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/rwstagingmgr.py +++ b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/rwstagingmgr.py @@ -36,6 +36,10 @@ from gi.repository import ( RwDts as rwdts, RwStagingMgmtYang) import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, +) from . import rpc from . import store @@ -43,14 +47,36 @@ from .server import StagingApplication from .publisher import StagingStorePublisher +class StagingManagerProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(StagingManagerProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self.publisher = StagingStorePublisher(self) + # For recovery + self.publisher.delegate = tasklet.store + + @asyncio.coroutine + def register (self): + yield from self.publisher.register() + + def deregister(self): + self.publisher.deregister() + + class StagingManagerTasklet(rift.tasklets.Tasklet): """Tasklet to handle all staging related operations """ def __init__(self, *args, **kwargs): try: super().__init__(*args, **kwargs) + self._project_handler = None + self.projects = {} + except Exception as e: - self.log.exception(e) + self.log.exception("Staging Manager tasklet init: {}". + format(e)) def start(self): super().start() @@ -72,14 +98,7 @@ class StagingManagerTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): - self.store = store.StagingFileStore(log=self.log) - self.publisher = StagingStorePublisher(self.log, self.dts, self.loop) - # Fore recovery - self.publisher.delegate = self.store - # For create and delete events - self.store.delegate = self.publisher - yield from self.publisher.register() - + self.store = store.StagingFileStore(self) io_loop = rift.tasklets.tornado.TaskletAsyncIOLoop(asyncio_loop=self.loop) self.app = StagingApplication(self.store) @@ -107,9 +126,12 @@ class StagingManagerTasklet(rift.tasklets.Tasklet): self.dts, self.loop, self.store) - yield from self.create_stg_rpc.register() + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, StagingManagerProject) + self.project_handler.register() + @asyncio.coroutine def run(self): self.server.listen(self.app.PORT) diff --git a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/store/file_store.py b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/store/file_store.py index aec4180b..8acefe18 100644 --- a/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/store/file_store.py +++ b/rwlaunchpad/plugins/rwstagingmgr/rift/tasklets/rwstagingmgr/store/file_store.py @@ -53,7 +53,7 @@ class StagingFileStore(StagingStorePublisherProtocol): META_YAML = "meta.yaml" DEFAULT_EXPIRY = 60 * 60 - def __init__(self, log=None, root_dir=None): + def __init__(self, tasklet, root_dir=None): default_path = os.path.join( os.getenv('RIFT_ARTIFACTS'), "launchpad/staging") @@ -63,11 +63,11 @@ class StagingFileStore(StagingStorePublisherProtocol): if not os.path.isdir(self.root_dir): os.makedirs(self.root_dir) - self.log = log or logging.getLogger() + self.log = tasklet.log self.tmp_dir = tempfile.mkdtemp(dir=self.root_dir) self._cache = {} - self.delegate = None + self.tasklet = tasklet def on_recovery(self, staging_areas): for area in staging_areas: @@ -82,6 +82,17 @@ class StagingFileStore(StagingStorePublisherProtocol): return self._cache[area_id] + def get_delegate(self, msg): + try: + proj = self.tasklet.projects[msg.project_name] + except Exception as e: + err = "Project or project name not found {}: {}". \ + format(msg.as_dict(), e) + self.log.error (err) + raise Exception (err) + + return proj.publisher + def create_staging_area(self, staging_area_config): """Create the staging area Args: @@ -93,6 +104,8 @@ class StagingFileStore(StagingStorePublisherProtocol): Raises: StagingAreaExists: if the staging area already exists """ + delegate = self.get_delegate(staging_area_config) + area_id = str(uuid.uuid4()) container_path = os.path.join(self.root_dir, str(area_id)) @@ -118,10 +131,10 @@ class StagingFileStore(StagingStorePublisherProtocol): self._cache[area_id] = staging_area try: - if self.delegate: - self.delegate.on_staging_area_create(staging_area.model) + if delegate: + delegate.on_staging_area_create(staging_area.model) except Exception as e: - self.log.exception(str(e)) + self.log.exception(e) return staging_area @@ -131,6 +144,8 @@ class StagingFileStore(StagingStorePublisherProtocol): staging_area (str or model.StagingArea): Staging ID or the StagingArea object """ + delegate = self.get_delegate(staging_area_config) + if type(staging_area) is str: staging_area = self.get_staging_area(staging_area) @@ -140,7 +155,7 @@ class StagingFileStore(StagingStorePublisherProtocol): staging_area.model.status = "EXPIRED" try: - if self.delegate: - self.delegate.on_staging_area_delete(staging_area.model) + if delegate: + delegate.on_staging_area_delete(staging_area.model) except Exception as e: - self.log.exception(str(e)) + self.log.exception(e) diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index 5ff29836..abf73aec 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -51,6 +51,10 @@ import rift.package.store import rift.package.cloud_init import rift.package.script import rift.mano.dts as mano_dts +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, + ) class VMResourceError(Exception): @@ -162,7 +166,8 @@ class VDURecordState(enum.Enum): class VcsComponent(object): """ VCS Component within the VNF descriptor """ - def __init__(self, dts, log, loop, cluster_name, vcs_handler, component, mangled_name): + def __init__(self, dts, log, loop, cluster_name, + vcs_handler, component, mangled_name): self._dts = dts self._log = log self._loop = loop @@ -184,7 +189,7 @@ class VcsComponent(object): @property def path(self): """ The path for this object """ - return("D,/rw-manifest:manifest" + + return ("D,/rw-manifest:manifest" + "/rw-manifest:operational-inventory" + "/rw-manifest:component" + "[rw-manifest:component-name = '{}']").format(self.name) @@ -269,6 +274,7 @@ class VirtualDeploymentUnitRecord(object): dts, log, loop, + project, vdud, vnfr, mgmt_intf, @@ -280,6 +286,7 @@ class VirtualDeploymentUnitRecord(object): self._dts = dts self._log = log self._loop = loop + self._project = project self._vdud = vdud self._vnfr = vnfr self._mgmt_intf = mgmt_intf @@ -298,7 +305,8 @@ class VirtualDeploymentUnitRecord(object): self._rm_regh = None self._vm_resp = None self._vdud_cloud_init = None - self._vdur_console_handler = VnfrConsoleOperdataDtsHandler(dts, log, loop, self._vnfr._vnfm, self._vnfr.vnfr_id, self._vdur_id,self.vdu_id) + self._vdur_console_handler = VnfrConsoleOperdataDtsHandler( + dts, log, loop, self._vnfr._vnfm, self._vnfr.vnfr_id, self._vdur_id,self.vdu_id) @asyncio.coroutine def vdu_opdata_register(self): @@ -479,14 +487,15 @@ class VirtualDeploymentUnitRecord(object): placement_groups.append(group.as_dict()) vdur_dict['placement_groups_info'] = placement_groups - return RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur.from_dict(vdur_dict) + return RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur.from_dict(vdur_dict) @property def resmgr_path(self): """ path for resource-mgr""" - return ("D,/rw-resource-mgr:resource-mgmt" + - "/vdu-event" + - "/vdu-event-data[event-id='{}']".format(self._request_id)) + xpath = self._project.add_project("D,/rw-resource-mgr:resource-mgmt" + + "/vdu-event" + + "/vdu-event-data[event-id='{}']".format(self._request_id)) + return xpath @property def vm_flavor_msg(self): @@ -541,15 +550,19 @@ class VirtualDeploymentUnitRecord(object): if availability_zones: if len(availability_zones) > 1: - self._log.error("Can not launch VDU: %s in multiple availability zones. Requested Zones: %s", self.name, availability_zones) - raise VNFMPlacementGroupError("Can not launch VDU: {} in multiple availability zones. Requsted Zones".format(self.name, availability_zones)) + self._log.error("Can not launch VDU: %s in multiple availability zones. " + + "Requested Zones: %s", self.name, availability_zones) + raise VNFMPlacementGroupError("Can not launch VDU: {} in multiple availability" + + " zones. Requsted Zones".format(self.name, availability_zones)) else: vm_create_msg_dict['availability_zone'] = availability_zones[0] if server_groups: if len(server_groups) > 1: - self._log.error("Can not launch VDU: %s in multiple Server Group. Requested Groups: %s", self.name, server_groups) - raise VNFMPlacementGroupError("Can not launch VDU: {} in multiple Server Groups. Requsted Groups".format(self.name, server_groups)) + self._log.error("Can not launch VDU: %s in multiple Server Group. " + + "Requested Groups: %s", self.name, server_groups) + raise VNFMPlacementGroupError("Can not launch VDU: {} in multiple " + + "Server Groups. Requsted Groups".format(self.name, server_groups)) else: vm_create_msg_dict['server_group'] = server_groups[0] @@ -702,7 +715,7 @@ class VirtualDeploymentUnitRecord(object): self._rm_regh = None if self._vdur_console_handler is not None: - self._log.error("Deregistering vnfr vdur registration handle") + self._log.debug("Deregistering vnfr vdur registration handle") self._vdur_console_handler._regh.deregister() self._vdur_console_handler._regh = None @@ -995,10 +1008,12 @@ class VlRecordState(enum.Enum): class InternalVirtualLinkRecord(object): """ Internal Virtual Link record """ - def __init__(self, dts, log, loop, ivld_msg, vnfr_name, cloud_account_name, ip_profile=None): + def __init__(self, dts, log, loop, project, + ivld_msg, vnfr_name, cloud_account_name, ip_profile=None): self._dts = dts self._log = log self._loop = loop + self._project = project self._ivld_msg = ivld_msg self._vnfr_name = vnfr_name self._cloud_account_name = cloud_account_name @@ -1028,7 +1043,8 @@ class InternalVirtualLinkRecord(object): def vlr_path(self): """ VLR path for this VLR instance""" - return "D,/vlr:vlr-catalog/vlr:vlr[vlr:id = '{}']".format(self.vlr_id) + return self._project.add_project("D,/vlr:vlr-catalog/vlr:vlr[vlr:id = '{}']". + format(self.vlr_id)) def create_vlr(self): """ Create the VLR record which will be instantiated """ @@ -1053,7 +1069,7 @@ class InternalVirtualLinkRecord(object): vlr_dict.update(vld_copy_dict) - vlr = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr.from_dict(vlr_dict) + vlr = RwVlrYang.YangData_RwProject_Project_VlrCatalog_Vlr.from_dict(vlr_dict) return vlr @asyncio.coroutine @@ -1150,6 +1166,7 @@ class VirtualNetworkFunctionRecord(object): self._dts = dts self._log = log self._loop = loop + self._project = vnfm._project self._cluster_name = cluster_name self._vnfr_msg = vnfr_msg self._vnfr_id = vnfr_msg.id @@ -1202,7 +1219,8 @@ class VirtualNetworkFunctionRecord(object): @staticmethod def vnfd_xpath(vnfd_id): """ VNFD xpath associated with this VNFR """ - return "C,/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = '{}']".format(vnfd_id) + return ("C,/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = '{}']". + format(vnfd_id)) @property def vnfd_ref_count(self): @@ -1283,7 +1301,7 @@ class VirtualNetworkFunctionRecord(object): def get_nsr_config(self): ### Need access to NS instance configuration for runtime resolution. ### This shall be replaced when deployment flavors are implemented - xpath = "C,/nsr:ns-instance-config" + xpath = self._project.add_project("C,/nsr:ns-instance-config") results = yield from self._dts.query_read(xpath, rwdts.XactFlag.MERGE) for result in results: @@ -1333,7 +1351,7 @@ class VirtualNetworkFunctionRecord(object): vnfd_fields = ["short_name", "vendor", "description", "version"] vnfd_copy_dict = {k: v for k, v in self.vnfd.as_dict().items() if k in vnfd_fields} - mgmt_intf = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MgmtInterface() + mgmt_intf = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MgmtInterface() ip_address, port = self.mgmt_intf_info() if ip_address is not None: @@ -1353,8 +1371,8 @@ class VirtualNetworkFunctionRecord(object): vnfr_dict.update(vnfd_copy_dict) - vnfr_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict) - vnfr_msg.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict()) + vnfr_msg = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.from_dict(vnfr_dict) + vnfr_msg.vnfd = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict()) vnfr_msg.create_time = self._create_time vnfr_msg.uptime = int(time.time()) - self._create_time @@ -1375,13 +1393,13 @@ class VirtualNetworkFunctionRecord(object): vnfr_msg.dashboard_url = self.dashboard_url for cpr in self._cprs: - new_cp = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_ConnectionPoint.from_dict(cpr.as_dict()) + new_cp = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_ConnectionPoint.from_dict(cpr.as_dict()) vnfr_msg.connection_point.append(new_cp) if self._vnf_mon is not None: for monp in self._vnf_mon.msg: vnfr_msg.monitoring_param.append( - VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MonitoringParam.from_dict(monp.as_dict())) + VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_MonitoringParam.from_dict(monp.as_dict())) if self._vnfr.vnf_configuration is not None: vnfr_msg.vnf_configuration.from_dict(self._vnfr.vnf_configuration.as_dict()) @@ -1390,7 +1408,7 @@ class VirtualNetworkFunctionRecord(object): vnfr_msg.vnf_configuration.config_access.mgmt_ip_address = ip_address for group in self._vnfr_msg.placement_groups_info: - group_info = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_PlacementGroupsInfo() + group_info = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_PlacementGroupsInfo() group_info.from_dict(group.as_dict()) vnfr_msg.placement_groups_info.append(group_info) @@ -1420,7 +1438,7 @@ class VirtualNetworkFunctionRecord(object): @property def xpath(self): """ path for this VNFR """ - return("D,/vnfr:vnfr-catalog" + return self._project.add_project("D,/vnfr:vnfr-catalog" "/vnfr:vnfr[vnfr:id='{}']".format(self.vnfr_id)) @asyncio.coroutine @@ -1495,7 +1513,7 @@ class VirtualNetworkFunctionRecord(object): for group_info in nsr_config.vnfd_placement_group_maps: if group_info.placement_group_ref == input_group.name and \ group_info.vnfd_id_ref == self.vnfd_id: - group = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_PlacementGroupsInfo() + group = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_PlacementGroupsInfo() group_dict = {k:v for k,v in group_info.as_dict().items() if (k != 'placement_group_ref' and k !='vnfd_id_ref')} @@ -1510,7 +1528,7 @@ class VirtualNetworkFunctionRecord(object): placement_groups = [] ### Step-1: Get VNF level placement groups for group in self._vnfr_msg.placement_groups_info: - #group_info = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_PlacementGroupsInfo() + #group_info = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_PlacementGroupsInfo() #group_info.from_dict(group.as_dict()) placement_groups.append(group) @@ -1524,10 +1542,11 @@ class VirtualNetworkFunctionRecord(object): group_info = self.resolve_placement_group_cloud_construct(group, nsr_config) if group_info is None: - self._log.info("Could not resolve cloud-construct for placement group: %s", group.name) - ### raise VNFMPlacementGroupError("Could not resolve cloud-construct for placement group: {}".format(group.name)) + self._log.info("Could not resolve cloud-construct for " + + "placement group: %s", group.name) else: - self._log.info("Successfully resolved cloud construct for placement group: %s for VDU: %s in VNF: %s (Member Index: %s)", + self._log.info("Successfully resolved cloud construct for " + + "placement group: %s for VDU: %s in VNF: %s (Member Index: %s)", str(group_info), vdu.name, self.vnf_name, @@ -1579,6 +1598,7 @@ class VirtualNetworkFunctionRecord(object): dts=self._dts, log=self._log, loop=self._loop, + project = self._project, vdud=vdu, vnfr=vnfr, mgmt_intf=self.has_mgmt_interface(vdu), @@ -1725,8 +1745,7 @@ class VirtualNetworkFunctionRecord(object): def vlr_xpath(self, vlr_id): """ vlr xpath """ - return( - "D,/vlr:vlr-catalog/" + return self._project.add_project("D,/vlr:vlr-catalog/" "vlr:vlr[vlr:id = '{}']".format(vlr_id)) def ext_vlr_by_id(self, vlr_id): @@ -1816,6 +1835,7 @@ class VirtualNetworkFunctionRecord(object): @asyncio.coroutine def instantiate(self, xact, restart_mode=False): """ instantiate this VNF """ + self._log.info("Instantiate VNF {}: {}".format(self._vnfr_id, self._state)) self.set_state(VirtualNetworkFunctionRecordState.VL_INIT_PHASE) self._rw_vnfd = yield from self._vnfm.fetch_vnfd(self._vnfd_id) @@ -1831,7 +1851,7 @@ class VirtualNetworkFunctionRecord(object): cp_copy_dict = {k: v for k, v in cp.as_dict().items() if k in cp_fields} cpr_dict = {} cpr_dict.update(cp_copy_dict) - return VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_ConnectionPoint.from_dict(cpr_dict) + return VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_ConnectionPoint.from_dict(cpr_dict) self._log.debug("Fetching VLRs for VNFR id = %s, cps = %s", self._vnfr_id, self._vnfr.connection_point) @@ -1843,7 +1863,7 @@ class VirtualNetworkFunctionRecord(object): vlr_path = self.vlr_xpath(cp.vlr_ref) self._log.debug("Fetching VLR with path = %s", vlr_path) - res_iter = yield from self._dts.query_read(self.vlr_xpath(cp.vlr_ref), + res_iter = yield from self._dts.query_read(vlr_path, rwdts.XactFlag.MERGE) for i in res_iter: r = yield from i @@ -1866,16 +1886,16 @@ class VirtualNetworkFunctionRecord(object): yield from self.publish_inventory(xact) # Publish inventory - self._log.debug("VNFR-ID %s: Creating VLs", self._vnfr_id) + self._log.debug("Create VLs {}: {}".format(self._vnfr_id, self._state)) yield from self.create_vls() # publish the VNFR - self._log.debug("VNFR-ID %s: Publish VNFR", self._vnfr_id) + self._log.debug("Publish VNFR {}: {}".format(self._vnfr_id, self._state)) yield from self.publish(xact) # instantiate VLs - self._log.debug("VNFR-ID %s: Instantiate VLs", self._vnfr_id) + self._log.debug("Instantiate VLs {}: {}".format(self._vnfr_id, self._state)) try: yield from self.instantiate_vls(xact, restart_mode) except Exception as e: @@ -1886,7 +1906,7 @@ class VirtualNetworkFunctionRecord(object): self.set_state(VirtualNetworkFunctionRecordState.VM_INIT_PHASE) # instantiate VDUs - self._log.debug("VNFR-ID %s: Create VDUs", self._vnfr_id) + self._log.debug("Create VDUs {}: {}".format(self._vnfr_id, self._state)) yield from self.create_vdus(self, restart_mode) try: @@ -1897,12 +1917,13 @@ class VirtualNetworkFunctionRecord(object): yield from self.publish(xact) # publish the VNFR - self._log.debug("VNFR-ID %s: Publish VNFR", self._vnfr_id) + self._log.debug("VNFR {}: Publish VNFR with state {}". + format(self._vnfr_id, self._state)) yield from self.publish(xact) # instantiate VDUs # ToDo: Check if this should be prevented during restart - self._log.debug("VNFR-ID %s: Instantiate VDUs", self._vnfr_id) + self._log.debug("Instantiate VDUs {}: {}".format(self._vnfr_id, self._state)) _ = self._loop.create_task(self.instantiate_vdus(xact, self)) # publish the VNFR @@ -1982,6 +2003,14 @@ class VnfdDtsHandler(object): """ DTS registration handle """ return self._regh + def deregister(self): + '''De-register from DTS''' + self._log.debug("De-register VNFD DTS handler for project {}". + format(self._project)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for VNFD configuration""" @@ -2014,14 +2043,14 @@ class VnfdDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - self._log.debug( - "Registering for VNFD config using xpath: %s", - VnfdDtsHandler.XPATH, - ) + xpath = self._vnfm._project.add_project(VnfdDtsHandler.XPATH) + self._log.debug("Registering for VNFD config using xpath: {}". + format(xpath)) + acg_hdl = rift.tasklets.AppConfGroup.Handler(on_apply=on_apply) with self._dts.appconf_group_create(handler=acg_hdl) as acg: self._regh = acg.register( - xpath=VnfdDtsHandler.XPATH, + xpath=xpath, flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, on_prepare=on_prepare) @@ -2044,6 +2073,14 @@ class VcsComponentDtsHandler(object): """ DTS registration handle """ return self._regh + def deregister(self): + '''De-register from DTS''' + self._log.debug("De-register VCS DTS handler for project {}". + format(self._project)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Registers VCS component dts publisher registration""" @@ -2069,11 +2106,16 @@ class VcsComponentDtsHandler(object): VcsComponentDtsHandler.XPATH, xact, path, msg) class VnfrConsoleOperdataDtsHandler(object): - """ registers 'D,/vnfr:vnfr-console/vnfr:vnfr[id]/vdur[id]' and handles CRUD from DTS""" + """ + Registers 'D,/rw-project:project/vnfr:vnfr-console/vnfr:vnfr[id]/vdur[id]' + and handles CRUD from DTS + """ + @property def vnfr_vdu_console_xpath(self): """ path for resource-mgr""" - return ("D,/rw-vnfr:vnfr-console/rw-vnfr:vnfr[rw-vnfr:id='{}']/rw-vnfr:vdur[vnfr:id='{}']".format(self._vnfr_id,self._vdur_id)) + return self._project.add_project("D,/rw-vnfr:vnfr-console/rw-vnfr:vnfr[rw-vnfr:id='{}']" + + "/rw-vnfr:vdur[vnfr:id='{}']".format(self._vnfr_id,self._vdur_id)) def __init__(self, dts, log, loop, vnfm, vnfr_id, vdur_id, vdu_id): self._dts = dts @@ -2086,6 +2128,16 @@ class VnfrConsoleOperdataDtsHandler(object): self._vdur_id = vdur_id self._vdu_id = vdu_id + self._project = vnfm._project + + def deregister(self): + '''De-register from DTS''' + self._log.debug("De-register VNFR console DTS handler for project {}". + format(self._project)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for VNFR VDU Operational Data read from dts """ @@ -2100,7 +2152,7 @@ class VnfrConsoleOperdataDtsHandler(object): ) if action == rwdts.QueryAction.READ: - schema = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur.schema() + schema = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur.schema() path_entry = schema.keyspec_to_entry(ks_path) self._log.debug("VDU Opdata path is {}".format(path_entry)) try: @@ -2117,7 +2169,7 @@ class VnfrConsoleOperdataDtsHandler(object): return with self._dts.transaction() as new_xact: resp = yield from vdur.read_resource(new_xact) - vdur_console = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur() + vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur() vdur_console.id = self._vdur_id if resp.console_url: vdur_console.console_url = resp.console_url @@ -2126,7 +2178,7 @@ class VnfrConsoleOperdataDtsHandler(object): self._log.debug("Recevied console URL for vdu {} is {}".format(self._vdu_id,vdur_console)) except Exception: self._log.exception("Caught exception while reading VDU %s", self._vdu_id) - vdur_console = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur() + vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur() vdur_console.id = self._vdur_id vdur_console.console_url = 'none' @@ -2151,7 +2203,7 @@ class VnfrConsoleOperdataDtsHandler(object): class VnfrDtsHandler(object): - """ registers 'D,/vnfr:vnfr-catalog/vnfr:vnfr' and handles CRUD from DTS""" + """ registers 'D,/rw-project:project/vnfr:vnfr-catalog/vnfr:vnfr' and handles CRUD from DTS""" XPATH = "D,/vnfr:vnfr-catalog/vnfr:vnfr" def __init__(self, dts, log, loop, vnfm): @@ -2161,6 +2213,7 @@ class VnfrDtsHandler(object): self._vnfm = vnfm self._regh = None + self._project = vnfm._project @property def regh(self): @@ -2172,6 +2225,14 @@ class VnfrDtsHandler(object): """ Return VNF manager instance """ return self._vnfm + def deregister(self): + '''De-register from DTS''' + self._log.debug("De-register VNFR DTS handler for project {}". + format(self._project)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for vnfr create/update/delete/read requests from dts """ @@ -2233,7 +2294,7 @@ class VnfrDtsHandler(object): vnfr.set_state(VirtualNetworkFunctionRecordState.FAILED) yield from vnfr.publish(None) elif action == rwdts.QueryAction.DELETE: - schema = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.schema() + schema = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.schema() path_entry = schema.keyspec_to_entry(ks_path) vnfr = self._vnfm.get_vnfr(path_entry.key00.id) @@ -2252,7 +2313,7 @@ class VnfrDtsHandler(object): self._log.error("Caught exception while deleting vnfr %s", path_entry.key00.id) elif action == rwdts.QueryAction.UPDATE: - schema = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.schema() + schema = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr.schema() path_entry = schema.keyspec_to_entry(ks_path) vnfr = None try: @@ -2280,14 +2341,15 @@ class VnfrDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - self._log.debug("Registering for VNFR using xpath: %s", - VnfrDtsHandler.XPATH,) + xpath = self._project.add_project(VnfrDtsHandler.XPATH) + self._log.debug("Registering for VNFR using xpath: {}". + format(xpath)) hdl = rift.tasklets.DTS.RegistrationHandler(on_commit=on_commit, on_prepare=on_prepare,) handlers = rift.tasklets.Group.Handler(on_event=on_event,) with self._dts.group_create(handler=handlers) as group: - self._regh = group.register(xpath=VnfrDtsHandler.XPATH, + self._regh = group.register(xpath=xpath, handler=hdl, flags=(rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | @@ -2295,10 +2357,11 @@ class VnfrDtsHandler(object): rwdts.Flag.DATASTORE),) @asyncio.coroutine - def create(self, xact, path, msg): + def create(self, xact, xpath, msg): """ Create a VNFR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Creating VNFR xact = %s, %s:%s", xact, path, msg) @@ -2307,10 +2370,11 @@ class VnfrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def update(self, xact, path, msg): + def update(self, xact, xpath, msg): """ Update a VNFR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Updating VNFR xact = %s, %s:%s", xact, path, msg) self.regh.update_element(path, msg) @@ -2318,10 +2382,11 @@ class VnfrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def delete(self, xact, path): + def delete(self, xact, xpath): """ Delete a VNFR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Deleting VNFR xact = %s, %s", xact, path) self.regh.delete_element(path) self._log.debug("Deleted VNFR xact = %s, %s", xact, path) @@ -2349,6 +2414,14 @@ class VnfdRefCountDtsHandler(object): """ Return the NS manager instance """ return self._vnfm + def deregister(self): + '''De-register from DTS''' + self._log.debug("De-register VNFD Ref DTS handler for project {}". + format(self._project)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for VNFD ref count read from dts """ @@ -2363,7 +2436,7 @@ class VnfdRefCountDtsHandler(object): ) if action == rwdts.QueryAction.READ: - schema = RwVnfrYang.YangData_Vnfr_VnfrCatalog_VnfdRefCount.schema() + schema = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_VnfdRefCount.schema() path_entry = schema.keyspec_to_entry(ks_path) vnfd_list = yield from self._vnfm.get_vnfd_refcount(path_entry.key00.vnfd_id_ref) for xpath, msg in vnfd_list: @@ -2378,7 +2451,8 @@ class VnfdRefCountDtsHandler(object): hdl = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare,) with self._dts.group_create() as group: - self._regh = group.register(xpath=VnfdRefCountDtsHandler.XPATH, + self._regh = group.register(xpath=self._vnfm._project.add_project( + VnfdRefCountDtsHandler.XPATH), handler=hdl, flags=rwdts.Flag.PUBLISHER, ) @@ -2506,16 +2580,18 @@ class VdurDatastore(object): class VnfManager(object): """ The virtual network function manager class """ - def __init__(self, dts, log, loop, cluster_name): + def __init__(self, dts, log, loop, project, cluster_name): self._dts = dts self._log = log self._loop = loop + self._project = project self._cluster_name = cluster_name self._vcs_handler = VcsComponentDtsHandler(dts, log, loop, self) self._vnfr_handler = VnfrDtsHandler(dts, log, loop, self) self._vnfr_ref_handler = VnfdRefCountDtsHandler(dts, log, loop, self) - self._nsr_handler = mano_dts.NsInstanceConfigSubscriber(log, dts, loop, callback=self.handle_nsr) + self._nsr_handler = mano_dts.NsInstanceConfigSubscriber( + log, dts, loop, project, callback=self.handle_nsr) self._dts_handlers = [VnfdDtsHandler(dts, log, loop, self), self._vnfr_handler, @@ -2542,6 +2618,11 @@ class VnfManager(object): for hdl in self._dts_handlers: yield from hdl.register() + def deregister(self): + self.log.debug("De-register VNFM project {}".format(self.name)) + for hdl in self._dts_handlers: + yield from hdl.deregister() + @asyncio.coroutine def run(self): """ Run this VNFM instance """ @@ -2627,11 +2708,13 @@ class VnfManager(object): @asyncio.coroutine def fetch_vnfd(self, vnfd_id): """ Fetch VNFDs based with the vnfd id""" - vnfd_path = VirtualNetworkFunctionRecord.vnfd_xpath(vnfd_id) + vnfd_path = self._project.add_project( + VirtualNetworkFunctionRecord.vnfd_xpath(vnfd_id)) self._log.debug("Fetch vnfd with path %s", vnfd_path) vnfd = None - res_iter = yield from self._dts.query_read(vnfd_path, rwdts.XactFlag.MERGE) + res_iter = yield from self._dts.query_read(vnfd_path, + rwdts.XactFlag.MERGE) for ent in res_iter: res = yield from ent @@ -2690,8 +2773,8 @@ class VnfManager(object): def vnfd_refcount_xpath(self, vnfd_id): """ xpath for ref count entry """ - return (VnfdRefCountDtsHandler.XPATH + - "[rw-vnfr:vnfd-id-ref = '{}']").format(vnfd_id) + return self._project.add_project(VnfdRefCountDtsHandler.XPATH + + "[rw-vnfr:vnfd-id-ref = '{}']").format(vnfd_id) @asyncio.coroutine def get_vnfd_refcount(self, vnfd_id): @@ -2699,12 +2782,12 @@ class VnfManager(object): vnfd_list = [] if vnfd_id is None or vnfd_id == "": for vnfd in self._vnfds_to_vnfr.keys(): - vnfd_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_VnfdRefCount() + vnfd_msg = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_VnfdRefCount() vnfd_msg.vnfd_id_ref = vnfd vnfd_msg.instance_ref_count = self._vnfds_to_vnfr[vnfd] vnfd_list.append((self.vnfd_refcount_xpath(vnfd), vnfd_msg)) elif vnfd_id in self._vnfds_to_vnfr: - vnfd_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_VnfdRefCount() + vnfd_msg = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_VnfdRefCount() vnfd_msg.vnfd_id_ref = vnfd_id vnfd_msg.instance_ref_count = self._vnfds_to_vnfr[vnfd_id] vnfd_list.append((self.vnfd_refcount_xpath(vnfd_id), vnfd_msg)) @@ -2712,6 +2795,31 @@ class VnfManager(object): return vnfd_list +class VnfmProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(VnfmProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self._vnfm = None + + @asyncio.coroutine + def register (self): + try: + vm_parent_name = self._tasklet.tasklet_info.get_parent_vm_parent_instance_name() + assert vm_parent_name is not None + self._vnfm = VnfManager(self._dts, self.log, self.loop, self, vm_parent_name) + yield from self._vnfm.run() + except Exception: + print("Caught Exception in VNFM init:", sys.exc_info()[0]) + raise + + def deregister(self): + self._log.debug("De-register project {} for VnfmProject". + format(self.name)) + self._vnfm.deregister() + + class VnfmTasklet(rift.tasklets.Tasklet): """ VNF Manager tasklet class """ def __init__(self, *args, **kwargs): @@ -2720,7 +2828,12 @@ class VnfmTasklet(rift.tasklets.Tasklet): self.rwlog.set_subcategory("vnfm") self._dts = None - self._vnfm = None + self._project_handler = None + self.projects = {} + + @property + def dts(self): + return self._dts def start(self): try: @@ -2754,14 +2867,9 @@ class VnfmTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): """ Task init callback """ - try: - vm_parent_name = self.tasklet_info.get_parent_vm_parent_instance_name() - assert vm_parent_name is not None - self._vnfm = VnfManager(self._dts, self.log, self.loop, vm_parent_name) - yield from self._vnfm.run() - except Exception: - print("Caught Exception in VNFM init:", sys.exc_info()[0]) - raise + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, VnfmProject) + self.project_handler.register() @asyncio.coroutine def run(self): diff --git a/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py b/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py index 9ec6764b..1720e391 100755 --- a/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py +++ b/rwlaunchpad/plugins/rwvns/rift/tasklets/rwvnstasklet/rwvnstasklet.py @@ -32,6 +32,10 @@ from gi.repository import ( ) import rift.tasklets +from rift.mano.utils.project import ( + ManoProject, + ProjectHandler, +) from rift.vlmgr import ( VlrDtsHandler, @@ -79,13 +83,16 @@ class SDNAccountDtsOperdataHandler(object): self._log = log 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 "D,/rw-project:project/rw-sdn:sdn/rw-sdn:account{}" \ + 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): @@ -112,12 +119,12 @@ class SDNAccountDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare), - flags=rwdts.Flag.PUBLISHER, - ) + 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(): @@ -125,6 +132,10 @@ class SDNAccountDtsOperdataHandler(object): @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") @@ -139,28 +150,46 @@ class SDNAccountDtsOperdataHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - yield from self._dts.register( - xpath=get_xpath(), - handler=rift.tasklets.DTS.RegistrationHandler( - on_prepare=on_prepare - ), - flags=rwdts.Flag.PUBLISHER, - ) + 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-project:project/rw-sdn:sdn/rw-sdn:account" + 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)) @@ -211,7 +240,7 @@ class SDNAccountDtsHandler(object): errmsg = "Cannot update SDN account's account-type." self._log.error(errmsg) xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, + self._xpath, errmsg) raise SdnAccountError(errmsg) @@ -223,7 +252,7 @@ class SDNAccountDtsHandler(object): errmsg = "New SDN account must contain account-type field." self._log.error(errmsg) xact_info.send_error_xpath(RwTypes.RwStatus.FAILURE, - SDNAccountDtsHandler.XPATH, + self._xpath, errmsg) raise SdnAccountError(errmsg) @@ -233,37 +262,47 @@ class SDNAccountDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) - self._log.debug("Registering for Sdn Account config using xpath: %s", - SDNAccountDtsHandler.XPATH, - ) + 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: - acg.register( - xpath=SDNAccountDtsHandler.XPATH, - flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, - on_prepare=on_prepare - ) + self._regh = acg.register( + xpath=self._xpath, + flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY, + on_prepare=on_prepare + ) + + 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 class VnsManager(object): """ The Virtual Network Service Manager """ - def __init__(self, dts, log, log_hdl, loop): + def __init__(self, dts, log, log_hdl, loop, project): self._dts = dts self._log = log self._log_hdl = log_hdl self._loop = loop + self._project = project + 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._acctmgr = SdnAccountMgr(self._log, self._log_hdl, self._loop, self._project) self._nwtopdata_store = NwtopDataStore(log) - self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, self._acctmgr, self._nwtopdata_store) - self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, self._acctmgr, self._nwtopdata_store) + self._nwtopdiscovery_handler = NwtopDiscoveryDtsHandler(dts, log, loop, self._project, + self._acctmgr, self._nwtopdata_store) + self._nwtopstatic_handler = NwtopStaticDtsHandler(dts, log, loop, self._project, + self._acctmgr, self._nwtopdata_store) self._vlrs = {} @asyncio.coroutine @@ -307,6 +346,14 @@ class VnsManager(object): # Not used for now yield from self.register_nwtopdiscovery_handler() + def deregister(self): + self._nwtopdiscovery_handler.deregister() + self._nwtopstatic_handler.deregister() + self._vld_handler.deregister() + self._vlr_handler.deregister() + self._sdn_opdata_handler.deregister() + self._sdn_handler.deregister() + def create_vlr(self, msg): """ Create VLR """ if msg.id in self._vlrs: @@ -360,19 +407,52 @@ class VnsManager(object): return False @asyncio.coroutine - def publish_vlr(self, xact, path, msg): + def publish_vlr(self, xact, xpath, msg): """ Publish a VLR """ + path = self._project.add_project(xpath) self._log.debug("Publish vlr called with path %s, msg %s", path, msg) yield from self._vlr_handler.update(xact, path, msg) @asyncio.coroutine - def unpublish_vlr(self, xact, path): + def unpublish_vlr(self, xact, xpath): """ Publish a VLR """ + path = self._project.add_project(xpath) self._log.debug("Unpublish vlr called with path %s", path) yield from self._vlr_handler.delete(xact, path) +class VnsProject(ManoProject): + + def __init__(self, name, tasklet, **kw): + super(VnsProject, self).__init__(tasklet.log, name) + self.update(tasklet) + + self._vlr_handler = None + self._vnsm = None + # A mapping of instantiated vlr_id's to VirtualLinkRecord objects + self._vlrs = {} + + @asyncio.coroutine + def register (self): + self._vnsm = VnsManager(dts=self._dts, + log=self.log, + log_hdl=self._log_hdl, + loop=self._loop, + project=self) + yield from self._vnsm.run() + + # NSM needs to detect VLD deletion that has active VLR + # self._vld_handler = VldDescriptorConfigDtsHandler( + # self._dts, self.log, self.loop, self._vlrs, + # ) + # yield from self._vld_handler.register() + + def deregister(self): + self._log.debug("De-register project {}".format(self.name)) + self._vnsm.deregister() + + class VnsTasklet(rift.tasklets.Tasklet): """ The VNS tasklet class """ def __init__(self, *args, **kwargs): @@ -381,11 +461,12 @@ class VnsTasklet(rift.tasklets.Tasklet): self.rwlog.set_subcategory("vns") self._dts = None - self._vlr_handler = None + self._project_handler = None + self.projects = {} - self._vnsm = None - # A mapping of instantiated vlr_id's to VirtualLinkRecord objects - self._vlrs = {} + @property + def dts(self): + return self._dts def start(self): super(VnsTasklet, self).start() @@ -413,17 +494,9 @@ class VnsTasklet(rift.tasklets.Tasklet): @asyncio.coroutine def init(self): """ task init callback""" - self._vnsm = VnsManager(dts=self._dts, - log=self.log, - log_hdl=self.log_hdl, - loop=self.loop) - yield from self._vnsm.run() - - # NSM needs to detect VLD deletion that has active VLR - # self._vld_handler = VldDescriptorConfigDtsHandler( - # self._dts, self.log, self.loop, self._vlrs, - # ) - # yield from self._vld_handler.register() + self.log.debug("creating project handler") + self.project_handler = ProjectHandler(self, VnsProject) + self.project_handler.register() @asyncio.coroutine def run(self): diff --git a/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py b/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py index b095fbc0..e1c7ea14 100755 --- a/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py +++ b/rwlaunchpad/plugins/rwvns/rift/topmgr/rwtopmgr.py @@ -39,8 +39,8 @@ import rift.tasklets class SdnGetPluginError(Exception): """ Error while fetching SDN plugin """ pass - - + + class SdnGetInterfaceError(Exception): """ Error while fetching SDN interface""" pass @@ -48,16 +48,17 @@ class SdnGetInterfaceError(Exception): class SdnAccountMgr(object): """ Implements the interface to backend plugins to fetch topology """ - def __init__(self, log, log_hdl, loop): + 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.SDNAccount_ConnectionStatus( + self._status = RwsdnYang.SdnConnectionStatus( status='unknown', details="Connection status lookup not started" ) @@ -160,9 +161,9 @@ class SdnAccountMgr(object): ) if rwstatus == RwTypes.RwStatus.SUCCESS: - self._status = RwsdnYang.SDNAccount_ConnectionStatus.from_dict(status.as_dict()) + self._status = RwsdnYang.SdnConnectionStatus.from_dict(status.as_dict()) else: - self._status = RwsdnYang.SDNAccount_ConnectionStatus( + self._status = RwsdnYang.SdnConnectionStatus( status="failure", details="Error when calling CAL validate sdn creds" ) @@ -185,10 +186,11 @@ class NwtopDiscoveryDtsHandler(object): """ Handles DTS interactions for the Discovered Topology registration """ DISC_XPATH = "D,/nd:network" - def __init__(self, dts, log, loop, acctmgr, nwdatastore): + def __init__(self, dts, log, loop, project, acctmgr, nwdatastore): self._dts = dts self._log = log self._loop = loop + self._project = project self._acctmgr = acctmgr self._nwdatastore = nwdatastore @@ -199,6 +201,13 @@ class NwtopDiscoveryDtsHandler(object): """ The registration handle associated with this Handler""" return self._regh + def deregister(self): + self._log.debug("De-register Topology discovery handler for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for the Discovered Topology path """ @@ -268,10 +277,11 @@ class NwtopStaticDtsHandler(object): """ Handles DTS interactions for the Static Topology registration """ STATIC_XPATH = "C,/nd:network" - def __init__(self, dts, log, loop, acctmgr, nwdatastore): + def __init__(self, dts, log, loop, project, acctmgr, nwdatastore): self._dts = dts self._log = log self._loop = loop + self._project = project self._acctmgr = acctmgr self._regh = None @@ -282,8 +292,14 @@ class NwtopStaticDtsHandler(object): def regh(self): """ The registration handle associated with this Handler""" return self._regh - - + + def deregister(self): + self._log.debug("De-register Topology static handler for project {}". + format(self._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None + @asyncio.coroutine def register(self): """ Register for the Static Topology path """ @@ -322,8 +338,6 @@ class NwtopStaticDtsHandler(object): on_apply=apply_nw_config) with self._dts.appconf_group_create(handler=handler) as acg: - acg.register(xpath = NwtopStaticDtsHandler.STATIC_XPATH, - flags = rwdts.Flag.SUBSCRIBER, - on_prepare=prepare_nw_cfg) - - + self._regh = acg.register(xpath = NwtopStaticDtsHandler.STATIC_XPATH, + flags = rwdts.Flag.SUBSCRIBER, + on_prepare=prepare_nw_cfg) diff --git a/rwlaunchpad/plugins/rwvns/rift/vlmgr/rwvlmgr.py b/rwlaunchpad/plugins/rwvns/rift/vlmgr/rwvlmgr.py index bdea4efc..8fc267ec 100755 --- a/rwlaunchpad/plugins/rwvns/rift/vlmgr/rwvlmgr.py +++ b/rwlaunchpad/plugins/rwvns/rift/vlmgr/rwvlmgr.py @@ -70,6 +70,7 @@ class VirtualLinkRecord(object): self._vnsm = vnsm self._vlr_msg = vlr_msg + self._project = vnsm._project self._network_id = None self._network_pool = None self._assigned_subnet = None @@ -85,7 +86,8 @@ class VirtualLinkRecord(object): @property def vld_xpath(self): """ VLD xpath associated with this VLR record """ - return "C,/vld:vld-catalog/vld:vld[id='{}']".format(self.vld_id) + return self._project.add_project("C,/vld:vld-catalog/vld:vld[id='{}']". + format(self.vld_id)) @property def vld_id(self): @@ -100,7 +102,7 @@ class VirtualLinkRecord(object): @property def xpath(self): """ path for this VLR """ - return("D,/vlr:vlr-catalog" + return self._project.add_project("D,/vlr:vlr-catalog" "/vlr:vlr[vlr:id='{}']".format(self.vlr_id)) @property @@ -116,7 +118,7 @@ class VirtualLinkRecord(object): @property def resmgr_path(self): """ path for resource-mgr""" - return ("D,/rw-resource-mgr:resource-mgmt" + + return self._project.add_project("D,/rw-resource-mgr:resource-mgmt" + "/vlink-event/vlink-event-data[event-id='{}']".format(self._request_id)) @property @@ -135,7 +137,7 @@ class VirtualLinkRecord(object): @property def msg(self): """ VLR message for this VLR """ - msg = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr() + msg = RwVlrYang.YangData_RwProject_Project_VlrCatalog_Vlr() msg.copy_from(self._vlr_msg) if self._network_id is not None: @@ -313,6 +315,7 @@ class VlrDtsHandler(object): self._vnsm = vnsm self._regh = None + self._project = vnsm._project @property def regh(self): @@ -369,7 +372,7 @@ class VlrDtsHandler(object): return elif action == rwdts.QueryAction.DELETE: # Delete an VLR record - schema = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr.schema() + schema = RwVlrYang.YangData_RwProject_Project_VlrCatalog_Vlr.schema() path_entry = schema.keyspec_to_entry(ks_path) self._log.debug("Terminating VLR id %s", path_entry.key00.id) yield from self._vnsm.delete_vlr(path_entry.key00.id, xact_info.xact) @@ -379,8 +382,9 @@ class VlrDtsHandler(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) return - self._log.debug("Registering for VLR using xpath: %s", - VlrDtsHandler.XPATH) + xpath = self._project.add_project(VlrDtsHandler.XPATH) + self._log.debug("Registering for VLR using xpath: {}". + format(xpath)) reg_handle = rift.tasklets.DTS.RegistrationHandler( on_commit=on_commit, @@ -389,16 +393,24 @@ class VlrDtsHandler(object): handlers = rift.tasklets.Group.Handler(on_event=on_event,) with self._dts.group_create(handler=handlers) as group: self._regh = group.register( - xpath=VlrDtsHandler.XPATH, + xpath=xpath, handler=reg_handle, flags=rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ| rwdts.Flag.DATASTORE, ) + 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 + @asyncio.coroutine - def create(self, xact, path, msg): + def create(self, xact, xpath, msg): """ Create a VLR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Creating VLR xact = %s, %s:%s", xact, path, msg) self.regh.create_element(path, msg) @@ -406,10 +418,11 @@ class VlrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def update(self, xact, path, msg): + def update(self, xact, xpath, msg): """ Update a VLR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Updating VLR xact = %s, %s:%s", xact, path, msg) self.regh.update_element(path, msg) @@ -417,10 +430,11 @@ class VlrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def delete(self, xact, path): + def delete(self, xact, xpath): """ Delete a VLR record in DTS with path and message """ + path = self._project.add_project(xpath) self._log.debug("Deleting VLR xact = %s, %s", xact, path) self.regh.delete_element(path) self._log.debug("Deleted VLR xact = %s, %s", xact, path) @@ -453,8 +467,13 @@ class VldDtsHandler(object): "Got on prepare for VLD update (ks_path: %s) (action: %s)", ks_path.to_xpath(VldYang.get_schema()), msg) - schema = VldYang.YangData_Vld_VldCatalog_Vld.schema() + schema = VldYang.YangData_RwProject_Project_VldCatalog_Vld.schema() path_entry = schema.keyspec_to_entry(ks_path) + # TODO: Check why on project delete this gets called + if not path_entry: + xact_info.respond_xpath(rwdts.XactRspCode.ACK) + return + vld_id = path_entry.key00.id disabled_actions = [rwdts.QueryAction.DELETE, rwdts.QueryAction.UPDATE] @@ -477,7 +496,14 @@ class VldDtsHandler(object): handler = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare) yield from self._dts.register( - VldDtsHandler.XPATH, + self._vnsm._project.add_project(VldDtsHandler.XPATH), flags=rwdts.Flag.SUBSCRIBER, handler=handler ) + + def deregister(self): + self._log.debug("De-register VLD handler for project {}". + format(self._vnsm._project.name)) + if self._regh: + self._regh.deregister() + self._regh = None diff --git a/rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang b/rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang index 5c68f002..747fbc52 100644 --- a/rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang +++ b/rwlaunchpad/plugins/yang/rw-pkg-mgmt.yang @@ -260,6 +260,7 @@ module rw-pkg-mgmt description "Valid ID to track the status of the task"; type string; } + uses manotypes:rpc-project-name; } } @@ -277,6 +278,7 @@ module rw-pkg-mgmt description "Valid ID to track the status of the task"; type string; } + uses manotypes:rpc-project-name; } } @@ -316,6 +318,8 @@ module rw-pkg-mgmt description "Valid ID to track the status of the task"; type string; } + + uses manotypes:rpc-project-name; } } @@ -333,6 +337,7 @@ module rw-pkg-mgmt description "Valid ID to track the status of the task"; type string; } + uses manotypes:rpc-project-name; } } @@ -354,6 +359,8 @@ module rw-pkg-mgmt description "Trace in case of a failure"; type string; } + + uses manotypes:rpc-project-name; } } diff --git a/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_slb.py b/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_slb.py index 557518b3..fb4bd613 100755 --- a/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_slb.py +++ b/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_slb.py @@ -71,7 +71,7 @@ def create_nsr(nsd_id, input_param_list, cloud_account_name): Return: NSR object """ - nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = rift.auto.mano.resource_name(nsr.id) @@ -210,11 +210,11 @@ class TestMultiVmVnfSlb(object): nsd = catalog.nsd[0] input_parameters = [] - descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id + descr_xpath = "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id descr_value = "New NSD Description" in_param_id = str(uuid.uuid4()) - input_param_1= NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( + input_param_1= NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( xpath=descr_xpath, value=descr_value) diff --git a/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_trafgen.py b/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_trafgen.py index ca6e9b52..b05a23cb 100755 --- a/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_trafgen.py +++ b/rwlaunchpad/ra/pytest/multivm_vnf/test_multi_vm_vnf_trafgen.py @@ -78,7 +78,7 @@ def create_nsr(nsd_id, input_param_list, cloud_account_name): Return: NSR object """ - nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = rift.auto.mano.resource_name(nsr.id) @@ -206,11 +206,11 @@ class TestMultiVmVnfTrafgenApp(object): nsd = catalog.nsd[0] input_parameters = [] - descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id + descr_xpath = "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id descr_value = "New NSD Description" in_param_id = str(uuid.uuid4()) - input_param_1= NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( + input_param_1= NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( xpath=descr_xpath, value=descr_value) diff --git a/rwlaunchpad/ra/pytest/ns/haproxy/test_scaling.py b/rwlaunchpad/ra/pytest/ns/haproxy/test_scaling.py index 846ef2e5..3cf2c526 100644 --- a/rwlaunchpad/ra/pytest/ns/haproxy/test_scaling.py +++ b/rwlaunchpad/ra/pytest/ns/haproxy/test_scaling.py @@ -25,8 +25,8 @@ def proxy(request, mgmt_session): return mgmt_session.proxy -ScalingGroupInstance = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup_Instance -ScalingGroup = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup +ScalingGroupInstance = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup_Instance +ScalingGroup = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup INSTANCE_ID = 1 diff --git a/rwlaunchpad/ra/pytest/ns/pingpong/test_pingpong.py b/rwlaunchpad/ra/pytest/ns/pingpong/test_pingpong.py index 45a7832c..ef4949cc 100644 --- a/rwlaunchpad/ra/pytest/ns/pingpong/test_pingpong.py +++ b/rwlaunchpad/ra/pytest/ns/pingpong/test_pingpong.py @@ -98,7 +98,7 @@ def create_nsr(nsd, input_param_list, cloud_account_name): Return: NSR object """ - nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = rift.auto.mano.resource_name(nsr.id) @@ -409,11 +409,11 @@ class TestPingPongStart(object): nsd = catalog.nsd[0] input_parameters = [] - descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:vendor" % nsd.id + descr_xpath = "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:vendor" % nsd.id descr_value = "automation" in_param_id = str(uuid.uuid4()) - input_param_1 = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( + input_param_1 = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( xpath=descr_xpath, value=descr_value) @@ -597,11 +597,11 @@ class TestUpdateNsr(object): nsd = catalog.nsd[0] input_parameters = [] - descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:vendor" % nsd.id + descr_xpath = "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:vendor" % nsd.id descr_value = "automation" in_param_id = str(uuid.uuid4()) - input_param_1 = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( + input_param_1 = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( xpath=descr_xpath, value=descr_value) diff --git a/rwlaunchpad/ra/pytest/ns/test_onboard.py b/rwlaunchpad/ra/pytest/ns/test_onboard.py index 5951ce87..ecec8340 100644 --- a/rwlaunchpad/ra/pytest/ns/test_onboard.py +++ b/rwlaunchpad/ra/pytest/ns/test_onboard.py @@ -99,7 +99,7 @@ def create_nsr(nsd, input_param_list, cloud_account_name): Return: NSR object """ - nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = rift.auto.mano.resource_name(nsr.id) @@ -333,11 +333,11 @@ class TestNsrStart(object): nsd = catalog.nsd[0] input_parameters = [] - descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id + descr_xpath = "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id descr_value = "New NSD Description" in_param_id = str(uuid.uuid4()) - input_param_1 = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( + input_param_1 = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( xpath=descr_xpath, value=descr_value) diff --git a/rwlaunchpad/test/mano_error_ut.py b/rwlaunchpad/test/mano_error_ut.py index e593ceef..0790290d 100755 --- a/rwlaunchpad/test/mano_error_ut.py +++ b/rwlaunchpad/test/mano_error_ut.py @@ -174,8 +174,8 @@ class NetworkResourceRequestMockEventHandler(object): class ResourceMgrMock(object): - VDU_REQUEST_XPATH = "D,/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data" - VLINK_REQUEST_XPATH = "D,/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data" + VDU_REQUEST_XPATH = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vdu-event/vdu-event-data" + VLINK_REQUEST_XPATH = "D,/rw-project:project/rw-resource-mgr:resource-mgmt/vlink-event/vlink-event-data" def __init__(self, dts, log, loop): self._log = log diff --git a/rwlaunchpad/test/mano_ut.py b/rwlaunchpad/test/mano_ut.py index 1482ec3c..3835ab50 100755 --- a/rwlaunchpad/test/mano_ut.py +++ b/rwlaunchpad/test/mano_ut.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# +# # Copyright 2016-2017 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -57,8 +57,14 @@ import rift.mano.examples.ping_pong_nsd as ping_pong_nsd import rift.tasklets import rift.test.dts import rw_peas +from rift.mano.utils.project import ( + ManoProject, + DEFAULT_PROJECT, +) +PROJECT = 'default' + openstack_info = { 'username': 'pluto', 'password': 'mypasswd', @@ -125,11 +131,8 @@ class XPaths(object): @staticmethod def cm_state(k=None): - if k is None: - return ("D,/rw-conman:cm-state/rw-conman:cm-nsr") - else: - return ("D,/rw-conman:cm-state/rw-conman:cm-nsr" + - ("[rw-conman:id='{}']".format(k) if k is not None else "")) + return ("D,/rw-conman:cm-state/rw-conman:cm-nsr" + + ("[rw-conman:id='{}']".format(k) if k is not None else "")) @staticmethod def nsr_scale_group_instance(nsr_id=None, group_name=None, index=None): @@ -149,19 +152,37 @@ class XPaths(object): ("/nsr:instance") + ("[nsr:index='{}']".format(index) if index is not None else "")) + @staticmethod + def cloud_account(k=None): + return ("C,/rw-cloud:cloud/rw-cloud:account" + + ("[rw-cloud:name='{}']".format(k) if k is not None else "")) + + @staticmethod + def project(k=None): + return ("C,/rw-project:project" + + ("[rw-project:name='{}']".format(k) if k is not None else "")) + class ManoQuerier(object): - def __init__(self, log, dts): + def __init__(self, log, dts, project): self.log = log self.dts = dts + self.project = project + + def add_project(self, xpath): + return self.project.add_project(xpath) @asyncio.coroutine - def _read_query(self, xpath, do_trace=False): - self.log.debug("Running XPATH read query: %s (trace: %s)", xpath, do_trace) + def _read_query(self, xpath, do_trace=False, project=True): + if project: + xp = self.add_project(xpath) + else: + xp = xpath + self.log.debug("Running XPATH read query: %s (trace: %s)", xp, do_trace) flags = rwdts.XactFlag.MERGE flags += rwdts.XactFlag.TRACE if do_trace else 0 res_iter = yield from self.dts.query_read( - xpath, flags=flags + xp, flags=flags ) results = [] @@ -172,6 +193,27 @@ class ManoQuerier(object): return results + @asyncio.coroutine + def _delete_query(self, xpath, flags=0): + xp = self.add_project(xpath) + self.log.debug("Running XPATH delete query: %s (flags: %d)", xp, flags) + with self.dts.transaction() as xact: + yield from self.dts.query_delete( + xp, + flags + ) + + @asyncio.coroutine + def _update_query(self, xpath, msg, flags=0): + xp = self.add_project(xpath) + self.log.debug("Running XPATH update query: %s (flags: %d)", xp, flags) + with self.dts.transaction() as xact: + yield from self.dts.query_update( + xp, + flags, + msg + ) + @asyncio.coroutine def get_cm_state(self, nsr_id=None): return (yield from self._read_query(XPaths.cm_state(nsr_id), False)) @@ -183,7 +225,6 @@ class ManoQuerier(object): @asyncio.coroutine def get_nsr_scale_group_instance_opdata(self, nsr_id=None, group_name=None, index=None): return (yield from self._read_query(XPaths.nsr_scale_group_instance(nsr_id, group_name, index), False)) - #return (yield from self._read_query(XPaths.nsr_scale_group_instance(nsr_id, group_name), True)) @asyncio.coroutine def get_nsr_configs(self, nsr_id=None): @@ -211,66 +252,34 @@ class ManoQuerier(object): @asyncio.coroutine def delete_nsr(self, nsr_id): - with self.dts.transaction() as xact: - yield from self.dts.query_delete( - XPaths.nsr_config(nsr_id), - 0 - #rwdts.XactFlag.TRACE, - #rwdts.Flag.ADVISE, - ) + return (yield from self._delete_query(XPaths.nsr_config(nsr_id))) @asyncio.coroutine def delete_nsd(self, nsd_id): - nsd_xpath = XPaths.nsd(nsd_id) - self.log.debug("Attempting to delete NSD with path = %s", nsd_xpath) - with self.dts.transaction() as xact: - yield from self.dts.query_delete( - nsd_xpath, - rwdts.XactFlag.ADVISE, - ) + return (yield from self._delete_query(XPaths.nsd(nsd_id), + rwdts.XactFlag.ADVISE)) @asyncio.coroutine def delete_vnfd(self, vnfd_id): - vnfd_xpath = XPaths.vnfd(vnfd_id) - self.log.debug("Attempting to delete VNFD with path = %s", vnfd_xpath) - with self.dts.transaction() as xact: - yield from self.dts.query_delete( - vnfd_xpath, - rwdts.XactFlag.ADVISE, - ) + return (yield from self._delete_query(XPaths.vnfd(vnfd_id), + rwdts.XactFlag.ADVISE)) @asyncio.coroutine def update_nsd(self, nsd_id, nsd_msg): - nsd_xpath = XPaths.nsd(nsd_id) - self.log.debug("Attempting to update NSD with path = %s", nsd_xpath) - with self.dts.transaction() as xact: - yield from self.dts.query_update( - nsd_xpath, - rwdts.XactFlag.ADVISE, - nsd_msg, - ) + return (yield from self._update_query(XPaths.nsd(nsd_id), nsd_msg, + rwdts.XactFlag.ADVISE)) @asyncio.coroutine def update_vnfd(self, vnfd_id, vnfd_msg): - vnfd_xpath = XPaths.vnfd(vnfd_id) - self.log.debug("Attempting to delete VNFD with path = %s", vnfd_xpath) - with self.dts.transaction() as xact: - yield from self.dts.query_update( - vnfd_xpath, - rwdts.XactFlag.ADVISE, - vnfd_msg, - ) + return (yield from self._update_query(XPaths.vnfd(vnfd_id), vnfd_msg, + rwdts.XactFlag.ADVISE)) @asyncio.coroutine def update_nsr_config(self, nsr_id, nsr_msg): - nsr_xpath = XPaths.nsr_config(nsr_id) - self.log.debug("Attempting to update NSR with path = %s", nsr_xpath) - with self.dts.transaction() as xact: - yield from self.dts.query_update( - nsr_xpath, - rwdts.XactFlag.ADVISE|rwdts.XactFlag.REPLACE, - nsr_msg, - ) + return (yield from self._update_query( + XPaths.nsr_config(nsr_id), + nsr_msg, + rwdts.XactFlag.ADVISE|rwdts.XactFlag.REPLACE)) class ManoTestCase(rift.test.dts.AbstractDTSTest): @@ -370,39 +379,47 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): nsd_ref_counts = yield from self.querier.get_nsd_ref_counts(nsd_id) self.assertEqual(num_ref, nsd_ref_counts[0].instance_ref_count) + class DescriptorPublisher(object): - def __init__(self, log, loop, dts): + def __init__(self, log, loop, dts, project): self.log = log self.loop = loop self.dts = dts + self.project = project self._registrations = [] @asyncio.coroutine def publish(self, w_path, path, desc): ready_event = asyncio.Event(loop=self.loop) + if 'rw-project' in path: + w_xp = w_path + xp = path + else: + w_xp = self.project.add_project(w_path) + xp = self.project.add_project(path) @asyncio.coroutine def on_ready(regh, status): self.log.debug("Create element: %s, obj-type:%s obj:%s", - path, type(desc), desc) + xp, type(desc), desc) with self.dts.transaction() as xact: - regh.create_element(path, desc, xact.xact) - self.log.debug("Created element: %s, obj:%s", path, desc) + regh.create_element(xp, desc, xact.xact) + self.log.debug("Created element: %s, obj:%s", xp, desc) ready_event.set() handler = rift.tasklets.DTS.RegistrationHandler( on_ready=on_ready ) - self.log.debug("Registering path: %s, obj:%s", w_path, desc) + self.log.debug("Registering path: %s, obj:%s", w_xp, desc) reg = yield from self.dts.register( - w_path, + w_xp, handler, flags=rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ ) self._registrations.append(reg) - self.log.debug("Registered path : %s", w_path) + self.log.debug("Registered path : %s", w_xp) yield from ready_event.wait() return reg @@ -413,23 +430,114 @@ class DescriptorPublisher(object): reg.deregister() +class ProjectPublisher(object): + XPATH = "C,/rw-project:project" + + def __init__(self, log, loop, dts, project): + self.dts = dts + self.log = log + self.loop = loop + self.project = project + self.ref = None + + self.querier = ManoQuerier(log, dts, project) + self.publisher = DescriptorPublisher(log, loop, + dts, project) + + self._ready_event = asyncio.Event(loop=self.loop) + asyncio.ensure_future(self.register(), loop=loop) + + @asyncio.coroutine + def register(self): + @asyncio.coroutine + def on_ready(regh, status): + self._ready_event.set() + + self.log.debug("Registering path: %s", ProjectPublisher.XPATH) + self.reg = yield from self.dts.register( + ProjectPublisher.XPATH, + flags=rwdts.Flag.PUBLISHER, + handler=rift.tasklets.DTS.RegistrationHandler( + on_ready=on_ready, + ), + ) + + def deregister(self): + if self.reg is not None: + self.reg.deregister() + + @asyncio.coroutine + def publish_project(self, config, xpath, xpath_wild): + # Publish project + self.log.debug("Publishing cloud_account path: %s - %s, type:%s, obj:%s", + xpath, xpath_wild, type(config), config) + yield from self.publisher.publish(xpath_wild, xpath, config) + + +class CloudAccountPublisher(object): + XPATH = "C,/rw-cloud:cloud" + + def __init__(self, log, loop, dts, project): + self.dts = dts + self.log = log + self.loop = loop + self.project = project + self.ref = None + + self.querier = ManoQuerier(log, dts, project) + self.publisher = DescriptorPublisher(log, loop, + dts, project) + + self.xpath = self.project.add_project(CloudAccountPublisher.XPATH) + + self._ready_event = asyncio.Event(loop=self.loop) + asyncio.ensure_future(self.register(), loop=loop) + + @asyncio.coroutine + def register(self): + @asyncio.coroutine + def on_ready(regh, status): + self._ready_event.set() + + self.log.debug("Registering path: %s", self.xpath) + self.reg = yield from self.dts.register( + self.xpath, + flags=rwdts.Flag.PUBLISHER, + handler=rift.tasklets.DTS.RegistrationHandler( + on_ready=on_ready, + ), + ) + + def deregister(self): + if self.reg is not None: + self.reg.deregister() + + @asyncio.coroutine + def publish_account(self, account, xpath, xpath_wild): + # Publish cloud account + self.log.debug("Publishing cloud_account path: %s - %s, type:%s, obj:%s", + xpath, xpath_wild, type(account), account) + yield from self.publisher.publish(xpath_wild, xpath, account) + + class PingPongNsrConfigPublisher(object): XPATH = "C,/nsr:ns-instance-config" - def __init__(self, log, loop, dts, ping_pong, cloud_account_name): + def __init__(self, log, loop, dts, ping_pong, cloud_account_name, project): self.dts = dts self.log = log self.loop = loop + self.project = project self.ref = None - self.querier = ManoQuerier(log, dts) - - self.nsr_config = rwnsryang.YangData_Nsr_NsInstanceConfig() + self.querier = ManoQuerier(log, dts, project) + self.xpath = self.project.add_project(PingPongNsrConfigPublisher.XPATH) + self.nsr_config = rwnsryang.YangData_RwProject_Project_NsInstanceConfig() - nsr = rwnsryang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = rwnsryang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "ns1.{}".format(nsr.id) - nsr.nsd = nsryang.YangData_Nsr_NsInstanceConfig_Nsr_Nsd() + nsr.nsd = nsryang.YangData_RwProject_Project_NsInstanceConfig_Nsr_Nsd() nsr.nsd.from_dict(ping_pong.ping_pong_nsd.nsd.as_dict()) nsr.cloud_account = cloud_account_name @@ -439,8 +547,9 @@ class PingPongNsrConfigPublisher(object): #'cloud_account':'mock_account1' }) - inputs = nsryang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter() - inputs.xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id={}]/nsd:name".format(ping_pong.nsd_id) + inputs = nsryang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter() + inputs.xpath = self.project.add_project( + "/nsd:nsd-catalog/nsd:nsd[nsd:id={}]/nsd:name".format(ping_pong.nsd_id)) inputs.value = "inigo montoya" fast_cpu = {'metadata_key': 'FASTCPU', 'metadata_value': 'True'} @@ -488,9 +597,9 @@ class PingPongNsrConfigPublisher(object): def on_ready(regh, status): self._ready_event.set() - self.log.debug("Registering path: %s", PingPongNsrConfigPublisher.XPATH) + self.log.debug("Registering path: %s", self.xpath) self.reg = yield from self.dts.register( - PingPongNsrConfigPublisher.XPATH, + self.xpath, flags=rwdts.Flag.PUBLISHER, handler=rift.tasklets.DTS.RegistrationHandler( on_ready=on_ready, @@ -503,7 +612,7 @@ class PingPongNsrConfigPublisher(object): yield from self._ready_event.wait() with self.dts.transaction() as xact: self.reg.create_element( - PingPongNsrConfigPublisher.XPATH, + self.xpath, self.nsr_config, xact=xact.xact, ) @@ -520,7 +629,7 @@ class PingPongNsrConfigPublisher(object): }) with self.dts.transaction() as xact: self.reg.update_element( - PingPongNsrConfigPublisher.XPATH, + self.xpath, self.nsr_config, xact=xact.xact, ) @@ -539,7 +648,7 @@ class PingPongNsrConfigPublisher(object): "cloud_type" : cloud_type, construct_type : construct_value, }) - + def create_vnfd_placement_group_map(self, nsr, @@ -555,21 +664,16 @@ class PingPongNsrConfigPublisher(object): "cloud_type" : cloud_type, construct_type : construct_value, }) - - + + @asyncio.coroutine def delete_scale_group_instance(self, group_name, index): self.log.debug("Deleting scale group %s instance %s", group_name, index) #del self.nsr_config.nsr[0].scaling_group[0].instance[0] - xpath = XPaths.nsr_scale_group_instance_config(self.nsr_config.nsr[0].id, group_name, index) + xpath = self.project.add_project( + XPaths.nsr_scale_group_instance_config(self.nsr_config.nsr[0].id, + group_name, index)) yield from self.dts.query_delete(xpath, flags=rwdts.XactFlag.ADVISE) - #with self.dts.transaction() as xact: - # self.reg.update_element( - # PingPongNsrConfigPublisher.XPATH, - # self.nsr_config, - # flags=rwdts.XactFlag.REPLACE, - # xact=xact.xact, - # ) def deregister(self): if self.reg is not None: @@ -617,10 +721,12 @@ class PingPongNsrConfigPublisher(object): def update_vnf_cloud_map(self,vnf_cloud_map): self.log.debug("Modifying NSR to add VNF cloud account map: {}".format(vnf_cloud_map)) for vnf_index,cloud_acct in vnf_cloud_map.items(): - vnf_maps = [vnf_map for vnf_map in self.nsr_config.nsr[0].vnf_cloud_account_map if vnf_index == vnf_map.member_vnf_index_ref] + vnf_maps = [vnf_map for vnf_map in \ + self.nsr_config.nsr[0].vnf_cloud_account_map \ + if vnf_index == vnf_map.member_vnf_index_ref] if vnf_maps: vnf_maps[0].cloud_account = cloud_acct - else: + else: self.nsr_config.nsr[0].vnf_cloud_account_map.add().from_dict({ 'member_vnf_index_ref':vnf_index, 'cloud_account':cloud_acct @@ -628,13 +734,16 @@ class PingPongNsrConfigPublisher(object): class PingPongDescriptorPublisher(object): - def __init__(self, log, loop, dts, num_external_vlrs=1, num_internal_vlrs=1, num_ping_vms=1): + def __init__(self, log, loop, dts, project, + num_external_vlrs=1, num_internal_vlrs=1, num_ping_vms=1): self.log = log self.loop = loop self.dts = dts + self.project = project - self.querier = ManoQuerier(self.log, self.dts) - self.publisher = DescriptorPublisher(self.log, self.loop, self.dts) + self.querier = ManoQuerier(self.log, self.dts, self.project) + self.publisher = DescriptorPublisher(self.log, self.loop, + self.dts, self.project) self.ping_vnfd, self.pong_vnfd, self.ping_pong_nsd = \ ping_pong_nsd.generate_ping_pong_descriptors( pingcount=1, @@ -717,8 +826,6 @@ class PingPongDescriptorPublisher(object): ) - - class ManoTestCase(rift.test.dts.AbstractDTSTest): """ DTS GI interface unittests @@ -755,9 +862,9 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): @staticmethod def get_cal_account(account_type, account_name): """ - Creates an object for class RwcalYang.Clo + Creates an object for class RwcalYang.Cloud """ - account = rwcloudyang.CloudAccount() + account = rwcloudyang.CloudAcc() if account_type == 'mock': account.name = account_name account.account_type = "mock" @@ -773,13 +880,33 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): return account @asyncio.coroutine - def configure_cloud_account(self, dts, cloud_type, cloud_name="cloud1"): + def configure_project(self, project=None): + if project is None: + project = self.project + + proj_xpath = "C,{}/project-config".format(project.prefix) + self.log.info("Creating project: {} with {}". + format(proj_xpath, project.config.as_dict())) + xpath_wild = "C,/rw-project:project/project-config" + yield from self.project_publisher.publish_project(project.config, + proj_xpath, + xpath_wild) + + @asyncio.coroutine + def configure_cloud_account(self, dts, cloud_type, cloud_name="cloud1", project=None): account = self.get_cal_account(cloud_type, cloud_name) - account_xpath = "C,/rw-project:project/rw-cloud:cloud/rw-cloud:account[rw-cloud:name='{}']".format(cloud_name) self.log.info("Configuring cloud-account: %s", account) - yield from dts.query_create(account_xpath, - rwdts.XactFlag.ADVISE, - account) + if project is None: + project = self.project + xpath = project.add_project(XPaths.cloud_account(account.name)) + xpath_wild = project.add_project(XPaths.cloud_account()) + + # account_xpath = project.add_project( + # "C,/rw-cloud:cloud/rw-cloud:account[rw-cloud:name='{}']".format(cloud_name)) + # yield from dts.query_create(account_xpath, + # rwdts.XactFlag.ADVISE, + # account) + yield from self.cloud_publisher.publish_account(account, xpath, xpath_wild) @asyncio.coroutine def wait_tasklets(self): @@ -789,22 +916,74 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): self.log.debug("STARTING - %s", self.id()) self.tinfo = self.new_tinfo(self.id()) self.dts = rift.tasklets.DTS(self.tinfo, self.schema, self.loop) - self.ping_pong = PingPongDescriptorPublisher(self.log, self.loop, self.dts) - self.querier = ManoQuerier(self.log, self.dts) + self.project = ManoProject(self.log, + name=DEFAULT_PROJECT) + self.project1 = ManoProject(self.log, + name='test-1') + self.ping_pong = PingPongDescriptorPublisher(self.log, self.loop, + self.dts, self.project) + self.querier = ManoQuerier(self.log, self.dts, self.project) + self.project_publisher = ProjectPublisher( + self.log, + loop, + self.dts, + self.project + ) + self.cloud_publisher = CloudAccountPublisher( + self.log, + loop, + self.dts, + self.project + ) self.nsr_publisher = PingPongNsrConfigPublisher( self.log, loop, self.dts, self.ping_pong, "mock_account", + self.project, ) def test_create_nsr_record(self): + @asyncio.coroutine + def verify_projects(termination=False): + self.log.debug("Verifying projects = %s", XPaths.project()) + + accts = yield from self.querier._read_query(XPaths.project(), + project=False) + projs = [] + for acc in accts: + self.log.debug("Project: {}".format(acc.as_dict())) + if acc.name not in projs: + projs.append(acc.name) + self.log.debug("Merged: {}".format(projs)) + self.assertEqual(2, len(projs)) + + @asyncio.coroutine + def verify_cloud_accounts(termination=False): + self.log.debug("Verifying cloud accounts = %s", XPaths.cloud_account()) + + accts = yield from self.querier._read_query(XPaths.cloud_account()) + self.assertEqual(2, len(accts)) + + accts = yield from self.querier._read_query( + self.project1.add_project(XPaths.cloud_account()), project=False) + self.assertEqual(1, len(accts)) + + accts = yield from self.querier._read_query( + "C,/rw-project:project/rw-cloud:cloud/rw-cloud:account", + project=False) + self.assertEqual(3, len(accts)) + + accts = yield from self.querier._read_query( + "C,/rw-project:project/rw-cloud:cloud/rw-cloud:account[rw-cloud:name='mock_account']", + project=False) + self.assertEqual(2, len(accts)) + @asyncio.coroutine def verify_cm_state(termination=False, nsrid=None): self.log.debug("Verifying cm_state path = %s", XPaths.cm_state(nsrid)) - #print("###>>> Verifying cm_state path:", XPaths.cm_state(nsrid)) loop_count = 10 loop_sleep = 10 @@ -878,7 +1057,7 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): nsr_config = nsr_configs[0] self.assertEqual( - "/nsd:nsd-catalog/nsd:nsd[nsd:id={}]/nsd:name".format(self.ping_pong.nsd_id), + "/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id={}]/nsd:name".format(self.ping_pong.nsd_id), nsr_config.input_parameter[0].xpath, ) @@ -1031,6 +1210,9 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): yield from verify_cm_state(termination, nsrid) yield from verify_nsr_config_status(termination, nsrid) + yield from verify_cloud_account(termination) + yield from verify_project_record(termination) + @asyncio.coroutine def verify_scale_instance(index): self.log.debug("Verifying scale record path = %s, Termination=%d", @@ -1074,12 +1256,20 @@ class ManoTestCase(rift.test.dts.AbstractDTSTest): def run_test(): yield from self.wait_tasklets() + yield from self.configure_project() + yield from self.configure_project(project=self.project1) cloud_type = "mock" yield from self.configure_cloud_account(self.dts, cloud_type, "mock_account") yield from self.configure_cloud_account(self.dts, cloud_type, "mock_account1") + yield from self.configure_cloud_account(self.dts, cloud_type, "mock_account", + project=self.project1) + + yield from verify_cloud_accounts() + yield from verify_projects() yield from self.ping_pong.publish_desciptors() + return # Attempt deleting VNFD not in use yield from self.ping_pong.update_ping_vnfd() diff --git a/rwlaunchpad/test/pytest/lp_kt_utm_test.py b/rwlaunchpad/test/pytest/lp_kt_utm_test.py index 0a8d6ba5..feb0b993 100644 --- a/rwlaunchpad/test/pytest/lp_kt_utm_test.py +++ b/rwlaunchpad/test/pytest/lp_kt_utm_test.py @@ -206,7 +206,7 @@ def wait_unboard_transaction_finished(logger, transaction_id, timeout_secs=600, raise DescriptorOnboardError(state) def create_nsr_from_nsd_id(nsd_id): - nsr = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "UTM-only" nsr.short_name = "UTM-only" @@ -255,14 +255,14 @@ class TestLaunchpadStartStop(object): "resource_type": "network", "pool_type" : "dynamic",}]}) - resource_mgr_proxy.merge_config('/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) + resource_mgr_proxy.merge_config('/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) def test_configure_resource_orchestrator(self, so_proxy): cfg = RwConmanYang.RoEndpoint.from_dict({'ro_ip_address': '127.0.0.1', 'ro_port' : 2022, 'ro_username' : 'admin', 'ro_password' : 'admin'}) - so_proxy.merge_config('/rw-conman:cm-config', cfg) + so_proxy.merge_config('/rw-project:project/rw-conman:cm-config', cfg) def test_configure_service_orchestrator(self, nsm_proxy): cfg = RwNsmYang.SoEndpoint.from_dict({'cm_ip_address': '127.0.0.1', diff --git a/rwlaunchpad/test/pytest/lp_kt_utm_wims_test.py b/rwlaunchpad/test/pytest/lp_kt_utm_wims_test.py index 9dc9fe21..4e73aac3 100644 --- a/rwlaunchpad/test/pytest/lp_kt_utm_wims_test.py +++ b/rwlaunchpad/test/pytest/lp_kt_utm_wims_test.py @@ -223,7 +223,7 @@ def wait_unboard_transaction_finished(logger, transaction_id, timeout_secs=600, raise DescriptorOnboardError(state) def create_nsr_from_nsd_id(nsd_id): - nsr = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "UTM-WIMS" nsr.short_name = "UTM-WIMS" @@ -272,14 +272,14 @@ class TestLaunchpadStartStop(object): "resource_type": "network", "pool_type" : "dynamic",}]}) - resource_mgr_proxy.merge_config('/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) + resource_mgr_proxy.merge_config('/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) def test_configure_resource_orchestrator(self, so_proxy): cfg = RwConmanYang.RoEndpoint.from_dict({'ro_ip_address': '127.0.0.1', 'ro_port' : 2022, 'ro_username' : 'admin', 'ro_password' : 'admin'}) - so_proxy.merge_config('/rw-conman:cm-config', cfg) + so_proxy.merge_config('/rw-project:project/rw-conman:cm-config', cfg) def test_configure_service_orchestrator(self, nsm_proxy): cfg = RwNsmYang.SoEndpoint.from_dict({'cm_ip_address': '127.0.0.1', diff --git a/rwlaunchpad/test/pytest/lp_test.py b/rwlaunchpad/test/pytest/lp_test.py index b987b358..8b41cb1f 100644 --- a/rwlaunchpad/test/pytest/lp_test.py +++ b/rwlaunchpad/test/pytest/lp_test.py @@ -172,7 +172,7 @@ def ping_pong_nsd_package_file(): def create_nsr_from_nsd_id(nsd_id): - nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = RwNsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "pingpong_{}".format(datetime.datetime.now().strftime("%Y%m%d_%H%M%S")) nsr.short_name = "nsr_short_name" @@ -181,8 +181,8 @@ def create_nsr_from_nsd_id(nsd_id): nsr.admin_status = "ENABLED" nsr.cloud_account = "openstack" - param = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter() - param.xpath = '/nsd:nsd-catalog/nsd:nsd/nsd:vendor' + param = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter() + param.xpath = '/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:vendor' param.value = "rift-o-matic" nsr.input_parameter.append(param) diff --git a/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_epa_test.py b/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_epa_test.py index 8f7334b8..3470b750 100644 --- a/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_epa_test.py +++ b/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_epa_test.py @@ -172,7 +172,7 @@ def tg_2vrouter_ts_nsd_package_file(): def create_nsr_from_nsd_id(nsd_id): - nsr = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "TG-2Vrouter-TS EPA" nsr.short_name = "TG-2Vrouter-TS EPA" @@ -251,14 +251,14 @@ class TestLaunchpadStartStop(object): "resource_type": "network", "pool_type" : "dynamic",}]}) - resource_mgr_proxy.merge_config('/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) + resource_mgr_proxy.merge_config('/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) def test_configure_resource_orchestrator(self, so_proxy): cfg = RwConmanYang.RoEndpoint.from_dict({'ro_ip_address': '127.0.0.1', 'ro_port' : 2022, 'ro_username' : 'admin', 'ro_password' : 'admin'}) - so_proxy.merge_config('/rw-conman:cm-config', cfg) + so_proxy.merge_config('/rw-project:project/rw-conman:cm-config', cfg) def test_configure_service_orchestrator(self, nsm_proxy): cfg = RwNsmYang.SoEndpoint.from_dict({'cm_ip_address': '127.0.0.1', diff --git a/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_test.py b/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_test.py index c2f27a7a..578c0af0 100644 --- a/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_test.py +++ b/rwlaunchpad/test/pytest/lp_tg_2vrouter_ts_test.py @@ -172,7 +172,7 @@ def tg_2vrouter_ts_nsd_package_file(): def create_nsr_from_nsd_id(nsd_id): - nsr = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "TG-2Vrouter-TS EPA" nsr.short_name = "TG-2Vrouter-TS EPA" @@ -251,14 +251,14 @@ class TestLaunchpadStartStop(object): "resource_type": "network", "pool_type" : "dynamic",}]}) - resource_mgr_proxy.merge_config('/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) + resource_mgr_proxy.merge_config('/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) def test_configure_resource_orchestrator(self, so_proxy): cfg = RwConmanYang.RoEndpoint.from_dict({'ro_ip_address': '127.0.0.1', 'ro_port' : 2022, 'ro_username' : 'admin', 'ro_password' : 'admin'}) - so_proxy.merge_config('/rw-conman:cm-config', cfg) + so_proxy.merge_config('/rw-project:project/rw-conman:cm-config', cfg) def test_configure_service_orchestrator(self, nsm_proxy): cfg = RwNsmYang.SoEndpoint.from_dict({'cm_ip_address': '127.0.0.1', diff --git a/rwlaunchpad/test/pytest/lp_tg_vrouter_ts_epa_sriov_test.py b/rwlaunchpad/test/pytest/lp_tg_vrouter_ts_epa_sriov_test.py index c8518476..405bee67 100644 --- a/rwlaunchpad/test/pytest/lp_tg_vrouter_ts_epa_sriov_test.py +++ b/rwlaunchpad/test/pytest/lp_tg_vrouter_ts_epa_sriov_test.py @@ -171,7 +171,7 @@ def tg_vrouter_ts_nsd_package_file(): def create_nsr_from_nsd_id(nsd_id): - nsr = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr.id = str(uuid.uuid4()) nsr.name = "TG-Vrouter-TS-EPA-SRIOV" nsr.short_name = "TG-Vrouter-TS-EPA-SRIOV" @@ -250,14 +250,14 @@ class TestLaunchpadStartStop(object): "resource_type": "network", "pool_type" : "dynamic",}]}) - resource_mgr_proxy.merge_config('/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) + resource_mgr_proxy.merge_config('/rw-project:project/rw-resource-mgr:resource-mgr-config/rw-resource-mgr:resource-pools', pools) def test_configure_resource_orchestrator(self, so_proxy): cfg = RwConmanYang.RoEndpoint.from_dict({'ro_ip_address': '127.0.0.1', 'ro_port' : 2022, 'ro_username' : 'admin', 'ro_password' : 'admin'}) - so_proxy.merge_config('/rw-conman:cm-config', cfg) + so_proxy.merge_config('/rw-project:project/rw-conman:cm-config', cfg) def test_configure_service_orchestrator(self, nsm_proxy): cfg = RwNsmYang.SoEndpoint.from_dict({'cm_ip_address': '127.0.0.1', diff --git a/rwlaunchpad/test/utest_nsr_handler.py b/rwlaunchpad/test/utest_nsr_handler.py index ffab9296..d921aca3 100755 --- a/rwlaunchpad/test/utest_nsr_handler.py +++ b/rwlaunchpad/test/utest_nsr_handler.py @@ -47,8 +47,8 @@ if sys.version_info < (3, 4, 4): class NsrDtsHandler(object): """ The network service DTS handler """ - NSR_XPATH = "C,/nsr:ns-instance-config/nsr:nsr" - SCALE_INSTANCE_XPATH = "C,/nsr:ns-instance-config/nsr:nsr/nsr:scaling-group/nsr:instance" + NSR_XPATH = "C,/rw-project:project/nsr:ns-instance-config/nsr:nsr" + SCALE_INSTANCE_XPATH = "C,/rw-project:project/nsr:ns-instance-config/nsr:nsr/nsr:scaling-group/nsr:instance" def __init__(self, dts, log, loop, nsm): self._dts = dts @@ -66,12 +66,12 @@ class NsrDtsHandler(object): def get_scale_group_instances(self, nsr_id, group_name): def nsr_id_from_keyspec(ks): - nsr_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) + nsr_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) nsr_id = nsr_path_entry.key00.id return nsr_id def group_name_from_keyspec(ks): - group_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) + group_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) group_name = group_path_entry.key00.scaling_group_name_ref return group_name @@ -95,12 +95,12 @@ class NsrDtsHandler(object): """ Register for Nsr create/update/delete/read requests from dts """ def nsr_id_from_keyspec(ks): - nsr_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) + nsr_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr.schema().keyspec_to_entry(ks) nsr_id = nsr_path_entry.key00.id return nsr_id def group_name_from_keyspec(ks): - group_path_entry = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) + group_path_entry = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup.schema().keyspec_to_entry(ks) group_name = group_path_entry.key00.scaling_group_name_ref return group_name @@ -327,11 +327,11 @@ class NsrDtsHandler(object): class XPaths(object): @staticmethod def nsr_config(nsr_id=None): - return ("C,/nsr:ns-instance-config/nsr:nsr" + + return ("C,/rw-project:project/nsr:ns-instance-config/nsr:nsr" + ("[nsr:id='{}']".format(nsr_id) if nsr_id is not None else "")) def scaling_group_instance(nsr_id, group_name, instance_id): - return ("C,/nsr:ns-instance-config/nsr:nsr" + + return ("C,/rw-project:project/nsr:ns-instance-config/nsr:nsr" + "[nsr:id='{}']".format(nsr_id) + "/nsr:scaling-group" + "[nsr:scaling-group-name-ref='{}']".format(group_name) + @@ -377,7 +377,7 @@ class NsrHandlerTestCase(rift.test.dts.AbstractDTSTest): block = xact.block_create() block.add_query_update( XPaths.nsr_config(nsr1_uuid), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr(id=nsr1_uuid, name="fu"), + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr(id=nsr1_uuid, name="fu"), flags=rwdts.XactFlag.ADVISE | rwdts.XactFlag.TRACE, ) yield from block.execute(now=True) @@ -388,7 +388,7 @@ class NsrHandlerTestCase(rift.test.dts.AbstractDTSTest): block = xact.block_create() block.add_query_update( XPaths.scaling_group_instance(nsr1_uuid, "group", 1234), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup_Instance(id=1234), + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup_Instance(id=1234), flags=rwdts.XactFlag.ADVISE | rwdts.XactFlag.TRACE, ) yield from block.execute(now=True) @@ -409,7 +409,7 @@ class NsrHandlerTestCase(rift.test.dts.AbstractDTSTest): block = xact.block_create() block.add_query_create( XPaths.scaling_group_instance(nsr1_uuid, "group", 12345), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_ScalingGroup_Instance(id=12345), + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_ScalingGroup_Instance(id=12345), flags=rwdts.XactFlag.ADVISE | rwdts.XactFlag.TRACE, ) yield from block.execute(now=True) @@ -427,7 +427,7 @@ class NsrHandlerTestCase(rift.test.dts.AbstractDTSTest): block = xact.block_create() block.add_query_update( XPaths.nsr_config(nsr2_uuid), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr(id=nsr2_uuid, name="fu2"), + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr(id=nsr2_uuid, name="fu2"), flags=rwdts.XactFlag.ADVISE | rwdts.XactFlag.TRACE, ) yield from block.execute(now=True) diff --git a/rwlaunchpad/test/utest_ro_account.py b/rwlaunchpad/test/utest_ro_account.py index aa485efe..9ed28fdc 100755 --- a/rwlaunchpad/test/utest_ro_account.py +++ b/rwlaunchpad/test/utest_ro_account.py @@ -115,7 +115,7 @@ class RoAccountDtsTestCase(rift.test.dts.AbstractDTSTest): {'name': 'rift-ro', 'account_type': 'rift_ro', 'rift_ro': {'rift_ro': True}}) # Test rift-ro plugin CREATE - w_xpath = "C,/rw-launchpad:resource-orchestrator" + w_xpath = "C,/rw-project:project/rw-launchpad:resource-orchestrator" xpath = w_xpath yield from self.publisher.publish(w_xpath, xpath, mock_orch_acc) yield from asyncio.sleep(5, loop=self.loop) @@ -139,7 +139,7 @@ class RoAccountDtsTestCase(rift.test.dts.AbstractDTSTest): # Test update mock_orch_acc.openmano.port = 9789 mock_orch_acc.openmano.host = "10.64.11.78" - yield from self.dts.query_update("C,/rw-launchpad:resource-orchestrator", + yield from self.dts.query_update("C,/rw-project:project/rw-launchpad:resource-orchestrator", rwdts.XactFlag.ADVISE, mock_orch_acc) assert orch.ro_plugin._cli_api._port == mock_orch_acc.openmano.port assert orch.ro_plugin._cli_api._host == mock_orch_acc.openmano.host @@ -150,11 +150,11 @@ class RoAccountDtsTestCase(rift.test.dts.AbstractDTSTest): mock_orch_acc.openmano.port = 9788 with self.assertRaises(Exception): - yield from self.dts.query_update("C,/rw-launchpad:resource-orchestrator", + yield from self.dts.query_update("C,/rw-project:project/rw-launchpad:resource-orchestrator", rwdts.XactFlag.ADVISE, mock_orch_acc) # Test delete - yield from self.dts.query_delete("C,/rw-launchpad:resource-orchestrator", + yield from self.dts.query_delete("C,/rw-project:project/rw-launchpad:resource-orchestrator", flags=rwdts.XactFlag.ADVISE) assert orch.ro_plugin == None diff --git a/rwlaunchpad/test/utest_rwmonitor.py b/rwlaunchpad/test/utest_rwmonitor.py index 46c33b3d..91f162f4 100755 --- a/rwlaunchpad/test/utest_rwmonitor.py +++ b/rwlaunchpad/test/utest_rwmonitor.py @@ -108,17 +108,17 @@ class MockTasklet(object): def make_nsr(ns_instance_config_ref=str(uuid.uuid4())): - nsr = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr() + nsr = NsrYang.YangData_RwProject_Project_NsInstanceOpdata_Nsr() nsr.ns_instance_config_ref = ns_instance_config_ref return nsr def make_vnfr(id=str(uuid.uuid4())): - vnfr = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + vnfr = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() vnfr.id = id return vnfr def make_vdur(id=str(uuid.uuid4()), vim_id=str(uuid.uuid4())): - vdur = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur() + vdur = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur() vdur.id = id vdur.vim_id = vim_id return vdur @@ -149,7 +149,7 @@ class TestNfviMetricsCache(unittest.TestCase): mock = self.plugin_manager.plugin(self.account.name) mock.set_impl(TestNfviMetricsCache.Plugin()) - self.vdur = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur() + self.vdur = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur() self.vdur.id = "test-vdur-id" self.vdur.vim_id = "test-vim-id" self.vdur.vm_flavor.vcpu_count = 4 @@ -207,7 +207,7 @@ class TestNfviMetrics(unittest.TestCase): return True def nfvi_metrics(self, account, vim_id): - metrics = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_NfviMetrics() + metrics = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_NfviMetrics() metrics.vcpu.utilization = 0.5 return None, metrics @@ -340,7 +340,7 @@ class TestNfviInterface(unittest.TestCase): pass def test_alarm_create_and_destroy(self): - alarm = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur_Alarms() + alarm = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur_Alarms() alarm.name = "test-alarm" alarm.description = "test-description" alarm.vdur_id = "test-vdur-id" @@ -606,7 +606,7 @@ class TestMonitor(unittest.TestCase): self.monitor.add_cloud_account(self.account) # Create a VNFR associated with the cloud account - vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() vnfr.cloud_account = self.account.name vnfr.id = 'test-vnfr-id' @@ -644,7 +644,7 @@ class TestMonitor(unittest.TestCase): to retrieve the NFVI metrics associated with the VDU. """ # Define the VDUR to be registered - vdur = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur() + vdur = VnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur() vdur.vm_flavor.vcpu_count = 4 vdur.vm_flavor.memory_mb = 100 vdur.vm_flavor.storage_gb = 2 @@ -680,11 +680,11 @@ class TestMonitor(unittest.TestCase): the VDURs contained in the VNFR are unregistered. """ # Define the VDUR to be registered - vdur = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur() + vdur = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur() vdur.vim_id = 'test-vim-id-1' vdur.id = 'test-vdur-id-1' - vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() vnfr.cloud_account = self.account.name vnfr.id = 'test-vnfr-id' @@ -699,7 +699,7 @@ class TestMonitor(unittest.TestCase): # Add another VDUR to the VNFR and update the monitor. Both VDURs # should now be registered - vdur = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur() + vdur = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr_Vdur() vdur.vim_id = 'test-vim-id-2' vdur.id = 'test-vdur-id-2' @@ -730,7 +730,7 @@ class TestMonitor(unittest.TestCase): Monitor. """ # Create the VNFR - vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr() + vnfr = RwVnfrYang.YangData_RwProject_Project_VnfrCatalog_Vnfr() vnfr.cloud_account = self.account.name vnfr.id = 'test-vnfr-id' diff --git a/rwlaunchpad/test/utest_rwnsm.py b/rwlaunchpad/test/utest_rwnsm.py index e125739e..fa997518 100755 --- a/rwlaunchpad/test/utest_rwnsm.py +++ b/rwlaunchpad/test/utest_rwnsm.py @@ -46,18 +46,18 @@ class TestGiXpath(unittest.TestCase): """ # Create the initial NSD catalog - nsd_catalog = NsdYang.YangData_Nsd_NsdCatalog() + nsd_catalog = NsdYang.YangData_RwProject_Project_NsdCatalog() # Create an NSD, set its 'id', and add it to the catalog nsd_id = str(uuid.uuid4()) nsd_catalog.nsd.append( - NsdYang.YangData_Nsd_NsdCatalog_Nsd( + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd( id=nsd_id, ) ) # Retrieve the NSD using and xpath expression - xpath = '/nsd:nsd-catalog/nsd:nsd[nsd:id={}]'.format(nsd_id) + xpath = '/rw-project:project/nsd:nsd-catalog/nsd:nsd[nsd:id={}]'.format(nsd_id) nsd = rwxpath.getxattr(nsd_catalog, xpath) self.assertEqual(nsd_id, nsd.id) @@ -74,19 +74,19 @@ class TestGiXpath(unittest.TestCase): """ # Define a simple NSD - nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd() + nsd = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() # Check that the unset fields are in fact set to None - self.assertEqual(None, rwxpath.getxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:name")) - self.assertEqual(None, rwxpath.getxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:short-name")) + self.assertEqual(None, rwxpath.getxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name")) + self.assertEqual(None, rwxpath.getxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name")) # Set the values of the 'name' and 'short-name' fields - rwxpath.setxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:name", "test-name") - rwxpath.setxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:short-name", "test-short-name") + rwxpath.setxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name", "test-name") + rwxpath.setxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name", "test-short-name") # Check that the 'name' and 'short-name' fields are correctly set - self.assertEqual(nsd.name, rwxpath.getxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:name")) - self.assertEqual(nsd.short_name, rwxpath.getxattr(nsd, "/nsd:nsd-catalog/nsd:nsd/nsd:short-name")) + self.assertEqual(nsd.name, rwxpath.getxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name")) + self.assertEqual(nsd.short_name, rwxpath.getxattr(nsd, "/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name")) class TestInputParameterSubstitution(unittest.TestCase): @@ -99,8 +99,8 @@ class TestInputParameterSubstitution(unittest.TestCase): config, no exception should be raised. """ - nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd() - nsr_config = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsd = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() + nsr_config = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() self.substitute_input_parameters(None, None) self.substitute_input_parameters(nsd, None) @@ -115,27 +115,27 @@ class TestInputParameterSubstitution(unittest.TestCase): """ # Define the original NSD - nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd() + nsd = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() nsd.name = "robert" nsd.short_name = "bob" # Define which parameters may be modified nsd.input_parameter_xpath.append( - NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:name", + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name", label="NSD Name", ) ) # Define the input parameters that are intended to be modified - nsr_config = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr_config = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr_config.input_parameter.extend([ - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:name", + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name", value="alice", ), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:short-name", + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name", value="alice", ), ]) @@ -153,31 +153,31 @@ class TestInputParameterSubstitution(unittest.TestCase): """ # Define the original NSD - nsd = NsdYang.YangData_Nsd_NsdCatalog_Nsd() + nsd = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd() nsd.name = "robert" nsd.short_name = "bob" # Define which parameters may be modified nsd.input_parameter_xpath.extend([ - NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:name", + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name", label="NSD Name", ), - NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:short-name", + NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name", label="NSD Short Name", ), ]) # Define the input parameters that are intended to be modified - nsr_config = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr() + nsr_config = NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr() nsr_config.input_parameter.extend([ - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:name", + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:name", value="robert", ), - NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter( - xpath="/nsd:nsd-catalog/nsd:nsd/nsd:short-name", + NsrYang.YangData_RwProject_Project_NsInstanceConfig_Nsr_InputParameter( + xpath="/rw-project:project/nsd:nsd-catalog/nsd:nsd/nsd:short-name", value="bob", ), ]) -- 2.25.1