--- /dev/null
+# 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,
+ }
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
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({}))
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()