From bccb6989845893299a5e487c4f15b0c8066fddb0 Mon Sep 17 00:00:00 2001
From: Dan Prince <dprince@redhat.com>
Date: Mon, 27 Jul 2015 07:35:49 -0400
Subject: [PATCH] Configure ctlplane network with a static IP

This patch updates all network configuration templates so that
we configure the ctlplane network interface with a static IP
instead of using DHCP.

The IP address used for the static IP is passed into each
nested stack network configuration template via the ControlPlaneIp
parameter.

Three new nested stack parameters called ControlPlaneSubnetCidr,
ControlPlaneDefaultRoute, and EC2MetadataIp have been added to help
configure the CIDR, default route, and EC2 metadata route on the ctlplane
statically.  These parameters can be customized via the
parameter_defaults section in the heat environment.

A single new template called net-config-static-bridge.yaml has
been added to help migrate towards using the static
configuration templates when not using network isolation.

Depends-On: I257e1cba6dee16f73f75512d1284e1e3b9d4c831

Change-Id: Ib267e6dcf2d5ff77f7a82ee20a123965c2d07565
---
 ceph-storage.yaml                             |  1 +
 cinder-storage.yaml                           |  1 +
 compute.yaml                                  |  1 +
 controller.yaml                               |  1 +
 net-config-bond.yaml                          |  4 +
 net-config-bridge.yaml                        |  4 +
 net-config-noop.yaml                          |  4 +
 net-config-static-bridge.yaml                 | 79 +++++++++++++++++++
 .../config/bond-with-vlans/ceph-storage.yaml  | 41 ++++++++--
 .../bond-with-vlans/cinder-storage.yaml       | 44 +++++++++--
 network/config/bond-with-vlans/compute.yaml   | 44 +++++++++--
 .../config/bond-with-vlans/controller.yaml    | 42 ++++++++--
 .../config/bond-with-vlans/swift-storage.yaml | 44 +++++++++--
 .../config/single-nic-vlans/ceph-storage.yaml | 38 +++++++--
 .../single-nic-vlans/cinder-storage.yaml      | 42 ++++++++--
 network/config/single-nic-vlans/compute.yaml  | 42 ++++++++--
 .../config/single-nic-vlans/controller.yaml   | 40 +++++++---
 .../single-nic-vlans/swift-storage.yaml       | 42 ++++++++--
 puppet/ceph-storage-puppet.yaml               |  1 +
 puppet/cinder-storage-puppet.yaml             |  1 +
 puppet/compute-puppet.yaml                    |  1 +
 puppet/controller-puppet.yaml                 |  1 +
 puppet/swift-storage-puppet.yaml              |  1 +
 swift-storage.yaml                            |  1 +
 24 files changed, 454 insertions(+), 66 deletions(-)
 create mode 100644 net-config-static-bridge.yaml

diff --git a/ceph-storage.yaml b/ceph-storage.yaml
index 5f9f537332..0dbcd3e7c8 100644
--- a/ceph-storage.yaml
+++ b/ceph-storage.yaml
@@ -85,6 +85,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::CephStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [CephStorage, networks, ctlplane, 0]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}
 
