Remove bgp-vpn from scenario004-multinode-containers
[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 if network.enabled|default(true) %}
495       {{network.name}}IpList: {get_attr: [{{role.name}}, {{network.name_lower}}_ip_address]}
496 {%- endfor %}
497       EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
498       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
499       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
500       NetworkHostnameMap: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
501
502   {{role.name}}NetworkHostnameMap:
503     type: OS::Heat::Value
504     properties:
505       type: json
506       value:
507         # Note (shardy) this somewhat complex yaql may be replaced
508         # with a map_deep_merge function in ocata.  It merges the
509         # list of maps, but appends to colliding lists so we can
510         # create a map of lists for all nodes for each network
511         yaql:
512           expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
513           data:
514             - {get_attr: [{{role.name}}, hostname_map]}
515
516   {{role.name}}:
517     type: OS::Heat::ResourceGroup
518     depends_on: Networks
519     update_policy:
520       batch_create:
521         max_batch_size: {get_param: NodeCreateBatchSize}
522     properties:
523       count: {get_param: {{role.name}}Count}
524       removal_policies: {get_param: {{role.name}}RemovalPolicies}
525       resource_def:
526         type: OS::TripleO::{{role.name}}
527         properties:
528           CloudDomain: {get_param: CloudDomain}
529           ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
530           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
531           Hostname:
532             str_replace:
533               template: {get_param: {{role.name}}HostnameFormat}
534               params:
535                 '%stackname%': {get_param: 'OS::stack_name'}
536           NodeIndex: '%index%'
537           # Note, SchedulerHints must be defined here, not only in the
538           # nested template, as it can contain %index%
539           {{role.name}}SchedulerHints:
540             map_merge:
541 {%- if role.deprecated_param_scheduler_hints is defined %}
542               - {get_param: {{role.deprecated_param_scheduler_hints}}}
543 {%- endif %}
544               - {get_param: {{role.name}}SchedulerHints}
545           ServiceConfigSettings: {get_attr: [{{role.name}}ServiceConfigSettings, value]}
546           ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
547           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChainRoleData, value, monitoring_subscriptions]}
548           ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_metadata_settings]}
549           DeploymentServerBlacklistDict: {get_attr: [DeploymentServerBlacklistDict, value]}
550           RoleParameters: {get_param: {{role.name}}Parameters}
551 {% endfor %}
552
553 {% for role in roles %}
554   {{role.name}}Servers:
555     type: OS::Heat::Value
556     depends_on: {{role.name}}
557     properties:
558       type: json
559       value:
560         yaql:
561           expression: let(servers=>switch(isDict($.data.servers) => $.data.servers, true => {})) -> $servers.deleteAll($servers.keys().where($servers[$] = null))
562           data:
563             servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
564 {% endfor %}
565
566   # This is a different format to *Servers, as it creates a map of lists
567   # whereas *Servers creates a map of maps with keys of the nested resource names
568   ServerIdMap:
569     type: OS::Heat::Value
570     properties:
571       value:
572         server_ids:
573 {% for role in roles %}
574           {{role.name}}: {get_attr: [{{role.name}}, nova_server_resource]}
575 {% endfor %}
576         bootstrap_server_id:
577           yaql:
578             expression: coalesce($.data, []).first(null)
579             data: {get_attr: [{{primary_role_name}}, nova_server_resource]}
580
581   # This resource just creates a dict out of the DeploymentServerBlacklist,
582   # which is a list. The dict is used in the role templates to set a condition
583   # on whether to create the deployment resources. We can't use the list
584   # directly because there is no way to ask Heat if a list contains a specific
585   # value.
586   DeploymentServerBlacklistDict:
587     type: OS::Heat::Value
588     properties:
589       type: json
590       value:
591         map_merge:
592           repeat:
593             template:
594               hostname: 1
595             for_each:
596               hostname: {get_param: DeploymentServerBlacklist}
597
598   hostsConfig:
599     type: OS::TripleO::Hosts::SoftwareConfig
600     properties:
601       hosts:
602         list_join:
603         - "\n"
604         - - if:
605             - add_vips_to_etc_hosts
606             - {get_attr: [VipHosts, value]}
607             - ''
608         -
609 {% for role in roles %}
610           - list_join:
611             - ""
612             - {get_attr: [{{role.name}}, hosts_entry]}
613 {% endfor %}
614
615   allNodesConfig:
616     type: OS::TripleO::AllNodes::SoftwareConfig
617     properties:
618 {%- for network in networks if network.vip|default(false) %}
619 {%- if network.name == 'External' %}
620   # Special case the External hostname param, which is CloudName
621       cloud_name_{{network.name_lower}}: {get_param: CloudName}
622 {%- elif network.name == 'InternalApi' %}
623   # Special case the Internal API hostname param, which is CloudNameInternal
624       cloud_name_{{network.name_lower}}: {get_param: CloudNameInternal}
625 {%- elif network.name == 'StorageMgmt' %}
626   # Special case StorageMgmt hostname param, which is CloudNameStorageManagement
627       cloud_name_{{network.name_lower}}: {get_param: CloudNameStorageManagement}
628 {%- else %}
629       cloud_name_{{network.name_lower}}: {get_param: CloudName{{network.name}}}
630 {%- endif %}
631 {%- endfor %}
632       cloud_name_ctlplane: {get_param: CloudNameCtlplane}
633       enabled_services:
634         list_join:
635           - ','
636 {% for role in roles %}
637           - {get_attr: [{{role.name}}ServiceNames, value]}
638 {% endfor %}
639       logging_groups:
640         yaql:
641           expression: >
642             $.data.groups.flatten()
643           data:
644             groups:
645 {% for role in roles %}
646               - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_groups]}
647 {% endfor %}
648       logging_sources:
649         yaql:
650           expression: >
651             $.data.sources.flatten()
652           data:
653             sources:
654 {% for role in roles %}
655               - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_sources]}
656 {% endfor %}
657       controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
658       controller_names: {get_attr: [{{primary_role_name}}, hostname]}
659       service_ips:
660         # Note (shardy) this somewhat complex yaql may be replaced
661         # with a map_deep_merge function in ocata.  It merges the
662         # list of maps, but appends to colliding lists when a service
663         # is deployed on more than one role
664         yaql:
665           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
666           data:
667             l:
668 {% for role in roles %}
669               - {get_attr: [{{role.name}}IpListMap, service_ips]}
670 {% endfor %}
671       service_node_names:
672         yaql:
673           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
674           data:
675             l:
676 {% for role in roles %}
677               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
678 {% endfor %}
679       short_service_node_names:
680         yaql:
681           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
682           data:
683             l:
684 {% for role in roles %}
685               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
686 {% endfor %}
687       short_service_bootstrap_node:
688         yaql:
689           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
690           data:
691             l:
692 {% for role in roles %}
693               - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
694 {% endfor %}
695       NetVipMap: {get_attr: [VipMap, net_ip_map]}
696       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
697       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
698       DeployIdentifier: {get_param: DeployIdentifier}
699       UpdateIdentifier: {get_param: UpdateIdentifier}
700
701   MysqlRootPassword:
702     type: OS::TripleO::RandomString
703     properties:
704       length: 10
705
706   RabbitCookie:
707     type: OS::TripleO::RandomString
708     properties:
709       length: 20
710       salt: {get_param: RabbitCookieSalt}
711
712   DefaultPasswords:
713     type: OS::TripleO::DefaultPasswords
714     properties:
715       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
716       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
717       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
718       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
719       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
720
721   # creates the network architecture
722   Networks:
723     type: OS::TripleO::Network
724
725   ControlVirtualIP:
726     type: OS::TripleO::Network::Ports::ControlPlaneVipPort
727     depends_on: Networks
728     properties:
729       name: control_virtual_ip
730       network: {get_param: NeutronControlPlaneID}
731       fixed_ips: {get_param: ControlFixedIPs}
732       replacement_policy: AUTO
733
734   RedisVirtualIP:
735     depends_on: Networks
736     type: OS::TripleO::Network::Ports::RedisVipPort
737     properties:
738       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
739       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
740       PortName: redis_virtual_ip
741       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
742       ServiceName: redis
743       FixedIPs: {get_param: RedisVirtualFixedIPs}
744
745 {%- for network in networks if network.vip|default(false) %}
746 {%- if network.name == 'External' %}
747   # The public VIP is on the External net, falls back to ctlplane
748   PublicVirtualIP:
749     depends_on: Networks
750     type: OS::TripleO::Network::Ports::ExternalVipPort
751     properties:
752       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
753       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
754       PortName: public_virtual_ip
755       FixedIPs: {get_param: PublicVirtualFixedIPs}
756 {%- elif network.name == 'StorageMgmt' %}
757   {{network.name}}VirtualIP:
758     depends_on: Networks
759     type: OS::TripleO::Network::Ports::{{network.name}}VipPort
760     properties:
761       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
762       PortName: storage_management_virtual_ip
763       FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
764 {%- else %}
765   {{network.name}}VirtualIP:
766     depends_on: Networks
767     type: OS::TripleO::Network::Ports::{{network.name}}VipPort
768     properties:
769       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
770       PortName: {{network.name_lower}}_virtual_ip
771       FixedIPs: {get_param: {{network.name}}VirtualFixedIPs}
772 {%- endif %}
773 {%- endfor %}
774
775   VipMap:
776     type: OS::TripleO::Network::Ports::NetVipMap
777     properties:
778       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
779 {%- for network in networks if network.vip|default(false) %}
780 {%- if network.name == 'External' %}
781       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
782       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
783 {%- else %}
784       {{network.name}}Ip: {get_attr: [{{network.name}}VirtualIP, ip_address]}
785       {{network.name}}IpUri: {get_attr: [{{network.name}}VirtualIP, ip_address_uri]}
786 {%- endif %}
787 {%- endfor %}
788       # No tenant or management VIP required
789     # Because of nested get_attr functions in the KeystoneAdminVip output, we
790     # can't determine which attributes of VipMap are used until after
791     # ServiceNetMap's attribute values are available.
792     depends_on: ServiceNetMap
793
794   # All Nodes Validations
795   AllNodesValidationConfig:
796     type: OS::TripleO::AllNodes::Validation
797     properties:
798       PingTestIps:
799         list_join:
800         - ' '
801         -
802 {%- for network in networks if network.enabled|default(true) %}
803           - yaql:
804               expression: coalesce($.data, []).first(null)
805               data: {get_attr: [{{primary_role_name}}, {{network.name_lower}}_ip_address]}
806 {%- endfor %}
807
808   UpdateWorkflow:
809     type: OS::TripleO::Tasks::UpdateWorkflow
810     depends_on:
811 {% for role in roles %}
812       - {{role.name}}AllNodesDeployment
813 {% endfor %}
814     properties:
815       servers:
816 {% for role in roles %}
817         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
818 {% endfor %}
819       input_values:
820         deploy_identifier: {get_param: DeployIdentifier}
821         update_identifier: {get_param: UpdateIdentifier}
822
823   # Optional ExtraConfig for all nodes - all roles are passed in here, but
824   # the nested template may configure each role differently (or not at all)
825   AllNodesExtraConfig:
826     type: OS::TripleO::AllNodesExtraConfig
827     depends_on:
828       - UpdateWorkflow
829 {% for role in roles %}
830       - {{role.name}}AllNodesValidationDeployment
831 {% endfor %}
832     properties:
833       servers:
834 {% for role in roles %}
835         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
836 {% endfor %}
837
838   # Post deployment steps for all roles
839   AllNodesDeploySteps:
840     type: OS::TripleO::PostDeploySteps
841     depends_on:
842       - AllNodesExtraConfig
843 {% for role in roles %}
844       - {{role.name}}AllNodesDeployment
845 {% endfor %}
846     properties:
847       servers:
848 {% for role in roles %}
849         {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
850 {% endfor %}
851       stack_name: {get_param: 'OS::stack_name'}
852       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
853       ctlplane_service_ips:
854         # Note (shardy) this somewhat complex yaql may be replaced
855         # with a map_deep_merge function in ocata.  It merges the
856         # list of maps, but appends to colliding lists when a service
857         # is deployed on more than one role
858         yaql:
859           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
860           data:
861             l:
862 {% for role in roles %}
863               - {get_attr: [{{role.name}}IpListMap, ctlplane_service_ips]}
864 {% endfor %}
865       role_data:
866 {% for role in roles %}
867         {{role.name}}:
868           map_merge:
869           - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
870           - {get_attr: [{{role.name}}MergedConfigSettings, value]}
871 {% endfor %}
872
873   ServerOsCollectConfigData:
874     type: OS::Heat::Value
875     properties:
876       type: json
877       value:
878 {% for role in roles %}
879         {{role.name}}: {get_attr: [{{role.name}}, attributes, os_collect_config]}
880 {% endfor %}
881
882   DeployedServerEnvironment:
883     type: OS::TripleO::DeployedServerEnvironment
884     properties:
885       RoleCounts:
886 {% for role in roles %}
887         {{role.name}}DeployedServerCount: {get_param: {{role.name}}Count}
888 {% endfor %}
889       VipMap:
890         map_merge:
891           - {get_attr: [VipMap, net_ip_map]}
892           - redis: {get_attr: [RedisVirtualIP, ip_address]}
893       DeployedServerPortMap:
894         map_merge:
895           list_concat:
896 {% for role in roles %}
897               - {get_attr: [{{role.name}}, deployed_server_port_map]}
898 {% endfor %}
899       DeployedServerDeploymentSwiftDataMap:
900         map_merge:
901           list_concat:
902 {% for role in roles %}
903               - {get_attr: [{{role.name}}, deployed_server_deployment_swift_data_map]}
904 {% endfor %}
905       DefaultRouteIp:
906         str_split:
907           - ':'
908           - str_split:
909             - '/'
910             - {get_attr: [ServerOsCollectConfigData, value, {{primary_role_name}}, '0', request, metadata_url]}
911             - 2
912           - 0
913
914 outputs:
915   ManagedEndpoints:
916     description: Asserts that the keystone endpoints have been provisioned.
917     value: true
918   KeystoneURL:
919     description: URL for the Overcloud Keystone service
920     value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
921   KeystoneAdminVip:
922     description: Keystone Admin VIP endpoint
923     # Note that these nested get_attr functions require a dependency
924     # relationship between VipMap and ServiceNetMap, since we can't determine
925     # which attributes of VipMap are used until after ServiceNetMap's attribute
926     # values are available. If this is ever reworked to not use nested
927     # get_attr, that dependency can be removed.
928     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
929   EndpointMap:
930     description: |
931       Mapping of the resources with the needed info for their endpoints.
932       This includes the protocol used, the IP, port and also a full
933       representation of the URI.
934     value: {get_attr: [EndpointMapData, value]}
935   HostsEntry:
936     description: |
937       The content that should be appended to your /etc/hosts if you want to get
938       hostname-based access to the deployed nodes (useful for testing without
939       setting up a DNS).
940     value:
941       list_join:
942       - "\n"
943       - - {get_attr: [hostsConfig, hosts_entries]}
944       - - {get_attr: [VipHosts, value]}
945   EnabledServices:
946     description: The services enabled on each role
947     value:
948 {% for role in roles %}
949       {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
950 {% endfor %}
951   RoleData:
952     description: The configuration data associated with each role
953     value:
954 {% for role in roles %}
955       {{role.name}}:
956         map_merge:
957         - {get_attr: [{{role.name}}ServiceChainRoleData, value]}
958         - {get_attr: [{{role.name}}MergedConfigSettings, value]}
959 {% endfor %}
960   RoleConfig:
961     description: The configuration workflows associated with each role
962     value: {get_attr: [AllNodesDeploySteps, RoleConfig]}
963   RoleNetIpMap:
964     description: Mapping of each network to a list of IPs for each role
965     value:
966 {% for role in roles %}
967       {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}
968 {% endfor %}
969   RoleNetHostnameMap:
970     description: Mapping of each network to a list of hostnames for each role
971     value:
972 {% for role in roles %}
973       {{role.name}}: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
974 {% endfor %}
975   ServerOsCollectConfigData:
976     description: The os-collect-config configuration associated with each server resource
977     value: {get_attr: [ServerOsCollectConfigData, value]}
978   VipMap:
979     description: Mapping of each network to VIP addresses. Also includes the Redis VIP.
980     value:
981       map_merge:
982         - {get_attr: [VipMap, net_ip_map]}
983         - redis: {get_attr: [RedisVirtualIP, ip_address]}
984   ServerIdData:
985     description: Mapping of each role to a list of nova server IDs and the bootstrap ID
986     value: {get_attr: [ServerIdMap, value]}
987   DeployedServerEnvironment:
988     description:
989       Environment data that can be used as input into the services stack when
990       using split-stack.
991     value: {get_attr: [DeployedServerEnvironment, deployed_server_environment]}