diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 7b9714bda..481b1fae2 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -198,7 +198,7 @@ function install_murano_apps() { function configure_service_broker { #Add needed options to murano.conf iniset $MURANO_CONF_FILE cfapi tenant "$MURANO_CFAPI_DEFAULT_TENANT" - iniset $MURANO_CONF_FILE cfapi bind_host $HOST_IP + iniset $MURANO_CONF_FILE cfapi bind_host "$MURANO_SERVICE_HOST" iniset $MURANO_CONF_FILE cfapi bind_port "$MURANO_CFAPI_SERVICE_PORT" iniset $MURANO_CONF_FILE cfapi auth_url "http://${KEYSTONE_AUTH_HOST}:5000/v2.0" } @@ -273,6 +273,19 @@ function cleanup_murano() { sudo rm -rf $MURANO_KEYSTONE_SIGNING_DIR } +function configure_murano_tempest_plugin() { + + # Check tempest for enabling + if is_service_enabled tempest; then + # Set murano service availability flag + iniset $TEMPEST_CONFIG service_available murano "True" + if is_service_enabled murano-cfapi; then + # Enable Service Broker tests if cfapi enabled + iniset $TEMPEST_CONFIG service_broker run_service_broker_tests "True" + fi + fi +} + #### lib/murano-dashboard # Dependencies: @@ -430,6 +443,9 @@ if is_service_enabled murano; then start_service_broker fi + echo_summary "Configuring Murano Tempest plugin" + configure_murano_tempest_plugin + # Give Murano some time to Start sleep 3 diff --git a/devstack/settings b/devstack/settings index 3662dd2d3..56c332d55 100644 --- a/devstack/settings +++ b/devstack/settings @@ -53,6 +53,10 @@ MURANO_APPS_BRANCH=${MURANO_APPS_BRANCH:-master} # This couldn't be done using exitsting variables, so that's why this variable was added. MURANO_RABBIT_VHOST=${MURANO_RABBIT_VHOST:-''} +# Settings needed for the Murano Tempest Plugin installation +TEMPEST_DIR=$DEST/tempest +TEMPEST_CONFIG_DIR=${TEMPEST_CONFIG_DIR:-$TEMPEST_DIR/etc} +TEMPEST_CONFIG=$TEMPEST_CONFIG_DIR/tempest.conf enable_service murano enable_service murano-api diff --git a/murano_tempest_tests/__init__.py b/murano_tempest_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/clients.py b/murano_tempest_tests/clients.py new file mode 100644 index 000000000..57a949430 --- /dev/null +++ b/murano_tempest_tests/clients.py @@ -0,0 +1,39 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest import clients +from tempest.common import cred_provider + +from murano_tempest_tests.services.service_broker import service_broker_client + + +class Manager(clients.Manager): + def __init__(self, credentials=None, service=None): + super(Manager, self).__init__(credentials, service) + self.service_broker_client = service_broker_client.ServiceBrokerClient( + self.auth_provider) + + +class AltManager(Manager): + def __init__(self, service=None): + super(AltManager, self).__init__( + cred_provider.get_configured_credentials('alt_user'), service) + + +class AdminManager(Manager): + def __init__(self, service=None): + super(AdminManager, self).__init__( + cred_provider.get_configured_credentials('identity_admin'), + service) diff --git a/murano_tempest_tests/config.py b/murano_tempest_tests/config.py new file mode 100644 index 000000000..15aa2b33a --- /dev/null +++ b/murano_tempest_tests/config.py @@ -0,0 +1,98 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg + +service_available_group = cfg.OptGroup(name="service_available", + title="Available OpenStack Services") + +ServiceAvailableGroup = [ + cfg.BoolOpt("murano", + default=True, + help="Whether or not murano is expected to be available"), +] + +application_catalog_group = cfg.OptGroup(name="application_catalog", + title="Application Catalog Options") + +service_broker_group = cfg.OptGroup(name="service_broker", + title="Service Broker Options") + +ApplicationCatalogGroup = [ + # Aplication catalog tempest configuration + cfg.StrOpt("region", + default="", + help="The application_catalog region name to use. If empty, " + "the value of identity.region is used instead. " + "If no such region is found in the service catalog, " + "the first found one is used."), + + cfg.StrOpt("identity_version", + default="v2", + help="Default identity version for " + "REST client authentication."), + + cfg.StrOpt("catalog_type", + default="application_catalog", + help="Catalog type of Application Catalog."), + + cfg.StrOpt("endpoint_type", + default="publicURL", + choices=["publicURL", "adminURL", "internalURL"], + help="The endpoint type for application catalog service."), + + cfg.IntOpt("build_interval", + default=3, + help="Time in seconds between applcation catalog" + " availability checks."), + + cfg.IntOpt("build_timeout", + default=500, + help="Timeout in seconds to wait for a application catalog" + " to become available.") +] + +ServiceBrokerGroup = [ + # Test runs control + cfg.StrOpt("run_service_broker_tests", + default=False, + help="Defines whether run service broker api tests or not"), + + cfg.StrOpt("identity_version", + default="v2", + help="Default identity version for " + "REST client authentication."), + + cfg.StrOpt("catalog_type", + default="service_broker", + help="Catalog type of Service Broker API"), + + cfg.StrOpt("endpoint_type", + default="publicURL", + choices=["publicURL", "adminURL", "internalURL"], + help="The endpoint type for service broker service"), + + cfg.IntOpt("build_interval", + default=3, + help="Time in seconds between service broker" + " availability checks."), + + cfg.IntOpt("build_timeout", + default=500, + help="Timeout in seconds to wait for a service broker" + " to become available.") + + +] diff --git a/murano_tempest_tests/plugin.py b/murano_tempest_tests/plugin.py new file mode 100644 index 000000000..50d5e825c --- /dev/null +++ b/murano_tempest_tests/plugin.py @@ -0,0 +1,47 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# 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 os + +from tempest import config +from tempest.test_discover import plugins + +from murano_tempest_tests import config as config_application_catalog + + +class MuranoTempestPlugin(plugins.TempestPlugin): + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(__file__)))[0] + test_dir = "murano_tempest_tests/tests" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + config.register_opt_group( + conf, config_application_catalog.service_available_group, + config_application_catalog.ServiceAvailableGroup) + config.register_opt_group( + conf, config_application_catalog.application_catalog_group, + config_application_catalog.ApplicationCatalogGroup) + config.register_opt_group( + conf, config_application_catalog.service_broker_group, + config_application_catalog.ServiceBrokerGroup) + + def get_opt_lists(self): + return [(config_application_catalog.application_catalog_group.name, + config_application_catalog.ApplicationCatalogGroup), + (config_application_catalog.service_broker_group.name, + config_application_catalog.ServiceBrokerGroup)] diff --git a/murano_tempest_tests/services/__init__.py b/murano_tempest_tests/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/services/service_broker/__init__.py b/murano_tempest_tests/services/service_broker/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/services/service_broker/service_broker_client.py b/murano_tempest_tests/services/service_broker/service_broker_client.py new file mode 100644 index 000000000..ced86c80e --- /dev/null +++ b/murano_tempest_tests/services/service_broker/service_broker_client.py @@ -0,0 +1,56 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# 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 base64 + +from tempest import config +from tempest_lib.common import rest_client + +CONF = config.CONF + + +class ServiceBrokerClient(rest_client.RestClient): + """Tempest REST client for Murano Service Broker""" + + def __init__(self, auth_provider): + super(ServiceBrokerClient, self).__init__( + auth_provider, + CONF.service_broker.catalog_type, + CONF.identity.region, + endpoint_type=CONF.service_broker.endpoint_type) + self.build_interval = CONF.service_broker.build_interval + self.build_timeout = CONF.service_broker.build_timeout + self.headers = self._generate_headers(auth_provider) + + @classmethod + def _generate_headers(cls, auth_provider): + """Generate base64-encoded auth string for murano-cfapi + + :param auth_provider: + :return: headers + """ + uname = auth_provider.credentials.username + pwd = auth_provider.credentials.password + + encoded_auth = base64.b64encode('{0}:{1}'.format(uname, pwd)) + headers = {"Authorization": "Basic " + encoded_auth} + return headers + + def get_applications_list(self): + """Get list of all available applications""" + uri = "/v2/catalog" + resp, body = self.get(uri, headers=self.headers) + self.expected_success(200, resp.status) + return self._parse_resp(body) diff --git a/murano_tempest_tests/tests/__init__.py b/murano_tempest_tests/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/tests/api/__init__.py b/murano_tempest_tests/tests/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/tests/api/service_broker/__init__.py b/murano_tempest_tests/tests/api/service_broker/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/murano_tempest_tests/tests/api/service_broker/base.py b/murano_tempest_tests/tests/api/service_broker/base.py new file mode 100644 index 000000000..2d1816a31 --- /dev/null +++ b/murano_tempest_tests/tests/api/service_broker/base.py @@ -0,0 +1,100 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.common import dynamic_creds +from tempest import config +from tempest import test + +from murano_tempest_tests import clients + +CONF = config.CONF + + +class BaseServiceBrokerTest(test.BaseTestCase): + """Base test class for Murano Service Broker API tests.""" + + @classmethod + def get_client_with_isolated_creds(cls, name=None, + type_of_creds="admin"): + + cls.dynamic_cred = dynamic_creds.DynamicCredentialProvider( + identity_version=CONF.service_broker.identity_version, + name=cls.__name__) + if "admin" in type_of_creds: + creds = cls.dynamic_cred.get_admin_creds() + elif "alt" in type_of_creds: + creds = cls.dynamic_cred.get_alt_creds() + else: + creds = cls.dynamic_cred.get_credentials(type_of_creds) + cls.dynamic_cred.type_of_creds = type_of_creds + + os = clients.Manager(credentials=creds) + client = os.service_broker_client + + return client + + @classmethod + def verify_nonempty(cls, *args): + if not all(args): + msg = "Missing API credentials in configuration." + raise cls.skipException(msg) + + @classmethod + def resource_setup(cls): + if not CONF.service_broker.run_service_broker_tests: + skip_msg = "Service Broker API tests are disabled" + cls.skipException(skip_msg) + if not CONF.service_available.murano: + skip_msg = "Murano is disabled" + raise cls.skipException(skip_msg) + super(BaseServiceBrokerTest, cls).resource_setup() + if not hasattr(cls, "os"): + cls.username = CONF.identity.username + cls.password = CONF.identity.password + cls.tenant_name = CONF.identity.tenant_name + cls.verify_nonempty(cls.username, cls.password, cls.tenant_name) + cls.os = clients.Manager() + cls.service_broker_client = cls.os.service_broker_client + + def setUp(self): + super(BaseServiceBrokerTest, self).setUp() + self.addCleanup(self.clear_isolated_creds) + + @classmethod + def resource_cleanup(cls): + super(BaseServiceBrokerTest, cls).resource_cleanup() + cls.clear_isolated_creds() + + @classmethod + def clear_isolated_creds(cls): + if hasattr(cls, "dynamic_cred"): + cls.dynamic_cred.clear_creds() + + +class BaseServiceBrokerAdminTest(BaseServiceBrokerTest): + + @classmethod + def resource_setup(cls): + if hasattr(CONF.identity, 'admin_username'): + cls.username = CONF.identity.admin_username + cls.password = CONF.identity.admin_password + cls.tenant_name = CONF.identity.admin_tenant_name + else: + cls.username = CONF.auth.admin_username + cls.password = CONF.auth.admin_password + cls.tenant_name = CONF.auth.admin_tenant_name + cls.verify_nonempty(cls.username, cls.password, cls.tenant_name) + cls.os = clients.AdminManager() + super(BaseServiceBrokerAdminTest, cls).resource_setup() diff --git a/murano_tempest_tests/tests/api/service_broker/test_service_broker_actions.py b/murano_tempest_tests/tests/api/service_broker/test_service_broker_actions.py new file mode 100644 index 000000000..c8710b06b --- /dev/null +++ b/murano_tempest_tests/tests/api/service_broker/test_service_broker_actions.py @@ -0,0 +1,26 @@ +# Copyright (c) 2015 Mirantis, Inc. +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest import test + +from murano_tempest_tests.tests.api.service_broker import base + + +class ServiceBrokerActionsTest(base.BaseServiceBrokerAdminTest): + + @test.attr(type=["smoke", "gate"]) + def test_applications_listing(self): + app_list = self.service_broker_client.get_applications_list() + self.assertIsInstance(app_list, list) diff --git a/setup.cfg b/setup.cfg index 4c566610d..670cffdf4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -50,6 +50,8 @@ console_scripts = murano-cfapi = murano.cmd.cfapi:main oslo.config.opts = murano = murano.opts:list_opts +tempest.test_plugins = + murano_tests = murano_tempest_tests.plugin:MuranoTempestPlugin murano_policy_modify_actions = remove-object = murano.policy.modify.actions.default_actions:RemoveObjectAction