0)
a) CreateVPC == Create Virtual Network
b) CreateSubnet == Create Subnet in Virtual Network(VPC)
c) CreateInternetGateway == Get external network defined in the Project
d) AttachInternetGateway == Connect external network to routers in the Virtual Network(VPC)
e) CreateRouteTable == Create a router and attach to Virtual Network(VPC)
f) AssociateRouteTable == Attach subnet to router
g) CreateEIP == Attach floating ip to instance
1)
heat/heat/engine/resource.py
class Resource(object):
@scheduler.wrappertask
def create(self):
'''
Create the resource. Subclasses should provide a handle_create() method
to customise creation.
'''
@scheduler.wrappertask
def update(self, after, before=None, prev_resource=None):
'''
update the resource. Subclasses should provide a handle_update() method
to customise update, the base-class handle_update will fail by default.
'''
def resource_id_set(self, inst):
self.resource_id = inst
def action_handler_task(self, action, args=[], action_prefix=None):
'''
A task to call the Resource subclass's handler methods for an action.
Calls the handle_() method for the given action and then calls
the check__complete() method with the result in a loop until it
returns True. If the methods are not provided, the call is omitted.
Any args provided are passed to the handler.
If a prefix is supplied, the handler method handle__()
is called instead.
'''
def physical_resource_name(self):
name = '%s-%s-%s' % (self.stack.name,
self.name,
short_id.get_id(self.id))
return name
def neutron(self):
return self.client('neutron')
2)
heat/heat/engine/resources/vpc.py
class VPC(resource.Resource):
PROPERTIES = (
CIDR_BLOCK, INSTANCE_TENANCY, TAGS,
) = (
'CidrBlock', 'InstanceTenancy', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
# 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']
self.resource_id_set(net['id'])
client.create_router({'router': router_props})['router']
def check_create_complete(self, *args):
....
def handle_delete(self):
....
def resource_mapping(): return {
'AWS::EC2::VPC': VPC,
}
3)
heat/heat/engine/resources/subnet.py
class Subnet(resource.Resource):
PROPERTIES = (
AVAILABILITY_ZONE, CIDR_BLOCK, VPC_ID, TAGS,
) = (
'AvailabilityZone', 'CidrBlock', 'VpcId', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
# TODO(sbaker) Verify that this CidrBlock is within the vpc CidrBlock
network_id = self.properties.get(self.VPC_ID)
props = {
'network_id': network_id,
'cidr': self.properties.get(self.CIDR_BLOCK),
'name': self.physical_resource_name(),
'ip_version': 4
}
subnet = client.create_subnet({'subnet': props})['subnet']
self.resource_id_set(subnet['id'])
router = vpc.VPC.router_for_vpc(self.neutron(), network_id)
if router:
client.add_interface_router(
router['id'],
{'subnet_id': subnet['id']})
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::Subnet': Subnet,
}
4)
heat/heat/engine/resources/route_table.py
class RouteTable(resource.Resource):
PROPERTIES = (
VPC_ID, TAGS,
) = (
'VpcId', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.client()
props = {'name': self.physical_resource_name()}
router = client.create_router({'router': props})['router']
self.resource_id_set(router['id'])
def check_create_complete(self, *args):
client.add_gateway_router(self.resource_id, {
'network_id': external_network_id})
return True
def handle_delete(self):
....
class SubnetRouteTableAssociation(resource.Resource):
PROPERTIES = (
ROUTE_TABLE_ID, SUBNET_ID,
) = (
'RouteTableId', 'SubnetId',
)
properties_schema = { .... }
def handle_create(self):
client = self.client()
subnet_id = self.properties.get(self.SUBNET_ID)
router_id = self.properties.get(self.ROUTE_TABLE_ID)
#remove the default router association for this subnet.
try:
previous_router = self._router_for_subnet(subnet_id)
if previous_router:
client.remove_interface_router(
previous_router['id'],
{'subnet_id': subnet_id})
except Exception as ex:
self.client_plugin().ignore_not_found(ex)
client.add_interface_router(
router_id, {'subnet_id': subnet_id})
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::RouteTable': RouteTable,
'AWS::EC2::SubnetRouteTableAssociation': SubnetRouteTableAssociation,
}
5)
heat/heat/engine/resources/internet_gateway.py
class InternetGateway(resource.Resource):
PROPERTIES = (
TAGS,
) = (
'Tags',
)
properties_schema = { .... }
def handle_create(self):
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:
# TODO(sbaker) if there is more than one external network
# add a heat configuration variable to set the ID of
# the default one
raise exception.Error(
_('Expected 1 external network, found %d') % len(ext_nets))
external_network_id = ext_nets[0]['id']
return external_network_id
class VPCGatewayAttachment(resource.Resource):
PROPERTIES = (
VPC_ID, INTERNET_GATEWAY_ID, VPN_GATEWAY_ID,
) = (
'VpcId', 'InternetGatewayId', 'VpnGatewayId',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
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):
....
def resource_mapping():
return {
'AWS::EC2::InternetGateway': InternetGateway,
'AWS::EC2::VPCGatewayAttachment': VPCGatewayAttachment,
}
6)
heat/heat/engine/resources/eip.py
class ElasticIp(resource.Resource):
PROPERTIES = (
DOMAIN, INSTANCE_ID,
) = (
'Domain', 'InstanceId',
)
properties_schema = { .... }
def handle_create(self):
"""Allocate a floating IP for the current tenant."""
ips = None
if self.properties[self.DOMAIN]:
from heat.engine.resources import internet_gateway
ext_net = internet_gateway.InternetGateway.get_external_network_id(
self.neutron())
props = {'floating_network_id': ext_net}
ips = self.neutron().create_floatingip({
'floatingip': props})['floatingip']
self.ipaddress = ips['floating_ip_address']
self.resource_id_set(ips['id'])
LOG.info(_LI('ElasticIp create %s'), str(ips))
else:
try:
ips = self.nova().floating_ips.create()
except Exception as e:
with excutils.save_and_reraise_exception():
if self.client_plugin('nova').is_not_found(e):
LOG.error(_LE("No default floating IP pool configured."
" Set 'default_floating_pool' in "
"nova.conf."))
if ips:
self.ipaddress = ips.ip
self.resource_id_set(ips.id)
LOG.info(_LI('ElasticIp create %s'), str(ips))
instance_id = self.properties[self.INSTANCE_ID]
if instance_id:
server = self.nova().servers.get(instance_id)
server.add_floating_ip(self._ipaddress())
def handle_delete(self):
....
class ElasticIpAssociation(resource.Resource):
PROPERTIES = (
INSTANCE_ID, EIP, ALLOCATION_ID, NETWORK_INTERFACE_ID,
) = (
'InstanceId', 'EIP', 'AllocationId', 'NetworkInterfaceId',
)
properties_schema = { .... }
def handle_create(self):
"""Add a floating IP address to a server."""
if self.properties[self.EIP]:
server = self.nova().servers.get(self.properties[self.INSTANCE_ID])
server.add_floating_ip(self.properties[self.EIP])
self.resource_id_set(self.properties[self.EIP])
LOG.debug('ElasticIpAssociation '
'%(instance)s.add_floating_ip(%(eip)s)',
{'instance': self.properties[self.INSTANCE_ID],
'eip': self.properties[self.EIP]})
elif self.properties[self.ALLOCATION_ID]:
ni_id = self.properties[self.NETWORK_INTERFACE_ID]
instance_id = self.properties[self.INSTANCE_ID]
port_id, port_rsrc = self._get_port_info(ni_id, instance_id)
if not port_id or not port_rsrc:
LOG.warn(_LW('Skipping association, resource not specified'))
return
float_id = self.properties[self.ALLOCATION_ID]
network_id = port_rsrc['network_id']
self._neutron_add_gateway_router(float_id, network_id)
self._neutron_update_floating_ip(float_id, port_id)
self.resource_id_set(float_id)
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::EIP': ElasticIp,
'AWS::EC2::EIPAssociation': ElasticIpAssociation,
}
7)
heat/heat/tests/test_vpc.py
class VPCTestBase(common.HeatTestCase):
class VPCTest(VPCTestBase):
class SubnetTest(VPCTestBase):
class NetworkInterfaceTest(VPCTestBase):
class InternetGatewayTest(VPCTestBase):
class RouteTableTest(VPCTestBase):
8)
heat/heat/tests/test_eip.py
class EIPTest(common.HeatTestCase):
class AllocTest(common.HeatTestCase):
a) CreateVPC == Create Virtual Network
b) CreateSubnet == Create Subnet in Virtual Network(VPC)
c) CreateInternetGateway == Get external network defined in the Project
d) AttachInternetGateway == Connect external network to routers in the Virtual Network(VPC)
e) CreateRouteTable == Create a router and attach to Virtual Network(VPC)
f) AssociateRouteTable == Attach subnet to router
g) CreateEIP == Attach floating ip to instance
1)
heat/heat/engine/resource.py
class Resource(object):
@scheduler.wrappertask
def create(self):
'''
Create the resource. Subclasses should provide a handle_create() method
to customise creation.
'''
@scheduler.wrappertask
def update(self, after, before=None, prev_resource=None):
'''
update the resource. Subclasses should provide a handle_update() method
to customise update, the base-class handle_update will fail by default.
'''
def resource_id_set(self, inst):
self.resource_id = inst
def action_handler_task(self, action, args=[], action_prefix=None):
'''
A task to call the Resource subclass's handler methods for an action.
Calls the handle_
the check_
returns True. If the methods are not provided, the call is omitted.
Any args provided are passed to the handler.
If a prefix is supplied, the handler method handle_
is called instead.
'''
def physical_resource_name(self):
name = '%s-%s-%s' % (self.stack.name,
self.name,
short_id.get_id(self.id))
return name
def neutron(self):
return self.client('neutron')
2)
heat/heat/engine/resources/vpc.py
class VPC(resource.Resource):
PROPERTIES = (
CIDR_BLOCK, INSTANCE_TENANCY, TAGS,
) = (
'CidrBlock', 'InstanceTenancy', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
# 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']
self.resource_id_set(net['id'])
client.create_router({'router': router_props})['router']
def check_create_complete(self, *args):
....
def handle_delete(self):
....
def resource_mapping(): return {
'AWS::EC2::VPC': VPC,
}
3)
heat/heat/engine/resources/subnet.py
class Subnet(resource.Resource):
PROPERTIES = (
AVAILABILITY_ZONE, CIDR_BLOCK, VPC_ID, TAGS,
) = (
'AvailabilityZone', 'CidrBlock', 'VpcId', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
# TODO(sbaker) Verify that this CidrBlock is within the vpc CidrBlock
network_id = self.properties.get(self.VPC_ID)
props = {
'network_id': network_id,
'cidr': self.properties.get(self.CIDR_BLOCK),
'name': self.physical_resource_name(),
'ip_version': 4
}
subnet = client.create_subnet({'subnet': props})['subnet']
self.resource_id_set(subnet['id'])
router = vpc.VPC.router_for_vpc(self.neutron(), network_id)
if router:
client.add_interface_router(
router['id'],
{'subnet_id': subnet['id']})
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::Subnet': Subnet,
}
4)
heat/heat/engine/resources/route_table.py
class RouteTable(resource.Resource):
PROPERTIES = (
VPC_ID, TAGS,
) = (
'VpcId', 'Tags',
)
properties_schema = { .... }
def handle_create(self):
client = self.client()
props = {'name': self.physical_resource_name()}
router = client.create_router({'router': props})['router']
self.resource_id_set(router['id'])
def check_create_complete(self, *args):
client.add_gateway_router(self.resource_id, {
'network_id': external_network_id})
return True
def handle_delete(self):
....
class SubnetRouteTableAssociation(resource.Resource):
PROPERTIES = (
ROUTE_TABLE_ID, SUBNET_ID,
) = (
'RouteTableId', 'SubnetId',
)
properties_schema = { .... }
def handle_create(self):
client = self.client()
subnet_id = self.properties.get(self.SUBNET_ID)
router_id = self.properties.get(self.ROUTE_TABLE_ID)
#remove the default router association for this subnet.
try:
previous_router = self._router_for_subnet(subnet_id)
if previous_router:
client.remove_interface_router(
previous_router['id'],
{'subnet_id': subnet_id})
except Exception as ex:
self.client_plugin().ignore_not_found(ex)
client.add_interface_router(
router_id, {'subnet_id': subnet_id})
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::RouteTable': RouteTable,
'AWS::EC2::SubnetRouteTableAssociation': SubnetRouteTableAssociation,
}
5)
heat/heat/engine/resources/internet_gateway.py
class InternetGateway(resource.Resource):
PROPERTIES = (
TAGS,
) = (
'Tags',
)
properties_schema = { .... }
def handle_create(self):
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:
# TODO(sbaker) if there is more than one external network
# add a heat configuration variable to set the ID of
# the default one
raise exception.Error(
_('Expected 1 external network, found %d') % len(ext_nets))
external_network_id = ext_nets[0]['id']
return external_network_id
class VPCGatewayAttachment(resource.Resource):
PROPERTIES = (
VPC_ID, INTERNET_GATEWAY_ID, VPN_GATEWAY_ID,
) = (
'VpcId', 'InternetGatewayId', 'VpnGatewayId',
)
properties_schema = { .... }
def handle_create(self):
client = self.neutron()
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):
....
def resource_mapping():
return {
'AWS::EC2::InternetGateway': InternetGateway,
'AWS::EC2::VPCGatewayAttachment': VPCGatewayAttachment,
}
6)
heat/heat/engine/resources/eip.py
class ElasticIp(resource.Resource):
PROPERTIES = (
DOMAIN, INSTANCE_ID,
) = (
'Domain', 'InstanceId',
)
properties_schema = { .... }
def handle_create(self):
"""Allocate a floating IP for the current tenant."""
ips = None
if self.properties[self.DOMAIN]:
from heat.engine.resources import internet_gateway
ext_net = internet_gateway.InternetGateway.get_external_network_id(
self.neutron())
props = {'floating_network_id': ext_net}
ips = self.neutron().create_floatingip({
'floatingip': props})['floatingip']
self.ipaddress = ips['floating_ip_address']
self.resource_id_set(ips['id'])
LOG.info(_LI('ElasticIp create %s'), str(ips))
else:
try:
ips = self.nova().floating_ips.create()
except Exception as e:
with excutils.save_and_reraise_exception():
if self.client_plugin('nova').is_not_found(e):
LOG.error(_LE("No default floating IP pool configured."
" Set 'default_floating_pool' in "
"nova.conf."))
if ips:
self.ipaddress = ips.ip
self.resource_id_set(ips.id)
LOG.info(_LI('ElasticIp create %s'), str(ips))
instance_id = self.properties[self.INSTANCE_ID]
if instance_id:
server = self.nova().servers.get(instance_id)
server.add_floating_ip(self._ipaddress())
def handle_delete(self):
....
class ElasticIpAssociation(resource.Resource):
PROPERTIES = (
INSTANCE_ID, EIP, ALLOCATION_ID, NETWORK_INTERFACE_ID,
) = (
'InstanceId', 'EIP', 'AllocationId', 'NetworkInterfaceId',
)
properties_schema = { .... }
def handle_create(self):
"""Add a floating IP address to a server."""
if self.properties[self.EIP]:
server = self.nova().servers.get(self.properties[self.INSTANCE_ID])
server.add_floating_ip(self.properties[self.EIP])
self.resource_id_set(self.properties[self.EIP])
LOG.debug('ElasticIpAssociation '
'%(instance)s.add_floating_ip(%(eip)s)',
{'instance': self.properties[self.INSTANCE_ID],
'eip': self.properties[self.EIP]})
elif self.properties[self.ALLOCATION_ID]:
ni_id = self.properties[self.NETWORK_INTERFACE_ID]
instance_id = self.properties[self.INSTANCE_ID]
port_id, port_rsrc = self._get_port_info(ni_id, instance_id)
if not port_id or not port_rsrc:
LOG.warn(_LW('Skipping association, resource not specified'))
return
float_id = self.properties[self.ALLOCATION_ID]
network_id = port_rsrc['network_id']
self._neutron_add_gateway_router(float_id, network_id)
self._neutron_update_floating_ip(float_id, port_id)
self.resource_id_set(float_id)
def handle_delete(self):
....
def resource_mapping():
return {
'AWS::EC2::EIP': ElasticIp,
'AWS::EC2::EIPAssociation': ElasticIpAssociation,
}
7)
heat/heat/tests/test_vpc.py
class VPCTestBase(common.HeatTestCase):
class VPCTest(VPCTestBase):
class SubnetTest(VPCTestBase):
class NetworkInterfaceTest(VPCTestBase):
class InternetGatewayTest(VPCTestBase):
class RouteTableTest(VPCTestBase):
8)
heat/heat/tests/test_eip.py
class EIPTest(common.HeatTestCase):
class AllocTest(common.HeatTestCase):
What version of Python was this in?
ReplyDelete