Using OpenStack Heat to Deploy an IPv6-enabled Instance

In this post I will talk about how to use a basic OpenStack Heat template to build a dual-stack (IPv4 and IPv6) Neutron network, router and launch an instance that will use StateLess Address AutoConfiguration (SLAAC) for IPv6 address assignment.

In the May, 2015 post I discussed, in detail, how to build a dual-stack tenant and use a variety of IPv6 address assignment methods (SLAAC, Stateless DHCPv6, Stateful DHCPv6) for OpenStack instances.

To build on the previous post, I wanted to show a basic Heat template for building an IPv4 and IPv6 network with the basic parameters such as CIDR, gateway and pools.  I want Heat to also launch an OpenStack instance (what Heat calls a Server) that attaches to those networks.  Finally, the template will create a new security group that will create security group rules for both IPv4 and IPv6.

The Heat template that I am referring to in this post can be found here: That specific template is using SLAAC.  You can also take a look at this template which uses Stateless DHCPv6: You can modify the template from there to play around with DHCPv6 Stateful. Hint, it’s all in the resource properties of:

ipv6_address_mode: <slaac/dhcpv6-stateless/dhcpv6-stateful>
ipv6_ra_mode: <slaac/dhcpv6-stateless/dhcpv6-stateful>)

Heat Template

I am not going to teach you Heat. There are countless resources out there that do a much better job than I ever could on teaching Heat.  A couple of places to start are:

The Heat Orchestration Template (HOT) Guide is a great resource for finding the various parameters, resources and properties that can be used in Heat.

The primary place to dig into IPv6 capabilities within Heat is in the Heat template guide under OS::Neutron::Subnet.  You can jump to it here:  I am not going to walk through all of what is in the guide but I will point out specific properties that I have used in the example Heat template I referenced before.

Let’s take a look at the IPv6-specific parts of the example template.  In the example template file I have created a parameter section that includes various items such as key, image, flavor and so on.  The IPv6 section includes:

  • The private IPv6 network (2001:db8:cafe:1e::/64)
  • The private IPv6 gateway (2001:db8:cafe:1e::1)
  • The beginning and ending range of the IPv6 address allocation pool (2001:db8:cafe:1e::2 – 2001:db8:cafe:1e:ffff:ffff:ffff:fffe)
    type: string
    description: Private IPv6 subnet address
    default: 2001:db8:cafe:1e::/64
    type: string
    description: Private IPv6 network gateway address
    default: 2001:db8:cafe:1e::1
    type: string
    description: Start of private network IPv6 address allocation pool
    default: 2001:db8:cafe:1e::2
    type: string
    description: End of private network IPv6 address allocation pool
    default: 2001:db8:cafe:1e:ffff:ffff:ffff:fffe

The next section to look at is in the “resources” section and this is where things go into action. The “private_v6_subnet” has various resource types and properties to include:

  • Version is IPv6
  • IPv6 address and RA modes are SLAAC
  • The network property (set in the parameter section)
  • The CIDR property which is the “private_net_v6” from the parameter section
  • The gateway IPv6 address is defined in the “private_net_v6_gateway” parameter
  • The allocation pool is defined in the “private_net_v6_pool_start/end” parameters
    type: OS::Neutron::Subnet
      ip_version: 6
      ipv6_address_mode: slaac
      ipv6_ra_mode: slaac
      network: { get_resource: private_net }
      cidr: { get_param: private_net_v6 }
      gateway_ip: { get_param: private_net_v6_gateway }
        - start: { get_param: private_net_v6_pool_start }
          end: { get_param: private_net_v6_pool_end }

The next IPv6-relevant area of the resource section is “router_interface_v6”. In the “router_interface_v6” resource, there is a reference to the previously created “router” resource (see template file for full resource list) and the “private_v6_subnet”. This entry is simply attaching a new router interface to the Private IPv6 subnet.

    type: OS::Neutron::RouterInterface
      router: { get_resource: router }
      subnet: { get_resource: private_v6_subnet }

Next, there is the Server (AKA “instance” or “VM”) creation section. There is nothing IPv6 specific here. On the network property line, Heat is pointing to “get_resource: private_net” which is the private network that both IPv4 and IPv6 subnets are associated with. That line, basically, attaches the server to a dual-stack network.

    type: OS::Nova::Server
      name: Server1
      image: { get_param: image }
      flavor: { get_param: flavor }
      key_name: { get_param: key_name }
        - network: { get_resource: private_net }
      config_drive: "true"
      user_data_format: RAW
      user_data: |
      security_groups: [{ get_resource: server_security_group }]

