Enabling dhcp isolated networks and metadata network
[apex.git] / build / opnfv-tripleo-heat-templates.patch
index fd1cd0e..a8ae97e 100644 (file)
@@ -1,28 +1,35 @@
-From f9cd66d8c353411b8c3b32a45ab765eaaee02fec Mon Sep 17 00:00:00 2001
+From 1291bb9e965e0a12bb0b769e1ece4b0cdb9a7659 Mon Sep 17 00:00:00 2001
 From: Tim Rozet <tdrozet@gmail.com>
 Date: Tue, 12 Jan 2016 16:49:57 -0500
-Subject: [PATCH] Adds current opnfv patch with ODL L2/L3 and ONOS support
+Subject: [PATCH] Adds current opnfv patch with ODL and ONOS support
 
 ---
  environments/onos.yaml                             |   8 +
  environments/opendaylight-external.yaml            |  25 ++
  environments/opendaylight.yaml                     |  25 ++
- environments/opendaylight_l3.yaml                  |   8 +
+ environments/opendaylight_l3.yaml                  |   9 +
+ environments/opendaylight_sdnvpn.yaml              |  29 ++
+ environments/opendaylight_sfc.yaml                 |  28 ++
+ network/endpoints/endpoint_map.yaml                |  31 ++
  overcloud-resource-registry-puppet.yaml            |   3 +
- overcloud-without-mergepy.yaml                     |  73 +++++
- puppet/all-nodes-config.yaml                       |   6 +
- puppet/compute.yaml                                |  35 +++
- puppet/controller.yaml                             |  47 ++++
- puppet/manifests/overcloud_compute.pp              |  33 ++-
- puppet/manifests/overcloud_controller.pp           |  86 +++++-
- puppet/manifests/overcloud_controller_pacemaker.pp | 302 +++++++++++++--------
+ overcloud-without-mergepy.yaml                     |  94 +++++
+ puppet/all-nodes-config.yaml                       |  17 +
+ puppet/compute.yaml                                |  41 ++
+ puppet/controller.yaml                             |  93 ++++-
+ puppet/hieradata/common.yaml                       |   1 +
+ puppet/hieradata/controller.yaml                   |   5 +-
+ puppet/manifests/overcloud_compute.pp              |  47 ++-
+ puppet/manifests/overcloud_controller.pp           | 126 +++++-
+ puppet/manifests/overcloud_controller_pacemaker.pp | 456 ++++++++++++++-------
  puppet/manifests/overcloud_opendaylight.pp         |  27 ++
- puppet/opendaylight-puppet.yaml                    | 217 +++++++++++++++
- 14 files changed, 771 insertions(+), 124 deletions(-)
+ puppet/opendaylight-puppet.yaml                    | 223 ++++++++++
+ 19 files changed, 1126 insertions(+), 162 deletions(-)
  create mode 100644 environments/onos.yaml
  create mode 100644 environments/opendaylight-external.yaml
  create mode 100644 environments/opendaylight.yaml
  create mode 100644 environments/opendaylight_l3.yaml
+ create mode 100644 environments/opendaylight_sdnvpn.yaml
+ create mode 100644 environments/opendaylight_sfc.yaml
  create mode 100644 puppet/manifests/overcloud_opendaylight.pp
  create mode 100644 puppet/opendaylight-puppet.yaml
 
@@ -104,18 +111,151 @@ index 0000000..c8abf75
 +      opendaylight_install: true
 diff --git a/environments/opendaylight_l3.yaml b/environments/opendaylight_l3.yaml
 new file mode 100644
-index 0000000..be7c2a8
+index 0000000..05c0aff
 --- /dev/null
 +++ b/environments/opendaylight_l3.yaml
-@@ -0,0 +1,8 @@
+@@ -0,0 +1,9 @@
 +parameters:
 +    #NeutronEnableL3Agent: false
 +    NeutronEnableForceMetadata: true
-+    OpenDaylightEnableL3: true
++    OpenDaylightEnableL3: "'yes'"
 +    NeutronServicePlugins: "networking_odl.l3.l3_odl.OpenDaylightL3RouterPlugin"
 +    ExtraConfig:
 +      neutron_mechanism_drivers: ['opendaylight']
 +      neutron_tenant_network_type: vxlan
++      opendaylight_install: true
+diff --git a/environments/opendaylight_sdnvpn.yaml b/environments/opendaylight_sdnvpn.yaml
+new file mode 100644
+index 0000000..3a14975
+--- /dev/null
++++ b/environments/opendaylight_sdnvpn.yaml
+@@ -0,0 +1,29 @@
++# Environment file used to enable OpenDaylight
++# Currently uses overcloud image that is assumed
++# to be virt-customized with ODL RPM already on it
++
++# These parameters customize the OpenDaylight Node
++# The user name and password are for the ODL service
++# Defaults are included here for reference
++#parameter_defaults:
++#  OpenDaylightFlavor: baremetal
++#  OpenDaylightHostname: opendaylight-server
++#  OpenDaylightImage: overcloud-full
++#  OpenDaylightUsername: admin
++#  OpenDaylightPassword: admin
++
++parameters:
++    # increase this if you need more ODL nodes
++    # OpenDaylightCount: 1
++    ControllerEnableSwiftStorage: false
++    OpenDaylightFeatures: "odl-ovsdb-openstack,odl-vpnservice-api,odl-vpnservice-impl,odl-vpnservice-impl-rest,odl-vpnservice-impl-ui,odl-vpnservice-core"
++    NeutronL3HA: false
++    NeutronServicePlugins: "router,qos,networking_bgpvpn.neutron.services.plugin.BGPVPNPlugin"
++    ExtraConfig:
++      tripleo::ringbuilder::build_ring: False
++      neutron_mechanism_drivers: ['opendaylight']
++      neutron_tenant_network_type: vxlan
++      # Enable this if you want OpenDaylight on the contollers
++      # reduce OpenDaylightCount to 0 if you don't want any
++      # OpenDaylight only nodes
++      opendaylight_install: true
+diff --git a/environments/opendaylight_sfc.yaml b/environments/opendaylight_sfc.yaml
+new file mode 100644
+index 0000000..3dd1e13
+--- /dev/null
++++ b/environments/opendaylight_sfc.yaml
+@@ -0,0 +1,28 @@
++# Environment file used to enable OpenDaylight
++# Currently uses overcloud image that is assumed
++# to be virt-customized with ODL RPM already on it
++
++# These parameters customize the OpenDaylight Node
++# The user name and password are for the ODL service
++# Defaults are included here for reference
++#parameter_defaults:
++#  OpenDaylightFlavor: baremetal
++#  OpenDaylightHostname: opendaylight-server
++#  OpenDaylightImage: overcloud-full
++#  OpenDaylightUsername: admin
++#  OpenDaylightPassword: admin
++
++parameters:
++    # increase this if you need more ODL nodes
++    # OpenDaylightCount: 1
++    ControllerEnableSwiftStorage: false
++    OpenDaylightFeatures: "odl-ovsdb-sfc-rest"
++    NeutronL3HA: false
++    ExtraConfig:
++      tripleo::ringbuilder::build_ring: False
++      neutron_mechanism_drivers: ['opendaylight']
++      neutron_tenant_network_type: vxlan
++      # Enable this if you want OpenDaylight on the contollers
++      # reduce OpenDaylightCount to 0 if you don't want any
++      # OpenDaylight only nodes
++      opendaylight_install: true
+diff --git a/network/endpoints/endpoint_map.yaml b/network/endpoints/endpoint_map.yaml
+index 0521401..7caa91b 100644
+--- a/network/endpoints/endpoint_map.yaml
++++ b/network/endpoints/endpoint_map.yaml
+@@ -4,6 +4,9 @@ description: >
+   A Map of OpenStack Endpoints
+ parameters:
++  AodhApiVirtualIP:
++    type: string
++    default: ''
+   CeilometerApiVirtualIP:
+     type: string
+     default: ''
+@@ -43,6 +46,9 @@ parameters:
+   EndpointMap:
+     type: json
+     default:
++      AodhAdmin: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
++      AodhInternal: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
++      AodhPublic: {protocol: 'http', port: '8042', host: 'IP_ADDRESS'}
+       CeilometerAdmin: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
+       CeilometerInternal: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
+       CeilometerPublic: {protocol: 'http', port: '8777', host: 'IP_ADDRESS'}
+@@ -83,6 +89,28 @@ parameters:
+ resources:
++  AodhInternal:
++    type: OS::TripleO::Endpoint
++    properties:
++      EndpointName: AodhInternal
++      EndpointMap: { get_param: EndpointMap }
++      CloudName: {get_param: CloudName}
++      IP: {get_param: AodhApiVirtualIP}
++  AodhPublic:
++    type: OS::TripleO::Endpoint
++    properties:
++      EndpointName: AodhPublic
++      EndpointMap: { get_param: EndpointMap }
++      CloudName: {get_param: CloudName}
++      IP: {get_param: PublicVirtualIP}
++  AodhAdmin:
++    type: OS::TripleO::Endpoint
++    properties:
++      EndpointName: AodhAdmin
++      EndpointMap: { get_param: EndpointMap }
++      CloudName: {get_param: CloudName}
++      IP: {get_param: AodhApiVirtualIP}
++
+   CeilometerInternal:
+     type: OS::TripleO::Endpoint
+     properties:
+@@ -407,6 +435,9 @@ resources:
+ outputs:
+   endpoint_map:
+     value:
++      AodhInternal: {get_attr: [ AodhInternal, endpoint] }
++      AodhPublic: {get_attr: [ AodhPublic, endpoint] }
++      AodhAdmin: {get_attr: [ AodhAdmin, endpoint] }
+       CeilometerInternal: {get_attr: [ CeilometerInternal, endpoint] }
+       CeilometerPublic: {get_attr: [ CeilometerPublic, endpoint] }
+       CeilometerAdmin: {get_attr: [ CeilometerAdmin, endpoint] }
 diff --git a/overcloud-resource-registry-puppet.yaml b/overcloud-resource-registry-puppet.yaml
 index 4cfed6b..adecc79 100644
 --- a/overcloud-resource-registry-puppet.yaml