diff --git a/cinder-storage.yaml b/cinder-storage.yaml
index f65d928936..7a68697087 100644
--- a/cinder-storage.yaml
+++ b/cinder-storage.yaml
@@ -165,6 +165,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::BlockStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [BlockStorage, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}
diff --git a/compute.yaml b/compute.yaml
index 9a2c6f17ba..dd968e72f3 100644
--- a/compute.yaml
+++ b/compute.yaml
@@ -315,6 +315,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::Compute::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [NovaCompute, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       TenantIpSubnet: {get_attr: [TenantPort, ip_subnet]}
diff --git a/controller.yaml b/controller.yaml
index 79f5ece5f4..d6438d13bf 100644
--- a/controller.yaml
+++ b/controller.yaml
@@ -585,6 +585,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::Controller::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [Controller, networks, ctlplane, 0]}
       ExternalIpSubnet: {get_attr: [ExternalPort, ip_subnet]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
diff --git a/net-config-bond.yaml b/net-config-bond.yaml
index d74fc0bc75..797df4bf0c 100644
--- a/net-config-bond.yaml
+++ b/net-config-bond.yaml
@@ -4,6 +4,10 @@ description: >
   Software Config to drive os-net-config with 2 bonded nics on a bridge.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
diff --git a/net-config-bridge.yaml b/net-config-bridge.yaml
index c3416e0278..ad16ef0bc6 100644
--- a/net-config-bridge.yaml
+++ b/net-config-bridge.yaml
@@ -4,6 +4,10 @@ description: >
   Software Config to drive os-net-config for a simple bridge.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
diff --git a/net-config-noop.yaml b/net-config-noop.yaml
index 3d88dd9ce0..30de5846b5 100644
--- a/net-config-noop.yaml
+++ b/net-config-noop.yaml
@@ -5,6 +5,10 @@ description: >
   to use the parameter driven (init-neutron-ovs) configuration instead.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
diff --git a/net-config-static-bridge.yaml b/net-config-static-bridge.yaml
new file mode 100644
index 0000000000..38b483bd18
--- /dev/null
+++ b/net-config-static-bridge.yaml
@@ -0,0 +1,79 @@
+heat_template_version: 2015-04-30
+
+description: >
+  Software Config to drive os-net-config for a simple bridge configured
+  with a static IP address for the ctlplane network.
+
+parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
+  ExternalIpSubnet:
+    default: ''
+    description: IP address/subnet on the external network
+    type: string
+  InternalApiIpSubnet:
+    default: ''
+    description: IP address/subnet on the internal API network
+    type: string
+  StorageIpSubnet:
+    default: ''
+    description: IP address/subnet on the storage network
+    type: string
+  StorageMgmtIpSubnet:
+    default: ''
+    description: IP address/subnet on the storage mgmt network
+    type: string
+  TenantIpSubnet:
+    default: ''
+    description: IP address/subnet on the tenant network
+    type: string
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
+
+resources:
+  OsNetConfigImpl:
+    type: OS::Heat::StructuredConfig
+    properties:
+      group: os-apply-config
+      config:
+        os_net_config:
+          network_config:
+            -
+              type: ovs_bridge
+              name: {get_input: bridge_name}
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
+              members:
+                -
+                  type: interface
+                  name: {get_input: interface_name}
+                  # force the MAC address of the bridge to this interface
+                  primary: true
+
+outputs:
+  OS::stack_id:
+    description: The OsNetConfigImpl resource.
+    value: {get_resource: OsNetConfigImpl}
diff --git a/network/config/bond-with-vlans/ceph-storage.yaml b/network/config/bond-with-vlans/ceph-storage.yaml
index cd70cbef18..cffc06f4b7 100644
--- a/network/config/bond-with-vlans/ceph-storage.yaml
+++ b/network/config/bond-with-vlans/ceph-storage.yaml
@@ -5,6 +5,10 @@ description: >
   with VLANs attached for the ceph storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -38,7 +42,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
-
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -48,6 +61,24 @@ resources:
       config:
         os_net_config:
           network_config:
+            -
+              type: interface
+              name: nic1
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
             -
               type: ovs_bridge
               name: br-bond
@@ -69,15 +100,15 @@ resources:
                   device: bond1
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/bond-with-vlans/cinder-storage.yaml b/network/config/bond-with-vlans/cinder-storage.yaml
index 866112cb18..894d59826d 100644
--- a/network/config/bond-with-vlans/cinder-storage.yaml
+++ b/network/config/bond-with-vlans/cinder-storage.yaml
@@ -5,6 +5,10 @@ description: >
   with VLANs attached for the cinder storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -42,6 +46,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -51,6 +65,24 @@ resources:
       config:
         os_net_config:
           network_config:
+            -
+              type: interface
+              name: nic1
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
             -
               type: ovs_bridge
               name: br-bond
@@ -72,22 +104,22 @@ resources:
                   device: bond1
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/bond-with-vlans/compute.yaml b/network/config/bond-with-vlans/compute.yaml
index 3a46a48de1..7c79cd1aba 100644
--- a/network/config/bond-with-vlans/compute.yaml
+++ b/network/config/bond-with-vlans/compute.yaml
@@ -5,6 +5,10 @@ description: >
   with VLANs attached for the compute role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -42,6 +46,16 @@ parameters:
     default: 50
     description: Vlan ID for the tenant network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -51,6 +65,24 @@ resources:
       config:
         os_net_config:
           network_config:
+            -
+              type: interface
+              name: nic1
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
             -
               type: ovs_bridge
               name: {get_input: bridge_name}
@@ -72,22 +104,22 @@ resources:
                   device: bond1
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: TenantNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: TenantIpSubnet}
+                    -
+                      ip_netmask: {get_param: TenantIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/bond-with-vlans/controller.yaml b/network/config/bond-with-vlans/controller.yaml
index 3c19f515ac..cd1961ad35 100644
--- a/network/config/bond-with-vlans/controller.yaml
+++ b/network/config/bond-with-vlans/controller.yaml
@@ -5,6 +5,10 @@ description: >
   with VLANs attached for the controller role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -55,6 +59,13 @@ parameters:
     default: '10.0.0.1'
     description: default route for the external network
     type: string
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -64,6 +75,21 @@ resources:
       config:
         os_net_config:
           network_config:
+            -
+              type: interface
+              name: nic1
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
             -
               type: ovs_bridge
               name: {get_input: bridge_name}
@@ -96,29 +122,29 @@ resources:
                   device: bond1
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: TenantNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: TenantIpSubnet}
+                    -
+                      ip_netmask: {get_param: TenantIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/bond-with-vlans/swift-storage.yaml b/network/config/bond-with-vlans/swift-storage.yaml
index f31ed0e7d7..f182baef5f 100644
--- a/network/config/bond-with-vlans/swift-storage.yaml
+++ b/network/config/bond-with-vlans/swift-storage.yaml
@@ -5,6 +5,10 @@ description: >
   with VLANs attached for the swift storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -42,6 +46,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -51,6 +65,24 @@ resources:
       config:
         os_net_config:
           network_config:
+            -
+              type: interface
+              name: nic1
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
             -
               type: ovs_bridge
               name: br-bond
@@ -72,22 +104,22 @@ resources:
                   device: bond1
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   device: bond1
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/single-nic-vlans/ceph-storage.yaml b/network/config/single-nic-vlans/ceph-storage.yaml
index 4a25f7631b..ddb4163312 100644
--- a/network/config/single-nic-vlans/ceph-storage.yaml
+++ b/network/config/single-nic-vlans/ceph-storage.yaml
@@ -5,6 +5,10 @@ description: >
   ceph storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -33,6 +37,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -45,7 +59,21 @@ resources:
             -
               type: ovs_bridge
               name: br-storage
-              use_dhcp: true
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
               members:
                 -
                   type: interface
@@ -56,14 +84,14 @@ resources:
                   type: vlan
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/single-nic-vlans/cinder-storage.yaml b/network/config/single-nic-vlans/cinder-storage.yaml
index 397b1ecdd4..4b2a5753be 100644
--- a/network/config/single-nic-vlans/cinder-storage.yaml
+++ b/network/config/single-nic-vlans/cinder-storage.yaml
@@ -5,6 +5,10 @@ description: >
   cinder storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -37,6 +41,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -49,7 +63,21 @@ resources:
             -
               type: ovs_bridge
               name: br-storage
-              use_dhcp: true
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
               members:
                 -
                   type: interface
@@ -60,20 +88,20 @@ resources:
                   type: vlan
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/single-nic-vlans/compute.yaml b/network/config/single-nic-vlans/compute.yaml
index c73aed5ecf..dc8d685155 100644
--- a/network/config/single-nic-vlans/compute.yaml
+++ b/network/config/single-nic-vlans/compute.yaml
@@ -5,6 +5,10 @@ description: >
   compute role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -37,6 +41,16 @@ parameters:
     default: 50
     description: Vlan ID for the tenant network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -49,7 +63,21 @@ resources:
             -
               type: ovs_bridge
               name: {get_input: bridge_name}
-              use_dhcp: true
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
               members:
                 -
                   type: interface
@@ -60,20 +88,20 @@ resources:
                   type: vlan
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: TenantNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: TenantIpSubnet}
+                    -
+                      ip_netmask: {get_param: TenantIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/single-nic-vlans/controller.yaml b/network/config/single-nic-vlans/controller.yaml
index 4cfa1317a0..c0f4132b11 100644
--- a/network/config/single-nic-vlans/controller.yaml
+++ b/network/config/single-nic-vlans/controller.yaml
@@ -5,6 +5,10 @@ description: >
   controller role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -49,6 +53,13 @@ parameters:
     default: '10.0.0.1'
     description: default route for the external network
     type: string
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -61,7 +72,18 @@ resources:
             -
               type: ovs_bridge
               name: {get_input: bridge_name}
-              use_dhcp: true
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
               members:
                 -
                   type: interface
@@ -82,26 +104,26 @@ resources:
                   type: vlan
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: TenantNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: TenantIpSubnet}
+                    -
+                      ip_netmask: {get_param: TenantIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/network/config/single-nic-vlans/swift-storage.yaml b/network/config/single-nic-vlans/swift-storage.yaml
index f033ced7d5..5a308df0be 100644
--- a/network/config/single-nic-vlans/swift-storage.yaml
+++ b/network/config/single-nic-vlans/swift-storage.yaml
@@ -5,6 +5,10 @@ description: >
   swift storage role.
 
 parameters:
+  ControlPlaneIp:
+    default: ''
+    description: IP address/subnet on the ctlplane network
+    type: string
   ExternalIpSubnet:
     default: ''
     description: IP address/subnet on the external network
@@ -37,6 +41,16 @@ parameters:
     default: 40
     description: Vlan ID for the storage mgmt network traffic.
     type: number
+  ControlPlaneSubnetCidr: # Override this via parameter_defaults
+    default: '24'
+    description: The subnet CIDR of the control plane network.
+    type: string
+  ControlPlaneDefaultRoute: # Override this via parameter_defaults
+    description: The default route of the control plane network.
+    type: string
+  EC2MetadataIp: # Override this via parameter_defaults
+    description: The IP address of the EC2 metadata server.
+    type: string
 
 resources:
   OsNetConfigImpl:
@@ -49,7 +63,21 @@ resources:
             -
               type: ovs_bridge
               name: br-storage
-              use_dhcp: true
+              use_dhcp: false
+              addresses:
+                -
+                  ip_netmask:
+                    list_join:
+                      - '/'
+                      - - {get_param: ControlPlaneIp}
+                        - {get_param: ControlPlaneSubnetCidr}
+              routes:
+                -
+                  ip_netmask: 169.254.169.254/32
+                  next_hop: {get_param: EC2MetadataIp}
+                -
+                  default: true
+                  next_hop: {get_param: ControlPlaneDefaultRoute}
               members:
                 -
                   type: interface
@@ -60,20 +88,20 @@ resources:
                   type: vlan
                   vlan_id: {get_param: InternalApiNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: InternalApiIpSubnet}
+                    -
+                      ip_netmask: {get_param: InternalApiIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageIpSubnet}
                 -
                   type: vlan
                   vlan_id: {get_param: StorageMgmtNetworkVlanID}
                   addresses:
-                  -
-                    ip_netmask: {get_param: StorageMgmtIpSubnet}
+                    -
+                      ip_netmask: {get_param: StorageMgmtIpSubnet}
 
 outputs:
   OS::stack_id:
diff --git a/puppet/ceph-storage-puppet.yaml b/puppet/ceph-storage-puppet.yaml
index f08b83cd87..4b4c76fc8d 100644
--- a/puppet/ceph-storage-puppet.yaml
+++ b/puppet/ceph-storage-puppet.yaml
@@ -102,6 +102,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::CephStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [CephStorage, networks, ctlplane, 0]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}
 
