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