@@ -131,10 +271,22 @@ index 4cfed6b..adecc79 100644
    # NodeUserData == Cloud-init additional user-data, e.g cloud-config
    # ControllerExtraConfigPre == Controller configuration pre service deployment
 diff --git a/overcloud-without-mergepy.yaml b/overcloud-without-mergepy.yaml
-index a532c2f..1aa87ae 100644
+index a532c2f..67d83af 100644
 --- a/overcloud-without-mergepy.yaml
 +++ b/overcloud-without-mergepy.yaml
-@@ -113,6 +113,10 @@ parameters:
+@@ -15,6 +15,11 @@ parameters:
+     description: The password for the keystone admin account, used for monitoring, querying neutron etc.
+     type: string
+     hidden: true
++  AodhPassword:
++    default: unset
++    description: The password for the aodh services
++    type: string
++    hidden: true
+   CeilometerBackend:
+     default: 'mongodb'
+     description: The ceilometer backend type.
+@@ -113,6 +118,10 @@ parameters:
      default: ''
      type: string
      description: Neutron ID for ctlplane network.
@@ -145,7 +297,7 @@ index a532c2f..1aa87ae 100644
    NeutronEnableTunnelling:
      type: string
      default: "True"
-@@ -227,6 +231,27 @@ parameters:
+@@ -227,6 +236,31 @@ parameters:
      default: false
      description: Should MongoDb journaling be disabled
      type: boolean
@@ -157,6 +309,10 @@ index a532c2f..1aa87ae 100644
 +    description: Knob to enable/disable ODL L3
 +    type: string
 +    default: 'no'
++  OpenDaylightFeatures:
++    description: List of features to install with ODL
++    type: comma_delimited_list
++    default: "odl-ovsdb-openstack"
 +  OpenDaylightInstall:
 +    default: false
 +    description: Whether to install OpenDaylight on the control nodes.
@@ -173,7 +329,15 @@ index a532c2f..1aa87ae 100644
    PublicVirtualFixedIPs:
      default: []
      description: >
-@@ -664,6 +689,18 @@ parameters:
+@@ -575,6 +609,7 @@ parameters:
+     default:
+       NeutronTenantNetwork: tenant
+       CeilometerApiNetwork: internal_api
++      AodhApiNetwork: internal_api
+       MongoDbNetwork: internal_api
+       CinderApiNetwork: internal_api
+       CinderIscsiNetwork: storage
+@@ -664,6 +699,18 @@ parameters:
        structure as ExtraConfig.
      type: json
  
@@ -192,7 +356,7 @@ index a532c2f..1aa87ae 100644
    # Hostname format for each role
    # Note %index% is translated into the index of the node, e.g 0/1/2 etc
    # and %stackname% is replaced with OS::stack_name in the template below.
-@@ -688,6 +725,10 @@ parameters:
+@@ -688,6 +735,10 @@ parameters:
      type: string
      description: Format for CephStorage node hostnames
      default: '%stackname%-cephstorage-%index%'
@@ -203,7 +367,15 @@ index a532c2f..1aa87ae 100644
  
    # Identifiers to trigger tasks on nodes
    UpdateIdentifier:
-@@ -770,6 +811,28 @@ resources:
+@@ -758,6 +809,7 @@ resources:
+     properties:
+       CloudName: {get_param: CloudName}
+       CeilometerApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
++      AodhApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+       CinderApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CinderApiNetwork]}]}
+       GlanceApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
+       GlanceRegistryVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceRegistryNetwork]}]}
+@@ -770,6 +822,29 @@ resources:
        SwiftProxyVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, SwiftProxyNetwork]}]}
        PublicVirtualIP: {get_attr: [VipMap, net_ip_map, external]}
  
@@ -221,6 +393,7 @@ index a532c2f..1aa87ae 100644
 +          OpenDaylightImage: {get_param: OpenDaylightImage}
 +          OpenDaylightPort: {get_param: OpenDaylightPort}
 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
++          OpenDaylightFeatures: {get_param: OpenDaylightFeatures}
 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
 +          OpenDaylightEnableL3: {get_param: OpenDaylightEnableL3}
 +          OpenDaylightHostname:
@@ -232,7 +405,15 @@ index a532c2f..1aa87ae 100644
    Controller:
      type: OS::Heat::ResourceGroup
      depends_on: Networks
-@@ -832,6 +895,7 @@ resources:
+@@ -781,6 +856,7 @@ resources:
+         properties:
+           AdminPassword: {get_param: AdminPassword}
+           AdminToken: {get_param: AdminToken}
++          AodhPassword: {get_param: AodhPassword}
+           CeilometerBackend: {get_param: CeilometerBackend}
+           CeilometerMeteringSecret: {get_param: CeilometerMeteringSecret}
+           CeilometerPassword: {get_param: CeilometerPassword}
+@@ -832,6 +908,7 @@ resources:
            NeutronBridgeMappings: {get_param: NeutronBridgeMappings}
            NeutronExternalNetworkBridge: {get_param: NeutronExternalNetworkBridge}
            NeutronEnableTunnelling: {get_param: NeutronEnableTunnelling}
@@ -240,29 +421,39 @@ index a532c2f..1aa87ae 100644
            NeutronNetworkVLANRanges: {get_param: NeutronNetworkVLANRanges}
            NeutronPublicInterface: {get_param: NeutronPublicInterface}
            NeutronPublicInterfaceDefaultRoute: {get_param: NeutronPublicInterfaceDefaultRoute}
