Merge "Add all services to container scenarios" into stable/pike
[apex-tripleo-heat-templates.git] / overcloud.j2.yaml
index c7db59f..3506fe8 100644 (file)
@@ -21,76 +21,99 @@ 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 storage management endpoint. E.g.
-      'ci-overcloud.management.tripleo.org'.
+      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'}]
+  ExtraConfig:
+    default: {}
+    description: |
+      Additional hiera configuration to inject into the cluster.
     type: json
-  InternalApiVirtualFixedIPs:
-    default: []
-    description: >
-        Control the IP allocation for the InternalApiVirtualInterface port. E.g.
-        [{'ip_address':'1.2.3.4'}]
+{%- for role in roles %}
+  {{role.name}}ExtraConfig:
+    default: {}
+    description: |
+      Role specific additional hiera configuration to inject into the cluster.
+    type: json
+{%- if role.deprecated_param_extraconfig is defined %}
+  {{role.deprecated_param_extraconfig}}:
+    default: {}
+    description: |
+      DEPRECATED use {{role.name}}ExtraConfig instead
     type: json
+{%- endif %}
+{%- endfor %}
   NeutronControlPlaneID:
     default: 'ctlplane'
     type: string
     description: Neutron ID or name for ctlplane network.
   NeutronPublicInterface:
     default: nic1
-    description: What interface to bridge onto br-ex for network nodes.
+    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: >
@@ -101,8 +124,8 @@ parameters:
     default: 'localdomain'
     type: string
     description: >
-      The DNS domain used for the hosts. This should match the dhcp_domain
-      configured in the Undercloud neutron. Defaults to localdomain.
+      The DNS domain used for the hosts. This must match the
+      overcloud_domain_name configured on the undercloud.
   ServerMetadata:
     default: {}
     description: >
@@ -154,7 +177,6 @@ parameters:
   {% else %}
     default: "%stackname%-{{role.name.lower()}}-%index%"
   {% endif %}
-
   {{role.name}}RemovalPolicies:
     default: []
     type: json
@@ -163,14 +185,16 @@ parameters:
       doing an update which requires removal of specific resources.
       Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
 
-{% if role.name != 'Compute' %}
   {{role.name}}SchedulerHints:
-{% else %}
-  NovaComputeSchedulerHints:
-{% endif %}
     type: json
     description: Optional scheduler hints to pass to nova
     default: {}
+{%- if role.deprecated_param_scheduler_hints is defined %}
+  {{role.deprecated_param_scheduler_hints}}:
+    type: json
+    description: DEPRECATED - use {{role.name}}SchedulerHints instead
+    default: {}
+{%- endif %}
 
   {{role.name}}Parameters:
     type: json
@@ -203,6 +227,24 @@ parameters:
     description: >
       List of server hostnames to blacklist from any triggered deployments.
 
+{% for role in roles %}
+{%- if role.deprecated_param_scheduler_hints is defined or role.deprecated_param_extraconfig is defined %}
+{%- if not parameter_groups_defined|default(false) %}
+parameter_groups:
+- label: deprecated
+  description: Do not use deprecated params, they will be removed.
+  parameters:
+{%- set parameter_groups_defined = true %}
+{%- endif %}
+{%- endif %}
+{%- if role.deprecated_param_scheduler_hints is defined %}
+    - {{role.deprecated_param_scheduler_hints}}
+{%- endif %}
+{%- if role.deprecated_param_extraconfig is defined %}
+    - {{role.deprecated_param_extraconfig}}
+{%- endif %}
+{%- endfor %}
+
 conditions:
   add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
 
@@ -218,42 +260,66 @@ 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::Heat::RandomString
+    type: OS::TripleO::RandomString
 
   PcsdPassword:
-    type: OS::Heat::RandomString
+    type: OS::TripleO::RandomString
     properties:
       length: 16
 
   HorizonSecret:
-    type: OS::Heat::RandomString
+    type: OS::TripleO::RandomString
     properties:
       length: 10
 
