From a3da69387840a29e8ebd8c13758621869ff7bdf3 Mon Sep 17 00:00:00 2001 From: Valeriy Ponomaryov Date: Mon, 5 Oct 2015 19:18:46 +0300 Subject: [PATCH] [Manila] Add security-services context Add possibility to create "security-services" on-fly and attach them to "share-networks". Note that, it is not added to benchmarks as it cannot be tested in CI because requires appropriate services be set up and running. Change-Id: I64aa960f3109bfb576da2c2260b017925d4c7c68 --- .../openstack/context/manila/consts.py | 1 + .../manila/manila_security_services.py | 88 +++++++++ .../context/manila/manila_share_networks.py | 5 + ...autocreated-share-networks-and-delete.json | 23 +++ ...autocreated-share-networks-and-delete.yaml | 21 +++ .../manila/test_manila_security_services.py | 172 ++++++++++++++++++ .../manila/test_manila_share_networks.py | 76 +++++++- 7 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 rally/plugins/openstack/context/manila/manila_security_services.py create mode 100644 tests/unit/plugins/openstack/context/manila/test_manila_security_services.py diff --git a/rally/plugins/openstack/context/manila/consts.py b/rally/plugins/openstack/context/manila/consts.py index d7465704f3..d3c62bc405 100644 --- a/rally/plugins/openstack/context/manila/consts.py +++ b/rally/plugins/openstack/context/manila/consts.py @@ -14,3 +14,4 @@ # under the License. SHARE_NETWORKS_CONTEXT_NAME = "manila_share_networks" +SECURITY_SERVICES_CONTEXT_NAME = "manila_security_services" diff --git a/rally/plugins/openstack/context/manila/manila_security_services.py b/rally/plugins/openstack/context/manila/manila_security_services.py new file mode 100644 index 0000000000..951ac3e48f --- /dev/null +++ b/rally/plugins/openstack/context/manila/manila_security_services.py @@ -0,0 +1,88 @@ +# Copyright 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 + +from rally.common.i18n import _ +from rally.common import logging +from rally.common import utils +from rally import consts as rally_consts +from rally.plugins.openstack.cleanup import manager as resource_manager +from rally.plugins.openstack.context.manila import consts +from rally.plugins.openstack.scenarios.manila import utils as manila_utils +from rally.task import context + +CONF = cfg.CONF +LOG = logging.getLogger(__name__) + +CONTEXT_NAME = consts.SECURITY_SERVICES_CONTEXT_NAME + + +@context.configure(name=CONTEXT_NAME, order=445) +class SecurityServices(context.Context): + """This context creates 'security services' for Manila project.""" + + CONFIG_SCHEMA = { + "type": "object", + "$schema": rally_consts.JSON_SCHEMA, + "properties": { + # NOTE(vponomaryov): context arg 'security_services' is expected + # to be list of dicts with data for creation of security services. + # Example: + # security_services = [ + # {'type': 'LDAP', 'dns_ip': 'foo_ip', 'server': 'bar_ip', + # 'domain': 'quuz_domain', 'user': 'ololo', + # 'password': 'fake_password'} + # ] + # Where 'type' is required key and should have one of following + # values: 'active_directory', 'kerberos' or 'ldap'. + # This context arg is used only if share networks are used and + # autocreated. + "security_services": {"type": "array"}, + }, + "additionalProperties": False + } + DEFAULT_CONFIG = { + "security_services": [], + } + + @logging.log_task_wrapper( + LOG.info, _("Enter context: `%s`") % CONTEXT_NAME) + def setup(self): + for user, tenant_id in (utils.iterate_per_tenants( + self.context.get("users", []))): + self.context["tenants"][tenant_id][CONTEXT_NAME] = { + "security_services": [], + } + if self.config["security_services"]: + manila_scenario = manila_utils.ManilaScenario({ + "task": self.task, + "user": user, + "config": { + "api_versions": self.context["config"].get( + "api_versions", [])} + }) + for ss in self.config["security_services"]: + inst = manila_scenario._create_security_service( + **ss).to_dict() + self.context["tenants"][tenant_id][CONTEXT_NAME][ + "security_services"].append(inst) + + @logging.log_task_wrapper(LOG.info, _("Exit context: `%s`") % CONTEXT_NAME) + def cleanup(self): + resource_manager.cleanup( + names=["manila.security_services"], + users=self.context.get("users", []), + ) diff --git a/rally/plugins/openstack/context/manila/manila_share_networks.py b/rally/plugins/openstack/context/manila/manila_share_networks.py index 53a8232ef7..82c156de90 100644 --- a/rally/plugins/openstack/context/manila/manila_share_networks.py +++ b/rally/plugins/openstack/context/manila/manila_share_networks.py @@ -153,6 +153,11 @@ class ShareNetworks(context.Context): **data).to_dict() self.context["tenants"][tenant_id][CONTEXT_NAME][ "share_networks"].append(share_network) + for ss in self.context["tenants"][tenant_id].get( + consts.SECURITY_SERVICES_CONTEXT_NAME, {}).get( + "security_services", []): + manila_scenario._add_security_service_to_share_network( + share_network["id"], ss["id"]) if networks: for network in networks: diff --git a/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.json b/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.json index ee8ba5a9b7..55de4e3415 100644 --- a/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.json +++ b/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.json @@ -1,3 +1,4 @@ +{% set use_security_services = use_security_services or False %} { "ManilaShares.create_and_delete_share": [ { @@ -32,6 +33,28 @@ "manila_share_networks": { "use_share_networks": true } + {% if use_security_services %} + , + "manila_security_services": { + "security_services": [ + {"security_service_type": "ldap", + "server": "LDAP server address", + "user": "User that will be used", + "password": "Password for specified user"}, + {"security_service_type": "kerberos", + "dns_ip": "IP address of DNS service to be used", + "server": "Kerberos server address", + "domain": "Kerberos realm", + "user": "User that will be used", + "password": "Password for specified user"}, + {"security_service_type": "active_directory", + "dns_ip": "IP address of DNS service to be used", + "domain": "Domain from 'Active Directory'", + "user": "User from 'Active Directory'", + "password": "password for specified user"} + ] + } + {% endif %} } } ] diff --git a/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.yaml b/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.yaml index 16cb022fee..2dc9756cb7 100644 --- a/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.yaml +++ b/samples/tasks/scenarios/manila/create-share-with-autocreated-share-networks-and-delete.yaml @@ -1,3 +1,4 @@ +{% set use_security_services = use_security_services or False %} --- ManilaShares.create_and_delete_share: - @@ -25,3 +26,23 @@ start_cidr: "99.0.0.0/24" manila_share_networks: use_share_networks: True + {% if use_security_services %} + manila_security_services: + security_services: [ + {"security_service_type": "ldap", + "server": "LDAP server address", + "user": "User that will be used", + "password": "Password for specified user"}, + {"security_service_type": "kerberos", + "dns_ip": "IP address of DNS service to be used", + "server": "Kerberos server address", + "domain": "Kerberos realm", + "user": "User that will be used", + "password": "Password for specified user"}, + {"security_service_type": "active_directory", + "dns_ip": "IP address of DNS service to be used", + "domain": "Domain from 'Active Directory'", + "user": "User from 'Active Directory'", + "password": "password for specified user"}, + ] + {% endif %} diff --git a/tests/unit/plugins/openstack/context/manila/test_manila_security_services.py b/tests/unit/plugins/openstack/context/manila/test_manila_security_services.py new file mode 100644 index 0000000000..228b0ff2e7 --- /dev/null +++ b/tests/unit/plugins/openstack/context/manila/test_manila_security_services.py @@ -0,0 +1,172 @@ +# Copyright 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 ddt +import mock +import six + +from rally import consts as rally_consts +from rally.plugins.openstack.context.manila import consts +from rally.plugins.openstack.context.manila import manila_security_services +from tests.unit import test + +CONTEXT_NAME = consts.SECURITY_SERVICES_CONTEXT_NAME + + +@ddt.ddt +class SecurityServicesTestCase(test.ScenarioTestCase): + TENANTS_AMOUNT = 3 + USERS_PER_TENANT = 4 + SECURITY_SERVICES = [ + {"security_service_type": ss_type, + "dns_ip": "fake_dns_ip_%s" % ss_type, + "server": "fake_server_%s" % ss_type, + "domain": "fake_domain_%s" % ss_type, + "user": "fake_user_%s" % ss_type, + "password": "fake_password_%s" % ss_type} + for ss_type in ("ldap", "kerberos", "active_directory") + ] + + def _get_context(self, security_services=None, networks_per_tenant=2, + neutron_network_provider=True): + if security_services is None: + security_services = self.SECURITY_SERVICES + tenants = {} + for t_id in range(self.TENANTS_AMOUNT): + tenants[six.text_type(t_id)] = {"name": six.text_type(t_id)} + tenants[six.text_type(t_id)]["networks"] = [] + for i in range(networks_per_tenant): + network = {"id": "fake_net_id_%s" % i} + if neutron_network_provider: + network["subnets"] = ["fake_subnet_id_of_net_%s" % i] + else: + network["cidr"] = "101.0.5.0/24" + tenants[six.text_type(t_id)]["networks"].append(network) + users = [] + for t_id in tenants.keys(): + for i in range(self.USERS_PER_TENANT): + users.append({"id": i, "tenant_id": t_id, "endpoint": "fake"}) + context = { + "config": { + "users": { + "tenants": self.TENANTS_AMOUNT, + "users_per_tenant": self.USERS_PER_TENANT, + }, + CONTEXT_NAME: { + "security_services": security_services, + }, + }, + "admin": { + "endpoint": mock.MagicMock(), + }, + "task": mock.MagicMock(), + "users": users, + "tenants": tenants, + } + return context + + def test_init(self): + context = { + "task": mock.MagicMock(), + "config": { + CONTEXT_NAME: {"foo": "bar"}, + "not_manila": {"not_manila_key": "not_manila_value"}, + } + } + + inst = manila_security_services.SecurityServices(context) + + self.assertEqual(inst.config.get("foo"), "bar") + self.assertFalse(inst.config.get("security_services")) + self.assertIn( + rally_consts.JSON_SCHEMA, inst.CONFIG_SCHEMA.get("$schema")) + self.assertEqual(False, inst.CONFIG_SCHEMA.get("additionalProperties")) + self.assertEqual("object", inst.CONFIG_SCHEMA.get("type")) + props = inst.CONFIG_SCHEMA.get("properties", {}) + self.assertEqual({"type": "array"}, props.get("security_services")) + self.assertEqual(445, inst.get_order()) + self.assertEqual(CONTEXT_NAME, inst.get_name()) + + @mock.patch.object(manila_security_services.manila_utils, "ManilaScenario") + @ddt.data(True, False) + def test_setup_security_services_set(self, neutron_network_provider, + mock_manila_scenario): + ctxt = self._get_context( + neutron_network_provider=neutron_network_provider) + inst = manila_security_services.SecurityServices(ctxt) + + inst.setup() + + self.assertEqual( + self.TENANTS_AMOUNT, mock_manila_scenario.call_count) + self.assertEqual( + mock_manila_scenario.call_args_list, + [mock.call({ + "task": inst.task, + "config": {"api_versions": []}, + "user": user}) + for user in inst.context["users"] if user["id"] == 0] + ) + mock_create_security_service = ( + mock_manila_scenario.return_value._create_security_service) + expected_calls = [] + for ss in self.SECURITY_SERVICES: + expected_calls.extend([mock.call(**ss), mock.call().to_dict()]) + mock_create_security_service.assert_has_calls(expected_calls) + self.assertEqual( + self.TENANTS_AMOUNT * len(self.SECURITY_SERVICES), + mock_create_security_service.call_count) + self.assertEqual( + self.TENANTS_AMOUNT, + len(inst.context["config"][CONTEXT_NAME]["security_services"])) + for tenant in inst.context["tenants"]: + self.assertEqual( + self.TENANTS_AMOUNT, + len(inst.context["tenants"][tenant][CONTEXT_NAME][ + "security_services"]) + ) + + @mock.patch.object(manila_security_services.manila_utils, "ManilaScenario") + def test_setup_security_services_not_set(self, mock_manila_scenario): + ctxt = self._get_context(security_services=[]) + inst = manila_security_services.SecurityServices(ctxt) + + inst.setup() + + self.assertFalse(mock_manila_scenario.called) + self.assertFalse( + mock_manila_scenario.return_value._create_security_service.called) + self.assertIn(CONTEXT_NAME, inst.context["config"]) + self.assertIn( + "security_services", inst.context["config"][CONTEXT_NAME]) + self.assertEqual( + 0, + len(inst.context["config"][CONTEXT_NAME]["security_services"])) + for tenant in inst.context["tenants"]: + self.assertEqual( + 0, + len(inst.context["tenants"][tenant][CONTEXT_NAME][ + "security_services"]) + ) + + @mock.patch.object(manila_security_services, "resource_manager") + def test_cleanup_security_services_enabled(self, mock_resource_manager): + ctxt = self._get_context() + inst = manila_security_services.SecurityServices(ctxt) + + inst.cleanup() + + mock_resource_manager.cleanup.assert_called_once_with( + names=["manila.security_services"], users=ctxt["users"]) diff --git a/tests/unit/plugins/openstack/context/manila/test_manila_share_networks.py b/tests/unit/plugins/openstack/context/manila/test_manila_share_networks.py index 5e55e64dc2..b098eb3970 100644 --- a/tests/unit/plugins/openstack/context/manila/test_manila_share_networks.py +++ b/tests/unit/plugins/openstack/context/manila/test_manila_share_networks.py @@ -56,7 +56,7 @@ class ShareNetworksTestCase(test.TestCase): for ss_type in ("ldap", "kerberos", "active_directory") ] - def _get_context(self, networks_per_tenant=2, + def _get_context(self, use_security_services=False, networks_per_tenant=2, neutron_network_provider=True): tenants = {} for t_id in range(self.TENANTS_AMOUNT): @@ -85,6 +85,11 @@ class ShareNetworksTestCase(test.TestCase): "use_share_networks": True, "share_networks": [], }, + consts.SECURITY_SERVICES_CONTEXT_NAME: { + "security_services": ( + self.SECURITY_SERVICES + if use_security_services else []) + }, "network": { "networks_per_tenant": networks_per_tenant, "start_cidr": "101.0.5.0/24", @@ -242,6 +247,64 @@ class ShareNetworksTestCase(test.TestCase): self.assertRaises(exceptions.ContextSetupFailure, inst.setup) + @ddt.data(True, False) + @mock.patch("rally.osclients.Clients") + @mock.patch(MANILA_UTILS_PATH + "_create_share_network") + @mock.patch(MANILA_UTILS_PATH + "_add_security_service_to_share_network") + def test_setup_autocreate_share_networks_with_security_services( + self, + neutron, + mock_manila_scenario__add_security_service_to_share_network, + mock_manila_scenario__create_share_network, + mock_clients): + networks_per_tenant = 2 + ctxt = self._get_context( + networks_per_tenant=networks_per_tenant, + neutron_network_provider=neutron, + use_security_services=True, + ) + inst = manila_share_networks.ShareNetworks(ctxt) + for tenant_id in list(ctxt["tenants"].keys()): + inst.context["tenants"][tenant_id][ + consts.SECURITY_SERVICES_CONTEXT_NAME] = { + "security_services": [ + Fake(id="fake_id").to_dict() for i in (1, 2, 3) + ] + } + + inst.setup() + + self.assertEqual(ctxt["task"], inst.context.get("task")) + self.assertEqual(ctxt["config"], inst.context.get("config")) + self.assertEqual(ctxt["users"], inst.context.get("users")) + self.assertEqual(ctxt["tenants"], inst.context.get("tenants")) + mock_add_security_service_to_share_network = ( + mock_manila_scenario__add_security_service_to_share_network) + mock_add_security_service_to_share_network.assert_has_calls([ + mock.call(mock.ANY, mock.ANY) + for i in range( + self.TENANTS_AMOUNT * + networks_per_tenant * + len(self.SECURITY_SERVICES))]) + if neutron: + sn_args = { + "neutron_net_id": mock.ANY, + "neutron_subnet_id": mock.ANY, + } + else: + sn_args = {"nova_net_id": mock.ANY} + expected_calls = [ + mock.call(**sn_args), + mock.call().to_dict(), + mock.ANY, + mock.ANY, + mock.ANY, + ] + mock_manila_scenario__create_share_network.assert_has_calls( + expected_calls * (self.TENANTS_AMOUNT * networks_per_tenant)) + mock_clients.assert_has_calls([ + mock.call("fake", {}) for i in range(self.TENANTS_AMOUNT)]) + @ddt.data(True, False) @mock.patch("rally.osclients.Clients") @mock.patch(MANILA_UTILS_PATH + "_create_share_network") @@ -329,6 +392,7 @@ class ShareNetworksTestCase(test.TestCase): self.assertIn(mock.call(user["credential"], {}), mock_clients.mock_calls) + @ddt.data(True, False) @mock.patch("rally.task.utils.wait_for_status") @mock.patch("rally.osclients.Clients") @mock.patch(MANILA_UTILS_PATH + "_delete_share_network") @@ -336,7 +400,7 @@ class ShareNetworksTestCase(test.TestCase): @mock.patch(MANILA_UTILS_PATH + "_add_security_service_to_share_network") @mock.patch(MANILA_UTILS_PATH + "_list_share_servers") def test_cleanup_autocreated_share_networks( - self, + self, use_security_services, mock_manila_scenario__list_share_servers, mock_manila_scenario__add_security_service_to_share_network, mock_manila_scenario__create_share_network, @@ -349,8 +413,16 @@ class ShareNetworksTestCase(test.TestCase): networks_per_tenant = 2 ctxt = self._get_context( networks_per_tenant=networks_per_tenant, + use_security_services=use_security_services, ) inst = manila_share_networks.ShareNetworks(ctxt) + for tenant_id in list(ctxt["tenants"].keys()): + inst.context["tenants"][tenant_id][ + consts.SECURITY_SERVICES_CONTEXT_NAME] = { + "security_services": [ + Fake(id="fake_id").to_dict() for i in (1, 2, 3) + ] + } inst.setup() mock_clients.assert_has_calls([