-@@ -853,6 +917,11 @@ resources:
+@@ -853,6 +930,12 @@ resources:
            NovaPassword: {get_param: NovaPassword}
            NtpServer: {get_param: NtpServer}
            MongoDbNoJournal: {get_param: MongoDbNoJournal}
 +          OpenDaylightPort: {get_param: OpenDaylightPort}
 +          OpenDaylightInstall: {get_param: OpenDaylightInstall}
 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
++          OpenDaylightFeatures: {get_param: OpenDaylightFeatures}
 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
 +          OpenDaylightEnableL3: {get_param: OpenDaylightEnableL3}
            PcsdPassword: {get_resource: PcsdPassword}
            PublicVirtualInterface: {get_param: PublicVirtualInterface}
            RabbitPassword: {get_param: RabbitPassword}
-@@ -948,6 +1017,9 @@ resources:
+@@ -878,6 +961,7 @@ resources:
+           ServiceNetMap: {get_param: ServiceNetMap}
+           EndpointMap: {get_attr: [EndpointMap, endpoint_map]}
+           CeilometerApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
++          AodhApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+           CinderApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CinderApiNetwork]}]}
+           HeatApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
+           GlanceApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
+@@ -948,6 +1032,10 @@ resources:
            NovaPublicIP: {get_attr: [PublicVirtualIP, ip_address]}
            NovaPassword: {get_param: NovaPassword}
            NtpServer: {get_param: NtpServer}
 +          OpenDaylightPort: {get_param: OpenDaylightPort}
 +          OpenDaylightUsername: {get_param: OpenDaylightUsername}
 +          OpenDaylightPassword: {get_param: OpenDaylightPassword}
++          OpenDaylightFeatures: {get_param: OpenDaylightFeatures}
            RabbitHost: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, RabbitMqNetwork]}]}
            RabbitPassword: {get_param: RabbitPassword}
            RabbitUserName: {get_param: RabbitUserName}
-@@ -1068,6 +1140,7 @@ resources:
+@@ -1068,6 +1156,7 @@ resources:
        compute_hosts: {get_attr: [Compute, hosts_entry]}
        controller_hosts: {get_attr: [Controller, hosts_entry]}
        controller_ips: {get_attr: [Controller, ip_address]}
@@ -270,8 +461,34 @@ index a532c2f..1aa87ae 100644
        block_storage_hosts: {get_attr: [BlockStorage, hosts_entry]}
        object_storage_hosts: {get_attr: [ObjectStorage, hosts_entry]}
        ceph_storage_hosts: {get_attr: [CephStorage, hosts_entry]}
+@@ -1081,6 +1170,7 @@ resources:
+       heat_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
+       swift_proxy_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, SwiftProxyNetwork]}]}
+       ceilometer_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
++      aodh_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+       nova_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
+       nova_metadata_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
+       glance_api_node_ips: {get_attr: [ControllerIpListMap, net_ip_map, {get_param: [ServiceNetMap, GlanceApiNetwork]}]}
+@@ -1189,6 +1279,7 @@ resources:
+         nova_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
+         nova_metadata_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
+         ceilometer_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
++        aodh_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+         heat_api_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HeatApiNetwork]}]}
+         horizon_vip: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, HorizonNetwork]}]}
+         redis_vip: {get_attr: [RedisVirtualIP, ip_address]}
+@@ -1434,6 +1525,9 @@ outputs:
+   PublicVip:
+     description: Controller VIP for public API endpoints
+     value: {get_attr: [PublicVirtualIP, ip_address]}
++  AodhInternalVip:
++    description: VIP for Aodh API internal endpoint
++    value: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+   CeilometerInternalVip:
+     description: VIP for Ceilometer API internal endpoint
+     value: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
 diff --git a/puppet/all-nodes-config.yaml b/puppet/all-nodes-config.yaml
-index 2bc519b..98283c2 100644
+index 2bc519b..d649ba0 100644
 --- a/puppet/all-nodes-config.yaml
 +++ b/puppet/all-nodes-config.yaml
 @@ -8,6 +8,8 @@ parameters:
@@ -283,7 +500,16 @@ index 2bc519b..98283c2 100644
    block_storage_hosts:
      type: comma_delimited_list
    object_storage_hosts:
-@@ -82,6 +84,10 @@ resources:
+@@ -34,6 +36,8 @@ parameters:
+     type: comma_delimited_list
+   ceilometer_api_node_ips:
+     type: comma_delimited_list
++  aodh_api_node_ips:
++    type: comma_delimited_list
+   nova_api_node_ips:
+     type: comma_delimited_list
+   nova_metadata_node_ips:
+@@ -82,6 +86,10 @@ resources:
                raw_data: {get_file: hieradata/RedHat.yaml}
              all_nodes:
                mapped_data:
@@ -294,11 +520,34 @@ index 2bc519b..98283c2 100644
                  controller_node_ips:
                    list_join:
                    - ','
+@@ -166,6 +174,14 @@ resources:
+                         list_join:
+                         - "','"
+                         - {get_param: ceilometer_api_node_ips}
++                aodh_api_node_ips:
++                  str_replace:
++                    template: "['SERVERS_LIST']"
++                    params:
++                      SERVERS_LIST:
++                        list_join:
++                        - "','"
++                        - {get_param: aodh_api_node_ips}
+                 nova_api_node_ips:
+                   str_replace:
+                     template: "['SERVERS_LIST']"
+@@ -239,6 +255,7 @@ resources:
+                 neutron::rabbit_hosts: *rabbit_nodes_array
+                 nova::rabbit_hosts: *rabbit_nodes_array
+                 keystone::rabbit_hosts: *rabbit_nodes_array
++                aodh::rabbit_hosts: *rabbit_nodes_array
+ outputs:
+   config_id:
 diff --git a/puppet/compute.yaml b/puppet/compute.yaml
-index 70c7403..13fd4f6 100644
+index 70c7403..834cff9 100644
 --- a/puppet/compute.yaml
 +++ b/puppet/compute.yaml
-@@ -213,6 +213,23 @@ parameters:
+@@ -213,6 +213,27 @@ parameters:
    NtpServer:
      type: string
      default: ''
@@ -315,6 +564,10 @@ index 70c7403..13fd4f6 100644
 +    type: string
 +    description: The password for the opendaylight server.
 +    hidden: true
++  OpenDaylightFeatures:
++    description: List of features to install with ODL
++    type: comma_delimited_list
++    default: "odl-ovsdb-openstack"
 +  ONOSPort:
 +    default: 8181
 +    description: Set onos service port
@@ -322,7 +575,7 @@ index 70c7403..13fd4f6 100644
    RabbitHost:
      type: string
      default: ''  # Has to be here because of the ignored empty value bug
-@@ -320,6 +337,11 @@ resources:
+@@ -320,6 +341,11 @@ resources:
      properties:
        ControlPlaneIP: {get_attr: [NovaCompute, networks, ctlplane, 0]}
  
@@ -334,7 +587,7 @@ index 70c7403..13fd4f6 100644
    NetIpMap:
      type: OS::TripleO::Network::Ports::NetIpMap
      properties:
-@@ -327,6 +349,7 @@ resources:
+@@ -327,6 +353,7 @@ resources:
        InternalApiIp: {get_attr: [InternalApiPort, ip_address]}
        StorageIp: {get_attr: [StoragePort, ip_address]}
        TenantIp: {get_attr: [TenantPort, ip_address]}
@@ -342,7 +595,7 @@ index 70c7403..13fd4f6 100644
  
    NetworkConfig:
      type: OS::TripleO::Compute::Net::SoftwareConfig
-@@ -335,6 +358,7 @@ resources:
+@@ -335,6 +362,7 @@ resources:
        InternalApiIpSubnet: {get_attr: [InternalApiPort, ip_subnet]}
        StorageIpSubnet: {get_attr: [StoragePort, ip_subnet]}
        TenantIpSubnet: {get_attr: [TenantPort, ip_subnet]}
@@ -350,29 +603,31 @@ index 70c7403..13fd4f6 100644
  
    NetworkDeployment:
      type: OS::TripleO::SoftwareDeployment
