Merge "Adds LBaaS context, verify network extension"

This commit is contained in:
Jenkins 2015-07-23 09:33:12 +00:00 committed by Gerrit Code Review
commit 104d7433f7
11 changed files with 334 additions and 28 deletions

View File

@ -106,6 +106,9 @@
tenants: 1 tenants: 1
users_per_tenant: 1 users_per_tenant: 1
network: {} network: {}
lbaas:
pool: {}
lbaas_version: 1
quotas: quotas:
neutron: neutron:
network: -1 network: -1

View File

@ -111,7 +111,8 @@ class UserGenerator(context.Context):
if consts.Service.NEUTRON not in clients.services().values(): if consts.Service.NEUTRON not in clients.services().values():
return return
use_sg, msg = network.wrap(clients).supports_security_group() use_sg, msg = network.wrap(clients).supports_extension(
"security-group")
if not use_sg: if not use_sg:
LOG.debug("Security group context is disabled: %s" % msg) LOG.debug("Security group context is disabled: %s" % msg)
return return

View File

@ -96,7 +96,7 @@ class AllowSSH(context.Context):
net_wrapper = network.wrap( net_wrapper = network.wrap(
osclients.Clients(admin_or_user["endpoint"]), osclients.Clients(admin_or_user["endpoint"]),
self.config) self.config)
use_sg, msg = net_wrapper.supports_security_group() use_sg, msg = net_wrapper.supports_extension("security-group")
if not use_sg: if not use_sg:
LOG.info(_("Security group context is disabled: %s") % msg) LOG.info(_("Security group context is disabled: %s") % msg)
return return

View File

