]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Rework associations from vpc to quantum resources.
authorSteve Baker <sbaker@redhat.com>
Mon, 20 May 2013 22:01:16 +0000 (10:01 +1200)
committerSteve Baker <sbaker@redhat.com>
Tue, 2 Jul 2013 03:36:26 +0000 (15:36 +1200)
Metadata cannot be used to store the relationships between VPC
resources and the underlying quantum resources, since this prevents
VPC or Subnet refs from being passed in as parameters.

This rework results in no state being stored in metadata. Route table
relationships are inferred from the template. An assumption is made that
the quantum router associatiated with the VPC will have the same name
as the quantum net. The easiest way of ensuring that is to creat the VPC
in a different Heat stack.

Fixes bug: #1165050
Fixes bug: #1165056
Fixes bug: #1166779
Fixes bug: #1166787

Change-Id: I6fb087bfc63ed13703089a16ed57858e6911b7d8

heat/engine/resources/internet_gateway.py
heat/engine/resources/route_table.py
heat/engine/resources/subnet.py
heat/engine/resources/vpc.py
heat/tests/test_vpc.py

index b25756a81909b152e7b2fdddba71d1e4139dbd48..f265a40790b1400c746f9641054bf00b0289c077 100644 (file)
@@ -35,8 +35,13 @@ class InternetGateway(resource.Resource):
     }
 
     def handle_create(self):
-        client = self.quantum()
+        self.resource_id_set(self.physical_resource_name())
 
+    def handle_delete(self):
+        pass
+
+    @staticmethod
+    def get_external_network_id(client):
         ext_filter = {'router:external': True}
         ext_nets = client.list_networks(**ext_filter)['networks']
         if len(ext_nets) != 1:
@@ -45,15 +50,8 @@ class InternetGateway(resource.Resource):
             # the default one
             raise exception.Error(
                 'Expected 1 external network, found %d' % len(ext_nets))
-
         external_network_id = ext_nets[0]['id']
-        md = {
-            'external_network_id': external_network_id
-        }
-        self.metadata = md
-
-    def handle_delete(self):
-        pass
+        return external_network_id
 
 
 class VPCGatewayAttachment(resource.Resource):
@@ -68,24 +66,36 @@ class VPCGatewayAttachment(resource.Resource):
             'Implemented': False}
     }
 
+    def _vpc_route_tables(self):
+        for resource in self.stack.resources.itervalues():
+            if (resource.type() == 'AWS::EC2::RouteTable' and
+                resource.properties.get('VpcId') ==
+                    self.properties.get('VpcId')):
+                        yield resource
+
+    def add_dependencies(self, deps):
+        super(VPCGatewayAttachment, self).add_dependencies(deps)
+        # Depend on any route table in this template with the same
+        # VpcId as this VpcId.
+        # All route tables must exist before gateway attachment
+        # as attachment happens to routers (not VPCs)
+        for route_table in self._vpc_route_tables():
+            deps += (self, route_table)
+
     def handle_create(self):
         client = self.quantum()