-@@ -406,6 +430,10 @@ resources:
+@@ -406,6 +434,11 @@ resources:
                  neutron::rabbit_user: {get_input: rabbit_user}
                  neutron::rabbit_use_ssl: {get_input: rabbit_client_use_ssl}
                  neutron::rabbit_port: {get_input: rabbit_client_port}
 +                opendaylight_port: {get_input: opendaylight_port}
 +                opendaylight_username: {get_input: opendaylight_username}
 +                opendaylight_password: {get_input: opendaylight_password}
++                opendaylight_features: {get_input: opendaylight_features}
 +                onos_port: {get_input: onos_port}
                  neutron_flat_networks: {get_input: neutron_flat_networks}
                  neutron_host: {get_input: neutron_host}
                  neutron::agents::ml2::ovs::local_ip: {get_input: neutron_local_ip}
-@@ -459,6 +487,10 @@ resources:
+@@ -459,6 +492,11 @@ resources:
          snmpd_readonly_user_name: {get_param: SnmpdReadonlyUserName}
          snmpd_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
          glance_api_servers: {get_param: [EndpointMap, GlanceInternal, uri]}
 +        opendaylight_port: {get_param: OpenDaylightPort}
 +        opendaylight_username: {get_param: OpenDaylightUsername}
 +        opendaylight_password: {get_param: OpenDaylightPassword}
++        opendaylight_features: {get_param: OpenDaylightFeatures}
 +        onos_port: {get_param: ONOSPort}
          neutron_flat_networks: {get_param: NeutronFlatNetworks}
          neutron_host: {get_param: NeutronHost}
          neutron_local_ip: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronTenantNetwork]}]}
-@@ -570,6 +602,9 @@ outputs:
+@@ -570,6 +608,9 @@ outputs:
    tenant_ip_address:
      description: IP address of the server in the tenant network
      value: {get_attr: [TenantPort, ip_address]}
@@ -383,10 +638,25 @@ index 70c7403..13fd4f6 100644
      description: Hostname of the server
      value: {get_attr: [NovaCompute, name]}
 diff --git a/puppet/controller.yaml b/puppet/controller.yaml
-index ea0b3af..1d52922 100644
+index ea0b3af..bd82e93 100644
 --- a/puppet/controller.yaml
 +++ b/puppet/controller.yaml
-@@ -357,6 +357,10 @@ parameters:
+@@ -14,6 +14,14 @@ parameters:
+     description: The keystone auth secret and db password.
+     type: string
+     hidden: true
++  AodhApiVirtualIP:
++    type: string
++    default: ''
++  AodhPassword:
++    default: unset
++    description: The password for the aodh services.
++    type: string
++    hidden: true
+   CeilometerApiVirtualIP:
+     type: string
+     default: ''
+@@ -357,6 +365,10 @@ parameters:
      default: 'True'
      description: Allow automatic l3-agent failover
      type: string
@@ -397,7 +667,7 @@ index ea0b3af..1d52922 100644
    NeutronEnableTunnelling:
      type: string
      default: "True"
-@@ -443,6 +447,31 @@ parameters:
+@@ -443,6 +455,35 @@ parameters:
    NtpServer:
      type: string
      default: ''
@@ -422,6 +692,10 @@ index ea0b3af..1d52922 100644
 +    description: Knob to enable/disable ODL L3
 +    type: string
 +    default: 'no'
++  OpenDaylightFeatures:
++    description: List of features to install with ODL
++    type: comma_delimited_list
++    default: "odl-ovsdb-openstack"
 +  ONOSPort:
 +    default: 8181
 +    description: Set onos service port
@@ -429,7 +703,7 @@ index ea0b3af..1d52922 100644
    PcsdPassword:
      type: string
      description: The password for the 'pcsd' user.
-@@ -696,6 +725,7 @@ resources:
+@@ -696,6 +737,7 @@ resources:
        input_values:
          bootstack_nodeid: {get_attr: [Controller, name]}
          neutron_enable_tunneling: {get_param: NeutronEnableTunnelling}
@@ -437,7 +711,15 @@ index ea0b3af..1d52922 100644
          haproxy_log_address: {get_param: HAProxySyslogAddress}
          heat.watch_server_url:
            list_join:
-@@ -805,6 +835,12 @@ resources:
+@@ -774,6 +816,7 @@ resources:
+               - {get_param: MysqlVirtualIP}
+               - '/heat'
+         keystone_ca_certificate: {get_param: KeystoneCACertificate}
++        keystone_admin_vip: {get_param: KeystoneAdminApiVirtualIP}
+         keystone_signing_key: {get_param: KeystoneSigningKey}
+         keystone_signing_certificate: {get_param: KeystoneSigningCertificate}
+         keystone_ssl_certificate: {get_param: KeystoneSSLCertificate}
+@@ -805,6 +848,13 @@ resources:
              template: tripleo-CLUSTER
              params:
                CLUSTER: {get_param: MysqlClusterUniquePart}
@@ -446,11 +728,50 @@ index ea0b3af..1d52922 100644
 +        opendaylight_username: {get_param: OpenDaylightUsername}
 +        opendaylight_password: {get_param: OpenDaylightPassword}
 +        opendaylight_enable_l3: {get_param: OpenDaylightEnableL3}
++        opendaylight_features: {get_param: OpenDaylightFeatures}
 +        onos_port: {get_param: ONOSPort}
          neutron_flat_networks: {get_param: NeutronFlatNetworks}
          neutron_metadata_proxy_shared_secret: {get_param: NeutronMetadataProxySharedSecret}
          neutron_agent_mode: {get_param: NeutronAgentMode}
-@@ -1136,6 +1172,16 @@ resources:
+@@ -879,6 +929,7 @@ resources:
+         ceilometer_backend: {get_param: CeilometerBackend}
+         ceilometer_metering_secret: {get_param: CeilometerMeteringSecret}
+         ceilometer_password: {get_param: CeilometerPassword}
++        aodh_password: {get_param: AodhPassword}
+         ceilometer_coordination_url:
+           list_join:
+             - ''
+@@ -891,6 +942,12 @@ resources:
+             - - 'mysql://ceilometer:unset@'
+               - {get_param: MysqlVirtualIP}
+               - '/ceilometer'
++        ceilometer_public_url: {get_param: [EndpointMap, CeilometerPublic, uri]}
++        ceilometer_internal_url: {get_param: [EndpointMap, CeilometerInternal, uri]}
++        ceilometer_admin_url: {get_param: [EndpointMap, CeilometerAdmin, uri]}
++        aodh_public_url: {get_param: [EndpointMap, AodhPublic, uri]}
++        aodh_internal_url: {get_param: [EndpointMap, AodhInternal, uri]}
++        aodh_admin_url: {get_param: [EndpointMap, AodhAdmin, uri]}
+         snmpd_readonly_user_name: {get_param: SnmpdReadonlyUserName}
+         snmpd_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
+         nova_password: {get_param: NovaPassword}
+@@ -948,6 +1005,7 @@ resources:
+         neutron_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronApiNetwork]}]}
+         neutron_local_ip: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NeutronTenantNetwork]}]}
+         ceilometer_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, CeilometerApiNetwork]}]}
++        aodh_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, AodhApiNetwork]}]}
+         nova_api_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
+         nova_metadata_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, NovaMetadataNetwork]}]}
+         horizon_network: {get_attr: [NetIpMap, net_ip_map, {get_param: [ServiceNetMap, HorizonNetwork]}]}
+@@ -1041,7 +1099,7 @@ resources:
+                 cinder_iscsi_ip_address: {get_input: cinder_iscsi_network}
+                 cinder::database_connection: {get_input: cinder_dsn}
+                 cinder::api::keystone_password: {get_input: cinder_password}
+-                cinder::api::auth_uri: {get_input: keystone_auth_uri}
++                cinder::api::keystone_auth_host: {get_input: keystone_admin_vip}
+                 cinder::api::identity_uri: {get_input: keystone_identity_uri}
+                 cinder::api::bind_host: {get_input: cinder_api_network}
+                 cinder::rabbit_userid: {get_input: rabbit_username}
+@@ -1136,6 +1194,17 @@ resources:
                  mysql_bind_host: {get_input: mysql_network}
                  mysql_virtual_ip: {get_input: mysql_virtual_ip}
  