Finally, there is the security group section which enables rules for both IPv4 and IPv6. In this example ports 22, 80 and ICMP are open for IPv4 and IPv6.

    type: OS::Neutron::SecurityGroup
      description: Heat-deployed security group.
      name: heat-security-group
      rules: [
        protocol: tcp,
        port_range_min: 22,
        port_range_max: 22},
        protocol: icmp},
        protocol: tcp,
        port_range_min: 80,
        port_range_max: 80},
        {remote_ip_prefix: "::/0",
        ethertype: IPv6,
        protocol: tcp,
        port_range_min: 22,
        port_range_max: 22},
        {remote_ip_prefix: "::/0",
        ethertype: IPv6,
        protocol: icmp},
        {remote_ip_prefix: "::/0",
        ethertype: IPv6,
        protocol: tcp,
        port_range_min: 80,
        port_range_max: 80}]

Now, let’s deploy this template and see how it all looks. I am deploying the Heat “stack” using the Heat “stack-create” command (alternatively you can deploy it using the ‘Orchestration > Stacks > Launch Stack’ interface in the OpenStack Dashboard). In this example I am running “stack-create” using the “-r” argument to indicate ‘rollback’ (in the event something goes wrong, I don’t want the whole stack to build out). Then I am using the “-f” argument to indicate that I am using a file to build the Heat stack. The stack is named “demo-v6”:

root@c71-kilo-aio:~$ heat stack-create -r -f Heat-Templates/single-v6-test.yaml demo-v6
| id                                   | stack_name | stack_status       | creation_time        |
| 688388f5-4ae1-4d39-bf85-6f9a591a4420 | demo-v6    | CREATE_IN_PROGRESS | 2015-06-29T15:44:18Z |

After a few minutes, the Heat stack is built:

root@c71-kilo-aio:~$ heat stack-list
| id                                   | stack_name | stack_status    | creation_time        |
| 688388f5-4ae1-4d39-bf85-6f9a591a4420 | demo-v6    | CREATE_COMPLETE | 2015-06-29T15:44:18Z |

Here is a messy view of the obligatory OpenStack Dashboard Network Topology view (Note: Some Horizon guru needs to line break the IPv4 and IPv6 addresses for the instances so they are readable ;-)):



Here’s a cleaner view of things:

  • Network list – You can see the new Heat-built “test_net” with the two subnets (IPv4/IPv6) as well as the previously built (by the admin) “Public-Network”:
root@c71-kilo-aio:~$ neutron net-list
| id                                   | name           | subnets                                                    |
| 2e03a628-e85e-4519-b1bb-a579880be0ae | test_net       | 93764f36-c56b-4c65-b7d7-cb78a694353b         |
|                                      |                | cafb610a-2aaa-4640-b0f0-8bb4b60cbaf2 2001:db8:cafe:1e::/64 |
| f6a55029-d875-48a8-aab9-1a5a5399592b | Public-Network | dda7d8f1-89a6-40bb-b11b-64a62c103828       |
|                                      |                | f2107125-c98e-4375-a81f-d0f4d34bdae3 2001:db8:cafe:51::/64 |
  • Subnet list:
root@c71-kilo-aio:~$ neutron subnet-list
| id                                   | name                                   | cidr                  | allocation_pools                                                                |
| 93764f36-c56b-4c65-b7d7-cb78a694353b | demo-v6-private_subnet-6evpyylqyux7    |         | {"start": "", "end": ""}                                  |
| dda7d8f1-89a6-40bb-b11b-64a62c103828 | Public-Subnet-v4                       |       | {"start": "", "end": ""}                              |
| f2107125-c98e-4375-a81f-d0f4d34bdae3 | Public-Subnet-v6                       | 2001:db8:cafe:51::/64 | {"start": "2001:db8:cafe:51::3", "end": "2001:db8:cafe:51:ffff:ffff:ffff:fffe"} |
| cafb610a-2aaa-4640-b0f0-8bb4b60cbaf2 | demo-v6-private_v6_subnet-vvsmlbkc6sds | 2001:db8:cafe:1e::/64 | {"start": "2001:db8:cafe:1e::2", "end": "2001:db8:cafe:1e:ffff:ffff:ffff:fffe"} |
  • Here is the “subnet-show” of the Heat-built subnet for the Private IPv6 subnet. The allocation pool range, gateway, IPv6 version, IPv6 address mode and IPv6 RA modes are all defined as we wanted (based on the Heat template):