@ -0,0 +1,92 @@
# 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 six
from rally.common.i18n import _
from rally.common import log as logging
from rally.common import utils
from rally import consts
from rally import osclients
from rally.plugins.openstack.wrappers import network as network_wrapper
from rally.task import context
LOG = logging.getLogger(__name__)
@context.context(name="lbaas", order=360)
class Lbaas(context.Context):
CONFIG_SCHEMA = {
"type": "object",
"$schema": consts.JSON_SCHEMA,
"properties": {
"pool": {
"type": "object"
},
"lbaas_version": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": False
}
DEFAULT_CONFIG = {
"pool": {
"lb_method": "ROUND_ROBIN",
"protocol": "HTTP"
},
"lbaas_version": 1
}
@utils.log_task_wrapper(LOG.info, _("Enter context: `lbaas`"))
def setup(self):
net_wrapper = network_wrapper.wrap(
osclients.Clients(self.context["admin"]["endpoint"]),
self.config)
use_lb, msg = net_wrapper.supports_extension("lbaas")
if not use_lb:
LOG.info(msg)
return
# Creates a lb-pool for every subnet created in network context.
for user, tenant_id in (utils.iterate_per_tenants(
self.context.get("users", []))):
for network in self.context["tenants"][tenant_id]["networks"]:
for subnet in network.get("subnets", []):
if self.config["lbaas_version"] == 1:
network.setdefault("lb_pools", []).append(
net_wrapper.create_v1_pool(
subnet,
**self.config["pool"]))
else:
raise NotImplementedError(
"Context for LBaaS version %s not implemented."
% self.config["lbaas_version"])
@utils.log_task_wrapper(LOG.info, _("Exit context: `lbaas`"))
def cleanup(self):
net_wrapper = network_wrapper.wrap(
osclients.Clients(self.context["admin"]["endpoint"]),
self.config)
for tenant_id, tenant_ctx in six.iteritems(self.context["tenants"]):
for network in tenant_ctx.get("networks", []):
for pool in network.get("lb_pools", []):
with logging.ExceptionLogger(
LOG,
_("Failed to delete pool %(pool)s for tenant "
"%(tenant)s") % {"pool": pool["pool"]["id"],
"tenant": tenant_id}):
if self.config["lbaas_version"] == 1:
net_wrapper.delete_v1_pool(pool["pool"]["id"])

View File

@ -95,8 +95,8 @@ class NetworkWrapper(object):
"""Delete floating IP.""" """Delete floating IP."""
@abc.abstractmethod @abc.abstractmethod
def supports_security_group(self): def supports_extension(self):
"""Checks whether security group is supported.""" """Checks whether a network extension is supported."""
class NovaNetworkWrapper(NetworkWrapper): class NovaNetworkWrapper(NetworkWrapper):
@ -174,18 +174,25 @@ class NovaNetworkWrapper(NetworkWrapper):
fip_id, fip_id,
update_resource=lambda i: self._get_floating_ip(i, do_raise=True)) update_resource=lambda i: self._get_floating_ip(i, do_raise=True))
def supports_security_group(self): def supports_extension(self, extension):
"""Check whether security group is supported """Check whether a Nova-network extension is supported
:return: result tuple. Always (True, "") for nova-network. :param extension: str Nova network extension
:returns: result tuple. Always (True, "") for secgroups in nova-network
:rtype: (bool, string) :rtype: (bool, string)
""" """
return True, "" # TODO(rkiran): Add other extensions whenever necessary
if extension == "security-group":
return True, ""
return False, _("Nova driver does not support %s") % (extension)
class NeutronWrapper(NetworkWrapper): class NeutronWrapper(NetworkWrapper):
SERVICE_IMPL = consts.Service.NEUTRON SERVICE_IMPL = consts.Service.NEUTRON
SUBNET_IP_VERSION = 4 SUBNET_IP_VERSION = 4
LB_METHOD = "ROUND_ROBIN"
LB_PROTOCOL = "HTTP"
@property @property
def external_networks(self): def external_networks(self):
@ -227,6 +234,23 @@ class NeutronWrapper(NetworkWrapper):
"network_id": net["id"], "enable_snat": True} "network_id": net["id"], "enable_snat": True}
return self.client.create_router({"router": kwargs})["router"] return self.client.create_router({"router": kwargs})["router"]
def create_v1_pool(self, subnet_id, **kwargs):
"""Create LB Pool (v1).
:param subnet_id: str, neutron subnet-id
:param **kwargs: extra options
:returns: neutron lb-pool dict
"""
pool_args = {
"pool": {
"name": utils.generate_random_name("rally_pool_"),
"subnet_id": subnet_id,
"lb_method": kwargs.get("lb_method", self.LB_METHOD),
"protocol": kwargs.get("protocol", self.LB_PROTOCOL)
}
}
return self.client.create_pool(pool_args)
def _generate_cidr(self): def _generate_cidr(self):
# TODO(amaretskiy): Generate CIDRs unique for network, not cluster # TODO(amaretskiy): Generate CIDRs unique for network, not cluster
return generate_cidr(start_cidr=self.start_cidr) return generate_cidr(start_cidr=self.start_cidr)
@ -280,6 +304,13 @@ class NeutronWrapper(NetworkWrapper):
"router_id": router and router["id"] or None, "router_id": router and router["id"] or None,
"tenant_id": tenant_id} "tenant_id": tenant_id}
def delete_v1_pool(self, pool_id):
"""Delete LB Pool (v1)
:param pool_id: str, Lb-Pool-id
"""
self.client.delete_pool(pool_id)
def delete_network(self, network): def delete_network(self, network):
net_dhcps = self.client.list_dhcp_agent_hosting_networks( net_dhcps = self.client.list_dhcp_agent_hosting_networks(
network["id"])["agents"] network["id"])["agents"]
@ -366,19 +397,18 @@ class NeutronWrapper(NetworkWrapper):
""" """
self.client.delete_floatingip(fip_id) self.client.delete_floatingip(fip_id)
def supports_security_group(self): def supports_extension(self, extension):
"""Check whether security group is supported """Check whether a neutron extension is supported
:param extension: str, neutron extension
:return: result tuple :return: result tuple
:rtype: (bool, string) :rtype: (bool, string)
""" """
extensions = self.client.list_extensions().get("extensions", []) extensions = self.client.list_extensions().get("extensions", [])
use_sg = any(ext.get("alias") == "security-group" if any(ext.get("alias") == extension for ext in extensions):
for ext in extensions)
if use_sg:
return True, "" return True, ""
return False, _("neutron driver does not support security groups") return False, _("Neutron driver does not support %s") % (extension)
def wrap(clients, config=None): def wrap(clients, config=None):

