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 must match the
105 overcloud_domain_name configured on the undercloud.
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.
200 DeploymentServerBlacklist:
202 type: comma_delimited_list
204 List of server hostnames to blacklist from any triggered deployments.
207 add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
212 type: OS::Heat::Value
221 IP: {get_attr: [VipMap, net_ip_map, external]}
222 HOST: {get_param: CloudName}
226 IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
227 HOST: {get_param: CloudNameCtlplane}
231 IP: {get_attr: [VipMap, net_ip_map, internal_api]}
232 HOST: {get_param: CloudNameInternal}
236 IP: {get_attr: [VipMap, net_ip_map, storage]}
237 HOST: {get_param: CloudNameStorage}
241 IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
242 HOST: {get_param: CloudNameStorageManagement}
244 HeatAuthEncryptionKey:
245 type: OS::Heat::RandomString
248 type: OS::Heat::RandomString
253 type: OS::Heat::RandomString
258 type: OS::TripleO::ServiceNetMap
261 type: OS::TripleO::EndpointMap
264 external: {get_param: CloudName}
265 internal_api: {get_param: CloudNameInternal}
266 storage: {get_param: CloudNameStorage}
267 storage_mgmt: {get_param: CloudNameStorageManagement}
268 ctlplane: {get_param: CloudNameCtlplane}
269 NetIpMap: {get_attr: [VipMap, net_ip_map]}
270 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
273 type: OS::Heat::Value
276 value: {get_attr: [EndpointMap, endpoint_map]}
279 type: OS::TripleO::Ssh::KnownHostsConfig
284 {% for role in roles %}
285 - {get_attr: [{{role.name}}, known_hosts_entry]}
288 # Jinja loop for Role in roles_data.yaml
289 {% for role in roles %}
290 # Resources generated for {{role.name}} Role
291 {{role.name}}ServiceChain:
292 type: OS::TripleO::Services
295 get_param: {{role.name}}Services
296 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
297 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
298 DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
299 RoleName: {{role.name}}
300 RoleParameters: {get_param: {{role.name}}Parameters}
302 # Lookup of role_data via heat outputs is slow, so workaround this by caching
303 # the value in an OS::Heat::Value resource
304 {{role.name}}ServiceChainRoleData:
305 type: OS::Heat::Value
308 value: {get_attr: [{{role.name}}ServiceChain, role_data]}
310 # Filter any null/None service_names which may be present due to mapping
311 # of services to OS::Heat::None
312 {{role.name}}ServiceNames:
313 type: OS::Heat::Value
314 depends_on: {{role.name}}ServiceChain
316 type: comma_delimited_list
319 expression: coalesce($.data, []).where($ != null)
320 data: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_names]}
322 {{role.name}}HostsDeployment:
323 type: OS::Heat::StructuredDeployments
325 name: {{role.name}}HostsDeployment
326 config: {get_attr: [hostsConfig, config_id]}
327 servers: {get_attr: [{{role.name}}Servers, value]}
329 {{role.name}}SshKnownHostsDeployment:
330 type: OS::Heat::StructuredDeployments
332 name: {{role.name}}SshKnownHostsDeployment
333 config: {get_resource: SshKnownHostsConfig}
334 servers: {get_attr: [{{role.name}}Servers, value]}
336 {{role.name}}AllNodesDeployment:
337 type: OS::Heat::StructuredDeployments
339 {% for role_inner in roles %}
340 - {{role_inner.name}}HostsDeployment
343 name: {{role.name}}AllNodesDeployment
344 config: {get_attr: [allNodesConfig, config_id]}
345 servers: {get_attr: [{{role.name}}Servers, value]}
347 # Note we have to use yaql to look up the first hostname/ip in the
348 # list because heat path based attributes operate on the attribute
349 # inside the ResourceGroup, not the exposed list ref discussion in
350 # https://bugs.launchpad.net/heat/+bug/1640488
351 # The coalesce is needed because $.data is None during heat validation
354 expression: coalesce($.data, []).first(null)
355 data: {get_attr: [{{role.name}}, hostname]}
358 expression: coalesce($.data, []).first(null)
359 data: {get_attr: [{{role.name}}, ip_address]}
361 {{role.name}}AllNodesValidationDeployment:
362 type: OS::Heat::StructuredDeployments
363 depends_on: {{role.name}}AllNodesDeployment
365 name: {{role.name}}AllNodesValidationDeployment
366 config: {get_resource: AllNodesValidationConfig}
367 servers: {get_attr: [{{role.name}}Servers, value]}
369 {{role.name}}IpListMap:
370 type: OS::TripleO::Network::Ports::NetIpListMap
372 ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
373 ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
374 InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
375 StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
376 StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
377 TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
378 ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
379 EnabledServices: {get_attr: [{{role.name}}ServiceNames, value]}
380 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
381 ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
382 NetworkHostnameMap: {get_attr: [{{role.name}}NetworkHostnameMap, value]}
384 {{role.name}}NetworkHostnameMap:
385 type: OS::Heat::Value
389 # Note (shardy) this somewhat complex yaql may be replaced
390 # with a map_deep_merge function in ocata. It merges the
391 # list of maps, but appends to colliding lists so we can
392 # create a map of lists for all nodes for each network
394 expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
396 - {get_attr: [{{role.name}}, hostname_map]}
399 type: OS::Heat::ResourceGroup
403 max_batch_size: {get_param: NodeCreateBatchSize}
405 count: {get_param: {{role.name}}Count}
406 removal_policies: {get_param: {{role.name}}RemovalPolicies}
408 type: OS::TripleO::{{role.name}}
410 CloudDomain: {get_param: CloudDomain}
411 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
412 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
415 template: {get_param: {{role.name}}HostnameFormat}
417 '%stackname%': {get_param: 'OS::stack_name'}
419 {% if role.name != 'Compute' %}
420 {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
422 NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
424 ServiceConfigSettings:
426 - get_attr: [{{role.name}}ServiceChainRoleData, value, config_settings]
428 - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
430 # This next step combines two yaql passes:
431 # - The inner one does a deep merge on the service_config_settings for all roles
432 # - The outer one filters the map based on the services enabled for the role
433 # then merges the result into one map.
435 expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
439 expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
442 - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
444 services: {get_attr: [{{role.name}}ServiceNames, value]}
445 ServiceNames: {get_attr: [{{role.name}}ServiceNames, value]}
446 MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChainRoleData, value, monitoring_subscriptions]}
447 ServiceMetadataSettings: {get_attr: [{{role.name}}ServiceChainRoleData, value, service_metadata_settings]}
448 DeploymentServerBlacklistDict: {get_attr: [DeploymentServerBlacklistDict, value]}
449 RoleParameters: {get_param: {{role.name}}Parameters}
452 {% for role in roles %}
453 {{role.name}}Servers:
454 type: OS::Heat::Value
455 depends_on: {{role.name}}
460 expression: let(servers=>switch(isDict($.data.servers) => $.data.servers, true => {})) -> $servers.deleteAll($servers.keys().where($servers[$] = null))
462 servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
465 # This resource just creates a dict out of the DeploymentServerBlacklist,
466 # which is a list. The dict is used in the role templates to set a condition
467 # on whether to create the deployment resources. We can't use the list
468 # directly because there is no way to ask Heat if a list contains a specific
470 DeploymentServerBlacklistDict:
471 type: OS::Heat::Value
480 hostname: {get_param: DeploymentServerBlacklist}
483 type: OS::TripleO::Hosts::SoftwareConfig
489 - add_vips_to_etc_hosts
490 - {get_attr: [VipHosts, value]}
493 {% for role in roles %}
496 - {get_attr: [{{role.name}}, hosts_entry]}
500 type: OS::TripleO::AllNodes::SoftwareConfig
502 cloud_name_external: {get_param: CloudName}
503 cloud_name_internal_api: {get_param: CloudNameInternal}
504 cloud_name_storage: {get_param: CloudNameStorage}
505 cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
506 cloud_name_ctlplane: {get_param: CloudNameCtlplane}
510 {% for role in roles %}
511 - {get_attr: [{{role.name}}ServiceNames, value]}
516 $.data.groups.flatten()
519 {% for role in roles %}
520 - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_groups]}
525 $.data.sources.flatten()
528 {% for role in roles %}
529 - {get_attr: [{{role.name}}ServiceChainRoleData, value, logging_sources]}
531 controller_ips: {get_attr: [{{primary_role_name}}, ip_address]}
532 controller_names: {get_attr: [{{primary_role_name}}, hostname]}
534 # Note (shardy) this somewhat complex yaql may be replaced
535 # with a map_deep_merge function in ocata. It merges the
536 # list of maps, but appends to colliding lists when a service
537 # is deployed on more than one role
539 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
542 {% for role in roles %}
543 - {get_attr: [{{role.name}}IpListMap, service_ips]}
547 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
550 {% for role in roles %}
551 - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
553 short_service_node_names:
555 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
558 {% for role in roles %}
559 - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
561 short_service_bootstrap_node:
563 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten().first()]))
566 {% for role in roles %}
567 - {get_attr: [{{role.name}}IpListMap, short_service_bootstrap_hostnames]}
569 # FIXME(shardy): These require further work to move into service_ips
570 memcache_node_ips: {get_attr: [{{primary_role_name}}IpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
571 NetVipMap: {get_attr: [VipMap, net_ip_map]}
572 RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
573 ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
574 DeployIdentifier: {get_param: DeployIdentifier}
575 UpdateIdentifier: {get_param: UpdateIdentifier}
578 type: OS::Heat::RandomString
583 type: OS::Heat::RandomString
586 salt: {get_param: RabbitCookieSalt}
589 type: OS::TripleO::DefaultPasswords
591 DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
592 DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
593 DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
594 DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
595 DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
597 # creates the network architecture
599 type: OS::TripleO::Network
602 type: OS::TripleO::Network::Ports::ControlPlaneVipPort
605 name: control_virtual_ip
606 network: {get_param: NeutronControlPlaneID}
607 fixed_ips: {get_param: ControlFixedIPs}
608 replacement_policy: AUTO
612 type: OS::TripleO::Network::Ports::RedisVipPort
614 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
615 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
616 PortName: redis_virtual_ip
617 NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
619 FixedIPs: {get_param: RedisVirtualFixedIPs}
621 # The public VIP is on the External net, falls back to ctlplane
624 type: OS::TripleO::Network::Ports::ExternalVipPort
626 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
627 ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
628 PortName: public_virtual_ip
629 FixedIPs: {get_param: PublicVirtualFixedIPs}
631 InternalApiVirtualIP:
633 type: OS::TripleO::Network::Ports::InternalApiVipPort
635 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
636 PortName: internal_api_virtual_ip
637 FixedIPs: {get_param: InternalApiVirtualFixedIPs}
641 type: OS::TripleO::Network::Ports::StorageVipPort
643 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
644 PortName: storage_virtual_ip
645 FixedIPs: {get_param: StorageVirtualFixedIPs}
647 StorageMgmtVirtualIP:
649 type: OS::TripleO::Network::Ports::StorageMgmtVipPort
651 ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
652 PortName: storage_management_virtual_ip
653 FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
656 type: OS::TripleO::Network::Ports::NetVipMap
658 ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
659 ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
660 ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
661 InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
662 InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
663 StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
664 StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
665 StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
666 StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
667 # No tenant or management VIP required
669 # All Nodes Validations
670 AllNodesValidationConfig:
671 type: OS::TripleO::AllNodes::Validation
677 expression: coalesce($.data, []).first(null)
678 data: {get_attr: [{{primary_role_name}}, external_ip_address]}
680 expression: coalesce($.data, []).first(null)
681 data: {get_attr: [{{primary_role_name}}, internal_api_ip_address]}
683 expression: coalesce($.data, []).first(null)
684 data: {get_attr: [{{primary_role_name}}, storage_ip_address]}
686 expression: coalesce($.data, []).first(null)
687 data: {get_attr: [{{primary_role_name}}, storage_mgmt_ip_address]}
689 expression: coalesce($.data, []).first(null)
690 data: {get_attr: [{{primary_role_name}}, tenant_ip_address]}
692 expression: coalesce($.data, []).first(null)
693 data: {get_attr: [{{primary_role_name}}, management_ip_address]}
696 type: OS::TripleO::Tasks::UpdateWorkflow
698 {% for role in roles %}
699 - {{role.name}}AllNodesDeployment
703 {% for role in roles %}
704 {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
707 deploy_identifier: {get_param: DeployIdentifier}
708 update_identifier: {get_param: UpdateIdentifier}
710 # Optional ExtraConfig for all nodes - all roles are passed in here, but
711 # the nested template may configure each role differently (or not at all)
713 type: OS::TripleO::AllNodesExtraConfig
716 {% for role in roles %}
717 - {{role.name}}AllNodesValidationDeployment
721 {% for role in roles %}
722 {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
725 # Post deployment steps for all roles
727 type: OS::TripleO::PostDeploySteps
729 - AllNodesExtraConfig
730 {% for role in roles %}
731 - {{role.name}}AllNodesDeployment
735 {% for role in roles %}
736 {{role.name}}: {get_attr: [{{role.name}}Servers, value]}
738 stack_name: {get_param: 'OS::stack_name'}
739 EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
740 ctlplane_service_ips:
741 # Note (shardy) this somewhat complex yaql may be replaced
742 # with a map_deep_merge function in ocata. It merges the
743 # list of maps, but appends to colliding lists when a service
744 # is deployed on more than one role
746 expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
749 {% for role in roles %}
750 - {get_attr: [{{role.name}}IpListMap, ctlplane_service_ips]}
753 {% for role in roles %}
754 {{role.name}}: {get_attr: [{{role.name}}ServiceChainRoleData, value]}
759 description: Asserts that the keystone endpoints have been provisioned.
762 description: URL for the Overcloud Keystone service
763 value: {get_attr: [EndpointMapData, value, KeystonePublic, uri]}
765 description: Keystone Admin VIP endpoint
766 value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
769 Mapping of the resources with the needed info for their endpoints.
770 This includes the protocol used, the IP, port and also a full
771 representation of the URI.
772 value: {get_attr: [EndpointMapData, value]}
775 The content that should be appended to your /etc/hosts if you want to get
776 hostname-based access to the deployed nodes (useful for testing without
781 - - {get_attr: [hostsConfig, hosts_entries]}
782 - - {get_attr: [VipHosts, value]}
784 description: The services enabled on each role
786 {% for role in roles %}
787 {{role.name}}: {get_attr: [{{role.name}}ServiceNames, value]}
790 description: The configuration data associated with each role
792 {% for role in roles %}
793 {{role.name}}: {get_attr: [{{role.name}}ServiceChainRoleData, value]}
796 description: Mapping of each network to a list of IPs for each role
798 {% for role in roles %}
799 {{role.name}}: {get_attr: [{{role.name}}IpListMap, net_ip_map]}
802 description: Mapping of each network to a list of hostnames for each role
804 {% for role in roles %}
805 {{role.name}}: {get_attr: [{{role.name}}NetworkHostnameMap, value]}