Browse Source

Render VIPs dynamically based on network_data.yaml

This change modifies the templates to dynamically define the VIPs
based on network_data.yaml. If a network is defined and marked
with "vip: true" in network_data.yaml, it will be included in the
overcloud.yaml which defines the deployment-level resources.

This should make it possible to create custom networks and
use them for services which use high-availability through VIPs.

Also, extraconfig/nova_metadata/krb-service-pricipals.yaml
was modified to dynamically produce the FQDN map for VIPs on
isolated networks, to match overcloud.j2.yaml.

Depends-On: If074f87494a46305c990a0ea332c7b576d3c6ed8
Depends-On: Iab8aca2f1fcaba0c8f109717a4b3068f629c9aab
Partially-implements: blueprint composable-networks
Closes-bug: 1667104
Change-Id: I71339a6ac41133e95dbc3f93abb7a9fdeb0f2da0
changes/60/486260/15
Dan Sneddon 5 years ago committed by Bob Fournier
parent
commit
b19b88bd1c
  1. 44
      extraconfig/nova_metadata/krb-service-principals.j2.yaml
  2. 40
      network/ports/net_vip_map_external.j2.yaml
  3. 68
      network/ports/net_vip_map_external.yaml
  4. 45
      network/ports/net_vip_map_external_v6.j2.yaml
  5. 88
      network/ports/net_vip_map_external_v6.yaml
  6. 208
      overcloud.j2.yaml

44
extraconfig/nova_metadata/krb-service-principals.yaml → extraconfig/nova_metadata/krb-service-principals.j2.yaml

@ -5,30 +5,38 @@ parameters:
RoleData:
type: json
description: the list containing the 'role_data' output for the ServiceChain
# Coming from parameter_defaults
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# Special case the External hostname param, which is CloudName
CloudName:
default: overcloud.localdomain
description: The DNS name of this cloud. E.g. ci-overcloud.tripleo.org
type: string
{%- elif network.name == 'InternalApi' %}
# Special case the Internal API hostname param, which is CloudNameInternal
CloudNameInternal:
default: overcloud.internalapi.localdomain
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's internal API endpoint. E.g.
'ci-overcloud.internalapi.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
CloudNameStorage:
default: overcloud.storage.localdomain
{%- elif network.name == 'StorageMgmt' %}
# Special case StorageMgmt hostname param, which is CloudNameStorageManagement
CloudNameStorageManagement:
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's storage endpoint. E.g.
'ci-overcloud.storage.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
CloudNameStorageManagement:
default: overcloud.storagemgmt.localdomain
{%- else %}
CloudName{{network.name}}:
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's storage management endpoint. E.g.
'ci-overcloud.storagemgmt.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
{%- endif %}
{%- endfor %}
CloudNameCtlplane:
default: overcloud.ctlplane.localdomain
description: >
@ -61,10 +69,17 @@ resources:
data:
metadata: {get_attr: [IncomingMetadataSettings, value]}
fqdns:
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
external: {get_param: CloudName}
{%- elif network.name == 'InternalApi' %}
internal_api: {get_param: CloudNameInternal}
storage: {get_param: CloudNameStorage}
{%- elif network.name == 'StorageMgmt' %}
storage_mgmt: {get_param: CloudNameStorageManagement}
{%- else %}
{{network.name_lower}}: {get_param: CloudName{{network.name}}}
{%- endif %}
{%- endfor %}
ctlplane: {get_param: CloudNameCtlplane}
CompactServices:
@ -82,3 +97,4 @@ outputs:
map_merge:
- {get_attr: [IndividualServices, value]}
- compact_services: {get_attr: [CompactServices, value]}

40
network/ports/net_vip_map_external.j2.yaml