+  NetCidrMapValue:
+    type: OS::Heat::Value
+    properties:
+      type: json
+      value:
+        map_replace:
+        - map_merge:
+          - {get_attr: [Networks, net_cidr_map]}
+          - ctlplane: {get_attr: [ControlVirtualIP, subnets, 0, cidr]}
+        - keys:
+            ctlplane: {get_param: NeutronControlPlaneID}
+          values:
+            disabled: {get_attr: [ControlVirtualIP, subnets, 0, cidr]}
+
   ServiceNetMap:
     type: OS::TripleO::ServiceNetMap
 
@@ -261,11 +327,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]}
 
@@ -294,6 +370,8 @@ resources:
       Services:
         get_param: {{role.name}}Services
       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
+      ServiceData:
+        net_cidr_map: {get_attr: [NetCidrMapValue, value]}
       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
       DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
       RoleName: {{role.name}}
@@ -307,6 +385,49 @@ resources:
       type: json
       value: {get_attr: [{{role.name}}ServiceChain, role_data]}
 
+  {{role.name}}ServiceConfigSettings:
+    type: OS::Heat::Value
+    properties:
+      type: json
+      value:
+        map_merge:
+          - get_attr: [{{role.name}}ServiceChainRoleData, value, config_settings]
+          {% for r in roles %}
+          - get_attr: [{{r.name}}ServiceChainRoleData, value, global_config_settings]
+          {% endfor %}
+          # This next step combines two yaql passes:
+          # - The inner one does a deep merge on the service_config_settings for all roles
+          # - The outer one filters the map based on the services enabled for the role
+          #   then merges the result into one map.
+          - yaql:
+              expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
+              data:
+                map:
+                  yaql:
+                    expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
+                    data:
+                    {% for r in roles %}
+                      - get_attr: [{{r.name}}ServiceChainRoleData, value, service_config_settings]
+                    {% endfor %}
+                services: {get_attr: [{{role.name}}ServiceNames, value]}
+
+  {{role.name}}MergedConfigSettings:
+    type: OS::Heat::Value
+    properties:
+      type: json
+      value:
+        config_settings: {}
+        global_config_settings: {}
+        service_config_settings: {}
+        merged_config_settings:
+          map_merge:
+          - get_attr: [{{role.name}}ServiceConfigSettings, value]
+          - get_param: ExtraConfig
+{%- if role.deprecated_param_extraconfig is defined %}
+          - get_param: {{role.deprecated_param_extraconfig}}
+{%- endif %}
+          - get_param: {{role.name}}ExtraConfig
+
   # Filter any null/None service_names which may be present due to mapping
   # of services to OS::Heat::None
   {{role.name}}ServiceNames:
@@ -334,7 +455,7 @@ resources:
       servers: {get_attr: [{{role.name}}Servers, value]}
 
   {{role.name}}AllNodesDeployment:
-    type: OS::Heat::StructuredDeployments
+    type: OS::TripleO::AllNodesDeployment
     depends_on:
 {% for role_inner in roles %}
       - {{role_inner.name}}HostsDeployment
@@ -370,12 +491,13 @@ 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]}
+  {%- else %}
+      {{network.name}}IpList: {get_attr: [{{role.name}}, ip_address]}
+  {%- endif %}
+{%- endfor %}
       EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
@@ -416,34 +538,19 @@ resources:
               params:
                 '%stackname%': {get_param: 'OS::stack_name'}
           NodeIndex: '%index%'
-  {% if role.name != 'Compute' %}
-          {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
-  {% else %}
-          NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
-  {% endif %}
-          ServiceConfigSettings:
+          # Note, SchedulerHints must be defined here, not only in the
+          # nested template, as it can contain %index%
+          {{role.name}}SchedulerHints:
             map_merge:
-              -  get_attr: [{{role.name}}ServiceChainRoleData, value, config_settings]
-          {% for r in roles %}
-              - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
-          {% endfor %}
-              # This next step combines two yaql passes:
-              # - The inner one does a deep merge on the service_config_settings for all roles
-              # - The outer one filters the map based on the services enabled for the role
-              #   then merges the result into one map.
-              - yaql:
-                  expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
-                  data:
-                    map:
-                      yaql:
-                        expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
-                        data:
-                        {% for r in roles %}
-                          - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
-                        {% endfor %}
-                    services: {get_attr: [{{role.name}}ServiceNames, value]}
+{%- if role.deprecated_param_scheduler_hints is defined %}
+              - {get_param: {{role.deprecated_param_scheduler_hints}}}
+{%- endif %}
+              - {get_param: {{role.name}}SchedulerHints}
+          ServiceConfigSettings: {get_attr: [{{role.name}}ServiceConfigSettings, value]}
           ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChainRoleData, value, monitoring_subscriptions]}