@@ -460,14 +781,15 @@ index ea0b3af..1d52922 100644
 +                opendaylight_username: {get_input: opendaylight_username}
 +                opendaylight_password: {get_input: opendaylight_password}
 +                opendaylight_enable_l3: {get_input: opendaylight_enable_l3}
-+                
++                opendaylight_features: {get_input: opendaylight_features}
++
 +                # ONOS
 +                onos_port: {get_input: onos_port}
 +
                  # Neutron
                  neutron::bind_host: {get_input: neutron_api_network}
                  neutron::rabbit_password: {get_input: rabbit_password}
-@@ -1152,6 +1198,7 @@ resources:
+@@ -1152,6 +1221,7 @@ resources:
                  neutron_flat_networks: {get_input: neutron_flat_networks}
                  neutron::agents::metadata::shared_secret: {get_input: neutron_metadata_proxy_shared_secret}
                  neutron::agents::metadata::metadata_ip: {get_input: neutron_api_network}
@@ -475,11 +797,88 @@ index ea0b3af..1d52922 100644
                  neutron_agent_mode: {get_input: neutron_agent_mode}
                  neutron_router_distributed: {get_input: neutron_router_distributed}
                  neutron::core_plugin: {get_input: neutron_core_plugin}
+@@ -1198,6 +1268,27 @@ resources:
+                 snmpd_readonly_user_name: {get_input: snmpd_readonly_user_name}
+                 snmpd_readonly_user_password: {get_input: snmpd_readonly_user_password}
++                # Aodh
++                aodh::rabbit_userid: {get_input: rabbit_username}
++                aodh::rabbit_password: {get_input: rabbit_password}
++                aodh::rabbit_use_ssl: {get_input: rabbit_client_use_ssl}
++                aodh::rabbit_port: {get_input: rabbit_client_port}
++                aodh::debug: {get_input: debug}
++                aodh::wsgi::apache::ssl: false
++                aodh::api::service_name: 'httpd'
++                aodh::api::host: {get_input: aodh_api_network}
++                aodh::api::keystone_password: {get_input: aodh_password}
++                aodh::api::keystone_auth_uri: {get_input: keystone_auth_uri}
++                aodh::api::keystone_identity_uri: {get_input: keystone_identity_uri}
++                aodh::auth::auth_password: {get_input: aodh_password}
++                aodh::keystone::auth::public_url: {get_input: aodh_public_url }
++                aodh::keystone::auth::internal_url: {get_input: aodh_internal_url }
++                aodh::keystone::auth::admin_url: {get_input: aodh_admin_url }
++                aodh::keystone::auth::password: {get_input: aodh_password }
++                aodh::keystone::auth::region: {get_input: keystone_region}
++                # for a migration path from ceilometer-alarm to aodh, we use the same database & coordination
++                aodh::evaluator::coordination_url: {get_input: ceilometer_coordination_url}
++
+                 # Nova
+                 nova::rabbit_userid: {get_input: rabbit_username}
+                 nova::rabbit_password: {get_input: rabbit_password}
+diff --git a/puppet/hieradata/common.yaml b/puppet/hieradata/common.yaml
+index 030f661..5840016 100644
+--- a/puppet/hieradata/common.yaml
++++ b/puppet/hieradata/common.yaml
+@@ -6,6 +6,7 @@ ceilometer::agent::auth::auth_region: 'regionOne'
+ # FIXME: Might be better to use 'service' tenant here but this requires
+ # changes in the tripleo-incubator keystone role setup
+ ceilometer::agent::auth::auth_tenant_name: 'admin'
++aodh::auth::auth_tenant_name: 'admin'
+ nova::network::neutron::neutron_admin_tenant_name: 'service'
+ nova::network::neutron::neutron_admin_username: 'neutron'
+diff --git a/puppet/hieradata/controller.yaml b/puppet/hieradata/controller.yaml
+index 4b7fd81..7dbc2e9 100644
+--- a/puppet/hieradata/controller.yaml
++++ b/puppet/hieradata/controller.yaml
+@@ -32,6 +32,7 @@ redis::sentinel::notification_script: '/usr/local/bin/redis-notifications.sh'
+ # service tenant
+ nova::api::admin_tenant_name: 'service'
+ glance::api::keystone_tenant: 'service'
++aodh::api::keystone_tenant: 'service'
+ glance::registry::keystone_tenant: 'service'
+ neutron::server::auth_tenant: 'service'
+ neutron::agents::metadata::auth_tenant: 'service'
+@@ -39,6 +40,7 @@ cinder::api::keystone_tenant: 'service'
+ swift::proxy::authtoken::admin_tenant_name: 'service'
+ ceilometer::api::keystone_tenant: 'service'
+ heat::keystone_tenant: 'service'
++aodh::keystone::auth::tenant: 'service'
+ # keystone
+ keystone::cron::token_flush::maxdelay: 3600
+@@ -72,7 +74,7 @@ neutron::agents::dhcp::dnsmasq_config_file: /etc/neutron/dnsmasq-neutron.conf
+ # nova
+ nova::notify_on_state_change: 'vm_and_task_state'
+-nova::api::default_floating_pool: 'public'
++nova::api::default_floating_pool: 'external'
+ nova::api::osapi_v3: true
+ nova::scheduler::filter::ram_allocation_ratio: '1.0'
+@@ -115,6 +117,7 @@ tripleo::loadbalancer::mysql: true
+ tripleo::loadbalancer::redis: true
+ tripleo::loadbalancer::swift_proxy_server: true
+ tripleo::loadbalancer::ceilometer: true
++tripleo::loadbalancer::aodh: true
+ tripleo::loadbalancer::heat_api: true
+ tripleo::loadbalancer::heat_cloudwatch: true
+ tripleo::loadbalancer::heat_cfn: true
 diff --git a/puppet/manifests/overcloud_compute.pp b/puppet/manifests/overcloud_compute.pp
-index cd41cc7..b8336ee 100644
+index cd41cc7..b6dc2fe 100644
 --- a/puppet/manifests/overcloud_compute.pp
 +++ b/puppet/manifests/overcloud_compute.pp
-@@ -75,9 +75,36 @@ class { '::neutron::plugins::ml2':
+@@ -75,9 +75,50 @@ class { '::neutron::plugins::ml2':
    tenant_network_types => [hiera('neutron_tenant_network_type')],
  }
  
@@ -495,14 +894,28 @@ index cd41cc7..b8336ee 100644
 +    $opendaylight_controller_ip = hiera('opendaylight_controller_ip')
 +  }
 +
