--- /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.common import exception
+from heat.openstack.common import log as logging
+from heat.engine import resource
+
+logger = logging.getLogger(__name__)
+
+
+class Subnet(resource.Resource):
+ tags_schema = {'Key': {'Type': 'String',
+ 'Required': True},
+ 'Value': {'Type': 'String',
+ 'Required': True}}
+
+ properties_schema = {
+ 'AvailabilityZone': {'Type': 'String'},
+ 'CidrBlock': {
+ 'Type': 'String',
+ 'Required': True},
+ 'VpcId': {
+ 'Type': 'String',
+ 'Required': True},
+ 'Tags': {'Type': 'List', 'Schema': {
+ 'Type': 'Map',
+ 'Implemented': False,
+ 'Schema': tags_schema}}
+ }
+
+ def __init__(self, name, json_snippet, stack):
+ super(Subnet, self).__init__(name, json_snippet, stack)
+
+ def handle_create(self):
+ client = self.quantum()
+ # TODO sbaker Verify that this CidrBlock is within the vpc CidrBlock
+ (network_id, router_id) = self.properties.get('VpcId').split(':')
+ props = {
+ 'network_id': network_id,
+ 'cidr': self.properties.get('CidrBlock'),
+ 'name': self.name
+ }
+ subnet = self.quantum().create_subnet({'subnet': props})['subnet']
+
+ client.add_interface_router(
+ router_id,
+ {'subnet_id': subnet['id']})
+ self.resource_id_set(subnet['id'])
+
+ def handle_delete(self):
+ client = self.quantum()
+ (network_id, router_id) = self.properties.get('VpcId').split(':')
+ try:
+ client.remove_interface_router(
+ router_id,
+ {'subnet_id': self.resource_id})
+ except QuantumClientException as ex:
+ if ex.status_code != 404:
+ raise ex
+
+ try:
+ client.delete_subnet(self.resource_id)
+ except QuantumClientException as ex:
+ if ex.status_code != 404:
+ raise ex
+
+ def handle_update(self, json_snippet):
+ return self.UPDATE_REPLACE
+
+ def FnGetAtt(self, key):
+ if key == 'AvailabilityZone':
+ return self.properties.get(key, '')
+ raise exception.InvalidTemplateAttribute(resource=self.name, key=key)
+
+
+def resource_mapping():
+ return {
+ 'AWS::EC2::Subnet': Subnet,
+ }
from nose.plugins.attrib import attr
from heat.common import context
+from heat.common import exception
from heat.common import template_format
from heat.engine.resources import vpc
+from heat.engine.resources import subnet
from heat.engine import parser
+from quantumclient.common.exceptions import QuantumClientException
+from quantumclient.v2_0 import client as quantumclient
+
test_template_vpc = '''
Resources:
the_vpc:
Type: AWS::EC2::VPC
- Properties: {CidrBlock: '10.0.0.0/24'}
+ Properties: {CidrBlock: '10.0.0.0/16'}
'''
-
-class FakeQuantum():
-
- def create_network(self, name):
- return {"network": {
- "status": "ACTIVE",
- "subnets": [],
- "name": "name",
- "admin_state_up": True,
- "shared": False,
- "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
- "id": "aaaa"
- }}
-
- def create_router(self, name):
- return {"router": {
- "status": "ACTIVE",
- "name": "name",
- "admin_state_up": True,
- "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
- "id": "bbbb"
- }}
-
- def delete_network(self, id):
- pass
-
- def delete_router(self, id):
- pass
+test_template_subnet = '''
+Resources:
+ the_vpc2:
+ 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_vpc2}
+ AvailabilityZone: moon
+'''
@attr(tag=['unit', 'resource'])
@attr(speed='fast')
-class QuantumTest(unittest.TestCase):
+class VPCTest(unittest.TestCase):
def setUp(self):
self.m = mox.Mox()
- self.m.StubOutWithMock(vpc.VPC, 'quantum')
+ self.m.StubOutWithMock(quantumclient.Client, 'create_network')
+ self.m.StubOutWithMock(quantumclient.Client, 'create_router')
+ self.m.StubOutWithMock(quantumclient.Client, 'delete_network')
+ self.m.StubOutWithMock(quantumclient.Client, 'delete_router')
def tearDown(self):
self.m.UnsetStubs()
- print "QuantumTest teardown complete"
+ print "VPCTest teardown complete"
def parse_stack(self, t):
ctx = context.RequestContext.from_dict({
'auth_url': 'http://localhost:5000/v2.0'})
stack_name = 'test_stack'
tmpl = parser.Template(t)
- params = parser.Parameters(stack_name, tmpl,
- {'external_network': 'abcd1234'})
+ params = parser.Parameters(stack_name, tmpl, {})
stack = parser.Stack(ctx, stack_name, tmpl, params)
return stack
def create_vpc(self, t, stack, resource_name):
- resource = vpc.VPC('the_vpc', t['Resources'][resource_name], stack)
+ resource = vpc.VPC(
+ resource_name,
+ t['Resources'][resource_name],
+ stack)
self.assertEqual(None, resource.create())
self.assertEqual(vpc.VPC.CREATE_COMPLETE, resource.state)
return resource
def test_vpc(self):
- fq = FakeQuantum()
- vpc.VPC.quantum().MultipleTimes().AndReturn(fq)
-
+ quantumclient.Client.create_network(
+ {'network': {'name': 'the_vpc'}}).AndReturn({"network": {
+ "status": "ACTIVE",
+ "subnets": [],
+ "name": "name",
+ "admin_state_up": True,
+ "shared": False,
+ "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+ "id": "aaaa"
+ }})
+ quantumclient.Client.create_router(
+ {'router': {'name': 'the_vpc'}}).AndReturn({"router": {
+ "status": "ACTIVE",
+ "name": "name",
+ "admin_state_up": True,
+ "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+ "id": "bbbb"
+ }})
+ quantumclient.Client.delete_router('bbbb').AndReturn(None)
+ quantumclient.Client.delete_network('aaaa').AndReturn(None)
self.m.ReplayAll()
t = template_format.parse(test_template_vpc)
stack = self.parse_stack(t)
self.assertEqual(None, resource.delete())
self.m.VerifyAll()
+
+
+@attr(tag=['unit', 'resource'])
+@attr(speed='fast')
+class SubnetTest(unittest.TestCase):
+ def setUp(self):
+ self.m = mox.Mox()
+ self.m.StubOutWithMock(quantumclient.Client, 'create_network')
+ self.m.StubOutWithMock(quantumclient.Client, 'create_router')
+ self.m.StubOutWithMock(quantumclient.Client, 'create_subnet')
+ self.m.StubOutWithMock(quantumclient.Client, 'add_interface_router')
+ self.m.StubOutWithMock(quantumclient.Client, 'remove_interface_router')
+ self.m.StubOutWithMock(quantumclient.Client, 'delete_network')
+ self.m.StubOutWithMock(quantumclient.Client, 'delete_router')
+ self.m.StubOutWithMock(quantumclient.Client, 'delete_subnet')
+
+ def tearDown(self):
+ self.m.UnsetStubs()
+ print "SubnetTest teardown complete"
+
+ def parse_stack(self, t):
+ ctx = context.RequestContext.from_dict({
+ 'tenant': 'test_tenant',
+ 'username': 'test_username',
+ 'password': 'password',
+ 'auth_url': 'http://localhost:5000/v2.0'})
+ params = parser.Parameters('test_stack', t, {})
+ stack = parser.Stack(
+ ctx,
+ 'test_stack',
+ parser.Template(t),
+ parameters=params)
+ stack.store()
+ return stack
+
+ def create_vpc(self, t, stack, resource_name):
+ resource = vpc.VPC(
+ resource_name,
+ t['Resources'][resource_name],
+ stack)
+ self.assertEqual(None, resource.create())
+ self.assertEqual(vpc.VPC.CREATE_COMPLETE, resource.state)
+ return resource
+
+ def create_subnet(self, t, stack, resource_name):
+ resource = subnet.Subnet(
+ resource_name,
+ t['Resources'][resource_name],
+ stack)
+ self.assertEqual(None, resource.create())
+ self.assertEqual(subnet.Subnet.CREATE_COMPLETE, resource.state)
+ return resource
+
+ def test_subnet(self):
+ quantumclient.Client.create_network(
+ {'network': {'name': 'the_vpc2'}}).AndReturn({"network": {
+ "status": "ACTIVE",
+ "subnets": [],
+ "name": "the_vpc2",
+ "admin_state_up": True,
+ "shared": False,
+ "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+ "id": "aaaa"
+ }})
+ quantumclient.Client.create_router(
+ {'router': {'name': 'the_vpc2'}}).AndReturn({"router": {
+ "status": "ACTIVE",
+ "name": "the_vpc2",
+ "admin_state_up": True,
+ "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+ "id": "bbbb"
+ }})
+ quantumclient.Client.create_subnet(
+ {'subnet': {
+ 'network_id': u'aaaa',
+ 'cidr': u'10.0.0.0/24',
+ 'name': u'the_subnet'}}).AndReturn({
+ "subnet": {
+ "status": "ACTIVE",
+ "name": "the_subnet",
+ "admin_state_up": True,
+ "tenant_id": "c1210485b2424d48804aad5d39c61b8f",
+ "id": "cccc"}})
+ quantumclient.Client.add_interface_router(
+ u'bbbb',
+ {'subnet_id': 'cccc'}).AndReturn(None)
+ quantumclient.Client.remove_interface_router(
+ u'bbbb',
+ {'subnet_id': 'cccc'}).AndReturn(None)
+ quantumclient.Client.delete_subnet('cccc').AndReturn(None)
+ quantumclient.Client.delete_router('bbbb').AndReturn(None)
+ quantumclient.Client.delete_network('aaaa').AndReturn(None)
+
+ quantumclient.Client.remove_interface_router(
+ u'bbbb',
+ {'subnet_id': 'cccc'}).AndRaise(
+ QuantumClientException(status_code=404))
+ quantumclient.Client.delete_subnet('cccc').AndRaise(
+ QuantumClientException(status_code=404))
+
+ self.m.ReplayAll()
+ t = template_format.parse(test_template_subnet)
+ stack = self.parse_stack(t)
+ stack.create()
+ resource = stack['the_subnet']
+
+ resource.validate()
+
+ ref_id = resource.FnGetRefId()
+ self.assertEqual('cccc', ref_id)
+
+ self.assertEqual(vpc.VPC.UPDATE_REPLACE, resource.handle_update({}))
+ self.assertRaises(
+ exception.InvalidTemplateAttribute,
+ resource.FnGetAtt,
+ 'Foo')
+
+ self.assertEqual('moon', resource.FnGetAtt('AvailabilityZone'))
+
+ self.assertEqual(None, resource.delete())
+ resource.state_set(resource.CREATE_COMPLETE, 'to delete again')
+ self.assertEqual(None, resource.delete())
+ self.assertEqual(None, stack['the_vpc2'].delete())
+ self.m.VerifyAll()