From 573479ca6fca0e9f43a91464b68259dd754bb68a Mon Sep 17 00:00:00 2001 From: Saju Madhavan Date: Sun, 24 Jul 2016 01:40:40 +0530 Subject: [PATCH] Transition default VIM to API and DB operation Set the default-vim in the tacker-db and provide API to manage it. Change-Id: I7cdc40005122680098b2049cd95e74a8fea145eb Partial-Bug: 1592957 --- devstack/lib/tacker | 3 +- .../alembic_migrations/versions/HEAD | 2 +- .../d4f265e8eb9d_add_default_to_vim.py | 37 ++++++++++++++++++ tacker/db/nfvo/nfvo_db.py | 32 +++++++++++++++- tacker/extensions/nfvo.py | 14 +++++++ tacker/tests/functional/nfvo/test_vim.py | 3 +- tacker/tests/unit/vm/test_vim_client.py | 34 +++++++++++++++++ tacker/vm/vim_client.py | 38 ++++++++++++++----- 8 files changed, 148 insertions(+), 15 deletions(-) create mode 100644 tacker/db/migration/alembic_migrations/versions/d4f265e8eb9d_add_default_to_vim.py diff --git a/devstack/lib/tacker b/devstack/lib/tacker index 0d13bb652..04f72d50f 100644 --- a/devstack/lib/tacker +++ b/devstack/lib/tacker @@ -403,9 +403,8 @@ function tacker_register_default_vim { get_or_add_user_project_role "admin" $DEFAULT_VIM_USER $DEFAULT_VIM_PROJECT_NAME get_or_add_user_project_role "advsvc" $DEFAULT_VIM_USER $DEFAULT_VIM_PROJECT_NAME VIM_CONFIG_FILE="$TACKER_DIR/devstack/vim_config.yaml" - default_vim_id=$(tacker vim-register --name $DEFAULT_VIM_NAME --config-file $VIM_CONFIG_FILE -f value -c id) + default_vim_id=$(tacker vim-register --is-default --name $DEFAULT_VIM_NAME --config-file $VIM_CONFIG_FILE -f value -c id) echo $default_vim_id - iniset $TACKER_CONF nfvo_vim default_vim $DEFAULT_VIM_NAME } function modify_heat_flavor_policy_rule { diff --git a/tacker/db/migration/alembic_migrations/versions/HEAD b/tacker/db/migration/alembic_migrations/versions/HEAD index bfeee502c..141ac69db 100644 --- a/tacker/db/migration/alembic_migrations/versions/HEAD +++ b/tacker/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -22f5385a3d3f +d4f265e8eb9d \ No newline at end of file diff --git a/tacker/db/migration/alembic_migrations/versions/d4f265e8eb9d_add_default_to_vim.py b/tacker/db/migration/alembic_migrations/versions/d4f265e8eb9d_add_default_to_vim.py new file mode 100644 index 000000000..933323d05 --- /dev/null +++ b/tacker/db/migration/alembic_migrations/versions/d4f265e8eb9d_add_default_to_vim.py @@ -0,0 +1,37 @@ +# Copyright 2016 OpenStack Foundation +# +# 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. +# + +"""add default to vim + +Revision ID: d4f265e8eb9d +Revises: 22f5385a3d3f +Create Date: 2016-07-14 11:07:28.115225 + +""" + +# revision identifiers, used by Alembic. +revision = 'd4f265e8eb9d' +down_revision = '22f5385a3d3f' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import sql + + +def upgrade(active_plugins=None, options=None): + op.add_column('vims', sa.Column('is_default', + sa.Boolean(), + server_default=sql.false(), + nullable=False)) diff --git a/tacker/db/nfvo/nfvo_db.py b/tacker/db/nfvo/nfvo_db.py index 419d4ffbb..e44659dbf 100644 --- a/tacker/db/nfvo/nfvo_db.py +++ b/tacker/db/nfvo/nfvo_db.py @@ -33,7 +33,7 @@ from tacker import manager VIM_ATTRIBUTES = ('id', 'type', 'tenant_id', 'name', 'description', - 'placement_attr', 'shared', 'status') + 'placement_attr', 'shared', 'is_default', 'status') VIM_AUTH_ATTRIBUTES = ('auth_url', 'vim_project', 'password', 'auth_cred') @@ -44,6 +44,8 @@ class Vim(model_base.BASE, models_v1.HasId, models_v1.HasTenant): placement_attr = sa.Column(types.Json, nullable=True) shared = sa.Column(sa.Boolean, default=True, server_default=sql.true( ), nullable=False) + is_default = sa.Column(sa.Boolean, default=False, server_default=sql.false( + ), nullable=False) vim_auth = orm.relationship('VimAuth') status = sa.Column(sa.String(255), nullable=False) @@ -94,6 +96,7 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): raise def create_vim(self, context, vim): + self._validate_default_vim(context, vim) vim_cred = vim['auth_cred'] try: with context.session.begin(subtransactions=True): @@ -104,6 +107,7 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): name=vim.get('name'), description=vim.get('description'), placement_attr=vim.get('placement_attr'), + is_default=vim.get('is_default'), status=vim.get('status')) context.session.add(vim_db) vim_auth_db = VimAuth( @@ -142,10 +146,15 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): filters=filters, fields=fields) def update_vim(self, context, vim_id, vim): + self._validate_default_vim(context, vim, vim_id=vim_id) with context.session.begin(subtransactions=True): vim_cred = vim['auth_cred'] vim_project = vim['vim_project'] + is_default = vim.get('is_default') try: + if is_default: + vim_db = self._get_resource(context, Vim, vim_id) + vim_db.update({'is_default': is_default}) vim_auth_db = (self._model_query(context, VimAuth).filter( VimAuth.vim_id == vim_id).with_lockmode('update').one()) except orm_exc.NoResultFound: @@ -165,11 +174,13 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): vim_db.update({'status': status}) return self._make_vim_dict(vim_db) + # Deprecated. Will be removed in Ocata release def get_vim_by_name(self, context, vim_name, fields=None, mask_password=True): vim_db = self._get_by_name(context, Vim, vim_name) return self._make_vim_dict(vim_db, mask_password=mask_password) + # Deprecated. Will be removed in Ocata release def _get_by_name(self, context, model, name): try: query = self._model_query(context, model) @@ -177,3 +188,22 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): except orm_exc.NoResultFound: if issubclass(model, Vim): raise + + def _validate_default_vim(self, context, vim, vim_id=None): + if not vim.get('is_default'): + return True + try: + vim_db = self._get_default_vim(context) + except orm_exc.NoResultFound: + return True + if vim_id == vim_db.id: + return True + raise nfvo.VimDefaultDuplicateException(vim_id=vim_db.id) + + def _get_default_vim(self, context): + query = self._model_query(context, Vim) + return query.filter(Vim.is_default == sql.true()).one() + + def get_default_vim(self, context): + vim_db = self._get_default_vim(context) + return self._make_vim_dict(vim_db, mask_password=False) diff --git a/tacker/extensions/nfvo.py b/tacker/extensions/nfvo.py index 9801df6ab..4c3c66dbe 100644 --- a/tacker/extensions/nfvo.py +++ b/tacker/extensions/nfvo.py @@ -37,18 +37,24 @@ class VimInUseException(exceptions.TackerException): message = _("VIM %(vim_id)s is still in use by VNF") +# Deprecated. Will be removed in Ocata release class VimDefaultNameNotDefined(exceptions.TackerException): message = _("Default VIM is not set. Either specify a" " valid VIM during the VNF creation or set default VIM" " in tacker.conf") +# Deprecated. Will be removed in Ocata release class VimDefaultIdException(exceptions.TackerException): message = _("Default VIM name %(vim_name)s is invalid or there are " "multiple VIM matches found. Please specify a valid default " "VIM in tacker.conf") +class VimDefaultDuplicateException(exceptions.TackerException): + message = _("Default VIM already exists %(vim_id)s.") + + class VimNotFoundException(exceptions.TackerException): message = _("Specified VIM id %(vim_id)s is invalid. Please verify and " "pass a valid VIM id") @@ -139,6 +145,11 @@ RESOURCE_ATTRIBUTE_MAP = { 'convert_to': attr.convert_to_boolean, 'required_by_policy': True }, + 'is_default': { + 'allow_post': True, + 'allow_put': True, + 'is_visible': True, + }, } } @@ -217,3 +228,6 @@ class NFVOPluginBase(service_base.NFVPluginBase): def get_vim_by_name(self, context, vim_name, fields=None, mask_password=True): raise NotImplementedError() + + def get_default_vim(self, context): + raise NotImplementedError() diff --git a/tacker/tests/functional/nfvo/test_vim.py b/tacker/tests/functional/nfvo/test_vim.py index 2a97cd575..cf10512c6 100644 --- a/tacker/tests/functional/nfvo/test_vim.py +++ b/tacker/tests/functional/nfvo/test_vim.py @@ -36,7 +36,8 @@ class VimTestCreate(base.BaseTackerTest): 'auth_url': auth_url, 'auth_cred': {'username': username, 'password': password}, - 'vim_project': {'name': project_name}}} + 'vim_project': {'name': project_name}, + 'is_default': False}} # Register vim vim_res = self.client.create_vim(vim_arg) diff --git a/tacker/tests/unit/vm/test_vim_client.py b/tacker/tests/unit/vm/test_vim_client.py index eca68e7da..f50b06714 100644 --- a/tacker/tests/unit/vm/test_vim_client.py +++ b/tacker/tests/unit/vm/test_vim_client.py @@ -13,6 +13,8 @@ import mock from oslo_config import cfg +from sqlalchemy.orm import exc as orm_exc + from tacker.extensions import nfvo from tacker import manager from tacker.tests.unit import base @@ -21,12 +23,44 @@ from tacker.vm import vim_client class TestVIMClient(base.TestCase): + def setUp(self): + super(TestVIMClient, self).setUp() + self.vim_info = {'id': 'aaaa', 'name': 'VIM0', + 'auth_cred': {'password': '****'}} + def test_get_vim_without_defined_default_vim(self): cfg.CONF.set_override( 'default_vim', '', 'nfvo_vim', enforce_type=True) vimclient = vim_client.VimClient() service_plugins = mock.Mock() + nfvo_plugin = mock.Mock() + nfvo_plugin.get_default_vim.side_effect = \ + orm_exc.NoResultFound() + service_plugins.get.return_value = nfvo_plugin with mock.patch.object(manager.TackerManager, 'get_service_plugins', return_value=service_plugins): self.assertRaises(nfvo.VimDefaultNameNotDefined, vimclient.get_vim, None) + + def test_get_vim_without_defined_default_vim_in_db(self): + cfg.CONF.set_override( + 'default_vim', 'VIM0', 'nfvo_vim', enforce_type=True) + vimclient = vim_client.VimClient() + service_plugins = mock.Mock() + nfvo_plugin = mock.Mock() + nfvo_plugin.get_default_vim.side_effect = \ + orm_exc.NoResultFound() + service_plugins.get.return_value = nfvo_plugin + with mock.patch.object(manager.TackerManager, 'get_service_plugins', + return_value=service_plugins): + get_vim_by_name = \ + mock.patch.object(vimclient, + '_get_default_vim_by_name').start() + get_vim_by_name.return_value = self.vim_info + build_vim_auth = \ + mock.patch.object(vimclient, + '_build_vim_auth').start() + build_vim_auth.return_value = mock.Mock() + vimclient.get_vim(None) + vimclient._get_default_vim_by_name.\ + assert_called_once_with(mock.ANY, mock.ANY, 'VIM0') diff --git a/tacker/vm/vim_client.py b/tacker/vm/vim_client.py index ee4ea29e8..c5a6e3806 100644 --- a/tacker/vm/vim_client.py +++ b/tacker/vm/vim_client.py @@ -1,5 +1,5 @@ -# Copyright 2015-2016 Brocade Communications Systems Inc -# All Rights Reserved. + +# Copyright 2015-2016 Brocade Communications Systems Ine All Rights Reserved. # # 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 @@ -18,6 +18,7 @@ import os from cryptography.fernet import Fernet from oslo_config import cfg from oslo_log import log as logging +from oslo_log import versionutils from tacker.extensions import nfvo from tacker import manager @@ -26,9 +27,12 @@ from tacker.plugins.common import constants LOG = logging.getLogger(__name__) CONF = cfg.CONF + OPTS = [ cfg.StrOpt( - 'default_vim', help=_('Default VIM for launching VNFs')) + 'default_vim', help=_('Default VIM for launching VNFs. ' + 'This option is deprecated and will be removed in Ocata release.'), + deprecated_for_removal=True) ] cfg.CONF.register_opts(OPTS, 'nfvo_vim') @@ -50,15 +54,20 @@ class VimClient(object): if not vim_id: LOG.debug(_('VIM id not provided. Attempting to find default ' 'VIM id')) - vim_name = cfg.CONF.nfvo_vim.default_vim - if not vim_name: - raise nfvo.VimDefaultNameNotDefined() try: - vim_info = nfvo_plugin.get_vim_by_name(context, vim_name, - mask_password=False) + vim_info = nfvo_plugin.get_default_vim(context) except Exception: - raise nfvo.VimDefaultIdException( - vim_name=vim_name) + LOG.debug(_('Default vim not set in db.' + 'Attempting to find default vim from tacker.conf')) + vim_name = cfg.CONF.nfvo_vim.default_vim + if not vim_name: + raise nfvo.VimDefaultNameNotDefined() + versionutils.report_deprecated_feature(LOG, 'Configuration of ' + 'default-vim in tacker.conf is deprecated and will be ' + 'removed in Newton cycle') + vim_info = self._get_default_vim_by_name(context, + nfvo_plugin, + vim_name) else: try: vim_info = nfvo_plugin.get_vim(context, vim_id, @@ -79,6 +88,15 @@ class VimClient(object): def region_valid(vim_regions, region_name): return region_name in vim_regions + # Deprecated. Will be removed in Ocata release + def _get_default_vim_by_name(self, context, plugin, vim_name): + try: + vim_info = plugin.get_vim_by_name(context, vim_name, + mask_password=False) + except Exception: + raise nfvo.VimDefaultIdException(vim_name=vim_name) + return vim_info + def _build_vim_auth(self, vim_info): LOG.debug('VIM id is %s', vim_info['id']) vim_auth = vim_info['auth_cred']