-+  if str2bool(hiera('opendaylight_install', 'false')) {
-+    class { 'neutron::plugins::ovs::opendaylight':
++  # co-existence hacks for SFC
++  if hiera('opendaylight_features', 'odl-ovsdb-openstack') =~ /odl-ovsdb-sfc-rest/ {
++    $opendaylight_port = hiera('opendaylight_port')
++    $odl_username = hiera('opendaylight_username')
++    $odl_password = hiera('opendaylight_password')
++    $sfc_coexist_url = "http://${opendaylight_controller_ip}:${opendaylight_port}/restconf/config/sfc-of-renderer:sfc-of-renderer-config"
++    # Coexist for SFC
++    exec { 'Check SFC table offset has been set':
++      command   => "curl --fail --silent -u ${odl_username}:${odl_password} ${sfc_coexist_url} | grep :11 > /dev/null",
++      tries     => 15,
++      try_sleep => 60,
++      path      => '/usr/sbin:/usr/bin:/sbin:/bin',
++      before    => Class['neutron::plugins::ovs::opendaylight'],
++    }
++  }
++
++  class { 'neutron::plugins::ovs::opendaylight':
 +      odl_controller_ip => $opendaylight_controller_ip,
 +      tunnel_ip         => hiera('neutron::agents::ml2::ovs::local_ip'),
 +      odl_port          => hiera('opendaylight_port'),
 +      odl_username      => hiera('opendaylight_username'),
 +      odl_password      => hiera('opendaylight_password'),
-+    }
 +  }
 +
 +} elsif 'onos_ml2' in hiera('neutron_mechanism_drivers') {
@@ -520,7 +933,7 @@ index cd41cc7..b8336ee 100644
  
  if 'cisco_n1kv' in hiera('neutron_mechanism_drivers') {
 diff --git a/puppet/manifests/overcloud_controller.pp b/puppet/manifests/overcloud_controller.pp
-index 1f6c2be..7851b45 100644
+index 1f6c2be..67a9f04 100644
 --- a/puppet/manifests/overcloud_controller.pp
 +++ b/puppet/manifests/overcloud_controller.pp
 @@ -30,6 +30,21 @@ if hiera('step') >= 1 {
@@ -529,7 +942,7 @@ index 1f6c2be..7851b45 100644
  
 +  if str2bool(hiera('opendaylight_install', 'false')) {
 +    class {"opendaylight":
-+      extra_features => ['odl-ovsdb-openstack'],
++      extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
 +      odl_rest_port  => hiera('opendaylight_port'),
 +      enable_l3      => hiera('opendaylight_enable_l3', 'no'),
 +    }
@@ -545,7 +958,17 @@ index 1f6c2be..7851b45 100644
    if count(hiera('ntp::servers')) > 0 {
      include ::ntp
    }
-@@ -223,9 +238,7 @@ if hiera('step') >= 3 {
+@@ -158,6 +173,9 @@ if hiera('step') >= 2 {
+ if hiera('step') >= 3 {
++  # Apache
++  include ::apache
++
+   include ::keystone
+   #TODO: need a cleanup-keystone-tokens.sh solution here
+@@ -223,9 +241,7 @@ if hiera('step') >= 3 {
    include ::nova::scheduler
    include ::nova::scheduler::filter
  
@@ -555,7 +978,7 @@ index 1f6c2be..7851b45 100644
    include ::neutron::agents::dhcp
    include ::neutron::agents::metadata
  
-@@ -237,15 +250,76 @@ if hiera('step') >= 3 {
+@@ -237,15 +253,99 @@ if hiera('step') >= 3 {
      require => Package['neutron'],
    }
  
@@ -566,10 +989,8 @@ index 1f6c2be..7851b45 100644
 +    }
 +  } else {
 +    include ::neutron
-+    if 'opendaylight' in hiera('neutron_mechanism_drivers') {
-+      if ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
-+        include ::neutron::agents::l3
-+      }
++    if ! ('opendaylight' in hiera('neutron_mechanism_drivers')) or ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
++      include ::neutron::agents::l3
 +    }
 +  }
 +  
@@ -586,6 +1007,7 @@ index 1f6c2be..7851b45 100644
 +    if ! str2bool(hiera('opendaylight_enable_l3', 'no')) {
 +      Service['neutron-server'] -> Service['neutron-l3']
 +    }
++
 +    if str2bool(hiera('opendaylight_install', 'false')) {
 +      $controller_ips = split(hiera('controller_node_ips'), ',')
 +      $opendaylight_controller_ip = $controller_ips[0]
@@ -593,6 +1015,30 @@ index 1f6c2be..7851b45 100644
 +      $opendaylight_controller_ip = hiera('opendaylight_controller_ip')
 +    }
 +
++    # co-existence hacks for SFC
++    if hiera('opendaylight_features', 'odl-ovsdb-openstack') =~ /odl-ovsdb-sfc-rest/ {
++      $opendaylight_port = hiera('opendaylight_port')
++      $netvirt_coexist_url = "http://${opendaylight_controller_ip}:${opendaylight_port}/restconf/config/netvirt-providers-config:netvirt-providers-config"
++      $netvirt_post_body = "{'netvirt-providers-config': {'table-offset': 1}}"
++      $sfc_coexist_url = "http://${opendaylight_controller_ip}:${opendaylight_port}/restconf/config/sfc-of-renderer:sfc-of-renderer-config"
++      $sfc_post_body = "{ 'sfc-of-renderer-config' : { 'sfc-of-table-offset' : 150, 'sfc-of-app-egress-table-offset' : 11 }}"
++      $odl_username = hiera('opendaylight_username')
++      $odl_password = hiera('opendaylight_password')
++      exec { 'Coexistence table offsets for netvirt':
++        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",
++        tries     => 5,
++        try_sleep => 30,
++        path      => '/usr/sbin:/usr/bin:/sbin:/bin',
++      } ->
++      # Coexist for SFC
++      exec { 'Coexistence table offsets for sfc':
++        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",
++        tries     => 5,
++        try_sleep => 30,
++        path      => '/usr/sbin:/usr/bin:/sbin:/bin',
++      }
++    }
++
 +    class { 'neutron::plugins::ml2::opendaylight':
 +      odl_controller_ip => $opendaylight_controller_ip,
 +      odl_username      => hiera('opendaylight_username'),
@@ -635,7 +1081,7 @@ index 1f6c2be..7851b45 100644
    if 'cisco_n1kv' in hiera('neutron_mechanism_drivers') {
      include ::neutron::plugins::ml2::cisco::nexus1000v
  
-@@ -280,8 +354,6 @@ if hiera('step') >= 3 {
+@@ -280,8 +380,6 @@ if hiera('step') >= 3 {
    }
  
    Service['neutron-server'] -> Service['neutron-dhcp-service']
@@ -644,8 +1090,29 @@ index 1f6c2be..7851b45 100644
    Service['neutron-server'] -> Service['neutron-metadata']
  
    include ::cinder
+@@ -447,6 +545,20 @@ if hiera('step') >= 3 {
+   Cron <| title == 'ceilometer-expirer' |> { command => "sleep $((\$(od -A n -t d -N 3 /dev/urandom) % 86400)) && ${::ceilometer::params::expirer_command}" }
++  # Aodh
++  include ::aodh::auth
++  include ::aodh::api
++  include ::aodh::evaluator
++  include ::aodh::notifier
++  include ::aodh::listener
++  include ::aodh::client
++  include ::aodh::db::sync
++  class { '::aodh' :
++    database_connection => $ceilometer_database_connection,
++  }
++  # To manage the upgrade:
++  Exec['ceilometer-dbsync'] -> Exec['aodh-db-sync']
++
+   # Heat
+   include ::heat
+   include ::heat::api
 diff --git a/puppet/manifests/overcloud_controller_pacemaker.pp b/puppet/manifests/overcloud_controller_pacemaker.pp
-index 3fb92f3..9568390 100644
+index 3fb92f3..cb00e9a 100644
 --- a/puppet/manifests/overcloud_controller_pacemaker.pp
 +++ b/puppet/manifests/overcloud_controller_pacemaker.pp
 @@ -380,6 +380,21 @@ if hiera('step') >= 2 {
@@ -654,7 +1121,7 @@ index 3fb92f3..9568390 100644
  
 +  if str2bool(hiera('opendaylight_install', 'false')) {
 +    class {"opendaylight":
-+      extra_features => ['odl-ovsdb-openstack'],
++      extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
 +      odl_rest_port  => hiera('opendaylight_port'),
 +      enable_l3      => hiera('opendaylight_enable_l3', 'no'),
 +    }
@@ -697,10 +1164,22 @@ index 3fb92f3..9568390 100644
    class { '::neutron::agents::metadata':
      manage_service => false,
      enabled        => false,
-@@ -609,18 +627,68 @@ if hiera('step') >= 3 {
+@@ -609,18 +627,80 @@ if hiera('step') >= 3 {
      notify  => Service['neutron-dhcp-service'],
      require => Package['neutron'],
    }
++
++  # SDNVPN Hack
++  if ('networking_bgpvpn.neutron.services.plugin.BGPVPNPlugin' in hiera('neutron::service_plugins'))
++  {
++    class  { 'neutron::config':
++      server_config => {
++        'service_providers/service_provider' => {
++          'value' => 'BGPVPN:Dummy:networking_bgpvpn.neutron.services.service_drivers.driver_api.BGPVPNDriver:default'
++        }
++      }
++    }
++  }
 +
    class { '::neutron::plugins::ml2':
      flat_networks        => split(hiera('neutron_flat_networks'), ','),
@@ -730,7 +1209,7 @@ index 3fb92f3..9568390 100644
 +      odl_password      => hiera('opendaylight_password'),
 +      odl_port          => hiera('opendaylight_port'),
 +    }
++
 +    if str2bool(hiera('opendaylight_install', 'false')) {
 +      class { 'neutron::plugins::ovs::opendaylight':
 +        odl_controller_ip => $opendaylight_controller_ip,
@@ -750,7 +1229,7 @@ index 3fb92f3..9568390 100644
 +    #config ml2_conf.ini with onos url address
 +    $onos_port = hiera('onos_port')
 +    $private_ip = hiera('neutron::agents::ml2::ovs::local_ip')
-+
 +    neutron_plugin_ml2 {
 +      'onos/username':         value => 'admin';
 +      'onos/password':         value => 'admin';
@@ -772,7 +1251,7 @@ index 3fb92f3..9568390 100644
    if 'cisco_ucsm' in hiera('neutron_mechanism_drivers') {
      include ::neutron::plugins::ml2::cisco::ucsm
    }
-@@ -645,8 +713,10 @@ if hiera('step') >= 3 {
+@@ -645,8 +725,10 @@ if hiera('step') >= 3 {
    if hiera('neutron_enable_bigswitch_ml2', false) {
      include ::neutron::plugins::ml2::bigswitch::restproxy
    }
@@ -785,7 +1264,85 @@ index 3fb92f3..9568390 100644
    }
    neutron_dhcp_agent_config {
      'DEFAULT/ovs_use_veth': value => hiera('neutron_ovs_use_veth', false);
-@@ -1055,62 +1125,21 @@ if hiera('step') >= 4 {
+@@ -813,13 +895,13 @@ if hiera('step') >= 3 {
+     swift::storage::filter::healthcheck { $swift_components : }
+   }
++  $mongo_node_string = join($mongo_node_ips_with_port, ',')
+   # Ceilometer
+   case downcase(hiera('ceilometer_backend')) {
+     /mysql/: {
+       $ceilometer_database_connection = hiera('ceilometer_mysql_conn_string')
+     }
+     default: {
+-      $mongo_node_string = join($mongo_node_ips_with_port, ',')
+       $ceilometer_database_connection = "mongodb://${mongo_node_string}/ceilometer?replicaSet=${mongodb_replset}"
+     }
+   }
+@@ -879,6 +961,62 @@ if hiera('step') >= 3 {
+     enabled        => false,
+   }
++  $aodh_database_connection = "mongodb://${mongo_node_string}/aodh?replicaSet=${mongodb_replset}"
++
++  class { '::aodh::db':
++    database_connection => $aodh_database_connection
++  }
++
++  # Aodh
++  include ::aodh
++  include ::aodh::config
++  include ::aodh::auth
++  include ::aodh::client
++  class { '::aodh::api':
++    manage_service => false,
++    enabled        => false,
++  }
++  class { '::aodh::evaluator':
++    manage_service => false,
++    enabled        => false,
++  }
++  class { '::aodh::notifier':
++    manage_service => false,
++    enabled        => false,
++  }
++  class { '::aodh::listener':
++    manage_service => false,
++    enabled        => false,
++  }
++
++  $event_pipeline = "---
++sources:
++    - name: event_source
++      events:
++          - \"*\"
++      sinks:
++          - event_sink
++sinks:
++    - name: event_sink
++      transformers:
++      triggers:
++      publishers:
++          - notifier://?topic=alarm.all
++          - notifier://
++"
++
++  # aodh hacks
++  file { '/etc/ceilometer/event_pipeline.yaml':
++    ensure  => present,
++    content => $event_pipeline
++  }
++
++  user { 'aodh':
++     groups => 'nobody'
++  }
++
++
++
+   # httpd/apache and horizon
+   # NOTE(gfidente): server-status can be consumed by the pacemaker resource agent
+   class { '::apache' :
+@@ -1055,62 +1193,21 @@ if hiera('step') >= 4 {
        clone_params => 'interleave=true',
        require      => Pacemaker::Resource::Service[$::keystone::params::service_name],
      }
@@ -852,7 +1409,7 @@ index 3fb92f3..9568390 100644
      pacemaker::constraint::base { 'keystone-to-neutron-server-constraint':
        constraint_type => 'order',
        first_resource  => "${::keystone::params::service_name}-clone",
-@@ -1120,65 +1149,110 @@ if hiera('step') >= 4 {
+@@ -1120,65 +1217,110 @@ if hiera('step') >= 4 {
        require         => [Pacemaker::Resource::Service[$::keystone::params::service_name],
                            Pacemaker::Resource::Service[$::neutron::params::server_service]],
      }
@@ -1019,9 +1576,147 @@ index 3fb92f3..9568390 100644
      # Nova
      pacemaker::resource::service { $::nova::params::api_service_name :
        clone_params => 'interleave=true',
+@@ -1276,7 +1418,7 @@ if hiera('step') >= 4 {
+                   Pacemaker::Resource::Service[$::nova::params::conductor_service_name]],
+     }
+-    # Ceilometer
++    # Ceilometer and Aodh
+     case downcase(hiera('ceilometer_backend')) {
+       /mysql/: {
+         pacemaker::resource::service { $::ceilometer::params::agent_central_service_name :
+@@ -1298,10 +1440,19 @@ if hiera('step') >= 4 {
+     pacemaker::resource::service { $::ceilometer::params::api_service_name :
+       clone_params => 'interleave=true',
+     }
+-    pacemaker::resource::service { $::ceilometer::params::alarm_evaluator_service_name :
++    pacemaker::resource::service { $::aodh::params::notifier_service_name :
+       clone_params => 'interleave=true',
+     }
+-    pacemaker::resource::service { $::ceilometer::params::alarm_notifier_service_name :
++    pacemaker::resource::service { $::aodh::params::expirer_package_serice :
++      clone_params => 'interleave=true',
++    }
++    pacemaker::resource::service { $::aodh::params::listener_service_name :
++      clone_params => 'interleave=true',
++    }
++    pacemaker::resource::service { $::aodh::params::api_service_name :
++      clone_params => 'interleave=true',
++    }
++    pacemaker::resource::service { $::aodh::params::evaluator_service_name :
+       clone_params => 'interleave=true',
+     }
+     pacemaker::resource::service { $::ceilometer::params::agent_notification_service_name :
+@@ -1315,8 +1466,19 @@ if hiera('step') >= 4 {
+     # Fedora doesn't know `require-all` parameter for constraints yet
+     if $::operatingsystem == 'Fedora' {
+       $redis_ceilometer_constraint_params = undef
++      $redis_aodh_constraint_params = undef
+     } else {
+       $redis_ceilometer_constraint_params = 'require-all=false'
++      $redis_aodh_constraint_params = 'require-all=false'
++    }
++    pacemaker::constraint::base { 'keystone-then-aodh-api-constraint':
++      constraint_type => 'order',
++      first_resource  => "${::keystone::params::service_name}-clone",
++      second_resource => "${::aodh::params::api_service_name}-clone",
++      first_action    => 'start',
++      second_action   => 'start',
++      require         => [Pacemaker::Resource::Service[$::aodh::params::api_service_name],
++                          Pacemaker::Resource::Service[$::keystone::params::service_name]],
+     }
+     pacemaker::constraint::base { 'redis-then-ceilometer-central-constraint':
+       constraint_type   => 'order',
+@@ -1328,6 +1490,16 @@ if hiera('step') >= 4 {
+       require           => [Pacemaker::Resource::Ocf['redis'],
+                             Pacemaker::Resource::Service[$::ceilometer::params::agent_central_service_name]],
+     }
++    pacemaker::constraint::base { 'redis-then-aodh-evaluator-constraint':
++      constraint_type   => 'order',
++      first_resource    => 'redis-master',
++      second_resource   => "${::aodh::params::evaluator_service_name}-clone",
++      first_action      => 'promote',
++      second_action     => 'start',
++      constraint_params => $redis_aodh_constraint_params,
++      require           => [Pacemaker::Resource::Ocf['redis'],
++                            Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name]],
++    }
+     pacemaker::constraint::base { 'keystone-then-ceilometer-central-constraint':
+       constraint_type => 'order',
+       first_resource  => "${::keystone::params::service_name}-clone",
+@@ -1378,53 +1550,37 @@ if hiera('step') >= 4 {
+       require => [Pacemaker::Resource::Service[$::ceilometer::params::api_service_name],
+                   Pacemaker::Resource::Ocf['delay']],
+     }
+-    pacemaker::constraint::base { 'ceilometer-delay-then-ceilometer-alarm-evaluator-constraint':
++    pacemaker::constraint::base { 'aodh-delay-then-aodh-evaluator-constraint':
+       constraint_type => 'order',
+       first_resource  => 'delay-clone',
+-      second_resource => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
++      second_resource => "${::aodh::params::evaluator_service_name}-clone",
+       first_action    => 'start',
+       second_action   => 'start',
+-      require         => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
++      require         => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
+                           Pacemaker::Resource::Ocf['delay']],
+     }
+-    pacemaker::constraint::colocation { 'ceilometer-alarm-evaluator-with-ceilometer-delay-colocation':
+-      source  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
++    pacemaker::constraint::colocation { 'aodh-evaluator-with-aodh-delay-colocation':
++      source  => "${::aodh::params::evaluator_service_name}-clone",
+       target  => 'delay-clone',
+       score   => 'INFINITY',
+-      require => [Pacemaker::Resource::Service[$::ceilometer::params::api_service_name],
++      require => [Pacemaker::Resource::Service[$::horizon::params::http_service],
+                   Pacemaker::Resource::Ocf['delay']],
+     }
+-    pacemaker::constraint::base { 'ceilometer-alarm-evaluator-then-ceilometer-alarm-notifier-constraint':
+-      constraint_type => 'order',
+-      first_resource  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
+-      second_resource => "${::ceilometer::params::alarm_notifier_service_name}-clone",
+-      first_action    => 'start',
+-      second_action   => 'start',
+-      require         => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
+-                          Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
+-    }
+-    pacemaker::constraint::colocation { 'ceilometer-alarm-notifier-with-ceilometer-alarm-evaluator-colocation':
+-      source  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
+-      target  => "${::ceilometer::params::alarm_evaluator_service_name}-clone",
+-      score   => 'INFINITY',
+-      require => [Pacemaker::Resource::Service[$::ceilometer::params::alarm_evaluator_service_name],
+-                  Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
+-    }
+-    pacemaker::constraint::base { 'ceilometer-alarm-notifier-then-ceilometer-notification-constraint':
++    pacemaker::constraint::base { 'aodh-evaluator-then-aodh-notifier-constraint':
+       constraint_type => 'order',
+-      first_resource  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
+-      second_resource => "${::ceilometer::params::agent_notification_service_name}-clone",
++      first_resource  => "${::aodh::params::evaluator_service_name}-clone",
++      second_resource => "${::aodh::params::notifier_service_name}-clone",
+       first_action    => 'start',
+       second_action   => 'start',
+-      require         => [Pacemaker::Resource::Service[$::ceilometer::params::agent_notification_service_name],
+-                          Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
++      require         => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
++                          Pacemaker::Resource::Service[$::aodh::params::notifier_service_name]],
+     }
+-    pacemaker::constraint::colocation { 'ceilometer-notification-with-ceilometer-alarm-notifier-colocation':
+-      source  => "${::ceilometer::params::agent_notification_service_name}-clone",
+-      target  => "${::ceilometer::params::alarm_notifier_service_name}-clone",
++    pacemaker::constraint::colocation { 'aodh-notifier-with-aodh-evaluator-colocation':
++      source  => "${::aodh::params::notifier_service_name}-clone",
++      target  => "${::aodh::params::evaluator_service_name}-clone",
+       score   => 'INFINITY',
+-      require => [Pacemaker::Resource::Service[$::ceilometer::params::agent_notification_service_name],
+-                  Pacemaker::Resource::Service[$::ceilometer::params::alarm_notifier_service_name]],
++      require => [Pacemaker::Resource::Service[$::aodh::params::evaluator_service_name],
++                  Pacemaker::Resource::Service[$::aodh::params::notifier_service_name]],
+     }
+     if downcase(hiera('ceilometer_backend')) == 'mongodb' {
+       pacemaker::constraint::base { 'mongodb-then-ceilometer-central-constraint':
 diff --git a/puppet/manifests/overcloud_opendaylight.pp b/puppet/manifests/overcloud_opendaylight.pp
 new file mode 100644
-index 0000000..33f609a
+index 0000000..aeb31be
 --- /dev/null
 +++ b/puppet/manifests/overcloud_opendaylight.pp
 @@ -0,0 +1,27 @@
@@ -1047,17 +1742,17 @@ index 0000000..33f609a
 +}
 +
 +class {"opendaylight":
-+  extra_features => ['odl-ovsdb-openstack'],
++  extra_features => any2array(hiera('opendaylight_features', 'odl-ovsdb-openstack')),
 +  odl_rest_port  => hiera('opendaylight_port'),
 +  enable_l3      => hiera('opendaylight_enable_l3', 'no'),
 +}
 +
 diff --git a/puppet/opendaylight-puppet.yaml b/puppet/opendaylight-puppet.yaml
 new file mode 100644
-index 0000000..b876dc7
+index 0000000..6488e0e
 --- /dev/null
 +++ b/puppet/opendaylight-puppet.yaml
-@@ -0,0 +1,217 @@
+@@ -0,0 +1,223 @@
 +heat_template_version: 2015-04-30
 +
 +description: >
@@ -1089,6 +1784,10 @@ index 0000000..b876dc7
 +    description: Knob to enable/disable ODL L3
 +    type: string
 +    default: 'no'
++  OpenDaylightFeatures:
++    description: List of features to install with ODL
++    type: comma_delimited_list
++    default: "odl-ovsdb-openstack"
 +  OpenDaylightPort:
 +    default: 8081
 +    description: Set OpenDaylight service port
@@ -1190,6 +1889,7 @@ index 0000000..b876dc7
 +        opendaylight_enable_l3: {get_param: OpenDaylightEnableL3}
 +        opendaylight_username: {get_param: OpenDaylightUsername}
 +        opendaylight_password: {get_param: OpenDaylightPassword}
++        opendaylight_features: {get_param: OpenDaylightFeatures}
 +
 +  OpenDaylightConfig:
 +    type: OS::Heat::StructuredConfig
@@ -1215,6 +1915,7 @@ index 0000000..b876dc7
 +                opendaylight::admin_password: {get_param: OpenDaylightPassword}
 +                opendaylight_port: {get_input: opendaylight_port}
 +                opendaylight_enable_l3: {get_input: opendaylight_enable_l3}
++                opendaylight_features: {get_input: opendaylight_features}
 +            ceph:
 +              raw_data: {get_file: hieradata/ceph.yaml}
 +