@ -0,0 +1,40 @@
heat_template_version: pike
parameters:
# Set these via parameter defaults to configure external VIPs
ControlPlaneIP:
default: ''
type: string
{%- for network in networks if network.vip|default(false) %}
{{network.name}}NetworkVip:
default: ''
type: string
{%- endfor %}
# The following are unused in this template
ControlPlaneIp:
default: ''
type: string
{%- for network in networks if network.vip|default(false) %}
{{network.name}}Ip:
default: ''
type: string
{{network.name}}IpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
{%- endfor %}
outputs:
net_ip_map:
description: >
A Hash containing a mapping of network names to assigned IPs
for a specific machine.
value:
ctlplane: {get_param: ControlPlaneIP}
{%- for network in networks if network.vip|default(false) %}
{{network.name_lower}}: {get_param: {{network.name}}NetworkVip}
{%- endfor %}
ctlplane_uri: {get_param: ControlPlaneIP}
{%- for network in networks if network.vip|default(false) %}
{{network.name_lower}}_uri: {get_param: {{network.name}}NetworkVip}
{%- endfor %}

68
network/ports/net_vip_map_external.yaml

@ -1,68 +0,0 @@
heat_template_version: pike
parameters:
# Set these via parameter defaults to configure external VIPs
ControlPlaneIP:
default: ''
type: string
ExternalNetworkVip:
default: ''
type: string
InternalApiNetworkVip:
default: ''
type: string
StorageNetworkVip:
default: ''
type: string
StorageMgmtNetworkVip:
default: ''
type: string
# The following are unused in this template
ControlPlaneIp:
default: ''
type: string
ExternalIp:
default: ''
type: string
ExternalIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
InternalApiIp:
default: ''
type: string
InternalApiIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
StorageIp:
default: ''
type: string
StorageIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
StorageMgmtIp:
default: ''
type: string
StorageMgmtIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
outputs:
net_ip_map:
description: >
A Hash containing a mapping of network names to assigned IPs
for a specific machine.
value:
ctlplane: {get_param: ControlPlaneIP}
external: {get_param: ExternalNetworkVip}
internal_api: {get_param: InternalApiNetworkVip}
storage: {get_param: StorageNetworkVip}
storage_mgmt: {get_param: StorageMgmtNetworkVip}
ctlplane_uri: {get_param: ControlPlaneIP}
external_uri: {get_param: ExternalNetworkVip}
internal_api_uri: {get_param: InternalApiNetworkVip}
storage_uri: {get_param: StorageNetworkVip}
storage_mgmt_uri: {get_param: StorageMgmtNetworkVip}

45
network/ports/net_vip_map_external_v6.j2.yaml

@ -0,0 +1,45 @@
heat_template_version: pike
parameters:
# Set these via parameter defaults to configure external VIPs
ControlPlaneIP:
default: ''
type: string
{%- for network in networks if network.vip|default(false) %}
{{network.name}}NetworkVip:
default: ''
type: string
{%- endfor %}
# The following are unused in this template
ControlPlaneIp:
default: ''
type: string
{%- for network in networks if network.vip|default(false) %}
{{network.name}}Ip:
default: ''
type: string
{{network.name}}IpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
{%- endfor %}
outputs:
net_ip_map:
description: >
A Hash containing a mapping of network names to assigned IPs
for a specific machine.
value:
ctlplane: {get_param: ControlPlaneIP}
{%- for network in networks if network.vip|default(false) %}
{{network.name_lower}}: {get_param: {{network.name}}NetworkVip}
{%- endfor %}
ctlplane_uri: {get_param: ControlPlaneIP}
{%- for network in networks if network.vip|default(false) %}
{{network.name_lower}}_uri:
list_join:
- ''
- - '['
- {get_param: {{network.name}}NetworkVip}
- ']'
{%- endfor %}

88
network/ports/net_vip_map_external_v6.yaml

