Merge pull request #1 from salt-formulas/known-fixups

Cleanup + extend tests/pillars
diff --git a/.gitignore b/.gitignore
index 1bfce6e..aa8e42a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
+.kitchen
 tests/build/
 *.swp
 *.pyc
-.ropeproject
\ No newline at end of file
+.ropeproject
diff --git a/.gitreview b/.gitreview
deleted file mode 100644
index 7b183fe..0000000
--- a/.gitreview
+++ /dev/null
@@ -1,4 +0,0 @@
-[gerrit]
-host=review.openstack.org
-port=29418
-project=openstack/salt-formula-neutron.git
diff --git a/Makefile b/Makefile
index fc83783..1043fbe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,38 @@
 DESTDIR=/
 SALTENVDIR=/usr/share/salt-formulas/env
 RECLASSDIR=/usr/share/salt-formulas/reclass
-FORMULANAME=$(shell grep name: metadata.yml|head -1|cut -d : -f 2|grep -Eo '[a-z0-9\-]*')
+FORMULANAME=$(shell grep name: metadata.yml|head -1|cut -d : -f 2|grep -Eo '[a-z0-9\-\_]*')
+VERSION=$(shell grep version: metadata.yml|head -1|cut -d : -f 2|grep -Eo '[a-z0-9\.\-\_]*')
+VERSION_MAJOR := $(shell echo $(VERSION)|cut -d . -f 1-2)
+VERSION_MINOR := $(shell echo $(VERSION)|cut -d . -f 3)
+
+NEW_MAJOR_VERSION ?= $(shell date +%Y.%m|sed 's,\.0,\.,g')
+NEW_MINOR_VERSION ?= $(shell /bin/bash -c 'echo $$[ $(VERSION_MINOR) + 1 ]')
+
+MAKE_PID := $(shell echo $$PPID)
+JOB_FLAG := $(filter -j%, $(subst -j ,-j,$(shell ps T | grep "^\s*$(MAKE_PID).*$(MAKE)")))
+
+ifneq ($(subst -j,,$(JOB_FLAG)),)
+JOBS := $(subst -j,,$(JOB_FLAG))
+else
+JOBS := 1
+endif
+
+KITCHEN_LOCAL_YAML?=.kitchen.yml
+KITCHEN_OPTS?="--concurrency=$(JOBS)"
+KITCHEN_OPTS_CREATE?=""
+KITCHEN_OPTS_CONVERGE?=""
+KITCHEN_OPTS_VERIFY?=""
+KITCHEN_OPTS_TEST?=""
 
 all:
 	@echo "make install - Install into DESTDIR"
 	@echo "make test    - Run tests"
+	@echo "make kitchen - Run Kitchen CI tests (create, converge, verify)"
 	@echo "make clean   - Cleanup after tests run"
+	@echo "make release-major  - Generate new major release"
+	@echo "make release-minor  - Generate new minor release"
+	@echo "make changelog      - Show changes since last release"
 
 install:
 	# Formula
@@ -14,6 +40,7 @@
 	cp -a $(FORMULANAME) $(DESTDIR)/$(SALTENVDIR)/
 	[ ! -d _modules ] || cp -a _modules $(DESTDIR)/$(SALTENVDIR)/
 	[ ! -d _states ] || cp -a _states $(DESTDIR)/$(SALTENVDIR)/ || true
