Removes deprecated overcloud VIP outputs
[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         bootstrap_nodeid: {get_attr: [{{role.name}}, resource.0.hostname]}
235         bootstrap_nodeid_ip: {get_attr: [{{role.name}}, resource.0.ip_address]}
236
237   {{role.name}}AllNodesValidationDeployment:
238     type: OS::Heat::StructuredDeployments
239     depends_on: {{role.name}}AllNodesDeployment
240     properties:
241       name: {{role.name}}AllNodesValidationDeployment
242       config: {get_resource: AllNodesValidationConfig}
243       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
244
245   {{role.name}}IpListMap:
246     type: OS::TripleO::Network::Ports::NetIpListMap
247     properties:
248       ControlPlaneIpList: {get_attr: [{{role.name}}, ip_address]}
249       ExternalIpList: {get_attr: [{{role.name}}, external_ip_address]}
250       InternalApiIpList: {get_attr: [{{role.name}}, internal_api_ip_address]}
251       StorageIpList: {get_attr: [{{role.name}}, storage_ip_address]}
252       StorageMgmtIpList: {get_attr: [{{role.name}}, storage_mgmt_ip_address]}
253       TenantIpList: {get_attr: [{{role.name}}, tenant_ip_address]}
254       ManagementIpList: {get_attr: [{{role.name}}, management_ip_address]}
255       EnabledServices: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
256       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
257       ServiceHostnameList: {get_attr: [{{role.name}}, hostname]}
258       NetworkHostnameMap:
259         # Note (shardy) this somewhat complex yaql may be replaced
260         # with a map_deep_merge function in ocata.  It merges the
261         # list of maps, but appends to colliding lists so we can
262         # create a map of lists for all nodes for each network
263         yaql:
264           expression: dict($.data.where($ != null).flatten().selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
265           data:
266             - {get_attr: [{{role.name}}, hostname_map]}
267
268   {{role.name}}:
269     type: OS::Heat::ResourceGroup
270     depends_on: Networks
271     properties:
272       count: {get_param: {{role.name}}Count}
273       removal_policies: {get_param: {{role.name}}RemovalPolicies}
274       resource_def:
275         type: OS::TripleO::{{role.name}}
276         properties:
277           CloudDomain: {get_param: CloudDomain}
278           ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map]}
279           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
280           Hostname:
281             str_replace:
282               template: {get_param: {{role.name}}HostnameFormat}
283               params:
284                 '%stackname%': {get_param: 'OS::stack_name'}
285           NodeIndex: '%index%'
286   {% if role.name != 'Compute' %}
287           {{role.name}}SchedulerHints: {get_param: {{role.name}}SchedulerHints}
288   {% else %}
289           NovaComputeSchedulerHints: {get_param: NovaComputeSchedulerHints}
290   {% endif %}
291           ServiceConfigSettings:
292             map_merge:
293               -  get_attr: [{{role.name}}ServiceChain, role_data, config_settings]
294           {% for r in roles %}
295               - get_attr: [{{r.name}}ServiceChain, role_data, global_config_settings]
296           {% endfor %}
297               # This next step combines two yaql passes:
298               # - The inner one does a deep merge on the service_config_settings for all roles
299               # - The outer one filters the map based on the services enabled for the role
300               #   then merges the result into one map.
301               - yaql:
302                   expression: let(root => $) -> $.data.map.items().where($[0] in $root.data.services).select($[1]).reduce($1.mergeWith($2), {})
303                   data:
304                     map:
305                       yaql:
306                         expression: $.data.where($ != null).reduce($1.mergeWith($2), {})
307                         data:
308                         {% for r in roles %}
309                           - get_attr: [{{r.name}}ServiceChain, role_data, service_config_settings]
310                         {% endfor %}
311                     services: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
312           ServiceNames: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
313           MonitoringSubscriptions: {get_attr: [{{role.name}}ServiceChain, role_data, monitoring_subscriptions]}
314 {% endfor %}
315
316   hostsConfig:
317     type: OS::TripleO::Hosts::SoftwareConfig
318     properties:
319       hosts:
320 {% for role in roles %}
321         - list_join:
322             - '\n'
323             - {get_attr: [{{role.name}}, hosts_entry]}
324 {% endfor %}
325
326   allNodesConfig:
327     type: OS::TripleO::AllNodes::SoftwareConfig
328     properties:
329       cloud_name_external: {get_param: CloudName}
330       cloud_name_internal_api: {get_param: CloudNameInternal}
331       cloud_name_storage: {get_param: CloudNameStorage}
332       cloud_name_storage_mgmt: {get_param: CloudNameStorageManagement}
333       cloud_name_ctlplane: {get_param: CloudNameCtlplane}
334       enabled_services:
335         list_join:
336           - ','
337 {% for role in roles %}
338           - {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
339 {% endfor %}
340       logging_groups:
341         yaql:
342           expression: >
343             $.data.groups.flatten()
344           data:
345             groups:
346 {% for role in roles %}
347               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_groups]}
348 {% endfor %}
349       logging_sources:
350         yaql:
351           expression: >
352             $.data.sources.flatten()
353           data:
354             sources:
355 {% for role in roles %}
356               - {get_attr: [{{role.name}}ServiceChain, role_data, logging_sources]}
357 {% endfor %}
358       controller_ips: {get_attr: [Controller, ip_address]}
359       controller_names: {get_attr: [Controller, hostname]}
360       service_ips:
361         # Note (shardy) this somewhat complex yaql may be replaced
362         # with a map_deep_merge function in ocata.  It merges the
363         # list of maps, but appends to colliding lists when a service
364         # is deployed on more than one role
365         yaql:
366           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
367           data:
368             l:
369 {% for role in roles %}
370               - {get_attr: [{{role.name}}IpListMap, service_ips]}
371 {% endfor %}
372       service_node_names:
373         yaql:
374           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
375           data:
376             l:
377 {% for role in roles %}
378               - {get_attr: [{{role.name}}IpListMap, service_hostnames]}
379 {% endfor %}
380       short_service_node_names:
381         yaql:
382           expression: dict($.data.l.where($ != null).selectMany($.items()).groupBy($[0], $[1], [$[0], $[1].flatten()]))
383           data:
384             l:
385 {% for role in roles %}
386               - {get_attr: [{{role.name}}IpListMap, short_service_hostnames]}
387 {% endfor %}
388       # FIXME(shardy): These require further work to move into service_ips
389       memcache_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, MemcachedNetwork]}]}
390       NetVipMap: {get_attr: [VipMap, net_ip_map]}
391       RedisVirtualIP: {get_attr: [RedisVirtualIP, ip_address]}
392       ServiceNetMap: {get_attr: [ServiceNetMap, service_net_map_lower]}
393       DeployIdentifier: {get_param: DeployIdentifier}
394       UpdateIdentifier: {get_param: UpdateIdentifier}
395
396   MysqlRootPassword:
397     type: OS::Heat::RandomString
398     properties:
399       length: 10
400
401   RabbitCookie:
402     type: OS::Heat::RandomString
403     properties:
404       length: 20
405       salt: {get_param: RabbitCookieSalt}
406
407   DefaultPasswords:
408     type: OS::TripleO::DefaultPasswords
409     properties:
410       DefaultMysqlRootPassword: {get_attr: [MysqlRootPassword, value]}
411       DefaultRabbitCookie: {get_attr: [RabbitCookie, value]}
412       DefaultHeatAuthEncryptionKey: {get_attr: [HeatAuthEncryptionKey, value]}
413       DefaultPcsdPassword: {get_attr: [PcsdPassword, value]}
414       DefaultHorizonSecret: {get_attr: [HorizonSecret, value]}
415
416   # creates the network architecture
417   Networks:
418     type: OS::TripleO::Network
419
420   ControlVirtualIP:
421     type: OS::Neutron::Port
422     depends_on: Networks
423     properties:
424       name: control_virtual_ip
425       network: {get_param: NeutronControlPlaneID}
426       fixed_ips: {get_param: ControlFixedIPs}
427       replacement_policy: AUTO
428
429   RedisVirtualIP:
430     depends_on: Networks
431     type: OS::TripleO::Network::Ports::RedisVipPort
432     properties:
433       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
434       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
435       PortName: redis_virtual_ip
436       NetworkName: {get_attr: [ServiceNetMap, service_net_map, RedisNetwork]}
437       ServiceName: redis
438       FixedIPs: {get_param: RedisVirtualFixedIPs}
439
440   # The public VIP is on the External net, falls back to ctlplane
441   PublicVirtualIP:
442     depends_on: Networks
443     type: OS::TripleO::Network::Ports::ExternalVipPort
444     properties:
445       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
446       ControlPlaneNetwork: {get_param: NeutronControlPlaneID}
447       PortName: public_virtual_ip
448       FixedIPs: {get_param: PublicVirtualFixedIPs}
449
450   InternalApiVirtualIP:
451     depends_on: Networks
452     type: OS::TripleO::Network::Ports::InternalApiVipPort
453     properties:
454       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
455       PortName: internal_api_virtual_ip
456       FixedIPs: {get_param: InternalApiVirtualFixedIPs}
457
458   StorageVirtualIP:
459     depends_on: Networks
460     type: OS::TripleO::Network::Ports::StorageVipPort
461     properties:
462       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
463       PortName: storage_virtual_ip
464       FixedIPs: {get_param: StorageVirtualFixedIPs}
465
466   StorageMgmtVirtualIP:
467     depends_on: Networks
468     type: OS::TripleO::Network::Ports::StorageMgmtVipPort
469     properties:
470       ControlPlaneIP: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
471       PortName: storage_management_virtual_ip
472       FixedIPs: {get_param: StorageMgmtVirtualFixedIPs}
473
474   VipMap:
475     type: OS::TripleO::Network::Ports::NetVipMap
476     properties:
477       ControlPlaneIp: {get_attr: [ControlVirtualIP, fixed_ips, 0, ip_address]}
478       ExternalIp: {get_attr: [PublicVirtualIP, ip_address]}
479       ExternalIpUri: {get_attr: [PublicVirtualIP, ip_address_uri]}
480       InternalApiIp: {get_attr: [InternalApiVirtualIP, ip_address]}
481       InternalApiIpUri: {get_attr: [InternalApiVirtualIP, ip_address_uri]}
482       StorageIp: {get_attr: [StorageVirtualIP, ip_address]}
483       StorageIpUri: {get_attr: [StorageVirtualIP, ip_address_uri]}
484       StorageMgmtIp: {get_attr: [StorageMgmtVirtualIP, ip_address]}
485       StorageMgmtIpUri: {get_attr: [StorageMgmtVirtualIP, ip_address_uri]}
486       # No tenant or management VIP required
487
488   # All Nodes Validations
489   AllNodesValidationConfig:
490     type: OS::TripleO::AllNodes::Validation
491     properties:
492       PingTestIps:
493         list_join:
494         - ' '
495         - - {get_attr: [Controller, resource.0.external_ip_address]}
496           - {get_attr: [Controller, resource.0.internal_api_ip_address]}
497           - {get_attr: [Controller, resource.0.storage_ip_address]}
498           - {get_attr: [Controller, resource.0.storage_mgmt_ip_address]}
499           - {get_attr: [Controller, resource.0.tenant_ip_address]}
500           - {get_attr: [Controller, resource.0.management_ip_address]}
501
502   UpdateWorkflow:
503     type: OS::TripleO::Tasks::UpdateWorkflow
504     depends_on:
505 {% for role in roles %}
506       - {{role.name}}AllNodesDeployment
507 {% endfor %}
508     properties:
509       servers:
510 {% for role in roles %}
511         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
512 {% endfor %}
513       input_values:
514         deploy_identifier: {get_param: DeployIdentifier}
515         update_identifier: {get_param: UpdateIdentifier}
516
517   # Optional ExtraConfig for all nodes - all roles are passed in here, but
518   # the nested template may configure each role differently (or not at all)
519   AllNodesExtraConfig:
520     type: OS::TripleO::AllNodesExtraConfig
521     depends_on:
522       - UpdateWorkflow
523 {% for role in roles %}
524       - {{role.name}}AllNodesValidationDeployment
525 {% endfor %}
526     properties:
527 {% for role in roles %}
528       servers: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
529 {% endfor %}
530
531   # Post deployment steps for all roles
532   AllNodesDeploySteps:
533     type: OS::TripleO::PostDeploySteps
534 {% for role in roles %}
535     depends_on:
536       - {{role.name}}AllNodesDeployment
537 {% endfor %}
538     properties:
539       servers:
540 {% for role in roles %}
541         {{role.name}}: {get_attr: [{{role.name}}, attributes, nova_server_resource]}
542 {% endfor %}
543       role_data:
544 {% for role in roles %}
545         {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data]}
546 {% endfor %}
547
548 outputs:
549   ManagedEndpoints:
550     description: Asserts that the keystone endpoints have been provisioned.
551     value: true
552   KeystoneURL:
553     description: URL for the Overcloud Keystone service
554     value: {get_attr: [EndpointMap, endpoint_map, KeystonePublic, uri]}
555   KeystoneAdminVip:
556     description: Keystone Admin VIP endpoint
557     value: {get_attr: [VipMap, net_ip_map, {get_attr: [ServiceNetMap, service_net_map, KeystoneAdminApiNetwork]}]}
558   EndpointMap:
559     description: |
560       Mapping of the resources with the needed info for their endpoints.
561       This includes the protocol used, the IP, port and also a full
562       representation of the URI.
563     value: {get_attr: [EndpointMap, endpoint_map]}
564   HostsEntry:
565     description: |
566       The content that should be appended to your /etc/hosts if you want to get
567       hostname-based access to the deployed nodes (useful for testing without
568       setting up a DNS).
569     value:
570       list_join:
571       - "\n"
572       - - {get_attr: [hostsConfig, hosts_entries]}
573       -
574         - str_replace:
575             template: IP  HOST
576             params:
577               IP: {get_attr: [VipMap, net_ip_map, external]}
578               HOST: {get_param: CloudName}
579         - str_replace:
580             template: IP  HOST
581             params:
582               IP: {get_attr: [VipMap, net_ip_map, ctlplane]}
583               HOST: {get_param: CloudNameCtlplane}
584         - str_replace:
585             template: IP  HOST
586             params:
587               IP: {get_attr: [VipMap, net_ip_map, internal_api]}
588               HOST: {get_param: CloudNameInternal}
589         - str_replace:
590             template: IP  HOST
591             params:
592               IP: {get_attr: [VipMap, net_ip_map, storage]}
593               HOST: {get_param: CloudNameStorage}
594         - str_replace:
595             template: IP  HOST
596             params:
597               IP: {get_attr: [VipMap, net_ip_map, storage_mgmt]}
598               HOST: {get_param: CloudNameStorageManagement}
599   EnabledServices:
600     description: The services enabled on each role
601     value:
602 {% for role in roles %}
603       {{role.name}}: {get_attr: [{{role.name}}ServiceChain, role_data, service_names]}
604 {% endfor %}