]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Implement RouteTable and subnet association
authorSteve Baker <sbaker@redhat.com>
Sun, 17 Feb 2013 20:12:12 +0000 (09:12 +1300)
committerSteve Baker <sbaker@redhat.com>
Mon, 18 Feb 2013 00:11:49 +0000 (13:11 +1300)
Implements blueprint resource-type-routetable
and blueprint resource-type-srta

Change-Id: Idb6e8d060563d22847d100220e9a1750340583a8

heat/engine/resources/route_table.py [new file with mode: 0644]
heat/engine/resources/subnet.py
heat/tests/test_vpc.py

diff --git a/heat/engine/resources/route_table.py b/heat/engine/resources/route_table.py
new file mode 100644 (file)
index 0000000..dd50227
--- /dev/null
@@ -0,0 +1,149 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+#    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 quantumclient.common.exceptions import QuantumClientException
+
+from heat.openstack.common import log as logging
+from heat.engine import resource
+
+logger = logging.getLogger(__name__)
+
+
+class RouteTable(resource.Resource):
+    tags_schema = {'Key': {'Type': 'String',
+                           'Required': True},
+                   'Value': {'Type': 'String',
+                             'Required': True}}
+
+    properties_schema = {
+        'VpcId': {
+            'Type': 'String',
+            'Required': True},
+        'Tags': {'Type': 'List', 'Schema': {
+            'Type': 'Map',
+            'Implemented': False,
+            'Schema': tags_schema}}
+    }
+
+    def __init__(self, name, json_snippet, stack):
+        super(RouteTable, self).__init__(name, json_snippet, stack)
+
+    def handle_create(self):
+        client = self.quantum()
+        props = {'name': self.physical_resource_name()}
+        router = client.create_router({'router': props})['router']
+
+        router_id = router['id']
+
+        # add this router to the list of all routers in the VPC
+        vpc = self.stack[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
+        md = {
+            'router_id': router_id
+        }
+        self.metadata = md
+
+    def handle_delete(self):
+        client = self.quantum()
+
+        router_id = self.metadata['router_id']
+        try:
+            client.delete_router(router_id)
+        except QuantumClientException as ex:
+            if ex.status_code != 404:
+                raise ex
+
+        # remove this router from the list of all routers in the VPC
+        vpc = self.stack[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
+
+    def handle_update(self, json_snippet):
+        return self.UPDATE_REPLACE
+
+
+class SubnetRouteTableAssocation(resource.Resource):
+
+    properties_schema = {
+        'RouteTableId': {
+            'Type': 'String',
+            'Required': True},
+        'SubnetId': {
+            'Type': 'String',
+            'Required': True}
+    }
+
+    def __init__(self, name, json_snippet, stack):
+        super(SubnetRouteTableAssocation, self).__init__(
+            name, json_snippet, stack)
+
+    def handle_create(self):
+        client = self.quantum()
+        subnet = self.stack[self.properties.get('SubnetId')]
+        subnet_id = subnet.metadata['subnet_id']
+        previous_router_id = subnet.metadata['router_id']
+
+        route_table = self.stack[self.properties.get('RouteTableId')]
+        router_id = route_table.metadata['router_id']
+
+        #remove the default router association for this subnet.
+        try:
+            client.remove_interface_router(
+                previous_router_id,
+                {'subnet_id': subnet_id})
+        except QuantumClientException as ex:
+            if ex.status_code != 404:
+                raise ex
+
+        client.add_interface_router(
+            router_id, {'subnet_id': subnet_id})
+
+    def handle_delete(self):
+        client = self.quantum()
+        subnet = self.stack[self.properties.get('SubnetId')]
+        subnet_id = subnet.metadata['subnet_id']
+        default_router_id = subnet.metadata['default_router_id']
+
+        route_table = self.stack[self.properties.get('RouteTableId')]
+        router_id = route_table.metadata['router_id']
+
+        try:
+            client.remove_interface_router(router_id, {
+                'subnet_id': subnet_id})
+        except QuantumClientException as ex:
+            if ex.status_code != 404:
+                raise ex
+
+        # add back the default router
+        client.add_interface_router(
+            default_router_id, {'subnet_id': subnet_id})
+
+    def handle_update(self, json_snippet):
+        return self.UPDATE_REPLACE
+
+
+def resource_mapping():
+    return {
+        'AWS::EC2::RouteTable': RouteTable,
+        'AWS::EC2::SubnetRouteTableAssocation': SubnetRouteTableAssocation,
+    }
index b2d7d7151b46efa63ece0f53bc7131acd6402305..5e988c30e4b394247c7b8174985208185b66a40e 100644 (file)
@@ -68,6 +68,7 @@ class Subnet(resource.Resource):
         md = {
             'network_id': network_id,
             'router_id': router_id,
+            'default_router_id': router_id,
             'subnet_id': subnet['id']
         }
         self.metadata = md
index ca37a0fcbf6588d9671792d16e5f5808d4ca1ded..c42f9cf3eef132fe8396288b88c055e94844514c 100644 (file)
@@ -22,10 +22,7 @@ from heat.common import context
 from heat.common import exception
 from heat.common import template_format
 from heat.engine import parser
-from heat.engine.resources import network_interface
-from heat.engine.resources import subnet
-from heat.engine.resources import vpc
-from heat.engine.resources import internet_gateway
+import heat.engine.resources
 
 from quantumclient.common.exceptions import QuantumClientException
 from quantumclient.v2_0 import client as quantumclient
@@ -197,6 +194,7 @@ Resources:
         self.assertResourceState(resource, 'the_subnet', {
             'network_id': 'aaaa',
             'router_id': 'bbbb',
+            'default_router_id': 'bbbb',
             'subnet_id': 'cccc'})
 
         self.assertEqual(resource.UPDATE_REPLACE, resource.handle_update({}))
@@ -362,3 +360,102 @@ Resources:
 
         stack.delete()
         self.m.VerifyAll()
+
+
+@attr(tag=['unit', 'resource'])
+@attr(speed='fast')
+class RouteTableTest(VPCTestBase):
+
+    test_template = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Resources:
+  the_vpc:
+    Type: AWS::EC2::VPC
+    Properties:
+      CidrBlock: '10.0.0.0/16'
+  the_subnet:
+    Type: AWS::EC2::Subnet
+    Properties:
+      CidrBlock: 10.0.0.0/24
+      VpcId: {Ref: the_vpc}
+      AvailabilityZone: moon
+  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_route_table(self):
+        quantumclient.Client.create_router(
+            {'router': {'name': u'test_stack.the_route_table'}}).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(
+            'bbbb',
+            {'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'bbbb',
+            {'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()
+        self.mock_create_route_table()
+        self.mock_create_association()
+        self.mock_delete_association()
+        self.mock_delete_route_table()
+        self.mock_delete_subnet()
+        self.mock_delete_network()
+
+        self.m.ReplayAll()
+
+        stack = self.create_stack(self.test_template)
+
+        vpc = stack['the_vpc']
+        self.assertEqual(['bbbb', 'ffff'], vpc.metadata['all_router_ids'])
+
+        route_table = stack['the_route_table']
+        self.assertResourceState(route_table, 'the_route_table', {
+            'router_id': 'ffff'})
+        self.assertEqual(
+            route_table.UPDATE_REPLACE,
+            route_table.handle_update({}))
+
+        association = stack['the_association']
+        self.assertResourceState(association, 'the_association', {})
+        self.assertEqual(
+            association.UPDATE_REPLACE,
+            association.handle_update({}))
+
+        association.delete()
+        route_table.delete()
+
+        vpc = stack['the_vpc']
+        self.assertEqual(['bbbb'], vpc.metadata['all_router_ids'])
+
+        stack.delete()
+        self.m.VerifyAll()