Change flat network name for nosdn fdio scenario
[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       cellv2_discovery_hosts:
646         # Collects compute hostnames for all roles with a service that requires cellv2 host discovery
647         list_join:
648           - ','
649           - yaql:
650               expression: coalesce($.data.e.zip($.data.l).where($[0]).select($[1]).flatten(),  [])
651               data:
652                 e: # list of true/fails for whether cellsv2 host discovery is required for the roles
653 {%- for role in roles %}
654                   - {get_attr: [{{role.name}}ServiceChainRoleData, value, cellv2_discovery]}
655 {%- endfor %}
656                 l: # list of list of compute hostnames for the roles
657 {%- for role in roles %}
658                   - {get_attr: [{{role.name}}, hostname_map, canonical]}
659 {%- endfor %}
660       controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
661       controller_names: {get_attr: [{{primary_role_name}}, hostname]}
662       service_ips:
663         # Note (shardy) this somewhat complex yaql may be replaced
664         # with a map_deep_merge function in ocata.  It merges the
665         # list of maps, but appends to colliding lists when a service
666         # is deployed on more than one role
667         yaql:
668           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
669           data:
670             l:
671 {% for role in roles %}
672               - {get_attr: [{{role.name}}IpListMap, service_ips]}
673 {% endfor %}
674       service_node_names:
675         yaql:
676           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
677           data:
678             l:
679 {% for role in roles %}
680               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
681 {% endfor %}
682       short_service_node_names:
683         yaql:
684           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
685           data:
686             l:
687 {% for role in roles %}
688               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
689 {% endfor %}
690       short_service_bootstrap_node:
691         yaql:
692           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
693           data:
694             l:
695 {% for role in roles %}
696               - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
697 {% endfor %}
698       NetVipMap: {get_attr: [VipMap, net_ip_map]}
699       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
700       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
701       DeployIdentifier: {get_param: DeployIdentifier}
702       UpdateIdentifier: {get_param: UpdateIdentifier}
703
704   MysqlRootPassword:
705     type: OS::TripleO::RandomString
706     properties:
707       length: 10
708
709   RabbitCookie:
710     type: OS::TripleO::RandomString
711     properties:
712       length: 20
713       salt: {get_param: RabbitCookieSalt}
714
715   DefaultPasswords:
716     type: OS::TripleO::DefaultPasswords
717     properties:
718       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
719       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
720       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
721       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
722       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
723
724   # creates the network architecture
725   Networks:
726     type: OS::TripleO::Network
727
728   ControlVirtualIP:
729     type: OS::TripleO::Network::Ports::ControlPlaneVipPort
730     depends_on: Networks
731     properties:
732       name: control_virtual_ip
733       network: {get_param: NeutronControlPlaneID}
734       fixed_ips: {get_param: ControlFixedIPs}
735       replacement_policy: AUTO
736
737   RedisVirtualIP:
738     depends_on: Networks
739     type: OS::TripleO::Network::Ports::RedisVipPort
740     properties:
741       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
742       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
743       PortName: redis_virtual_ip
744       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
745       ServiceName: redis
746       FixedIPs: {get_param: RedisVirtualFixedIPs}
747
748 {%- for network in networks if network.vip|default(false) %}
749 {%- if network.name == 'External' %}
750   # The public VIP is on the External net, falls back to ctlplane
751   PublicVirtualIP:
752     depends_on: Networks
753     type: OS::TripleO::Network::Ports::ExternalVipPort
754     properties:
755       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
756       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
757       PortName: public_virtual_ip
758       FixedIPs: {get_param: PublicVirtualFixedIPs}
759 {%- elif network.name == 'StorageMgmt' %}
760   {{network.name}}VirtualIP:
761     depends_on: Networks
762     type: OS::TripleO::Network::Ports::{{network.name}}VipPort
763     properties:
764       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
765       PortName: storage_management_virtual_ip
766       FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
767 {%- else %}
768   {{network.name}}VirtualIP:
769     depends_on: Networks
770     type: OS::TripleO::Network::Ports::{{network.name}}VipPort
771     properties:
772       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
773       PortName: {{network.name_lower}}_virtual_ip
774       FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
775 {%- endif %}
776 {%- endfor %}
777
778   VipMap:
779     type: OS::TripleO::Network::Ports::NetVipMap
780     properties:
781       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
782 {%- for network in networks if network.vip|default(false) %}
783 {%- if network.name == 'External' %}
784       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
785       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
786 {%- else %}
787       {{network.name}}Ip: {get_attr: [{{network.name}}VirtualIP, ip_address]}
788       {{network.name}}IpUri: {get_attr: [{{network.name}}VirtualIP, ip_address_uri]}
789 {%- endif %}
790 {%- endfor %}
791       # No tenant or management VIP required
792     # Because of nested get_attr functions in the KeystoneAdminVip output, we
793     # can't determine which attributes of VipMap are used until after
794     # ServiceNetMap's attribute values are available.
795     depends_on: ServiceNetMap
796
797   # All Nodes Validations
798   AllNodesValidationConfig:
799     type: OS::TripleO::AllNodes::Validation
800     properties:
801       PingTestIps:
802         list_join:
803         - ' '
804         -
805 {%- for network in networks if network.enabled|default(true) %}
806           - yaql:
807               expression: coalesce($.data, []).first(null)
808               data: {get_attr: [{{primary_role_name}}, {{network.name_lower}}_ip_address]}
809 {%- endfor %}
810
811   UpdateWorkflow:
812     type: OS::TripleO::Tasks::UpdateWorkflow
813     depends_on:
814 {% for role in roles %}
815       - {{role.name}}AllNodesDeployment
816 {% endfor %}
817     properties:
818       servers:
819 {% for role in roles %}
820         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
821 {% endfor %}
822       input_values:
823         deploy_identifier: {get_param: DeployIdentifier}
824         update_identifier: {get_param: UpdateIdentifier}
825
826   # Optional ExtraConfig for all nodes - all roles are passed in here, but
827   # the nested template may configure each role differently (or not at all)
828   AllNodesExtraConfig:
829     type: OS::TripleO::AllNodesExtraConfig
830     depends_on:
831       - UpdateWorkflow
832 {% for role in roles %}
833       - {{role.name}}AllNodesValidationDeployment
834 {% endfor %}
835     properties:
836       servers:
837 {% for role in roles %}
838         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
839 {% endfor %}
840
841   # Post deployment steps for all roles
842   AllNodesDeploySteps:
843     type: OS::TripleO::PostDeploySteps
844     depends_on:
845       - AllNodesExtraConfig
846 {% for role in roles %}
847       - {{role.name}}AllNodesDeployment
848 {% endfor %}
849     properties:
850       servers:
851 {% for role in roles %}
852         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
853 {% endfor %}
854       stack_name: {get_param: 'OS::stack_name'}
855       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
856       ctlplane_service_ips:
857         # Note (shardy) this somewhat complex yaql may be replaced
858         # with a map_deep_merge function in ocata.  It merges the
859         # list of maps, but appends to colliding lists when a service
860         # is deployed on more than one role
861         yaql:
862           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
863           data:
864             l:
865 {% for role in roles %}
866               - {get_attr: [{{role.name}}IpListMap, ctlplane_service_ips]}
867 {% endfor %}
868       role_data:
869 {% for role in roles %}
870         {{role.name}}:
871           map_merge:
872           - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
873           - {get_attr: [{{role.name}}MergedConfigSettings, value]}
874 {% endfor %}
875
876   ServerOsCollectConfigData:
877     type: OS::Heat::Value
878     properties:
879       type: json
880       value:
881 {% for role in roles %}
882         {{role.name}}: {get_attr: [{{role.name}}, attributes, os_collect_config]}
883 {% endfor %}
884
885   DeployedServerEnvironment:
886     type: OS::TripleO::DeployedServerEnvironment
887     properties:
888       RoleCounts:
889 {% for role in roles %}
890         {{role.name}}DeployedServerCount: {get_param: {{role.name}}Count}
891 {% endfor %}
892       VipMap:
893         map_merge:
894           - {get_attr: [VipMap, net_ip_map]}
895           - redis: {get_attr: [RedisVirtualIP, ip_address]}
896       DeployedServerPortMap:
897         map_merge:
898           list_concat:
899 {% for role in roles %}
900               - {get_attr: [{{role.name}}, deployed_server_port_map]}
901 {% endfor %}
902       DeployedServerDeploymentSwiftDataMap:
903         map_merge:
904           list_concat:
905 {% for role in roles %}
906               - {get_attr: [{{role.name}}, deployed_server_deployment_swift_data_map]}
907 {% endfor %}
908       DefaultRouteIp:
909         str_split:
910           - ':'
911           - str_split:
912             - '/'
913             - {get_attr: [ServerOsCollectConfigData, value, {{primary_role_name}}, '0', request, metadata_url]}
914             - 2
915           - 0
916
917 outputs:
918   ManagedEndpoints:
919     description: Asserts that the keystone endpoints have been provisioned.
920     value: true
921   KeystoneURL:
922     description: URL for the Overcloud Keystone service
923     value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
924   KeystoneAdminVip:
925     description: Keystone Admin VIP endpoint
926     # Note that these nested get_attr functions require a dependency
927     # relationship between VipMap and ServiceNetMap, since we can't determine
928     # which attributes of VipMap are used until after ServiceNetMap's attribute
929     # values are available. If this is ever reworked to not use nested
930     # get_attr, that dependency can be removed.
931     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
932   EndpointMap:
933     description: |
934       Mapping of the resources with the needed info for their endpoints.
935       This includes the protocol used, the IP, port and also a full
936       representation of the URI.
937     value: {get_attr: [EndpointMapData, value]}
938   HostsEntry:
939     description: |
940       The content that should be appended to your /etc/hosts if you want to get
941       hostname-based access to the deployed nodes (useful for testing without
942       setting up a DNS).
943     value:
944       list_join:
945       - "\n"
946       - - {get_attr: [hostsConfig, hosts_entries]}
947       - - {get_attr: [VipHosts, value]}
948   EnabledServices:
949     description: The services enabled on each role
950     value:
951 {% for role in roles %}
952       {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
953 {% endfor %}
954   RoleData:
955     description: The configuration data associated with each role
956     value:
957 {% for role in roles %}
958       {{role.name}}:
959         map_merge:
960         - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
961         - {get_attr: [{{role.name}}MergedConfigSettings, value]}
962 {% endfor %}
963   RoleConfig:
964     description: The configuration workflows associated with each role
965     value: {get_attr: [AllNodesDeploySteps, RoleConfig]}
966   RoleNetIpMap:
967     description: Mapping of each network to a list of IPs for each role
968     value:
969 {% for role in roles %}
970       {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}
971 {% endfor %}
972   RoleNetHostnameMap:
973     description: Mapping of each network to a list of hostnames for each role
974     value:
975 {% for role in roles %}
976       {{role.name}}: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
977 {% endfor %}
978   ServerOsCollectConfigData:
979     description: The os-collect-config configuration associated with each server resource
980     value: {get_attr: [ServerOsCollectConfigData, value]}
981   VipMap:
982     description: Mapping of each network to VIP addresses. Also includes the Redis VIP.
983     value:
984       map_merge:
985         - {get_attr: [VipMap, net_ip_map]}
986         - redis: {get_attr: [RedisVirtualIP, ip_address]}
987   ServerIdData:
988     description: Mapping of each role to a list of nova server IDs and the bootstrap ID
989     value: {get_attr: [ServerIdMap, value]}
990   DeployedServerEnvironment:
991     description:
992       Environment data that can be used as input into the services stack when
993       using split-stack.
994     value: {get_attr: [DeployedServerEnvironment, deployed_server_environment]}