Make many networking parameters consistent
[apex-tripleo-heat-templates.git] / overcloud.j2.yaml
1 {%- set primary_role = [roles[0]] -%}
2 {%- for role in roles -%}
3   {%- if 'primary' in role.tags and 'controller' in role.tags -%}
4     {%- set _ = primary_role.pop() -%}
5     {%- set _ = primary_role.append(role) -%}
6   {%- endif -%}
7 {%- endfor -%}
8 {%- set primary_role_name = primary_role[0].name -%}
9 # primary role is: {{primary_role_name}}
10 heat_template_version: pike
11
12 description: >
13   Deploy an OpenStack environment, consisting of several node types (roles),
14   Controller, Compute, BlockStorage, SwiftStorage and CephStorage. The Storage
15   roles enable independent scaling of the storage components, but the minimal
16   deployment is one Controller and one Compute node.
17
18
19 # TODO(shadower): we should probably use the parameter groups to put
20 # some order in here.
21 parameters:
22
23   # Common parameters (not specific to a role)
24   CloudName:
25     default: overcloud.localdomain
26     description: The DNS name of this cloud. E.g. ci-overcloud.tripleo.org
27     type: string
28   CloudNameInternal:
29     default: overcloud.internalapi.localdomain
30     description: >
31       The DNS name of this cloud's internal API endpoint. E.g.
32       'ci-overcloud.internalapi.tripleo.org'.
33     type: string
34   CloudNameStorage:
35     default: overcloud.storage.localdomain
36     description: >
37       The DNS name of this cloud's storage endpoint. E.g.
38       'ci-overcloud.storage.tripleo.org'.
39     type: string
40   CloudNameStorageManagement:
41     default: overcloud.storagemgmt.localdomain
42     description: >
43       The DNS name of this cloud's storage management endpoint. E.g.
44       'ci-overcloud.storagemgmt.tripleo.org'.
45     type: string
46   CloudNameCtlplane:
47     default: overcloud.ctlplane.localdomain
48     description: >
49       The DNS name of this cloud's storage management endpoint. E.g.
50       'ci-overcloud.management.tripleo.org'.
51     type: string
52   ControlFixedIPs:
53     default: []
54     description: >
55         Control the IP allocation for the ControlVirtualIP port. E.g.
56         [{'ip_address':'1.2.3.4'}]
57     type: json
58   ExtraConfig:
59     default: {}
60     description: |
61       Additional hiera configuration to inject into the cluster.
62     type: json
63 {%- for role in roles %}
64   {{role.name}}ExtraConfig:
65     default: {}
66     description: |
67       Role specific additional hiera configuration to inject into the cluster.
68     type: json
69 {%- endfor %}
70   controllerExtraConfig:
71     default: {}
72     description: |
73       DEPRECATED use ControllerExtraConfig instead
74     type: json
75   NovaComputeExtraConfig:
76     default: {}
77     description: |
78       DEPRECATED use ComputeExtraConfig instead
79     type: json
80   InternalApiVirtualFixedIPs:
81     default: []
82     description: >
83         Control the IP allocation for the InternalApiVirtualInterface port. E.g.
84         [{'ip_address':'1.2.3.4'}]
85     type: json
86   NeutronControlPlaneID:
87     default: 'ctlplane'
88     type: string
89     description: Neutron ID or name for ctlplane network.
90   NeutronPublicInterface:
91     default: nic1
92     description: Which interface to add to the NeutronPhysicalBridge.
93     type: string
94   PublicVirtualFixedIPs:
95     default: []
96     description: >
97         Control the IP allocation for the PublicVirtualInterface port. E.g.
98         [{'ip_address':'1.2.3.4'}]
99     type: json
100   RabbitCookieSalt:
101     type: string
102     default: unset
103     description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
104   StorageVirtualFixedIPs:
105     default: []
106     description: >
107         Control the IP allocation for the StorageVirtualInterface port. E.g.
108         [{'ip_address':'1.2.3.4'}]
109     type: json
110   StorageMgmtVirtualFixedIPs:
111     default: []
112     description: >
113         Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
114         [{'ip_address':'1.2.3.4'}]
115     type: json
116   RedisVirtualFixedIPs:
117     default: []
118     description: >
119         Control the IP allocation for the virtual IP used by Redis. E.g.
120         [{'ip_address':'1.2.3.4'}]
121     type: json
122   CloudDomain:
123     default: 'localdomain'
124     type: string
125     description: >
126       The DNS domain used for the hosts. This must match the
127       overcloud_domain_name configured on the undercloud.
128   ServerMetadata:
129     default: {}
130     description: >
131       Extra properties or metadata passed to Nova for the created nodes in
132       the overcloud. It's accessible via the Nova metadata API.
133     type: json
134
135 # Compute-specific params
136 # FIXME(shardy) handle these deprecated names as they don't match compute.yaml
137   HypervisorNeutronPhysicalBridge:
138     default: 'br-ex'
139     description: >
140       An OVS bridge to create on each hypervisor. This defaults to br-ex the
141       same as the control plane nodes, as we have a uniform configuration of
142       the openvswitch agent. Typically should not need to be changed.
143     type: string
144   HypervisorNeutronPublicInterface:
145     default: nic1
146     description: What interface to add to the HypervisorNeutronPhysicalBridge.
147     type: string
148
149   NodeCreateBatchSize:
150     default: 30
151     description: Maxiumum batch size for creating nodes
152     type: number
153
154   # Jinja loop for Role in role_data.yaml
155 {% for role in roles %}
156   # Parameters generated for {{role.name}} Role
157   {{role.name}}Services:
158     description: A list of service resources (configured in the Heat
159                  resource_registry) which represent nested stacks
160                  for each service that should get installed on the {{role.name}} role.
161     type: comma_delimited_list
162
163   {{role.name}}Count:
164     description: Number of {{role.name}} nodes to deploy
165     type: number
166     default: {{role.CountDefault|default(0)}}
167
168   {{role.name}}HostnameFormat:
169     type: string
170     description: >
171       Format for {{role.name}} node hostnames
172       Note %index% is translated into the index of the node, e.g 0/1/2 etc
173       and %stackname% is replaced with the stack name e.g overcloud
174   {% if role.HostnameFormatDefault %}
175     default: "{{role.HostnameFormatDefault}}"
176   {% else %}
177     default: "%stackname%-{{role.name.lower()}}-%index%"
178   {% endif %}
179   {{role.name}}RemovalPolicies:
180     default: []
181     type: json
182     description: >
183       List of resources to be removed from {{role.name}} ResourceGroup when
184       doing an update which requires removal of specific resources.
185       Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
186
187 {% if role.name != 'Compute' %}
188   {{role.name}}SchedulerHints:
189 {% else %}
190   NovaComputeSchedulerHints:
191 {% endif %}
192     type: json
193     description: Optional scheduler hints to pass to nova
194     default: {}
195
196   {{role.name}}Parameters:
197     type: json
198     description: Optional Role Specific parameters to be provided to service
199     default: {}
200 {% endfor %}
201
202   # Identifiers to trigger tasks on nodes
203   UpdateIdentifier:
204     default: ''
205     type: string
206     description: >
207       Setting to a previously unused value during stack-update will trigger
208       package update on all nodes
209   DeployIdentifier:
210     default: ''
211     type: string
212     description: >
213       Setting this to a unique value will re-run any deployment tasks which
214       perform configuration on a Heat stack-update.
215   AddVipsToEtcHosts:
216     default: True
217     type: boolean
218     description: >
219       Set to true to append per network Vips to /etc/hosts on each node.
220
221   DeploymentServerBlacklist:
222     default: []
223     type: comma_delimited_list
224     description: >
225       List of server hostnames to blacklist from any triggered deployments.
226
227 parameter_groups:
228 - label: deprecated
229   description: Do not use deprecated params, they will be removed.
230   parameters:
231   - controllerExtraConfig
232   - NovaComputeExtraConfig
233
234 conditions:
235   add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
236
237 resources:
238
239   VipHosts:
240     type: OS::Heat::Value
241     properties:
242       type: string
243       value:
244         list_join:
245         - "\n"
246         - - str_replace:
247               template: IP  HOST
248               params:
249                 IP: {get_attr: [VipMap, net_ip_map, external]}
250                 HOST: {get_param: CloudName}
251           - str_replace:
252               template: IP  HOST
253               params:
254                 IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
255                 HOST: {get_param: CloudNameCtlplane}
256           - str_replace:
257               template: IP  HOST
258               params:
259                 IP: {get_attr: [VipMap, net_ip_map, internal_api]}
260                 HOST: {get_param: CloudNameInternal}
261           - str_replace:
262               template: IP  HOST
263               params:
264                 IP: {get_attr: [VipMap, net_ip_map, storage]}
265                 HOST: {get_param: CloudNameStorage}
266           - str_replace:
267               template: IP  HOST
268               params:
269                 IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
270                 HOST: {get_param: CloudNameStorageManagement}
271
272   HeatAuthEncryptionKey:
273     type: OS::TripleO::RandomString
274
275   PcsdPassword:
276     type: OS::TripleO::RandomString
277     properties:
278       length: 16
279
280   HorizonSecret:
281     type: OS::TripleO::RandomString
282     properties:
283       length: 10
284
285   NetCidrMapValue:
286     type: OS::Heat::Value
287     properties:
288       type: json
289       value:
290         map_replace:
291         - map_merge:
292           - {get_attr: [Networks, net_cidr_map]}
293           - ctlplane: {get_attr: [ControlVirtualIP, subnets, 0, cidr]}
294         - keys:
295             ctlplane: {get_param: NeutronControlPlaneID}
296           values:
297             disabled: {get_attr: [ControlVirtualIP, subnets, 0, cidr]}
298
299   ServiceNetMap:
300     type: OS::TripleO::ServiceNetMap
301
302   EndpointMap:
303     type: OS::TripleO::EndpointMap
304     properties:
305       CloudEndpoints:
306         external: {get_param: CloudName}
307         internal_api: {get_param: CloudNameInternal}
308         storage: {get_param: CloudNameStorage}
309         storage_mgmt: {get_param: CloudNameStorageManagement}
310         ctlplane: {get_param: CloudNameCtlplane}
311       NetIpMap: {get_attr: [VipMap, net_ip_map]}
312       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
313
314   EndpointMapData:
315     type: OS::Heat::Value
316     properties:
317       type: json
318       value: {get_attr: [EndpointMap, endpoint_map]}
319
320   SshKnownHostsConfig:
321     type: OS::TripleO::Ssh::KnownHostsConfig
322     properties:
323       known_hosts:
324         list_join:
325           - ''
326           {% for role in roles %}
327           - {get_attr: [{{role.name}}, known_hosts_entry]}
328           {% endfor %}
329
330   # Jinja loop for Role in roles_data.yaml
331 {% for role in roles %}
332   # Resources generated for {{role.name}} Role
333   {{role.name}}ServiceChain:
334     type: OS::TripleO::Services
335     properties:
336       Services:
337         get_param: {{role.name}}Services
338       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
339       ServiceData:
340         net_cidr_map: {get_attr: [NetCidrMapValue, value]}
341       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
342       DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
343       RoleName: {{role.name}}
344       RoleParameters: {get_param: {{role.name}}Parameters}
345
346   # Lookup of role_data via heat outputs is slow, so workaround this by caching
347   # the value in an OS::Heat::Value resource
348   {{role.name}}ServiceChainRoleData:
349     type: OS::Heat::Value
350     properties:
351       type: json
352       value: {get_attr: [{{role.name}}ServiceChain, role_data]}
353
354   {{role.name}}ServiceConfigSettings:
355     type: OS::Heat::Value
356     properties:
357       type: json
358       value:
359         map_merge:
360           - get_attr: [{{role.name}}ServiceChainRoleData, value, config_settings]
361           {% for r in roles %}
362           - get_attr: [{{r.name}}ServiceChainRoleData, value, global_config_settings]
363           {% endfor %}
364           # This next step combines two yaql passes:
365           # - The inner one does a deep merge on the service_config_settings for all roles
366           # - The outer one filters the map based on the services enabled for the role
367           #   then merges the result into one map.
368           - yaql:
369               expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
370               data:
371                 map:
372                   yaql:
373                     expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
374                     data:
375                     {% for r in roles %}
376                       - get_attr: [{{r.name}}ServiceChainRoleData, value, service_config_settings]
377                     {% endfor %}
378                 services: {get_attr: [{{role.name}}ServiceNames, value]}
379
380   {{role.name}}MergedConfigSettings:
381     type: OS::Heat::Value
382     properties:
383       type: json
384       value:
385         config_settings: {}
386         global_config_settings: {}
387         service_config_settings: {}
388         merged_config_settings:
389           map_merge:
390           - get_attr: [{{role.name}}ServiceConfigSettings, value]
391           - get_param: ExtraConfig
392           {%- if role.name == 'Controller' %}
393           - map_merge:
394             - get_param: controllerExtraConfig
395             - get_param: {{role.name}}ExtraConfig
396           {%- elif role.name == 'Compute' %}
397           - map_merge:
398             - get_param: NovaComputeExtraConfig
399             - get_param: {{role.name}}ExtraConfig
400           {%- else %}
401           - get_param: {{role.name}}ExtraConfig
402           {%- endif %}
403
404   # Filter any null/None service_names which may be present due to mapping
405   # of services to OS::Heat::None
406   {{role.name}}ServiceNames:
407     type: OS::Heat::Value
408     depends_on: {{role.name}}ServiceChain
409     properties:
410       type: comma_delimited_list
411       value:
412         yaql:
413           expression: coalesce($.data, []).where($ != null)
414           data: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_names]}
415
416   {{role.name}}HostsDeployment:
417     type: OS::Heat::StructuredDeployments
418     properties:
419       name: {{role.name}}HostsDeployment
420       config: {get_attr: [hostsConfig, config_id]}
421       servers: {get_attr: [{{role.name}}Servers, value]}
422
423   {{role.name}}SshKnownHostsDeployment:
424     type: OS::Heat::StructuredDeployments
425     properties:
426       name: {{role.name}}SshKnownHostsDeployment
427       config: {get_resource: SshKnownHostsConfig}
428       servers: {get_attr: [{{role.name}}Servers, value]}
429
430   {{role.name}}AllNodesDeployment:
431     type: OS::TripleO::AllNodesDeployment
432     depends_on:
433 {% for role_inner in roles %}
434       - {{role_inner.name}}HostsDeployment
435 {% endfor %}
436     properties:
437       name: {{role.name}}AllNodesDeployment
438       config: {get_attr: [allNodesConfig, config_id]}
439       servers: {get_attr: [{{role.name}}Servers, value]}
440       input_values:
441         # Note we have to use yaql to look up the first hostname/ip in the
442         # list because heat path based attributes operate on the attribute
443         # inside the ResourceGroup, not the exposed list ref discussion in
444         # https://bugs.launchpad.net/heat/+bug/1640488
445         # The coalesce is needed because $.data is None during heat validation
446         bootstrap_nodeid:
447           yaql:
448             expression: coalesce($.data, []).first(null)
449             data: {get_attr: [{{role.name}}, hostname]}
450         bootstrap_nodeid_ip:
451           yaql:
452             expression: coalesce($.data, []).first(null)
453             data: {get_attr: [{{role.name}}, ip_address]}
454
455   {{role.name}}AllNodesValidationDeployment:
456     type: OS::Heat::StructuredDeployments
457     depends_on: {{role.name}}AllNodesDeployment
458     properties:
459       name: {{role.name}}AllNodesValidationDeployment
460       config: {get_resource: AllNodesValidationConfig}
461       servers: {get_attr: [{{role.name}}Servers, value]}
462
463   {{role.name}}IpListMap:
464     type: OS::TripleO::Network::Ports::NetIpListMap
465     properties:
466       ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
467       ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
468       InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
469       StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
470       StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
471       TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
472       ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
473       EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
474       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
475       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
476       NetworkHostnameMap: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
477
478   {{role.name}}NetworkHostnameMap:
479     type: OS::Heat::Value
480     properties:
481       type: json
482       value:
483         # Note (shardy) this somewhat complex yaql may be replaced
484         # with a map_deep_merge function in ocata.  It merges the
485         # list of maps, but appends to colliding lists so we can
486         # create a map of lists for all nodes for each network
487         yaql:
488           expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
489           data:
490             - {get_attr: [{{role.name}}, hostname_map]}
491
492   {{role.name}}:
493     type: OS::Heat::ResourceGroup
494     depends_on: Networks
495     update_policy:
496       batch_create:
497         max_batch_size: {get_param: NodeCreateBatchSize}
498     properties:
499       count: {get_param: {{role.name}}Count}
500       removal_policies: {get_param: {{role.name}}RemovalPolicies}
501       resource_def:
502         type: OS::TripleO::{{role.name}}
503         properties:
504           CloudDomain: {get_param: CloudDomain}
505           ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
506           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
507           Hostname:
508             str_replace:
509               template: {get_param: {{role.name}}HostnameFormat}
510               params:
511                 '%stackname%': {get_param: 'OS::stack_name'}
512           NodeIndex: '%index%'
513   {% if role.name != 'Compute' %}
514           {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
515   {% else %}
516           NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
517   {% endif %}
518           ServiceConfigSettings: {get_attr: [{{role.name}}ServiceConfigSettings, value]}
519           ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
520           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChainRoleData, value, monitoring_subscriptions]}
521           ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_metadata_settings]}
522           DeploymentServerBlacklistDict: {get_attr: [DeploymentServerBlacklistDict, value]}
523           RoleParameters: {get_param: {{role.name}}Parameters}
524 {% endfor %}
525
526 {% for role in roles %}
527   {{role.name}}Servers:
528     type: OS::Heat::Value
529     depends_on: {{role.name}}
530     properties:
531       type: json
532       value:
533         yaql:
534           expression: let(servers=>switch(isDict($.data.servers) => $.data.servers, true => {})) -> $servers.deleteAll($servers.keys().where($servers[$] = null))
535           data:
536             servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
537 {% endfor %}
538
539   # This is a different format to *Servers, as it creates a map of lists
540   # whereas *Servers creates a map of maps with keys of the nested resource names
541   ServerIdMap:
542     type: OS::Heat::Value
543     properties:
544       value:
545         server_ids:
546 {% for role in roles %}
547           {{role.name}}: {get_attr: [{{role.name}}, nova_server_resource]}
548 {% endfor %}
549         bootstrap_server_id:
550           yaql:
551             expression: coalesce($.data, []).first(null)
552             data: {get_attr: [{{primary_role_name}}, nova_server_resource]}
553
554   # This resource just creates a dict out of the DeploymentServerBlacklist,
555   # which is a list. The dict is used in the role templates to set a condition
556   # on whether to create the deployment resources. We can't use the list
557   # directly because there is no way to ask Heat if a list contains a specific
558   # value.
559   DeploymentServerBlacklistDict:
560     type: OS::Heat::Value
561     properties:
562       type: json
563       value:
564         map_merge:
565           repeat:
566             template:
567               hostname: 1
568             for_each:
569               hostname: {get_param: DeploymentServerBlacklist}
570
571   hostsConfig:
572     type: OS::TripleO::Hosts::SoftwareConfig
573     properties:
574       hosts:
575         list_join:
576         - "\n"
577         - - if:
578             - add_vips_to_etc_hosts
579             - {get_attr: [VipHosts, value]}
580             - ''
581         -
582 {% for role in roles %}
583           - list_join:
584             - ""
585             - {get_attr: [{{role.name}}, hosts_entry]}
586 {% endfor %}
587
588   allNodesConfig:
589     type: OS::TripleO::AllNodes::SoftwareConfig
590     properties:
591       cloud_name_external: {get_param: CloudName}
592       cloud_name_internal_api: {get_param: CloudNameInternal}
593       cloud_name_storage: {get_param: CloudNameStorage}
594       cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
595       cloud_name_ctlplane: {get_param: CloudNameCtlplane}
596       enabled_services:
597         list_join:
598           - ','
599 {% for role in roles %}
600           - {get_attr: [{{role.name}}ServiceNames, value]}
601 {% endfor %}
602       logging_groups:
603         yaql:
604           expression: >
605             $.data.groups.flatten()
606           data:
607             groups:
608 {% for role in roles %}
609               - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_groups]}
610 {% endfor %}
611       logging_sources:
612         yaql:
613           expression: >
614             $.data.sources.flatten()
615           data:
616             sources:
617 {% for role in roles %}
618               - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_sources]}
619 {% endfor %}
620       controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
621       controller_names: {get_attr: [{{primary_role_name}}, hostname]}
622       service_ips:
623         # Note (shardy) this somewhat complex yaql may be replaced
624         # with a map_deep_merge function in ocata.  It merges the
625         # list of maps, but appends to colliding lists when a service
626         # is deployed on more than one role
627         yaql:
628           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
629           data:
630             l:
631 {% for role in roles %}
632               - {get_attr: [{{role.name}}IpListMap, service_ips]}
633 {% endfor %}
634       service_node_names:
635         yaql:
636           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
637           data:
638             l:
639 {% for role in roles %}
640               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
641 {% endfor %}
642       short_service_node_names:
643         yaql:
644           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
645           data:
646             l:
647 {% for role in roles %}
648               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
649 {% endfor %}
650       short_service_bootstrap_node:
651         yaql:
652           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
653           data:
654             l:
655 {% for role in roles %}
656               - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
657 {% endfor %}
658       NetVipMap: {get_attr: [VipMap, net_ip_map]}
659       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
660       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
661       DeployIdentifier: {get_param: DeployIdentifier}
662       UpdateIdentifier: {get_param: UpdateIdentifier}
663
664   MysqlRootPassword:
665     type: OS::TripleO::RandomString
666     properties:
667       length: 10
668
669   RabbitCookie:
670     type: OS::TripleO::RandomString
671     properties:
672       length: 20
673       salt: {get_param: RabbitCookieSalt}
674
675   DefaultPasswords:
676     type: OS::TripleO::DefaultPasswords
677     properties:
678       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
679       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
680       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
681       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
682       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
683
684   # creates the network architecture
685   Networks:
686     type: OS::TripleO::Network
687
688   ControlVirtualIP:
689     type: OS::TripleO::Network::Ports::ControlPlaneVipPort
690     depends_on: Networks
691     properties:
692       name: control_virtual_ip
693       network: {get_param: NeutronControlPlaneID}
694       fixed_ips: {get_param: ControlFixedIPs}
695       replacement_policy: AUTO
696
697   RedisVirtualIP:
698     depends_on: Networks
699     type: OS::TripleO::Network::Ports::RedisVipPort
700     properties:
701       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
702       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
703       PortName: redis_virtual_ip
704       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
705       ServiceName: redis
706       FixedIPs: {get_param: RedisVirtualFixedIPs}
707
708   # The public VIP is on the External net, falls back to ctlplane
709   PublicVirtualIP:
710     depends_on: Networks
711     type: OS::TripleO::Network::Ports::ExternalVipPort
712     properties:
713       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
714       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
715       PortName: public_virtual_ip
716       FixedIPs: {get_param: PublicVirtualFixedIPs}
717
718   InternalApiVirtualIP:
719     depends_on: Networks
720     type: OS::TripleO::Network::Ports::InternalApiVipPort
721     properties:
722       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
723       PortName: internal_api_virtual_ip
724       FixedIPs: {get_param: InternalApiVirtualFixedIPs}
725
726   StorageVirtualIP:
727     depends_on: Networks
728     type: OS::TripleO::Network::Ports::StorageVipPort
729     properties:
730       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
731       PortName: storage_virtual_ip
732       FixedIPs: {get_param: StorageVirtualFixedIPs}
733
734   StorageMgmtVirtualIP:
735     depends_on: Networks
736     type: OS::TripleO::Network::Ports::StorageMgmtVipPort
737     properties:
738       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
739       PortName: storage_management_virtual_ip
740       FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
741
742   VipMap:
743     type: OS::TripleO::Network::Ports::NetVipMap
744     properties:
745       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
746       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
747       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
748       InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
749       InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
750       StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
751       StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
752       StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
753       StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
754       # No tenant or management VIP required
755     # Because of nested get_attr functions in the KeystoneAdminVip output, we
756     # can't determine which attributes of VipMap are used until after
757     # ServiceNetMap's attribute values are available.
758     depends_on: ServiceNetMap
759
760   # All Nodes Validations
761   AllNodesValidationConfig:
762     type: OS::TripleO::AllNodes::Validation
763     properties:
764       PingTestIps:
765         list_join:
766         - ' '
767         - - yaql:
768               expression: coalesce($.data, []).first(null)
769               data: {get_attr: [{{primary_role_name}}, external_ip_address]}
770           - yaql:
771               expression: coalesce($.data, []).first(null)
772               data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
773           - yaql:
774               expression: coalesce($.data, []).first(null)
775               data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
776           - yaql:
777               expression: coalesce($.data, []).first(null)
778               data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
779           - yaql:
780               expression: coalesce($.data, []).first(null)
781               data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
782           - yaql:
783               expression: coalesce($.data, []).first(null)
784               data: {get_attr: [{{primary_role_name}}, management_ip_address]}
785
786   UpdateWorkflow:
787     type: OS::TripleO::Tasks::UpdateWorkflow
788     depends_on:
789 {% for role in roles %}
790       - {{role.name}}AllNodesDeployment
791 {% endfor %}
792     properties:
793       servers:
794 {% for role in roles %}
795         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
796 {% endfor %}
797       input_values:
798         deploy_identifier: {get_param: DeployIdentifier}
799         update_identifier: {get_param: UpdateIdentifier}
800
801   # Optional ExtraConfig for all nodes - all roles are passed in here, but
802   # the nested template may configure each role differently (or not at all)
803   AllNodesExtraConfig:
804     type: OS::TripleO::AllNodesExtraConfig
805     depends_on:
806       - UpdateWorkflow
807 {% for role in roles %}
808       - {{role.name}}AllNodesValidationDeployment
809 {% endfor %}
810     properties:
811       servers:
812 {% for role in roles %}
813         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
814 {% endfor %}
815
816   # Post deployment steps for all roles
817   AllNodesDeploySteps:
818     type: OS::TripleO::PostDeploySteps
819     depends_on:
820       - AllNodesExtraConfig
821 {% for role in roles %}
822       - {{role.name}}AllNodesDeployment
823 {% endfor %}
824     properties:
825       servers:
826 {% for role in roles %}
827         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
828 {% endfor %}
829       stack_name: {get_param: 'OS::stack_name'}
830       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
831       ctlplane_service_ips:
832         # Note (shardy) this somewhat complex yaql may be replaced
833         # with a map_deep_merge function in ocata.  It merges the
834         # list of maps, but appends to colliding lists when a service
835         # is deployed on more than one role
836         yaql:
837           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
838           data:
839             l:
840 {% for role in roles %}
841               - {get_attr: [{{role.name}}IpListMap, ctlplane_service_ips]}
842 {% endfor %}
843       role_data:
844 {% for role in roles %}
845         {{role.name}}:
846           map_merge:
847           - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
848           - {get_attr: [{{role.name}}MergedConfigSettings, value]}
849 {% endfor %}
850
851   ServerOsCollectConfigData:
852     type: OS::Heat::Value
853     properties:
854       type: json
855       value:
856 {% for role in roles %}
857         {{role.name}}: {get_attr: [{{role.name}}, attributes, os_collect_config]}
858 {% endfor %}
859
860   DeployedServerEnvironment:
861     type: OS::TripleO::DeployedServerEnvironment
862     properties:
863       RoleCounts:
864 {% for role in roles %}
865         {{role.name}}DeployedServerCount: {get_param: {{role.name}}Count}
866 {% endfor %}
867       VipMap:
868         map_merge:
869           - {get_attr: [VipMap, net_ip_map]}
870           - redis: {get_attr: [RedisVirtualIP, ip_address]}
871       DeployedServerPortMap:
872         map_merge:
873           list_concat:
874 {% for role in roles %}
875               - {get_attr: [{{role.name}}, deployed_server_port_map]}
876 {% endfor %}
877       DeployedServerDeploymentSwiftDataMap:
878         map_merge:
879           list_concat:
880 {% for role in roles %}
881               - {get_attr: [{{role.name}}, deployed_server_deployment_swift_data_map]}
882 {% endfor %}
883       DefaultRouteIp:
884         str_split:
885           - ':'
886           - str_split:
887             - '/'
888             - {get_attr: [ServerOsCollectConfigData, value, {{primary_role_name}}, '0', request, metadata_url]}
889             - 2
890           - 0
891
892 outputs:
893   ManagedEndpoints:
894     description: Asserts that the keystone endpoints have been provisioned.
895     value: true
896   KeystoneURL:
897     description: URL for the Overcloud Keystone service
898     value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
899   KeystoneAdminVip:
900     description: Keystone Admin VIP endpoint
901     # Note that these nested get_attr functions require a dependency
902     # relationship between VipMap and ServiceNetMap, since we can't determine
903     # which attributes of VipMap are used until after ServiceNetMap's attribute
904     # values are available. If this is ever reworked to not use nested
905     # get_attr, that dependency can be removed.
906     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
907   EndpointMap:
908     description: |
909       Mapping of the resources with the needed info for their endpoints.
910       This includes the protocol used, the IP, port and also a full
911       representation of the URI.
912     value: {get_attr: [EndpointMapData, value]}
913   HostsEntry:
914     description: |
915       The content that should be appended to your /etc/hosts if you want to get
916       hostname-based access to the deployed nodes (useful for testing without
917       setting up a DNS).
918     value:
919       list_join:
920       - "\n"
921       - - {get_attr: [hostsConfig, hosts_entries]}
922       - - {get_attr: [VipHosts, value]}
923   EnabledServices:
924     description: The services enabled on each role
925     value:
926 {% for role in roles %}
927       {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
928 {% endfor %}
929   RoleData:
930     description: The configuration data associated with each role
931     value:
932 {% for role in roles %}
933       {{role.name}}:
934         map_merge:
935         - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
936         - {get_attr: [{{role.name}}MergedConfigSettings, value]}
937 {% endfor %}
938   RoleNetIpMap:
939     description: Mapping of each network to a list of IPs for each role
940     value:
941 {% for role in roles %}
942       {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}
943 {% endfor %}
944   RoleNetHostnameMap:
945     description: Mapping of each network to a list of hostnames for each role
946     value:
947 {% for role in roles %}
948       {{role.name}}: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
949 {% endfor %}
950   ServerOsCollectConfigData:
951     description: The os-collect-config configuration associated with each server resource
952     value: {get_attr: [ServerOsCollectConfigData, value]}
953   VipMap:
954     description: Mapping of each network to VIP addresses. Also includes the Redis VIP.
955     value:
956       map_merge:
957         - {get_attr: [VipMap, net_ip_map]}
958         - redis: {get_attr: [RedisVirtualIP, ip_address]}
959   ServerIdData:
960     description: Mapping of each role to a list of nova server IDs and the bootstrap ID
961     value: {get_attr: [ServerIdMap, value]}
962   DeployedServerEnvironment:
963     description:
964       Environment data that can be used as input into the services stack when
965       using split-stack.
966     value: {get_attr: [DeployedServerEnvironment, deployed_server_environment]}