+	[ ! -d _grains ] || cp -a _grains $(DESTDIR)/$(SALTENVDIR)/ || true
 	# Metadata
 	[ -d $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME) ] || mkdir -p $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
 	cp -a metadata/service/* $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
@@ -21,6 +48,71 @@
 test:
 	[ ! -d tests ] || (cd tests; ./run_tests.sh)
 
+release-major: check-changes
+	@echo "Current version is $(VERSION), new version is $(NEW_MAJOR_VERSION)"
+	@[ $(VERSION_MAJOR) != $(NEW_MAJOR_VERSION) ] || (echo "Major version $(NEW_MAJOR_VERSION) already released, nothing to do. Do you want release-minor?" && exit 1)
+	echo "$(NEW_MAJOR_VERSION)" > VERSION
+	sed -i 's,version: .*,version: "$(NEW_MAJOR_VERSION)",g' metadata.yml
+	[ ! -f debian/changelog ] || dch -v $(NEW_MAJOR_VERSION) -m --force-distribution -D `dpkg-parsechangelog -S Distribution` "New version"
+	make genchangelog-$(NEW_MAJOR_VERSION)
+	(git add -u; git commit -m "Version $(NEW_MAJOR_VERSION)")
+	git tag -s -m $(NEW_MAJOR_VERSION) $(NEW_MAJOR_VERSION)
+
+release-minor: check-changes
+	@echo "Current version is $(VERSION), new version is $(VERSION_MAJOR).$(NEW_MINOR_VERSION)"
+	echo "$(VERSION_MAJOR).$(NEW_MINOR_VERSION)" > VERSION
+	sed -i 's,version: .*,version: "$(VERSION_MAJOR).$(NEW_MINOR_VERSION)",g' metadata.yml
+	[ ! -f debian/changelog ] || dch -v $(VERSION_MAJOR).$(NEW_MINOR_VERSION) -m --force-distribution -D `dpkg-parsechangelog -S Distribution` "New version"
+	make genchangelog-$(VERSION_MAJOR).$(NEW_MINOR_VERSION)
+	(git add -u; git commit -m "Version $(VERSION_MAJOR).$(NEW_MINOR_VERSION)")
+	git tag -s -m $(NEW_MAJOR_VERSION) $(VERSION_MAJOR).$(NEW_MINOR_VERSION)
+
+check-changes:
+	@git log --pretty=oneline --decorate $(VERSION)..HEAD | grep -Eqc '.*' || (echo "No new changes since version $(VERSION)"; exit 1)
+
+changelog:
+	git log --pretty=short --invert-grep --grep="Merge pull request" --decorate $(VERSION)..HEAD
+
+genchangelog: genchangelog-$(VERSION_MAJOR).$(NEW_MINOR_VERSION)
+
+genchangelog-%:
+	$(eval NEW_VERSION := $(patsubst genchangelog-%,%,$@))
+	(echo "=========\nChangelog\n=========\n"; \
+	(echo $(NEW_VERSION);git tag) | sort -r | grep -E '^[0-9\.]+' | while read i; do \
+	    cur=$$i; \
+	    test $$i = $(NEW_VERSION) && i=HEAD; \
+	    prev=`(echo $(NEW_VERSION);git tag)|sort|grep -E '^[0-9\.]+'|grep -B1 "$$cur\$$"|head -1`; \
+	    echo "Version $$cur\n=============================\n"; \
+	    git log --pretty=short --invert-grep --grep="Merge pull request" --decorate $$prev..$$i; \
+	    echo; \
+	done) > CHANGELOG.rst
+
+kitchen-check:
+	@[ -e $(KITCHEN_LOCAL_YAML) ] || (echo "Kitchen tests not available, there's no $(KITCHEN_LOCAL_YAML)." && exit 1)
+
+kitchen: kitchen-check kitchen-create kitchen-converge kitchen-verify kitchen-list
+
+kitchen-create: kitchen-check
+	kitchen create ${KITCHEN_OPTS} ${KITCHEN_OPTS_CREATE}
+	[ "$(shell echo $(KITCHEN_LOCAL_YAML)|grep -Eo docker)" = "docker" ] || sleep 120
+
+kitchen-converge: kitchen-check
+	kitchen converge ${KITCHEN_OPTS} ${KITCHEN_OPTS_CONVERGE} &&\
+	kitchen converge ${KITCHEN_OPTS} ${KITCHEN_OPTS_CONVERGE}
+
+kitchen-verify: kitchen-check
+	[ ! -d tests/integration ] || kitchen verify -t tests/integration ${KITCHEN_OPTS} ${KITCHEN_OPTS_VERIFY}
+	[ -d tests/integration ]   || kitchen verify ${KITCHEN_OPTS} ${KITCHEN_OPTS_VERIFY}
+
+kitchen-test: kitchen-check
+	[ ! -d tests/integration ] || kitchen test -t tests/integration ${KITCHEN_OPTS} ${KITCHEN_OPTS_TEST}
+	[ -d tests/integration ]   || kitchen test ${KITCHEN_OPTS} ${KITCHEN_OPTS_TEST}
+
+kitchen-list: kitchen-check
+	kitchen list
+
 clean:
+	[ ! -x "$(shell which kitchen)" ] || kitchen destroy
+	[ ! -d .kitchen ] || rm -rf .kitchen
 	[ ! -d tests/build ] || rm -rf tests/build
 	[ ! -d build ] || rm -rf build
diff --git a/README.rst b/README.rst
index 084c423..b1a3047 100644
--- a/README.rst
+++ b/README.rst
@@ -21,6 +21,8 @@
       server:
         enabled: true
         version: mitaka
+        allow_pagination: true
+        pagination_max_limit: 100
         bind:
           address: 172.20.0.1
           port: 9696
@@ -52,6 +54,11 @@
         audit:
           enabled: false
 
+Note: The pagination is useful to retrieve a large bunch of resources,
+because a single request may fail (timeout). This is enabled with both
+parameters *allow_pagination* and *pagination_max_limit* as shown above.
+
+
 Neutron VXLAN tenant networks with Network Nodes (with DVR for East-West
  and Network node for North-South)
 =========================================================================
@@ -589,6 +596,107 @@
       ....
 
 
+Client role
+-----------
+
+Neutron networks
+
+.. code-block:: yaml
+
+    neutron:
+      client:
+        enabled: true
+        server:
+          identity:
+            network:
+              inet1:
+                tenant: demo
+                shared: False
+                admin_state_up: True
+                router_external: True
+                provider_physical_network: inet
+                provider_network_type: flat
+                provider_segmentation_id: 2
+                subnet:
+                  inet1-subnet1:
+                    cidr: 192.168.90.0/24
+                    enable_dhcp: False
+              inet2:
+                tenant: admin
+                shared: False
+                router_external: True
+                provider_network_type: "vlan"
+                subnet:
+                  inet2-subnet1:
+                    cidr: 192.168.92.0/24
+                    enable_dhcp: False
+                  inet2-subnet2:
+                    cidr: 192.168.94.0/24
+                    enable_dhcp: True
+          identity1:
+            network:
+              ...
+
+
+Neutron routers
+
+.. code-block:: yaml
+
+    neutron:
+      client:
+        enabled: true
+        server:
+          identity:
+            router:
+              inet1-router:
+                tenant: demo
+                admin_state_up: True
+                gateway_network: inet
+                interfaces:
+                  - inet1-subnet1
+                  - inet1-subnet2
+          identity1:
+            router:
+              ...
+
+    TODO: implement adding new interfaces to a router while updating it
+
+
+Neutron security groups
+
+.. code-block:: yaml
+
+    neutron:
+      client:
+        enabled: true
+        server:
+          identity:
+            security_group:
+              security_group1:
+                tenant: demo
+                description: security group 1
+                rules:
+                  - direction: ingress
+                    ethertype: IPv4
+                    protocol: TCP
+                    port_range_min: 1
+                    port_range_max: 65535
+                    remote_ip_prefix: 0.0.0.0/0
+                  - direction: ingress
+                    ethertype: IPv4
+                    protocol: UDP
+                    port_range_min: 1
+                    port_range_max: 65535
+                    remote_ip_prefix: 0.0.0.0/0
+                  - direction: ingress
+                    protocol: ICMP
+                    remote_ip_prefix: 0.0.0.0/0
+          identity1:
+            security_group:
+              ...
+
+    TODO: implement updating existing security rules (now it adds new rule if trying to update existing one)
+
 Usage
 =====
 
@@ -623,3 +731,36 @@
 Developers should also join the discussion on the IRC list, at:
 
     https://wiki.openstack.org/wiki/Meetings/openstack-salt
+
+Documentation and Bugs
+======================
+
+To learn how to install and update salt-formulas, consult the documentation
+available online at:
+
+    http://salt-formulas.readthedocs.io/
+
+In the unfortunate event that bugs are discovered, they should be reported to
+the appropriate issue tracker. Use Github issue tracker for specific salt
+formula:
+
+    https://github.com/salt-formulas/salt-formula-neutron/issues
+
+For feature requests, bug reports or blueprints affecting entire ecosystem,
+use Launchpad salt-formulas project:
+
+    https://launchpad.net/salt-formulas
+
+You can also join salt-formulas-users team and subscribe to mailing list:
+
+    https://launchpad.net/~salt-formulas-users
+
+Developers wishing to work on the salt-formulas projects should always base
+their work on master branch and submit pull request against specific formula.
+
+    https://github.com/salt-formulas/salt-formula-neutron
+
+Any questions or feedback is always welcome so feel free to join our IRC
+channel:
+
+    #salt-formulas @ irc.freenode.net
diff --git a/metadata/service/compute/single.yml b/metadata/service/compute/single.yml
index d86284d..e1196b2 100644
--- a/metadata/service/compute/single.yml
+++ b/metadata/service/compute/single.yml
@@ -1,5 +1,7 @@
 applications:
 - neutron
+classes:
+- service.neutron.support
 parameters:
   neutron:
     compute:
@@ -24,5 +26,3 @@
         mechanism:
           ovs:
             driver: openvswitch
-
-
diff --git a/metadata/service/gateway/single.yml b/metadata/service/gateway/single.yml
index 1e8fa2b..4ff30d1 100644
--- a/metadata/service/gateway/single.yml
+++ b/metadata/service/gateway/single.yml
@@ -1,5 +1,7 @@
 applications:
 - neutron
+classes:
+- service.neutron.support
 parameters:
   neutron:
     gateway:
diff --git a/neutron/_modules/neutronng.py b/neutron/_modules/neutronng.py
new file mode 100644
index 0000000..55a5acd
--- /dev/null
+++ b/neutron/_modules/neutronng.py
@@ -0,0 +1,420 @@
+# -*- coding: utf-8 -*-
+
+import logging
+from functools import wraps
+LOG = logging.getLogger(__name__)
+
+# Import third party libs
+HAS_NEUTRON = False
+try:
+    from neutronclient.v2_0 import client
+    HAS_NEUTRON = True
+except ImportError:
+    pass
+
+__opts__ = {}
+
+
+def __virtual__():
+    '''
+    Only load this module if neutron
+    is installed on this minion.
+    '''
+    if HAS_NEUTRON:
+        return 'neutronng'
+    return False
+
+
+def _autheticate(func_name):
+    '''
+    Authenticate requests with the salt keystone module and format return data
+    '''
+    @wraps(func_name)
+    def decorator_method(*args, **kwargs):
+        '''
+        Authenticate request and format return data
+        '''
+        connection_args = {'profile': kwargs.get('profile', None)}
+        nkwargs = {}
+        for kwarg in kwargs:
+            if 'connection_' in kwarg:
+                connection_args.update({kwarg: kwargs[kwarg]})
+            elif '__' not in kwarg:
+                nkwargs.update({kwarg: kwargs[kwarg]})
+        kstone = __salt__['keystone.auth'](**connection_args)
+        token = kstone.auth_token
+        endpoint = kstone.service_catalog.url_for(
+            service_type='network',
+            endpoint_type='publicURL')
+        neutron_interface = client.Client(
+            endpoint_url=endpoint, token=token)
+        return_data = func_name(neutron_interface, *args, **nkwargs)
+        if isinstance(return_data, list):
+            # format list as a dict for rendering
+            return {data.get('name', None) or data['id']: data
+                    for data in return_data}
+        return return_data
+    return decorator_method
+
+
+@_autheticate
+def list_floatingips(neutron_interface, **kwargs):
+    '''
+    list all floatingips
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_floatingips
+    '''
+    return neutron_interface.list_floatingips(**kwargs)['floatingips']
+
+
+@_autheticate
+def list_security_groups(neutron_interface, **kwargs):
+    '''
+    list all security_groups
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_security_groups
+    '''
+    return neutron_interface.list_security_groups(**kwargs)['security_groups']
+
+
+@_autheticate
+def list_subnets(neutron_interface, **kwargs):
+    '''
+    list all subnets
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_subnets
+    '''
+    return neutron_interface.list_subnets(**kwargs)['subnets']
+
+
+@_autheticate
+def list_networks(neutron_interface, **kwargs):
+    '''
+    list all networks
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_networks
+    '''
+    return neutron_interface.list_networks(**kwargs)['networks']
+
+
+@_autheticate
+def list_ports(neutron_interface, **kwargs):
+    '''
+    list all ports
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_ports
+    '''
+    return neutron_interface.list_ports(**kwargs)['ports']
+
+
+@_autheticate
+def list_routers(neutron_interface, **kwargs):
+    '''
+    list all routers
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.list_routers
+    '''
+    return neutron_interface.list_routers(**kwargs)['routers']
+
+@_autheticate
+def update_floatingip(neutron_interface, fip, port_id=None):
+    '''
+    update floating IP. Should be used to associate and disassociate
+    floating IP with instance
+    CLI Example:
+    .. code-block:: bash
+        to associate with an instance's port
+        salt '*' neutron.update_floatingip openstack-floatingip-id port-id
+        to disassociate from an instance's port
+        salt '*' neutron.update_floatingip openstack-floatingip-id
+    '''
+    neutron_interface.update_floatingip(fip, {"floatingip":
+                                              {"port_id": port_id}})
+
+
+@_autheticate
+def update_subnet(neutron_interface, subnet_id, **subnet_params):
+    '''
+    update given subnet
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_subnet openstack-subnet-id name='new_name'
+    '''
+    neutron_interface.update_subnet(subnet_id, {'subnet': subnet_params})
+
+
+@_autheticate
+def update_network(neutron_interface, network_id, **net_params):
+    '''
+    Update give network
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_network openstack-net-id admin_state_up=false
+    '''
+    network_params = {}
+    for param in net_params:
+        if 'provider_' in param or 'router_' in param:
+            network_params[param.replace('_', ':', 1)] = net_params[param]
+        else:
+            network_params[param] = net_params[param]
+    LOG.info('ATTRIBUTES ' + str(network_params))
+    neutron_interface.update_network(network_id, {'network': network_params})
+
+
+@_autheticate
+def update_router(neutron_interface, router_id, **router_params):
+    '''
+    update given router
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_router openstack-router-id name='new_name'
+            external_gateway='openstack-network-id' administrative_state=true
+    '''
+    neutron_interface.update_router(router_id, {'router': router_params})
+
+
+@_autheticate
+def router_gateway_set(neutron_interface, router_id, external_gateway):
+    '''
+    Set external gateway for a router
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_router openstack-router-id openstack-network-id
+    '''
+    neutron_interface.update_router(
+        router_id, {'router': {'external_gateway_info':
+                               {'network_id': external_gateway}}})
+
+
+@_autheticate
+def router_gateway_clear(neutron_interface, router_id):
+    '''
+    Clear external gateway for a router
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_router openstack-router-id
+    '''
+    neutron_interface.update_router(
+        router_id, {'router': {'external_gateway_info': None}})
+
+
+@_autheticate
+def create_router(neutron_interface, **router_params):
+    '''
+    Create OpenStack Neutron router
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_router name=R1
+    '''
+    response = neutron_interface.create_router({'router': router_params})
+    if 'router' in response and 'id' in response['router']:
+        return response['router']['id']
+
+
+@_autheticate
+def router_add_interface(neutron_interface, router_id, subnet_id):
+    '''
+    Attach router to a subnet
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.router_add_interface openstack-router-id subnet-id
+    '''
+    neutron_interface.add_interface_router(router_id, {'subnet_id': subnet_id})
+
+
+@_autheticate
+def router_rem_interface(neutron_interface, router_id, subnet_id):
+    '''
+    Dettach router from a subnet
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.router_rem_interface openstack-router-id subnet-id
+    '''
+    neutron_interface.remove_interface_router(
+        router_id, {'subnet_id': subnet_id})
+
+
+@_autheticate
+def create_security_group(neutron_interface, **sg_params):
+    '''
+    Create a new security group
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_security_group name='new_rule'
+            description='test rule'
+    '''
+    response = neutron_interface.create_security_group(
+        {'security_group': sg_params})
+    if 'security_group' in response and 'id' in response['security_group']:
+        return response['security_group']['id']
+
+
+@_autheticate
+def create_security_group_rule(neutron_interface, **rule_params):
+    '''
+    Create a rule entry for a security group
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_security_group_rule
+    '''
+    neutron_interface.create_security_group_rule(
+        {'security_group_rule': rule_params})
+
+
+@_autheticate
+def create_floatingip(neutron_interface, **floatingip_params):
+    '''
+    Create a new floating IP
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_floatingip floating_network_id=ext-net-id
+    '''
+    response = neutron_interface.create_floatingip(
+        {'floatingip': floatingip_params})
+    if 'floatingip' in response and 'id' in response['floatingip']:
+        return response['floatingip']['id']
+
+
+@_autheticate
+def create_subnet(neutron_interface, **subnet_params):
+    '''
+    Create a new subnet in OpenStack
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_subnet name='subnet name'
+            network_id='openstack-network-id' cidr='192.168.10.0/24' \\
+            gateway_ip='192.168.10.1' ip_version='4' enable_dhcp=false \\
+            start_ip='192.168.10.10' end_ip='192.168.10.20'
+    '''
+    if 'start_ip' in subnet_params:
+        subnet_params.update(
+            {'allocation_pools': [{'start': subnet_params.pop('start_ip'),
+                                   'end': subnet_params.pop('end_ip', None)}]})
+    response = neutron_interface.create_subnet({'subnet': subnet_params})
+    if 'subnet' in response and 'id' in response['subnet']:
+        return response['subnet']['id']
+
+
+@_autheticate
+def create_network(neutron_interface, **net_params):
+    '''
+    Create a new network segment in OpenStack
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_network name=External
+            provider_network_type=flat provider_physical_network=ext
+    '''
+    network_params = {}
+    for param in net_params:
+        if 'provider_' in param or 'router_' in param:
+            network_params[param.replace('_', ':', 1)] = net_params[param]
+        else:
+            network_params[param] = net_params[param]
+    response = neutron_interface.create_network({'network': network_params})
+    if 'network' in response and 'id' in response['network']:
+        return response['network']['id']
+
+
+@_autheticate
+def create_port(neutron_interface, **port_params):
+    '''
+    Create a new port in OpenStack
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.create_port network_id='openstack-network-id'
+    '''
+    response = neutron_interface.create_port({'port': port_params})
+    if 'port' in response and 'id' in response['port']:
+        return response['port']['id']
+
+
+@_autheticate
+def update_port(neutron_interface, port_id, **port_params):
+    '''
+    Create a new port in OpenStack
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.update_port name='new_port_name'
+    '''
+    neutron_interface.update_port(port_id, {'port': port_params})
+
+
+@_autheticate
+def delete_floatingip(neutron_interface, floating_ip_id):
+    '''
+    delete a floating IP
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_floatingip openstack-floating-ip-id
+    '''
+    neutron_interface.delete_floatingip(floating_ip_id)
+
+
+@_autheticate
+def delete_security_group(neutron_interface, sg_id):
+    '''
+    delete a security group
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_security_group openstack-security-group-id
+    '''
+    neutron_interface.delete_security_group(sg_id)
+
+
+@_autheticate
+def delete_security_group_rule(neutron_interface, rule):
+    '''
+    delete a security group rule. pass all rule params that match the rule
+    to be deleted
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_security_group_rule direction='ingress'
+            ethertype='ipv4' security_group_id='openstack-security-group-id'
+            port_range_min=100 port_range_max=4096 protocol='tcp'
+            remote_group_id='default'
+    '''
+    sg_rules = neutron_interface.list_security_group_rules(
+        security_group_id=rule['security_group_id'])
+    for sg_rule in sg_rules['security_group_rules']:
+        sgr_id = sg_rule.pop('id')
+        if sg_rule == rule:
+            neutron_interface.delete_security_group_rule(sgr_id)
+
+
+@_autheticate
+def delete_subnet(neutron_interface, subnet_id):
+    '''
+    delete given subnet
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_subnet openstack-subnet-id
+    '''
+    neutron_interface.delete_subnet(subnet_id)
+
+
+@_autheticate
+def delete_network(neutron_interface, network_id):
+    '''
+    delete given network
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_network openstack-network-id
+    '''
+    neutron_interface.delete_network(network_id)
+
+
+@_autheticate
+def delete_router(neutron_interface, router_id):
+    '''
+    delete given router
+    CLI Example:
+    .. code-block:: bash
+        salt '*' neutron.delete_router openstack-router-id
+    '''
+    neutron_interface.delete_router(router_id)
\ No newline at end of file
diff --git a/neutron/_states/neutronng.py b/neutron/_states/neutronng.py
new file mode 100644
index 0000000..b7c9195
--- /dev/null
+++ b/neutron/_states/neutronng.py
@@ -0,0 +1,522 @@
+# -*- coding: utf-8 -*-
+'''
+Management of Neutron resources
+===============================
+:depends:   - neutronclient Python module
+:configuration: See :py:mod:`salt.modules.neutron` for setup instructions.
+.. code-block:: yaml
+    neutron network present:
+      neutron.network_present:
+        - name: Netone
+        - provider_physical_network: PHysnet1
+        - provider_network_type: vlan
+'''
+import logging
+from functools import wraps
+LOG = logging.getLogger(__name__)
+
+
+def __virtual__():
+    '''
+    Only load if neutron module is present in __salt__
+    '''
+    return 'neutronng' if 'neutron.list_networks' in __salt__ else False
+
+
+def _test_call(method):
+    (resource, functionality) = method.func_name.split('_')
+    if functionality == 'present':
+        functionality = 'updated'
+    else:
+        functionality = 'removed'
+
+    @wraps(method)
+    def check_for_testing(name, *args, **kwargs):
+        if __opts__.get('test', None):
+            return _no_change(name, resource, test=functionality)
+        return method(name, *args, **kwargs)
+    return check_for_testing
+
+
+def _neutron_module_call(method, *args, **kwargs):
+    return __salt__['neutronng.{0}'.format(method)](*args, **kwargs)
+
+
+def _auth(profile=None):
+    '''
+    Set up neutron credentials
+    '''
+    if profile:
+        credentials = __salt__['config.option'](profile)
+        user = credentials['keystone.user']
+        password = credentials['keystone.password']
+        tenant = credentials['keystone.tenant']
+        auth_url = credentials['keystone.auth_url']
+
+    kwargs = {
+        'connection_user': user,
+        'connection_password': password,
+        'connection_tenant': tenant,
+        'connection_auth_url': auth_url
+    }
+
+    return kwargs
+
+@_test_call
+def network_present(name=None,
+                    tenant=None,
+                    provider_network_type=None,
+                    provider_physical_network=None,
+                    router_external=None,
+                    admin_state_up=None,
+                    shared=None,
+                    provider_segmentation_id=None,
+                    profile=None):
+    '''
+    Ensure that the neutron network is present with the specified properties.
+    name
+        The name of the network to manage
+    '''
+    tenant_name = tenant
+    connection_args = _auth(profile)
+    try:
+        tenant_id = __salt__['keystone.tenant_get'](
+            name=tenant_name, **connection_args)[tenant_name]['id']
+    except:
+        tenant_id = None
+        LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
+            connection_args['connection_user']))
+    existing_network = _neutron_module_call(
+        'list_networks', name=name, **connection_args)
+    network_arguments = _get_non_null_args(
+        name=name,
+        provider_network_type=provider_network_type,
+        provider_physical_network=provider_physical_network,
+        router_external=router_external,
+        admin_state_up=admin_state_up,
+        shared=shared,
+        tenant_id=tenant_id,
+        provider_segmentation_id=provider_segmentation_id)
+
+    if not existing_network:
+        network_arguments.update(connection_args)
+        _neutron_module_call('create_network', **network_arguments)
+        existing_network = _neutron_module_call(
+            'list_networks', name=name, **connection_args)
+        if existing_network:
+            return _created(name, 'network', existing_network[name])
+        return _update_failed(name, 'network')
+
+    LOG.info('CONNECTION STRINGS' + str(connection_args))
+    LOG.info('existing ' + str(existing_network))
+    LOG.info('new ' + str(network_arguments))
+    existing_network = dict((key.replace(':', '_', 1), value)
+                            for key, value in
+                            existing_network[name].iteritems())
+    # generate differential
+    diff = dict((key, value) for key, value in network_arguments.iteritems()
+                if existing_network.get(key, None) != value)
+    if diff:
+        # update the changes
+        network_arguments = diff.copy()
+        network_arguments.update(connection_args)
+        try:
+            LOG.debug('updating network {0} with changes {1}'.format(
+                name, str(diff)))
+            _neutron_module_call('update_network',
+                                 existing_network['id'],
+                                 **network_arguments)
+            changes_dict = _created(name, 'network', diff)
+            changes_dict['comment'] = '{1} {0} updated'.format(name, 'network')
+            return changes_dict
+        except:
+            LOG.exception('Could not update network {0}'.format(name))
+            return _update_failed(name, 'network')
+    return _no_change(name, 'network')
+
+
+@_test_call
+def network_absent(name, profile=None):
+    connection_args = _auth(profile)
+    existing_network = _neutron_module_call(
+        'list_networks', name=name, **connection_args)
+    if existing_network:
+        _neutron_module_call(
+            'delete_network', existing_network[name]['id'], **connection_args)
+        if _neutron_module_call('list_networks', name=name, **connection_args):
+            return _delete_failed(name, 'network')
+        return _deleted(name, 'network', existing_network[name])
+    return _absent(name, 'network')
+
+
+@_test_call
+def subnet_present(name=None,
+                   tenant=None,
+                   network=None,
+                   cidr=None,
+                   ip_version=4,
+                   enable_dhcp=True,
+                   allocation_pools=None,
+                   gateway_ip=None,
+                   dns_nameservers=None,
+                   host_routes=None,
+                   profile=None):
+    '''
+    Ensure that the neutron subnet is present with the specified properties.
+    name
+        The name of the subnet to manage
+    '''
+    connection_args = _auth(profile)
+    tenant_name = tenant
+    try:
+        tenant_id = __salt__['keystone.tenant_get'](
+            name=tenant_name, **connection_args)[tenant_name]['id']
+    except:
+        tenant_id = None
+        LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
+            connection_args['connection_user']))
+    existing_subnet = _neutron_module_call(
+        'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
+    subnet_arguments = _get_non_null_args(
+        name=name,
+        network=network,
+        cidr=cidr,
+        ip_version=ip_version,
+        enable_dhcp=enable_dhcp,
+        allocation_pools=allocation_pools,
+        gateway_ip=gateway_ip,
+        dns_nameservers=dns_nameservers,
+        host_routes=host_routes)
+    # replace network with network_id
+    if 'network' in subnet_arguments:
+        network = subnet_arguments.pop('network', None)
+        existing_network = _neutron_module_call(
+            'list_networks', tenant_id=tenant_id, name=network, **connection_args)
+        if existing_network:
+            subnet_arguments['network_id'] = existing_network[network]['id']
+    if not existing_subnet:
+        subnet_arguments.update(connection_args)
+        _neutron_module_call('create_subnet', tenant_id=tenant_id, **subnet_arguments)
+        existing_subnet = _neutron_module_call(
+            'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
+        if existing_subnet:
+            return _created(name, 'subnet', existing_subnet[name])
+        return _update_failed(name, 'subnet')
+    # change from internal representation
+    existing_subnet = existing_subnet[name]
+    # create differential
+    LOG.error('existing ' + str(existing_subnet))
+    LOG.error('new ' + str(subnet_arguments))
+    diff = dict((key, value) for key, value in subnet_arguments.iteritems()
+                if existing_subnet.get(key, None) != value)
+    if diff:
+        # update the changes
+        subnet_arguments = diff.copy()
+        subnet_arguments.update(connection_args)
+        try:
+            LOG.debug('updating subnet {0} with changes {1}'.format(
+                name, str(diff)))
+            _neutron_module_call('update_subnet',
+                                 existing_subnet['id'],
+                                 **subnet_arguments)
+            changes_dict = _created(name, 'subnet', diff)
+            changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet')
+            return changes_dict
+        except:
+            LOG.exception('Could not update subnet {0}'.format(name))
+            return _update_failed(name, 'subnet')
+    return _no_change(name, 'subnet')
+
+
+@_test_call
+def subnet_absent(name, profile=None):
+    connection_args = _auth(profile)
+    existing_subnet = _neutron_module_call(
+        'list_subnets', tenant_id=tenant_id, name=name, **connection_args)
+    if existing_subnet:
+        _neutron_module_call(
+            'delete_subnet', existing_subnet[name]['id'], **connection_args)
+        if _neutron_module_call('list_subnets', name=name, **connection_args):
+            return _delete_failed(name, 'subnet')
+        return _deleted(name, 'subnet', existing_subnet[name])
+    return _absent(name, 'subnet')
+    return _absent(name, 'network')
+
+
+@_test_call
+def router_present(name=None,
+                   tenant=None,
+                   gateway_network=None,
+                   interfaces=None,
+                   admin_state_up=True,
+                   profile=None):
+    '''
+    Ensure that the neutron router is present with the specified properties.
+    name
+        The name of the subnet to manage
+    gateway_network
+        The network that would be the router's default gateway
+    interfaces
+        list of subnets the router attaches to
+    '''
+    connection_args = _auth(profile)
+    tenant_name = tenant
+    try:
+        tenant_id = __salt__['keystone.tenant_get'](
+            name=tenant_name, **connection_args)[tenant_name]['id']
+    except:
+        tenant_id = None
+        LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
+            connection_args['connection_user']))
+    existing_router = _neutron_module_call(
+        'list_routers', name=name, **connection_args)
+    if not existing_router:
+        _neutron_module_call('create_router', name=name, tenant_id=tenant_id, admin_state_up=admin_state_up, **connection_args)
+        created_router = _neutron_module_call(
+            'list_routers', name=name, **connection_args)
+        if created_router:
+            router_id = created_router[name]['id']
+            network = _neutron_module_call(
+                'list_networks', name=gateway_network, **connection_args)
+            gateway_network_id = network[gateway_network]['id']
+            _neutron_module_call('router_gateway_set',
+                                 router_id=router_id,
+                                 external_gateway=gateway_network_id,
+                                 **connection_args)
+            for interface in interfaces:
+                subnet = _neutron_module_call(
+                    'list_subnets', name=interface, **connection_args)
+                subnet_id = subnet[interface]['id']
+                _neutron_module_call('router_add_interface',
+                                     router_id=router_id,
+                                     subnet_id=subnet_id,
+                                     **connection_args)
+            return _created(name,
+                            'router',
+                            _neutron_module_call('list_routers',
+                                                 name=name,
+                                                 **connection_args))
+        return _create_failed(name, 'router')
+
+    router_id = existing_router[name]['id']
+    existing_router = existing_router[name]
+    diff = {}
+    if ( admin_state_up == True or admin_state_up == False ) and existing_router['admin_state_up'] != admin_state_up:
+        diff.update({'admin_state_up': admin_state_up})
+    if gateway_network:
+        network = _neutron_module_call(
+            'list_networks', name=gateway_network, **connection_args)
+        gateway_network_id = network[gateway_network]['id']
+        if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None:
+            if existing_router['external_gateway_info']['network_id'] != gateway_network_id:
+                diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
+        elif not existing_router['external_gateway_info'] == None:
+            if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id:
+                diff.update({'external_gateway_info': {'network_id': gateway_network_id}})
+    if diff:
+        # update the changes
+        router_args = diff.copy()
+        router_args.update(connection_args)
+        try:
+            _neutron_module_call('update_router', existing_router['id'], **router_args)
+            changes_dict = _created(name, 'router', diff)
+            changes_dict['comment'] = 'Router {0} updated'.format(name)
+            return changes_dict
+        except:
+            LOG.exception('Router {0} could not be updated'.format(name))
+            return _update_failed(name, 'router')
+    return _no_change(name, 'router')
+
+def security_group_present(name=None,
+                           tenant=None,
+                           description=None,
+                           rules=[],
+                           profile=None):
+    '''
+    Ensure that the security group is present with the specified properties.
+    name
+        The name of the security group
+    description
+        The description of the security group
+    rules
+        list of rules to be added to the given security group
+    '''
+    # If the user is an admin, he's able to see the security groups from
+    # other tenants. In this case, we'll use the tenant id to get an existing
+    # security group.
+    connection_args = _auth(profile)
+    tenant_name = tenant
+    try:
+        tenant_id = __salt__['keystone.tenant_get'](
+            name=tenant_name, **connection_args)[tenant_name]['id']
+    except:
+        tenant_id = None
+        LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format(
+            connection_args['connection_user']))
+    if tenant_id:
+        security_group = _neutron_module_call(
+            'list_security_groups', name=name, tenant_id=tenant_id,
+            **connection_args)
+    else:
+        security_group = _neutron_module_call(
+            'list_security_groups', name=name, **connection_args)
+
+    if not security_group:
+        # Create the security group as it doesn't exist already.
+        security_group_id = _neutron_module_call('create_security_group',
+                                                 name=name,
+                                                 description=description,
+                                                 tenant_id=tenant_id,
+                                                 **connection_args)
+    else:
+        security_group_id = security_group[name]['id']
+
+    # Set the missing rules attributes (in case the user didn't specify them
+    # in pillar) to some default values.
+    rules_attributes_defaults = {
+        'direction': 'ingress',
+        'ethertype': 'IPv4',
+        'protocol': 'TCP',
+        'port_range_min': None,
+        'port_range_max': None,
+        'remote_ip_prefix': None
+    }
+    for rule in rules:
+        for attribute in rules_attributes_defaults.keys():
+            if not rule.has_key(attribute):
+                rule[attribute] = rules_attributes_defaults[attribute]
+
+    # Remove all the duplicates rules given by the user in pillar.
+    unique_rules = []
+    for rule in rules:
+        if rule not in unique_rules:
+            unique_rules.append(rule)
+
+    # Get the existing security group rules.
+    existing_rules = _neutron_module_call(
+        'list_security_groups',
+        id=security_group_id,
+        **connection_args)[name]['security_group_rules']
+
+    new_rules = {}
+    for rule in unique_rules:
+        rule_found = False
+        for existing_rule in existing_rules:
+            attributes_match = True
+            # Compare the attributes of the existing security group rule with
+            # the attributes of the rule that we want to add.
+            for attribute in rules_attributes_defaults.keys():
+                existing_attribute = '' if not existing_rule[attribute] \
+                                        else str(existing_rule[attribute]).lower()
+                attribute = '' if not rule[attribute] \
+                               else str(rule[attribute]).lower()
+                if existing_attribute != attribute:
+                    attributes_match = False
+                    break
+            if attributes_match:
+                rule_found = True
+                break
+        if rule_found:
+            # Skip adding the rule as it already exists.
+            continue
+        rule_index = len(new_rules) + 1
+        new_rules.update({'Rule {0}'.format(rule_index): rule})
+        _neutron_module_call('create_security_group_rule',
+                             security_group_id=security_group_id,
+                             direction=rule['direction'],
+                             ethertype=rule['ethertype'],
+                             protocol=rule['protocol'],
+                             port_range_min=rule['port_range_min'],
+                             port_range_max=rule['port_range_max'],
+                             remote_ip_prefix=rule['remote_ip_prefix'],
+                             tenant_id=tenant_id,
+                             **connection_args)
+
+    if not security_group:
+        # The security group didn't exist. It was created and specified
+        # rules were added to it.
+        security_group = _neutron_module_call('list_security_groups',
+                                              id=security_group_id,
+                                              **connection_args)[name]
+        return _created(name, 'security_group', security_group)
+    if len(new_rules) == 0:
+        # Security group already exists and specified rules are already
+        # present.
+        return _no_change(name, 'security_group')
+    # Security group already exists, but the specified rules were added to it.
+    return _updated(name, 'security_group', {'New Rules': new_rules})
+
+def _created(name, resource, resource_definition):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': True,
+                    'comment': '{0} {1} created'.format(resource, name)}
+    return changes_dict
+
+def _updated(name, resource, resource_definition):
+    changes_dict = {'name': name,
+                    'changes': resource_definition,
+                    'result': True,
+                    'comment': '{0} {1} updated'.format(resource, name)}
+    return changes_dict
+
+def _no_change(name, resource, test=False):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'result': True}
+    if test:
+        changes_dict['comment'] = \
+            '{0} {1} will be {2}'.format(resource, name, test)
+    else:
+        changes_dict['comment'] = \
+            '{0} {1} is in correct state'.format(resource, name)
+    return changes_dict
+
+
+def _deleted(name, resource, resource_definition):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} removed'.format(resource, name),
+                    'result': True}
+    return changes_dict
+
+
+def _absent(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} not present'.format(resource, name),
+                    'result': True}
+    return changes_dict
+
+
+def _delete_failed(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} failed to delete'.format(resource,
+                                                                 name),
+                    'result': False}
+    return changes_dict
+
+def _create_failed(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} failed to create'.format(resource,
+                                                                 name),
+                    'result': False}
+    return changes_dict
+
+def _update_failed(name, resource):
+    changes_dict = {'name': name,
+                    'changes': {},
+                    'comment': '{0} {1} failed to update'.format(resource,
+                                                                 name),
+                    'result': False}
+    return changes_dict
+
+
+def _get_non_null_args(**kwargs):
+    '''
+    Return those kwargs which are not null
+    '''
+    return dict((key, value,) for key, value in kwargs.iteritems()
+                if value is not None)
\ No newline at end of file
diff --git a/neutron/client.sls b/neutron/client.sls
new file mode 100644
index 0000000..865186e
--- /dev/null
+++ b/neutron/client.sls
@@ -0,0 +1,96 @@
+{%- from "neutron/map.jinja" import client with context %}
+{%- if client.enabled %}
+
+neutron_client_packages:
+  pkg.installed:
+  - names: {{ client.pkgs }}
+
+
+{%- for identity_name, identity in client.server.iteritems() %}
+
+{%- for network_name, network in identity.network.iteritems() %}
+
+neutron_openstack_network_{{ network_name }}:
+  neutronng.network_present:
+    - name: {{ network_name }}
+    - profile: {{ identity_name }}
+    - tenant: {{ network.tenant }}
+
+    {%- if network.provider_network_type is defined %}
+    - provider_network_type: {{ network.provider_network_type }}
+    {%- endif %}
+    {%- if network.provider_physical_network is defined %}
+    - provider_physical_network: {{ network.provider_physical_network }}
+    {%- endif %}
+    {%- if network.router_external is defined %}
+    - router_external: {{ network.router_external }}
+    {%- endif %}
+    {%- if network.admin_state_up is defined %}
+    - admin_state_up: {{ network.admin_state_up }}
+    {%- endif %}
+    {%- if network.shared is defined %}
+    - shared: {{ network.shared }}
+    {%- endif %}
+    {%- if network.provider_segmentation_id is defined %}
+    - provider_segmentation_id: {{ network.provider_segmentation_id }}
+    {%- endif %}
+
+{%- for subnet_name, subnet in network.subnet.iteritems() %}
+neutron_openstack_subnet_{{ subnet_name }}:
+  neutronng.subnet_present:
+    - name: {{ subnet_name }}
+    - network: {{ network_name }}
+    - profile: {{ identity_name }}
+    - tenant: {{ network.tenant }}
+
+    {%- if subnet.cidr is defined %}
+    - cidr: {{ subnet.cidr  }}
+    {%- endif %}
+    {%- if subnet.ip_version is defined %}
+    - ip_version: {{ subnet.ip_version }}
+    {%- endif %}
+    {%- if subnet.enable_dhcp is defined %}
+    - enable_dhcp: {{ subnet.enable_dhcp }}
+    {%- endif %}
+    {%- if subnet.allocation_pools is defined %}
+    - allocation_pools: {{ subnet.allocation_pools }}
+    {%- endif %}
+    {%- if subnet.gateway_ip is defined %}
+    - gateway_ip: {{ subnet.gateway_ip }}
+    {%- endif %}
+    {%- if subnet.dns_nameservers is defined %}
+    - dns_nameservers: {{ subnet.dns_nameservers }}
+    {%- endif %}
+    {%- if subnet.host_routes is defined %}
+    - host_routes: {{ subnet.host_routes }}
+    {%- endif %}
+    - require:
+      - neutronng: neutron_openstack_network_{{ network_name }}
+
+{%- endfor %}
+{%- endfor %}
+
+{%- for router_name, router in identity.router.iteritems() %}
+neutron_openstack_router_{{ router_name }}:
+  neutronng.router_present:
+    - name: {{ router_name }}
+    - interfaces: {{ router.interfaces }}
+    - gateway_network: {{ router.gateway_network }}
+    - profile: {{ identity_name }}
+    - tenant: {{ router.tenant }}
+    - admin_state_up: {{ router.admin_state_up }}
+{%- endfor %}
+
+{%- for security_group_name, security_group in identity.security_group.iteritems() %}
+openstack_security_group_{{ security_group_name }}:
+  neutronng.security_group_present:
+    - name: {{ security_group_name }}
+    - description: {{ security_group.description }}
+    - rules: {{ security_group.rules }}
+    - profile: {{ identity_name }}
+    - tenant: {{ security_group.tenant }}
+{%- endfor %}
+
+{%- endfor %}
+
+{%- endif %}
diff --git a/neutron/files/collectd_openstack_neutron.conf b/neutron/files/collectd_openstack_neutron.conf
index 8593cdf..43bbf02 100644
--- a/neutron/files/collectd_openstack_neutron.conf
+++ b/neutron/files/collectd_openstack_neutron.conf
@@ -5,6 +5,8 @@
     Username "{{ plugin.username }}"
     Password "{{ plugin.password }}"
     Tenant "{{ plugin.tenant }}"
-    MaxRetries "2"
-    Timeout "20"
+    MaxRetries "{{ plugin.max_retries|default(2) }}"
+    Timeout "{{ plugin.timeout|default(20) }}"
+    PaginationLimit "{{ plugin.pagination_limit|default(100) }}"
+    PollingInterval "{{ plugin.polling_interval|default(60) }}"
 </Module>
diff --git a/neutron/files/collectd_openstack_neutron_agents.conf b/neutron/files/collectd_openstack_neutron_agents.conf
new file mode 100644
index 0000000..41a42b4
--- /dev/null
+++ b/neutron/files/collectd_openstack_neutron_agents.conf
@@ -0,0 +1,10 @@
+Import "openstack_neutron_agents"
+
+<Module "openstack_neutron_agents">
+    KeystoneUrl "{{ plugin.url }}"
+    Username "{{ plugin.username }}"
+    Password "{{ plugin.password }}"
+    Tenant "{{ plugin.tenant }}"
+    MaxRetries "2"
+    Timeout "20"
+</Module>
diff --git a/neutron/files/grafana_dashboards/neutron_openvswitch_influxdb.json b/neutron/files/grafana_dashboards/neutron_openvswitch_influxdb.json
new file mode 100644
index 0000000..f8419ec
--- /dev/null
+++ b/neutron/files/grafana_dashboards/neutron_openvswitch_influxdb.json
@@ -0,0 +1,3808 @@
+{
+  "annotations": {
+    "enable": true,
+    "list": [
+      {
+        "datasource": "lma",
+        "enable": true,
+        "iconColor": "#C0C6BE",
+        "iconSize": 13,
+        "lineColor": "rgba(255, 96, 96, 0.592157)",
+        "name": "Status",
+        "query": "select title,tags,text from annotations where $timeFilter and cluster =~ /^neutron/",
+        "showLine": true,
+        "tagsColumn": "tags",
+        "textColumn": "text",
+        "titleColumn": "title"
+      }
+    ]
+  },
+  "editable": true,
+  "gnetId": null,
+  "hideControls": false,
+  "id": null,
+  "links": [],
+  "refresh": "1m",
+  "rows": [
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "cacheTimeout": null,
+          "colorBackground": true,
+          "colorValue": false,
+          "colors": [
+            "rgba(71, 212, 59, 0.4)",
+            "rgba(241, 181, 37, 0.73)",
+            "rgba(225, 40, 40, 0.59)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 6,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 1,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "condition": "",
+              "dsType": "influxdb",
+              "fill": "",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "groupby_field": "",
+              "interval": "",
+              "measurement": "cluster_status",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"cluster_status\" WHERE \"environment_label\" =~ /^$environment$/ AND \"cluster_name\" = 'neutron' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "cluster_name",
+                  "operator": "=",
+                  "value": "neutron-control"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,3",
+          "title": "control plane",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "no data",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OKAY",
+              "value": "0"
+            },
+            {
+              "op": "=",
+              "text": "WARN",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "UNKN",
+              "value": "2"
+            },
+            {
+              "op": "=",
+              "text": "CRIT",
+              "value": "3"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "4"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": true,
+          "colorValue": false,
+          "colors": [
+            "rgba(71, 212, 59, 0.4)",
+            "rgba(241, 181, 37, 0.73)",
+            "rgba(225, 40, 40, 0.59)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 42,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 1,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "condition": "",
+              "dsType": "influxdb",
+              "fill": "",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "groupby_field": "",
+              "interval": "",
+              "measurement": "cluster_status",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"cluster_status\" WHERE \"environment_label\" =~ /^$environment$/ AND \"cluster_name\" = 'neutron' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "cluster_name",
+                  "operator": "=",
+                  "value": "neutron-data"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,3",
+          "title": "data plane",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "no data",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OKAY",
+              "value": "0"
+            },
+            {
+              "op": "=",
+              "text": "WARN",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "UNKN",
+              "value": "2"
+            },
+            {
+              "op": "=",
+              "text": "CRIT",
+              "value": "3"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "4"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(71, 212, 59, 0.4)",
+            "rgba(245, 150, 40, 0.73)",
+            "rgba(225, 40, 40, 0.59)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 13,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": true
+          },
+          "targets": [
+            {
+              "column": "value",
+              "condition": "",
+              "dsType": "influxdb",
+              "fill": "",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupby_field": "",
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '5xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "5xx"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "HTTP 5xx errors",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "0",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 7,
+          "interval": ">60s",
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": false,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 8,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "GET",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT max(\"upper_90\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_method\" = 'GET' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "upper_90"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "max"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_method",
+                  "operator": "=",
+                  "value": "GET"
+                }
+              ]
+            },
+            {
+              "alias": "POST",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT max(\"upper_90\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_method\" = 'POST' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "B",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "upper_90"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "max"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_method",
+                  "operator": "=",
+                  "value": "POST"
+                }
+              ]
+            },
+            {
+              "alias": "PUT",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT max(\"upper_90\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_method\" = 'PUT' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "C",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "upper_90"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "max"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_method",
+                  "operator": "=",
+                  "value": "PUT"
+                }
+              ]
+            },
+            {
+              "alias": "DELETE",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT max(\"upper_90\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_method\" = 'DELETE' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "D",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "upper_90"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "max"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_method",
+                  "operator": "=",
+                  "value": "DELETE"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "HTTP response time on $server",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "fill": 1,
+          "grid": {},
+          "id": 9,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 2,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 4,
+          "stack": false,
+          "steppedLine": true,
+          "targets": [
+            {
+              "alias": "healthy",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [],
+              "measurement": "openstack_check_api",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_check_api\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'neutron-api' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "service",
+                  "operator": "=",
+                  "value": "neutron-api"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "API Availability",
+          "tooltip": {
+            "msResolution": false,
+            "shared": false,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "none",
+              "label": "",
+              "logBase": 1,
+              "max": 1,
+              "min": 0,
+              "show": false
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": true,
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 8,
+          "interval": "> 60s",
+          "legend": {
+            "alignAsTable": true,
+            "avg": false,
+            "current": false,
+            "hideEmpty": false,
+            "max": true,
+            "min": true,
+            "rightSide": false,
+            "show": true,
+            "total": true,
+            "values": true
+          },
+          "lines": false,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 8,
+          "stack": true,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "2xx",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '2xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "2xx"
+                }
+              ]
+            },
+            {
+              "alias": "1xx",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "hide": false,
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '1xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "B",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "1xx"
+                }
+              ]
+            },
+            {
+              "alias": "3xx",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "hide": false,
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '3xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "C",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "3xx"
+                }
+              ]
+            },
+            {
+              "alias": "4xx",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "hide": false,
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '4xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "D",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "4xx"
+                }
+              ]
+            },
+            {
+              "alias": "5xx",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "count",
+              "groupBy": [
+                {
+                  "interval": "auto",
+                  "params": [
+                    "auto"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "hide": false,
+              "interval": "",
+              "measurement": "openstack_neutron_http_response_times",
+              "policy": "default",
+              "query": "SELECT sum(\"count\") FROM \"openstack_neutron_http_response_times\" WHERE \"hostname\" =~ /$server/ AND \"http_status\" = '5xx' AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "E",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "count"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "sum"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "hostname",
+                  "operator": "=~",
+                  "value": "/$server/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "http_status",
+                  "operator": "=",
+                  "value": "5xx"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Number of HTTP responses on $server",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Service Status",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "100px",
+      "panels": [
+        {
+          "content": "<br />\n<h3 align=\"center\"> Up </h3>",
+          "editable": true,
+          "error": false,
+          "id": 29,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 255, 255, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 30,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "haproxy_backend_servers",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"haproxy_backend_servers\" WHERE \"environment_label\" = '$environment' AND \"backend\" = 'neutron_api' AND \"state\" = 'up' AND $timeFilter",
+              "rawQuery": true,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "backend",
+                  "operator": "=",
+                  "value": "neutron_api"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "up"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,1",
+          "title": "Neutron",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "content": "",
+          "editable": true,
+          "error": false,
+          "id": 31,
+          "links": [],
+          "mode": "markdown",
+          "span": 8,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Down </h3>",
+          "editable": true,
+          "error": false,
+          "id": 32,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(255, 255, 255, 0.97)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(245, 54, 54, 0.9)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 33,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "mean",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "haproxy_backend_servers",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"haproxy_backend_servers\" WHERE \"environment_label\" =~ /^$environment$/ AND \"backend\" = 'neutron_api' AND \"state\" = 'down' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "backend",
+                  "operator": "=",
+                  "value": "neutron_api"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "down"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "content": "",
+          "editable": true,
+          "error": false,
+          "id": 34,
+          "links": [],
+          "mode": "markdown",
+          "span": 8,
+          "style": {},
+          "title": "",
+          "type": "text"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Neutron API",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "100",
+      "panels": [
+        {
+          "content": "<br />\n<h3 align=\"center\"> Up </h3>",
+          "editable": true,
+          "error": false,
+          "id": 43,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 255, 255, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 44,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "dhcp"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "up"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,1",
+          "title": "DHCP",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 255, 255, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 45,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "l3"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "up"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,1",
+          "title": "L3",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 255, 255, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 46,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "metadata"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "up"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,1",
+          "title": "Metadata",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 255, 255, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 47,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "openvswitch"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "up"
+                }
+              ]
+            }
+          ],
+          "thresholds": "1,1",
+          "title": "OpenvSwitch",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "content": "",
+          "editable": true,
+          "error": false,
+          "id": 48,
+          "links": [],
+          "mode": "markdown",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Down </h3>",
+          "editable": true,
+          "error": false,
+          "id": 49,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(255, 255, 255, 0.9)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(255, 0, 0, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 50,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "hide": false,
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "dhcp"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "down"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(255, 255, 255, 0.97)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(245, 54, 54, 0.9)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 51,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "l3"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "down"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(255, 255, 255, 0.97)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(245, 54, 54, 0.9)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 52,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "metadata"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "down"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(255, 255, 255, 0.97)",
+            "rgba(255, 255, 255, 0.89)",
+            "rgba(245, 54, 54, 0.9)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 53,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "dsType": "influxdb",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "null"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "measurement": "openstack_neutron_agents",
+              "policy": "default",
+              "query": "SELECT mean(\"value\") FROM \"openstack_neutron_agents\" WHERE \"environment_label\" =~ /^$environment$/ AND \"service\" = 'dhcp' AND \"state\" = 'up' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "mean"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=~",
+                  "value": "/^$environment$/"
+                },
+                {
+                  "condition": "AND",
+                  "key": "service",
+                  "operator": "=",
+                  "value": "openvswitch"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "down"
+                }
+              ]
+            }
+          ],
+          "thresholds": "0,1",
+          "title": "",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "content": "",
+          "editable": true,
+          "error": false,
+          "id": 54,
+          "links": [],
+          "mode": "markdown",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Neutron Agents",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 1,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": " active",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "fill": "",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_networks",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_networks\" WHERE \"environment_label\" =~ /^$environment$/ AND \"state\" = 'active' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "state",
+                  "operator": "=",
+                  "value": "active"
+                }
+              ]
+            }
+          ],
+          "thresholds": "",
+          "title": "Networks",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 35,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "$tag_state",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [
+                "state"
+              ],
+              "measurement": "openstack_neutron_networks",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_networks\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 2,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": " active",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_subnets",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_subnets\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": "",
+          "title": "Subnets",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 36,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "number of subnets",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_subnets",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_subnets\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 3,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": " active",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_ports",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_ports\" WHERE \"environment_label\" =~ /^$environment$/ AND \"owner\" = 'compute' AND \"state\" = 'active' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "owner",
+                  "operator": "=",
+                  "value": "compute"
+                },
+                {
+                  "condition": "AND",
+                  "key": "state",
+                  "operator": "=",
+                  "value": "active"
+                }
+              ]
+            }
+          ],
+          "thresholds": "",
+          "title": "Compute ports",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 37,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "$tag_owner",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "owner"
+                  ],
+                  "type": "tag"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [
+                "owner"
+              ],
+              "measurement": "openstack_neutron_ports",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_ports\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 6,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": " active",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_routers",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_routers\" WHERE \"environment_label\" =~ /^$environment$/ AND \"state\" = 'active' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "state",
+                  "operator": "=",
+                  "value": "active"
+                }
+              ]
+            }
+          ],
+          "thresholds": "",
+          "title": "Routers",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "decimals": 0,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 38,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "$tag_state",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "state"
+                  ],
+                  "type": "tag"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [
+                "state"
+              ],
+              "measurement": "openstack_neutron_routers",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_routers\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": false,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 41,
+          "interval": "> 60s",
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": " associated",
+          "postfixFontSize": "50%",
+          "prefix": "",
+          "prefixFontSize": "50%",
+          "rangeMaps": [
+            {
+              "from": "null",
+              "text": "N/A",
+              "to": "null"
+            }
+          ],
+          "span": 2,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "targets": [
+            {
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [],
+              "groupByTags": [],
+              "measurement": "openstack_neutron_floatingips",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_floatingips\" WHERE \"environment_label\" =~ /^$environment$/ AND \"state\" = 'associated' AND $timeFilter GROUP BY time($interval) fill(null)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                },
+                {
+                  "key": "state",
+                  "operator": "=",
+                  "value": "associated"
+                }
+              ]
+            }
+          ],
+          "thresholds": "",
+          "title": "Floating IP addresses",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "datasource": null,
+          "editable": true,
+          "error": false,
+          "fill": 0,
+          "grid": {},
+          "id": 39,
+          "interval": "> 60s",
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "connected",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "alias": "$tag_state",
+              "column": "value",
+              "dsType": "influxdb",
+              "function": "last",
+              "groupBy": [
+                {
+                  "params": [
+                    "$interval"
+                  ],
+                  "type": "time"
+                },
+                {
+                  "params": [
+                    "state"
+                  ],
+                  "type": "tag"
+                },
+                {
+                  "params": [
+                    "0"
+                  ],
+                  "type": "fill"
+                }
+              ],
+              "groupByTags": [
+                "state"
+              ],
+              "measurement": "openstack_neutron_floatingips",
+              "policy": "default",
+              "query": "SELECT last(\"value\") FROM \"openstack_neutron_floatingips\" WHERE \"environment_label\" =~ /^$environment$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+              "rawQuery": false,
+              "refId": "A",
+              "resultFormat": "time_series",
+              "select": [
+                [
+                  {
+                    "params": [
+                      "value"
+                    ],
+                    "type": "field"
+                  },
+                  {
+                    "params": [],
+                    "type": "last"
+                  }
+                ]
+              ],
+              "tags": [
+                {
+                  "key": "environment_label",
+                  "operator": "=",
+                  "value": "$environment"
+                }
+              ]
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "msResolution": false,
+            "shared": true,
+            "sort": 0,
+            "value_type": "cumulative"
+          },
+          "type": "graph",
+          "xaxis": {
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": 0,
+              "show": true
+            },
+            {
+              "format": "short",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Resources",
+      "titleSize": "h6"
+    }
+  ],
+  "schemaVersion": 13,
+  "sharedCrosshair": true,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "enable": true,
+    "list": [
+      {
+        "allFormat": "regex values",
+        "allValue": null,
+        "current": {},
+        "datasource": null,
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "environment",
+        "options": [],
+        "query": "show tag values from cpu_idle with key = environment_label",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 0,
+        "tagValuesQuery": null,
+        "tagsQuery": null,
+        "type": "query"
+      },
+      {
+        "allFormat": "regex values",
+        "allValue": null,
+        "current": {},
+        "datasource": null,
+        "hide": 0,
+        "includeAll": true,
+        "label": null,
+        "multi": false,
+        "name": "server",
+        "options": [],
+        "query": " show tag values from openstack_neutron_http_response_times with key = hostname where environment_label =~ /^$environment$/ ",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 0,
+        "tagValuesQuery": null,
+        "tagsQuery": null,
+        "type": "query"
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "collapse": false,
+    "enable": true,
+    "notice": false,
+    "now": true,
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "status": "Stable",
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ],
+    "type": "timepicker"
+  },
+  "timezone": "browser",
+  "title": "Neutron",
+  "version": 1
+}
\ No newline at end of file
diff --git a/neutron/files/liberty/neutron-server.conf.Debian b/neutron/files/liberty/neutron-server.conf.Debian
index 80a4559..5e30e61 100644
--- a/neutron/files/liberty/neutron-server.conf.Debian
+++ b/neutron/files/liberty/neutron-server.conf.Debian
@@ -137,7 +137,11 @@
 # Enable or disable bulk create/update/delete operations
 # allow_bulk = True
 # Enable or disable pagination
-# allow_pagination = False
+{% if server.get('allow_pagination', false) %}
+allow_pagination = True
+{% else %}
+allow_pagination = False
+{% endif %}
 # Enable or disable sorting
 # allow_sorting = False
 # Enable or disable overlapping IPs for subnets
@@ -155,7 +159,7 @@
 # be greater than 0. If the number of items requested is greater than
 # pagination_max_limit, server will just return pagination_max_limit
 # of number of items.
-# pagination_max_limit = -1
+pagination_max_limit = {{ server.pagination_max_limit|default('-1') }}
 
 # Maximum number of DNS nameservers per subnet
 # max_dns_nameservers = 5
diff --git a/neutron/files/mitaka/neutron-server.conf.Debian b/neutron/files/mitaka/neutron-server.conf.Debian
index eae3a94..bc05c66 100644
--- a/neutron/files/mitaka/neutron-server.conf.Debian
+++ b/neutron/files/mitaka/neutron-server.conf.Debian
@@ -62,14 +62,18 @@
 #allow_bulk = true
 
 # Allow the usage of the pagination (boolean value)
-#allow_pagination = false
+{% if server.get('allow_pagination', false) %}
+allow_pagination = True
+{% else %}
+allow_pagination = False
+{% endif %}
 
 # Allow the usage of the sorting (boolean value)
 #allow_sorting = false
 
 # The maximum number of items returned in a single response, value was
 # 'infinite' or negative integer means no limit (string value)
-#pagination_max_limit = -1
+pagination_max_limit = {{ server.pagination_max_limit|default('-1') }}
 
 # Default value of availability zone hints. The availability zone aware
 # schedulers use this when the resources availability_zone_hints is empty.
diff --git a/neutron/init.sls b/neutron/init.sls
index 8e39f6f..c12f434 100644
--- a/neutron/init.sls
+++ b/neutron/init.sls
@@ -8,4 +8,7 @@
 {% endif %}
 {% if pillar.neutron.compute is defined %}
 - neutron.compute
+{% endif %}
+{% if pillar.neutron.client is defined %}
+- neutron.client
 {% endif %}
\ No newline at end of file
diff --git a/neutron/map.jinja b/neutron/map.jinja
index 1af2a68..0eaf45f 100644
--- a/neutron/map.jinja
+++ b/neutron/map.jinja
@@ -48,13 +48,22 @@
     },
 }, merge=pillar.neutron.get('server', {})) %}
 
+{% set client = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['python-neutronclient']
+    },
+    'RedHat': {
+        'pkgs': ['python-neutronclient']
+    },
+}, merge=pillar.neutron.get('client', {})) %}
+
 {%- if pillar.neutron.server is defined %}
 
 {%- set tmp_server = pillar.neutron.server %}
 
 {%- if not tmp_server.backend is defined %}
 
-{%- if tmp_server.plugin == "contrail" %}
+{%- if pillar.opencontrail is defined and tmp_server.plugin == "contrail" %}
 
 {%- from "opencontrail/map.jinja" import config with context %}
 
diff --git a/neutron/meta/collectd.yml b/neutron/meta/collectd.yml
index 4fb96c5..e96fe23 100644
--- a/neutron/meta/collectd.yml
+++ b/neutron/meta/collectd.yml
@@ -1,4 +1,4 @@
-{%- if pillar.neutron.server %}
+{%- if pillar.neutron.server is defined %}
 {%- from "neutron/map.jinja" import server with context %}
 {%- if server.get('enabled', False) %}
 local_plugin:
@@ -15,5 +15,12 @@
     username: {{ server.identity.user }}
     password: {{ server.identity.password }}
     tenant: {{ server.identity.tenant }}
+  openstack_neutron_agents:
+    plugin: python
+    template: neutron/files/collectd_openstack_neutron_agents.conf
+    url: "http://{{ server.identity.host }}:{{ server.identity.port }}/v{% if server.identity.get('api_version', 2)|int == 2 %}2.0{% else %}3{% endif %}"
+    username: {{ server.identity.user }}
+    password: {{ server.identity.password }}
+    tenant: {{ server.identity.tenant }}
 {%- endif %}
 {%- endif %}
diff --git a/neutron/meta/grafana.yml b/neutron/meta/grafana.yml
index 358949f..602d6dc 100644
--- a/neutron/meta/grafana.yml
+++ b/neutron/meta/grafana.yml
@@ -1,4 +1,38 @@
 dashboard:
+  main:
+    row:
+      ost-control-plane:
+        title: OpenStack Control Plane
+        panel:
+          neutron:
+            title: Neutron
+            links:
+            - dashboard: Neutron
+              title: Neutron
+              type: dashboard
+            target:
+              cluster_status:
+                rawQuery: true
+                query: SELECT last(value) FROM cluster_status WHERE cluster_name = 'neutron-control' AND environment_label = '$environment' AND $timeFilter GROUP BY time($interval) fill(null)
+{%- if pillar.neutron.get('server', {}).get('backend', {}).engine is defined and pillar.neutron.server.backend.engine == "ml2" %}
+      ost-data-plane:
+        title: OpenStack Data Plane
+        panel:
+          neutron:
+            title: Neutron
+            links:
+            - dashboard: Neutron
+              title: Neutron
+              type: dashboard
+            target:
+              cluster_status:
+                rawQuery: true
+                query: SELECT last(value) FROM cluster_status WHERE cluster_name = 'neutron-data' AND environment_label = '$environment' AND $timeFilter GROUP BY time($interval) fill(null)
+  neutron:
+    format: json
+    template: neutron/files/grafana_dashboards/neutron_openvswitch_influxdb.json
+{%- elif pillar.neutron.server is defined %}
   neutron:
     format: json
     template: neutron/files/grafana_dashboards/neutron_influxdb.json
+{%- endif %}
diff --git a/neutron/meta/heka.yml b/neutron/meta/heka.yml
index 17fdc06..b14c967 100644
--- a/neutron/meta/heka.yml
+++ b/neutron/meta/heka.yml
@@ -1,10 +1,12 @@
 {%- from "neutron/map.jinja" import server with context %}
-{%- if server.backend.engine == "ml2" %}
+{%- if server.get('backend', {}).engine is defined and server.backend.engine == "ml2" %}
 {% set neutron_agents = ('l3', 'dhcp', 'metadata', 'openvswitch') %}
 {%- else %}
 {% set neutron_agents = () %}
 {%- endif %}
 
+{% set ovs_support = pillar.neutron.get('compute', {}).get('enabled', False) or pillar.neutron.get('gateway', {}).get('enabled', False) %}
+
 log_collector:
   decoder:
     neutron:
@@ -12,6 +14,12 @@
       module_file: /usr/share/lma_collector/decoders/openstack_log.lua
       module_dir: /usr/share/lma_collector/common;/usr/share/heka/lua_modules
       adjust_timezone: true
+{%- if ovs_support %}
+    ovs:
+      engine: sandbox
+      module_file: /usr/share/lma_collector/decoders/ovs_log.lua
+      module_dir: /usr/share/lma_collector/common;/usr/share/heka/lua_modules
+{%- endif %}
   splitter:
     neutron:
       engine: token
@@ -20,11 +28,21 @@
     neutron_log:
       engine: logstreamer
       log_directory: "/var/log"
-      file_match: 'neutron/(?P<Service>(dhcp-agent|l3-agent|metadata-agent|neutron-netns-cleanup|openvswitch-agent|server))\.log\.?(?P<Seq>\d*)$'
+      file_match: 'neutron/(?P<Service>(dhcp-agent|l3-agent|metadata-agent|neutron-netns-cleanup|openvswitch-agent|neutron-lbaas-agent|server))\.log\.?(?P<Seq>\d*)$'
       differentiator: ['neutron', '_', 'Service']
       priority: ["^Seq"]
       decoder: "neutron_decoder"
       splitter: "neutron_splitter"
+{%- if ovs_support %}
+    ovs_log:
+      engine: logstreamer
+      log_directory: "/var/log/openvswitch"
+      file_match: '(?P<Service>ovs\-vswitchd|ovsdb\-server|ovs\-ctl)\.log$'
+      differentiator: ['Service']
+      priority: ["^Seq"]
+      decoder: "ovs_decoder"
+      splitter: "TokenSplitter"
+{%- endif %}
 metric_collector:
   trigger:
     neutron_logs_error:
@@ -41,7 +59,7 @@
         window: 70
         periods: 0
         function: max
-    {%- if pillar.neutron.server is defined %}
+{%- if pillar.neutron.server is defined %}
     neutron_api_local_endpoint:
       description: 'Neutron API is locally down'
       severity: down
@@ -54,25 +72,32 @@
         window: 60
         periods: 0
         function: last
-    {%- endif %}
+{%- endif %}
   alarm:
-    {%- if pillar.neutron.server is defined %}
-    neutron_logs:
+{%- if pillar.neutron.server is defined %}
+    neutron_logs_control:
       alerting: enabled
       triggers:
       - neutron_logs_error
       dimension:
-        service: neutron-logs
+        service: neutron-logs-control
     neutron_api_endpoint:
       alerting: enabled
       triggers:
       - neutron_api_local_endpoint
       dimension:
         service: neutron-api-endpoint
-    {%- endif %}
+{%- else %}
+    neutron_logs_data:
+      alerting: enabled
+      triggers:
+      - neutron_logs_error
+      dimension:
+        service: neutron-logs-data
+{%- endif %}
+{%- if pillar.neutron.server is defined %}
 remote_collector:
   trigger:
-    {%- if pillar.neutron.server is defined %}
     neutron_api_check_failed:
       description: 'Endpoint check for neutron-api is failed'
       severity: down
@@ -85,8 +110,7 @@
         window: 60
         periods: 0
         function: last
-    {%- endif %}
-    {%- for agent in neutron_agents %}
+{%- for agent in neutron_agents %}
     neutron_{{ agent }}_two_up:
       description: 'Some Neutron {{ agent }} agents are down'
       severity: warning
@@ -146,16 +170,14 @@
         window: 60
         periods: 0
         function: last
-    {%- endfor %}
+{%- endfor %}
   alarm:
-    {%- if pillar.neutron.server is defined %}
     neutron_api_check:
       triggers:
       - neutron_api_check_failed
       dimension:
         service: neutron-api-check
-    {%- endif %}
-    {%- for agent in neutron_agents %}
+{%- for agent in neutron_agents %}
     neutron_{{ agent }}:
       alerting: enabled
       triggers:
@@ -164,17 +186,19 @@
       - neutron_{{ agent }}_two_up
       dimension:
         service: neutron-{{ agent }}
-    {%- endfor %}
+{%- endfor %}
+{%- endif %}
 aggregator:
   alarm_cluster:
-    neutron_logs:
+{%- if pillar.neutron.server is defined %}
+    neutron_logs_control:
       policy: status_of_members
       alerting: enabled
       group_by: hostname
       match:
-        service: neutron-logs
+        service: neutron-logs-control
       members:
-      - neutron_logs
+      - neutron_logs_control
       dimension:
         service: neutron-control
         nagios_host: 01-service-clusters
@@ -205,13 +229,13 @@
       match:
         service: neutron-control
       members:
-      - neutron_logs
+      - neutron_logs_control
       - neutron_api_endpoint
       - neutron_api_check
       dimension:
         cluster_name: neutron-control
         nagios_host: 00-top-clusters
-    {%- for agent in neutron_agents %}
+{%- for agent in neutron_agents %}
     neutron_{{ agent }}:
       policy: highest_severity
       alerting: enabled
@@ -222,7 +246,7 @@
       dimension:
         service: neutron-data
         nagios_host: 01-service-clusters
-    {%- endfor %}
+{%- endfor %}
 {%- if neutron_agents|length > 0 %}
     neutron_data:
       policy: highest_severity
@@ -230,10 +254,24 @@
       match:
         service: neutron-data
       members:
-    {%- for agent in neutron_agents %}
+      - neutron_logs_data
+{%- for agent in neutron_agents %}
       - neutron_{{ agent }}
-    {%- endfor %}
+{%- endfor %}
       dimension:
         cluster_name: neutron-data
         nagios_host: 00-top-clusters
 {%- endif %}
+{%- else %}
+    neutron_logs_data:
+      policy: status_of_members
+      alerting: enabled
+      group_by: hostname
+      match:
+        service: neutron-logs-data
+      members:
+      - neutron_logs_data
+      dimension:
+        service: neutron-data
+        nagios_host: 01-service-clusters
+{%- endif %}
diff --git a/neutron/server.sls b/neutron/server.sls
index 370e402..23b439b 100644
--- a/neutron/server.sls
+++ b/neutron/server.sls
@@ -1,5 +1,5 @@
 {%- from "neutron/map.jinja" import server with context %}
-{%- if server.enabled %}
+{%- if server.get('enabled', False) %}
 
 neutron_server_packages:
   pkg.installed: