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]}
265 # Jinja loop for Role in roles_data.yaml
266 {% for role in roles %}
267 # Resources generated for {{role.name}} Role
268 {{role.name}}ServiceChain:
269 type: OS::TripleO::Services
272 get_param: {{role.name}}Services
273 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
274 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
275 DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
277 # Filter any null/None service_names which may be present due to mapping
278 # of services to OS::Heat::None
279 {{role.name}}ServiceNames:
280 type: OS::Heat::Value
281 depends_on: {{role.name}}ServiceChain
283 type: comma_delimited_list
286 expression: coalesce($.data, []).where($ != null)
287 data: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
289 {{role.name}}HostsDeployment:
290 type: OS::Heat::StructuredDeployments
292 name: {{role.name}}HostsDeployment
293 config: {get_attr: [hostsConfig, config_id]}
294 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
296 {{role.name}}AllNodesDeployment:
297 type: OS::Heat::StructuredDeployments
299 {% for role_inner in roles %}
300 - {{role_inner.name}}HostsDeployment
303 name: {{role.name}}AllNodesDeployment
304 config: {get_attr: [allNodesConfig, config_id]}
305 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
307 # Note we have to use yaql to look up the first hostname/ip in the
308 # list because heat path based attributes operate on the attribute
309 # inside the ResourceGroup, not the exposed list ref discussion in
310 # https://bugs.launchpad.net/heat/+bug/1640488
311 # The coalesce is needed because $.data is None during heat validation
314 expression: coalesce($.data, []).first(null)
315 data: {get_attr: [{{role.name}}, hostname]}
318 expression: coalesce($.data, []).first(null)
319 data: {get_attr: [{{role.name}}, ip_address]}
321 {{role.name}}AllNodesValidationDeployment:
322 type: OS::Heat::StructuredDeployments
323 depends_on: {{role.name}}AllNodesDeployment
325 name: {{role.name}}AllNodesValidationDeployment
326 config: {get_resource: AllNodesValidationConfig}
327 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
329 {{role.name}}IpListMap:
330 type: OS::TripleO::Network::Ports::NetIpListMap
332 ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
333 ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
334 InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
335 StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
336 StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
337 TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
338 ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
339 EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
340 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
341 ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
343 # Note (shardy) this somewhat complex yaql may be replaced
344 # with a map_deep_merge function in ocata. It merges the
345 # list of maps, but appends to colliding lists so we can
346 # create a map of lists for all nodes for each network
348 expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
350 - {get_attr: [{{role.name}}, hostname_map]}
353 type: OS::Heat::ResourceGroup
357 max_batch_size: {get_param: NodeCreateBatchSize}
359 count: {get_param: {{role.name}}Count}
360 removal_policies: {get_param: {{role.name}}RemovalPolicies}
362 type: OS::TripleO::{{role.name}}
364 CloudDomain: {get_param: CloudDomain}
365 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
366 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
369 template: {get_param: {{role.name}}HostnameFormat}
371 '%stackname%': {get_param: 'OS::stack_name'}
373 {% if role.name != 'Compute' %}
374 {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
376 NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
378 ServiceConfigSettings:
380 - get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
382 - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
384 # This next step combines two yaql passes:
385 # - The inner one does a deep merge on the service_config_settings for all roles
386 # - The outer one filters the map based on the services enabled for the role
387 # then merges the result into one map.
389 expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
393 expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
396 - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
398 services: {get_attr: [{{role.name}}ServiceNames, value]}
399 ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
400 MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
401 ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChain, role_data, service_metadata_settings]}
405 type: OS::TripleO::Hosts::SoftwareConfig
411 - add_vips_to_etc_hosts
412 - {get_attr: [VipHosts, value]}
415 {% for role in roles %}
418 - {get_attr: [{{role.name}}, hosts_entry]}
422 type: OS::TripleO::AllNodes::SoftwareConfig
424 cloud_name_external: {get_param: CloudName}
425 cloud_name_internal_api: {get_param: CloudNameInternal}
426 cloud_name_storage: {get_param: CloudNameStorage}
427 cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
428 cloud_name_ctlplane: {get_param: CloudNameCtlplane}
432 {% for role in roles %}
433 - {get_attr: [{{role.name}}ServiceNames, value]}
438 $.data.groups.flatten()
441 {% for role in roles %}
442 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
447 $.data.sources.flatten()
450 {% for role in roles %}
451 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
453 controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
454 controller_names: {get_attr: [{{primary_role_name}}, hostname]}
456 # Note (shardy) this somewhat complex yaql may be replaced
457 # with a map_deep_merge function in ocata. It merges the
458 # list of maps, but appends to colliding lists when a service
459 # is deployed on more than one role
461 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
464 {% for role in roles %}
465 - {get_attr: [{{role.name}}IpListMap, service_ips]}
469 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
472 {% for role in roles %}
473 - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
475 short_service_node_names:
477 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
480 {% for role in roles %}
481 - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
483 short_service_bootstrap_node:
485 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
488 {% for role in roles %}
489 - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
491 # FIXME(shardy): These require further work to move into service_ips
492 memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
493 NetVipMap: {get_attr: [VipMap, net_ip_map]}
494 RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
495 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
496 DeployIdentifier: {get_param: DeployIdentifier}
497 UpdateIdentifier: {get_param: UpdateIdentifier}
500 type: OS::Heat::RandomString
505 type: OS::Heat::RandomString
508 salt: {get_param: RabbitCookieSalt}
511 type: OS::TripleO::DefaultPasswords
513 DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
514 DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
515 DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
516 DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
517 DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
519 # creates the network architecture
521 type: OS::TripleO::Network
524 type: OS::TripleO::Network::Ports::ControlPlaneVipPort
527 name: control_virtual_ip
528 network: {get_param: NeutronControlPlaneID}
529 fixed_ips: {get_param: ControlFixedIPs}
530 replacement_policy: AUTO
534 type: OS::TripleO::Network::Ports::RedisVipPort
536 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
537 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
538 PortName: redis_virtual_ip
539 NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
541 FixedIPs: {get_param: RedisVirtualFixedIPs}
543 # The public VIP is on the External net, falls back to ctlplane
546 type: OS::TripleO::Network::Ports::ExternalVipPort
548 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
549 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
550 PortName: public_virtual_ip
551 FixedIPs: {get_param: PublicVirtualFixedIPs}
553 InternalApiVirtualIP:
555 type: OS::TripleO::Network::Ports::InternalApiVipPort
557 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
558 PortName: internal_api_virtual_ip
559 FixedIPs: {get_param: InternalApiVirtualFixedIPs}
563 type: OS::TripleO::Network::Ports::StorageVipPort
565 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
566 PortName: storage_virtual_ip
567 FixedIPs: {get_param: StorageVirtualFixedIPs}
569 StorageMgmtVirtualIP:
571 type: OS::TripleO::Network::Ports::StorageMgmtVipPort
573 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
574 PortName: storage_management_virtual_ip
575 FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
578 type: OS::TripleO::Network::Ports::NetVipMap
580 ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
581 ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
582 ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
583 InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
584 InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
585 StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
586 StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
587 StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
588 StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
589 # No tenant or management VIP required
591 # All Nodes Validations
592 AllNodesValidationConfig:
593 type: OS::TripleO::AllNodes::Validation
599 expression: coalesce($.data, []).first(null)
600 data: {get_attr: [{{primary_role_name}}, external_ip_address]}
602 expression: coalesce($.data, []).first(null)
603 data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
605 expression: coalesce($.data, []).first(null)
606 data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
608 expression: coalesce($.data, []).first(null)
609 data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
611 expression: coalesce($.data, []).first(null)
612 data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
614 expression: coalesce($.data, []).first(null)
615 data: {get_attr: [{{primary_role_name}}, management_ip_address]}
618 type: OS::TripleO::Tasks::UpdateWorkflow
620 {% for role in roles %}
621 - {{role.name}}AllNodesDeployment
625 {% for role in roles %}
626 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
629 deploy_identifier: {get_param: DeployIdentifier}
630 update_identifier: {get_param: UpdateIdentifier}
632 # Optional ExtraConfig for all nodes - all roles are passed in here, but
633 # the nested template may configure each role differently (or not at all)
635 type: OS::TripleO::AllNodesExtraConfig
638 {% for role in roles %}
639 - {{role.name}}AllNodesValidationDeployment
643 {% for role in roles %}
644 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
647 # Post deployment steps for all roles
649 type: OS::TripleO::PostDeploySteps
651 {% for role in roles %}
652 - {{role.name}}AllNodesDeployment
656 {% for role in roles %}
657 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
659 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
661 {% for role in roles %}
662 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
667 description: Asserts that the keystone endpoints have been provisioned.
670 description: URL for the Overcloud Keystone service
671 value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
673 description: Keystone Admin VIP endpoint
674 value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
677 Mapping of the resources with the needed info for their endpoints.
678 This includes the protocol used, the IP, port and also a full
679 representation of the URI.
680 value: {get_attr: [EndpointMapData, value]}
683 The content that should be appended to your /etc/hosts if you want to get
684 hostname-based access to the deployed nodes (useful for testing without
689 - - {get_attr: [hostsConfig, hosts_entries]}
690 - - {get_attr: [VipHosts, value]}
692 description: The services enabled on each role
694 {% for role in roles %}
695 {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
698 description: The configuration data associated with each role
700 {% for role in roles %}
701 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
704 description: Mapping of each network to a list of IPs for each role
706 {% for role in roles %}
707 {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}