View File

@ -65,7 +65,7 @@ class UserGeneratorTestCase(test.TestCase):
@mock.patch("%s.network.wrap" % CTX) @mock.patch("%s.network.wrap" % CTX)
def test__remove_default_security_group_neutron_no_sg(self, mock_wrap): def test__remove_default_security_group_neutron_no_sg(self, mock_wrap):
net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON) net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON)
net_wrapper.supports_security_group.return_value = (False, None) net_wrapper.supports_extension.return_value = (False, None)
mock_wrap.return_value = net_wrapper mock_wrap.return_value = net_wrapper
user_generator = users.UserGenerator(self.context) user_generator = users.UserGenerator(self.context)
@ -80,7 +80,8 @@ class UserGeneratorTestCase(test.TestCase):
user_generator._remove_default_security_group() user_generator._remove_default_security_group()
mock_wrap.assert_called_once_with(admin_clients) mock_wrap.assert_called_once_with(admin_clients)
net_wrapper.supports_security_group.assert_called_once_with() net_wrapper.supports_extension.assert_called_once_with(
"security-group")
@mock.patch("rally.common.utils.iterate_per_tenants") @mock.patch("rally.common.utils.iterate_per_tenants")
@mock.patch("%s.network" % CTX) @mock.patch("%s.network" % CTX)
@ -90,7 +91,7 @@ class UserGeneratorTestCase(test.TestCase):
self, mock_check_service_status, mock_network, self, mock_check_service_status, mock_network,
mock_iterate_per_tenants): mock_iterate_per_tenants):
net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON) net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON)
net_wrapper.supports_security_group.return_value = (True, None) net_wrapper.supports_extension.return_value = (True, None)
mock_network.wrap.return_value = net_wrapper mock_network.wrap.return_value = net_wrapper
user_generator = users.UserGenerator(self.context) user_generator = users.UserGenerator(self.context)

View File