-        gateway = self.stack[self.properties.get('InternetGatewayId')]
-        vpc = self.stack.resource_by_refid(self.properties.get('VpcId'))
-        external_network_id = gateway.metadata['external_network_id']
-
-        for router_id in vpc.metadata['all_router_ids']:
-            client.add_gateway_router(router_id, {
+        external_network_id = InternetGateway.get_external_network_id(client)
+        for router in self._vpc_route_tables():
+            client.add_gateway_router(router.resource_id, {
                 'network_id': external_network_id})
 
     def handle_delete(self):
         from quantumclient.common.exceptions import QuantumClientException
 
         client = self.quantum()
-        vpc = self.stack.resource_by_refid(self.properties.get('VpcId'))
-        for router_id in vpc.metadata['all_router_ids']:
+        for router in self._vpc_route_tables():
             try:
-                client.remove_gateway_router(router_id)
+                client.remove_gateway_router(router.resource_id)
             except QuantumClientException as ex:
                 if ex.status_code != 404:
                     raise ex
index b2aac1653951e5713a8581076b2e83f92034d00a..6311e3e88ce60bbd0265274fbab798c429340411 100644 (file)
@@ -16,6 +16,8 @@
 from heat.engine import clients
 from heat.openstack.common import log as logging
 from heat.engine import resource
+from heat.engine.resources.quantum import quantum
+from heat.engine.resources.vpc import VPC
 
 if clients.quantumclient is not None:
     from quantumclient.common.exceptions import QuantumClientException
@@ -43,17 +45,26 @@ class RouteTable(resource.Resource):
         client = self.quantum()
         props = {'name': self.physical_resource_name()}
         router = client.create_router({'router': props})['router']
-
-        # add this router to the list of all routers in the VPC
-        vpc = self.stack.resource_by_refid(self.properties.get('VpcId'))
-        vpc_md = vpc.metadata
-        vpc_md['all_router_ids'].append(router['id'])
-        vpc.metadata = vpc_md
-
-        # TODO(sbaker) all_router_ids has changed, any VPCGatewayAttachment
-        # for this vpc needs to be notified
         self.resource_id_set(router['id'])
 
+    def check_create_complete(self, *args):
+        client = self.quantum()
+        attributes = client.show_router(
+            self.resource_id)['router']
+        if not quantum.QuantumResource.is_built(attributes):
+            return False
+
+        network_id = self.properties.get('VpcId')
+        default_router = VPC.router_for_vpc(client, network_id)
+        if default_router and default_router.get('external_gateway_info'):
+            # the default router for the VPC is connected
+            # to the external router, so do it for this too.
+            external_network_id = default_router[
+                'external_gateway_info']['network_id']
+            client.add_gateway_router(self.resource_id, {
+                'network_id': external_network_id})
+        return True
+
     def handle_delete(self):
         client = self.quantum()
 
@@ -64,13 +75,12 @@ class RouteTable(resource.Resource):
             if ex.status_code != 404:
                 raise ex
 
-        # remove this router from the list of all routers in the VPC
-        vpc = self.stack.resource_by_refid(self.properties.get('VpcId'))
-        vpc_md = vpc.metadata
-        vpc_md['all_router_ids'].remove(router_id)
-        vpc.metadata = vpc_md
-        # TODO(sbaker) all_router_ids has changed, any VPCGatewayAttachment
-        # for this vpc needs to be notified
+        # just in case this router has been added to a gateway, remove it
+        try:
+            client.remove_gateway_router(router_id)
+        except QuantumClientException as ex:
+            if ex.status_code != 404:
+                raise ex
 
 
 class SubnetRouteTableAssocation(resource.Resource):
@@ -86,17 +96,17 @@ class SubnetRouteTableAssocation(resource.Resource):
 
     def handle_create(self):
         client = self.quantum()
-        subnet = self.stack.resource_by_refid(self.properties.get('SubnetId'))
         subnet_id = self.properties.get('SubnetId')
-        previous_router_id = subnet.metadata['router_id']
 
         router_id = self.properties.get('RouteTableId')
 
         #remove the default router association for this subnet.
         try:
-            client.remove_interface_router(
-                previous_router_id,
-                {'subnet_id': subnet_id})
+            previous_router = self._router_for_subnet(subnet_id)
+            if previous_router:
+                client.remove_interface_router(
+                    previous_router['id'],
+                    {'subnet_id': subnet_id})
         except QuantumClientException as ex:
             if ex.status_code != 404:
                 raise ex
@@ -104,11 +114,16 @@ class SubnetRouteTableAssocation(resource.Resource):
         client.add_interface_router(
             router_id, {'subnet_id': subnet_id})
 
+    def _router_for_subnet(self, subnet_id):
+        client = self.quantum()
+        subnet = client.show_subnet(
+            subnet_id)['subnet']
+        network_id = subnet['network_id']
+        return VPC.router_for_vpc(client, network_id)
+
     def handle_delete(self):
         client = self.quantum()
-        subnet = self.stack.resource_by_refid(self.properties.get('SubnetId'))
         subnet_id = self.properties.get('SubnetId')
-        default_router_id = subnet.metadata['default_router_id']
 
         router_id = self.properties.get('RouteTableId')
 
@@ -120,8 +135,14 @@ class SubnetRouteTableAssocation(resource.Resource):
                 raise ex
 
         # add back the default router
-        client.add_interface_router(
-            default_router_id, {'subnet_id': subnet_id})
+        try:
+            default_router = self._router_for_subnet(subnet_id)
+            if default_router:
+                client.add_interface_router(
+                    default_router['id'], {'subnet_id': subnet_id})
+        except QuantumClientException as ex:
+            if ex.status_code != 404:
+                raise ex
 
 
 def resource_mapping():
index eed95b61a979a122f7330442254e04f31bbd8d3e..d79fac98f376dbf1abceb63830b6945d33acd4bf 100644 (file)
@@ -17,6 +17,7 @@ from heat.engine import clients
 from heat.common import exception
 from heat.openstack.common import log as logging
 from heat.engine import resource
+from heat.engine.resources.vpc import VPC
 
 logger = logging.getLogger(__name__)
 
@@ -45,8 +46,6 @@ class Subnet(resource.Resource):
         client = self.quantum()
         # TODO(sbaker) Verify that this CidrBlock is within the vpc CidrBlock
         network_id = self.properties.get('VpcId')
-        vpc = self.stack.resource_by_refid(network_id)
-        router_id = vpc.metadata['router_id']
 
         props = {
             'network_id': network_id,
@@ -56,31 +55,26 @@ class Subnet(resource.Resource):
         }
         subnet = client.create_subnet({'subnet': props})['subnet']
 
-        #TODO(sbaker) check for a non-default router for this network
-        # and use that instead if it exists
-        client.add_interface_router(
-            router_id,
-            {'subnet_id': subnet['id']})
-        md = {
-            'router_id': router_id,
-            'default_router_id': router_id
-        }
-        self.metadata = md
+        router = VPC.router_for_vpc(self.quantum(), network_id)
+        if router:
+            client.add_interface_router(
+                router['id'],
+                {'subnet_id': subnet['id']})
         self.resource_id_set(subnet['id'])
 
     def handle_delete(self):
         from quantumclient.common.exceptions import QuantumClientException
 
         client = self.quantum()
-        router_id = self.metadata['router_id']
+        network_id = self.properties.get('VpcId')
         subnet_id = self.resource_id
 
-        #TODO(sbaker) check for a non-default router for this network
-        # and remove that instead if it exists
         try:
-            client.remove_interface_router(
-                router_id,
-                {'subnet_id': subnet_id})
+            router = VPC.router_for_vpc(self.quantum(), network_id)
+            if router:
+                client.remove_interface_router(
+                    router['id'],
+                    {'subnet_id': subnet_id})
         except QuantumClientException as ex:
             if ex.status_code != 404:
                 raise ex
index f9a42b9dd29aca2d0b4454f86d5ee0d54c672ddf..7d8f5e2fda9f62de6101bfb2e1aa85e52735b4cc 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from heat.common import exception
 from heat.engine import clients
 from heat.openstack.common import log as logging
 from heat.engine import resource
+from heat.engine.resources.quantum import quantum
 
 logger = logging.getLogger(__name__)
 
@@ -42,31 +44,53 @@ class VPC(resource.Resource):
 
     def handle_create(self):
         client = self.quantum()
-        props = {'name': self.physical_resource_name()}
-        # Creates a network with an implicit router
-        net = client.create_network({'network': props})['network']
-        router = client.create_router({'router': props})['router']
-        md = {
-            'router_id': router['id'],
-            'all_router_ids': [router['id']]
-        }
-        self.metadata = md
+        # The VPC's net and router are associated by having identical names.
+        net_props = {'name': self.physical_resource_name()}
+        router_props = {'name': self.physical_resource_name()}
+
+        net = client.create_network({'network': net_props})['network']
+        client.create_router({'router': router_props})['router']
+
         self.resource_id_set(net['id'])
 
+    @staticmethod
+    def network_for_vpc(client, network_id):
+        return client.show_network(network_id)['network']
+
+    @staticmethod
+    def router_for_vpc(client, network_id):
+        # first get the quantum net
+        net = VPC.network_for_vpc(client, network_id)
+        # then find a router with the same name
+        routers = client.list_routers(name=net['name'])['routers']
+        if len(routers) == 0:
+            # There may be no router if the net was created manually
+            # instead of in another stack.
+            return None
+        if len(routers) > 1:
+            raise exception.Error(
+                _('Multiple routers found with name %s') % net['name'])
+        return routers[0]
+
+    def check_create_complete(self, *args):
+        net = self.network_for_vpc(self.quantum(), self.resource_id)
+        if not quantum.QuantumResource.is_built(net):
+            return False
+        router = self.router_for_vpc(self.quantum(), self.resource_id)
+        return quantum.QuantumResource.is_built(router)
+
     def handle_delete(self):
         from quantumclient.common.exceptions import QuantumClientException
-
         client = self.quantum()
-        network_id = self.resource_id
-        router_id = self.metadata['router_id']
+        router = self.router_for_vpc(client, self.resource_id)
         try:
-            client.delete_router(router_id)
+            client.delete_router(router['id'])
         except QuantumClientException as ex:
             if ex.status_code != 404:
                 raise ex
 
         try:
-            client.delete_network(network_id)
+            client.delete_network(self.resource_id)
         except QuantumClientException as ex:
             if ex.status_code != 404:
                 raise ex
index 0591682f2a5cbfdb87df50e822a81eabef597c92..b9e2cac2d2036e58608f634efea8a49d07bcba31 100644 (file)
@@ -50,10 +50,12 @@ class VPCTestBase(HeatTestCase):
         self.m.StubOutWithMock(quantumclient.Client, 'delete_router')
         self.m.StubOutWithMock(quantumclient.Client, 'delete_subnet')
         self.m.StubOutWithMock(quantumclient.Client, 'list_networks')
+        self.m.StubOutWithMock(quantumclient.Client, 'list_routers')
         self.m.StubOutWithMock(quantumclient.Client, 'remove_gateway_router')
         self.m.StubOutWithMock(quantumclient.Client, 'remove_interface_router')
         self.m.StubOutWithMock(quantumclient.Client, 'show_subnet')
         self.m.StubOutWithMock(quantumclient.Client, 'show_network')
+        self.m.StubOutWithMock(quantumclient.Client, 'show_router')
         self.m.StubOutWithMock(quantumclient.Client, 'create_security_group')
         self.m.StubOutWithMock(quantumclient.Client, 'show_security_group')
         self.m.StubOutWithMock(quantumclient.Client, 'delete_security_group')
@@ -87,51 +89,87 @@ class VPCTestBase(HeatTestCase):
         return stack
 
     def mock_create_network(self):
-        vpc_name = utils.PhysName('test_stack', 'the_vpc')
+        self.vpc_name = utils.PhysName('test_stack', 'the_vpc')
         quantumclient.Client.create_network(
             {
-                'network': {'name': vpc_name}
+                'network': {'name': self.vpc_name}
             }).AndReturn({'network': {
-                'status': 'ACTIVE',
+                'status': 'BUILD',
                 'subnets': [],
-                'name': vpc_name,
+                'name': 'name',
                 'admin_state_up': True,
                 'shared': False,
                 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
                 'id': 'aaaa'
             }})
+        quantumclient.Client.show_network(
+            'aaaa'
+        ).AndReturn({"network": {
+            "status": "BUILD",
+            "subnets": [],
+            "name": self.vpc_name,
+            "admin_state_up": False,
+            "shared": False,
+            "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+            "id": "aaaa"
+        }})
+
+        quantumclient.Client.show_network(
+            'aaaa'
+        ).MultipleTimes().AndReturn({"network": {
+            "status": "ACTIVE",
+            "subnets": [],
+            "name": self.vpc_name,
+            "admin_state_up": False,
+            "shared": False,
+            "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+            "id": "aaaa"
+        }})
         quantumclient.Client.create_router(
-            {'router': {'name': vpc_name}}).AndReturn({'router': {
-                'status': 'ACTIVE',
-                'name': vpc_name,
-                'admin_state_up': True,
-                'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
-                'id': 'rrrr'
-            }})
+            {'router': {'name': self.vpc_name}}).AndReturn({
+                'router': {
+                    'status': 'BUILD',
+                    'name': self.vpc_name,
+                    'admin_state_up': True,
+                    'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
+                    'id': 'bbbb'
+                }})
+        quantumclient.Client.list_routers(name=self.vpc_name).AndReturn({
+            "routers": [{
+                "status": "BUILD",
+                "external_gateway_info": None,
+                "name": self.vpc_name,
+                "admin_state_up": True,
+                "tenant_id": "3e21026f2dc94372b105808c0e721661",
+                "routes": [],
+                "id": "bbbb"
+            }]
+        })
+        self.mock_router_for_vpc()
 
     def mock_create_subnet(self):
-        subnet_name = utils.PhysName('test_stack', 'the_subnet')
+        self.subnet_name = utils.PhysName('test_stack', 'the_subnet')
         quantumclient.Client.create_subnet(
             {'subnet': {
                 'network_id': u'aaaa',
                 'cidr': u'10.0.0.0/24',
                 'ip_version': 4,
-                'name': subnet_name}}).AndReturn({
+                'name': self.subnet_name}}).AndReturn({
                     'subnet': {
                         'status': 'ACTIVE',
-                        'name': subnet_name,
+                        'name': self.subnet_name,
                         'admin_state_up': True,
                         'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
                         'id': 'cccc'}})