+          LoggingSources: {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_sources]}
+          LoggingGroups: {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_groups]}
           ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_metadata_settings]}
           DeploymentServerBlacklistDict: {get_attr: [DeploymentServerBlacklistDict, value]}
           RoleParameters: {get_param: {{role.name}}Parameters}
@@ -462,6 +569,21 @@ resources:
             servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
 {% endfor %}
 
+  # This is a different format to *Servers, as it creates a map of lists
+  # whereas *Servers creates a map of maps with keys of the nested resource names
+  ServerIdMap:
+    type: OS::Heat::Value
+    properties:
+      value:
+        server_ids:
+{% for role in roles %}
+          {{role.name}}: {get_attr: [{{role.name}}, nova_server_resource]}
+{% endfor %}
+        bootstrap_server_id:
+          yaql:
+            expression: coalesce($.data, []).first(null)
+            data: {get_attr: [{{primary_role_name}}, nova_server_resource]}
+
   # This resource just creates a dict out of the DeploymentServerBlacklist,
   # which is a list. The dict is used in the role templates to set a condition
   # on whether to create the deployment resources. We can't use the list
@@ -499,34 +621,26 @@ 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:
           - ','
 {% for role in roles %}
           - {get_attr: [{{role.name}}ServiceNames, value]}
-{% endfor %}
-      logging_groups:
-        yaql:
-          expression: >
-            $.data.groups.flatten()
-          data:
-            groups:
-{% for role in roles %}
-              - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_groups]}
-{% endfor %}
-      logging_sources:
-        yaql:
-          expression: >
-            $.data.sources.flatten()
-          data:
-            sources:
-{% for role in roles %}
-              - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_sources]}
 {% endfor %}
       controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
       controller_names: {get_attr: [{{primary_role_name}}, hostname]}
@@ -566,8 +680,6 @@ resources:
 {% for role in roles %}
               - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
 {% endfor %}
-      # FIXME(shardy): These require further work to move into service_ips
-      memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
       NetVipMap: {get_attr: [VipMap, net_ip_map]}
       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
@@ -575,12 +687,12 @@ resources:
       UpdateIdentifier: {get_param: UpdateIdentifier}
 
   MysqlRootPassword:
-    type: OS::Heat::RandomString
+    type: OS::TripleO::RandomString
     properties:
       length: 10
 
   RabbitCookie:
-    type: OS::Heat::RandomString
+    type: OS::TripleO::RandomString
     properties:
       length: 20
       salt: {get_param: RabbitCookieSalt}
@@ -618,6 +730,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
@@ -627,44 +741,43 @@ resources:
       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
       PortName: public_virtual_ip
       FixedIPs: {get_param: PublicVirtualFixedIPs}
-
-  InternalApiVirtualIP:
-    depends_on: Networks
-    type: OS::TripleO::Network::Ports::InternalApiVipPort
-    properties:
-      ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
-      PortName: internal_api_virtual_ip
-      FixedIPs: {get_param: InternalApiVirtualFixedIPs}
-
-  StorageVirtualIP:
+{%- elif network.name == 'StorageMgmt' %}
+  {{network.name}}VirtualIP:
     depends_on: Networks
-    type: OS::TripleO::Network::Ports::StorageVipPort
+    type: OS::TripleO::Network::Ports::{{network.name}}VipPort
     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
+    # ServiceNetMap's attribute values are available.
+    depends_on: ServiceNetMap
 
   # All Nodes Validations
   AllNodesValidationConfig:
@@ -673,24 +786,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
@@ -735,12 +836,69 @@ resources:
 {% for role in roles %}
         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
 {% endfor %}
+      stack_name: {get_param: 'OS::stack_name'}
       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
