Merge "[wrappers] Move wrappers under plugins.openstack"

This commit is contained in:
Jenkins 2015-06-03 11:47:55 +00:00 committed by Gerrit Code Review
commit 02b38b6268
10 changed files with 1330 additions and 5 deletions

View File

@ -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__)

View File

@ -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__)

View File

@ -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 = [

View File

@ -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__)

View File

@ -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)

View File

@ -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)

View File

@ -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"))

View File

@ -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)