+        self.mock_router_for_vpc()
         quantumclient.Client.add_interface_router(
-            u'rrrr',
+            u'bbbb',
             {'subnet_id': 'cccc'}).AndReturn(None)
 
     def mock_show_subnet(self):
-        subnet_name = utils.PhysName('test_stack', 'the_subnet')
         quantumclient.Client.show_subnet('cccc').AndReturn({
             'subnet': {
-                'name': subnet_name,
+                'name': self.subnet_name,
                 'network_id': 'aaaa',
                 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
                 'allocation_pools': [{'start': '10.0.0.2',
@@ -144,16 +182,16 @@ class VPCTestBase(HeatTestCase):
             }})
 
     def mock_create_security_group(self):
-        sg_name = utils.PhysName('test_stack', 'the_sg')
+        self.sg_name = utils.PhysName('test_stack', 'the_sg')
         quantumclient.Client.create_security_group({
             'security_group': {
-                'name': sg_name,
+                'name': self.sg_name,
                 'description': 'SSH access'
             }
         }).AndReturn({
             'security_group': {
                 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
-                'name': sg_name,
+                'name': self.sg_name,
                 'description': 'SSH access',
                 'security_group_rules': [],
                 'id': 'eeee'
@@ -205,21 +243,95 @@ class VPCTestBase(HeatTestCase):
         quantumclient.Client.delete_security_group_rule('bbbb').AndReturn(None)
         quantumclient.Client.delete_security_group('eeee').AndReturn(None)
 
+    def mock_router_for_vpc(self):
+        quantumclient.Client.list_routers(name=self.vpc_name).AndReturn({
+            "routers": [{
+                "status": "ACTIVE",
+                "external_gateway_info": {
+                    "network_id": "zzzz",
+                    "enable_snat": True},
+                "name": self.vpc_name,
+                "admin_state_up": True,
+                "tenant_id": "3e21026f2dc94372b105808c0e721661",
+                "routes": [],
+                "id": "bbbb"
+            }]
+        })
+
     def mock_delete_network(self):
-        quantumclient.Client.delete_router('rrrr').AndReturn(None)
+        self.mock_router_for_vpc()
+        quantumclient.Client.delete_router('bbbb').AndReturn(None)
         quantumclient.Client.delete_network('aaaa').AndReturn(None)
 
     def mock_delete_subnet(self):
+        self.mock_router_for_vpc()
         quantumclient.Client.remove_interface_router(
-            u'rrrr',
+            u'bbbb',
             {'subnet_id': 'cccc'}).AndReturn(None)
         quantumclient.Client.delete_subnet('cccc').AndReturn(None)
 
-    def assertResourceState(self, rsrc, ref_id, metadata={}):
-        self.assertEqual(None, rsrc.validate())
-        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
-        self.assertEqual(ref_id, rsrc.FnGetRefId())
-        self.assertEqual(metadata, dict(rsrc.metadata))
+    def mock_create_route_table(self):
+        self.rt_name = utils.PhysName('test_stack', 'the_route_table')
+        quantumclient.Client.create_router({
+            'router': {'name': self.rt_name}}).AndReturn({
+                'router': {
+                    'status': 'BUILD',
+                    'name': self.rt_name,
+                    'admin_state_up': True,
+                    'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
+                    'id': 'ffff'
+                }
+            })
+        quantumclient.Client.show_router('ffff').AndReturn({
+            'router': {
+                'status': 'BUILD',
+                'name': self.rt_name,
+                'admin_state_up': True,
+                'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
+                'id': 'ffff'
+            }
+        })
+        quantumclient.Client.show_router('ffff').AndReturn({
+            'router': {
+                'status': 'ACTIVE',
+                'name': self.rt_name,
+                'admin_state_up': True,
+                'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
+                'id': 'ffff'
+            }
+        })
+        self.mock_router_for_vpc()
+        quantumclient.Client.add_gateway_router(
+            'ffff', {'network_id': 'zzzz'}).AndReturn(None)
+
+    def mock_create_association(self):
+        self.mock_show_subnet()
+        self.mock_router_for_vpc()
+        quantumclient.Client.remove_interface_router(
+            'bbbb',
+            {'subnet_id': u'cccc'}).AndReturn(None)
+        quantumclient.Client.add_interface_router(
+            u'ffff',
+            {'subnet_id': 'cccc'}).AndReturn(None)
+
+    def mock_delete_association(self):
+        self.mock_show_subnet()
+        self.mock_router_for_vpc()
+        quantumclient.Client.remove_interface_router(
+            'ffff',
+            {'subnet_id': u'cccc'}).AndReturn(None)
+        quantumclient.Client.add_interface_router(
+            u'bbbb',
+            {'subnet_id': 'cccc'}).AndReturn(None)
+
+    def mock_delete_route_table(self):
+        quantumclient.Client.delete_router('ffff').AndReturn(None)
+        quantumclient.Client.remove_gateway_router('ffff').AndReturn(None)
+
+    def assertResourceState(self, resource, ref_id):
+        self.assertEqual(None, resource.validate())
+        self.assertEqual((resource.CREATE, resource.COMPLETE), resource.state)
+        self.assertEqual(ref_id, resource.FnGetRefId())
 
 
 class VPCTest(VPCTestBase):
@@ -232,23 +344,18 @@ Resources:
     Properties: {CidrBlock: '10.0.0.0/16'}
 '''
 
-    def stub_sleep(self):
-        pass
-
     def test_vpc(self):
         self.mock_create_network()
         self.mock_delete_network()
         self.m.ReplayAll()
 
         stack = self.create_stack(self.test_template)
-        rsrc = stack['the_vpc']
-        self.assertResourceState(rsrc, 'aaaa', {
-            'router_id': 'rrrr',
-            'all_router_ids': ['rrrr']})
+        vpc = stack['the_vpc']
+        self.assertResourceState(vpc, 'aaaa')
         self.assertRaises(resource.UpdateReplace,
-                          rsrc.handle_update, {}, {}, {})
+                          vpc.handle_update, {}, {}, {})
 
-        self.assertEqual(None, rsrc.delete())
+        self.assertEqual(None, vpc.delete())
         self.m.VerifyAll()
 
 
@@ -275,8 +382,9 @@ Resources:
         self.mock_delete_network()
 
         # mock delete subnet which is already deleted
+        self.mock_router_for_vpc()
         quantumclient.Client.remove_interface_router(
-            u'rrrr',
+            u'bbbb',
             {'subnet_id': 'cccc'}).AndRaise(
                 QuantumClientException(status_code=404))
         quantumclient.Client.delete_subnet('cccc').AndRaise(
@@ -285,23 +393,21 @@ Resources:
         self.m.ReplayAll()
         stack = self.create_stack(self.test_template)
 
-        rsrc = stack['the_subnet']
-        self.assertResourceState(rsrc, 'cccc', {
-            'router_id': 'rrrr',
-            'default_router_id': 'rrrr'})
+        subnet = stack['the_subnet']
+        self.assertResourceState(subnet, 'cccc')
 
         self.assertRaises(resource.UpdateReplace,
-                          rsrc.handle_update, {}, {}, {})
+                          subnet.handle_update, {}, {}, {})
         self.assertRaises(
             exception.InvalidTemplateAttribute,
-            rsrc.FnGetAtt,
+            subnet.FnGetAtt,
             'Foo')
 
-        self.assertEqual('moon', rsrc.FnGetAtt('AvailabilityZone'))
+        self.assertEqual('moon', subnet.FnGetAtt('AvailabilityZone'))
 
-        self.assertEqual(None, rsrc.delete())
-        rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
-        self.assertEqual(None, rsrc.delete())
+        self.assertEqual(None, subnet.delete())
+        subnet.state_set(subnet.CREATE, subnet.COMPLETE, 'to delete again')
+        self.assertEqual(None, subnet.delete())
         self.assertEqual(None, stack['the_vpc'].delete())
         self.m.VerifyAll()
 
@@ -411,13 +517,13 @@ Resources:
 '''
 
     def mock_create_network_interface(self, security_groups=['eeee']):
-        nic_name = utils.PhysName('test_stack', 'the_nic')
+        self.nic_name = utils.PhysName('test_stack', 'the_nic')
         port = {'network_id': 'aaaa',
                 'fixed_ips': [{
                     'subnet_id': u'cccc',
                     'ip_address': u'10.0.0.100'
                 }],
-                'name': nic_name,
+                'name': self.nic_name,
                 'admin_state_up': True}
         if security_groups:
                 port['security_groups'] = security_groups
@@ -435,7 +541,7 @@ Resources:
                 ],
                 'id': 'dddd',
                 'mac_address': 'fa:16:3e:25:32:5d',
-                'name': nic_name,
+                'name': self.nic_name,
                 'network_id': 'aaaa',
                 'status': 'ACTIVE',
                 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f'
@@ -530,7 +636,6 @@ Resources:
     Type: AWS::EC2::InternetGateway
   the_vpc:
     Type: AWS::EC2::VPC
-    DependsOn : the_gateway
     Properties:
       CidrBlock: '10.0.0.0/16'
   the_subnet:
@@ -541,10 +646,18 @@ Resources:
       AvailabilityZone: moon
   the_attachment:
     Type: AWS::EC2::VPCGatewayAttachment
-    DependsOn : the_subnet
     Properties:
       VpcId: {Ref: the_vpc}
       InternetGatewayId: {Ref: the_gateway}
+  the_route_table:
+    Type: AWS::EC2::RouteTable
+    Properties:
+      VpcId: {Ref: the_vpc}
+  the_association:
+    Type: AWS::EC2::SubnetRouteTableAssocation
+    Properties:
+      RouteTableId: {Ref: the_route_table}
+      SubnetId: {Ref: the_subnet}
 '''
 
     def mock_create_internet_gateway(self):
@@ -562,17 +675,21 @@ Resources:
 
     def mock_create_gateway_attachment(self):
         quantumclient.Client.add_gateway_router(
-            'rrrr', {'network_id': 'eeee'}).AndReturn(None)
+            'ffff', {'network_id': 'eeee'}).AndReturn(None)
 
     def mock_delete_gateway_attachment(self):
-        quantumclient.Client.remove_gateway_router('rrrr').AndReturn(None)
+        quantumclient.Client.remove_gateway_router('ffff').AndReturn(None)
 
     def test_internet_gateway(self):
         self.mock_create_internet_gateway()
         self.mock_create_network()
         self.mock_create_subnet()
+        self.mock_create_route_table()
+        self.mock_create_association()
         self.mock_create_gateway_attachment()
         self.mock_delete_gateway_attachment()
+        self.mock_delete_association()
+        self.mock_delete_route_table()
         self.mock_delete_subnet()
         self.mock_delete_network()
 
@@ -581,8 +698,7 @@ Resources:
         stack = self.create_stack(self.test_template)
 
         gateway = stack['the_gateway']
-        self.assertResourceState(gateway, 'the_gateway', {
-            'external_network_id': 'eeee'})
+        self.assertResourceState(gateway, gateway.physical_resource_name())
         self.assertRaises(resource.UpdateReplace, gateway.handle_update,
                           {}, {}, {})
 
@@ -591,6 +707,9 @@ Resources:
         self.assertRaises(resource.UpdateReplace,
                           attachment.handle_update, {}, {}, {})
 
+        route_table = stack['the_route_table']
+        self.assertEqual([route_table], list(attachment._vpc_route_tables()))
+
         stack.delete()
         self.m.VerifyAll()
 
@@ -621,38 +740,6 @@ Resources:
       SubnetId: {Ref: the_subnet}
 '''
 
-    def mock_create_route_table(self):
-        rt_name = utils.PhysName('test_stack', 'the_route_table')
-        quantumclient.Client.create_router(
-            {'router': {'name': rt_name}}).AndReturn({
-                'router': {
-                    'status': 'ACTIVE',
-                    'name': 'name',
-                    'admin_state_up': True,
-                    'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
-                    'id': 'ffff'
-                }
-            })
-
-    def mock_create_association(self):
-        quantumclient.Client.remove_interface_router(
-            'rrrr',
-            {'subnet_id': u'cccc'}).AndReturn(None)
-        quantumclient.Client.add_interface_router(
-            u'ffff',
-            {'subnet_id': 'cccc'}).AndReturn(None)
-
-    def mock_delete_association(self):
-        quantumclient.Client.remove_interface_router(
-            'ffff',
-            {'subnet_id': u'cccc'}).AndReturn(None)
-        quantumclient.Client.add_interface_router(
-            u'rrrr',
-            {'subnet_id': 'cccc'}).AndReturn(None)
-
-    def mock_delete_route_table(self):
-        quantumclient.Client.delete_router('ffff').AndReturn(None)
-
     def test_route_table(self):
         self.mock_create_network()
         self.mock_create_subnet()
@@ -667,17 +754,14 @@ Resources:
 
         stack = self.create_stack(self.test_template)
 
-        vpc = stack['the_vpc']
-        self.assertEqual(['rrrr', 'ffff'], vpc.metadata['all_router_ids'])
-
         route_table = stack['the_route_table']
-        self.assertResourceState(route_table, 'ffff', {})
+        self.assertResourceState(route_table, 'ffff')
         self.assertRaises(
             resource.UpdateReplace,
             route_table.handle_update, {}, {}, {})
 
         association = stack['the_association']
-        self.assertResourceState(association, 'the_association', {})
+        self.assertResourceState(association, 'the_association')
         self.assertRaises(
             resource.UpdateReplace,
             association.handle_update, {}, {}, {})
@@ -685,8 +769,5 @@ Resources:
         association.delete()
         route_table.delete()
 
-        vpc = stack['the_vpc']
-        self.assertEqual(['rrrr'], vpc.metadata['all_router_ids'])
-
         stack.delete()
         self.m.VerifyAll()