47e39bc25de1501fe88a62bb396019771436f7e6
[apex.git] / build / opnfv-tripleo-heat-templates.patch
1 From c74ab53470e79a22e3f411c81ab1dce072d44908 Mon Sep 17 00:00:00 2001
2 From: Tim Rozet <tdrozet@gmail.com>
3 Date: Tue, 12 Jan 2016 16:49:57 -0500
4 Subject: [PATCH] Adds current opnfv patch with ODL and ONOS support
5
6 ---
7  environments/onos.yaml                             |   8 +
8  environments/opendaylight-external.yaml            |  25 ++
9  environments/opendaylight.yaml                     |  25 ++
10  environments/opendaylight_l3.yaml                  |   9 +
11  environments/opendaylight_sfc.yaml                 |  28 ++
12  network/endpoints/endpoint_map.yaml                |  31 ++
13  overcloud-resource-registry-puppet.yaml            |   3 +
14  overcloud-without-mergepy.yaml                     |  93 +++++
15  puppet/all-nodes-config.yaml                       |  17 +
16  puppet/compute.yaml                                |  35 ++
17  puppet/controller.yaml                             |  93 ++++-
18  puppet/hieradata/common.yaml                       |   1 +
19  puppet/hieradata/controller.yaml                   |   3 +
20  puppet/manifests/overcloud_compute.pp              |  31 +-
21  puppet/manifests/overcloud_controller.pp           | 128 +++++-
22  puppet/manifests/overcloud_controller_pacemaker.pp | 434 ++++++++++++++-------
23  puppet/manifests/overcloud_opendaylight.pp         |  27 ++
24  puppet/opendaylight-puppet.yaml                    | 223 +++++++++++
25  18 files changed, 1054 insertions(+), 160 deletions(-)
26  create mode 100644 environments/onos.yaml
27  create mode 100644 environments/opendaylight-external.yaml
28  create mode 100644 environments/opendaylight.yaml
29  create mode 100644 environments/opendaylight_l3.yaml
30  create mode 100644 environments/opendaylight_sfc.yaml
31  create mode 100644 puppet/manifests/overcloud_opendaylight.pp
32  create mode 100644 puppet/opendaylight-puppet.yaml
33
34 diff --git a/environments/onos.yaml b/environments/onos.yaml
35 new file mode 100644
36 index 0000000..510aca9
37 --- /dev/null
38 +++ b/environments/onos.yaml
39 @@ -0,0 +1,8 @@
40 +parameters:
41 +    #This a bug for odl deployment. Once bug fixed OpenDaylightCount can be remove.
42 +    OpenDaylightCount: 0
43 +    NeutronL3HA: false
44 +    ExtraConfig:
45 +      neutron_service_plugins: ['onos_router']
46 +      neutron_mechanism_drivers: ['onos_ml2']
47 +      neutron_tenant_network_type: vxlan
48 diff --git a/environments/opendaylight-external.yaml b/environments/opendaylight-external.yaml
49 new file mode 100644
50 index 0000000..411df21
51 --- /dev/null
52 +++ b/environments/opendaylight-external.yaml
53 @@ -0,0 +1,25 @@
54 +# Environment file used to enable OpenDaylight
55 +# Currently uses overcloud image that is assumed
56 +# to be virt-customized with ODL RPM already on it
57 +
58 +# These parameters customize the OpenDaylight Node
59 +# The user name and password are for the ODL service
60 +# Defaults are included here for reference
61 +#parameter_defaults:
62 +#  OpenDaylightFlavor: baremetal
63 +#  OpenDaylightHostname: opendaylight-server
64 +#  OpenDaylightImage: overcloud-full
65 +#  OpenDaylightUsername: admin
66 +#  OpenDaylightPassword: admin
67 +
68 +parameters:
69 +    # increase this if you need more ODL nodes
70 +    OpenDaylightCount: 1
71 +    NeutronL3HA: false
72 +    ExtraConfig:
73 +      neutron_mechanism_drivers: ['opendaylight']
74 +      neutron_tenant_network_type: vxlan
75 +      # Enable this if you want OpenDaylight on the contollers
76 +      # reduce OpenDaylightCount to 0 if you don't want any
77 +      # OpenDaylight only nodes
78 +      #opendaylight_install: true
79 diff --git a/environments/opendaylight.yaml b/environments/opendaylight.yaml
80 new file mode 100644
81 index 0000000..c8abf75
82 --- /dev/null
83 +++ b/environments/opendaylight.yaml
84 @@ -0,0 +1,25 @@
85 +# Environment file used to enable OpenDaylight
86 +# Currently uses overcloud image that is assumed
87 +# to be virt-customized with ODL RPM already on it
88 +
89 +# These parameters customize the OpenDaylight Node
90 +# The user name and password are for the ODL service
91 +# Defaults are included here for reference
92 +#parameter_defaults:
93 +#  OpenDaylightFlavor: baremetal
94 +#  OpenDaylightHostname: opendaylight-server
95 +#  OpenDaylightImage: overcloud-full
96 +#  OpenDaylightUsername: admin
97 +#  OpenDaylightPassword: admin
98 +
99 +parameters:
100 +    # increase this if you need more ODL nodes
101 +    # OpenDaylightCount: 1
102 +    NeutronL3HA: false
103 +    ExtraConfig:
104 +      neutron_mechanism_drivers: ['opendaylight']
105 +      neutron_tenant_network_type: vxlan
106 +      # Enable this if you want OpenDaylight on the contollers
107 +      # reduce OpenDaylightCount to 0 if you don't want any
108 +      # OpenDaylight only nodes
109 +      opendaylight_install: true
110 diff --git a/environments/opendaylight_l3.yaml b/environments/opendaylight_l3.yaml
111 new file mode 100644
112 index 0000000..05c0aff
113 --- /dev/null
114 +++ b/environments/opendaylight_l3.yaml
115 @@ -0,0 +1,9 @@
116 +parameters:
117 +    #NeutronEnableL3Agent: false
118 +    NeutronEnableForceMetadata: true
119 +    OpenDaylightEnableL3: "'yes'"
120 +    NeutronServicePlugins: "networking_odl.l3.l3_odl.OpenDaylightL3RouterPlugin"
121 +    ExtraConfig:
122 +      neutron_mechanism_drivers: ['opendaylight']
123 +      neutron_tenant_network_type: vxlan
124 +      opendaylight_install: true
125 diff --git a/environments/opendaylight_sfc.yaml b/environments/opendaylight_sfc.yaml
126 new file mode 100644
127 index 0000000..3dd1e13
128 --- /dev/null
129 +++ b/environments/opendaylight_sfc.yaml
130 @@ -0,0 +1,28 @@
131 +# Environment file used to enable OpenDaylight
132 +# Currently uses overcloud image that is assumed
133 +# to be virt-customized with ODL RPM already on it
134 +
135 +# These parameters customize the OpenDaylight Node
136 +# The user name and password are for the ODL service
137 +# Defaults are included here for reference
138 +#parameter_defaults:
139 +#  OpenDaylightFlavor: baremetal
140 +#  OpenDaylightHostname: opendaylight-server
141 +#  OpenDaylightImage: overcloud-full
142 +#  OpenDaylightUsername: admin
143 +#  OpenDaylightPassword: admin
144 +
145 +parameters:
146 +    # increase this if you need more ODL nodes
147 +    # OpenDaylightCount: 1
148 +    ControllerEnableSwiftStorage: false
149 +    OpenDaylightFeatures: "odl-ovsdb-sfc-rest"
150 +    NeutronL3HA: false
151 +    ExtraConfig:
152 +      tripleo::ringbuilder::build_ring: False
153 +      neutron_mechanism_drivers: ['opendaylight']
154 +      neutron_tenant_network_type: vxlan
155 +      # Enable this if you want OpenDaylight on the contollers
156 +      # reduce OpenDaylightCount to 0 if you don't want any
157 +      # OpenDaylight only nodes
158 +      opendaylight_install: true
159 diff --git a/network/endpoints/endpoint_map.yaml b/network/endpoints/endpoint_map.yaml
160 index 0521401..7caa91b 100644
161 --- a/network/endpoints/endpoint_map.yaml
162 +++ b/network/endpoints/endpoint_map.yaml
163 @@ -4,6 +4,9 @@ description: >
164    A Map of OpenStack Endpoints
165  
166  parameters:
167 +  AodhApiVirtualIP:
168 +    type: string
169 +    default: ''
170    CeilometerApiVirtualIP:
171      type: string
172      default: ''
173 @@ -43,6 +46,9 @@ parameters:
174    EndpointMap:
175      type: json
176      default:
177 +      AodhAdmin: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
178 +      AodhInternal: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
179 +      AodhPublic: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
180        CeilometerAdmin: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
181        CeilometerInternal: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
182        CeilometerPublic: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
183 @@ -83,6 +89,28 @@ parameters:
184  
185  resources:
186  
187 +  AodhInternal:
188 +    type: OS::TripleO::Endpoint
189 +    properties:
190 +      EndpointName: AodhInternal
191 +      EndpointMap: { get_param: EndpointMap }
192 +      CloudName: {get_param: CloudName}
193 +      IP: {get_param: AodhApiVirtualIP}
194 +  AodhPublic:
195 +    type: OS::TripleO::Endpoint
196 +    properties:
197 +      EndpointName: AodhPublic
198 +      EndpointMap: { get_param: EndpointMap }
199 +      CloudName: {get_param: CloudName}
200 +      IP: {get_param: PublicVirtualIP}
201 +  AodhAdmin:
202 +    type: OS::TripleO::Endpoint
203 +    properties:
204 +      EndpointName: AodhAdmin
205 +      EndpointMap: { get_param: EndpointMap }
206 +      CloudName: {get_param: CloudName}
207 +      IP: {get_param: AodhApiVirtualIP}
208 +
209    CeilometerInternal:
210      type: OS::TripleO::Endpoint
211      properties:
212 @@ -407,6 +435,9 @@ resources:
213  outputs:
214    endpoint_map:
215      value:
216 +      AodhInternal: {get_attr: [ AodhInternal, endpoint] }
217 +      AodhPublic: {get_attr: [ AodhPublic, endpoint] }
218 +      AodhAdmin: {get_attr: [ AodhAdmin, endpoint] }
219        CeilometerInternal: {get_attr: [ CeilometerInternal, endpoint] }
220        CeilometerPublic: {get_attr: [ CeilometerPublic, endpoint] }
221        CeilometerAdmin: {get_attr: [ CeilometerAdmin, endpoint] }
222 diff --git a/overcloud-resource-registry-puppet.yaml b/overcloud-resource-registry-puppet.yaml
223 index 4cfed6b..adecc79 100644
224 --- a/overcloud-resource-registry-puppet.yaml
225 +++ b/overcloud-resource-registry-puppet.yaml
226 @@ -27,6 +27,9 @@ resource_registry:
227    # To disable, replace with firstboot/userdata_default.yaml
228    OS::TripleO::NodeAdminUserData: firstboot/userdata_heat_admin.yaml
229  
230 +  # This configures OpenDaylight to drive the network
231 +  OS::TripleO::OpenDaylightNode: puppet/opendaylight-puppet.yaml
232 +
233    # Hooks for operator extra config
234    # NodeUserData == Cloud-init additional user-data, e.g cloud-config
235    # ControllerExtraConfigPre == Controller configuration pre service deployment
236 diff --git a/overcloud-without-mergepy.yaml b/overcloud-without-mergepy.yaml
237 index a532c2f..965ca4c 100644
238 --- a/overcloud-without-mergepy.yaml
239 +++ b/overcloud-without-mergepy.yaml
240 @@ -15,6 +15,11 @@ parameters:
241      description: The password for the keystone admin account, used for monitoring, querying neutron etc.
242      type: string
243      hidden: true
244 +  AodhPassword:
245 +    default: unset
246 +    description: The password for the aodh services
247 +    type: string
248 +    hidden: true
249    CeilometerBackend:
250      default: 'mongodb'
251      description: The ceilometer backend type.
252 @@ -113,6 +118,10 @@ parameters:
253      default: ''
254      type: string
255      description: Neutron ID for ctlplane network.
256 +  NeutronEnableForceMetadata:
257 +    default: 'False'
258 +    description: If True, DHCP always provides metadata route to VM.
259 +    type: string
260    NeutronEnableTunnelling:
261      type: string
262      default: "True"
263 @@ -227,6 +236,31 @@ parameters:
264      default: false
265      description: Should MongoDb journaling be disabled
266      type: boolean
267 +  OpenDaylightPort:
268 +    default: 8081
269 +    description: Set opendaylight service port
270 +    type: number
271 +  OpenDaylightEnableL3:
272 +    description: Knob to enable/disable ODL L3
273 +    type: string
274 +    default: 'no'
275 +  OpenDaylightFeatures:
276 +    description: List of features to install with ODL
277 +    type: comma_delimited_list
278 +    default: "odl-ovsdb-openstack"
279 +  OpenDaylightInstall:
280 +    default: false
281 +    description: Whether to install OpenDaylight on the control nodes.
282 +    type: boolean
283 +  OpenDaylightUsername:
284 +    default: 'admin'
285 +    description: The username for the opendaylight server.
286 +    type: string
287 +  OpenDaylightPassword:
288 +    default: 'admin'
289 +    type: string
290 +    description: The password for the opendaylight server.
291 +    hidden: true
292    PublicVirtualFixedIPs:
293      default: []
294      description: >
295 @@ -575,6 +609,7 @@ parameters:
296      default:
297        NeutronTenantNetwork: tenant
298        CeilometerApiNetwork: internal_api
299 +      AodhApiNetwork: internal_api
300        MongoDbNetwork: internal_api
301        CinderApiNetwork: internal_api
302        CinderIscsiNetwork: storage
303 @@ -664,6 +699,18 @@ parameters:
304        structure as ExtraConfig.
305      type: json
306  
307 +# OpenDaylight specific parameters
308 +  OpenDaylightCount:
309 +    type: number
310 +    default: 0
311 +  OpenDaylightImage:
312 +    default: overcloud-full
313 +    type: string
314 +  OpenDaylightFlavor:
315 +    default: baremetal
316 +    description: Flavor for OpenDaylight node
317 +    type: string
318 +
319    # Hostname format for each role
320    # Note %index% is translated into the index of the node, e.g 0/1/2 etc
321    # and %stackname% is replaced with OS::stack_name in the template below.
322 @@ -688,6 +735,10 @@ parameters:
323      type: string
324      description: Format for CephStorage node hostnames
325      default: '%stackname%-cephstorage-%index%'
326 +  OpenDaylightHostnameFormat:
327 +    type: string
328 +    description: Format for OpenDaylight node hostnames
329 +    default: '%stackname%-opendaylight-%index%'
330  
331    # Identifiers to trigger tasks on nodes
332    UpdateIdentifier:
333 @@ -758,6 +809,7 @@ resources:
334      properties:
335        CloudName: {get_param: CloudName}
336        CeilometerApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
337 +      AodhApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
338        CinderApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CinderApiNetwork]}]}
339        GlanceApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
340        GlanceRegistryVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceRegistryNetwork]}]}
341 @@ -770,6 +822,29 @@ resources:
342        SwiftProxyVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, SwiftProxyNetwork]}]}
343        PublicVirtualIP: {get_attr: [VipMap, net_ip_map, external]}
344  
345 +  OpenDaylightNode:
346 +    type: OS::Heat::ResourceGroup
347 +    depends_on: Networks
348 +    properties:
349 +      count: {get_param: OpenDaylightCount}
350 +      removal_policies: {get_param: ComputeRemovalPolicies}
351 +      resource_def:
352 +        type: OS::TripleO::OpenDaylightNode
353 +        properties:
354 +          UpdateIdentifier: {get_param: UpdateIdentifier}
355 +          OpenDaylightFlavor: {get_param: OpenDaylightFlavor}
356 +          OpenDaylightImage: {get_param: OpenDaylightImage}
357 +          OpenDaylightPort: {get_param: OpenDaylightPort}
358 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
359 +          OpenDaylightFeatures: {get_param: OpenDaylightFeatures}
360 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
361 +          OpenDaylightEnableL3: {get_param: OpenDaylightEnableL3}
362 +          OpenDaylightHostname:
363 +            str_replace:
364 +              template: {get_param: OpenDaylightHostnameFormat}
365 +              params:
366 +                '%stackname%': {get_param: 'OS::stack_name'}
367 +
368    Controller:
369      type: OS::Heat::ResourceGroup
370      depends_on: Networks
371 @@ -781,6 +856,7 @@ resources:
372          properties:
373            AdminPassword: {get_param: AdminPassword}
374            AdminToken: {get_param: AdminToken}
375 +          AodhPassword: {get_param: AodhPassword}
376            CeilometerBackend: {get_param: CeilometerBackend}
377            CeilometerMeteringSecret: {get_param: CeilometerMeteringSecret}
378            CeilometerPassword: {get_param: CeilometerPassword}
379 @@ -832,6 +908,7 @@ resources:
380            NeutronBridgeMappings: {get_param: NeutronBridgeMappings}
381            NeutronExternalNetworkBridge: {get_param: NeutronExternalNetworkBridge}
382            NeutronEnableTunnelling: {get_param: NeutronEnableTunnelling}
383 +          NeutronEnableForceMetadata: {get_param: NeutronEnableForceMetadata}
384            NeutronNetworkVLANRanges: {get_param: NeutronNetworkVLANRanges}
385            NeutronPublicInterface: {get_param: NeutronPublicInterface}
386            NeutronPublicInterfaceDefaultRoute: {get_param: NeutronPublicInterfaceDefaultRoute}
387 @@ -853,6 +930,12 @@ resources:
388            NovaPassword: {get_param: NovaPassword}
389            NtpServer: {get_param: NtpServer}
390            MongoDbNoJournal: {get_param: MongoDbNoJournal}
391 +          OpenDaylightPort: {get_param: OpenDaylightPort}
392 +          OpenDaylightInstall: {get_param: OpenDaylightInstall}
393 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
394 +          OpenDaylightFeatures: {get_param: OpenDaylightFeatures}
395 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
396 +          OpenDaylightEnableL3: {get_param: OpenDaylightEnableL3}
397            PcsdPassword: {get_resource: PcsdPassword}
398            PublicVirtualInterface: {get_param: PublicVirtualInterface}
399            RabbitPassword: {get_param: RabbitPassword}
400 @@ -878,6 +961,7 @@ resources:
401            ServiceNetMap: {get_param: ServiceNetMap}
402            EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
403            CeilometerApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
404 +          AodhApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
405            CinderApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CinderApiNetwork]}]}
406            HeatApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
407            GlanceApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
408 @@ -948,6 +1032,9 @@ resources:
409            NovaPublicIP: {get_attr: [PublicVirtualIP, ip_address]}
410            NovaPassword: {get_param: NovaPassword}
411            NtpServer: {get_param: NtpServer}
412 +          OpenDaylightPort: {get_param: OpenDaylightPort}
413 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
414 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
415            RabbitHost: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, RabbitMqNetwork]}]}
416            RabbitPassword: {get_param: RabbitPassword}
417            RabbitUserName: {get_param: RabbitUserName}
418 @@ -1068,6 +1155,7 @@ resources:
419        compute_hosts: {get_attr: [Compute, hosts_entry]}
420        controller_hosts: {get_attr: [Controller, hosts_entry]}
421        controller_ips: {get_attr: [Controller, ip_address]}
422 +      opendaylight_ip: {get_attr: [OpenDaylightNode, ip_address]}
423        block_storage_hosts: {get_attr: [BlockStorage, hosts_entry]}
424        object_storage_hosts: {get_attr: [ObjectStorage, hosts_entry]}
425        ceph_storage_hosts: {get_attr: [CephStorage, hosts_entry]}
426 @@ -1081,6 +1169,7 @@ resources:
427        heat_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
428        swift_proxy_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, SwiftProxyNetwork]}]}
429        ceilometer_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
430 +      aodh_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
431        nova_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
432        nova_metadata_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
433        glance_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
434 @@ -1189,6 +1278,7 @@ resources:
435          nova_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
436          nova_metadata_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
437          ceilometer_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
438 +        aodh_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
439          heat_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
440          horizon_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HorizonNetwork]}]}
441          redis_vip: {get_attr: [RedisVirtualIP, ip_address]}
442 @@ -1434,6 +1524,9 @@ outputs:
443    PublicVip:
444      description: Controller VIP for public API endpoints
445      value: {get_attr: [PublicVirtualIP, ip_address]}
446 +  AodhInternalVip:
447 +    description: VIP for Aodh API internal endpoint
448 +    value: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
449    CeilometerInternalVip:
450      description: VIP for Ceilometer API internal endpoint
451      value: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
452 diff --git a/puppet/all-nodes-config.yaml b/puppet/all-nodes-config.yaml
453 index 2bc519b..d649ba0 100644
454 --- a/puppet/all-nodes-config.yaml
455 +++ b/puppet/all-nodes-config.yaml
456 @@ -8,6 +8,8 @@ parameters:
457      type: comma_delimited_list
458    controller_ips:
459      type: comma_delimited_list
460 +  opendaylight_ip:
461 +    type: comma_delimited_list
462    block_storage_hosts:
463      type: comma_delimited_list
464    object_storage_hosts:
465 @@ -34,6 +36,8 @@ parameters:
466      type: comma_delimited_list
467    ceilometer_api_node_ips:
468      type: comma_delimited_list
469 +  aodh_api_node_ips:
470 +    type: comma_delimited_list
471    nova_api_node_ips:
472      type: comma_delimited_list
473    nova_metadata_node_ips:
474 @@ -82,6 +86,10 @@ resources:
475                raw_data: {get_file: hieradata/RedHat.yaml}
476              all_nodes:
477                mapped_data:
478 +                opendaylight_controller_ip:
479 +                  list_join:
480 +                  - ','
481 +                  - {get_param: opendaylight_ip}
482                  controller_node_ips:
483                    list_join:
484                    - ','
485 @@ -166,6 +174,14 @@ resources:
486                          list_join:
487                          - "','"
488                          - {get_param: ceilometer_api_node_ips}
489 +                aodh_api_node_ips:
490 +                  str_replace:
491 +                    template: "['SERVERS_LIST']"
492 +                    params:
493 +                      SERVERS_LIST:
494 +                        list_join:
495 +                        - "','"
496 +                        - {get_param: aodh_api_node_ips}
497                  nova_api_node_ips:
498                    str_replace:
499                      template: "['SERVERS_LIST']"
500 @@ -239,6 +255,7 @@ resources:
501                  neutron::rabbit_hosts: *rabbit_nodes_array
502                  nova::rabbit_hosts: *rabbit_nodes_array
503                  keystone::rabbit_hosts: *rabbit_nodes_array
504 +                aodh::rabbit_hosts: *rabbit_nodes_array
505  
506  outputs:
507    config_id:
508 diff --git a/puppet/compute.yaml b/puppet/compute.yaml
509 index 70c7403..13fd4f6 100644
510 --- a/puppet/compute.yaml
511 +++ b/puppet/compute.yaml
512 @@ -213,6 +213,23 @@ parameters:
513    NtpServer:
514      type: string
515      default: ''
516 +  OpenDaylightPort:
517 +    default: 8081
518 +    description: Set opendaylight service port
519 +    type: number
520 +  OpenDaylightUsername:
521 +    default: 'admin'
522 +    description: The username for the opendaylight server.
523 +    type: string
524 +  OpenDaylightPassword:
525 +    default: 'admin'
526 +    type: string
527 +    description: The password for the opendaylight server.
528 +    hidden: true
529 +  ONOSPort:
530 +    default: 8181
531 +    description: Set onos service port
532 +    type: number
533    RabbitHost:
534      type: string
535      default: ''  # Has to be here because of the ignored empty value bug
536 @@ -320,6 +337,11 @@ resources:
537      properties:
538        ControlPlaneIP: {get_attr: [NovaCompute, networks, ctlplane, 0]}
539  
540 +  ExternalPort:
541 +    type: OS::TripleO::Controller::Ports::ExternalPort
542 +    properties:
543 +      ControlPlaneIP: {get_attr: [NovaCompute, networks, ctlplane, 0]}
544 +
545    NetIpMap:
546      type: OS::TripleO::Network::Ports::NetIpMap
547      properties:
548 @@ -327,6 +349,7 @@ resources:
549        InternalApiIp: {get_attr: [InternalApiPort, ip_address]}
550        StorageIp: {get_attr: [StoragePort, ip_address]}
551        TenantIp: {get_attr: [TenantPort, ip_address]}
552 +      ExternalIp: {get_attr: [ExternalPort, ip_address]}
553  
554    NetworkConfig:
555      type: OS::TripleO::Compute::Net::SoftwareConfig
556 @@ -335,6 +358,7 @@ resources:
557        InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
558        StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
559        TenantIpSubnet: {get_attr: [TenantPort, ip_subnet]}
560 +      ExternalIpSubnet: {get_attr: [ExternalPort, ip_subnet]}
561  
562    NetworkDeployment:
563      type: OS::TripleO::SoftwareDeployment
564 @@ -406,6 +430,10 @@ resources:
565                  neutron::rabbit_user: {get_input: rabbit_user}
566                  neutron::rabbit_use_ssl: {get_input: rabbit_client_use_ssl}
567                  neutron::rabbit_port: {get_input: rabbit_client_port}
568 +                opendaylight_port: {get_input: opendaylight_port}
569 +                opendaylight_username: {get_input: opendaylight_username}
570 +                opendaylight_password: {get_input: opendaylight_password}
571 +                onos_port: {get_input: onos_port}
572                  neutron_flat_networks: {get_input: neutron_flat_networks}
573                  neutron_host: {get_input: neutron_host}
574                  neutron::agents::ml2::ovs::local_ip: {get_input: neutron_local_ip}
575 @@ -459,6 +487,10 @@ resources:
576          snmpd_readonly_user_name: {get_param: SnmpdReadonlyUserName}
577          snmpd_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
578          glance_api_servers: {get_param: [EndpointMap, GlanceInternal, uri]}
579 +        opendaylight_port: {get_param: OpenDaylightPort}
580 +        opendaylight_username: {get_param: OpenDaylightUsername}
581 +        opendaylight_password: {get_param: OpenDaylightPassword}
582 +        onos_port: {get_param: ONOSPort}
583          neutron_flat_networks: {get_param: NeutronFlatNetworks}
584          neutron_host: {get_param: NeutronHost}
585          neutron_local_ip: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronTenantNetwork]}]}
586 @@ -570,6 +602,9 @@ outputs:
587    tenant_ip_address:
588      description: IP address of the server in the tenant network
589      value: {get_attr: [TenantPort, ip_address]}
590 +  external_ip_address:
591 +    description: IP address of the server in the external network
592 +    value: {get_attr: [ExternalPort, ip_address]}
593    hostname:
594      description: Hostname of the server
595      value: {get_attr: [NovaCompute, name]}
596 diff --git a/puppet/controller.yaml b/puppet/controller.yaml
597 index ea0b3af..bd82e93 100644
598 --- a/puppet/controller.yaml
599 +++ b/puppet/controller.yaml
600 @@ -14,6 +14,14 @@ parameters:
601      description: The keystone auth secret and db password.
602      type: string
603      hidden: true
604 +  AodhApiVirtualIP:
605 +    type: string
606 +    default: ''
607 +  AodhPassword:
608 +    default: unset
609 +    description: The password for the aodh services.
610 +    type: string
611 +    hidden: true
612    CeilometerApiVirtualIP:
613      type: string
614      default: ''
615 @@ -357,6 +365,10 @@ parameters:
616      default: 'True'
617      description: Allow automatic l3-agent failover
618      type: string
619 +  NeutronEnableForceMetadata:
620 +    default: 'False'
621 +    description: If True, DHCP always provides metadata route to VM.
622 +    type: string
623    NeutronEnableTunnelling:
624      type: string
625      default: "True"
626 @@ -443,6 +455,35 @@ parameters:
627    NtpServer:
628      type: string
629      default: ''
630 +  OpenDaylightPort:
631 +    default: 8081
632 +    description: Set opendaylight service port
633 +    type: number
634 +  OpenDaylightInstall:
635 +    default: false
636 +    description: Whether to install OpenDaylight on the control nodes.
637 +    type: boolean
638 +  OpenDaylightUsername:
639 +    default: 'admin'
640 +    description: The username for the opendaylight server.
641 +    type: string
642 +  OpenDaylightPassword:
643 +    default: 'admin'
644 +    type: string
645 +    description: The password for the opendaylight server.
646 +    hidden: true
647 +  OpenDaylightEnableL3:
648 +    description: Knob to enable/disable ODL L3
649 +    type: string
650 +    default: 'no'
651 +  OpenDaylightFeatures:
652 +    description: List of features to install with ODL
653 +    type: comma_delimited_list
654 +    default: "odl-ovsdb-openstack"
655 +  ONOSPort:
656 +    default: 8181
657 +    description: Set onos service port
658 +    type: number
659    PcsdPassword:
660      type: string
661      description: The password for the 'pcsd' user.
662 @@ -696,6 +737,7 @@ resources:
663        input_values:
664          bootstack_nodeid: {get_attr: [Controller, name]}
665          neutron_enable_tunneling: {get_param: NeutronEnableTunnelling}
666 +        neutron_enable_force_metadata: {get_param: NeutronEnableForceMetadata}
667          haproxy_log_address: {get_param: HAProxySyslogAddress}
668          heat.watch_server_url:
669            list_join:
670 @@ -774,6 +816,7 @@ resources:
671                - {get_param: MysqlVirtualIP}
672                - '/heat'
673          keystone_ca_certificate: {get_param: KeystoneCACertificate}
674 +        keystone_admin_vip: {get_param: KeystoneAdminApiVirtualIP}
675          keystone_signing_key: {get_param: KeystoneSigningKey}
676          keystone_signing_certificate: {get_param: KeystoneSigningCertificate}
677          keystone_ssl_certificate: {get_param: KeystoneSSLCertificate}
678 @@ -805,6 +848,13 @@ resources:
679              template: tripleo-CLUSTER
680              params:
681                CLUSTER: {get_param: MysqlClusterUniquePart}
682 +        opendaylight_port: {get_param: OpenDaylightPort}
683 +        opendaylight_install: {get_param: OpenDaylightInstall}
684 +        opendaylight_username: {get_param: OpenDaylightUsername}
685 +        opendaylight_password: {get_param: OpenDaylightPassword}
686 +        opendaylight_enable_l3: {get_param: OpenDaylightEnableL3}
687 +        opendaylight_features: {get_param: OpenDaylightFeatures}
688 +        onos_port: {get_param: ONOSPort}
689          neutron_flat_networks: {get_param: NeutronFlatNetworks}
690          neutron_metadata_proxy_shared_secret: {get_param: NeutronMetadataProxySharedSecret}
691          neutron_agent_mode: {get_param: NeutronAgentMode}
692 @@ -879,6 +929,7 @@ resources:
693          ceilometer_backend: {get_param: CeilometerBackend}
694          ceilometer_metering_secret: {get_param: CeilometerMeteringSecret}
695          ceilometer_password: {get_param: CeilometerPassword}
696 +        aodh_password: {get_param: AodhPassword}
697          ceilometer_coordination_url:
698            list_join:
699              - ''
700 @@ -891,6 +942,12 @@ resources:
701              - - 'mysql://ceilometer:unset@'
702                - {get_param: MysqlVirtualIP}
703                - '/ceilometer'
704 +        ceilometer_public_url: {get_param: [EndpointMap, CeilometerPublic, uri]}
705 +        ceilometer_internal_url: {get_param: [EndpointMap, CeilometerInternal, uri]}
706 +        ceilometer_admin_url: {get_param: [EndpointMap, CeilometerAdmin, uri]}
707 +        aodh_public_url: {get_param: [EndpointMap, AodhPublic, uri]}
708 +        aodh_internal_url: {get_param: [EndpointMap, AodhInternal, uri]}
709 +        aodh_admin_url: {get_param: [EndpointMap, AodhAdmin, uri]}
710          snmpd_readonly_user_name: {get_param: SnmpdReadonlyUserName}
711          snmpd_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
712          nova_password: {get_param: NovaPassword}
713 @@ -948,6 +1005,7 @@ resources:
714          neutron_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronApiNetwork]}]}
715          neutron_local_ip: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronTenantNetwork]}]}
716          ceilometer_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
717 +        aodh_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
718          nova_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
719          nova_metadata_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
720          horizon_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, HorizonNetwork]}]}
721 @@ -1041,7 +1099,7 @@ resources:
722                  cinder_iscsi_ip_address: {get_input: cinder_iscsi_network}
723                  cinder::database_connection: {get_input: cinder_dsn}
724                  cinder::api::keystone_password: {get_input: cinder_password}
725 -                cinder::api::auth_uri: {get_input: keystone_auth_uri}
726 +                cinder::api::keystone_auth_host: {get_input: keystone_admin_vip}
727                  cinder::api::identity_uri: {get_input: keystone_identity_uri}
728                  cinder::api::bind_host: {get_input: cinder_api_network}
729                  cinder::rabbit_userid: {get_input: rabbit_username}
730 @@ -1136,6 +1194,17 @@ resources:
731                  mysql_bind_host: {get_input: mysql_network}
732                  mysql_virtual_ip: {get_input: mysql_virtual_ip}
733  
734 +                # OpenDaylight
735 +                opendaylight_port: {get_input: opendaylight_port}
736 +                opendaylight_install: {get_input: opendaylight_install}
737 +                opendaylight_username: {get_input: opendaylight_username}
738 +                opendaylight_password: {get_input: opendaylight_password}
739 +                opendaylight_enable_l3: {get_input: opendaylight_enable_l3}
740 +                opendaylight_features: {get_input: opendaylight_features}
741 +
742 +                # ONOS
743 +                onos_port: {get_input: onos_port}
744 +
745                  # Neutron
746                  neutron::bind_host: {get_input: neutron_api_network}
747                  neutron::rabbit_password: {get_input: rabbit_password}
748 @@ -1152,6 +1221,7 @@ resources:
749                  neutron_flat_networks: {get_input: neutron_flat_networks}
750                  neutron::agents::metadata::shared_secret: {get_input: neutron_metadata_proxy_shared_secret}
751                  neutron::agents::metadata::metadata_ip: {get_input: neutron_api_network}
752 +                neutron::agents::dhcp::enable_force_metadata: {get_input: neutron_enable_force_metadata}
753                  neutron_agent_mode: {get_input: neutron_agent_mode}
754                  neutron_router_distributed: {get_input: neutron_router_distributed}
755                  neutron::core_plugin: {get_input: neutron_core_plugin}
756 @@ -1198,6 +1268,27 @@ resources:
757                  snmpd_readonly_user_name: {get_input: snmpd_readonly_user_name}
758                  snmpd_readonly_user_password: {get_input: snmpd_readonly_user_password}
759  
760 +                # Aodh
761 +                aodh::rabbit_userid: {get_input: rabbit_username}
762 +                aodh::rabbit_password: {get_input: rabbit_password}
763 +                aodh::rabbit_use_ssl: {get_input: rabbit_client_use_ssl}
764 +                aodh::rabbit_port: {get_input: rabbit_client_port}
765 +                aodh::debug: {get_input: debug}
766 +                aodh::wsgi::apache::ssl: false
767 +                aodh::api::service_name: 'httpd'
768 +                aodh::api::host: {get_input: aodh_api_network}
769 +                aodh::api::keystone_password: {get_input: aodh_password}
770 +                aodh::api::keystone_auth_uri: {get_input: keystone_auth_uri}
771 +                aodh::api::keystone_identity_uri: {get_input: keystone_identity_uri}
772 +                aodh::auth::auth_password: {get_input: aodh_password}
773 +                aodh::keystone::auth::public_url: {get_input: aodh_public_url }
774 +                aodh::keystone::auth::internal_url: {get_input: aodh_internal_url }
775 +                aodh::keystone::auth::admin_url: {get_input: aodh_admin_url }
776 +                aodh::keystone::auth::password: {get_input: aodh_password }
777 +                aodh::keystone::auth::region: {get_input: keystone_region}
778 +                # for a migration path from ceilometer-alarm to aodh, we use the same database & coordination
779 +                aodh::evaluator::coordination_url: {get_input: ceilometer_coordination_url}
780 +
781                  # Nova
782                  nova::rabbit_userid: {get_input: rabbit_username}
783                  nova::rabbit_password: {get_input: rabbit_password}
784 diff --git a/puppet/hieradata/common.yaml b/puppet/hieradata/common.yaml
785 index 030f661..5840016 100644
786 --- a/puppet/hieradata/common.yaml
787 +++ b/puppet/hieradata/common.yaml
788 @@ -6,6 +6,7 @@ ceilometer::agent::auth::auth_region: 'regionOne'
789  # FIXME: Might be better to use 'service' tenant here but this requires
790  # changes in the tripleo-incubator keystone role setup
791  ceilometer::agent::auth::auth_tenant_name: 'admin'
792 +aodh::auth::auth_tenant_name: 'admin'
793  
794  nova::network::neutron::neutron_admin_tenant_name: 'service'
795  nova::network::neutron::neutron_admin_username: 'neutron'
796 diff --git a/puppet/hieradata/controller.yaml b/puppet/hieradata/controller.yaml
797 index 4b7fd81..4f1fef6 100644
798 --- a/puppet/hieradata/controller.yaml
799 +++ b/puppet/hieradata/controller.yaml
800 @@ -32,6 +32,7 @@ redis::sentinel::notification_script: '/usr/local/bin/redis-notifications.sh'
801  # service tenant
802  nova::api::admin_tenant_name: 'service'
803  glance::api::keystone_tenant: 'service'
804 +aodh::api::keystone_tenant: 'service'
805  glance::registry::keystone_tenant: 'service'
806  neutron::server::auth_tenant: 'service'
807  neutron::agents::metadata::auth_tenant: 'service'
808 @@ -39,6 +40,7 @@ cinder::api::keystone_tenant: 'service'
809  swift::proxy::authtoken::admin_tenant_name: 'service'
810  ceilometer::api::keystone_tenant: 'service'
811  heat::keystone_tenant: 'service'
812 +aodh::keystone::auth::tenant: 'service'
813  
814  # keystone
815  keystone::cron::token_flush::maxdelay: 3600
816 @@ -115,6 +117,7 @@ tripleo::loadbalancer::mysql: true
817  tripleo::loadbalancer::redis: true
818  tripleo::loadbalancer::swift_proxy_server: true
819  tripleo::loadbalancer::ceilometer: true
820 +tripleo::loadbalancer::aodh: true
821  tripleo::loadbalancer::heat_api: true
822  tripleo::loadbalancer::heat_cloudwatch: true
823  tripleo::loadbalancer::heat_cfn: true
824 diff --git a/puppet/manifests/overcloud_compute.pp b/puppet/manifests/overcloud_compute.pp
825 index cd41cc7..110ca1d 100644
826 --- a/puppet/manifests/overcloud_compute.pp
827 +++ b/puppet/manifests/overcloud_compute.pp
828 @@ -75,9 +75,34 @@ class { '::neutron::plugins::ml2':
829    tenant_network_types => [hiera('neutron_tenant_network_type')],
830  }
831  
832 -class { '::neutron::agents::ml2::ovs':
833 -  bridge_mappings => split(hiera('neutron_bridge_mappings'), ','),
834 -  tunnel_types    => split(hiera('neutron_tunnel_types'), ','),
835 +if 'opendaylight' in hiera('neutron_mechanism_drivers') {
836 +
837 +  if str2bool(hiera('opendaylight_install', 'false')) {
838 +    $controller_ips = split(hiera('controller_node_ips'), ',')
839 +    $opendaylight_controller_ip = $controller_ips[0]
840 +  } else {
841 +    $opendaylight_controller_ip = hiera('opendaylight_controller_ip')
842 +  }
843 +
844 +  class { 'neutron::plugins::ovs::opendaylight':
845 +      odl_controller_ip => $opendaylight_controller_ip,
846 +      tunnel_ip         => hiera('neutron::agents::ml2::ovs::local_ip'),
847 +      odl_port          => hiera('opendaylight_port'),
848 +      odl_username      => hiera('opendaylight_username'),
849 +      odl_password      => hiera('opendaylight_password'),
850 +  }
851 +
852 +} elsif 'onos_ml2' in hiera('neutron_mechanism_drivers') {
853 +  $controller_ips = split(hiera('controller_node_ips'), ',')
854 +  class {'onos::ovs_computer':
855 +    manager_ip => $controller_ips[0]
856 +  }
857 +
858 +} else {
859 +  class { 'neutron::agents::ml2::ovs':
860 +    bridge_mappings => split(hiera('neutron_bridge_mappings'), ','),
861 +    tunnel_types    => split(hiera('neutron_tunnel_types'), ','),
862 +  }
863  }
864  
865  if 'cisco_n1kv' in hiera('neutron_mechanism_drivers') {
866 diff --git a/puppet/manifests/overcloud_controller.pp b/puppet/manifests/overcloud_controller.pp
867 index 1f6c2be..1095758 100644
868 --- a/puppet/manifests/overcloud_controller.pp
869 +++ b/puppet/manifests/overcloud_controller.pp
870 @@ -30,6 +30,21 @@ if hiera('step') >= 1 {
871  
872  if hiera('step') >= 2 {
873  
874 +  if str2bool(hiera('opendaylight_install', 'false')) {
875 +    class {"opendaylight":
876 +      extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
877 +      odl_rest_port  => hiera('opendaylight_port'),
878 +      enable_l3      => hiera('opendaylight_enable_l3', 'no'),
879 +    }
880 +  }
881 +  
882 +  if 'onos_ml2' in hiera('neutron_mechanism_drivers') {
883 +    # install onos and config ovs
884 +    class {"onos":
885 +      controllers_ip => $controller_node_ips
886 +    }
887 +  }
888 +
889    if count(hiera('ntp::servers')) > 0 {
890      include ::ntp
891    }
892 @@ -158,6 +173,9 @@ if hiera('step') >= 2 {
893  
894  if hiera('step') >= 3 {
895  
896 +  # Apache
897 +  include ::apache
898 +
899    include ::keystone
900  
901    #TODO: need a cleanup-keystone-tokens.sh solution here
902 @@ -223,9 +241,7 @@ if hiera('step') >= 3 {
903    include ::nova::scheduler
904    include ::nova::scheduler::filter
905  
906 -  include ::neutron
907    include ::neutron::server
908 -  include ::neutron::agents::l3
909    include ::neutron::agents::dhcp
910    include ::neutron::agents::metadata
911  
912 @@ -237,15 +253,101 @@ if hiera('step') >= 3 {
913      require => Package['neutron'],
914    }
915  
916 +  if 'onos_ml2' in hiera('neutron_mechanism_drivers') {
917 +    # config neutron service_plugins to onos driver
918 +    class { '::neutron':
919 +      service_plugins  => [hiera('neutron_service_plugins')]
920 +    }
921 +  } else {
922 +    include ::neutron
923 +    if 'opendaylight' in hiera('neutron_mechanism_drivers') {
924 +      if ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
925 +        include ::neutron::agents::l3
926 +      }
927 +    }
928 +  }
929 +  
930    class { '::neutron::plugins::ml2':
931      flat_networks        => split(hiera('neutron_flat_networks'), ','),
932      tenant_network_types => [hiera('neutron_tenant_network_type')],
933      mechanism_drivers    => [hiera('neutron_mechanism_drivers')],
934    }
935 -  class { '::neutron::agents::ml2::ovs':
936 -    bridge_mappings => split(hiera('neutron_bridge_mappings'), ','),
937 -    tunnel_types    => split(hiera('neutron_tunnel_types'), ','),
938 +
939 +  if 'opendaylight' in hiera('neutron_mechanism_drivers') {
940 +    if ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
941 +      Service['neutron-server'] -> Service['neutron-l3']
942 +    }
943 +
944 +    if str2bool(hiera('opendaylight_install', 'false')) {
945 +      $controller_ips = split(hiera('controller_node_ips'), ',')
946 +      $opendaylight_controller_ip = $controller_ips[0]
947 +    } else {
948 +      $opendaylight_controller_ip = hiera('opendaylight_controller_ip')
949 +    }
950 +
951 +    # co-existence hacks for SFC
952 +    if hiera('opendaylight_features', 'odl-ovsdb-openstack') =~ /odl-ovsdb-sfc-rest/ {
953 +      $opendaylight_port = hiera('opendaylight_port')
954 +      $netvirt_coexist_url = "http://${opendaylight_controller_ip}:${opendaylight_port}/restconf/config/netvirt-providers-config:netvirt-providers-config"
955 +      $netvirt_post_body = "{'netvirt-providers-config': {'table-offset': 1}}"
956 +      $sfc_coexist_url = "http://${opendaylight_controller_ip}:${opendaylight_port}/restconf/config/sfc-of-renderer:sfc-of-renderer-config"
957 +      $sfc_post_body = "{ 'sfc-of-renderer-config' : { 'sfc-of-table-offset' : 150, 'sfc-of-app-egress-table-offset' : 11 }}"
958 +      $odl_username = hiera('opendaylight_username')
959 +      $odl_password = hiera('opendaylight_password')
960 +      exec { 'Coexistence table offsets for netvirt':
961 +        command   => "curl -o /dev/null --fail --silent -u ${odl_username}:${odl_password} ${netvirt_coexist_url} -i -H 'Content-Type: application/json' --data \'${netvirt_post_body}\' -X PUT",
962 +        tries     => 5,
963 +        try_sleep => 30,
964 +        path      => '/usr/sbin:/usr/bin:/sbin:/bin',
965 +      } ->
966 +      # Coexist for SFC
967 +      exec { 'Coexistence table offsets for sfc':
968 +        command   => "curl -o /dev/null --fail --silent -u ${odl_username}:${odl_password} ${sfc_coexist_url} -i -H 'Content-Type: application/json' --data \'${sfc_post_body}\' -X PUT",
969 +        tries     => 5,
970 +        try_sleep => 30,
971 +        path      => '/usr/sbin:/usr/bin:/sbin:/bin',
972 +      }
973 +    }
974 +
975 +    class { 'neutron::plugins::ml2::opendaylight':
976 +      odl_controller_ip => $opendaylight_controller_ip,
977 +      odl_username      => hiera('opendaylight_username'),
978 +      odl_password      => hiera('opendaylight_password'),
979 +      odl_port          => hiera('opendaylight_port'),
980 +    }
981 +
982 +    if str2bool(hiera('opendaylight_install', 'false')) {
983 +      class { 'neutron::plugins::ovs::opendaylight':
984 +        odl_controller_ip => $opendaylight_controller_ip,
985 +        tunnel_ip         => hiera('neutron::agents::ml2::ovs::local_ip'),
986 +        odl_port          => hiera('opendaylight_port'),
987 +        odl_username      => hiera('opendaylight_username'),
988 +        odl_password      => hiera('opendaylight_password'),
989 +      }
990 +    }
991 +
992 +  } elsif 'onos_ml2' in hiera('neutron_mechanism_drivers') {
993 +    #config ml2_conf.ini with onos url address
994 +    $onos_port = hiera('onos_port')
995 +    $private_ip = hiera('neutron::agents::ml2::ovs::local_ip')
996 +
997 +    neutron_plugin_ml2 {
998 +      'onos/username':         value => 'admin';
999 +      'onos/password':         value => 'admin';
1000 +      'onos/url_path':         value => "http://${controller_node_ips[0]}:${onos_port}/onos/vtn";
1001 +    }
1002 +
1003 +  } else {
1004 +
1005 +    class { 'neutron::agents::ml2::ovs':
1006 +      bridge_mappings => split(hiera('neutron_bridge_mappings'), ','),
1007 +      tunnel_types => split(hiera('neutron_tunnel_types'), ','),
1008 +    }
1009 +
1010 +    Service['neutron-server'] -> Service['neutron-ovs-agent-service']
1011 +    Service['neutron-server'] -> Service['neutron-l3']
1012    }
1013 +
1014    if 'cisco_n1kv' in hiera('neutron_mechanism_drivers') {
1015      include ::neutron::plugins::ml2::cisco::nexus1000v
1016  
1017 @@ -280,8 +382,6 @@ if hiera('step') >= 3 {
1018    }
1019  
1020    Service['neutron-server'] -> Service['neutron-dhcp-service']
1021 -  Service['neutron-server'] -> Service['neutron-l3']
1022 -  Service['neutron-server'] -> Service['neutron-ovs-agent-service']
1023    Service['neutron-server'] -> Service['neutron-metadata']
1024  
1025    include ::cinder
1026 @@ -447,6 +547,20 @@ if hiera('step') >= 3 {
1027  
1028    Cron <| title == 'ceilometer-expirer' |> { command => "sleep $((\$(od -A n -t d -N 3 /dev/urandom) % 86400)) && ${::ceilometer::params::expirer_command}" }
1029  
1030 +  # Aodh
1031 +  include ::aodh::auth
1032 +  include ::aodh::api
1033 +  include ::aodh::evaluator
1034 +  include ::aodh::notifier
1035 +  include ::aodh::listener
1036 +  include ::aodh::client
1037 +  include ::aodh::db::sync
1038 +  class { '::aodh' :
1039 +    database_connection => $ceilometer_database_connection,
1040 +  }
1041 +  # To manage the upgrade:
1042 +  Exec['ceilometer-dbsync'] -> Exec['aodh-db-sync']
1043 +
1044    # Heat
1045    include ::heat
1046    include ::heat::api
1047 diff --git a/puppet/manifests/overcloud_controller_pacemaker.pp b/puppet/manifests/overcloud_controller_pacemaker.pp
1048 index 3fb92f3..42b9f06 100644
1049 --- a/puppet/manifests/overcloud_controller_pacemaker.pp
1050 +++ b/puppet/manifests/overcloud_controller_pacemaker.pp
1051 @@ -380,6 +380,21 @@ if hiera('step') >= 2 {
1052  
1053    }
1054  
1055 +  if str2bool(hiera('opendaylight_install', 'false')) {
1056 +    class {"opendaylight":
1057 +      extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
1058 +      odl_rest_port  => hiera('opendaylight_port'),
1059 +      enable_l3      => hiera('opendaylight_enable_l3', 'no'),
1060 +    }
1061 +  }
1062 +
1063 +  if 'onos_ml2' in hiera('neutron_mechanism_drivers') {
1064 +    # install onos and config ovs
1065 +    class {"onos":
1066 +      controllers_ip => $controller_node_ips
1067 +    }
1068 +  }
1069 +  
1070    exec { 'galera-ready' :
1071      command     => '/usr/bin/clustercheck >/dev/null',
1072      timeout     => 30,
1073 @@ -584,7 +599,14 @@ if hiera('step') >= 3 {
1074    include ::nova::network::neutron
1075  
1076    # Neutron class definitions
1077 -  include ::neutron
1078 +  if 'onos_ml2' in hiera('neutron_mechanism_drivers') {
1079 +    # config neutron service_plugins to onos driver
1080 +    class { '::neutron':
1081 +      service_plugins  => [hiera('neutron_service_plugins')]
1082 +    }
1083 +  } else {
1084 +    include ::neutron
1085 +  }
1086    class { '::neutron::server' :
1087      sync_db        => $sync_db,
1088      manage_service => false,
1089 @@ -594,10 +616,6 @@ if hiera('step') >= 3 {
1090      manage_service => false,
1091      enabled        => false,
1092    }
1093 -  class { '::neutron::agents::l3' :
1094 -    manage_service => false,
1095 -    enabled        => false,
1096 -  }
1097    class { '::neutron::agents::metadata':
1098      manage_service => false,
1099      enabled        => false,
1100 @@ -609,18 +627,68 @@ if hiera('step') >= 3 {
1101      notify  => Service['neutron-dhcp-service'],
1102      require => Package['neutron'],
1103    }
1104 +
1105    class { '::neutron::plugins::ml2':
1106      flat_networks        => split(hiera('neutron_flat_networks'), ','),
1107      tenant_network_types => [hiera('neutron_tenant_network_type')],
1108      mechanism_drivers    => [hiera('neutron_mechanism_drivers')],
1109    }
1110 -  class { '::neutron::agents::ml2::ovs':
1111 -    manage_service  => false,
1112 -    enabled         => false,
1113 -    bridge_mappings => split(hiera('neutron_bridge_mappings'), ','),
1114 -    tunnel_types    => split(hiera('neutron_tunnel_types'), ','),
1115 -  }
1116 +  if 'opendaylight' in hiera('neutron_mechanism_drivers') {
1117 +    if str2bool(hiera('opendaylight_install', 'false')) {
1118 +      $controller_ips = split(hiera('controller_node_ips'), ',')
1119 +      $opendaylight_controller_ip = $controller_ips[0]
1120 +    } else {
1121 +      $opendaylight_controller_ip = hiera('opendaylight_controller_ip')
1122 +    }
1123 +
1124 +    $opendaylight_port = hiera('opendaylight_port')
1125 +    $private_ip = hiera('neutron::agents::ml2::ovs::local_ip')
1126 +
1127 +    class { 'neutron::plugins::ml2::opendaylight':
1128 +      odl_controller_ip => $opendaylight_controller_ip,
1129 +      odl_username      => hiera('opendaylight_username'),
1130 +      odl_password      => hiera('opendaylight_password'),
1131 +      odl_port          => hiera('opendaylight_port'),
1132 +    }
1133  
1134 +    if str2bool(hiera('opendaylight_install', 'false')) {
1135 +      class { 'neutron::plugins::ovs::opendaylight':
1136 +        odl_controller_ip => $opendaylight_controller_ip,
1137 +        tunnel_ip         => hiera('neutron::agents::ml2::ovs::local_ip'),
1138 +        odl_port          => hiera('opendaylight_port'),
1139 +        odl_username      => hiera('opendaylight_username'),
1140 +        odl_password      => hiera('opendaylight_password'),
1141 +      }
1142 +    }
1143 +    if ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
1144 +      class { '::neutron::agents::l3' :
1145 +        manage_service => false,
1146 +        enabled        => false,
1147 +      }
1148 +    }
1149 +  } elsif 'onos_ml2' in hiera('neutron_mechanism_drivers') {
1150 +    #config ml2_conf.ini with onos url address
1151 +    $onos_port = hiera('onos_port')
1152 +    $private_ip = hiera('neutron::agents::ml2::ovs::local_ip')
1153 +
1154 +    neutron_plugin_ml2 {
1155 +      'onos/username':         value => 'admin';
1156 +      'onos/password':         value => 'admin';
1157 +      'onos/url_path':         value => "http://${controller_node_ips[0]}:${onos_port}/onos/vtn";
1158 +    }
1159 +
1160 +  } else {
1161 +    class { '::neutron::agents::l3' :
1162 +      manage_service => false,
1163 +      enabled        => false,
1164 +    }
1165 +    class { 'neutron::agents::ml2::ovs':
1166 +      manage_service   => false,
1167 +      enabled          => false,
1168 +      bridge_mappings  => split(hiera('neutron_bridge_mappings'), ','),
1169 +      tunnel_types     => split(hiera('neutron_tunnel_types'), ','),
1170 +    }
1171 +  }
1172    if 'cisco_ucsm' in hiera('neutron_mechanism_drivers') {
1173      include ::neutron::plugins::ml2::cisco::ucsm
1174    }
1175 @@ -645,8 +713,10 @@ if hiera('step') >= 3 {
1176    if hiera('neutron_enable_bigswitch_ml2', false) {
1177      include ::neutron::plugins::ml2::bigswitch::restproxy
1178    }
1179 -  neutron_l3_agent_config {
1180 -    'DEFAULT/ovs_use_veth': value => hiera('neutron_ovs_use_veth', false);
1181 +  if !('onos_ml2' in hiera('neutron_mechanism_drivers') or str2bool(hiera('opendaylight_enable_l3', 'no'))) {
1182 +    neutron_l3_agent_config {
1183 +      'DEFAULT/ovs_use_veth': value => hiera('neutron_ovs_use_veth', false);
1184 +    }
1185    }
1186    neutron_dhcp_agent_config {
1187      'DEFAULT/ovs_use_veth': value => hiera('neutron_ovs_use_veth', false);
1188 @@ -879,6 +949,28 @@ if hiera('step') >= 3 {
1189      enabled        => false,
1190    }
1191  
1192 +  # Aodh
1193 +  include ::aodh
1194 +  include ::aodh::config
1195 +  include ::aodh::auth
1196 +  include ::aodh::client
1197 +  class { '::aodh::api':
1198 +    manage_service => false,
1199 +    enabled        => false,
1200 +  }
1201 +  class { '::aodh::evaluator':
1202 +    manage_service => false,
1203 +    enabled        => false,
1204 +  }
1205 +  class { '::aodh::notifier':
1206 +    manage_service => false,
1207 +    enabled        => false,
1208 +  }
1209 +  class { '::aodh::listener':
1210 +    manage_service => false,
1211 +    enabled        => false,
1212 +  }
1213 +
1214    # httpd/apache and horizon
1215    # NOTE(gfidente): server-status can be consumed by the pacemaker resource agent
1216    class { '::apache' :
1217 @@ -914,6 +1006,32 @@ if hiera('step') >= 3 {
1218  if hiera('step') >= 4 {
1219    include ::keystone::cron::token_flush
1220  
1221 +  $event_pipeline = "---
1222 +sources:
1223 +    - name: event_source
1224 +      events:
1225 +          - \"*\"
1226 +      sinks:
1227 +          - event_sink
1228 +sinks:
1229 +    - name: event_sink
1230 +      transformers:
1231 +      triggers:
1232 +      publishers:
1233 +          - notifier://?topic=alarm.all
1234 +          - notifier://
1235 +"
1236 +
1237 +  # aodh hacks
1238 +  file { '/etc/ceilometer/event_pipeline':
1239 +    ensure  => present,
1240 +    content => $event_pipeline
1241 +  }
1242 +
1243 +  user { 'aodh':
1244 +     groups => 'nobody'
1245 +  }
1246 +
1247    if $pacemaker_master {
1248  
1249      # Keystone
1250 @@ -1055,62 +1173,21 @@ if hiera('step') >= 4 {
1251        clone_params => 'interleave=true',
1252        require      => Pacemaker::Resource::Service[$::keystone::params::service_name],
1253      }
1254 -    pacemaker::resource::service { $::neutron::params::l3_agent_service:
1255 -      clone_params => 'interleave=true',
1256 +    if !('onos_ml2' in hiera('neutron_mechanism_drivers')) {
1257 +      pacemaker::resource::service { $::neutron::params::l3_agent_service:
1258 +        clone_params => 'interleave=true',
1259 +      }
1260      }
1261      pacemaker::resource::service { $::neutron::params::dhcp_agent_service:
1262        clone_params => 'interleave=true',
1263      }
1264 -    pacemaker::resource::service { $::neutron::params::ovs_agent_service:
1265 -      clone_params => 'interleave=true',
1266 -    }
1267      pacemaker::resource::service { $::neutron::params::metadata_agent_service:
1268        clone_params => 'interleave=true',
1269      }
1270 -    pacemaker::resource::ocf { $::neutron::params::ovs_cleanup_service:
1271 -      ocf_agent_name => 'neutron:OVSCleanup',
1272 -      clone_params   => 'interleave=true',
1273 -    }
1274      pacemaker::resource::ocf { 'neutron-netns-cleanup':
1275        ocf_agent_name => 'neutron:NetnsCleanup',
1276        clone_params   => 'interleave=true',
1277      }
1278 -
1279 -    # neutron - one chain ovs-cleanup-->netns-cleanup-->ovs-agent
1280 -    pacemaker::constraint::base { 'neutron-ovs-cleanup-to-netns-cleanup-constraint':
1281 -      constraint_type => 'order',
1282 -      first_resource  => "${::neutron::params::ovs_cleanup_service}-clone",
1283 -      second_resource => 'neutron-netns-cleanup-clone',
1284 -      first_action    => 'start',
1285 -      second_action   => 'start',
1286 -      require         => [Pacemaker::Resource::Ocf[$::neutron::params::ovs_cleanup_service],
1287 -                          Pacemaker::Resource::Ocf['neutron-netns-cleanup']],
1288 -    }
1289 -    pacemaker::constraint::colocation { 'neutron-ovs-cleanup-to-netns-cleanup-colocation':
1290 -      source  => 'neutron-netns-cleanup-clone',
1291 -      target  => "${::neutron::params::ovs_cleanup_service}-clone",
1292 -      score   => 'INFINITY',
1293 -      require => [Pacemaker::Resource::Ocf[$::neutron::params::ovs_cleanup_service],
1294 -                  Pacemaker::Resource::Ocf['neutron-netns-cleanup']],
1295 -    }
1296 -    pacemaker::constraint::base { 'neutron-netns-cleanup-to-openvswitch-agent-constraint':
1297 -      constraint_type => 'order',
1298 -      first_resource  => 'neutron-netns-cleanup-clone',
1299 -      second_resource => "${::neutron::params::ovs_agent_service}-clone",
1300 -      first_action    => 'start',
1301 -      second_action   => 'start',
1302 -      require         => [Pacemaker::Resource::Ocf['neutron-netns-cleanup'],
1303 -                          Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service]],
1304 -    }
1305 -    pacemaker::constraint::colocation { 'neutron-netns-cleanup-to-openvswitch-agent-colocation':
1306 -      source  => "${::neutron::params::ovs_agent_service}-clone",
1307 -      target  => 'neutron-netns-cleanup-clone',
1308 -      score   => 'INFINITY',
1309 -      require => [Pacemaker::Resource::Ocf['neutron-netns-cleanup'],
1310 -                  Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service]],
1311 -    }
1312 -
1313 -    #another chain keystone-->neutron-server-->ovs-agent-->dhcp-->l3
1314      pacemaker::constraint::base { 'keystone-to-neutron-server-constraint':
1315        constraint_type => 'order',
1316        first_resource  => "${::keystone::params::service_name}-clone",
1317 @@ -1120,65 +1197,110 @@ if hiera('step') >= 4 {
1318        require         => [Pacemaker::Resource::Service[$::keystone::params::service_name],
1319                            Pacemaker::Resource::Service[$::neutron::params::server_service]],
1320      }
1321 -    pacemaker::constraint::base { 'neutron-server-to-openvswitch-agent-constraint':
1322 -      constraint_type => 'order',
1323 -      first_resource  => "${::neutron::params::server_service}-clone",
1324 -      second_resource => "${::neutron::params::ovs_agent_service}-clone",
1325 -      first_action    => 'start',
1326 -      second_action   => 'start',
1327 -      require         => [Pacemaker::Resource::Service[$::neutron::params::server_service],
1328 -                          Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service]],
1329 -    }
1330 -    pacemaker::constraint::base { 'neutron-openvswitch-agent-to-dhcp-agent-constraint':
1331 -      constraint_type => 'order',
1332 -      first_resource  => "${::neutron::params::ovs_agent_service}-clone",
1333 -      second_resource => "${::neutron::params::dhcp_agent_service}-clone",
1334 -      first_action    => 'start',
1335 -      second_action   => 'start',
1336 -      require         => [Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service],
1337 -                          Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service]],
1338 +    if 'openvswitch' in hiera('neutron_mechanism_drivers') {
1339 +      pacemaker::resource::service { $::neutron::params::ovs_agent_service:
1340 +        clone_params => "interleave=true",
1341 +      }
1342 +      pacemaker::resource::ocf { $::neutron::params::ovs_cleanup_service:
1343 +        ocf_agent_name => "neutron:OVSCleanup",
1344 +        clone_params => "interleave=true",
1345 +      }
1346 +      # neutron - one chain ovs-cleanup-->netns-cleanup-->ovs-agent
1347 +      pacemaker::constraint::base { 'neutron-ovs-cleanup-to-netns-cleanup-constraint':
1348 +        constraint_type => "order",
1349 +        first_resource => "${::neutron::params::ovs_cleanup_service}-clone",
1350 +        second_resource => "neutron-netns-cleanup-clone",
1351 +        first_action => "start",
1352 +        second_action => "start",
1353 +        require => [Pacemaker::Resource::Ocf["${::neutron::params::ovs_cleanup_service}"],
1354 +                    Pacemaker::Resource::Ocf['neutron-netns-cleanup']],
1355 +      }
1356 +      pacemaker::constraint::colocation { 'neutron-ovs-cleanup-to-netns-cleanup-colocation':
1357 +        source => "neutron-netns-cleanup-clone",
1358 +        target => "${::neutron::params::ovs_cleanup_service}-clone",
1359 +        score => "INFINITY",
1360 +        require => [Pacemaker::Resource::Ocf["${::neutron::params::ovs_cleanup_service}"],
1361 +                    Pacemaker::Resource::Ocf['neutron-netns-cleanup']],
1362 +      }
1363 +      pacemaker::constraint::base { 'neutron-netns-cleanup-to-openvswitch-agent-constraint':
1364 +        constraint_type => "order",
1365 +        first_resource => "neutron-netns-cleanup-clone",
1366 +        second_resource => "${::neutron::params::ovs_agent_service}-clone",
1367 +        first_action => "start",
1368 +        second_action => "start",
1369 +        require => [Pacemaker::Resource::Ocf["neutron-netns-cleanup"],
1370 +                    Pacemaker::Resource::Service["${::neutron::params::ovs_agent_service}"]],
1371 +      }
1372 +      pacemaker::constraint::colocation { 'neutron-netns-cleanup-to-openvswitch-agent-colocation':
1373 +        source => "${::neutron::params::ovs_agent_service}-clone",
1374 +        target => "neutron-netns-cleanup-clone",
1375 +        score => "INFINITY",
1376 +        require => [Pacemaker::Resource::Ocf["neutron-netns-cleanup"],
1377 +                    Pacemaker::Resource::Service["${::neutron::params::ovs_agent_service}"]],
1378 +      }
1379  
1380 +      #another chain keystone-->neutron-server-->ovs-agent-->dhcp-->l3
1381 +      pacemaker::constraint::base { 'neutron-server-to-openvswitch-agent-constraint':
1382 +        constraint_type => "order",
1383 +        first_resource => "${::neutron::params::server_service}-clone",
1384 +        second_resource => "${::neutron::params::ovs_agent_service}-clone",
1385 +        first_action => "start",
1386 +        second_action => "start",
1387 +        require => [Pacemaker::Resource::Service[$::neutron::params::server_service],
1388 +                    Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service]],
1389 +      }
1390 +      pacemaker::constraint::base { 'neutron-openvswitch-agent-to-dhcp-agent-constraint':
1391 +        constraint_type => "order",
1392 +        first_resource => "${::neutron::params::ovs_agent_service}-clone",
1393 +        second_resource => "${::neutron::params::dhcp_agent_service}-clone",
1394 +        first_action => "start",
1395 +        second_action => "start",
1396 +        require => [Pacemaker::Resource::Service["${::neutron::params::ovs_agent_service}"],
1397 +                    Pacemaker::Resource::Service["${::neutron::params::dhcp_agent_service}"]],
1398 +
1399 +      }
1400 +      pacemaker::constraint::colocation { 'neutron-openvswitch-agent-to-dhcp-agent-colocation':
1401 +        source => "${::neutron::params::dhcp_agent_service}-clone",
1402 +        target => "${::neutron::params::ovs_agent_service}-clone",
1403 +        score => "INFINITY",
1404 +        require => [Pacemaker::Resource::Service["${::neutron::params::ovs_agent_service}"],
1405 +                    Pacemaker::Resource::Service["${::neutron::params::dhcp_agent_service}"]],
1406 +      }
1407      }
1408 -    pacemaker::constraint::colocation { 'neutron-openvswitch-agent-to-dhcp-agent-colocation':
1409 -      source  => "${::neutron::params::dhcp_agent_service}-clone",
1410 -      target  => "${::neutron::params::ovs_agent_service}-clone",
1411 -      score   => 'INFINITY',
1412 -      require => [Pacemaker::Resource::Service[$::neutron::params::ovs_agent_service],
1413 -                  Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service]],
1414 -    }
1415 -    pacemaker::constraint::base { 'neutron-dhcp-agent-to-l3-agent-constraint':
1416 -      constraint_type => 'order',
1417 -      first_resource  => "${::neutron::params::dhcp_agent_service}-clone",
1418 -      second_resource => "${::neutron::params::l3_agent_service}-clone",
1419 -      first_action    => 'start',
1420 -      second_action   => 'start',
1421 -      require         => [Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service],
1422 -                          Pacemaker::Resource::Service[$::neutron::params::l3_agent_service]],
1423 -    }
1424 -    pacemaker::constraint::colocation { 'neutron-dhcp-agent-to-l3-agent-colocation':
1425 -      source  => "${::neutron::params::l3_agent_service}-clone",
1426 -      target  => "${::neutron::params::dhcp_agent_service}-clone",
1427 -      score   => 'INFINITY',
1428 -      require => [Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service],
1429 -                  Pacemaker::Resource::Service[$::neutron::params::l3_agent_service]],
1430 -    }
1431 -    pacemaker::constraint::base { 'neutron-l3-agent-to-metadata-agent-constraint':
1432 -      constraint_type => 'order',
1433 -      first_resource  => "${::neutron::params::l3_agent_service}-clone",
1434 -      second_resource => "${::neutron::params::metadata_agent_service}-clone",
1435 -      first_action    => 'start',
1436 -      second_action   => 'start',
1437 -      require         => [Pacemaker::Resource::Service[$::neutron::params::l3_agent_service],
1438 -                          Pacemaker::Resource::Service[$::neutron::params::metadata_agent_service]],
1439 -    }
1440 -    pacemaker::constraint::colocation { 'neutron-l3-agent-to-metadata-agent-colocation':
1441 -      source  => "${::neutron::params::metadata_agent_service}-clone",
1442 -      target  => "${::neutron::params::l3_agent_service}-clone",
1443 -      score   => 'INFINITY',
1444 -      require => [Pacemaker::Resource::Service[$::neutron::params::l3_agent_service],
1445 -                  Pacemaker::Resource::Service[$::neutron::params::metadata_agent_service]],
1446 +    if !('onos_ml2' in hiera('neutron_mechanism_drivers') or str2bool(hiera('opendaylight_enable_l3', 'no'))) {
1447 +      pacemaker::constraint::base { 'neutron-dhcp-agent-to-l3-agent-constraint':
1448 +        constraint_type => 'order',
1449 +        first_resource  => "${::neutron::params::dhcp_agent_service}-clone",
1450 +        second_resource => "${::neutron::params::l3_agent_service}-clone",
1451 +        first_action    => 'start',
1452 +        second_action   => 'start',
1453 +        require         => [Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service],
1454 +                            Pacemaker::Resource::Service[$::neutron::params::l3_agent_service]],
1455 +      }
1456 +      pacemaker::constraint::colocation { 'neutron-dhcp-agent-to-l3-agent-colocation':
1457 +        source  => "${::neutron::params::l3_agent_service}-clone",
1458 +        target  => "${::neutron::params::dhcp_agent_service}-clone",
1459 +        score   => 'INFINITY',
1460 +        require => [Pacemaker::Resource::Service[$::neutron::params::dhcp_agent_service],
1461 +                    Pacemaker::Resource::Service[$::neutron::params::l3_agent_service]],
1462 +      }
1463 +      pacemaker::constraint::base { 'neutron-l3-agent-to-metadata-agent-constraint':
1464 +        constraint_type => 'order',
1465 +        first_resource  => "${::neutron::params::l3_agent_service}-clone",
1466 +        second_resource => "${::neutron::params::metadata_agent_service}-clone",
1467 +        first_action    => 'start',
1468 +        second_action   => 'start',
1469 +        require         => [Pacemaker::Resource::Service[$::neutron::params::l3_agent_service],
1470 +                            Pacemaker::Resource::Service[$::neutron::params::metadata_agent_service]],
1471 +      }
1472 +      pacemaker::constraint::colocation { 'neutron-l3-agent-to-metadata-agent-colocation':
1473 +        source  => "${::neutron::params::metadata_agent_service}-clone",
1474 +        target  => "${::neutron::params::l3_agent_service}-clone",
1475 +        score   => 'INFINITY',
1476 +        require => [Pacemaker::Resource::Service[$::neutron::params::l3_agent_service],
1477 +                    Pacemaker::Resource::Service[$::neutron::params::metadata_agent_service]],
1478 +      }
1479      }
1480 -
1481      # Nova
1482      pacemaker::resource::service { $::nova::params::api_service_name :
1483        clone_params => 'interleave=true',
1484 @@ -1276,7 +1398,7 @@ if hiera('step') >= 4 {
1485                    Pacemaker::Resource::Service[$::nova::params::conductor_service_name]],
1486      }
1487  
1488 -    # Ceilometer
1489 +    # Ceilometer and Aodh
1490      case downcase(hiera('ceilometer_backend')) {
1491        /mysql/: {
1492          pacemaker::resource::service { $::ceilometer::params::agent_central_service_name :
1493 @@ -1298,10 +1420,19 @@ if hiera('step') >= 4 {
1494      pacemaker::resource::service { $::ceilometer::params::api_service_name :
1495        clone_params => 'interleave=true',
1496      }
1497 -    pacemaker::resource::service { $::ceilometer::params::alarm_evaluator_service_name :
1498 +    pacemaker::resource::service { $::aodh::params::notifier_service_name :
1499        clone_params => 'interleave=true',
1500      }
1501 -    pacemaker::resource::service { $::ceilometer::params::alarm_notifier_service_name :
1502 +    pacemaker::resource::service { $::aodh::params::expirer_package_serice :
1503 +      clone_params => 'interleave=true',
1504 +    }
1505 +    pacemaker::resource::service { $::aodh::params::listener_service_name :
1506 +      clone_params => 'interleave=true',
1507 +    }
1508 +    pacemaker::resource::service { $::aodh::params::api_service_name :
1509 +      clone_params => 'interleave=true',
1510 +    }
1511 +    pacemaker::resource::service { $::aodh::params::evaluator_service_name :
1512        clone_params => 'interleave=true',
1513      }
1514      pacemaker::resource::service { $::ceilometer::params::agent_notification_service_name :
1515 @@ -1315,8 +1446,19 @@ if hiera('step') >= 4 {
1516      # Fedora doesn't know `require-all` parameter for constraints yet
1517      if $::operatingsystem == 'Fedora' {
1518        $redis_ceilometer_constraint_params = undef
1519 +      $redis_aodh_constraint_params = undef
1520      } else {
1521        $redis_ceilometer_constraint_params = 'require-all=false'
1522 +      $redis_aodh_constraint_params = 'require-all=false'
1523 +    }
1524 +    pacemaker::constraint::base { 'keystone-then-aodh-api-constraint':
1525 +      constraint_type => 'order',
1526 +      first_resource  => "${::keystone::params::service_name}-clone",
1527 +      second_resource => "${::aodh::params::api_service_name}-clone",
1528 +      first_action    => 'start',
1529 +      second_action   => 'start',
1530 +      require         => [Pacemaker::Resource::Service[$::aodh::params::api_service_name],
1531 +                          Pacemaker::Resource::Service[$::keystone::params::service_name]],
1532      }
1533      pacemaker::constraint::base { 'redis-then-ceilometer-central-constraint':
1534        constraint_type   => 'order',
1535 @@ -1328,6 +1470,16 @@ if hiera('step') >= 4 {
1536        require           => [Pacemaker::Resource::Ocf['redis'],
1537                              Pacemaker::Resource::Service[$::ceilometer::params::agent_central_service_name]],
1538      }
1539 +    pacemaker::constraint::base { 'redis-then-aodh-evaluator-constraint':
1540 +      constraint_type   => 'order',
1541 +      first_resource    => 'redis-master',
1542 +      second_resource   => "${::aodh::params::evaluator_service_name}-clone",
1543 +      first_action      => 'promote',
1544 +      second_action     => 'start',
1545 +      constraint_params => $redis_aodh_constraint_params,
1546 +      require           => [Pacemaker::Resource::Ocf['redis'],
1547 +                            Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name]],
1548 +    }
1549      pacemaker::constraint::base { 'keystone-then-ceilometer-central-constraint':
1550        constraint_type => 'order',
1551        first_resource  => "${::keystone::params::service_name}-clone",
1552 @@ -1378,53 +1530,37 @@ if hiera('step') >= 4 {
1553        require => [Pacemaker::Resource::Service[$::ceilometer::params::api_service_name],
1554                    Pacemaker::Resource::Ocf['delay']],
1555      }
1556 -    pacemaker::constraint::base { 'ceilometer-delay-then-ceilometer-alarm-evaluator-constraint':
1557 +    pacemaker::constraint::base { 'aodh-delay-then-aodh-evaluator-constraint':
1558        constraint_type => 'order',
1559        first_resource  => 'delay-clone',
1560 -      second_resource => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
1561 +      second_resource => "${::aodh::params::evaluator_service_name}-clone",
1562        first_action    => 'start',
1563        second_action   => 'start',
1564 -      require         => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
1565 +      require         => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
1566                            Pacemaker::Resource::Ocf['delay']],
1567      }
1568 -    pacemaker::constraint::colocation { 'ceilometer-alarm-evaluator-with-ceilometer-delay-colocation':
1569 -      source  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
1570 +    pacemaker::constraint::colocation { 'aodh-evaluator-with-aodh-delay-colocation':
1571 +      source  => "${::aodh::params::evaluator_service_name}-clone",
1572        target  => 'delay-clone',
1573        score   => 'INFINITY',
1574 -      require => [Pacemaker::Resource::Service[$::ceilometer::params::api_service_name],
1575 +      require => [Pacemaker::Resource::Service[$::horizon::params::http_service],
1576                    Pacemaker::Resource::Ocf['delay']],
1577      }
1578 -    pacemaker::constraint::base { 'ceilometer-alarm-evaluator-then-ceilometer-alarm-notifier-constraint':
1579 -      constraint_type => 'order',
1580 -      first_resource  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
1581 -      second_resource => "${::ceilometer::params::alarm_notifier_service_name}-clone",
1582 -      first_action    => 'start',
1583 -      second_action   => 'start',
1584 -      require         => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
1585 -                          Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
1586 -    }
1587 -    pacemaker::constraint::colocation { 'ceilometer-alarm-notifier-with-ceilometer-alarm-evaluator-colocation':
1588 -      source  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
1589 -      target  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
1590 -      score   => 'INFINITY',
1591 -      require => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
1592 -                  Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
1593 -    }
1594 -    pacemaker::constraint::base { 'ceilometer-alarm-notifier-then-ceilometer-notification-constraint':
1595 +    pacemaker::constraint::base { 'aodh-evaluator-then-aodh-notifier-constraint':
1596        constraint_type => 'order',
1597 -      first_resource  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
1598 -      second_resource => "${::ceilometer::params::agent_notification_service_name}-clone",
1599 +      first_resource  => "${::aodh::params::evaluator_service_name}-clone",
1600 +      second_resource => "${::aodh::params::notifier_service_name}-clone",
1601        first_action    => 'start',
1602        second_action   => 'start',
1603 -      require         => [Pacemaker::Resource::Service[$::ceilometer::params::agent_notification_service_name],
1604 -                          Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
1605 +      require         => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
1606 +                          Pacemaker::Resource::Service[$::aodh::params::notifier_service_name]],
1607      }
1608 -    pacemaker::constraint::colocation { 'ceilometer-notification-with-ceilometer-alarm-notifier-colocation':
1609 -      source  => "${::ceilometer::params::agent_notification_service_name}-clone",
1610 -      target  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
1611 +    pacemaker::constraint::colocation { 'aodh-notifier-with-aodh-evaluator-colocation':
1612 +      source  => "${::aodh::params::notifier_service_name}-clone",
1613 +      target  => "${::aodh::params::evaluator_service_name}-clone",
1614        score   => 'INFINITY',
1615 -      require => [Pacemaker::Resource::Service[$::ceilometer::params::agent_notification_service_name],
1616 -                  Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
1617 +      require => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
1618 +                  Pacemaker::Resource::Service[$::aodh::params::notifier_service_name]],
1619      }
1620      if downcase(hiera('ceilometer_backend')) == 'mongodb' {
1621        pacemaker::constraint::base { 'mongodb-then-ceilometer-central-constraint':
1622 diff --git a/puppet/manifests/overcloud_opendaylight.pp b/puppet/manifests/overcloud_opendaylight.pp
1623 new file mode 100644
1624 index 0000000..aeb31be
1625 --- /dev/null
1626 +++ b/puppet/manifests/overcloud_opendaylight.pp
1627 @@ -0,0 +1,27 @@
1628 +# Copyright 2015 Red Hat, Inc.
1629 +# All Rights Reserved.
1630 +#
1631 +# Licensed under the Apache License, Version 2.0 (the "License"); you may
1632 +# not use this file except in compliance with the License. You may obtain
1633 +# a copy of the License at
1634 +#
1635 +#     http://www.apache.org/licenses/LICENSE-2.0
1636 +#
1637 +# Unless required by applicable law or agreed to in writing, software
1638 +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1639 +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1640 +# License for the specific language governing permissions and limitations
1641 +# under the License.
1642 +
1643 +include ::tripleo::packages
1644 +
1645 +if count(hiera('ntp::servers')) > 0 {
1646 +  include ::ntp
1647 +}
1648 +
1649 +class {"opendaylight":
1650 +  extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
1651 +  odl_rest_port  => hiera('opendaylight_port'),
1652 +  enable_l3      => hiera('opendaylight_enable_l3', 'no'),
1653 +}
1654 +
1655 diff --git a/puppet/opendaylight-puppet.yaml b/puppet/opendaylight-puppet.yaml
1656 new file mode 100644
1657 index 0000000..6488e0e
1658 --- /dev/null
1659 +++ b/puppet/opendaylight-puppet.yaml
1660 @@ -0,0 +1,223 @@
1661 +heat_template_version: 2015-04-30
1662 +
1663 +description: >
1664 +  OpenDaylight node configured by Puppet.
1665 +
1666 +parameters:
1667 +  OpenDaylightFlavor:
1668 +    default: baremetal
1669 +    description: The flavor to use for the OpenDaylight node
1670 +    type: string
1671 +  OpenDaylightImage:
1672 +    default: overcloud-full
1673 +    description: The image to use for the OpenDaylight node
1674 +    type: string
1675 +  OpenDaylightHostname:
1676 +    default: opendaylight-server
1677 +    description: The hostname to use for the OpenDaylight node
1678 +    type: string
1679 +  OpenDaylightUsername:
1680 +    default: admin
1681 +    description: The admin user for the OpenDaylight node
1682 +    type: string
1683 +  OpenDaylightPassword:
1684 +    default: ''
1685 +    description: The admin password for the OpenDaylight node
1686 +    type: string
1687 +    hidden: true
1688 +  OpenDaylightEnableL3:
1689 +    description: Knob to enable/disable ODL L3
1690 +    type: string
1691 +    default: 'no'
1692 +  OpenDaylightFeatures:
1693 +    description: List of features to install with ODL
1694 +    type: comma_delimited_list
1695 +    default: "odl-ovsdb-openstack"
1696 +  OpenDaylightPort:
1697 +    default: 8081
1698 +    description: Set OpenDaylight service port
1699 +    type: number
1700 +  KeyName:
1701 +    description: The keypair to use for SSH access to the node (via heat-admin user)
1702 +    type: string
1703 +    default: default
1704 +    constraints:
1705 +      - custom_constraint: nova.keypair
1706 +  ImageUpdatePolicy:
1707 +    default: 'REBUILD_PRESERVE_EPHEMERAL'
1708 +    description: What policy to use when reconstructing instances. REBUILD for rebuilds, REBUILD_PRESERVE_EPHEMERAL to preserve /mnt.
1709 +    type: string
1710 +  UpdateIdentifier:
1711 +    default: ''
1712 +    type: string
1713 +    description: >
1714 +      Setting to a previously unused value during stack-update will trigger
1715 +      package update on all nodes
1716 +  NtpServer:
1717 +    type: string
1718 +    default: ''
1719 +  PublicInterface:
1720 +    default: nic1
1721 +    description: What interface to bridge onto br-ex for network nodes.
1722 +    type: string
1723 +
1724 +resources:
1725 +  OpenDaylightNode:
1726 +    type: OS::Nova::Server
1727 +    properties:
1728 +      image: {get_param: OpenDaylightImage}
1729 +      image_update_policy: {get_param: ImageUpdatePolicy}
1730 +      flavor: {get_param: OpenDaylightFlavor}
1731 +      key_name: {get_param: KeyName}
1732 +      networks:
1733 +        - network: ctlplane
1734 +      user_data_format: SOFTWARE_CONFIG
1735 +      user_data: {get_resource: NodeUserData}
1736 +      name: {get_param: OpenDaylightHostname}
1737 +
1738 +  NodeUserData:
1739 +    type: OS::TripleO::NodeUserData
1740 +
1741 +  ExternalPort:
1742 +    type: OS::TripleO::Controller::Ports::ExternalPort
1743 +    properties:
1744 +      ControlPlaneIP: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1745 +
1746 +  InternalApiPort:
1747 +    type: OS::TripleO::Controller::Ports::InternalApiPort
1748 +    properties:
1749 +      ControlPlaneIP: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1750 +
1751 +  NetIpMap:
1752 +    type: OS::TripleO::Network::Ports::NetIpMap
1753 +    properties:
1754 +      ControlPlaneIp: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1755 +      ExternalIp: {get_attr: [ExternalPort, ip_address]}
1756 +      InternalApiIp: {get_attr: [InternalApiPort, ip_address]}
1757 +
1758 +  NetIpSubnetMap:
1759 +    type: OS::TripleO::Network::Ports::NetIpSubnetMap
1760 +    properties:
1761 +      ControlPlaneIp: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1762 +      ExternalIpSubnet: {get_attr: [ExternalPort, ip_subnet]}
1763 +      InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
1764 +
1765 +  NetworkConfig:
1766 +    type: OS::TripleO::Controller::Net::SoftwareConfig
1767 +    properties:
1768 +      ControlPlaneIp: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1769 +      ExternalIpSubnet: {get_attr: [ExternalPort, ip_subnet]}
1770 +      InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
1771 +
1772 +  NetworkDeployment:
1773 +    type: OS::TripleO::SoftwareDeployment
1774 +    properties:
1775 +      config: {get_resource: NetworkConfig}
1776 +      server: {get_resource: OpenDaylightNode}
1777 +      input_values:
1778 +        bridge_name: br-ex
1779 +        interface_name: {get_param: PublicInterface}
1780 +
1781 +  OpenDaylightDeployment:
1782 +    type: OS::TripleO::SoftwareDeployment
1783 +    depends_on: NetworkDeployment
1784 +    properties:
1785 +      config: {get_resource: OpenDaylightConfig}
1786 +      server: {get_resource: OpenDaylightNode}
1787 +      input_values:
1788 +        ntp_servers:
1789 +          str_replace:
1790 +            template: '["server"]'
1791 +            params:
1792 +              server: {get_param: NtpServer}
1793 +        opendaylight_port: {get_param: OpenDaylightPort}
1794 +        opendaylight_enable_l3: {get_param: OpenDaylightEnableL3}
1795 +        opendaylight_username: {get_param: OpenDaylightUsername}
1796 +        opendaylight_password: {get_param: OpenDaylightPassword}
1797 +        opendaylight_features: {get_param: OpenDaylightFeatures}
1798 +
1799 +  OpenDaylightConfig:
1800 +    type: OS::Heat::StructuredConfig
1801 +    properties:
1802 +      group: os-apply-config
1803 +      config:
1804 +        hiera:
1805 +          hierarchy:
1806 +            - '"%{::uuid}"'
1807 +            - heat_config_%{::deploy_config_name}
1808 +            - extraconfig
1809 +            - bootstrap_node # provided by BootstrapNodeConfig
1810 +            - all_nodes # provided by allNodesConfig
1811 +            - vip_data # provided by vip-config
1812 +            - RedHat # Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1236143
1813 +            - common
1814 +          datafiles:
1815 +            common:
1816 +              raw_data: {get_file: hieradata/common.yaml}
1817 +              mapped_data:
1818 +                ntp::servers: {get_input: ntp_servers}
1819 +                opendaylight::admin_username: {get_param: OpenDaylightUsername}
1820 +                opendaylight::admin_password: {get_param: OpenDaylightPassword}
1821 +                opendaylight_port: {get_input: opendaylight_port}
1822 +                opendaylight_enable_l3: {get_input: opendaylight_enable_l3}
1823 +                opendaylight_features: {get_input: opendaylight_features}
1824 +            ceph:
1825 +              raw_data: {get_file: hieradata/ceph.yaml}
1826 +
1827 +  UpdateConfig:
1828 +    type: OS::TripleO::Tasks::PackageUpdate
1829 +
1830 +  UpdateDeployment:
1831 +    type: OS::Heat::SoftwareDeployment
1832 +    properties:
1833 +      config: {get_resource: UpdateConfig}
1834 +      server: {get_resource: OpenDaylightNode}
1835 +      input_values:
1836 +        update_identifier:
1837 +          get_param: UpdateIdentifier
1838 +
1839 +  OpenDaylightHostsConfig:
1840 +    type: OS::Heat::SoftwareConfig
1841 +    properties:
1842 +      group: script
1843 +      config: |
1844 +        #!/usr/bin/env bash
1845 +        echo -e "$(facter ipaddress)\t\t$(hostname -f)\t$(hostname -s)" >> /etc/hosts
1846 +
1847 +  OpenDaylightHostsDeployment:
1848 +    type: OS::Heat::StructuredDeployment
1849 +    depends_on: OpenDaylightDeployment
1850 +    properties:
1851 +      server: {get_resource: OpenDaylightNode}
1852 +      config: {get_resource: OpenDaylightHostsConfig}
1853 +
1854 +  OpenDaylightPuppetConfig:
1855 +    type: OS::Heat::SoftwareConfig
1856 +    properties:
1857 +      group: puppet
1858 +      config:
1859 +        get_file: manifests/overcloud_opendaylight.pp
1860 +
1861 +  OpenDaylightPuppetDeployment:
1862 +    depends_on: OpenDaylightHostsDeployment
1863 +    type: OS::Heat::StructuredDeployment
1864 +    properties:
1865 +      server: {get_resource: OpenDaylightNode}
1866 +      config: {get_resource: OpenDaylightPuppetConfig}
1867 +      input_values:
1868 +        update_identifier: {get_param: UpdateIdentifier}
1869 +
1870 +outputs:
1871 +  ip_address:
1872 +    description: IP address of the server in the ctlplane network
1873 +    value: {get_attr: [OpenDaylightNode, networks, ctlplane, 0]}
1874 +  opendaylight_controller_ip:
1875 +    description: IP address of the server on the internal network
1876 +    value: {get_attr: [InternalApiPort, ip_address]}
1877 +  config_identifier:
1878 +    description: identifier which changes if the node configuration may need re-applying
1879 +    value:
1880 +      list_join:
1881 +      - ','
1882 +      - - {get_attr: [OpenDaylightDeployment, deploy_stdout]}
1883 +        - {get_param: UpdateIdentifier}
1884 -- 
1885 2.5.0
1886