@ -1,88 +0,0 @@
heat_template_version: pike
parameters:
# Set these via parameter defaults to configure external VIPs
ControlPlaneIP:
default: ''
type: string
ExternalNetworkVip:
default: ''
type: string
InternalApiNetworkVip:
default: ''
type: string
StorageNetworkVip:
default: ''
type: string
StorageMgmtNetworkVip:
default: ''
type: string
# The following are unused in this template
ControlPlaneIp:
default: ''
type: string
ExternalIp:
default: ''
type: string
ExternalIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
InternalApiIp:
default: ''
type: string
InternalApiIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
StorageIp:
default: ''
type: string
StorageIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
StorageMgmtIp:
default: ''
type: string
StorageMgmtIpUri:
default: ''
type: string
description: IP address with brackets in case of IPv6
outputs:
net_ip_map:
description: >
A Hash containing a mapping of network names to assigned IPs
for a specific machine.
value:
ctlplane: {get_param: ControlPlaneIP}
external: {get_param: ExternalNetworkVip}
internal_api: {get_param: InternalApiNetworkVip}
storage: {get_param: StorageNetworkVip}
storage_mgmt: {get_param: StorageMgmtNetworkVip}
ctlplane_uri: {get_param: ControlPlaneIP}
external_uri:
list_join:
- ''
- - '['
- {get_param: ExternalNetworkVip}
- ']'
internal_api_uri:
list_join:
- ''
- - '['
- {get_param: InternalApiNetworkVip}
- ']'
storage_uri:
list_join:
- ''
- - '['
- {get_param: StorageNetworkVip}
- ']'
storage_mgmt_uri:
list_join:
- ''
- - '['
- {get_param: StorageMgmtNetworkVip}
- ']'

208
overcloud.j2.yaml

