Merge "Make Ceilometer notifications non-blocking"
[apex-tripleo-heat-templates.git] / overcloud.j2.yaml
1 heat_template_version: 2016-10-14
2
3 description: >
4   Deploy an OpenStack environment, consisting of several node types (roles),
5   Controller, Compute, BlockStorage, SwiftStorage and CephStorage. The Storage
6   roles enable independent scaling of the storage components, but the minimal
7   deployment is one Controller and one Compute node.
8
9
10 # TODO(shadower): we should probably use the parameter groups to put
11 # some order in here.
12 parameters:
13
14   # Common parameters (not specific to a role)
15   CloudName:
16     default: overcloud.localdomain
17     description: The DNS name of this cloud. E.g. ci-overcloud.tripleo.org
18     type: string
19   CloudNameInternal:
20     default: overcloud.internalapi.localdomain
21     description: >
22       The DNS name of this cloud's internal API endpoint. E.g.
23       'ci-overcloud.internalapi.tripleo.org'.
24     type: string
25   CloudNameStorage:
26     default: overcloud.storage.localdomain
27     description: >
28       The DNS name of this cloud's storage endpoint. E.g.
29       'ci-overcloud.storage.tripleo.org'.
30     type: string
31   CloudNameStorageManagement:
32     default: overcloud.storagemgmt.localdomain
33     description: >
34       The DNS name of this cloud's storage management endpoint. E.g.
35       'ci-overcloud.storagemgmt.tripleo.org'.
36     type: string
37   CloudNameCtlplane:
38     default: overcloud.ctlplane.localdomain
39     description: >
40       The DNS name of this cloud's storage management endpoint. E.g.
41       'ci-overcloud.management.tripleo.org'.
42     type: string
43   ControlFixedIPs:
44     default: []
45     description: Should be used for arbitrary ips.
46     type: json
47   InternalApiVirtualFixedIPs:
48     default: []
49     description: >
50         Control the IP allocation for the InternalApiVirtualInterface port. E.g.
51         [{'ip_address':'1.2.3.4'}]
52     type: json
53   NeutronControlPlaneID:
54     default: 'ctlplane'
55     type: string
56     description: Neutron ID or name for ctlplane network.
57   NeutronPublicInterface:
58     default: nic1
59     description: What interface to bridge onto br-ex for network nodes.
60     type: string
61   PublicVirtualFixedIPs:
62     default: []
63     description: >
64         Control the IP allocation for the PublicVirtualInterface port. E.g.
65         [{'ip_address':'1.2.3.4'}]
66     type: json
67   RabbitCookieSalt:
68     type: string
69     default: unset
70     description: Salt for the rabbit cookie, change this to force the randomly generated rabbit cookie to change.
71   StorageVirtualFixedIPs:
72     default: []
73     description: >
74         Control the IP allocation for the StorageVirtualInterface port. E.g.
75         [{'ip_address':'1.2.3.4'}]
76     type: json
77   StorageMgmtVirtualFixedIPs:
78     default: []
79     description: >
80         Control the IP allocation for the StorageMgmgVirtualInterface port. E.g.
81         [{'ip_address':'1.2.3.4'}]
82     type: json
83   RedisVirtualFixedIPs:
84     default: []
85     description: >
86         Control the IP allocation for the virtual IP used by Redis. E.g.
87         [{'ip_address':'1.2.3.4'}]
88     type: json
89   CloudDomain:
90     default: 'localdomain'
91     type: string
92     description: >
93       The DNS domain used for the hosts. This should match the dhcp_domain
94       configured in the Undercloud neutron. Defaults to localdomain.
95   ServerMetadata:
96     default: {}
97     description: >
98       Extra properties or metadata passed to Nova for the created nodes in
99       the overcloud. It's accessible via the Nova metadata API.
100     type: json
101
102 # Compute-specific params
103 # FIXME(shardy) handle these deprecated names as they don't match compute.yaml
104   HypervisorNeutronPhysicalBridge:
105     default: 'br-ex'
106     description: >
107       An OVS bridge to create on each hypervisor. This defaults to br-ex the
108       same as the control plane nodes, as we have a uniform configuration of
109       the openvswitch agent. Typically should not need to be changed.
110     type: string
111   HypervisorNeutronPublicInterface:
112     default: nic1
113     description: What interface to add to the HypervisorNeutronPhysicalBridge.
114     type: string
115
116   # Jinja loop for Role in role_data.yaml
117 {% for role in roles %}
118   # Parameters generated for {{role.name}} Role
119   {{role.name}}Services:
120     description: A list of service resources (configured in the Heat
121                  resource_registry) which represent nested stacks
122                  for each service that should get installed on the {{role.name}} role.
123     type: comma_delimited_list
124
125   {{role.name}}Count:
126     description: Number of {{role.name}} nodes to deploy
127     type: number
128     default: {{role.CountDefault|default(0)}}
129
130   {{role.name}}HostnameFormat:
131     type: string
132     description: >
133       Format for {{role.name}} node hostnames
134       Note %index% is translated into the index of the node, e.g 0/1/2 etc
135       and %stackname% is replaced with the stack name e.g overcloud
136   {% if role.HostnameFormatDefault %}
137     default: "{{role.HostnameFormatDefault}}"
138   {% else %}
139     default: "%stackname%-{{role.name.lower()}}-%index%"
140   {% endif %}
141
142   {{role.name}}RemovalPolicies:
143     default: []
144     type: json
145     description: >
146       List of resources to be removed from {{role.name}} ResourceGroup when
147       doing an update which requires removal of specific resources.
148       Example format ComputeRemovalPolicies: [{'resource_list': ['0']}]
149
150 {% if role.name != 'Compute' %}
151   {{role.name}}SchedulerHints:
152 {% else %}
153   NovaComputeSchedulerHints:
154 {% endif %}
155     type: json
156     description: Optional scheduler hints to pass to nova
157     default: {}
158 {% endfor %}
159
160   # Identifiers to trigger tasks on nodes
161   UpdateIdentifier:
162     default: ''
163     type: string
164     description: >
165       Setting to a previously unused value during stack-update will trigger
166       package update on all nodes
167   DeployIdentifier:
168     default: ''
169     type: string
170     description: >
171       Setting this to a unique value will re-run any deployment tasks which
172       perform configuration on a Heat stack-update.
173
174 resources:
175
176   HeatAuthEncryptionKey:
177     type: OS::Heat::RandomString
178
179   PcsdPassword:
180     type: OS::Heat::RandomString
181     properties:
182       length: 16
183
184   HorizonSecret:
185     type: OS::Heat::RandomString
186     properties:
187       length: 10
188
189   ServiceNetMap:
190     type: OS::TripleO::ServiceNetMap
191
192   EndpointMap:
193     type: OS::TripleO::EndpointMap
194     properties:
195       CloudEndpoints:
196         external: {get_param: CloudName}
197         internal_api: {get_param: CloudNameInternal}
198         storage: {get_param: CloudNameStorage}
199         storage_mgmt: {get_param: CloudNameStorageManagement}
200         ctlplane: {get_param: CloudNameCtlplane}
201       NetIpMap: {get_attr: [VipMap, net_ip_map]}
202       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
203
204   # Jinja loop for Role in roles_data.yaml
205 {% for role in roles %}
206   # Resources generated for {{role.name}} Role
207   {{role.name}}ServiceChain:
208     type: OS::TripleO::Services
209     properties:
210       Services:
211         get_param: {{role.name}}Services
212       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
213       EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
214       DefaultPasswords: {get_attr: [DefaultPasswords, passwords]}
215
216   {{role.name}}HostsDeployment:
217     type: OS::Heat::StructuredDeployments
218     properties:
219       name: {{role.name}}HostsDeployment
220       config: {get_attr: [hostsConfig, config_id]}
221       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
222
223   {{role.name}}AllNodesDeployment:
224     type: OS::Heat::StructuredDeployments
225     depends_on:
226 {% for role_inner in roles %}
227       - {{role_inner.name}}HostsDeployment
228 {% endfor %}
229     properties:
230       name: {{role.name}}AllNodesDeployment
231       config: {get_attr: [allNodesConfig, config_id]}
232       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
233       input_values:
234         # Note we have to use yaql to look up the first hostname/ip in the
235         # list because heat path based attributes operate on the attribute
236         # inside the ResourceGroup, not the exposed list ref discussion in
237         # https://bugs.launchpad.net/heat/+bug/1640488
238         # The coalesce is needed because $.data is None during heat validation
239         bootstrap_nodeid:
240           yaql:
241             expression: coalesce($.data, []).first(null)
242             data: {get_attr: [{{role.name}}, hostname]}
243         bootstrap_nodeid_ip:
244           yaql:
245             expression: coalesce($.data, []).first(null)
246             data: {get_attr: [{{role.name}}, ip_address]}
247
248   {{role.name}}AllNodesValidationDeployment:
249     type: OS::Heat::StructuredDeployments
250     depends_on: {{role.name}}AllNodesDeployment
251     properties:
252       name: {{role.name}}AllNodesValidationDeployment
253       config: {get_resource: AllNodesValidationConfig}
254       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
255
256   {{role.name}}IpListMap:
257     type: OS::TripleO::Network::Ports::NetIpListMap
258     properties:
259       ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
260       ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
261       InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
262       StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
263       StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
264       TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
265       ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
266       EnabledServices: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
267       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
268       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
269       NetworkHostnameMap:
270         # Note (shardy) this somewhat complex yaql may be replaced
271         # with a map_deep_merge function in ocata.  It merges the
272         # list of maps, but appends to colliding lists so we can
273         # create a map of lists for all nodes for each network
274         yaql:
275           expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
276           data:
277             - {get_attr: [{{role.name}}, hostname_map]}
278
279   {{role.name}}:
280     type: OS::Heat::ResourceGroup
281     depends_on: Networks
282     properties:
283       count: {get_param: {{role.name}}Count}
284       removal_policies: {get_param: {{role.name}}RemovalPolicies}
285       resource_def:
286         type: OS::TripleO::{{role.name}}
287         properties:
288           CloudDomain: {get_param: CloudDomain}
289           ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
290           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
291           Hostname:
292             str_replace:
293               template: {get_param: {{role.name}}HostnameFormat}
294               params:
295                 '%stackname%': {get_param: 'OS::stack_name'}
296           NodeIndex: '%index%'
297   {% if role.name != 'Compute' %}
298           {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
299   {% else %}
300           NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
301   {% endif %}
302           ServiceConfigSettings:
303             map_merge:
304               -  get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
305           {% for r in roles %}
306               - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
307           {% endfor %}
308               # This next step combines two yaql passes:
309               # - The inner one does a deep merge on the service_config_settings for all roles
310               # - The outer one filters the map based on the services enabled for the role
311               #   then merges the result into one map.
312               - yaql:
313                   expression: let(root => $) -> $.data.map.items().where($[0] in coalesce($root.data.services, [])).select($[1]).reduce($1.mergeWith($2), {})
314                   data:
315                     map:
316                       yaql:
317                         expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
318                         data:
319                         {% for r in roles %}
320                           - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
321                         {% endfor %}
322                     services: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
323           ServiceNames: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
324           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
325 {% endfor %}
326
327   hostsConfig:
328     type: OS::TripleO::Hosts::SoftwareConfig
329     properties:
330       hosts:
331 {% for role in roles %}
332         - list_join:
333             - '\n'
334             - {get_attr: [{{role.name}}, hosts_entry]}
335 {% endfor %}
336
337   allNodesConfig:
338     type: OS::TripleO::AllNodes::SoftwareConfig
339     properties:
340       cloud_name_external: {get_param: CloudName}
341       cloud_name_internal_api: {get_param: CloudNameInternal}
342       cloud_name_storage: {get_param: CloudNameStorage}
343       cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
344       cloud_name_ctlplane: {get_param: CloudNameCtlplane}
345       enabled_services:
346         list_join:
347           - ','
348 {% for role in roles %}
349           - {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
350 {% endfor %}
351       logging_groups:
352         yaql:
353           expression: >
354             $.data.groups.flatten()
355           data:
356             groups:
357 {% for role in roles %}
358               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
359 {% endfor %}
360       logging_sources:
361         yaql:
362           expression: >
363             $.data.sources.flatten()
364           data:
365             sources:
366 {% for role in roles %}
367               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
368 {% endfor %}
369       controller_ips: {get_attr: [Controller, ip_address]}
370       controller_names: {get_attr: [Controller, hostname]}
371       service_ips:
372         # Note (shardy) this somewhat complex yaql may be replaced
373         # with a map_deep_merge function in ocata.  It merges the
374         # list of maps, but appends to colliding lists when a service
375         # is deployed on more than one role
376         yaql:
377           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
378           data:
379             l:
380 {% for role in roles %}
381               - {get_attr: [{{role.name}}IpListMap, service_ips]}
382 {% endfor %}
383       service_node_names:
384         yaql:
385           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
386           data:
387             l:
388 {% for role in roles %}
389               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
390 {% endfor %}
391       short_service_node_names:
392         yaql:
393           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
394           data:
395             l:
396 {% for role in roles %}
397               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
398 {% endfor %}
399       # FIXME(shardy): These require further work to move into service_ips
400       memcache_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
401       NetVipMap: {get_attr: [VipMap, net_ip_map]}
402       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
403       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
404       DeployIdentifier: {get_param: DeployIdentifier}
405       UpdateIdentifier: {get_param: UpdateIdentifier}
406
407   MysqlRootPassword:
408     type: OS::Heat::RandomString
409     properties:
410       length: 10
411
412   RabbitCookie:
413     type: OS::Heat::RandomString
414     properties:
415       length: 20
416       salt: {get_param: RabbitCookieSalt}
417
418   DefaultPasswords:
419     type: OS::TripleO::DefaultPasswords
420     properties:
421       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
422       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
423       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
424       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
425       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
426
427   # creates the network architecture
428   Networks:
429     type: OS::TripleO::Network
430
431   ControlVirtualIP:
432     type: OS::Neutron::Port
433     depends_on: Networks
434     properties:
435       name: control_virtual_ip
436       network: {get_param: NeutronControlPlaneID}
437       fixed_ips: {get_param: ControlFixedIPs}
438       replacement_policy: AUTO
439
440   RedisVirtualIP:
441     depends_on: Networks
442     type: OS::TripleO::Network::Ports::RedisVipPort
443     properties:
444       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
445       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
446       PortName: redis_virtual_ip
447       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
448       ServiceName: redis
449       FixedIPs: {get_param: RedisVirtualFixedIPs}
450
451   # The public VIP is on the External net, falls back to ctlplane
452   PublicVirtualIP:
453     depends_on: Networks
454     type: OS::TripleO::Network::Ports::ExternalVipPort
455     properties:
456       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
457       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
458       PortName: public_virtual_ip
459       FixedIPs: {get_param: PublicVirtualFixedIPs}
460
461   InternalApiVirtualIP:
462     depends_on: Networks
463     type: OS::TripleO::Network::Ports::InternalApiVipPort
464     properties:
465       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
466       PortName: internal_api_virtual_ip
467       FixedIPs: {get_param: InternalApiVirtualFixedIPs}
468
469   StorageVirtualIP:
470     depends_on: Networks
471     type: OS::TripleO::Network::Ports::StorageVipPort
472     properties:
473       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
474       PortName: storage_virtual_ip
475       FixedIPs: {get_param: StorageVirtualFixedIPs}
476
477   StorageMgmtVirtualIP:
478     depends_on: Networks
479     type: OS::TripleO::Network::Ports::StorageMgmtVipPort
480     properties:
481       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
482       PortName: storage_management_virtual_ip
483       FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
484
485   VipMap:
486     type: OS::TripleO::Network::Ports::NetVipMap
487     properties:
488       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
489       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
490       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
491       InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
492       InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
493       StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
494       StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
495       StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
496       StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
497       # No tenant or management VIP required
498
499   # All Nodes Validations
500   AllNodesValidationConfig:
501     type: OS::TripleO::AllNodes::Validation
502     properties:
503       PingTestIps:
504         list_join:
505         - ' '
506         - - {get_attr: [Controller, resource.0.external_ip_address]}
507           - {get_attr: [Controller, resource.0.internal_api_ip_address]}
508           - {get_attr: [Controller, resource.0.storage_ip_address]}
509           - {get_attr: [Controller, resource.0.storage_mgmt_ip_address]}
510           - {get_attr: [Controller, resource.0.tenant_ip_address]}
511           - {get_attr: [Controller, resource.0.management_ip_address]}
512
513   UpdateWorkflow:
514     type: OS::TripleO::Tasks::UpdateWorkflow
515     depends_on:
516 {% for role in roles %}
517       - {{role.name}}AllNodesDeployment
518 {% endfor %}
519     properties:
520       servers:
521 {% for role in roles %}
522         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
523 {% endfor %}
524       input_values:
525         deploy_identifier: {get_param: DeployIdentifier}
526         update_identifier: {get_param: UpdateIdentifier}
527
528   # Optional ExtraConfig for all nodes - all roles are passed in here, but
529   # the nested template may configure each role differently (or not at all)
530   AllNodesExtraConfig:
531     type: OS::TripleO::AllNodesExtraConfig
532     depends_on:
533       - UpdateWorkflow
534 {% for role in roles %}
535       - {{role.name}}AllNodesValidationDeployment
536 {% endfor %}
537     properties:
538 {% for role in roles %}
539       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
540 {% endfor %}
541
542   # Post deployment steps for all roles
543   AllNodesDeploySteps:
544     type: OS::TripleO::PostDeploySteps
545     depends_on:
546 {% for role in roles %}
547       - {{role.name}}AllNodesDeployment
548 {% endfor %}
549     properties:
550       servers:
551 {% for role in roles %}
552         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
553 {% endfor %}
554       role_data:
555 {% for role in roles %}
556         {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
557 {% endfor %}
558
559 outputs:
560   ManagedEndpoints:
561     description: Asserts that the keystone endpoints have been provisioned.
562     value: true
563   KeystoneURL:
564     description: URL for the Overcloud Keystone service
565     value: {get_attr: [EndpointMap, endpoint_map, KeystonePublic, uri]}
566   KeystoneAdminVip:
567     description: Keystone Admin VIP endpoint
568     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
569   EndpointMap:
570     description: |
571       Mapping of the resources with the needed info for their endpoints.
572       This includes the protocol used, the IP, port and also a full
573       representation of the URI.
574     value: {get_attr: [EndpointMap, endpoint_map]}
575   HostsEntry:
576     description: |
577       The content that should be appended to your /etc/hosts if you want to get
578       hostname-based access to the deployed nodes (useful for testing without
579       setting up a DNS).
580     value:
581       list_join:
582       - "\n"
583       - - {get_attr: [hostsConfig, hosts_entries]}
584       -
585         - str_replace:
586             template: IP  HOST
587             params:
588               IP: {get_attr: [VipMap, net_ip_map, external]}
589               HOST: {get_param: CloudName}
590         - str_replace:
591             template: IP  HOST
592             params:
593               IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
594               HOST: {get_param: CloudNameCtlplane}
595         - str_replace:
596             template: IP  HOST
597             params:
598               IP: {get_attr: [VipMap, net_ip_map, internal_api]}
599               HOST: {get_param: CloudNameInternal}
600         - str_replace:
601             template: IP  HOST
602             params:
603               IP: {get_attr: [VipMap, net_ip_map, storage]}
604               HOST: {get_param: CloudNameStorage}
605         - str_replace:
606             template: IP  HOST
607             params:
608               IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
609               HOST: {get_param: CloudNameStorageManagement}
610   EnabledServices:
611     description: The services enabled on each role
612     value:
613 {% for role in roles %}
614       {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
615 {% endfor %}