From 5c158edac6950899c41d3090cde8c636e63c4a2f Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Fri, 19 Sep 2014 12:18:12 +1200 Subject: [PATCH] Associate floating IP with router interface This change will create a dependency from a FloatingIP to a RouterInterface in this template which interfaces with the same subnet that this FloatingIP's port is assigned to. It would be preferable to add the dependency based on matching FloatingIP floating_network_id and Router external_gateway_info network, but there is a valid use-case for the Router being external to the template, so the dependency is matched on the internal subnet instead, which is available from the RouterInterface property. Change-Id: Iedff00b382b4fcda741ca5c9b4adc23b176ec48c Closes-Bug: #1299259 --- heat/engine/resources/neutron/floatingip.py | 31 +++++++++++++++++++-- heat/tests/test_neutron.py | 17 +++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/heat/engine/resources/neutron/floatingip.py b/heat/engine/resources/neutron/floatingip.py index f3a22f1c4..a374103b6 100644 --- a/heat/engine/resources/neutron/floatingip.py +++ b/heat/engine/resources/neutron/floatingip.py @@ -14,6 +14,7 @@ from heat.engine import attributes from heat.engine import properties from heat.engine.resources.neutron import neutron +from heat.engine.resources.neutron import port from heat.engine.resources.neutron import router from heat.engine import support @@ -93,9 +94,10 @@ class FloatingIP(neutron.NeutronResource): def add_dependencies(self, deps): super(FloatingIP, self).add_dependencies(deps) - # depend on any RouterGateway in this template with the same - # network_id as this floating_network_id + for resource in self.stack.itervalues(): + # depend on any RouterGateway in this template with the same + # network_id as this floating_network_id if resource.has_interface('OS::Neutron::RouterGateway'): gateway_network = resource.properties.get( router.RouterGateway.NETWORK) or resource.properties.get( @@ -106,6 +108,31 @@ class FloatingIP(neutron.NeutronResource): if gateway_network == floating_network: deps += (self, resource) + # depend on any RouterInterface in this template which interfaces + # with the same subnet that this floating IP's port is assigned + # to + elif resource.has_interface('OS::Neutron::RouterInterface'): + + def port_on_subnet(resource, subnet): + if not resource.has_interface('OS::Neutron::Port'): + return False + for fixed_ip in resource.properties.get( + port.Port.FIXED_IPS): + + port_subnet = ( + fixed_ip.properties.get(port.Port.FIXED_IP_SUBNET) + or fixed_ip.get(port.Port.FIXED_IP_SUBNET_ID)) + return subnet == port_subnet + return False + + interface_subnet = ( + resource.properties.get(router.RouterInterface.SUBNET) or + resource.properties.get(router.RouterInterface.SUBNET_ID)) + for d in deps.required_by(self): + if port_on_subnet(d, interface_subnet): + deps += (self, resource) + break + def validate(self): super(FloatingIP, self).validate() self._validate_depr_property_required( diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py index 53235c69a..194aa6c96 100644 --- a/heat/tests/test_neutron.py +++ b/heat/tests/test_neutron.py @@ -269,6 +269,13 @@ neutron_floating_template_deprecated = ''' "router": { "Type": "OS::Neutron::Router" }, + "router_interface": { + "Type": "OS::Neutron::RouterInterface", + "Properties": { + "router_id": { "Ref" : "router" }, + "subnet": "sub1234" + } + }, "gateway": { "Type": "OS::Neutron::RouterGateway", "Properties": { @@ -312,6 +319,13 @@ neutron_floating_template = ''' "router": { "Type": "OS::Neutron::Router" }, + "router_interface": { + "Type": "OS::Neutron::RouterInterface", + "Properties": { + "router_id": { "Ref" : "router" }, + "subnet": "sub1234" + } + }, "gateway": { "Type": "OS::Neutron::RouterGateway", "Properties": { @@ -1927,6 +1941,9 @@ class NeutronFloatingIPTest(HeatTestCase): self.assertIn(stack['floating_ip'], deps) + deps = stack.dependencies[stack['router_interface']] + self.assertIn(stack['floating_ip'], deps) + fip = stack['floating_ip'] scheduler.TaskRunner(fip.create)() self.assertEqual((fip.CREATE, fip.COMPLETE), fip.state)