diff --git a/rally/plugins/openstack/context/existing_network.py b/rally/plugins/openstack/context/existing_network.py new file mode 100644 index 0000000000..f6e59ce41d --- /dev/null +++ b/rally/plugins/openstack/context/existing_network.py @@ -0,0 +1,53 @@ +# 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 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.configure(name="existing_network", order=349) +class ExistingNetwork(context.Context): + """This context supports using existing networks in Rally. + + This context should be used on a deployment with existing users. + """ + + CONFIG_SCHEMA = { + "type": "object", + "$schema": consts.JSON_SCHEMA, + "additionalProperties": False + } + + @utils.log_task_wrapper(LOG.info, _("Enter context: `existing_network`")) + def setup(self): + for user, tenant_id in utils.iterate_per_tenants( + self.context.get("users", [])): + net_wrapper = network_wrapper.wrap( + osclients.Clients(user["endpoint"]), + self.config) + self.context["tenants"][tenant_id]["networks"] = ( + net_wrapper.list_networks()) + + @utils.log_task_wrapper(LOG.info, _("Exit context: `existing_network`")) + def cleanup(self): + """Networks were not created by Rally, so nothing to do.""" diff --git a/rally/plugins/openstack/wrappers/network.py b/rally/plugins/openstack/wrappers/network.py index 4a49958557..f5cbd1c520 100644 --- a/rally/plugins/openstack/wrappers/network.py +++ b/rally/plugins/openstack/wrappers/network.py @@ -112,6 +112,21 @@ class NovaNetworkWrapper(NetworkWrapper): cidr = generate_cidr(start_cidr=self.start_cidr) return cidr + def _marshal_network_object(self, net_obj): + """Convert a Network object to a dict. + + This helps keep return values from the NovaNetworkWrapper + compatible with those from NeutronWrapper. + + :param net_obj: The Network object to convert to a dict + """ + return {"id": net_obj.id, + "cidr": net_obj.cidr, + "name": net_obj.label, + "status": "ACTIVE", + "external": False, + "tenant_id": net_obj.project_id} + def create_network(self, tenant_id, **kwargs): """Create network. @@ -123,18 +138,14 @@ class NovaNetworkWrapper(NetworkWrapper): 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} + return self._marshal_network_object(network) def delete_network(self, network): return self.client.networks.delete(network["id"]) def list_networks(self): - return self.client.networks.list() + return [self._marshal_network_object(n) + for n in self.client.networks.list()] def create_floating_ip(self, ext_network=None, **kwargs): """Allocate a floating ip from the given nova-network pool diff --git a/tests/unit/plugins/openstack/context/test_existing_network.py b/tests/unit/plugins/openstack/context/test_existing_network.py new file mode 100644 index 0000000000..4ad1fbebde --- /dev/null +++ b/tests/unit/plugins/openstack/context/test_existing_network.py @@ -0,0 +1,81 @@ +# 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.plugins.openstack.context import existing_network +from tests.unit import test + +CTX = "rally.plugins.openstack.context" + + +class ExistingNetworkTestCase(test.TestCase): + + def setUp(self): + super(ExistingNetworkTestCase, self).setUp() + + self.config = mock.MagicMock() + self.context = { + "task": mock.MagicMock(), + "users": [ + {"id": 1, + "tenant_id": "tenant1", + "endpoint": mock.Mock()}, + {"id": 2, + "tenant_id": "tenant2", + "endpoint": mock.Mock()}, + ], + "tenants": { + "tenant1": {}, + "tenant2": {}, + }, + "config": { + "existing_network": self.config + }, + } + + @mock.patch("rally.osclients.Clients") + @mock.patch("rally.plugins.openstack.wrappers.network.wrap") + def test_setup(self, mock_network_wrap, mock_clients): + networks = [mock.Mock(), mock.Mock(), mock.Mock()] + net_wrappers = { + "tenant1": mock.Mock( + **{"list_networks.return_value": networks[0:2]}), + "tenant2": mock.Mock( + **{"list_networks.return_value": networks[2:]}) + } + mock_network_wrap.side_effect = [net_wrappers["tenant1"], + net_wrappers["tenant2"]] + + existing_network.ExistingNetwork(self.context).setup() + + mock_clients.assert_has_calls([ + mock.call(u["endpoint"]) for u in self.context["users"]]) + mock_network_wrap.assert_has_calls([ + mock.call(mock_clients.return_value, self.config), + mock.call(mock_clients.return_value, self.config)]) + for net_wrapper in net_wrappers.values(): + net_wrapper.list_networks.assert_called_once_with() + + self.assertDictEqual( + self.context["tenants"], + { + "tenant1": {"networks": networks[0:2]}, + "tenant2": {"networks": networks[2:]}, + } + ) + + def test_cleanup(self): + # NOTE(stpierre): Test that cleanup is not abstract + existing_network.ExistingNetwork({"task": mock.MagicMock()}).cleanup() diff --git a/tests/unit/plugins/openstack/wrappers/test_network.py b/tests/unit/plugins/openstack/wrappers/test_network.py index deae8b3ec7..1dee08226b 100644 --- a/tests/unit/plugins/openstack/wrappers/test_network.py +++ b/tests/unit/plugins/openstack/wrappers/test_network.py @@ -30,6 +30,8 @@ class NovaNetworkWrapperTestCase(test.TestCase): class Net(object): def __init__(self, **kwargs): + if "tenant_id" in kwargs: + kwargs["project_id"] = kwargs.pop("tenant_id") self.__dict__.update(kwargs) def get_wrapper(self, *skip_cidrs, **kwargs): @@ -88,9 +90,18 @@ class NovaNetworkWrapperTestCase(test.TestCase): 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.return_value = [ + self.Net(id="foo_id", project_id="foo_tenant", cidr="foo_cidr", + label="foo_label"), + self.Net(id="bar_id", project_id="bar_tenant", cidr="bar_cidr", + label="bar_label")] + expected = [ + {"id": "foo_id", "cidr": "foo_cidr", "name": "foo_label", + "status": "ACTIVE", "external": False, "tenant_id": "foo_tenant"}, + {"id": "bar_id", "cidr": "bar_cidr", "name": "bar_label", + "status": "ACTIVE", "external": False, "tenant_id": "bar_tenant"}] + self.assertEqual(expected, service.list_networks()) service.client.networks.list.assert_called_once_with() def test__get_floating_ip(self):