From 96ddba4a710ad0878d54a25015128a1ce0d78b13 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 23 Feb 2016 14:53:23 -0800 Subject: [PATCH] Provide dry-run flag to validate deployment requirements Nova may want to validate that the setup requirements needed by the auto_allocate feature are met before issuing the actual request to provision resources. This patch adds this validation API. Partial-implements: blueprint get-me-a-network Change-Id: I9fb26e752650569fc5692ff8afda82a077647f7a --- neutron/services/auto_allocate/db.py | 20 +++++++++++ .../unit/services/auto_allocate/test_db.py | 36 +++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/neutron/services/auto_allocate/db.py b/neutron/services/auto_allocate/db.py index 8e8e7678e1e..da78d21729a 100644 --- a/neutron/services/auto_allocate/db.py +++ b/neutron/services/auto_allocate/db.py @@ -39,6 +39,7 @@ from neutron.services.auto_allocate import models LOG = logging.getLogger(__name__) IS_DEFAULT = 'is_default' +CHECK_REQUIREMENTS = 'dry-run' def _extend_external_network_default(self, net_res, net_db): @@ -91,6 +92,15 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): The topology will be provisioned upon return, if network is missing. """ + if CHECK_REQUIREMENTS in fields: + # for dry-run requests, simply validates that subsequent + # requests can be fullfilled based on a set of requirements + # such as existence of default networks, pools, etc. + return self._check_requirements(context) + elif fields: + raise n_exc.BadRequest(resource='auto_allocate', + msg=_("Unrecognized field")) + tenant_id = self._validate(context, tenant_id) # Check for an existent topology network_id = self._get_auto_allocated_network(context, tenant_id) @@ -123,6 +133,16 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): constants.L3_ROUTER_NAT) return self._l3_plugin + def _check_requirements(self, context): + """Raise if requirements are not met.""" + self._get_default_external_network(context) + try: + self._get_supported_subnetpools(context) + except n_exc.NotFound: + raise exceptions.AutoAllocationFailure( + reason=_("No default subnetpools defined")) + return {'id': 'dry-run=pass'} + def _validate(self, context, tenant_id): """Validate and return the tenant to be associated to the topology.""" if tenant_id == 'None': diff --git a/neutron/tests/unit/services/auto_allocate/test_db.py b/neutron/tests/unit/services/auto_allocate/test_db.py index 10e663038f5..179b297edf4 100644 --- a/neutron/tests/unit/services/auto_allocate/test_db.py +++ b/neutron/tests/unit/services/auto_allocate/test_db.py @@ -17,10 +17,10 @@ from neutron.common import exceptions as n_exc from neutron import context from neutron.services.auto_allocate import db from neutron.services.auto_allocate import exceptions -from neutron.tests import base +from neutron.tests.unit import testlib_api -class AutoAllocateTestCase(base.BaseTestCase): +class AutoAllocateTestCase(testlib_api.SqlTestCaseLight): def setUp(self): super(AutoAllocateTestCase, self).setUp() @@ -46,3 +46,35 @@ class AutoAllocateTestCase(base.BaseTestCase): mock_cleanup.assert_called_once_with( self.ctx, network_id='network_foo', router_id='router_foo', subnets=[]) + + def test_get_auto_allocated_topology_dry_run_happy_path_for_kevin(self): + with mock.patch.object(self.mixin, '_check_requirements') as f: + self.mixin.get_auto_allocated_topology( + self.ctx, mock.ANY, fields=['dry-run']) + self.assertEqual(1, f.call_count) + + def test_get_auto_allocated_topology_dry_run_bad_input(self): + self.assertRaises(n_exc.BadRequest, + self.mixin.get_auto_allocated_topology, + self.ctx, mock.ANY, fields=['foo']) + + def test__check_requirements_fail_on_missing_ext_net(self): + self.assertRaises(exceptions.AutoAllocationFailure, + self.mixin._check_requirements, self.ctx) + + def test__check_requirements_fail_on_missing_pools(self): + with mock.patch.object( + self.mixin, '_get_default_external_network'),\ + mock.patch.object( + self.mixin, '_get_supported_subnetpools') as g: + g.side_effect = n_exc.NotFound() + self.assertRaises(exceptions.AutoAllocationFailure, + self.mixin._check_requirements, self.ctx) + + def test__check_requirements_happy_path_for_kevin(self): + with mock.patch.object( + self.mixin, '_get_default_external_network'),\ + mock.patch.object( + self.mixin, '_get_supported_subnetpools'): + result = self.mixin._check_requirements(self.ctx) + self.assertEqual(list(result.values())[0], 'dry-run=pass')