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