Add role specific information to the service template
[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: ocata
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   InternalApiVirtualFixedIPs:
59     default: []
60     description: >
61         Control the IP allocation for the InternalApiVirtualInterface port. E.g.
62         [{'ip_address':'1.2.3.4'}]
63     type: json
64   NeutronControlPlaneID:
65     default: 'ctlplane'
66     type: string
67     description: Neutron ID or name for ctlplane network.
68   NeutronPublicInterface:
69     default: nic1
70     description: What interface to bridge onto br-ex for network nodes.
71     type: string
72   PublicVirtualFixedIPs:
73     default: []
74     description: >
75         Control the IP allocation for the PublicVirtualInterface port. E.g.
76         [{'ip_address':'1.2.3.4'}]
77     type: json
78   RabbitCookieSalt:
79     type: string
80     default: unset
81     description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
82   StorageVirtualFixedIPs:
83     default: []
84     description: >
85         Control the IP allocation for the StorageVirtualInterface port. E.g.
86         [{'ip_address':'1.2.3.4'}]
87     type: json
88   StorageMgmtVirtualFixedIPs:
89     default: []
90     description: >
91         Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
92         [{'ip_address':'1.2.3.4'}]
93     type: json
94   RedisVirtualFixedIPs:
95     default: []
96     description: >
97         Control the IP allocation for the virtual IP used by Redis. E.g.
98         [{'ip_address':'1.2.3.4'}]
99     type: json
100   CloudDomain:
101     default: 'localdomain'
102     type: string
103     description: >
104       The DNS domain used for the hosts. This should match the dhcp_domain
105       configured in the Undercloud neutron. Defaults to localdomain.
106   ServerMetadata:
107     default: {}
108     description: >
109       Extra properties or metadata passed to Nova for the created nodes in
110       the overcloud. It's accessible via the Nova metadata API.
111     type: json
112
113 # Compute-specific params
114 # FIXME(shardy) handle these deprecated names as they don't match compute.yaml
115   HypervisorNeutronPhysicalBridge:
116     default: 'br-ex'
117     description: >
118       An OVS bridge to create on each hypervisor. This defaults to br-ex the
119       same as the control plane nodes, as we have a uniform configuration of
120       the openvswitch agent. Typically should not need to be changed.
121     type: string
122   HypervisorNeutronPublicInterface:
123     default: nic1
124     description: What interface to add to the HypervisorNeutronPhysicalBridge.
125     type: string
126
127   NodeCreateBatchSize:
128     default: 30
129     description: Maxiumum batch size for creating nodes
130     type: number
131
132   # Jinja loop for Role in role_data.yaml
133 {% for role in roles %}
134   # Parameters generated for {{role.name}} Role
135   {{role.name}}Services:
136     description: A list of service resources (configured in the Heat
137                  resource_registry) which represent nested stacks
138                  for each service that should get installed on the {{role.name}} role.
139     type: comma_delimited_list
140
141   {{role.name}}Count:
142     description: Number of {{role.name}} nodes to deploy
143     type: number
144     default: {{role.CountDefault|default(0)}}
145
146   {{role.name}}HostnameFormat:
147     type: string
148     description: >
149       Format for {{role.name}} node hostnames
150       Note %index% is translated into the index of the node, e.g 0/1/2 etc
151       and %stackname% is replaced with the stack name e.g overcloud
152   {% if role.HostnameFormatDefault %}
153     default: "{{role.HostnameFormatDefault}}"
154   {% else %}
155     default: "%stackname%-{{role.name.lower()}}-%index%"
156   {% endif %}
157
158   {{role.name}}RemovalPolicies:
159     default: []
160     type: json
161     description: >
162       List of resources to be removed from {{role.name}} ResourceGroup when
163       doing an update which requires removal of specific resources.
164       Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
165
166 {% if role.name != 'Compute' %}
167   {{role.name}}SchedulerHints:
168 {% else %}
169   NovaComputeSchedulerHints:
170 {% endif %}
171     type: json
172     description: Optional scheduler hints to pass to nova
173     default: {}
174
175   {{role.name}}Parameters:
176     type: json
177     description: Optional Role Specific parameters to be provided to service
178     default: {}
179 {% endfor %}
180
181   # Identifiers to trigger tasks on nodes
182   UpdateIdentifier:
183     default: ''
184     type: string
185     description: >
186       Setting to a previously unused value during stack-update will trigger
187       package update on all nodes
188   DeployIdentifier:
189     default: ''
190     type: string
191     description: >
192       Setting this to a unique value will re-run any deployment tasks which
193       perform configuration on a Heat stack-update.
194   AddVipsToEtcHosts:
195     default: True
196     type: boolean
197     description: >
198       Set to true to append per network Vips to /etc/hosts on each node.
199
200 conditions:
201   add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
202
203 resources:
204
205   VipHosts:
206     type: OS::Heat::Value
207     properties:
208       type: string
209       value:
210         list_join:
211         - "\n"
212         - - str_replace:
213               template: IP  HOST
214               params:
215                 IP: {get_attr: [VipMap, net_ip_map, external]}
216                 HOST: {get_param: CloudName}
217           - str_replace:
218               template: IP  HOST
219               params:
220                 IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
221                 HOST: {get_param: CloudNameCtlplane}
222           - str_replace:
223               template: IP  HOST
224               params:
225                 IP: {get_attr: [VipMap, net_ip_map, internal_api]}
226                 HOST: {get_param: CloudNameInternal}
227           - str_replace:
228               template: IP  HOST
229               params:
230                 IP: {get_attr: [VipMap, net_ip_map, storage]}
231                 HOST: {get_param: CloudNameStorage}
232           - str_replace:
233               template: IP  HOST
234               params:
235                 IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
236                 HOST: {get_param: CloudNameStorageManagement}
237
238   HeatAuthEncryptionKey:
239     type: OS::Heat::RandomString
240
241   PcsdPassword:
242     type: OS::Heat::RandomString
243     properties:
244       length: 16
245
246   HorizonSecret:
247     type: OS::Heat::RandomString
248     properties:
249       length: 10
250
251   ServiceNetMap:
252     type: OS::TripleO::ServiceNetMap
253
254   EndpointMap:
255     type: OS::TripleO::EndpointMap
256     properties:
257       CloudEndpoints:
258         external: {get_param: CloudName}
259         internal_api: {get_param: CloudNameInternal}
260         storage: {get_param: CloudNameStorage}
261         storage_mgmt: {get_param: CloudNameStorageManagement}
262         ctlplane: {get_param: CloudNameCtlplane}
263       NetIpMap: {get_attr: [VipMap, net_ip_map]}
264       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
265
266   EndpointMapData:
267     type: OS::Heat::Value
268     properties:
269       type: json
270       value: {get_attr: [EndpointMap, endpoint_map]}
271
272   SshKnownHostsConfig:
273     type: OS::TripleO::Ssh::KnownHostsConfig
274     properties:
275       known_hosts:
276         list_join:
277           - ''
278           {% for role in roles %}
279           - {get_attr: [{{role.name}}, known_hosts_entry]}
280           {% endfor %}
281
282   # Jinja loop for Role in roles_data.yaml
283 {% for role in roles %}
284   # Resources generated for {{role.name}} Role
285   {{role.name}}ServiceChain:
286     type: OS::TripleO::Services
287     properties:
288       Services:
289         get_param: {{role.name}}Services
290       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
291       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
292       DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
293       RoleName: {{role.name}}
294       RoleParameters: {get_param: {{role.name}}Parameters}
295
296   # Filter any null/None service_names which may be present due to mapping
297   # of services to OS::Heat::None
298   {{role.name}}ServiceNames:
299     type: OS::Heat::Value
300     depends_on: {{role.name}}ServiceChain
301     properties:
302       type: comma_delimited_list
303       value:
304         yaql:
305           expression: coalesce($.data, []).where($ != null)
306           data: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
307
308   {{role.name}}HostsDeployment:
309     type: OS::Heat::StructuredDeployments
310     properties:
311       name: {{role.name}}HostsDeployment
312       config: {get_attr: [hostsConfig, config_id]}
313       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
314
315   {{role.name}}SshKnownHostsDeployment:
316     type: OS::Heat::StructuredDeployments
317     properties:
318       name: {{role.name}}SshKnownHostsDeployment
319       config: {get_resource: SshKnownHostsConfig}
320       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
321
322   {{role.name}}AllNodesDeployment:
323     type: OS::Heat::StructuredDeployments
324     depends_on:
325 {% for role_inner in roles %}
326       - {{role_inner.name}}HostsDeployment
327 {% endfor %}
328     properties:
329       name: {{role.name}}AllNodesDeployment
330       config: {get_attr: [allNodesConfig, config_id]}
331       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
332       input_values:
333         # Note we have to use yaql to look up the first hostname/ip in the
334         # list because heat path based attributes operate on the attribute
335         # inside the ResourceGroup, not the exposed list ref discussion in
336         # https://bugs.launchpad.net/heat/+bug/1640488
337         # The coalesce is needed because $.data is None during heat validation
338         bootstrap_nodeid:
339           yaql:
340             expression: coalesce($.data, []).first(null)
341             data: {get_attr: [{{role.name}}, hostname]}
342         bootstrap_nodeid_ip:
343           yaql:
344             expression: coalesce($.data, []).first(null)
345             data: {get_attr: [{{role.name}}, ip_address]}
346
347   {{role.name}}AllNodesValidationDeployment:
348     type: OS::Heat::StructuredDeployments
349     depends_on: {{role.name}}AllNodesDeployment
350     properties:
351       name: {{role.name}}AllNodesValidationDeployment
352       config: {get_resource: AllNodesValidationConfig}
353       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
354
355   {{role.name}}IpListMap:
356     type: OS::TripleO::Network::Ports::NetIpListMap
357     properties:
358       ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
359       ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
360       InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
361       StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
362       StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
363       TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
364       ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
365       EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
366       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
367       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
368       NetworkHostnameMap:
369         # Note (shardy) this somewhat complex yaql may be replaced
370         # with a map_deep_merge function in ocata.  It merges the
371         # list of maps, but appends to colliding lists so we can
372         # create a map of lists for all nodes for each network
373         yaql:
374           expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
375           data:
376             - {get_attr: [{{role.name}}, hostname_map]}
377
378   {{role.name}}:
379     type: OS::Heat::ResourceGroup
380     depends_on: Networks
381     update_policy:
382       batch_create:
383         max_batch_size: {get_param: NodeCreateBatchSize}
384     properties:
385       count: {get_param: {{role.name}}Count}
386       removal_policies: {get_param: {{role.name}}RemovalPolicies}
387       resource_def:
388         type: OS::TripleO::{{role.name}}
389         properties:
390           CloudDomain: {get_param: CloudDomain}
391           ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
392           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
393           Hostname:
394             str_replace:
395               template: {get_param: {{role.name}}HostnameFormat}
396               params:
397                 '%stackname%': {get_param: 'OS::stack_name'}
398           NodeIndex: '%index%'
399   {% if role.name != 'Compute' %}
400           {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
401   {% else %}
402           NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
403   {% endif %}
404           ServiceConfigSettings:
405             map_merge:
406               -  get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
407           {% for r in roles %}
408               - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
409           {% endfor %}
410               # This next step combines two yaql passes:
411               # - The inner one does a deep merge on the service_config_settings for all roles
412               # - The outer one filters the map based on the services enabled for the role
413               #   then merges the result into one map.
414               - yaql:
415                   expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
416                   data:
417                     map:
418                       yaql:
419                         expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
420                         data:
421                         {% for r in roles %}
422                           - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
423                         {% endfor %}
424                     services: {get_attr: [{{role.name}}ServiceNames, value]}
425           ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
426           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
427           ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChain, role_data, service_metadata_settings]}
428 {% endfor %}
429
430   hostsConfig:
431     type: OS::TripleO::Hosts::SoftwareConfig
432     properties:
433       hosts:
434         list_join:
435         - "\n"
436         - - if:
437             - add_vips_to_etc_hosts
438             - {get_attr: [VipHosts, value]}
439             - ''
440         -
441 {% for role in roles %}
442           - list_join:
443             - ""
444             - {get_attr: [{{role.name}}, hosts_entry]}
445 {% endfor %}
446
447   allNodesConfig:
448     type: OS::TripleO::AllNodes::SoftwareConfig
449     properties:
450       cloud_name_external: {get_param: CloudName}
451       cloud_name_internal_api: {get_param: CloudNameInternal}
452       cloud_name_storage: {get_param: CloudNameStorage}
453       cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
454       cloud_name_ctlplane: {get_param: CloudNameCtlplane}
455       enabled_services:
456         list_join:
457           - ','
458 {% for role in roles %}
459           - {get_attr: [{{role.name}}ServiceNames, value]}
460 {% endfor %}
461       logging_groups:
462         yaql:
463           expression: >
464             $.data.groups.flatten()
465           data:
466             groups:
467 {% for role in roles %}
468               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
469 {% endfor %}
470       logging_sources:
471         yaql:
472           expression: >
473             $.data.sources.flatten()
474           data:
475             sources:
476 {% for role in roles %}
477               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
478 {% endfor %}
479       controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
480       controller_names: {get_attr: [{{primary_role_name}}, hostname]}
481       service_ips:
482         # Note (shardy) this somewhat complex yaql may be replaced
483         # with a map_deep_merge function in ocata.  It merges the
484         # list of maps, but appends to colliding lists when a service
485         # is deployed on more than one role
486         yaql:
487           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
488           data:
489             l:
490 {% for role in roles %}
491               - {get_attr: [{{role.name}}IpListMap, service_ips]}
492 {% endfor %}
493       service_node_names:
494         yaql:
495           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
496           data:
497             l:
498 {% for role in roles %}
499               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
500 {% endfor %}
501       short_service_node_names:
502         yaql:
503           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
504           data:
505             l:
506 {% for role in roles %}
507               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
508 {% endfor %}
509       short_service_bootstrap_node:
510         yaql:
511           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
512           data:
513             l:
514 {% for role in roles %}
515               - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
516 {% endfor %}
517       # FIXME(shardy): These require further work to move into service_ips
518       memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
519       NetVipMap: {get_attr: [VipMap, net_ip_map]}
520       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
521       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
522       DeployIdentifier: {get_param: DeployIdentifier}
523       UpdateIdentifier: {get_param: UpdateIdentifier}
524
525   MysqlRootPassword:
526     type: OS::Heat::RandomString
527     properties:
528       length: 10
529
530   RabbitCookie:
531     type: OS::Heat::RandomString
532     properties:
533       length: 20
534       salt: {get_param: RabbitCookieSalt}
535
536   DefaultPasswords:
537     type: OS::TripleO::DefaultPasswords
538     properties:
539       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
540       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
541       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
542       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
543       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
544
545   # creates the network architecture
546   Networks:
547     type: OS::TripleO::Network
548
549   ControlVirtualIP:
550     type: OS::TripleO::Network::Ports::ControlPlaneVipPort
551     depends_on: Networks
552     properties:
553       name: control_virtual_ip
554       network: {get_param: NeutronControlPlaneID}
555       fixed_ips: {get_param: ControlFixedIPs}
556       replacement_policy: AUTO
557
558   RedisVirtualIP:
559     depends_on: Networks
560     type: OS::TripleO::Network::Ports::RedisVipPort
561     properties:
562       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
563       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
564       PortName: redis_virtual_ip
565       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
566       ServiceName: redis
567       FixedIPs: {get_param: RedisVirtualFixedIPs}
568
569   # The public VIP is on the External net, falls back to ctlplane
570   PublicVirtualIP:
571     depends_on: Networks
572     type: OS::TripleO::Network::Ports::ExternalVipPort
573     properties:
574       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
575       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
576       PortName: public_virtual_ip
577       FixedIPs: {get_param: PublicVirtualFixedIPs}
578
579   InternalApiVirtualIP:
580     depends_on: Networks
581     type: OS::TripleO::Network::Ports::InternalApiVipPort
582     properties:
583       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
584       PortName: internal_api_virtual_ip
585       FixedIPs: {get_param: InternalApiVirtualFixedIPs}
586
587   StorageVirtualIP:
588     depends_on: Networks
589     type: OS::TripleO::Network::Ports::StorageVipPort
590     properties:
591       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
592       PortName: storage_virtual_ip
593       FixedIPs: {get_param: StorageVirtualFixedIPs}
594
595   StorageMgmtVirtualIP:
596     depends_on: Networks
597     type: OS::TripleO::Network::Ports::StorageMgmtVipPort
598     properties:
599       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
600       PortName: storage_management_virtual_ip
601       FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
602
603   VipMap:
604     type: OS::TripleO::Network::Ports::NetVipMap
605     properties:
606       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
607       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
608       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
609       InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
610       InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
611       StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
612       StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
613       StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
614       StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
615       # No tenant or management VIP required
616
617   # All Nodes Validations
618   AllNodesValidationConfig:
619     type: OS::TripleO::AllNodes::Validation
620     properties:
621       PingTestIps:
622         list_join:
623         - ' '
624         - - yaql:
625               expression: coalesce($.data, []).first(null)
626               data: {get_attr: [{{primary_role_name}}, external_ip_address]}
627           - yaql:
628               expression: coalesce($.data, []).first(null)
629               data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
630           - yaql:
631               expression: coalesce($.data, []).first(null)
632               data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
633           - yaql:
634               expression: coalesce($.data, []).first(null)
635               data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
636           - yaql:
637               expression: coalesce($.data, []).first(null)
638               data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
639           - yaql:
640               expression: coalesce($.data, []).first(null)
641               data: {get_attr: [{{primary_role_name}}, management_ip_address]}
642
643   UpdateWorkflow:
644     type: OS::TripleO::Tasks::UpdateWorkflow
645     depends_on:
646 {% for role in roles %}
647       - {{role.name}}AllNodesDeployment
648 {% endfor %}
649     properties:
650       servers:
651 {% for role in roles %}
652         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
653 {% endfor %}
654       input_values:
655         deploy_identifier: {get_param: DeployIdentifier}
656         update_identifier: {get_param: UpdateIdentifier}
657
658   # Optional ExtraConfig for all nodes - all roles are passed in here, but
659   # the nested template may configure each role differently (or not at all)
660   AllNodesExtraConfig:
661     type: OS::TripleO::AllNodesExtraConfig
662     depends_on:
663       - UpdateWorkflow
664 {% for role in roles %}
665       - {{role.name}}AllNodesValidationDeployment
666 {% endfor %}
667     properties:
668       servers:
669 {% for role in roles %}
670         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
671 {% endfor %}
672
673   # Post deployment steps for all roles
674   AllNodesDeploySteps:
675     type: OS::TripleO::PostDeploySteps
676     depends_on:
677       - AllNodesExtraConfig
678 {% for role in roles %}
679       - {{role.name}}AllNodesDeployment
680 {% endfor %}
681     properties:
682       servers:
683 {% for role in roles %}
684         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
685 {% endfor %}
686       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
687       role_data:
688 {% for role in roles %}
689         {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
690 {% endfor %}
691
692 outputs:
693   ManagedEndpoints:
694     description: Asserts that the keystone endpoints have been provisioned.
695     value: true
696   KeystoneURL:
697     description: URL for the Overcloud Keystone service
698     value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
699   KeystoneAdminVip:
700     description: Keystone Admin VIP endpoint
701     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
702   EndpointMap:
703     description: |
704       Mapping of the resources with the needed info for their endpoints.
705       This includes the protocol used, the IP, port and also a full
706       representation of the URI.
707     value: {get_attr: [EndpointMapData, value]}
708   HostsEntry:
709     description: |
710       The content that should be appended to your /etc/hosts if you want to get
711       hostname-based access to the deployed nodes (useful for testing without
712       setting up a DNS).
713     value:
714       list_join:
715       - "\n"
716       - - {get_attr: [hostsConfig, hosts_entries]}
717       - - {get_attr: [VipHosts, value]}
718   EnabledServices:
719     description: The services enabled on each role
720     value:
721 {% for role in roles %}
722       {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
723 {% endfor %}
724   RoleData:
725     description: The configuration data associated with each role
726     value:
727 {% for role in roles %}
728       {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
729 {% endfor %}
730   RoleNetIpMap:
731     description: Mapping of each network to a list of IPs for each role
732     value:
733 {% for role in roles %}
734       {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}
735 {% endfor %}