Custom Search

Thursday, January 8, 2015

OpenStack Heat VPC Resources (Virtual Private Cloud)

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):

1 comment: