From 1e160f3fa1fd0209c929603144b9f78b64eee370 Mon Sep 17 00:00:00 2001 From: Yair Fried Date: Tue, 2 Jun 2015 16:04:59 +0300 Subject: [PATCH] [wrappers] Move wrappers under plugins.openstack Change-Id: I23cb04aea14e59d388ca8b34811f260ede79e116 Implements: blueprint split-plugins --- .../openstack/scenarios/neutron/utils.py | 2 +- .../openstack/scenarios/nova/servers.py | 2 +- .../plugins/openstack/scenarios/nova/utils.py | 3 +- rally/plugins/openstack/scenarios/vm/utils.py | 2 +- rally/plugins/openstack/wrappers/__init__.py | 0 rally/plugins/openstack/wrappers/keystone.py | 217 ++++++++ rally/plugins/openstack/wrappers/network.py | 407 +++++++++++++++ .../plugins/openstack/wrappers/__init__.py | 0 .../openstack/wrappers/test_keystone.py | 208 ++++++++ .../openstack/wrappers/test_network.py | 494 ++++++++++++++++++ 10 files changed, 1330 insertions(+), 5 deletions(-) create mode 100644 rally/plugins/openstack/wrappers/__init__.py create mode 100644 rally/plugins/openstack/wrappers/keystone.py create mode 100644 rally/plugins/openstack/wrappers/network.py create mode 100644 tests/unit/plugins/openstack/wrappers/__init__.py create mode 100644 tests/unit/plugins/openstack/wrappers/test_keystone.py create mode 100644 tests/unit/plugins/openstack/wrappers/test_network.py diff --git a/rally/plugins/openstack/scenarios/neutron/utils.py b/rally/plugins/openstack/scenarios/neutron/utils.py index 3869bee5..0fe7fab7 100644 --- a/rally/plugins/openstack/scenarios/neutron/utils.py +++ b/rally/plugins/openstack/scenarios/neutron/utils.py @@ -16,8 +16,8 @@ from oslo_utils import uuidutils as uid from rally.benchmark.scenarios import base -from rally.benchmark.wrappers import network as network_wrapper from rally.common import log as logging +from rally.plugins.openstack.wrappers import network as network_wrapper LOG = logging.getLogger(__name__) diff --git a/rally/plugins/openstack/scenarios/nova/servers.py b/rally/plugins/openstack/scenarios/nova/servers.py index 372e43a4..7c4c108e 100644 --- a/rally/plugins/openstack/scenarios/nova/servers.py +++ b/rally/plugins/openstack/scenarios/nova/servers.py @@ -19,12 +19,12 @@ from rally.benchmark.scenarios import base from rally.benchmark.scenarios import utils as scenario_utils from rally.benchmark import types as types from rally.benchmark import validation -from rally.benchmark.wrappers import network as network_wrapper from rally.common import log as logging from rally import consts from rally import exceptions as rally_exceptions from rally.plugins.openstack.scenarios.cinder import utils as cinder_utils from rally.plugins.openstack.scenarios.nova import utils +from rally.plugins.openstack.wrappers import network as network_wrapper LOG = logging.getLogger(__name__) diff --git a/rally/plugins/openstack/scenarios/nova/utils.py b/rally/plugins/openstack/scenarios/nova/utils.py index 0da063e9..58e5b443 100644 --- a/rally/plugins/openstack/scenarios/nova/utils.py +++ b/rally/plugins/openstack/scenarios/nova/utils.py @@ -21,9 +21,8 @@ import six from rally.benchmark.scenarios import base from rally.benchmark import utils as bench_utils -from rally.benchmark.wrappers import network as network_wrapper from rally import exceptions - +from rally.plugins.openstack.wrappers import network as network_wrapper NOVA_BENCHMARK_OPTS = [] option_names_and_defaults = [ diff --git a/rally/plugins/openstack/scenarios/vm/utils.py b/rally/plugins/openstack/scenarios/vm/utils.py index 7e47e062..3608c8e2 100644 --- a/rally/plugins/openstack/scenarios/vm/utils.py +++ b/rally/plugins/openstack/scenarios/vm/utils.py @@ -21,11 +21,11 @@ import six from rally.benchmark.scenarios import base from rally.benchmark import utils as bench_utils -from rally.benchmark.wrappers import network as network_wrapper from rally.common.i18n import _ from rally.common import log as logging from rally.common import sshutils from rally import exceptions +from rally.plugins.openstack.wrappers import network as network_wrapper LOG = logging.getLogger(__name__) diff --git a/rally/plugins/openstack/wrappers/__init__.py b/rally/plugins/openstack/wrappers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rally/plugins/openstack/wrappers/keystone.py b/rally/plugins/openstack/wrappers/keystone.py new file mode 100644 index 00000000..b1310b31 --- /dev/null +++ b/rally/plugins/openstack/wrappers/keystone.py @@ -0,0 +1,217 @@ +# Copyright 2014: 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 abc +import collections + +from keystoneclient import exceptions +import six + +from rally.common import log as logging + + +LOG = logging.getLogger(__name__) + +Project = collections.namedtuple("Project", ["id", "name", "domain_id"]) +User = collections.namedtuple("User", + ["id", "name", "project_id", "domain_id"]) +Service = collections.namedtuple("Service", ["id", "name"]) +Role = collections.namedtuple("Role", ["id", "name"]) + + +@six.add_metaclass(abc.ABCMeta) +class KeystoneWrapper(object): + def __init__(self, client): + self.client = client + + def __getattr__(self, attr_name): + return getattr(self.client, attr_name) + + @abc.abstractmethod + def create_project(self, project_name, domain_name="Default"): + """Creates new project/tenant and return project object. + + :param project_name: Name of project to be created. + :param domain_name: Name or id of domain where to create project, for + implementations that don't support domains this + argument must be None or 'Default'. + """ + + @abc.abstractmethod + def delete_project(self, project_id): + """Deletes project.""" + + @abc.abstractmethod + def create_user(self, username, password, email=None, project_id=None, + domain_name="Default"): + """Create user. + + :param username: name of user + :param password: user password + :param project: user's default project + :param domain_name: Name or id of domain where to create project, for + implementations that don't support domains this + argument must be None or 'Default'. + """ + + @abc.abstractmethod + def delete_user(self, user_id): + """Deletes user.""" + + @abc.abstractmethod + def list_users(self): + """List all users.""" + + @abc.abstractmethod + def list_projects(self): + """List all projects/tenants.""" + + def delete_service(self, service_id): + """Deletes service.""" + self.client.services.delete(service_id) + + def list_services(self): + """List all services.""" + return map(KeystoneWrapper._wrap_service, self.client.services.list()) + + def delete_role(self, role_id): + """Deletes role.""" + self.client.roles.delete(role_id) + + def list_roles(self): + """List all roles.""" + return map(KeystoneWrapper._wrap_role, self.client.roles.list()) + + @staticmethod + def _wrap_service(service): + return Service(id=service.id, name=service.name) + + @staticmethod + def _wrap_role(role): + return Role(id=role.id, name=role.name) + + +class KeystoneV2Wrapper(KeystoneWrapper): + def _check_domain(self, domain_name): + if domain_name.lower() != "default": + raise NotImplementedError("Domain functionality not implemented " + "in Keystone v2") + + @staticmethod + def _wrap_v2_tenant(tenant): + return Project(id=tenant.id, name=tenant.name, domain_id="default") + + @staticmethod + def _wrap_v2_user(user): + return User(id=user.id, name=user.name, + project_id=getattr(user, "tenantId", None), + domain_id="default") + + def create_project(self, project_name, domain_name="Default"): + self._check_domain(domain_name) + tenant = self.client.tenants.create(project_name) + return KeystoneV2Wrapper._wrap_v2_tenant(tenant) + + def delete_project(self, project_id): + self.client.tenants.delete(project_id) + + def create_user(self, username, password, email=None, project_id=None, + domain_name="Default"): + self._check_domain(domain_name) + user = self.client.users.create(username, password, email, project_id) + return KeystoneV2Wrapper._wrap_v2_user(user) + + def delete_user(self, user_id): + self.client.users.delete(user_id) + + def list_users(self): + return map(KeystoneV2Wrapper._wrap_v2_user, self.client.users.list()) + + def list_projects(self): + return map(KeystoneV2Wrapper._wrap_v2_tenant, + self.client.tenants.list()) + + +class KeystoneV3Wrapper(KeystoneWrapper): + def _get_domain_id(self, domain_name_or_id): + try: + # First try to find domain by ID + return self.client.domains.get(domain_name_or_id).id + except exceptions.NotFound: + # Domain not found by ID, try to find it by name + domains = self.client.domains.list(name=domain_name_or_id) + if domains: + return domains[0].id + # Domain not found by name, raise original NotFound exception + raise + + @staticmethod + def _wrap_v3_project(project): + return Project(id=project.id, name=project.name, + domain_id=project.domain_id) + + @staticmethod + def _wrap_v3_user(user): + # When user has default_project_id that is None user.default_project_id + # will raise AttributeError + project_id = getattr(user, "default_project_id", None) + return User(id=user.id, name=user.name, project_id=project_id, + domain_id=user.domain_id) + + def create_project(self, project_name, domain_name="Default"): + domain_id = self._get_domain_id(domain_name) + project = self.client.projects.create( + name=project_name, domain=domain_id) + return KeystoneV3Wrapper._wrap_v3_project(project) + + def delete_project(self, project_id): + self.client.projects.delete(project_id) + + def create_user(self, username, password, email=None, project_id=None, + domain_name="Default"): + domain_id = self._get_domain_id(domain_name) + user = self.client.users.create(name=username, password=password, + default_project=project_id, + email=email, domain=domain_id) + for role in self.client.roles.list(): + if "member" in role.name.lower(): + self.client.roles.grant(role.id, user=user.id, + project=project_id) + break + else: + LOG.warning("Unable to set member role to created user.") + return KeystoneV3Wrapper._wrap_v3_user(user) + + def delete_user(self, user_id): + self.client.users.delete(user_id) + + def list_users(self): + return map(KeystoneV3Wrapper._wrap_v3_user, self.client.users.list()) + + def list_projects(self): + return map(KeystoneV3Wrapper._wrap_v3_project, + self.client.projects.list()) + + +def wrap(client): + """Returns keystone wrapper based on keystone client version.""" + + if client.version == "v2.0": + return KeystoneV2Wrapper(client) + elif client.version == "v3": + return KeystoneV3Wrapper(client) + else: + raise NotImplementedError( + "Wrapper for version %s is not implemented." % client.version) diff --git a/rally/plugins/openstack/wrappers/network.py b/rally/plugins/openstack/wrappers/network.py new file mode 100644 index 00000000..c4e79523 --- /dev/null +++ b/rally/plugins/openstack/wrappers/network.py @@ -0,0 +1,407 @@ +# Copyright 2014: 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 abc + +import netaddr +import six + +from rally.benchmark import utils as bench_utils +from rally.common.i18n import _ +from rally.common import log as logging +from rally.common import utils +from rally import consts +from rally import exceptions + +from neutronclient.common import exceptions as neutron_exceptions +from novaclient import exceptions as nova_exceptions + + +LOG = logging.getLogger(__name__) + + +cidr_incr = utils.RAMInt() + + +def generate_cidr(start_cidr="10.2.0.0/24"): + """Generate next CIDR for network or subnet, without IP overlapping. + + This is process and thread safe, because `cidr_incr' points to + value stored directly in RAM. This guarantees that CIDRs will be + serial and unique even under hard multiprocessing/threading load. + + :param start_cidr: start CIDR str + :returns: next available CIDR str + """ + cidr = str(netaddr.IPNetwork(start_cidr).next(next(cidr_incr))) + LOG.debug("CIDR generated: %s" % cidr) + return cidr + + +class NetworkWrapperException(exceptions.RallyException): + msg_fmt = _("%(message)s") + + +@six.add_metaclass(abc.ABCMeta) +class NetworkWrapper(object): + """Base class for network service implementations. + + We actually have two network services implementations, with different API: + NovaNetwork and Neutron. The idea is (at least to try) to use unified + service, which hides most differences and routines behind the scenes. + This allows to significantly re-use and simplify code. + """ + START_CIDR = "10.2.0.0/24" + SERVICE_IMPL = None + + def __init__(self, clients, config=None): + if hasattr(clients, self.SERVICE_IMPL): + self.client = getattr(clients, self.SERVICE_IMPL)() + else: + self.client = clients(self.SERVICE_IMPL) + self.config = config or {} + self.start_cidr = self.config.get("start_cidr", self.START_CIDR) + + @abc.abstractmethod + def create_network(self): + """Create network.""" + + @abc.abstractmethod + def delete_network(self): + """Delete network.""" + + @abc.abstractmethod + def list_networks(self): + """List networks.""" + + @abc.abstractmethod + def create_floating_ip(self): + """Create floating IP.""" + + @abc.abstractmethod + def delete_floating_ip(self): + """Delete floating IP.""" + + @abc.abstractmethod + def supports_security_group(self): + """Checks whether security group is supported.""" + + +class NovaNetworkWrapper(NetworkWrapper): + SERVICE_IMPL = consts.Service.NOVA + + def __init__(self, *args): + super(NovaNetworkWrapper, self).__init__(*args) + self.skip_cidrs = [n.cidr for n in self.client.networks.list()] + + def _generate_cidr(self): + cidr = generate_cidr(start_cidr=self.start_cidr) + while cidr in self.skip_cidrs: + cidr = generate_cidr(start_cidr=self.start_cidr) + return cidr + + def create_network(self, tenant_id, **kwargs): + """Create network. + + :param tenant_id: str, tenant ID + :param **kwargs: for compatibility, not used here + :returns: dict, network data + """ + cidr = self._generate_cidr() + label = utils.generate_random_name("rally_net_") + network = self.client.networks.create( + tenant_id=tenant_id, cidr=cidr, label=label) + return {"id": network.id, + "cidr": network.cidr, + "name": network.label, + "status": "ACTIVE", + "external": False, + "tenant_id": tenant_id} + + def delete_network(self, network): + return self.client.networks.delete(network["id"]) + + def list_networks(self): + return self.client.networks.list() + + def create_floating_ip(self, ext_network=None, **kwargs): + """Allocate a floating ip from the given nova-network pool + + :param ext_network: name or external network, str + :param **kwargs: for compatibility, not used here + :returns: floating IP dict + """ + if not ext_network: + try: + ext_network = self.client.floating_ip_pools.list()[0].name + except IndexError: + raise NetworkWrapperException("No floating IP pools found") + fip = self.client.floating_ips.create(ext_network) + return {"id": fip.id, "ip": fip.ip} + + def _get_floating_ip(self, fip_id, do_raise=False): + try: + fip = self.client.floating_ips.get(fip_id) + except nova_exceptions.NotFound: + if not do_raise: + return None + raise exceptions.GetResourceNotFound( + resource="Floating IP %s" % fip_id) + return fip.id + + def delete_floating_ip(self, fip_id, wait=False): + """Delete floating IP. + + :param fip_id: int floating IP id + :param wait: if True then wait to return until floating ip is deleted + """ + self.client.floating_ips.delete(fip_id) + if not wait: + return + bench_utils.wait_for_delete( + fip_id, + update_resource=lambda i: self._get_floating_ip(i, do_raise=True)) + + def supports_security_group(self): + """Check whether security group is supported + + :return: result tuple. Always (True, "") for nova-network. + :rtype: (bool, string) + """ + return True, "" + + +class NeutronWrapper(NetworkWrapper): + SERVICE_IMPL = consts.Service.NEUTRON + SUBNET_IP_VERSION = 4 + + @property + def external_networks(self): + return self.client.list_networks(**{ + "router:external": True})["networks"] + + def get_network(self, net_id=None, name=None): + net = None + try: + if net_id: + net = self.client.show_network(net_id)["network"] + else: + for net in self.client.list_networks(name=name)["networks"]: + break + return {"id": net["id"], + "name": net["name"], + "tenant_id": net["tenant_id"], + "status": net["status"], + "external": net["router:external"], + "subnets": net["subnets"], + "router_id": None} + except (TypeError, neutron_exceptions.NeutronClientException): + raise NetworkWrapperException( + "Network not found: %s" % (name or net_id)) + + def create_router(self, external=False, **kwargs): + """Create neutron router. + + :param external: bool, whether to set setup external_gateway_info + :param **kwargs: POST /v2.0/routers request options + :returns: neutron router dict + """ + if "name" not in kwargs: + kwargs["name"] = utils.generate_random_name("rally_router_") + + if external and "external_gateway_info" not in kwargs: + for net in self.external_networks: + kwargs["external_gateway_info"] = { + "network_id": net["id"], "enable_snat": True} + return self.client.create_router({"router": kwargs})["router"] + + def _generate_cidr(self): + # TODO(amaretskiy): Generate CIDRs unique for network, not cluster + return generate_cidr(start_cidr=self.start_cidr) + + def create_network(self, tenant_id, **kwargs): + """Create network. + + :param tenant_id: str, tenant ID + :param **kwargs: extra options + :returns: dict, network data + """ + network_args = { + "network": { + "tenant_id": tenant_id, + "name": utils.generate_random_name("rally_net_") + } + } + network = self.client.create_network(network_args)["network"] + + router = None + if kwargs.get("add_router", False): + router = self.create_router(external=True, tenant_id=tenant_id) + + subnets = [] + subnets_num = kwargs.get("subnets_num", 0) + for i in range(subnets_num): + subnet_args = { + "subnet": { + "tenant_id": tenant_id, + "network_id": network["id"], + "name": utils.generate_random_name("rally_subnet_"), + "ip_version": self.SUBNET_IP_VERSION, + "cidr": self._generate_cidr(), + "enable_dhcp": True, + "dns_nameservers": kwargs.get("dns_nameservers", + ["8.8.8.8", "8.8.4.4"]) + } + } + subnet = self.client.create_subnet(subnet_args)["subnet"] + subnets.append(subnet["id"]) + + if router: + self.client.add_interface_router(router["id"], + {"subnet_id": subnet["id"]}) + + return {"id": network["id"], + "name": network["name"], + "status": network["status"], + "subnets": subnets, + "external": network.get("router:external", False), + "router_id": router and router["id"] or None, + "tenant_id": tenant_id} + + def delete_network(self, network): + net_dhcps = self.client.list_dhcp_agent_hosting_networks( + network["id"])["agents"] + for net_dhcp in net_dhcps: + self.client.remove_network_from_dhcp_agent(net_dhcp["id"], + network["id"]) + router_id = network["router_id"] + if router_id: + self.client.remove_gateway_router(router_id) + for subnet_id in network["subnets"]: + self.client.remove_interface_router(router_id, + {"subnet_id": subnet_id}) + self.client.delete_router(router_id) + + for port in self.client.list_ports(network_id=network["id"])["ports"]: + self.client.delete_port(port["id"]) + + for subnet_id in network["subnets"]: + self._delete_subnet(subnet_id) + + return self.client.delete_network(network["id"]) + + def _delete_subnet(self, subnet_id): + self.client.delete_subnet(subnet_id) + + def list_networks(self): + return self.client.list_networks()["networks"] + + def create_port(self, network_id, **kwargs): + """Create neutron port. + + :param network_id: neutron network id + :param **kwargs: POST /v2.0/ports request options + :returns: neutron port dict + """ + kwargs["network_id"] = network_id + if "name" not in kwargs: + kwargs["name"] = utils.generate_random_name("rally_port_") + return self.client.create_port({"port": kwargs})["port"] + + def create_floating_ip(self, ext_network=None, int_network=None, + tenant_id=None, port_id=None, **kwargs): + """Create Neutron floating IP. + + :param ext_network: floating network name or dict + :param int_network: fixed network name or dict + :param tenant_id str tenant id + :param port_id: str port id + :param **kwargs: for compatibility, not used here + :returns: floating IP dict + """ + if not tenant_id: + raise ValueError("Missed tenant_id") + + net_id = None + if type(ext_network) is dict: + net_id = ext_network["id"] + elif ext_network: + ext_net = self.get_network(name=ext_network) + if not ext_net["external"]: + raise NetworkWrapperException("Network is not external: %s" + % ext_network) + net_id = ext_net["id"] + else: + ext_networks = self.external_networks + if not ext_networks: + raise NetworkWrapperException( + "Failed to allocate floating IP: " + "no external networks found") + net_id = ext_networks[0]["id"] + + if not port_id: + if type(int_network) is dict: + port_id = self.create_port(int_network["id"])["id"] + elif int_network: + int_net = self.get_network(name=int_network) + if int_net["external"]: + raise NetworkWrapperException("Network is external: %s" + % int_network) + port_id = self.create_port(int_net["id"])["id"] + kwargs = {"floatingip": {"floating_network_id": net_id}, + "tenant_id": tenant_id, + "port_id": port_id} + + fip = self.client.create_floatingip(kwargs)["floatingip"] + return {"id": fip["id"], "ip": fip["floating_ip_address"]} + + def delete_floating_ip(self, fip_id, **kwargs): + """Delete floating IP. + + :param fip_id: int floating IP id + :param **kwargs: for compatibility, not used here + """ + self.client.delete_floatingip(fip_id) + + def supports_security_group(self): + """Check whether security group is supported + + :return: result tuple + :rtype: (bool, string) + """ + extensions = self.client.list_extensions().get("extensions", []) + use_sg = any(ext.get("alias") == "security-group" + for ext in extensions) + if use_sg: + return True, "" + + return False, _("neutron driver does not support security groups") + + +def wrap(clients, config=None): + """Returns available network wrapper instance. + + :param clients: rally.osclients.Clients instance + :param config: task config dict + :returns: NetworkWrapper subclass instance + """ + if hasattr(clients, "services"): + services = clients.services() + else: + services = clients("services") + + if consts.Service.NEUTRON in services.values(): + return NeutronWrapper(clients, config) + return NovaNetworkWrapper(clients, config) diff --git a/tests/unit/plugins/openstack/wrappers/__init__.py b/tests/unit/plugins/openstack/wrappers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/plugins/openstack/wrappers/test_keystone.py b/tests/unit/plugins/openstack/wrappers/test_keystone.py new file mode 100644 index 00000000..468caacc --- /dev/null +++ b/tests/unit/plugins/openstack/wrappers/test_keystone.py @@ -0,0 +1,208 @@ +# Copyright 2014: 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 keystoneclient import exceptions +import mock + +from rally.plugins.openstack.wrappers import keystone +from tests.unit import test + + +class KeystoneWrapperTestBase(object): + def test_list_services(self): + service = mock.MagicMock() + service.id = "fake_id" + service.name = "Foobar" + service.extra_field = "extra_field" + self.client.services.list.return_value = [service] + result = list(self.wrapped_client.list_services()) + self.assertEqual([("fake_id", "Foobar")], result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("Foobar", result[0].name) + self.assertFalse(hasattr(result[0], "extra_field")) + + def test_wrap(self): + client = mock.MagicMock() + client.version = "dummy" + self.assertRaises(NotImplementedError, keystone.wrap, client) + + def test_delete_service(self): + self.wrapped_client.delete_service("fake_id") + self.client.services.delete.assert_called_once_with("fake_id") + + def test_list_roles(self): + role = mock.MagicMock() + role.id = "fake_id" + role.name = "Foobar" + role.extra_field = "extra_field" + self.client.roles.list.return_value = [role] + result = list(self.wrapped_client.list_roles()) + self.assertEqual([("fake_id", "Foobar")], result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("Foobar", result[0].name) + self.assertFalse(hasattr(result[0], "extra_field")) + + def test_delete_role(self): + self.wrapped_client.delete_role("fake_id") + self.client.roles.delete.assert_called_once_with("fake_id") + + +class KeystoneV2WrapperTestCase(test.TestCase, KeystoneWrapperTestBase): + def setUp(self): + super(KeystoneV2WrapperTestCase, self).setUp() + self.client = mock.MagicMock() + self.client.version = "v2.0" + self.wrapped_client = keystone.wrap(self.client) + + def test_create_project(self): + self.wrapped_client.create_project("Foobar") + self.client.tenants.create.assert_called_once_with("Foobar") + + def test_create_project_in_non_default_domain_fail(self): + self.assertRaises( + NotImplementedError, self.wrapped_client.create_project, + "Foobar", "non-default-domain") + + def test_delete_project(self): + self.wrapped_client.delete_project("fake_id") + self.client.tenants.delete.assert_called_once_with("fake_id") + + def test_list_projects(self): + tenant = mock.MagicMock() + tenant.id = "fake_id" + tenant.name = "Foobar" + tenant.extra_field = "extra_field" + self.client.tenants.list.return_value = [tenant] + result = list(self.wrapped_client.list_projects()) + self.assertEqual([("fake_id", "Foobar", "default")], result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("Foobar", result[0].name) + self.assertEqual("default", result[0].domain_id) + self.assertFalse(hasattr(result[0], "extra_field")) + + def test_create_user(self): + self.wrapped_client.create_user("foo", "bar", email="foo@bar.com", + project_id="tenant_id", + domain_name="default") + self.client.users.create.assert_called_once_with( + "foo", "bar", "foo@bar.com", "tenant_id") + + def test_create_user_in_non_default_domain_fail(self): + self.assertRaises( + NotImplementedError, self.wrapped_client.create_user, + "foo", "bar", email="foo@bar.com", project_id="tenant_id", + domain_name="non-default-domain") + + def test_delete_user(self): + self.wrapped_client.delete_user("fake_id") + self.client.users.delete.assert_called_once_with("fake_id") + + def test_list_users(self): + user = mock.MagicMock() + user.id = "fake_id" + user.name = "foo" + user.tenantId = "tenant_id" + user.extra_field = "extra_field" + self.client.users.list.return_value = [user] + result = list(self.wrapped_client.list_users()) + self.assertEqual([("fake_id", "foo", "tenant_id", "default")], result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("foo", result[0].name) + self.assertEqual("tenant_id", result[0].project_id) + self.assertEqual("default", result[0].domain_id) + self.assertFalse(hasattr(result[0], "extra_field")) + + +class KeystoneV3WrapperTestCase(test.TestCase, KeystoneWrapperTestBase): + def setUp(self): + super(KeystoneV3WrapperTestCase, self).setUp() + self.client = mock.MagicMock() + self.client.version = "v3" + self.wrapped_client = keystone.wrap(self.client) + self.client.domains.get.side_effect = exceptions.NotFound + self.client.domains.list.return_value = [ + mock.MagicMock(id="domain_id")] + + def test_create_project(self): + self.wrapped_client.create_project("Foobar", "domain") + self.client.projects.create.assert_called_once_with( + name="Foobar", domain="domain_id") + + def test_create_project_with_non_existing_domain_fail(self): + self.client.domains.list.return_value = [] + self.assertRaises(exceptions.NotFound, + self.wrapped_client.create_project, + "Foobar", "non-existing-domain") + + def test_delete_project(self): + self.wrapped_client.delete_project("fake_id") + self.client.projects.delete.assert_called_once_with("fake_id") + + def test_list_projects(self): + project = mock.MagicMock() + project.id = "fake_id" + project.name = "Foobar" + project.domain_id = "domain_id" + project.extra_field = "extra_field" + self.client.projects.list.return_value = [project] + result = list(self.wrapped_client.list_projects()) + self.assertEqual([("fake_id", "Foobar", "domain_id")], result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("Foobar", result[0].name) + self.assertEqual("domain_id", result[0].domain_id) + self.assertFalse(hasattr(result[0], "extra_field")) + + def test_create_user(self): + fake_role = mock.MagicMock(id="fake_role_id") + fake_role.name = "__member__" + self.client.roles.list.return_value = [fake_role] + self.client.users.create.return_value = mock.MagicMock( + id="fake_user_id") + + self.wrapped_client.create_user( + "foo", "bar", email="foo@bar.com", + project_id="project_id", domain_name="domain") + self.client.users.create.assert_called_once_with( + name="foo", password="bar", + email="foo@bar.com", default_project="project_id", + domain="domain_id") + + def test_create_user_with_non_existing_domain_fail(self): + self.client.domains.list.return_value = [] + self.assertRaises(exceptions.NotFound, + self.wrapped_client.create_user, "foo", "bar", + email="foo@bar.com", project_id="project_id", + domain_name="non-existing-domain") + + def test_delete_user(self): + self.wrapped_client.delete_user("fake_id") + self.client.users.delete.assert_called_once_with("fake_id") + + def test_list_users(self): + user = mock.MagicMock() + user.id = "fake_id" + user.name = "foo" + user.default_project_id = "project_id" + user.domain_id = "domain_id" + user.extra_field = "extra_field" + self.client.users.list.return_value = [user] + result = list(self.wrapped_client.list_users()) + self.assertEqual([("fake_id", "foo", "project_id", "domain_id")], + result) + self.assertEqual("fake_id", result[0].id) + self.assertEqual("foo", result[0].name) + self.assertEqual("project_id", result[0].project_id) + self.assertEqual("domain_id", result[0].domain_id) + self.assertFalse(hasattr(result[0], "extra_field")) diff --git a/tests/unit/plugins/openstack/wrappers/test_network.py b/tests/unit/plugins/openstack/wrappers/test_network.py new file mode 100644 index 00000000..d875f173 --- /dev/null +++ b/tests/unit/plugins/openstack/wrappers/test_network.py @@ -0,0 +1,494 @@ +# Copyright 2014: 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 mock + +from rally import consts +from rally import exceptions +from rally.plugins.openstack.wrappers import network +from tests.unit import test + +from neutronclient.common import exceptions as neutron_exceptions +from novaclient import exceptions as nova_exceptions + +SVC = "rally.plugins.openstack.wrappers.network." + + +class NovaNetworkWrapperTestCase(test.TestCase): + + class Net(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def get_wrapper(self, *skip_cidrs, **kwargs): + mock_clients = mock.Mock() + mock_clients.nova.return_value.networks.list.return_value = [ + self.Net(cidr=cidr) for cidr in skip_cidrs] + return network.NovaNetworkWrapper(mock_clients, kwargs) + + def test__init__(self): + skip_cidrs = ["foo_cidr", "bar_cidr"] + service = self.get_wrapper(*skip_cidrs) + self.assertEqual(service.skip_cidrs, skip_cidrs) + service.client.networks.list.assert_called_once_with() + + @mock.patch("rally.plugins.openstack.wrappers.network.generate_cidr") + def test__generate_cidr(self, mock_cidr): + skip_cidrs = [5, 7] + cidrs = iter(range(7)) + mock_cidr.side_effect = lambda start_cidr: start_cidr + next(cidrs) + service = self.get_wrapper(*skip_cidrs, start_cidr=3) + self.assertEqual(service._generate_cidr(), 3) + self.assertEqual(service._generate_cidr(), 4) + self.assertEqual(service._generate_cidr(), 6) # 5 is skipped + self.assertEqual(service._generate_cidr(), 8) # 7 is skipped + self.assertEqual(service._generate_cidr(), 9) + self.assertEqual(mock_cidr.mock_calls, [mock.call(start_cidr=3)] * 7) + + @mock.patch("rally.common.utils.generate_random_name", + return_value="foo_name") + def test_create_network(self, mock_name): + service = self.get_wrapper() + service.client.networks.create.side_effect = ( + lambda **kwargs: self.Net(id="foo_id", **kwargs)) + service._generate_cidr = mock.Mock(return_value="foo_cidr") + net = service.create_network("foo_tenant", bar="spam") + self.assertEqual(net, {"id": "foo_id", + "name": "foo_name", + "cidr": "foo_cidr", + "status": "ACTIVE", + "external": False, + "tenant_id": "foo_tenant"}) + mock_name.assert_called_once_with("rally_net_") + service._generate_cidr.assert_called_once_with() + service.client.networks.create.assert_called_once_with( + tenant_id="foo_tenant", cidr="foo_cidr", label="foo_name") + + def test_delete_network(self): + service = self.get_wrapper() + service.client.networks.delete.return_value = "foo_deleted" + self.assertEqual(service.delete_network({"id": "foo_id"}), + "foo_deleted") + service.client.networks.delete.assert_called_once_with("foo_id") + + def test_list_networks(self): + service = self.get_wrapper() + service.client.networks.list.return_value = "foo_list" + service.client.networks.list.reset_mock() + self.assertEqual(service.list_networks(), "foo_list") + service.client.networks.list.assert_called_once_with() + + def test__get_floating_ip(self): + wrap = self.get_wrapper() + wrap.client.floating_ips.get.return_value = mock.Mock(id="foo_id", + ip="foo_ip") + fip = wrap._get_floating_ip("fip_id") + wrap.client.floating_ips.get.assert_called_once_with("fip_id") + self.assertEqual(fip, "foo_id") + + wrap.client.floating_ips.get.side_effect = ( + nova_exceptions.NotFound("")) + self.assertIsNone(wrap._get_floating_ip("fip_id")) + + self.assertRaises(exceptions.GetResourceNotFound, + wrap._get_floating_ip, "fip_id", do_raise=True) + + def test_create_floating_ip(self): + wrap = self.get_wrapper() + wrap.client.floating_ips.create.return_value = mock.Mock(id="foo_id", + ip="foo_ip") + fip = wrap.create_floating_ip(ext_network="bar_net", bar="spam") + self.assertEqual(fip, {"ip": "foo_ip", "id": "foo_id"}) + wrap.client.floating_ips.create.assert_called_once_with("bar_net") + + net = mock.Mock() + net.name = "foo_net" + wrap.client.floating_ip_pools.list.return_value = [net] + fip = wrap.create_floating_ip() + self.assertEqual(fip, {"ip": "foo_ip", "id": "foo_id"}) + wrap.client.floating_ips.create.assert_called_with("foo_net") + + def test_delete_floating_ip(self): + wrap = self.get_wrapper() + fip_found = iter(range(3)) + + def get_fip(*args, **kwargs): + for i in fip_found: + return "fip_id" + raise exceptions.GetResourceNotFound + wrap._get_floating_ip = mock.Mock(side_effect=get_fip) + + wrap.delete_floating_ip("fip_id") + wrap.client.floating_ips.delete.assert_called_once_with("fip_id") + self.assertFalse(wrap._get_floating_ip.called) + + wrap.delete_floating_ip("fip_id", wait=True) + self.assertEqual( + [mock.call("fip_id", do_raise=True)] * 4, + wrap._get_floating_ip.mock_calls) + + def test_supports_secgroup(self): + wrap = self.get_wrapper() + self.assertTrue(wrap.supports_security_group()[0]) + + +class NeutronWrapperTestCase(test.TestCase): + def get_wrapper(self, *skip_cidrs, **kwargs): + return network.NeutronWrapper(mock.Mock(), kwargs) + + def test_SUBNET_IP_VERSION(self): + self.assertEqual(network.NeutronWrapper.SUBNET_IP_VERSION, 4) + + @mock.patch("rally.plugins.openstack.wrappers.network.generate_cidr") + def test__generate_cidr(self, mock_cidr): + cidrs = iter(range(5)) + mock_cidr.side_effect = lambda start_cidr: start_cidr + next(cidrs) + service = self.get_wrapper(start_cidr=3) + self.assertEqual(service._generate_cidr(), 3) + self.assertEqual(service._generate_cidr(), 4) + self.assertEqual(service._generate_cidr(), 5) + self.assertEqual(service._generate_cidr(), 6) + self.assertEqual(service._generate_cidr(), 7) + self.assertEqual(mock_cidr.mock_calls, [mock.call(start_cidr=3)] * 5) + + def test_external_networks(self): + wrap = self.get_wrapper() + wrap.client.list_networks.return_value = {"networks": "foo_networks"} + self.assertEqual(wrap.external_networks, "foo_networks") + wrap.client.list_networks.assert_called_once_with( + **{"router:external": True}) + + def test_get_network(self): + wrap = self.get_wrapper() + neutron_net = {"id": "foo_id", + "name": "foo_name", + "tenant_id": "foo_tenant", + "status": "foo_status", + "router:external": "foo_external", + "subnets": "foo_subnets"} + expected_net = {"id": "foo_id", + "name": "foo_name", + "tenant_id": "foo_tenant", + "status": "foo_status", + "external": "foo_external", + "router_id": None, + "subnets": "foo_subnets"} + wrap.client.show_network.return_value = {"network": neutron_net} + net = wrap.get_network(net_id="foo_id") + self.assertEqual(net, expected_net) + wrap.client.show_network.assert_called_once_with("foo_id") + + wrap.client.show_network.side_effect = ( + neutron_exceptions.NeutronClientException) + self.assertRaises(network.NetworkWrapperException, wrap.get_network, + net_id="foo_id") + + wrap.client.list_networks.return_value = {"networks": [neutron_net]} + net = wrap.get_network(name="foo_name") + self.assertEqual(net, expected_net) + wrap.client.list_networks.assert_called_once_with(name="foo_name") + + wrap.client.list_networks.return_value = {"networks": []} + self.assertRaises(network.NetworkWrapperException, wrap.get_network, + name="foo_name") + + @mock.patch("rally.common.utils.generate_random_name") + def test_create_network(self, mock_name): + mock_name.return_value = "foo_name" + service = self.get_wrapper() + service.client.create_network.return_value = { + "network": {"id": "foo_id", + "name": "foo_name", + "status": "foo_status"}} + net = service.create_network("foo_tenant") + mock_name.assert_called_once_with("rally_net_") + service.client.create_network.assert_called_once_with({ + "network": {"tenant_id": "foo_tenant", "name": "foo_name"}}) + self.assertEqual(net, {"id": "foo_id", + "name": "foo_name", + "status": "foo_status", + "external": False, + "tenant_id": "foo_tenant", + "router_id": None, + "subnets": []}) + + @mock.patch("rally.common.utils.generate_random_name") + def test_create_network_with_subnets(self, mock_name): + subnets_num = 4 + mock_name.return_value = "foo_name" + service = self.get_wrapper() + subnets_cidrs = iter(range(subnets_num)) + subnets_ids = iter(range(subnets_num)) + service._generate_cidr = mock.Mock( + side_effect=lambda: "cidr-%d" % next(subnets_cidrs)) + service.client.create_subnet = mock.Mock( + side_effect=lambda i: { + "subnet": {"id": "subnet-%d" % next(subnets_ids)}}) + service.client.create_network.return_value = { + "network": {"id": "foo_id", + "name": "foo_name", + "status": "foo_status"}} + net = service.create_network("foo_tenant", subnets_num=subnets_num) + service.client.create_network.assert_called_once_with({ + "network": {"tenant_id": "foo_tenant", "name": "foo_name"}}) + self.assertEqual(net, {"id": "foo_id", + "name": "foo_name", + "status": "foo_status", + "external": False, + "router_id": None, + "tenant_id": "foo_tenant", + "subnets": ["subnet-%d" % i + for i in range(subnets_num)]}) + self.assertEqual( + service.client.create_subnet.mock_calls, + [mock.call({"subnet": {"name": "foo_name", + "enable_dhcp": True, + "network_id": "foo_id", + "tenant_id": "foo_tenant", + "ip_version": service.SUBNET_IP_VERSION, + "dns_nameservers": ["8.8.8.8", "8.8.4.4"], + "cidr": "cidr-%d" % i}}) + for i in range(subnets_num)]) + + @mock.patch("rally.common.utils.generate_random_name") + def test_create_network_with_router(self, mock_name): + mock_name.return_value = "foo_name" + service = self.get_wrapper() + service.create_router = mock.Mock(return_value={"id": "foo_router"}) + service.client.create_network.return_value = { + "network": {"id": "foo_id", + "name": "foo_name", + "status": "foo_status"}} + net = service.create_network("foo_tenant", add_router=True) + self.assertEqual(net, {"id": "foo_id", + "name": "foo_name", + "status": "foo_status", + "external": False, + "tenant_id": "foo_tenant", + "router_id": "foo_router", + "subnets": []}) + service.create_router.assert_called_once_with(external=True, + tenant_id="foo_tenant") + + @mock.patch("rally.common.utils.generate_random_name") + def test_create_network_with_router_and_subnets(self, mock_name): + subnets_num = 4 + mock_name.return_value = "foo_name" + service = self.get_wrapper() + service._generate_cidr = mock.Mock(return_value="foo_cidr") + service.create_router = mock.Mock(return_value={"id": "foo_router"}) + service.client.create_subnet = mock.Mock( + return_value={"subnet": {"id": "foo_subnet"}}) + service.client.create_network.return_value = { + "network": {"id": "foo_id", + "name": "foo_name", + "status": "foo_status"}} + net = service.create_network("foo_tenant", add_router=True, + subnets_num=subnets_num, + dns_nameservers=["foo_nameservers"]) + self.assertEqual(net, {"id": "foo_id", + "name": "foo_name", + "status": "foo_status", + "external": False, + "tenant_id": "foo_tenant", + "router_id": "foo_router", + "subnets": ["foo_subnet"] * subnets_num}) + service.create_router.assert_called_once_with(external=True, + tenant_id="foo_tenant") + self.assertEqual( + service.client.create_subnet.mock_calls, + [mock.call({"subnet": {"name": "foo_name", + "enable_dhcp": True, + "network_id": "foo_id", + "tenant_id": "foo_tenant", + "ip_version": service.SUBNET_IP_VERSION, + "dns_nameservers": ["foo_nameservers"], + "cidr": "foo_cidr"}})] * subnets_num) + self.assertEqual(service.client.add_interface_router.mock_calls, + [mock.call("foo_router", {"subnet_id": "foo_subnet"}) + for i in range(subnets_num)]) + + def test_delete_network(self): + service = self.get_wrapper() + service.client.list_dhcp_agent_hosting_networks.return_value = ( + {"agents": []}) + service.client.list_ports.return_value = {"ports": []} + service.client.delete_network.return_value = "foo_deleted" + result = service.delete_network({"id": "foo_id", "router_id": None, + "subnets": []}) + self.assertEqual(result, "foo_deleted") + self.assertEqual( + service.client.remove_network_from_dhcp_agent.mock_calls, []) + self.assertEqual(service.client.remove_gateway_router.mock_calls, []) + self.assertEqual( + service.client.remove_interface_router.mock_calls, []) + self.assertEqual(service.client.delete_router.mock_calls, []) + self.assertEqual(service.client.delete_subnet.mock_calls, []) + service.client.delete_network.assert_called_once_with("foo_id") + + def test_delete_network_with_dhcp_and_router_and_ports_and_subnets(self): + service = self.get_wrapper() + agents = ["foo_agent", "bar_agent"] + subnets = ["foo_subnet", "bar_subnet"] + ports = ["foo_port", "bar_port"] + service.client.list_dhcp_agent_hosting_networks.return_value = ( + {"agents": [{"id": agent_id} for agent_id in agents]}) + service.client.list_ports.return_value = ( + {"ports": [{"id": port_id} for port_id in ports]}) + service.client.delete_network.return_value = "foo_deleted" + result = service.delete_network( + {"id": "foo_id", "router_id": "foo_router", "subnets": subnets}) + self.assertEqual(result, "foo_deleted") + self.assertEqual( + service.client.remove_network_from_dhcp_agent.mock_calls, + [mock.call(agent_id, "foo_id") for agent_id in agents]) + self.assertEqual(service.client.remove_gateway_router.mock_calls, + [mock.call("foo_router")]) + self.assertEqual( + service.client.remove_interface_router.mock_calls, + [mock.call("foo_router", {"subnet_id": subnet_id}) + for subnet_id in subnets]) + self.assertEqual(service.client.delete_router.mock_calls, + [mock.call("foo_router")]) + self.assertEqual(service.client.delete_port.mock_calls, + [mock.call(port_id) for port_id in ports]) + self.assertEqual(service.client.delete_subnet.mock_calls, + [mock.call(subnet_id) for subnet_id in subnets]) + service.client.delete_network.assert_called_once_with("foo_id") + + def test_list_networks(self): + service = self.get_wrapper() + service.client.list_networks.return_value = {"networks": "foo_nets"} + self.assertEqual(service.list_networks(), "foo_nets") + service.client.list_networks.assert_called_once_with() + + @mock.patch(SVC + "NeutronWrapper.external_networks") + def test_create_floating_ip(self, mock_ext_networks): + wrap = self.get_wrapper() + wrap.create_port = mock.Mock(return_value={"id": "port_id"}) + wrap.client.create_floatingip = mock.Mock( + return_value={"floatingip": {"id": "fip_id", + "floating_ip_address": "fip_ip"}}) + + self.assertRaises(ValueError, wrap.create_floating_ip) + + mock_ext_networks.__get__ = lambda *args: [] + self.assertRaises(network.NetworkWrapperException, + wrap.create_floating_ip, tenant_id="foo_tenant") + + mock_ext_networks.__get__ = lambda *args: [{"id": "ext_id"}] + fip = wrap.create_floating_ip(tenant_id="foo_tenant") + self.assertEqual(fip, {"id": "fip_id", "ip": "fip_ip"}) + + wrap.get_network = mock.Mock( + return_value={"id": "foo_net", "external": True}) + self.assertRaises(network.NetworkWrapperException, + wrap.create_floating_ip, tenant_id="foo_tenant", + int_network="int_net") + wrap.create_floating_ip(tenant_id="foo_tenant", ext_network="ext_net") + + wrap.get_network = mock.Mock( + return_value={"id": "foo_net", "external": False}) + wrap.create_floating_ip(tenant_id="foo_tenant", int_network="int_net") + + self.assertRaises(network.NetworkWrapperException, + wrap.create_floating_ip, tenant_id="foo_tenant", + ext_network="ext_net") + + def test_delete_floating_ip(self): + wrap = self.get_wrapper() + wrap.delete_floating_ip("fip_id") + wrap.delete_floating_ip("fip_id", ignored_kwarg="bar") + self.assertEqual(wrap.client.delete_floatingip.mock_calls, + [mock.call("fip_id")] * 2) + + @mock.patch(SVC + "NeutronWrapper.external_networks") + @mock.patch("rally.common.utils.generate_random_name") + def test_create_router(self, mock_random, mock_ext_networks): + wrap = self.get_wrapper() + mock_random.return_value = "random_name" + wrap.client.create_router.return_value = {"router": "foo_router"} + mock_ext_networks.__get__ = lambda *args: [{"id": "ext_id"}] + + router = wrap.create_router(name="foo_name") + wrap.client.create_router.assert_called_once_with( + {"router": {"name": "foo_name"}}) + self.assertEqual(router, "foo_router") + + router = wrap.create_router(external=True, foo="bar") + wrap.client.create_router.assert_called_with( + {"router": {"name": "random_name", + "external_gateway_info": { + "network_id": "ext_id", + "enable_snat": True}, + "foo": "bar"}}) + + @mock.patch("rally.common.utils.generate_random_name") + def test_create_port(self, mock_random): + wrap = self.get_wrapper() + mock_random.return_value = "random_name" + wrap.client.create_port.return_value = {"port": "foo_port"} + + port = wrap.create_port("foo_net", name="foo_name") + wrap.client.create_port.assert_called_once_with( + {"port": {"network_id": "foo_net", "name": "foo_name"}}) + self.assertEqual(port, "foo_port") + + port = wrap.create_port("foo_net", foo="bar") + wrap.client.create_port.assert_called_with( + {"port": {"network_id": "foo_net", + "name": "random_name", "foo": "bar"}}) + + def test_supports_security_group(self): + wrap = self.get_wrapper() + wrap.client.list_extensions.return_value = ( + {"extensions": [{"alias": "security-group"}]}) + self.assertTrue(wrap.supports_security_group()[0]) + + wrap.client.list_extensions.return_value = ( + {"extensions": [{"alias": "dummy-group"}]}) + self.assertFalse(wrap.supports_security_group()[0]) + + wrap.client.list_extensions.return_value = {} + self.assertFalse(wrap.supports_security_group()[0]) + + +class FunctionsTestCase(test.TestCase): + + def test_generate_cidr(self): + with mock.patch("rally.plugins.openstack.wrappers.network.cidr_incr", + iter(range(1, 4))): + self.assertEqual(network.generate_cidr(), "10.2.1.0/24") + self.assertEqual(network.generate_cidr(), "10.2.2.0/24") + self.assertEqual(network.generate_cidr(), "10.2.3.0/24") + + with mock.patch("rally.plugins.openstack.wrappers.network.cidr_incr", + iter(range(1, 4))): + start_cidr = "1.1.0.0/26" + self.assertEqual(network.generate_cidr(start_cidr), "1.1.0.64/26") + self.assertEqual(network.generate_cidr(start_cidr), "1.1.0.128/26") + self.assertEqual(network.generate_cidr(start_cidr), "1.1.0.192/26") + + def test_wrap(self): + mock_clients = mock.Mock() + mock_clients.nova().networks.list.return_value = [] + + mock_clients.services.return_value = {"foo": consts.Service.NEUTRON} + self.assertIsInstance(network.wrap(mock_clients, {}), + network.NeutronWrapper) + + mock_clients.services.return_value = {"foo": "bar"} + self.assertIsInstance(network.wrap(mock_clients, {}), + network.NovaNetworkWrapper)