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