@ -21,40 +21,44 @@ description: >
parameters:
# Common parameters (not specific to a role)
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# Special case the External hostname param, which is CloudName
CloudName:
default: overcloud.localdomain
description: The DNS name of this cloud. E.g. ci-overcloud.tripleo.org
type: string
{%- elif network.name == 'InternalApi' %}
# Special case the Internal API hostname param, which is CloudNameInternal
CloudNameInternal:
default: overcloud.internalapi.localdomain
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's internal API endpoint. E.g.
'ci-overcloud.internalapi.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
CloudNameStorage:
default: overcloud.storage.localdomain
{%- elif network.name == 'StorageMgmt' %}
# Special case StorageMgmt hostname param, which is CloudNameStorageManagement
CloudNameStorageManagement:
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's storage endpoint. E.g.
'ci-overcloud.storage.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
CloudNameStorageManagement:
default: overcloud.storagemgmt.localdomain
{%- else %}
CloudName{{network.name}}:
default: overcloud.{{network.name.lower()}}.localdomain
description: >
The DNS name of this cloud's storage management endpoint. E.g.
'ci-overcloud.storagemgmt.tripleo.org'.
The DNS name of this cloud's {{network.name_lower}} endpoint. E.g.
'ci-overcloud.{{network.name.lower()}}.tripleo.org'.
type: string
{%- endif %}
{%- endfor %}
CloudNameCtlplane:
default: overcloud.ctlplane.localdomain
description: >
The DNS name of this cloud's provisioning network endpoint. E.g.
'ci-overcloud.ctlplane.tripleo.org'.
type: string
ControlFixedIPs:
default: []
description: >
Control the IP allocation for the ControlVirtualIP port. E.g.
[{'ip_address':'1.2.3.4'}]
type: json
ExtraConfig:
default: {}
description: |
@ -77,12 +81,6 @@ parameters:
description: |
DEPRECATED use ComputeExtraConfig instead
type: json
InternalApiVirtualFixedIPs:
default: []
description: >
Control the IP allocation for the InternalApiVirtualInterface port. E.g.
[{'ip_address':'1.2.3.4'}]
type: json
NeutronControlPlaneID:
default: 'ctlplane'
type: string
@ -91,28 +89,34 @@ parameters:
default: nic1
description: Which interface to add to the NeutronPhysicalBridge.
type: string
PublicVirtualFixedIPs:
ControlFixedIPs:
default: []
description: >
Control the IP allocation for the PublicVirtualInterface port. E.g.
Control the IP allocation for the ControlVirtualIP port. E.g.
[{'ip_address':'1.2.3.4'}]
type: json
RabbitCookieSalt:
type: string
default: unset
description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
StorageVirtualFixedIPs:
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# TODO (dsneddon) Legacy name, eventually refactor to match network name
PublicVirtualFixedIPs:
default: []
description: >
Control the IP allocation for the StorageVirtualInterface port. E.g.
Control the IP allocation for the PublicVirtualInterface port. E.g.
[{'ip_address':'1.2.3.4'}]
type: json
StorageMgmtVirtualFixedIPs:
{%- else %}
{{network.name}}VirtualFixedIPs:
default: []
description: >
Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
Control the IP allocation for the {{network.name}}VirtualInterface port. E.g.
[{'ip_address':'1.2.3.4'}]
type: json
{%- endif %}
{%- endfor %}
RabbitCookieSalt:
type: string
default: unset
description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
RedisVirtualFixedIPs:
default: []
description: >
@ -246,28 +250,38 @@ resources:
- - str_replace:
template: IP HOST
params:
IP: {get_attr: [VipMap, net_ip_map, external]}
HOST: {get_param: CloudName}
IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
HOST: {get_param: CloudNameCtlplane}
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# Special case the External hostname param, which is CloudName
- str_replace:
template: IP HOST
params:
IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
HOST: {get_param: CloudNameCtlplane}
IP: {get_attr: [VipMap, net_ip_map, {{network.name_lower}}]}
HOST: {get_param: CloudName}
{%- elif network.name == 'InternalApi' %}
# Special case the Internal API hostname param, which is CloudNameInternal
- str_replace:
template: IP HOST
params:
IP: {get_attr: [VipMap, net_ip_map, internal_api]}
IP: {get_attr: [VipMap, net_ip_map, {{network.name_lower}}]}
HOST: {get_param: CloudNameInternal}
{%- elif network.name == 'StorageMgmt' %}
# Special case StorageMgmt hostname param, which is CloudNameStorageManagement
- str_replace:
template: IP HOST
params:
IP: {get_attr: [VipMap, net_ip_map, storage]}
HOST: {get_param: CloudNameStorage}
IP: {get_attr: [VipMap, net_ip_map, {{network.name_lower}}]}
HOST: {get_param: CloudNameStorageManagement}
{%- else %}
- str_replace:
template: IP HOST
params:
IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
HOST: {get_param: CloudNameStorageManagement}
IP: {get_attr: [VipMap, net_ip_map, {{network.name_lower}}]}
HOST: {get_param: CloudName{{network.name}}}
{%- endif %}
{%- endfor %}
HeatAuthEncryptionKey:
type: OS::TripleO::RandomString
@ -303,11 +317,21 @@ resources:
type: OS::TripleO::EndpointMap
properties:
CloudEndpoints:
external: {get_param: CloudName}
internal_api: {get_param: CloudNameInternal}
storage: {get_param: CloudNameStorage}
storage_mgmt: {get_param: CloudNameStorageManagement}
ctlplane: {get_param: CloudNameCtlplane}
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# Special case the External hostname param, which is CloudName
{{network.name_lower}}: {get_param: CloudName}
{%- elif network.name == 'InternalApi' %}
# Special case the Internal API hostname param, which is CloudNameInternal
{{network.name_lower}}: {get_param: CloudNameInternal}
{%- elif network.name == 'StorageMgmt' %}
# Special case StorageMgmt hostname param, which is CloudNameStorageManagement
{{network.name_lower}}: {get_param: CloudNameStorageManagement}
{%- else %}
{{network.name_lower}}: {get_param: CloudName{{network.name}}}
{%- endif %}
{%- endfor %}
NetIpMap: {get_attr: [VipMap, net_ip_map]}
ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
@ -464,12 +488,9 @@ resources:
type: OS::TripleO::Network::Ports::NetIpListMap
properties:
ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
{%- for network in networks if network.enabled|default(true) %}
{{network.name}}IpList: {get_attr: [{{role.name}}, {{network.name_lower}}_ip_address]}
{%- endfor %}
EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
@ -588,10 +609,20 @@ resources:
allNodesConfig:
type: OS::TripleO::AllNodes::SoftwareConfig
properties:
cloud_name_external: {get_param: CloudName}
cloud_name_internal_api: {get_param: CloudNameInternal}
cloud_name_storage: {get_param: CloudNameStorage}
cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# Special case the External hostname param, which is CloudName
cloud_name_{{network.name_lower}}: {get_param: CloudName}
{%- elif network.name == 'InternalApi' %}
# Special case the Internal API hostname param, which is CloudNameInternal
cloud_name_{{network.name_lower}}: {get_param: CloudNameInternal}
{%- elif network.name == 'StorageMgmt' %}
# Special case StorageMgmt hostname param, which is CloudNameStorageManagement
cloud_name_{{network.name_lower}}: {get_param: CloudNameStorageManagement}
{%- else %}
cloud_name_{{network.name_lower}}: {get_param: CloudName{{network.name}}}
{%- endif %}
{%- endfor %}
cloud_name_ctlplane: {get_param: CloudNameCtlplane}
enabled_services:
list_join:
@ -705,6 +736,8 @@ resources:
ServiceName: redis
FixedIPs: {get_param: RedisVirtualFixedIPs}
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
# The public VIP is on the External net, falls back to ctlplane
PublicVirtualIP:
depends_on: Networks
@ -714,43 +747,38 @@ resources:
ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
PortName: public_virtual_ip
FixedIPs: {get_param: PublicVirtualFixedIPs}
InternalApiVirtualIP:
{%- elif network.name == 'StorageMgmt' %}
{{network.name}}VirtualIP:
depends_on: Networks
type: OS::TripleO::Network::Ports::InternalApiVipPort
type: OS::TripleO::Network::Ports::{{network.name}}VipPort
properties:
ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
PortName: internal_api_virtual_ip
FixedIPs: {get_param: InternalApiVirtualFixedIPs}
StorageVirtualIP:
depends_on: Networks
type: OS::TripleO::Network::Ports::StorageVipPort
properties:
ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
PortName: storage_virtual_ip
FixedIPs: {get_param: StorageVirtualFixedIPs}
StorageMgmtVirtualIP:
PortName: storage_management_virtual_ip
FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
{%- else %}
{{network.name}}VirtualIP:
depends_on: Networks
type: OS::TripleO::Network::Ports::StorageMgmtVipPort
type: OS::TripleO::Network::Ports::{{network.name}}VipPort
properties:
ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
PortName: storage_management_virtual_ip
FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
PortName: {{network.name_lower}}_virtual_ip
FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
{%- endif %}
{%- endfor %}
VipMap:
type: OS::TripleO::Network::Ports::NetVipMap
properties:
ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
{%- for network in networks if network.vip|default(false) %}
{%- if network.name == 'External' %}
ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
{%- else %}
{{network.name}}Ip: {get_attr: [{{network.name}}VirtualIP, ip_address]}
{{network.name}}IpUri: {get_attr: [{{network.name}}VirtualIP, ip_address_uri]}
{%- endif %}
{%- endfor %}
# No tenant or management VIP required
# Because of nested get_attr functions in the KeystoneAdminVip output, we
# can't determine which attributes of VipMap are used until after
@ -764,24 +792,12 @@ resources:
PingTestIps:
list_join:
- ' '
- - yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, external_ip_address]}
- yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
- yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
- yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
- yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
-
{%- for network in networks if network.enabled|default(true) %}
- yaql:
expression: coalesce($.data, []).first(null)
data: {get_attr: [{{primary_role_name}}, management_ip_address]}
data: {get_attr: [{{primary_role_name}}, {{network.name_lower}}_ip_address]}
{%- endfor %}
UpdateWorkflow:
type: OS::TripleO::Tasks::UpdateWorkflow

Loading…
Cancel
Save