diff --git a/puppet/cinder-storage-puppet.yaml b/puppet/cinder-storage-puppet.yaml
index d764c6f767..f597512a50 100644
--- a/puppet/cinder-storage-puppet.yaml
+++ b/puppet/cinder-storage-puppet.yaml
@@ -161,6 +161,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::BlockStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [BlockStorage, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}
diff --git a/puppet/compute-puppet.yaml b/puppet/compute-puppet.yaml
index 74e9b63e36..3f7309940a 100644
--- a/puppet/compute-puppet.yaml
+++ b/puppet/compute-puppet.yaml
@@ -312,6 +312,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::Compute::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [NovaCompute, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       TenantIpSubnet: {get_attr: [TenantPort, ip_subnet]}
diff --git a/puppet/controller-puppet.yaml b/puppet/controller-puppet.yaml
index eb19b36d24..084fe3d133 100644
--- a/puppet/controller-puppet.yaml
+++ b/puppet/controller-puppet.yaml
@@ -602,6 +602,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::Controller::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [Controller, networks, ctlplane, 0]}
       ExternalIpSubnet: {get_attr: [ExternalPort, ip_subnet]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
diff --git a/puppet/swift-storage-puppet.yaml b/puppet/swift-storage-puppet.yaml
index 5c4ff5a1c3..676177713d 100644
--- a/puppet/swift-storage-puppet.yaml
+++ b/puppet/swift-storage-puppet.yaml
@@ -130,6 +130,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::ObjectStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [SwiftStorage, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}
diff --git a/swift-storage.yaml b/swift-storage.yaml
index 1a2967faa0..d62d7d1a22 100644
--- a/swift-storage.yaml
+++ b/swift-storage.yaml
@@ -149,6 +149,7 @@ resources:
   NetworkConfig:
     type: OS::TripleO::ObjectStorage::Net::SoftwareConfig
     properties:
+      ControlPlaneIp: {get_attr: [SwiftStorage, networks, ctlplane, 0]}
       InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
       StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
       StorageMgmtIpSubnet: {get_attr: [StorageMgmtPort, ip_subnet]}