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) -%}
8 {%- set primary_role_name = primary_role[0].name -%}
9 # primary role is: {{primary_role_name}}
10 heat_template_version: ocata
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.
19 # TODO(shadower): we should probably use the parameter groups to put
23 # Common parameters (not specific to a role)
25 default: overcloud.localdomain
26 description: The DNS name of this cloud. E.g. ci-overcloud.tripleo.org
29 default: overcloud.internalapi.localdomain
31 The DNS name of this cloud's internal API endpoint. E.g.
32 'ci-overcloud.internalapi.tripleo.org'.
35 default: overcloud.storage.localdomain
37 The DNS name of this cloud's storage endpoint. E.g.
38 'ci-overcloud.storage.tripleo.org'.
40 CloudNameStorageManagement:
41 default: overcloud.storagemgmt.localdomain
43 The DNS name of this cloud's storage management endpoint. E.g.
44 'ci-overcloud.storagemgmt.tripleo.org'.
47 default: overcloud.ctlplane.localdomain
49 The DNS name of this cloud's storage management endpoint. E.g.
50 'ci-overcloud.management.tripleo.org'.
54 description: Should be used for arbitrary ips.
56 InternalApiVirtualFixedIPs:
59 Control the IP allocation for the InternalApiVirtualInterface port. E.g.
60 [{'ip_address':'1.2.3.4'}]
62 NeutronControlPlaneID:
65 description: Neutron ID or name for ctlplane network.
66 NeutronPublicInterface:
68 description: What interface to bridge onto br-ex for network nodes.
70 PublicVirtualFixedIPs:
73 Control the IP allocation for the PublicVirtualInterface port. E.g.
74 [{'ip_address':'1.2.3.4'}]
79 description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
80 StorageVirtualFixedIPs:
83 Control the IP allocation for the StorageVirtualInterface port. E.g.
84 [{'ip_address':'1.2.3.4'}]
86 StorageMgmtVirtualFixedIPs:
89 Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
90 [{'ip_address':'1.2.3.4'}]
95 Control the IP allocation for the virtual IP used by Redis. E.g.
96 [{'ip_address':'1.2.3.4'}]
99 default: 'localdomain'
102 The DNS domain used for the hosts. This should match the dhcp_domain
103 configured in the Undercloud neutron. Defaults to localdomain.
107 Extra properties or metadata passed to Nova for the created nodes in
108 the overcloud. It's accessible via the Nova metadata API.
111 # Compute-specific params
112 # FIXME(shardy) handle these deprecated names as they don't match compute.yaml
113 HypervisorNeutronPhysicalBridge:
116 An OVS bridge to create on each hypervisor. This defaults to br-ex the
117 same as the control plane nodes, as we have a uniform configuration of
118 the openvswitch agent. Typically should not need to be changed.
120 HypervisorNeutronPublicInterface:
122 description: What interface to add to the HypervisorNeutronPhysicalBridge.
127 description: Maxiumum batch size for creating nodes
130 # Jinja loop for Role in role_data.yaml
131 {% for role in roles %}
132 # Parameters generated for {{role.name}} Role
133 {{role.name}}Services:
134 description: A list of service resources (configured in the Heat
135 resource_registry) which represent nested stacks
136 for each service that should get installed on the {{role.name}} role.
137 type: comma_delimited_list
140 description: Number of {{role.name}} nodes to deploy
142 default: {{role.CountDefault|default(0)}}
144 {{role.name}}HostnameFormat:
147 Format for {{role.name}} node hostnames
148 Note %index% is translated into the index of the node, e.g 0/1/2 etc
149 and %stackname% is replaced with the stack name e.g overcloud
150 {% if role.HostnameFormatDefault %}
151 default: "{{role.HostnameFormatDefault}}"
153 default: "%stackname%-{{role.name.lower()}}-%index%"
156 {{role.name}}RemovalPolicies:
160 List of resources to be removed from {{role.name}} ResourceGroup when
161 doing an update which requires removal of specific resources.
162 Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
164 {% if role.name != 'Compute' %}
165 {{role.name}}SchedulerHints:
167 NovaComputeSchedulerHints:
170 description: Optional scheduler hints to pass to nova
174 # Identifiers to trigger tasks on nodes
179 Setting to a previously unused value during stack-update will trigger
180 package update on all nodes
185 Setting this to a unique value will re-run any deployment tasks which
186 perform configuration on a Heat stack-update.
191 Set to true to append per network Vips to /etc/hosts on each node.
194 add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
199 type: OS::Heat::Value
208 IP: {get_attr: [VipMap, net_ip_map, external]}
209 HOST: {get_param: CloudName}
213 IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
214 HOST: {get_param: CloudNameCtlplane}
218 IP: {get_attr: [VipMap, net_ip_map, internal_api]}
219 HOST: {get_param: CloudNameInternal}
223 IP: {get_attr: [VipMap, net_ip_map, storage]}
224 HOST: {get_param: CloudNameStorage}
228 IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
229 HOST: {get_param: CloudNameStorageManagement}
231 HeatAuthEncryptionKey:
232 type: OS::Heat::RandomString
235 type: OS::Heat::RandomString
240 type: OS::Heat::RandomString
245 type: OS::TripleO::ServiceNetMap
248 type: OS::TripleO::EndpointMap
251 external: {get_param: CloudName}
252 internal_api: {get_param: CloudNameInternal}
253 storage: {get_param: CloudNameStorage}
254 storage_mgmt: {get_param: CloudNameStorageManagement}
255 ctlplane: {get_param: CloudNameCtlplane}
256 NetIpMap: {get_attr: [VipMap, net_ip_map]}
257 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
260 type: OS::Heat::Value
263 value: {get_attr: [EndpointMap, endpoint_map]}
266 type: OS::TripleO::Ssh::KnownHostsConfig
271 {% for role in roles %}
272 - {get_attr: [{{role.name}}, known_hosts_entry]}
275 # Jinja loop for Role in roles_data.yaml
276 {% for role in roles %}
277 # Resources generated for {{role.name}} Role
278 {{role.name}}ServiceChain:
279 type: OS::TripleO::Services
282 get_param: {{role.name}}Services
283 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
284 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
285 DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
287 # Filter any null/None service_names which may be present due to mapping
288 # of services to OS::Heat::None
289 {{role.name}}ServiceNames:
290 type: OS::Heat::Value
291 depends_on: {{role.name}}ServiceChain
293 type: comma_delimited_list
296 expression: coalesce($.data, []).where($ != null)
297 data: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
299 {{role.name}}HostsDeployment:
300 type: OS::Heat::StructuredDeployments
302 name: {{role.name}}HostsDeployment
303 config: {get_attr: [hostsConfig, config_id]}
304 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
306 {{role.name}}SshKnownHostsDeployment:
307 type: OS::Heat::StructuredDeployments
309 name: {{role.name}}SshKnownHostsDeployment
310 config: {get_resource: SshKnownHostsConfig}
311 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
313 {{role.name}}AllNodesDeployment:
314 type: OS::Heat::StructuredDeployments
316 {% for role_inner in roles %}
317 - {{role_inner.name}}HostsDeployment
320 name: {{role.name}}AllNodesDeployment
321 config: {get_attr: [allNodesConfig, config_id]}
322 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
324 # Note we have to use yaql to look up the first hostname/ip in the
325 # list because heat path based attributes operate on the attribute
326 # inside the ResourceGroup, not the exposed list ref discussion in
327 # https://bugs.launchpad.net/heat/+bug/1640488
328 # The coalesce is needed because $.data is None during heat validation
331 expression: coalesce($.data, []).first(null)
332 data: {get_attr: [{{role.name}}, hostname]}
335 expression: coalesce($.data, []).first(null)
336 data: {get_attr: [{{role.name}}, ip_address]}
338 {{role.name}}AllNodesValidationDeployment:
339 type: OS::Heat::StructuredDeployments
340 depends_on: {{role.name}}AllNodesDeployment
342 name: {{role.name}}AllNodesValidationDeployment
343 config: {get_resource: AllNodesValidationConfig}
344 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
346 {{role.name}}IpListMap:
347 type: OS::TripleO::Network::Ports::NetIpListMap
349 ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
350 ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
351 InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
352 StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
353 StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
354 TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
355 ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
356 EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
357 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
358 ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
360 # Note (shardy) this somewhat complex yaql may be replaced
361 # with a map_deep_merge function in ocata. It merges the
362 # list of maps, but appends to colliding lists so we can
363 # create a map of lists for all nodes for each network
365 expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
367 - {get_attr: [{{role.name}}, hostname_map]}
370 type: OS::Heat::ResourceGroup
374 max_batch_size: {get_param: NodeCreateBatchSize}
376 count: {get_param: {{role.name}}Count}
377 removal_policies: {get_param: {{role.name}}RemovalPolicies}
379 type: OS::TripleO::{{role.name}}
381 CloudDomain: {get_param: CloudDomain}
382 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
383 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
386 template: {get_param: {{role.name}}HostnameFormat}
388 '%stackname%': {get_param: 'OS::stack_name'}
390 {% if role.name != 'Compute' %}
391 {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
393 NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
395 ServiceConfigSettings:
397 - get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
399 - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
401 # This next step combines two yaql passes:
402 # - The inner one does a deep merge on the service_config_settings for all roles
403 # - The outer one filters the map based on the services enabled for the role
404 # then merges the result into one map.
406 expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
410 expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
413 - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
415 services: {get_attr: [{{role.name}}ServiceNames, value]}
416 ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
417 MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
418 ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChain, role_data, service_metadata_settings]}
422 type: OS::TripleO::Hosts::SoftwareConfig
428 - add_vips_to_etc_hosts
429 - {get_attr: [VipHosts, value]}
432 {% for role in roles %}
435 - {get_attr: [{{role.name}}, hosts_entry]}
439 type: OS::TripleO::AllNodes::SoftwareConfig
441 cloud_name_external: {get_param: CloudName}
442 cloud_name_internal_api: {get_param: CloudNameInternal}
443 cloud_name_storage: {get_param: CloudNameStorage}
444 cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
445 cloud_name_ctlplane: {get_param: CloudNameCtlplane}
449 {% for role in roles %}
450 - {get_attr: [{{role.name}}ServiceNames, value]}
455 $.data.groups.flatten()
458 {% for role in roles %}
459 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
464 $.data.sources.flatten()
467 {% for role in roles %}
468 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
470 controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
471 controller_names: {get_attr: [{{primary_role_name}}, hostname]}
473 # Note (shardy) this somewhat complex yaql may be replaced
474 # with a map_deep_merge function in ocata. It merges the
475 # list of maps, but appends to colliding lists when a service
476 # is deployed on more than one role
478 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
481 {% for role in roles %}
482 - {get_attr: [{{role.name}}IpListMap, service_ips]}
486 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
489 {% for role in roles %}
490 - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
492 short_service_node_names:
494 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
497 {% for role in roles %}
498 - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
500 short_service_bootstrap_node:
502 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
505 {% for role in roles %}
506 - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
508 # FIXME(shardy): These require further work to move into service_ips
509 memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
510 NetVipMap: {get_attr: [VipMap, net_ip_map]}
511 RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
512 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
513 DeployIdentifier: {get_param: DeployIdentifier}
514 UpdateIdentifier: {get_param: UpdateIdentifier}
517 type: OS::Heat::RandomString
522 type: OS::Heat::RandomString
525 salt: {get_param: RabbitCookieSalt}
528 type: OS::TripleO::DefaultPasswords
530 DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
531 DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
532 DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
533 DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
534 DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
536 # creates the network architecture
538 type: OS::TripleO::Network
541 type: OS::TripleO::Network::Ports::ControlPlaneVipPort
544 name: control_virtual_ip
545 network: {get_param: NeutronControlPlaneID}
546 fixed_ips: {get_param: ControlFixedIPs}
547 replacement_policy: AUTO
551 type: OS::TripleO::Network::Ports::RedisVipPort
553 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
554 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
555 PortName: redis_virtual_ip
556 NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
558 FixedIPs: {get_param: RedisVirtualFixedIPs}
560 # The public VIP is on the External net, falls back to ctlplane
563 type: OS::TripleO::Network::Ports::ExternalVipPort
565 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
566 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
567 PortName: public_virtual_ip
568 FixedIPs: {get_param: PublicVirtualFixedIPs}
570 InternalApiVirtualIP:
572 type: OS::TripleO::Network::Ports::InternalApiVipPort
574 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
575 PortName: internal_api_virtual_ip
576 FixedIPs: {get_param: InternalApiVirtualFixedIPs}
580 type: OS::TripleO::Network::Ports::StorageVipPort
582 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
583 PortName: storage_virtual_ip
584 FixedIPs: {get_param: StorageVirtualFixedIPs}
586 StorageMgmtVirtualIP:
588 type: OS::TripleO::Network::Ports::StorageMgmtVipPort
590 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
591 PortName: storage_management_virtual_ip
592 FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
595 type: OS::TripleO::Network::Ports::NetVipMap
597 ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
598 ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
599 ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
600 InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
601 InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
602 StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
603 StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
604 StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
605 StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
606 # No tenant or management VIP required
608 # All Nodes Validations
609 AllNodesValidationConfig:
610 type: OS::TripleO::AllNodes::Validation
616 expression: coalesce($.data, []).first(null)
617 data: {get_attr: [{{primary_role_name}}, external_ip_address]}
619 expression: coalesce($.data, []).first(null)
620 data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
622 expression: coalesce($.data, []).first(null)
623 data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
625 expression: coalesce($.data, []).first(null)
626 data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
628 expression: coalesce($.data, []).first(null)
629 data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
631 expression: coalesce($.data, []).first(null)
632 data: {get_attr: [{{primary_role_name}}, management_ip_address]}
635 type: OS::TripleO::Tasks::UpdateWorkflow
637 {% for role in roles %}
638 - {{role.name}}AllNodesDeployment
642 {% for role in roles %}
643 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
646 deploy_identifier: {get_param: DeployIdentifier}
647 update_identifier: {get_param: UpdateIdentifier}
649 # Optional ExtraConfig for all nodes - all roles are passed in here, but
650 # the nested template may configure each role differently (or not at all)
652 type: OS::TripleO::AllNodesExtraConfig
655 {% for role in roles %}
656 - {{role.name}}AllNodesValidationDeployment
660 {% for role in roles %}
661 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
664 # Post deployment steps for all roles
666 type: OS::TripleO::PostDeploySteps
668 {% for role in roles %}
669 - {{role.name}}AllNodesDeployment
673 {% for role in roles %}
674 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
676 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
678 {% for role in roles %}
679 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
684 description: Asserts that the keystone endpoints have been provisioned.
687 description: URL for the Overcloud Keystone service
688 value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
690 description: Keystone Admin VIP endpoint
691 value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
694 Mapping of the resources with the needed info for their endpoints.
695 This includes the protocol used, the IP, port and also a full
696 representation of the URI.
697 value: {get_attr: [EndpointMapData, value]}
700 The content that should be appended to your /etc/hosts if you want to get
701 hostname-based access to the deployed nodes (useful for testing without
706 - - {get_attr: [hostsConfig, hosts_entries]}
707 - - {get_attr: [VipHosts, value]}
709 description: The services enabled on each role
711 {% for role in roles %}
712 {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
715 description: The configuration data associated with each role
717 {% for role in roles %}
718 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
721 description: Mapping of each network to a list of IPs for each role
723 {% for role in roles %}
724 {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}