@ -100,7 +100,7 @@ class AllowSSHContextTestCase(test.TestCase):
self, mock_network_wrap, mock__prepare_open_secgroup, self, mock_network_wrap, mock__prepare_open_secgroup,
mock_clients): mock_clients):
mock_network_wrapper = mock.MagicMock() mock_network_wrapper = mock.MagicMock()
mock_network_wrapper.supports_security_group.return_value = ( mock_network_wrapper.supports_extension.return_value = (
True, "") True, "")
mock_network_wrap.return_value = mock_network_wrapper mock_network_wrap.return_value = mock_network_wrapper
mock__prepare_open_secgroup.return_value = { mock__prepare_open_secgroup.return_value = {
@ -131,7 +131,7 @@ class AllowSSHContextTestCase(test.TestCase):
def test_secgroup_setup_with_secgroup_unsupported( def test_secgroup_setup_with_secgroup_unsupported(
self, mock_network_wrap, mock_clients): self, mock_network_wrap, mock_clients):
mock_network_wrapper = mock.MagicMock() mock_network_wrapper = mock.MagicMock()
mock_network_wrapper.supports_security_group.return_value = ( mock_network_wrapper.supports_extension.return_value = (
False, "Not supported") False, "Not supported")
mock_network_wrap.return_value = mock_network_wrapper mock_network_wrap.return_value = mock_network_wrapper
mock_clients.return_value = mock.MagicMock() mock_clients.return_value = mock.MagicMock()

View File

@ -0,0 +1,153 @@
# 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.plugins.openstack.context.neutron import lbaas as lbaas_context
from tests.unit import test
NET = "rally.plugins.openstack.wrappers.network."
class LbaasTestCase(test.TestCase):
def get_context(self, **kwargs):
foo_tenant = {"networks": [{"id": "foo_net",
"subnets": ["foo_subnet"]}]}
bar_tenant = {"networks": [{"id": "bar_net",
"subnets": ["bar_subnet"]}]}
return {"task": {"uuid": "foo_task"},
"admin": {"endpoint": "foo_admin"},
"users": [{"id": "foo_user", "tenant_id": "foo_tenant"},
{"id": "bar_user", "tenant_id": "bar_tenant"}],
"config": {"lbaas": kwargs},
"tenants": {"foo_tenant": foo_tenant,
"bar_tenant": bar_tenant}}
@mock.patch("rally.osclients.Clients")
@mock.patch(NET + "wrap", return_value="foo_service")
def test__init__default(self, mock_wrap, mock_clients):
context = lbaas_context.Lbaas(self.get_context())
self.assertEqual(
context.config["pool"]["lb_method"],
lbaas_context.Lbaas.DEFAULT_CONFIG["pool"]["lb_method"])
self.assertEqual(
context.config["pool"]["protocol"],
lbaas_context.Lbaas.DEFAULT_CONFIG["pool"]["protocol"])
self.assertEqual(
context.config["lbaas_version"],
lbaas_context.Lbaas.DEFAULT_CONFIG["lbaas_version"])
@mock.patch("rally.osclients.Clients")
@mock.patch(NET + "wrap", return_value="foo_service")
def test__init__explicit(self, mock_wrap, mock_clients):
context = lbaas_context.Lbaas(
self.get_context(pool={"lb_method": "LEAST_CONNECTIONS"}))
self.assertEqual(context.config["pool"]["lb_method"],
"LEAST_CONNECTIONS")
@mock.patch(NET + "wrap")
@mock.patch("rally.plugins.openstack.context.neutron.lbaas.utils")
@mock.patch("rally.osclients.Clients")
def test_setup_with_lbaas(self, mock_clients, mock_utils, mock_wrap):
mock_utils.iterate_per_tenants.return_value = [
("foo_user", "foo_tenant"),
("bar_user", "bar_tenant")]
foo_net = {"id": "foo_net",
"subnets": ["foo_subnet"],
"lb_pools": [{"pool": {"id": "foo_pool"}}]}
bar_net = {"id": "bar_net",
"subnets": ["bar_subnet"],
"lb_pools": [{"pool": {"id": "bar_pool"}}]}
expected_net = [bar_net, foo_net]
mock_create = mock.Mock(
side_effect=lambda t,
**kw: {"pool": {"id": str(t.split("_")[0]) + "_pool"}})
actual_net = []
mock_wrap.return_value = mock.Mock(create_v1_pool=mock_create)
net_wrapper = mock_wrap(mock_clients.return_value)
net_wrapper.supports_extension.return_value = (True, None)
fake_args = {"lbaas_version": 1}
lb_context = lbaas_context.Lbaas(self.get_context(**fake_args))
lb_context.setup()
mock_utils.iterate_per_tenants.called_once_with(
lb_context.context["users"])
net_wrapper.supports_extension.called_once_with("lbaas")
for tenant_id, tenant_ctx in (
sorted(lb_context.context["tenants"].items())):
for network in tenant_ctx["networks"]:
actual_net.append(network)
self.assertEqual(expected_net, actual_net)
@mock.patch(NET + "wrap")
@mock.patch("rally.plugins.openstack.context.neutron.lbaas.utils")
@mock.patch("rally.osclients.Clients")
def test_setup_with_no_lbaas(self, mock_clients, mock_utils, mock_wrap):
mock_utils.iterate_per_tenants.return_value = [
("bar_user", "bar_tenant")]
mock_create = mock.Mock(side_effect=lambda t, **kw: t + "-net")
mock_wrap.return_value = mock.Mock(create_v1_pool=mock_create)
fake_args = {"lbaas_version": 1}
lb_context = lbaas_context.Lbaas(self.get_context(**fake_args))
net_wrapper = mock_wrap(mock_clients.return_value)
net_wrapper.supports_extension.return_value = (False, None)
lb_context.setup()
mock_utils.iterate_per_tenants.called_once_with(
lb_context.context["users"])
net_wrapper.supports_extension.assert_called_once_with("lbaas")
assert not net_wrapper.create_v1_pool.called
@mock.patch(NET + "wrap")
@mock.patch("rally.plugins.openstack.context.neutron.lbaas.utils")
@mock.patch("rally.osclients.Clients")
def test_setup_with_lbaas_version_not_one(self, mock_clients,
mock_utils, mock_wrap):
mock_utils.iterate_per_tenants.return_value = [
("bar_user", "bar_tenant")]
mock_create = mock.Mock(side_effect=lambda t, **kw: t + "-net")
mock_wrap.return_value = mock.Mock(create_v1_pool=mock_create)
fake_args = {"lbaas_version": 2}
lb_context = lbaas_context.Lbaas(self.get_context(**fake_args))
net_wrapper = mock_wrap(mock_clients.return_value)
net_wrapper.supports_extension.return_value = (True, None)
self.assertRaises(NotImplementedError, lb_context.setup)
@mock.patch("rally.osclients.Clients")
@mock.patch(NET + "wrap")
def test_cleanup(self, mock_wrap, mock_clients):
net_wrapper = mock_wrap(mock_clients.return_value)
lb_context = lbaas_context.Lbaas(self.get_context())
expected_pools = []
for tenant_id, tenant_ctx in lb_context.context["tenants"].items():
resultant_pool = {"pool": {
"id": str(tenant_id.split("_")[0]) + "_pool"}}
expected_pools.append(resultant_pool)
for network in (
lb_context.context["tenants"][tenant_id]["networks"]):
network.setdefault("lb_pools", []).append(resultant_pool)
lb_context.cleanup()
net_wrapper.delete_v1_pool.assert_has_calls(
[mock.call(pool["pool"]["id"]) for pool in expected_pools])
@mock.patch("rally.osclients.Clients")
@mock.patch(NET + "wrap")
def test_cleanup_lbaas_version_not_one(self, mock_wrap, mock_clients):
fakeargs = {"lbaas_version": 2}
net_wrapper = mock_wrap(mock_clients.return_value)
lb_context = lbaas_context.Lbaas(self.get_context(**fakeargs))
for tenant_id, tenant_ctx in lb_context.context["tenants"].items():
resultant_pool = {"pool": {
"id": str(tenant_id.split("_")[0]) + "_pool"}}
for network in (
lb_context.context["tenants"][tenant_id]["networks"]):
network.setdefault("lb_pools", []).append(resultant_pool)
lb_context.cleanup()
assert not net_wrapper.delete_v1_pool.called

View File

@ -142,9 +142,10 @@ class NovaNetworkWrapperTestCase(test.TestCase):
[mock.call("fip_id", do_raise=True)] * 4, [mock.call("fip_id", do_raise=True)] * 4,
wrap._get_floating_ip.mock_calls) wrap._get_floating_ip.mock_calls)
def test_supports_secgroup(self): def test_supports_extension(self):
wrap = self.get_wrapper() wrap = self.get_wrapper()
self.assertTrue(wrap.supports_security_group()[0]) self.assertFalse(wrap.supports_extension("extension")[0])
self.assertTrue(wrap.supports_extension("security-group")[0])
class NeutronWrapperTestCase(test.TestCase): class NeutronWrapperTestCase(test.TestCase):
@ -210,6 +211,24 @@ class NeutronWrapperTestCase(test.TestCase):
self.assertRaises(network.NetworkWrapperException, wrap.get_network, self.assertRaises(network.NetworkWrapperException, wrap.get_network,
name="foo_name") name="foo_name")
@mock.patch("rally.common.utils.generate_random_name")
def test_create_v1_pool(self, mock_generate_random_name):
mock_generate_random_name.return_value = "foo_name"
subnet = "subnet_id"
service = self.get_wrapper()
expected_pool = {"pool": {
"id": "pool_id",
"name": "foo_name",
"subnet_id": subnet}}
service.client.create_pool.return_value = expected_pool
resultant_pool = service.create_v1_pool(subnet)
service.client.create_pool.assert_called_once_with({
"pool": {"lb_method": "ROUND_ROBIN",
"subnet_id": subnet,
"protocol": "HTTP",
"name": "foo_name"}})
self.assertEqual(resultant_pool, expected_pool)
@mock.patch("rally.common.utils.generate_random_name") @mock.patch("rally.common.utils.generate_random_name")
def test_create_network(self, mock_generate_random_name): def test_create_network(self, mock_generate_random_name):
mock_generate_random_name.return_value = "foo_name" mock_generate_random_name.return_value = "foo_name"
@ -345,6 +364,12 @@ class NeutronWrapperTestCase(test.TestCase):
self.assertEqual(service.client.delete_subnet.mock_calls, []) self.assertEqual(service.client.delete_subnet.mock_calls, [])
service.client.delete_network.assert_called_once_with("foo_id") service.client.delete_network.assert_called_once_with("foo_id")
def test_delete_v1_pool(self):
service = self.get_wrapper()
pool = {"pool": {"id": "pool-id"}}
service.delete_v1_pool(pool["pool"]["id"])
service.client.delete_pool.called_once_with([mock.call("pool-id")])
def test_delete_network_with_dhcp_and_router_and_ports_and_subnets(self): def test_delete_network_with_dhcp_and_router_and_ports_and_subnets(self):
service = self.get_wrapper() service = self.get_wrapper()
agents = ["foo_agent", "bar_agent"] agents = ["foo_agent", "bar_agent"]
@ -356,7 +381,8 @@ class NeutronWrapperTestCase(test.TestCase):
{"ports": [{"id": port_id} for port_id in ports]}) {"ports": [{"id": port_id} for port_id in ports]})
service.client.delete_network.return_value = "foo_deleted" service.client.delete_network.return_value = "foo_deleted"
result = service.delete_network( result = service.delete_network(
{"id": "foo_id", "router_id": "foo_router", "subnets": subnets}) {"id": "foo_id", "router_id": "foo_router", "subnets": subnets,
"lb_pools": []})
self.assertEqual(result, "foo_deleted") self.assertEqual(result, "foo_deleted")
self.assertEqual( self.assertEqual(
service.client.remove_network_from_dhcp_agent.mock_calls, service.client.remove_network_from_dhcp_agent.mock_calls,
@ -460,18 +486,18 @@ class NeutronWrapperTestCase(test.TestCase):
{"port": {"network_id": "foo_net", {"port": {"network_id": "foo_net",
"name": "random_name", "foo": "bar"}}) "name": "random_name", "foo": "bar"}})
def test_supports_security_group(self): def test_supports_extension(self):
wrap = self.get_wrapper() wrap = self.get_wrapper()
wrap.client.list_extensions.return_value = ( wrap.client.list_extensions.return_value = (
{"extensions": [{"alias": "security-group"}]}) {"extensions": [{"alias": "extension"}]})
self.assertTrue(wrap.supports_security_group()[0]) self.assertTrue(wrap.supports_extension("extension")[0])
wrap.client.list_extensions.return_value = ( wrap.client.list_extensions.return_value = (
{"extensions": [{"alias": "dummy-group"}]}) {"extensions": [{"alias": "extension"}]})
self.assertFalse(wrap.supports_security_group()[0]) self.assertFalse(wrap.supports_extension("dummy-group")[0])
wrap.client.list_extensions.return_value = {} wrap.client.list_extensions.return_value = {}
self.assertFalse(wrap.supports_security_group()[0]) self.assertFalse(wrap.supports_extension("extension")[0])
class FunctionsTestCase(test.TestCase): class FunctionsTestCase(test.TestCase):