root@c71-kilo-aio:~$ neutron subnet-show demo-v6-private_v6_subnet-vvsmlbkc6sds
| Field             | Value                                                                           |
| allocation_pools  | {"start": "2001:db8:cafe:1e::2", "end": "2001:db8:cafe:1e:ffff:ffff:ffff:fffe"} |
| cidr              | 2001:db8:cafe:1e::/64                                                           |
| dns_nameservers   |                                                                                 |
| enable_dhcp       | True                                                                            |
| gateway_ip        | 2001:db8:cafe:1e::1                                                             |
| host_routes       |                                                                                 |
| id                | cafb610a-2aaa-4640-b0f0-8bb4b60cbaf2                                            |
| ip_version        | 6                                                                               |
| ipv6_address_mode | slaac                                                                           |
| ipv6_ra_mode      | slaac                                                                           |
| name              | demo-v6-private_v6_subnet-vvsmlbkc6sds                                          |
| network_id        | 2e03a628-e85e-4519-b1bb-a579880be0ae                                            |
| subnetpool_id     |                                                                                 |
| tenant_id         | dc52b50429f74aeabb3935eb3e2bcb04                                                |
  • Router port list – You can see that the router has IPv4/IPv6 addresses on the tenant and public network interfaces:
root@c71-kilo-aio:~$ neutron router-port-list demo-v6-router-txy5s5bcixqd | grep ip_address | sed -e 's#.*ip_address": "\([^"]\+\).*#\1#'
  • Security Group list:
root@c71-kilo-aio:~$ neutron security-group-list
| id                          | name                | security_group_rules                              |
| 69f81e8e-5059-4a...         | heat-security-group | egress, IPv4                                      |                                                       |                             |                     | egress, IPv6                                      |
|                             |                     | ingress, IPv4, 22/tcp, remote_ip_prefix:|                                                       |                             |                     | ingress, IPv4, 80/tcp, remote_ip_prefix:|
|                             |                     | ingress, IPv4, icmp, remote_ip_prefix:  |
|                             |                     | ingress, IPv6, 22/tcp, remote_ip_prefix: ::/0     |
|                             |                     | ingress, IPv6, 80/tcp, remote_ip_prefix: ::/0     |
|                             |                     | ingress, IPv6, icmp, remote_ip_prefix: ::/0       |
  • Server/Instance list:
root@c71-kilo-aio:~$ nova list
| ID                                   | Name    | Status | Task State | Power State | Networks                                                  |
| d7bfc606-f9da-4be5-b3e8-2219882c3da6 | Server1 | ACTIVE | -          | Running     | test_net=, 2001:db8:cafe:1e:f816:3eff:fea8:7d2c |

Finally, inside the instance, you can see that both IPv4 and IPv6 addresses are assigned:

root@c71-kilo-aio:~$ ip netns exec qrouter-d2ff159a-b603-4a3b-b5f7-481bff40613e ssh fedora@2001:db8:cafe:1e:f816:3eff:fea8:7d2c
The authenticity of host '2001:db8:cafe:1e:f816:3eff:fea8:7d2c (2001:db8:cafe:1e:f816:3eff:fea8:7d2c)' can't be established.
ECDSA key fingerprint is 41:e2:ea:28:e5:6d:ae:50:24:81:ad:5e:db:d7:a0:21.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '2001:db8:cafe:1e:f816:3eff:fea8:7d2c' (ECDSA) to the list of known hosts.
[fedora@server1 ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc pfifo_fast state UP group default qlen 1000
    link/ether fa:16:3e:a8:7d:2c brd ff:ff:ff:ff:ff:ff
    inet brd scope global dynamic eth0
       valid_lft 79179sec preferred_lft 79179sec
    inet6 2001:db8:cafe:1e:f816:3eff:fea8:7d2c/64 scope global mngtmpaddr dynamic
       valid_lft 86400sec preferred_lft 14400sec
    inet6 fe80::f816:3eff:fea8:7d2c/64 scope link
       valid_lft forever preferred_lft forever

I hope this gives you a starting point for adding IPv6 to your Heat template collection.