+      ctlplane_service_ips:
+        # Note (shardy) this somewhat complex yaql may be replaced
+        # with a map_deep_merge function in ocata.  It merges the
+        # list of maps, but appends to colliding lists when a service
+        # is deployed on more than one role
+        yaql:
+          expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
+          data:
+            l:
+{% for role in roles %}
+              - {get_attr: [{{role.name}}IpListMap, ctlplane_service_ips]}
+{% endfor %}
       role_data:
 {% for role in roles %}
-        {{role.name}}: {get_attr: [{{role.name}}ServiceChainRoleData, value]}
+        {{role.name}}:
+          map_merge:
+          - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
+          - {get_attr: [{{role.name}}MergedConfigSettings, value]}
 {% endfor %}
 
+  ServerOsCollectConfigData:
+    type: OS::Heat::Value
+    properties:
+      type: json
+      value:
+{% for role in roles %}
+        {{role.name}}: {get_attr: [{{role.name}}, attributes, os_collect_config]}
+{% endfor %}
+
+  DeployedServerEnvironment:
+    type: OS::TripleO::DeployedServerEnvironment
+    properties:
+      RoleCounts:
+{% for role in roles %}
+        {{role.name}}DeployedServerCount: {get_param: {{role.name}}Count}
+{% endfor %}
+      VipMap:
+        map_merge:
+          - {get_attr: [VipMap, net_ip_map]}
+          - redis: {get_attr: [RedisVirtualIP, ip_address]}
+      DeployedServerPortMap:
+        map_merge:
+          list_concat:
+{% for role in roles %}
+              - {get_attr: [{{role.name}}, deployed_server_port_map]}
+{% endfor %}
+      DeployedServerDeploymentSwiftDataMap:
+        map_merge:
+          list_concat:
+{% for role in roles %}
+              - {get_attr: [{{role.name}}, deployed_server_deployment_swift_data_map]}
+{% endfor %}
+      DefaultRouteIp:
+        str_split:
+          - ':'
+          - str_split:
+            - '/'
+            - {get_attr: [ServerOsCollectConfigData, value, {{primary_role_name}}, '0', request, metadata_url]}
+            - 2
+          - 0
+
 outputs:
   ManagedEndpoints:
     description: Asserts that the keystone endpoints have been provisioned.
@@ -750,6 +908,11 @@ outputs:
     value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
   KeystoneAdminVip:
     description: Keystone Admin VIP endpoint
+    # Note that these nested get_attr functions require a dependency
+    # relationship between VipMap and ServiceNetMap, since we can't determine
+    # which attributes of VipMap are used until after ServiceNetMap's attribute
+    # values are available. If this is ever reworked to not use nested
+    # get_attr, that dependency can be removed.
     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
   EndpointMap:
     description: |
@@ -777,8 +940,14 @@ outputs:
     description: The configuration data associated with each role
     value:
 {% for role in roles %}
-      {{role.name}}: {get_attr: [{{role.name}}ServiceChainRoleData, value]}
+      {{role.name}}:
+        map_merge:
+        - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
+        - {get_attr: [{{role.name}}MergedConfigSettings, value]}
 {% endfor %}
+  RoleConfig:
+    description: The configuration workflows associated with each role
+    value: {get_attr: [AllNodesDeploySteps, RoleConfig]}
   RoleNetIpMap:
     description: Mapping of each network to a list of IPs for each role
     value:
@@ -791,3 +960,20 @@ outputs:
 {% for role in roles %}
       {{role.name}}: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
 {% endfor %}
+  ServerOsCollectConfigData:
+    description: The os-collect-config configuration associated with each server resource
+    value: {get_attr: [ServerOsCollectConfigData, value]}
+  VipMap:
+    description: Mapping of each network to VIP addresses. Also includes the Redis VIP.
+    value:
+      map_merge:
+        - {get_attr: [VipMap, net_ip_map]}
+        - redis: {get_attr: [RedisVirtualIP, ip_address]}
+  ServerIdData:
+    description: Mapping of each role to a list of nova server IDs and the bootstrap ID
+    value: {get_attr: [ServerIdMap, value]}
+  DeployedServerEnvironment:
+    description:
+      Environment data that can be used as input into the services stack when
+      using split-stack.
+    value: {get_attr: [DeployedServerEnvironment, deployed_server_environment]}