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: pike
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'.
55 Control the IP allocation for the ControlVirtualIP port. E.g.
56 [{'ip_address':'1.2.3.4'}]
58 InternalApiVirtualFixedIPs:
61 Control the IP allocation for the InternalApiVirtualInterface port. E.g.
62 [{'ip_address':'1.2.3.4'}]
64 NeutronControlPlaneID:
67 description: Neutron ID or name for ctlplane network.
68 NeutronPublicInterface:
70 description: What interface to bridge onto br-ex for network nodes.
72 PublicVirtualFixedIPs:
75 Control the IP allocation for the PublicVirtualInterface port. E.g.
76 [{'ip_address':'1.2.3.4'}]
81 description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
82 StorageVirtualFixedIPs:
85 Control the IP allocation for the StorageVirtualInterface port. E.g.
86 [{'ip_address':'1.2.3.4'}]
88 StorageMgmtVirtualFixedIPs:
91 Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
92 [{'ip_address':'1.2.3.4'}]
97 Control the IP allocation for the virtual IP used by Redis. E.g.
98 [{'ip_address':'1.2.3.4'}]
101 default: 'localdomain'
104 The DNS domain used for the hosts. This should match the dhcp_domain
105 configured in the Undercloud neutron. Defaults to localdomain.
109 Extra properties or metadata passed to Nova for the created nodes in
110 the overcloud. It's accessible via the Nova metadata API.
113 # Compute-specific params
114 # FIXME(shardy) handle these deprecated names as they don't match compute.yaml
115 HypervisorNeutronPhysicalBridge:
118 An OVS bridge to create on each hypervisor. This defaults to br-ex the
119 same as the control plane nodes, as we have a uniform configuration of
120 the openvswitch agent. Typically should not need to be changed.
122 HypervisorNeutronPublicInterface:
124 description: What interface to add to the HypervisorNeutronPhysicalBridge.
129 description: Maxiumum batch size for creating nodes
132 # Jinja loop for Role in role_data.yaml
133 {% for role in roles %}
134 # Parameters generated for {{role.name}} Role
135 {{role.name}}Services:
136 description: A list of service resources (configured in the Heat
137 resource_registry) which represent nested stacks
138 for each service that should get installed on the {{role.name}} role.
139 type: comma_delimited_list
142 description: Number of {{role.name}} nodes to deploy
144 default: {{role.CountDefault|default(0)}}
146 {{role.name}}HostnameFormat:
149 Format for {{role.name}} node hostnames
150 Note %index% is translated into the index of the node, e.g 0/1/2 etc
151 and %stackname% is replaced with the stack name e.g overcloud
152 {% if role.HostnameFormatDefault %}
153 default: "{{role.HostnameFormatDefault}}"
155 default: "%stackname%-{{role.name.lower()}}-%index%"
158 {{role.name}}RemovalPolicies:
162 List of resources to be removed from {{role.name}} ResourceGroup when
163 doing an update which requires removal of specific resources.
164 Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
166 {% if role.name != 'Compute' %}
167 {{role.name}}SchedulerHints:
169 NovaComputeSchedulerHints:
172 description: Optional scheduler hints to pass to nova
175 {{role.name}}Parameters:
177 description: Optional Role Specific parameters to be provided to service
181 # Identifiers to trigger tasks on nodes
186 Setting to a previously unused value during stack-update will trigger
187 package update on all nodes
192 Setting this to a unique value will re-run any deployment tasks which
193 perform configuration on a Heat stack-update.
198 Set to true to append per network Vips to /etc/hosts on each node.
201 add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
206 type: OS::Heat::Value
215 IP: {get_attr: [VipMap, net_ip_map, external]}
216 HOST: {get_param: CloudName}
220 IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
221 HOST: {get_param: CloudNameCtlplane}
225 IP: {get_attr: [VipMap, net_ip_map, internal_api]}
226 HOST: {get_param: CloudNameInternal}
230 IP: {get_attr: [VipMap, net_ip_map, storage]}
231 HOST: {get_param: CloudNameStorage}
235 IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
236 HOST: {get_param: CloudNameStorageManagement}
238 HeatAuthEncryptionKey:
239 type: OS::Heat::RandomString
242 type: OS::Heat::RandomString
247 type: OS::Heat::RandomString
252 type: OS::TripleO::ServiceNetMap
255 type: OS::TripleO::EndpointMap
258 external: {get_param: CloudName}
259 internal_api: {get_param: CloudNameInternal}
260 storage: {get_param: CloudNameStorage}
261 storage_mgmt: {get_param: CloudNameStorageManagement}
262 ctlplane: {get_param: CloudNameCtlplane}
263 NetIpMap: {get_attr: [VipMap, net_ip_map]}
264 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
267 type: OS::Heat::Value
270 value: {get_attr: [EndpointMap, endpoint_map]}
273 type: OS::TripleO::Ssh::KnownHostsConfig
278 {% for role in roles %}
279 - {get_attr: [{{role.name}}, known_hosts_entry]}
282 # Jinja loop for Role in roles_data.yaml
283 {% for role in roles %}
284 # Resources generated for {{role.name}} Role
285 {{role.name}}ServiceChain:
286 type: OS::TripleO::Services
289 get_param: {{role.name}}Services
290 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
291 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
292 DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
293 RoleName: {{role.name}}
294 RoleParameters: {get_param: {{role.name}}Parameters}
296 # Filter any null/None service_names which may be present due to mapping
297 # of services to OS::Heat::None
298 {{role.name}}ServiceNames:
299 type: OS::Heat::Value
300 depends_on: {{role.name}}ServiceChain
302 type: comma_delimited_list
305 expression: coalesce($.data, []).where($ != null)
306 data: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
308 {{role.name}}HostsDeployment:
309 type: OS::Heat::StructuredDeployments
311 name: {{role.name}}HostsDeployment
312 config: {get_attr: [hostsConfig, config_id]}
313 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
315 {{role.name}}SshKnownHostsDeployment:
316 type: OS::Heat::StructuredDeployments
318 name: {{role.name}}SshKnownHostsDeployment
319 config: {get_resource: SshKnownHostsConfig}
320 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
322 {{role.name}}AllNodesDeployment:
323 type: OS::Heat::StructuredDeployments
325 {% for role_inner in roles %}
326 - {{role_inner.name}}HostsDeployment
329 name: {{role.name}}AllNodesDeployment
330 config: {get_attr: [allNodesConfig, config_id]}
331 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
333 # Note we have to use yaql to look up the first hostname/ip in the
334 # list because heat path based attributes operate on the attribute
335 # inside the ResourceGroup, not the exposed list ref discussion in
336 # https://bugs.launchpad.net/heat/+bug/1640488
337 # The coalesce is needed because $.data is None during heat validation
340 expression: coalesce($.data, []).first(null)
341 data: {get_attr: [{{role.name}}, hostname]}
344 expression: coalesce($.data, []).first(null)
345 data: {get_attr: [{{role.name}}, ip_address]}
347 {{role.name}}AllNodesValidationDeployment:
348 type: OS::Heat::StructuredDeployments
349 depends_on: {{role.name}}AllNodesDeployment
351 name: {{role.name}}AllNodesValidationDeployment
352 config: {get_resource: AllNodesValidationConfig}
353 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
355 {{role.name}}IpListMap:
356 type: OS::TripleO::Network::Ports::NetIpListMap
358 ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
359 ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
360 InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
361 StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
362 StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
363 TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
364 ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
365 EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
366 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
367 ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
369 # Note (shardy) this somewhat complex yaql may be replaced
370 # with a map_deep_merge function in ocata. It merges the
371 # list of maps, but appends to colliding lists so we can
372 # create a map of lists for all nodes for each network
374 expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
376 - {get_attr: [{{role.name}}, hostname_map]}
379 type: OS::Heat::ResourceGroup
383 max_batch_size: {get_param: NodeCreateBatchSize}
385 count: {get_param: {{role.name}}Count}
386 removal_policies: {get_param: {{role.name}}RemovalPolicies}
388 type: OS::TripleO::{{role.name}}
390 CloudDomain: {get_param: CloudDomain}
391 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
392 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
395 template: {get_param: {{role.name}}HostnameFormat}
397 '%stackname%': {get_param: 'OS::stack_name'}
399 {% if role.name != 'Compute' %}
400 {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
402 NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
404 ServiceConfigSettings:
406 - get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
408 - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
410 # This next step combines two yaql passes:
411 # - The inner one does a deep merge on the service_config_settings for all roles
412 # - The outer one filters the map based on the services enabled for the role
413 # then merges the result into one map.
415 expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
419 expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
422 - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
424 services: {get_attr: [{{role.name}}ServiceNames, value]}
425 ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
426 MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
427 ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChain, role_data, service_metadata_settings]}
431 type: OS::TripleO::Hosts::SoftwareConfig
437 - add_vips_to_etc_hosts
438 - {get_attr: [VipHosts, value]}
441 {% for role in roles %}
444 - {get_attr: [{{role.name}}, hosts_entry]}
448 type: OS::TripleO::AllNodes::SoftwareConfig
450 cloud_name_external: {get_param: CloudName}
451 cloud_name_internal_api: {get_param: CloudNameInternal}
452 cloud_name_storage: {get_param: CloudNameStorage}
453 cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
454 cloud_name_ctlplane: {get_param: CloudNameCtlplane}
458 {% for role in roles %}
459 - {get_attr: [{{role.name}}ServiceNames, value]}
464 $.data.groups.flatten()
467 {% for role in roles %}
468 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
473 $.data.sources.flatten()
476 {% for role in roles %}
477 - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
479 controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
480 controller_names: {get_attr: [{{primary_role_name}}, hostname]}
482 # Note (shardy) this somewhat complex yaql may be replaced
483 # with a map_deep_merge function in ocata. It merges the
484 # list of maps, but appends to colliding lists when a service
485 # is deployed on more than one role
487 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
490 {% for role in roles %}
491 - {get_attr: [{{role.name}}IpListMap, service_ips]}
495 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
498 {% for role in roles %}
499 - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
501 short_service_node_names:
503 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
506 {% for role in roles %}
507 - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
509 short_service_bootstrap_node:
511 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
514 {% for role in roles %}
515 - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
517 # FIXME(shardy): These require further work to move into service_ips
518 memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
519 NetVipMap: {get_attr: [VipMap, net_ip_map]}
520 RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
521 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
522 DeployIdentifier: {get_param: DeployIdentifier}
523 UpdateIdentifier: {get_param: UpdateIdentifier}
526 type: OS::Heat::RandomString
531 type: OS::Heat::RandomString
534 salt: {get_param: RabbitCookieSalt}
537 type: OS::TripleO::DefaultPasswords
539 DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
540 DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
541 DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
542 DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
543 DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
545 # creates the network architecture
547 type: OS::TripleO::Network
550 type: OS::TripleO::Network::Ports::ControlPlaneVipPort
553 name: control_virtual_ip
554 network: {get_param: NeutronControlPlaneID}
555 fixed_ips: {get_param: ControlFixedIPs}
556 replacement_policy: AUTO
560 type: OS::TripleO::Network::Ports::RedisVipPort
562 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
563 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
564 PortName: redis_virtual_ip
565 NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
567 FixedIPs: {get_param: RedisVirtualFixedIPs}
569 # The public VIP is on the External net, falls back to ctlplane
572 type: OS::TripleO::Network::Ports::ExternalVipPort
574 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
575 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
576 PortName: public_virtual_ip
577 FixedIPs: {get_param: PublicVirtualFixedIPs}
579 InternalApiVirtualIP:
581 type: OS::TripleO::Network::Ports::InternalApiVipPort
583 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
584 PortName: internal_api_virtual_ip
585 FixedIPs: {get_param: InternalApiVirtualFixedIPs}
589 type: OS::TripleO::Network::Ports::StorageVipPort
591 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
592 PortName: storage_virtual_ip
593 FixedIPs: {get_param: StorageVirtualFixedIPs}
595 StorageMgmtVirtualIP:
597 type: OS::TripleO::Network::Ports::StorageMgmtVipPort
599 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
600 PortName: storage_management_virtual_ip
601 FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
604 type: OS::TripleO::Network::Ports::NetVipMap
606 ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
607 ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
608 ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
609 InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
610 InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
611 StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
612 StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
613 StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
614 StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
615 # No tenant or management VIP required
617 # All Nodes Validations
618 AllNodesValidationConfig:
619 type: OS::TripleO::AllNodes::Validation
625 expression: coalesce($.data, []).first(null)
626 data: {get_attr: [{{primary_role_name}}, external_ip_address]}
628 expression: coalesce($.data, []).first(null)
629 data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
631 expression: coalesce($.data, []).first(null)
632 data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
634 expression: coalesce($.data, []).first(null)
635 data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
637 expression: coalesce($.data, []).first(null)
638 data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
640 expression: coalesce($.data, []).first(null)
641 data: {get_attr: [{{primary_role_name}}, management_ip_address]}
644 type: OS::TripleO::Tasks::UpdateWorkflow
646 {% for role in roles %}
647 - {{role.name}}AllNodesDeployment
651 {% for role in roles %}
652 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
655 deploy_identifier: {get_param: DeployIdentifier}
656 update_identifier: {get_param: UpdateIdentifier}
658 # Optional ExtraConfig for all nodes - all roles are passed in here, but
659 # the nested template may configure each role differently (or not at all)
661 type: OS::TripleO::AllNodesExtraConfig
664 {% for role in roles %}
665 - {{role.name}}AllNodesValidationDeployment
669 {% for role in roles %}
670 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
673 # Post deployment steps for all roles
675 type: OS::TripleO::PostDeploySteps
677 - AllNodesExtraConfig
678 {% for role in roles %}
679 - {{role.name}}AllNodesDeployment
683 {% for role in roles %}
684 {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
686 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
688 {% for role in roles %}
689 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
694 description: Asserts that the keystone endpoints have been provisioned.
697 description: URL for the Overcloud Keystone service
698 value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
700 description: Keystone Admin VIP endpoint
701 value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
704 Mapping of the resources with the needed info for their endpoints.
705 This includes the protocol used, the IP, port and also a full
706 representation of the URI.
707 value: {get_attr: [EndpointMapData, value]}
710 The content that should be appended to your /etc/hosts if you want to get
711 hostname-based access to the deployed nodes (useful for testing without
716 - - {get_attr: [hostsConfig, hosts_entries]}
717 - - {get_attr: [VipHosts, value]}
719 description: The services enabled on each role
721 {% for role in roles %}
722 {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
725 description: The configuration data associated with each role
727 {% for role in roles %}
728 {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
731 description: Mapping of each network to a list of IPs for each role
733 {% for role in roles %}
734 {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}