Merge branch 'master' into scheduler_host_manager
diff --git a/.kitchen.travis.yml b/.kitchen.travis.yml
new file mode 100644
index 0000000..12d5cdf
--- /dev/null
+++ b/.kitchen.travis.yml
@@ -0,0 +1,6 @@
+suites:
+
+  - name: <%= ENV['SUITE'] %>
+    provisioner:
+      pillars-from-files:
+        nova.sls: tests/pillar/<%= ENV['SUITE'] %>.sls
diff --git a/.kitchen.yml b/.kitchen.yml
index c3e27e9..500aef2 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -32,13 +32,9 @@
         "*":
           - linux_repo_openstack
           - nova
-    nova:
-      controller:
-        version: <%= ENV['OS_VERSION'] || 'mitaka' %>
-      compute:
-        version: <%= ENV['OS_VERSION'] || 'mitaka' %>
+          - release
   pillars-from-files:
-    linux_repo_openstack.sls: tests/pillar/repo_mcp_openstack_<%= ENV['OS_VERSION'] || 'mitaka' %>.sls
+    linux_repo_openstack.sls: tests/pillar/repo_mcp_openstack_<%= ENV['OS_VERSION'] || 'ocata' %>.sls
 
 verifier:
   name: inspec
@@ -56,10 +52,20 @@
     provisioner:
       pillars-from-files:
         nova.sls: tests/pillar/compute_cluster.sls
+      pillars:
+        release.sls:
+          nova:
+            compute:
+              version: <%= ENV['OS_VERSION'] || 'ocata' %>
 
   - name: control_cluster
     provisioner:
       pillars-from-files:
         nova.sls: tests/pillar/control_cluster.sls
+      pillars:
+        release.sls:
+          nova:
+            controller:
+              version: <%= ENV['OS_VERSION'] || 'ocata' %>
 
 # vim: ft=yaml sw=2 ts=2 sts=2 tw=125
diff --git a/.travis.yml b/.travis.yml
index ceebb08..265206b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,16 +17,21 @@
   - bundle install
 
 env:
-  - PLATFORM=trevorj/salty-whales:trusty OS_VERSION=mitaka
-  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=mitaka
-  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=newton
+  - PLATFORM=trevorj/salty-whales:trusty OS_VERSION=mitaka SUITE=compute_cluster
+  - PLATFORM=trevorj/salty-whales:trusty OS_VERSION=mitaka SUITE=control_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=mitaka SUITE=compute_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=mitaka SUITE=control_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=newton SUITE=compute_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=newton SUITE=control_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=ocata SUITE=compute_cluster
+  - PLATFORM=trevorj/salty-whales:xenial OS_VERSION=ocata SUITE=control_cluster
 
 before_script:
   - set -o pipefail
   - make test | tail
 
 script:
-  - test ! -e .kitchen.yml || bundle exec kitchen test -t tests/integration
+  -  KITCHEN_LOCAL_YAML=.kitchen.travis.yml bundle exec kitchen test -t tests/integration
 
 notifications:
   webhooks:
diff --git a/README.rst b/README.rst
index 39df559..56ecdb1 100644
--- a/README.rst
+++ b/README.rst
@@ -26,6 +26,7 @@
         cpu_allocation_ratio: 8.0
         ram_allocation_ratio: 1.0
         disk_allocation_ratio: 1.0
+        cross_az_attach: false
         workers: 8
         report_interval: 60
         bind:
@@ -145,6 +146,61 @@
           # Add key without value to remove line from policy.json
           'compute:create:attach_network':
 
+
+Client-side RabbitMQ TLS configuration:
+---------------------------------------
+
+To enable TLS for oslo.messaging you need to provide the CA certificate.
+
+By default system-wide CA certs is used. Nothing should be specified except `ssl.enabled`.
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+
+
+
+Use `cacert_file` option to specify the CA-cert file path explicitly:
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+          cacert_file: /etc/ssl/rabbitmq-ca.pem
+
+To manage content of the `cacert_file` use the `cacert` option:
+
+.. code-block:: yaml
+
+  nova:
+    controller:
+      ....
+      message_queue:
+        ssl:
+          enabled: True
+          cacert: |
+
+          -----BEGIN CERTIFICATE-----
+                    ...
+          -----END CERTIFICATE-------
+
+          cacert_file: /etc/openstack/rabbitmq-ca.pem
+
+
+Notice:
+ * The `message_queue.port` is set to **5671** (AMQPS) by default if `ssl.enabled=True`.
+ * Use `message_queue.ssl.version` if you need to specify protocol version. By default is TLSv1 for python < 2.7.9 and TLSv1_2 for version above.
+
+
 Compute nodes
 -------------
 
@@ -157,12 +213,15 @@
         version: juno
         enabled: true
         virtualization: kvm
+        cross_az_attach: false
+        disk_cachemodes: network=writeback,block=none
         availability_zone: availability_zone_01
         aggregates:
         - hosts_with_fc
         - hosts_with_ssd
         security_group: true
         resume_guests_state_on_host_boot: False
+        my_ip: 10.1.0.16
         bind:
           vnc_address: 172.20.0.100
           vnc_port: 6080
@@ -207,6 +266,19 @@
         qemu:
           max_files: 4096
           max_processes: 4096
+        host: node-12.domain.tld
+
+Group membership for user nova (upgrade related)
+
+.. code-block:: yaml
+
+    nova:
+      compute:
+        enabled: true
+        ...
+        user:
+          groups:
+          - libvirt
 
 Nova services on compute node with OpenContrail
 
@@ -241,7 +313,7 @@
 .. code-block:: yaml
 
    nova:
-     controller:
+     compute:
        ....
        message_queue:
          engine: rabbitmq
@@ -254,7 +326,6 @@
          virtual_host: '/openstack'
       ....
 
-
 Nova with ephemeral configured with Ceph
 
 .. code-block:: yaml
@@ -268,11 +339,56 @@
           rbd_pool: nova
           rbd_user: nova
           secret_uuid: 03006edd-d957-40a3-ac4c-26cd254b3731
+      ....
 
+Nova with ephemeral configured with LVM
+
+.. code-block:: yaml
+
+    nova:
+      compute:
+        enabled: true
+        ...
+        lvm:
+          ephemeral: yes
+          images_volume_group: nova_vg
+
+    linux:
+      storage:
+        lvm:
+          nova_vg:
+            name: nova_vg
+            devices:
+              - /dev/sdf
+              - /dev/sdd
+              - /dev/sdg
+              - /dev/sde
+              - /dev/sdc
+              - /dev/sdj
+              - /dev/sdh
 
 Client role
 -----------
 
+Nova configured with NFS
+
+.. code-block:: yaml
+
+    nova:
+      compute:
+        instances_path: /mnt/nova/instances
+
+    linux:
+      storage:
+        enabled: true
+        mount:
+          nfs_nova:
+            enabled: true
+            path: ${nova:compute:instances_path}
+            device: 172.31.35.145:/data
+            file_system: nfs
+            opts: rw,vers=3
+
 Nova flavors
 
 .. code-block:: yaml
@@ -326,6 +442,20 @@
             - aggregate1
             - aggregate2
 
+Upgrade levels
+
+.. code-block:: yaml
+
+    nova:
+      controller:
+        upgrade_levels:
+          compute: juno
+
+    nova:
+      compute:
+        upgrade_levels:
+          compute: juno
+
 SR-IOV
 ------
 
@@ -399,17 +529,72 @@
   glance image-update --property hw_scsi_model=virtio-scsi <image>
   glance image-update --property hw_disk_bus=scsi <image>
 
+
 Scheduler Host Manager
 ----------------------
 
 Specify a custom host manager.
 
+libvirt CPU mode
+----------------
+
+Allow setting the model of CPU that is exposed to a VM. This allows better
+support live migration between hypervisors with different hardware, among other
+things. Defaults to host-passthrough.
+
+
 .. code-block:: yaml
 
   nova:
     controller:
       scheduler_host_manager: ironic_host_manager
 
+    compute:
+      cpu_mode: host-model
+
+Nova compute workarounds
+------------------------
+
+Live snapshotting is disabled by default in nova. To enable this, it needs a manual switch.
+
+From manual:
+
+.. code-block:: yaml
+
+  # When using libvirt 1.2.2 live snapshots fail intermittently under load
+  # (likely related to concurrent libvirt/qemu operations). This config
+  # option provides a mechanism to disable live snapshot, in favor of cold
+  # snapshot, while this is resolved. Cold snapshot causes an instance
+  # outage while the guest is going through the snapshotting process.
+  #
+  # For more information, refer to the bug report:
+  #
+  #   https://bugs.launchpad.net/nova/+bug/1334398
+
+Configurable pillar data:
+
+.. code-block:: yaml
+
+  nova:
+    compute:
+      workaround:
+        disable_libvirt_livesnapshot: False
+
+Config drive options
+--------------------
+
+See example below on how to configure the options for the config drive.
+
+.. code-block:: yaml
+
+  nova:
+    compute:
+      config_drive:
+        forced: True  # Default: True
+        cdrom: True  # Default: False
+        format: iso9660  # Default: vfat
+        inject_password: False  # Default: False
+
 
 Documentation and Bugs
 ======================
diff --git a/_modules/novang.py b/_modules/novang.py
index dcac2fc..4748151 100644
--- a/_modules/novang.py
+++ b/_modules/novang.py
@@ -1,13 +1,10 @@
 # -*- coding: utf-8 -*-
-from __future__ import absolute_import
+from __future__ import absolute_import, with_statement
 from pprint import pprint
 
 # Import python libs
 import logging
 
-# Import salt libs
-import salt.utils.openstack.nova as suon
-
 # Get logging started
 log = logging.getLogger(__name__)
 
@@ -25,7 +22,7 @@
     Only load this module if nova
     is installed on this minion.
     '''
-    if suon.check_nova():
+    if check_nova():
         return __virtualname__
     return (False, 'The nova execution module failed to load: '
             'only available if nova is installed.')
@@ -34,7 +31,7 @@
 __opts__ = {}
 
 
-def _auth(profile=None, tenant_name=None):
+def _authng(profile=None, tenant_name=None):
     '''
     Set up nova credentials
     '''
@@ -50,6 +47,8 @@
         region_name = credentials.get('keystone.region_name', None)
         api_key = credentials.get('keystone.api_key', None)
         os_auth_system = credentials.get('keystone.os_auth_system', None)
+        use_keystoneauth = credentials.get('keystone.use_keystoneauth', False)
+        verify = credentials.get('keystone.verify', True)
     else:
         user = __salt__['config.option']('keystone.user')
         password = __salt__['config.option']('keystone.password')
@@ -58,6 +57,8 @@
         region_name = __salt__['config.option']('keystone.region_name')
         api_key = __salt__['config.option']('keystone.api_key')
         os_auth_system = __salt__['config.option']('keystone.os_auth_system')
+        use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth', False)
+        verify = __salt__['config.option']('keystone.verify', True)
     kwargs = {
         'username': user,
         'password': password,
@@ -65,9 +66,11 @@
         'project_id': tenant,
         'auth_url': auth_url,
         'region_name': region_name,
-        'os_auth_plugin': os_auth_system
+        'os_auth_plugin': os_auth_system,
+        'use_keystoneauth': use_keystoneauth,
+        'verify': verify
     }
-    return suon.SaltNova(**kwargs)
+    return SaltNova(**kwargs)
 
 
 def server_list(profile=None, tenant_name=None):
@@ -77,7 +80,7 @@
     .. code-block:: bash
         salt '*' nova.server_list
     '''
-    conn = _auth(profile, tenant_name)
+    conn = _authng(profile, tenant_name)
     return conn.server_list()
 
 
@@ -120,7 +123,7 @@
     connection_args = get_connection_args(profile)
     tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
     tenant_id = tenant[tenant_name]['id']
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     item = nt_ks.quotas.get(tenant_id).__dict__
     return item
@@ -142,7 +145,7 @@
     connection_args = get_connection_args(profile)
     tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
     tenant_id = tenant[tenant_name]['id']
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     item = nt_ks.quotas.update(tenant_id, **quota_argument)
     return item
@@ -155,7 +158,7 @@
     .. code-block:: bash
         salt '*' nova.server_list
     '''
-    conn = _auth(profile, tenant_name)
+    conn = _authng(profile, tenant_name)
     return conn.server_list()
 
 
@@ -166,7 +169,7 @@
     .. code-block:: bash
         salt '*' nova.secgroup_list
     '''
-    conn = _auth(profile, tenant_name)
+    conn = _authng(profile, tenant_name)
     return conn.secgroup_list()
 
 
@@ -193,12 +196,12 @@
         salt '*' nova.image_list
     '''
     #kwargs = {'nics': nics}
-    conn = _auth(profile, tenant_name)
+    conn = _authng(profile, tenant_name)
     return conn.boot(name, flavor_id, image_id, timeout, **kwargs)
 
 
 def network_show(name, profile=None):
-    conn = _auth(profile)
+    conn = _authng(profile)
     return conn.network_show(name)
 
 
@@ -207,7 +210,7 @@
     list existing availability zones
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     ret = nt_ks.aggregates.list()
     return ret
@@ -218,7 +221,7 @@
     list existing availability zones
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     zone_exists=False
     items = availability_zone_list(profile)
@@ -234,7 +237,7 @@
     create availability zone
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     item = nt_ks.aggregates.create(name, availability_zone)
     ret = {
@@ -249,7 +252,7 @@
     list existing aggregates
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     ret = nt_ks.aggregates.list()
     return ret
@@ -260,7 +263,7 @@
     list existing aggregates
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     aggregate_exists=False
     items = aggregate_list(profile)
@@ -276,7 +279,7 @@
     create aggregate
     '''
     connection_args = get_connection_args(profile)
-    conn = _auth(profile)
+    conn = _authng(profile)
     nt_ks = conn.compute_conn
     item = nt_ks.aggregates.create(name, aggregate)
     ret = {
@@ -284,3 +287,1765 @@
         'Aggregate Name': item.__getattr__('name'),
     }
     return ret
+
+#
+# Moved from salt.utils.openstack.nova until this works in upstream
+#
+
+'''
+Nova class
+'''
+
+# Import Python libs
+import inspect
+import time
+
+from distutils.version import LooseVersion as _LooseVersion
+
+# Import third party libs
+import salt.ext.six as six
+HAS_NOVA = False
+# pylint: disable=import-error
+try:
+    import novaclient
+    from novaclient import client
+    from novaclient.shell import OpenStackComputeShell
+    import novaclient.utils
+    import novaclient.exceptions
+    import novaclient.extension
+    import novaclient.base
+    HAS_NOVA = True
+except ImportError:
+    pass
+
+OCATA = True
+try:
+    import novaclient.auth_plugin
+    OCATA = False
+except ImportError:
+    pass
+
+HAS_KEYSTONEAUTH = False
+try:
+    import keystoneauth1.loading
+    import keystoneauth1.session
+    HAS_KEYSTONEAUTH = True
+except ImportError:
+    pass
+# pylint: enable=import-error
+
+# Import salt libs
+import salt.utils
+from salt.exceptions import SaltCloudSystemExit
+
+# Version added to novaclient.client.Client function
+NOVACLIENT_MINVER = '2.6.1'
+
+# dict for block_device_mapping_v2
+CLIENT_BDM2_KEYS = {
+    'id': 'uuid',
+    'source': 'source_type',
+    'dest': 'destination_type',
+    'bus': 'disk_bus',
+    'device': 'device_name',
+    'size': 'volume_size',
+    'format': 'guest_format',
+    'bootindex': 'boot_index',
+    'type': 'device_type',
+    'shutdown': 'delete_on_termination',
+}
+
+
+def check_nova():
+    if HAS_NOVA:
+        novaclient_ver = _LooseVersion(novaclient.__version__)
+        min_ver = _LooseVersion(NOVACLIENT_MINVER)
+        if novaclient_ver >= min_ver:
+            return HAS_NOVA
+        log.debug('Newer novaclient version required.  Minimum: {0}'.format(NOVACLIENT_MINVER))
+    return False
+
+
+# kwargs has to be an object instead of a dictionary for the __post_parse_arg__
+class KwargsStruct(object):
+    def __init__(self, **entries):
+        self.__dict__.update(entries)
+
+
+def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None):
+    bdm = []
+    if block_device is None:
+        block_device = []
+    if ephemeral is None:
+        ephemeral = []
+
+    if boot_volume is not None:
+        bdm_dict = {'uuid': boot_volume, 'source_type': 'volume',
+                    'destination_type': 'volume', 'boot_index': 0,
+                    'delete_on_termination': False}
+        bdm.append(bdm_dict)
+
+    if snapshot is not None:
+        bdm_dict = {'uuid': snapshot, 'source_type': 'snapshot',
+                    'destination_type': 'volume', 'boot_index': 0,
+                    'delete_on_termination': False}
+        bdm.append(bdm_dict)
+
+    for device_spec in block_device:
+        bdm_dict = {}
+
+        for key, value in six.iteritems(device_spec):
+            bdm_dict[CLIENT_BDM2_KEYS[key]] = value
+
+        # Convert the delete_on_termination to a boolean or set it to true by
+        # default for local block devices when not specified.
+        if 'delete_on_termination' in bdm_dict:
+            action = bdm_dict['delete_on_termination']
+            bdm_dict['delete_on_termination'] = (action == 'remove')
+        elif bdm_dict.get('destination_type') == 'local':
+            bdm_dict['delete_on_termination'] = True
+
+        bdm.append(bdm_dict)
+
+    for ephemeral_spec in ephemeral:
+        bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
+                    'boot_index': -1, 'delete_on_termination': True}
+        if 'size' in ephemeral_spec:
+            bdm_dict['volume_size'] = ephemeral_spec['size']
+        if 'format' in ephemeral_spec:
+            bdm_dict['guest_format'] = ephemeral_spec['format']
+
+        bdm.append(bdm_dict)
+
+    if swap is not None:
+        bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
+                    'boot_index': -1, 'delete_on_termination': True,
+                    'guest_format': 'swap', 'volume_size': swap}
+        bdm.append(bdm_dict)
+
+    return bdm
+
+
+class NovaServer(object):
+    def __init__(self, name, server, password=None):
+        '''
+        Make output look like libcloud output for consistency
+        '''
+        self.name = name
+        self.id = server['id']
+        self.image = server.get('image', {}).get('id', 'Boot From Volume')
+        self.size = server['flavor']['id']
+        self.state = server['state']
+        self._uuid = None
+        self.extra = {
+            'metadata': server['metadata'],
+            'access_ip': server['accessIPv4']
+        }
+
+        self.addresses = server.get('addresses', {})
+        self.public_ips, self.private_ips = [], []
+        for network in self.addresses.values():
+            for addr in network:
+                if salt.utils.cloud.is_public_ip(addr['addr']):
+                    self.public_ips.append(addr['addr'])
+                else:
+                    self.private_ips.append(addr['addr'])
+
+        if password:
+            self.extra['password'] = password
+
+    def __str__(self):
+        return self.__dict__
+
+
+def get_entry(dict_, key, value, raise_error=True):
+    for entry in dict_:
+        if entry[key] == value:
+            return entry
+    if raise_error is True:
+        raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(key, dict_))
+    return {}
+
+
+def get_entry_multi(dict_, pairs, raise_error=True):
+    for entry in dict_:
+        if all([entry[key] == value for key, value in pairs]):
+            return entry
+    if raise_error is True:
+        raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(pairs, dict_))
+    return {}
+
+
+def sanatize_novaclient(kwargs):
+    variables = (
+        'username', 'api_key', 'project_id', 'auth_url', 'insecure',
+        'timeout', 'proxy_tenant_id', 'proxy_token', 'region_name',
+        'endpoint_type', 'extensions', 'service_type', 'service_name',
+        'volume_service_name', 'timings', 'bypass_url', 'os_cache',
+        'no_cache', 'http_log_debug', 'auth_system', 'auth_plugin',
+        'auth_token', 'cacert', 'tenant_id'
+    )
+    ret = {}
+    for var in kwargs:
+        if var in variables:
+            ret[var] = kwargs[var]
+
+    return ret
+
+
+def _format_v2_endpoints(endpoints_v2, services):
+    catalog = []
+    for endpoint_v2 in endpoints_v2:
+        endpoints = []
+        endpoint = endpoint_v2.copy()
+        if 'internalurl' in endpoint:
+            internalurl = endpoint.pop('internalurl')
+            endpoint['internalURL'] = internalurl
+
+        if 'adminurl' in endpoint:
+            adminurl = endpoint.pop('adminurl')
+            endpoint['adminURL'] = adminurl
+
+        if 'publicurl' in endpoint:
+            publicurl = endpoint.pop('publicurl')
+            endpoint['publicURL'] = publicurl
+ 
+        etype = endpoint.pop('type', '')
+        ename = endpoint.pop('name', '')
+        if endpoint.get('service_id', None) and not etype and not ename:
+            service = [s for s in services if s.get('id', '') == endpoint.get('service_id')]
+            etype = service[0].get('type', '')
+            ename = service[0].get('name', '')
+
+        entry = {
+            'type': etype,
+            'name': ename,
+            'id': endpoint.pop('id'),
+            'region': endpoint.get('region'),
+            'endpoints': [endpoint]
+        }
+        catalog.append(entry)
+ 
+    return catalog
+
+
+# Function alias to not shadow built-ins
+class SaltNova(object):
+    '''
+    Class for all novaclient functions
+    '''
+    extensions = []
+
+    def __init__(
+        self,
+        username,
+        project_id,
+        auth_url,
+        region_name=None,
+        password=None,
+        os_auth_plugin=None,
+        use_keystoneauth=False,
+        verify=True,
+        **kwargs
+    ):
+        '''
+        Set up nova credentials
+        '''
+        if all([use_keystoneauth, HAS_KEYSTONEAUTH]):
+            self._new_init(username=username,
+                           project_id=project_id,
+                           auth_url=auth_url,
+                           region_name=region_name,
+                           password=password,
+                           os_auth_plugin=os_auth_plugin,
+                           verify=verify,
+                           **kwargs)
+        else:
+            self._old_init(username=username,
+                           project_id=project_id,
+                           auth_url=auth_url,
+                           region_name=region_name,
+                           password=password,
+                           os_auth_plugin=os_auth_plugin,
+                           verify=verify,
+                           **kwargs)
+
+    def _new_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, auth=None, verify=True, **kwargs):
+        if auth is None:
+            auth = {}
+
+        loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or 'password')
+
+        self.client_kwargs = kwargs.copy()
+        self.kwargs = auth.copy()
+        if not self.extensions:
+            if hasattr(OpenStackComputeShell, '_discover_extensions'):
+                self.extensions = OpenStackComputeShell()._discover_extensions('2.0')
+            else:
+                self.extensions = client.discover_extensions('2.0')
+            for extension in self.extensions:
+                extension.run_hooks('__pre_parse_args__')
+            self.client_kwargs['extensions'] = self.extensions
+
+        self.kwargs['username'] = username
+        self.kwargs['project_name'] = project_id
+        self.kwargs['auth_url'] = auth_url
+        self.kwargs['password'] = password
+        if auth_url.endswith('3'):
+            self.kwargs['user_domain_name'] = kwargs.get('user_domain_name', 'default')
+            self.kwargs['project_domain_name'] = kwargs.get('project_domain_name', 'default')
+
+        self.client_kwargs['region_name'] = region_name
+        self.client_kwargs['service_type'] = 'compute'
+
+        if hasattr(self, 'extensions'):
+            # needs an object, not a dictionary
+            self.kwargstruct = KwargsStruct(**self.client_kwargs)
+            for extension in self.extensions:
+                extension.run_hooks('__post_parse_args__', self.kwargstruct)
+            self.client_kwargs = self.kwargstruct.__dict__
+
+        # Requires novaclient version >= 2.6.1
+        self.version = str(kwargs.get('version', 2))
+
+        self.client_kwargs = sanatize_novaclient(self.client_kwargs)
+        options = loader.load_from_options(**self.kwargs)
+        self.session = keystoneauth1.session.Session(auth=options, verify=verify)
+        conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+        self.kwargs['auth_token'] = conn.client.session.get_token()
+        if conn.client.get_endpoint(service_type='identity').endswith('v3'):
+            self.catalog = conn.client.session.get('/auth/catalog', endpoint_filter={'service_type': 'identity'}).json().get('catalog', [])
+            self._v3_setup(region_name)
+        else:
+            if OCATA:
+                endpoints_v2 = conn.client.session.get('/endpoints', endpoint_filter={'service_type': 'identity', 'interface': 'admin'}).json().get('endpoints', [])
+                services = conn.client.session.get('/OS-KSADM/services', endpoint_filter={'service_type': 'identity', 'interface': 'admin'}).json().get('OS-KSADM:services', [])
+                self.catalog = _format_v2_endpoints(endpoints_v2, services)
+            else:
+                self.catalog = conn.client.service_catalog.catalog['access']['serviceCatalog']
+            self._v2_setup(region_name)
+
+    def _old_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, **kwargs):
+        self.kwargs = kwargs.copy()
+        if not self.extensions:
+            if hasattr(OpenStackComputeShell, '_discover_extensions'):
+                self.extensions = OpenStackComputeShell()._discover_extensions('2.0')
+            else:
+                self.extensions = client.discover_extensions('2.0')
+            for extension in self.extensions:
+                extension.run_hooks('__pre_parse_args__')
+            self.kwargs['extensions'] = self.extensions
+
+        self.kwargs['username'] = username
+        self.kwargs['project_id'] = project_id
+        self.kwargs['auth_url'] = auth_url
+        self.kwargs['region_name'] = region_name
+        self.kwargs['service_type'] = 'compute'
+
+        # used in novaclient extensions to see if they are rackspace or not, to know if it needs to load
+        # the hooks for that extension or not.  This is cleaned up by sanatize_novaclient
+        self.kwargs['os_auth_url'] = auth_url
+
+        if os_auth_plugin is not None:
+            if OCATA:
+                msg = 'Module auth_plugin is no longer present in python-novaclient >= 7.0.0'
+                raise Exception(msg)
+            else:
+                novaclient.auth_plugin.discover_auth_systems()
+                auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_plugin)
+                self.kwargs['auth_plugin'] = auth_plugin
+                self.kwargs['auth_system'] = os_auth_plugin
+
+        if not self.kwargs.get('api_key', None):
+            self.kwargs['api_key'] = password
+
+        # This has to be run before sanatize_novaclient before extra variables are cleaned out.
+        if hasattr(self, 'extensions'):
+            # needs an object, not a dictionary
+            self.kwargstruct = KwargsStruct(**self.kwargs)
+            for extension in self.extensions:
+                extension.run_hooks('__post_parse_args__', self.kwargstruct)
+            self.kwargs = self.kwargstruct.__dict__
+
+        self.kwargs = sanatize_novaclient(self.kwargs)
+
+        # Requires novaclient version >= 2.6.1
+        self.kwargs['version'] = str(kwargs.get('version', 2))
+
+        conn = client.Client(**self.kwargs)
+        try:
+            conn.client.authenticate()
+        except novaclient.exceptions.AmbiguousEndpoints:
+            raise SaltCloudSystemExit(
+                "Nova provider requires a 'region_name' to be specified"
+            )
+
+        self.kwargs['auth_token'] = conn.client.auth_token
+
+        # There is currently no way to get service catalog in the expected format with Ocata compatible
+        # python-novaclient in _old_init, because there is no session
+        if OCATA:
+            msg = 'Method service_catalog is no longer present in python-novaclient >= 7.0.0'
+            raise Exception(msg)
+
+        self.catalog = conn.client.service_catalog.catalog['access']['serviceCatalog']
+
+        self._v2_setup(region_name)
+
+    def _v3_setup(self, region_name):
+        if region_name is not None:
+            servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
+            self.kwargs['bypass_url'] = get_entry_multi(
+                servers_endpoints,
+                [('region', region_name), ('interface', 'public')]
+            )['url']
+
+        if hasattr(self, 'session'):
+            self.compute_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+        else:
+            self.compute_conn = client.Client(**self.kwargs)
+
+        volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {})
+        if volume_endpoints:
+            if region_name is not None:
+                self.kwargs['bypass_url'] = get_entry_multi(
+                    volume_endpoints,
+                    [('region', region_name), ('interface', 'public')]
+                )['url']
+
+            if hasattr(self, 'session'):
+                self.volume_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+            else:
+                self.volume_conn = client.Client(**self.kwargs)
+
+            if hasattr(self, 'extensions'):
+                self.expand_extensions()
+        else:
+            self.volume_conn = None
+
+    def _v2_setup(self, region_name):
+        if region_name is not None:
+            servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
+            self.kwargs['bypass_url'] = get_entry(
+                servers_endpoints,
+                'region',
+                region_name
+            )['publicURL']
+
+        if hasattr(self, 'session'):
+            self.compute_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+        else:
+            self.compute_conn = client.Client(**self.kwargs)
+
+        volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {})
+        if volume_endpoints:
+            if region_name is not None:
+                self.kwargs['bypass_url'] = get_entry(
+                    volume_endpoints,
+                    'region',
+                    region_name
+                )['publicURL']
+
+            if hasattr(self, 'session'):
+                self.volume_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+            else:
+                self.volume_conn = client.Client(**self.kwargs)
+
+            if hasattr(self, 'extensions'):
+                self.expand_extensions()
+        else:
+            self.volume_conn = None
+
+    def expand_extensions(self):
+        for connection in (self.compute_conn, self.volume_conn):
+            if connection is None:
+                continue
+            for extension in self.extensions:
+                for attr in extension.module.__dict__:
+                    if not inspect.isclass(getattr(extension.module, attr)):
+                        continue
+                    for key, value in six.iteritems(connection.__dict__):
+                        if not isinstance(value, novaclient.base.Manager):
+                            continue
+                        if value.__class__.__name__ == attr:
+                            setattr(connection, key, extension.manager_class(connection))
+
+    def get_catalog(self):
+        '''
+        Return service catalog
+        '''
+        return self.catalog
+
+    def server_show_libcloud(self, uuid):
+        '''
+        Make output look like libcloud output for consistency
+        '''
+        server_info = self.server_show(uuid)
+        server = next(six.itervalues(server_info))
+        server_name = next(six.iterkeys(server_info))
+        if not hasattr(self, 'password'):
+            self.password = None
+        ret = NovaServer(server_name, server, self.password)
+
+        return ret
+
+    def boot(self, name, flavor_id=0, image_id=0, timeout=300, **kwargs):
+        '''
+        Boot a cloud server.
+        '''
+        nt_ks = self.compute_conn
+        kwargs['name'] = name
+        kwargs['flavor'] = flavor_id
+        kwargs['image'] = image_id or None
+        ephemeral = kwargs.pop('ephemeral', [])
+        block_device = kwargs.pop('block_device', [])
+        boot_volume = kwargs.pop('boot_volume', None)
+        snapshot = kwargs.pop('snapshot', None)
+        swap = kwargs.pop('swap', None)
+        kwargs['block_device_mapping_v2'] = _parse_block_device_mapping_v2(
+            block_device=block_device, boot_volume=boot_volume, snapshot=snapshot,
+            ephemeral=ephemeral, swap=swap
+        )
+        response = nt_ks.servers.create(**kwargs)
+        self.uuid = response.id
+        self.password = getattr(response, 'adminPass', None)
+
+        start = time.time()
+        trycount = 0
+        while True:
+            trycount += 1
+            try:
+                return self.server_show_libcloud(self.uuid)
+            except Exception as exc:
+                log.debug(
+                    'Server information not yet available: {0}'.format(exc)
+                )
+                time.sleep(1)
+                if time.time() - start > timeout:
+                    log.error('Timed out after {0} seconds '
+                              'while waiting for data'.format(timeout))
+                    return False
+
+                log.debug(
+                    'Retrying server_show() (try {0})'.format(trycount)
+                )
+
+    def show_instance(self, name):
+        '''
+        Find a server by its name (libcloud)
+        '''
+        return self.server_by_name(name)
+
+    def root_password(self, server_id, password):
+        '''
+        Change server(uuid's) root password
+        '''
+        nt_ks = self.compute_conn
+        nt_ks.servers.change_password(server_id, password)
+
+    def server_by_name(self, name):
+        '''
+        Find a server by its name
+        '''
+        return self.server_show_libcloud(
+            self.server_list().get(name, {}).get('id', '')
+        )
+
+    def _volume_get(self, volume_id):
+        '''
+        Organize information about a volume from the volume_id
+        '''
+        if self.volume_conn is None:
+            raise SaltCloudSystemExit('No cinder endpoint available')
+        nt_ks = self.volume_conn
+        volume = nt_ks.volumes.get(volume_id)
+        response = {'name': volume.display_name,
+                    'size': volume.size,
+                    'id': volume.id,
+                    'description': volume.display_description,
+                    'attachments': volume.attachments,
+                    'status': volume.status
+                    }
+        return response
+
+    def volume_list(self, search_opts=None):
+        '''
+        List all block volumes
+        '''
+        if self.volume_conn is None:
+            raise SaltCloudSystemExit('No cinder endpoint available')
+        nt_ks = self.volume_conn
+        volumes = nt_ks.volumes.list(search_opts=search_opts)
+        response = {}
+        for volume in volumes:
+            response[volume.display_name] = {
+                'name': volume.display_name,
+                'size': volume.size,
+                'id': volume.id,
+                'description': volume.display_description,
+                'attachments': volume.attachments,
+                'status': volume.status
+            }
+        return response
+
+    def volume_show(self, name):
+        '''
+        Show one volume
+        '''
+        if self.volume_conn is None:
+            raise SaltCloudSystemExit('No cinder endpoint available')
+        nt_ks = self.volume_conn
+        volumes = self.volume_list(
+            search_opts={'display_name': name},
+        )
+        volume = volumes[name]
+#        except Exception as esc:
+#            # volume doesn't exist
+#            log.error(esc.strerror)
+#            return {'name': name, 'status': 'deleted'}
+
+        return volume
+
+    def volume_create(self, name, size=100, snapshot=None, voltype=None,
+                      availability_zone=None):
+        '''
+        Create a block device
+        '''
+        if self.volume_conn is None:
+            raise SaltCloudSystemExit('No cinder endpoint available')
+        nt_ks = self.volume_conn
+        response = nt_ks.volumes.create(
+            size=size,
+            display_name=name,
+            volume_type=voltype,
+            snapshot_id=snapshot,
+            availability_zone=availability_zone
+        )
+
+        return self._volume_get(response.id)
+
+    def volume_delete(self, name):
+        '''
+        Delete a block device
+        '''
+        if self.volume_conn is None:
+            raise SaltCloudSystemExit('No cinder endpoint available')
+        nt_ks = self.volume_conn
+        try:
+            volume = self.volume_show(name)
+        except KeyError as exc:
+            raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
+        if volume['status'] == 'deleted':
+            return volume
+        response = nt_ks.volumes.delete(volume['id'])
+        return volume
+
+    def volume_detach(self,
+                      name,
+                      timeout=300):
+        '''
+        Detach a block device
+        '''
+        try:
+            volume = self.volume_show(name)
+        except KeyError as exc:
+            raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
+        if not volume['attachments']:
+            return True
+        response = self.compute_conn.volumes.delete_server_volume(
+            volume['attachments'][0]['server_id'],
+            volume['attachments'][0]['id']
+        )
+        trycount = 0
+        start = time.time()
+        while True:
+            trycount += 1
+            try:
+                response = self._volume_get(volume['id'])
+                if response['status'] == 'available':
+                    return response
+            except Exception as exc:
+                log.debug('Volume is detaching: {0}'.format(name))
+                time.sleep(1)
+                if time.time() - start > timeout:
+                    log.error('Timed out after {0} seconds '
+                              'while waiting for data'.format(timeout))
+                    return False
+
+                log.debug(
+                    'Retrying volume_show() (try {0})'.format(trycount)
+                )
+
+    def volume_attach(self,
+                      name,
+                      server_name,
+                      device='/dev/xvdb',
+                      timeout=300):
+        '''
+        Attach a block device
+        '''
+        try:
+            volume = self.volume_show(name)
+        except KeyError as exc:
+            raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
+        server = self.server_by_name(server_name)
+        response = self.compute_conn.volumes.create_server_volume(
+            server.id,
+            volume['id'],
+            device=device
+        )
+        trycount = 0
+        start = time.time()
+        while True:
+            trycount += 1
+            try:
+                response = self._volume_get(volume['id'])
+                if response['status'] == 'in-use':
+                    return response
+            except Exception as exc:
+                log.debug('Volume is attaching: {0}'.format(name))
+                time.sleep(1)
+                if time.time() - start > timeout:
+                    log.error('Timed out after {0} seconds '
+                              'while waiting for data'.format(timeout))
+                    return False
+
+                log.debug(
+                    'Retrying volume_show() (try {0})'.format(trycount)
+                )
+
+    def suspend(self, instance_id):
+        '''
+        Suspend a server
+        '''
+        nt_ks = self.compute_conn
+        response = nt_ks.servers.suspend(instance_id)
+        return True
+
+    def resume(self, instance_id):
+        '''
+        Resume a server
+        '''
+        nt_ks = self.compute_conn
+        response = nt_ks.servers.resume(instance_id)
+        return True
+
+    def lock(self, instance_id):
+        '''
+        Lock an instance
+        '''
+        nt_ks = self.compute_conn
+        response = nt_ks.servers.lock(instance_id)
+        return True
+
+    def delete(self, instance_id):
+        '''
+        Delete a server
+        '''
+        nt_ks = self.compute_conn
+        response = nt_ks.servers.delete(instance_id)
+        return True
+
+    def flavor_list(self):
+        '''
+        Return a list of available flavors (nova flavor-list)
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for flavor in nt_ks.flavors.list():
+            links = {}
+            for link in flavor.links:
+                links[link['rel']] = link['href']
+            ret[flavor.name] = {
+                'disk': flavor.disk,
+                'id': flavor.id,
+                'name': flavor.name,
+                'ram': flavor.ram,
+                'swap': flavor.swap,
+                'vcpus': flavor.vcpus,
+                'links': links,
+            }
+            if hasattr(flavor, 'rxtx_factor'):
+                ret[flavor.name]['rxtx_factor'] = flavor.rxtx_factor
+        return ret
+
+    list_sizes = flavor_list
+
+    def flavor_create(self,
+                      name,             # pylint: disable=C0103
+                      flavor_id=0,      # pylint: disable=C0103
+                      ram=0,
+                      disk=0,
+                      vcpus=1):
+        '''
+        Create a flavor
+        '''
+        nt_ks = self.compute_conn
+        nt_ks.flavors.create(
+            name=name, flavorid=flavor_id, ram=ram, disk=disk, vcpus=vcpus
+        )
+        return {'name': name,
+                'id': flavor_id,
+                'ram': ram,
+                'disk': disk,
+                'vcpus': vcpus}
+
+    def flavor_delete(self, flavor_id):  # pylint: disable=C0103
+        '''
+        Delete a flavor
+        '''
+        nt_ks = self.compute_conn
+        nt_ks.flavors.delete(flavor_id)
+        return 'Flavor deleted: {0}'.format(flavor_id)
+
+    def keypair_list(self):
+        '''
+        List keypairs
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for keypair in nt_ks.keypairs.list():
+            ret[keypair.name] = {
+                'name': keypair.name,
+                'fingerprint': keypair.fingerprint,
+                'public_key': keypair.public_key,
+            }
+        return ret
+
+    def keypair_add(self, name, pubfile=None, pubkey=None):
+        '''
+        Add a keypair
+        '''
+        nt_ks = self.compute_conn
+        if pubfile:
+            with salt.utils.fopen(pubfile, 'r') as fp_:
+                pubkey = fp_.read()
+        if not pubkey:
+            return False
+        nt_ks.keypairs.create(name, public_key=pubkey)
+        ret = {'name': name, 'pubkey': pubkey}
+        return ret
+
+    def keypair_delete(self, name):
+        '''
+        Delete a keypair
+        '''
+        nt_ks = self.compute_conn
+        nt_ks.keypairs.delete(name)
+        return 'Keypair deleted: {0}'.format(name)
+
+    def image_show(self, image_id):
+        '''
+        Show image details and metadata
+        '''
+        nt_ks = self.compute_conn
+        image = nt_ks.images.get(image_id)
+        links = {}
+        for link in image.links:
+            links[link['rel']] = link['href']
+        ret = {
+            'name': image.name,
+            'id': image.id,
+            'status': image.status,
+            'progress': image.progress,
+            'created': image.created,
+            'updated': image.updated,
+            'metadata': image.metadata,
+            'links': links,
+        }
+        if hasattr(image, 'minDisk'):
+            ret['minDisk'] = image.minDisk
+        if hasattr(image, 'minRam'):
+            ret['minRam'] = image.minRam
+
+        return ret
+
+    def image_list(self, name=None):
+        '''
+        List server images
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for image in nt_ks.images.list():
+            links = {}
+            for link in image.links:
+                links[link['rel']] = link['href']
+            ret[image.name] = {
+                'name': image.name,
+                'id': image.id,
+                'status': image.status,
+                'progress': image.progress,
+                'created': image.created,
+                'updated': image.updated,
+                'metadata': image.metadata,
+                'links': links,
+            }
+            if hasattr(image, 'minDisk'):
+                ret[image.name]['minDisk'] = image.minDisk
+            if hasattr(image, 'minRam'):
+                ret[image.name]['minRam'] = image.minRam
+        if name:
+            return {name: ret[name]}
+        return ret
+
+    list_images = image_list
+
+    def image_meta_set(self,
+                       image_id=None,
+                       name=None,
+                       **kwargs):  # pylint: disable=C0103
+        '''
+        Set image metadata
+        '''
+        nt_ks = self.compute_conn
+        if name:
+            for image in nt_ks.images.list():
+                if image.name == name:
+                    image_id = image.id  # pylint: disable=C0103
+        if not image_id:
+            return {'Error': 'A valid image name or id was not specified'}
+        nt_ks.images.set_meta(image_id, kwargs)
+        return {image_id: kwargs}
+
+    def image_meta_delete(self,
+                          image_id=None,     # pylint: disable=C0103
+                          name=None,
+                          keys=None):
+        '''
+        Delete image metadata
+        '''
+        nt_ks = self.compute_conn
+        if name:
+            for image in nt_ks.images.list():
+                if image.name == name:
+                    image_id = image.id  # pylint: disable=C0103
+        pairs = keys.split(',')
+        if not image_id:
+            return {'Error': 'A valid image name or id was not specified'}
+        nt_ks.images.delete_meta(image_id, pairs)
+        return {image_id: 'Deleted: {0}'.format(pairs)}
+
+    def server_list(self):
+        '''
+        List servers
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for item in nt_ks.servers.list():
+            try:
+                ret[item.name] = {
+                    'id': item.id,
+                    'name': item.name,
+                    'state': item.status,
+                    'accessIPv4': item.accessIPv4,
+                    'accessIPv6': item.accessIPv6,
+                    'flavor': {'id': item.flavor['id'],
+                               'links': item.flavor['links']},
+                    'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
+                              'links': item.image['links'] if item.image else ''},
+                    }
+            except TypeError:
+                pass
+        return ret
+
+    def server_list_min(self):
+        '''
+        List minimal information about servers
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for item in nt_ks.servers.list(detailed=False):
+            try:
+                ret[item.name] = {
+                    'id': item.id,
+                    'status': 'Running'
+                }
+            except TypeError:
+                pass
+        return ret
+
+    def server_list_detailed(self):
+        '''
+        Detailed list of servers
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for item in nt_ks.servers.list():
+            try:
+                ret[item.name] = {
+                    'OS-EXT-SRV-ATTR': {},
+                    'OS-EXT-STS': {},
+                    'accessIPv4': item.accessIPv4,
+                    'accessIPv6': item.accessIPv6,
+                    'addresses': item.addresses,
+                    'created': item.created,
+                    'flavor': {'id': item.flavor['id'],
+                               'links': item.flavor['links']},
+                    'hostId': item.hostId,
+                    'id': item.id,
+                    'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
+                              'links': item.image['links'] if item.image else ''},
+                    'key_name': item.key_name,
+                    'links': item.links,
+                    'metadata': item.metadata,
+                    'name': item.name,
+                    'state': item.status,
+                    'tenant_id': item.tenant_id,
+                    'updated': item.updated,
+                    'user_id': item.user_id,
+                }
+            except TypeError:
+                continue
+
+            ret[item.name]['progress'] = getattr(item, 'progress', '0')
+
+            if hasattr(item.__dict__, 'OS-DCF:diskConfig'):
+                ret[item.name]['OS-DCF'] = {
+                    'diskConfig': item.__dict__['OS-DCF:diskConfig']
+                }
+            if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:host'):
+                ret[item.name]['OS-EXT-SRV-ATTR']['host'] = \
+                    item.__dict__['OS-EXT-SRV-ATTR:host']
+            if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:hypervisor_hostname'):
+                ret[item.name]['OS-EXT-SRV-ATTR']['hypervisor_hostname'] = \
+                    item.__dict__['OS-EXT-SRV-ATTR:hypervisor_hostname']
+            if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:instance_name'):
+                ret[item.name]['OS-EXT-SRV-ATTR']['instance_name'] = \
+                    item.__dict__['OS-EXT-SRV-ATTR:instance_name']
+            if hasattr(item.__dict__, 'OS-EXT-STS:power_state'):
+                ret[item.name]['OS-EXT-STS']['power_state'] = \
+                    item.__dict__['OS-EXT-STS:power_state']
+            if hasattr(item.__dict__, 'OS-EXT-STS:task_state'):
+                ret[item.name]['OS-EXT-STS']['task_state'] = \
+                    item.__dict__['OS-EXT-STS:task_state']
+            if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'):
+                ret[item.name]['OS-EXT-STS']['vm_state'] = \
+                    item.__dict__['OS-EXT-STS:vm_state']
+            if hasattr(item.__dict__, 'security_groups'):
+                ret[item.name]['security_groups'] = \
+                    item.__dict__['security_groups']
+        return ret
+
+    def server_show(self, server_id):
+        '''
+        Show details of one server
+        '''
+        ret = {}
+        try:
+            servers = self.server_list_detailed()
+        except AttributeError:
+            raise SaltCloudSystemExit('Corrupt server in server_list_detailed. Remove corrupt servers.')
+        for server_name, server in six.iteritems(servers):
+            if str(server['id']) == server_id:
+                ret[server_name] = server
+        return ret
+
+    def secgroup_create(self, name, description):
+        '''
+        Create a security group
+        '''
+        nt_ks = self.compute_conn
+        nt_ks.security_groups.create(name, description)
+        ret = {'name': name, 'description': description}
+        return ret
+
+    def secgroup_delete(self, name):
+        '''
+        Delete a security group
+        '''
+        nt_ks = self.compute_conn
+        for item in nt_ks.security_groups.list():
+            if item.name == name:
+                nt_ks.security_groups.delete(item.id)
+                return {name: 'Deleted security group: {0}'.format(name)}
+        return 'Security group not found: {0}'.format(name)
+
+    def secgroup_list(self):
+        '''
+        List security groups
+        '''
+        nt_ks = self.compute_conn
+        ret = {}
+        for item in nt_ks.security_groups.list():
+            ret[item.name] = {
+                'name': item.name,
+                'description': item.description,
+                'id': item.id,
+                'tenant_id': item.tenant_id,
+                'rules': item.rules,
+            }
+        return ret
+
+    def _item_list(self):
+        '''
+        List items
+        '''
+        nt_ks = self.compute_conn
+        ret = []
+        for item in nt_ks.items.list():
+            ret.append(item.__dict__)
+        return ret
+
+    def _network_show(self, name, network_lst):
+        '''
+        Parse the returned network list
+        '''
+        for net in network_lst:
+            if net.label == name:
+                return net.__dict__
+        return {}
+
+    def network_show(self, name):
+        '''
+        Show network information
+        '''
+        nt_ks = self.compute_conn
+        net_list = nt_ks.networks.list()
+        return self._network_show(name, net_list)
+
+    def network_list(self):
+        '''
+        List extra private networks
+        '''
+        nt_ks = self.compute_conn
+        return [network.__dict__ for network in nt_ks.networks.list()]
+
+    def _sanatize_network_params(self, kwargs):
+        '''
+        Sanatize novaclient network parameters
+        '''
+        params = [
+            'label', 'bridge', 'bridge_interface', 'cidr', 'cidr_v6', 'dns1',
+            'dns2', 'fixed_cidr', 'gateway', 'gateway_v6', 'multi_host',
+            'priority', 'project_id', 'vlan_start', 'vpn_start'
+        ]
+
+        for variable in six.iterkeys(kwargs):  # iterate over a copy, we might delete some
+            if variable not in params:
+                del kwargs[variable]
+        return kwargs
+
+    def network_create(self, name, **kwargs):
+        '''
+        Create extra private network
+        '''
+        nt_ks = self.compute_conn
+        kwargs['label'] = name
+        kwargs = self._sanatize_network_params(kwargs)
+        net = nt_ks.networks.create(**kwargs)
+        return net.__dict__
+
+    def _server_uuid_from_name(self, name):
+        '''
+        Get server uuid from name
+        '''
+        return self.server_list().get(name, {}).get('id', '')
+
+    def virtual_interface_list(self, name):
+        '''
+        Get virtual interfaces on slice
+        '''
+        nt_ks = self.compute_conn
+        nets = nt_ks.virtual_interfaces.list(self._server_uuid_from_name(name))
+        return [network.__dict__ for network in nets]
+
+    def virtual_interface_create(self, name, net_name):
+        '''
+        Add an interfaces to a slice
+        '''
+        nt_ks = self.compute_conn
+        serverid = self._server_uuid_from_name(name)
+        networkid = self.network_show(net_name).get('id', None)
+        if networkid is None:
+            return {net_name: False}
+        nets = nt_ks.virtual_interfaces.create(networkid, serverid)
+        return nets
+
+    def floating_ip_pool_list(self):
+        '''
+        List all floating IP pools
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        pools = nt_ks.floating_ip_pools.list()
+        response = {}
+        for pool in pools:
+            response[pool.name] = {
+                'name': pool.name,
+            }
+        return response
+
+    def floating_ip_list(self):
+        '''
+        List floating IPs
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        floating_ips = nt_ks.floating_ips.list()
+        response = {}
+        for floating_ip in floating_ips:
+            response[floating_ip.ip] = {
+                'ip': floating_ip.ip,
+                'fixed_ip': floating_ip.fixed_ip,
+                'id': floating_ip.id,
+                'instance_id': floating_ip.instance_id,
+                'pool': floating_ip.pool
+            }
+        return response
+
+    def floating_ip_show(self, ip):
+        '''
+        Show info on specific floating IP
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        floating_ips = nt_ks.floating_ips.list()
+        for floating_ip in floating_ips:
+            if floating_ip.ip == ip:
+                return floating_ip
+        return {}
+
+    def floating_ip_create(self, pool=None):
+        '''
+        Allocate a floating IP
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        floating_ip = nt_ks.floating_ips.create(pool)
+        response = {
+            'ip': floating_ip.ip,
+            'fixed_ip': floating_ip.fixed_ip,
+            'id': floating_ip.id,
+            'instance_id': floating_ip.instance_id,
+            'pool': floating_ip.pool
+        }
+        return response
+
+    def floating_ip_delete(self, floating_ip):
+        '''
+        De-allocate a floating IP
+        .. versionadded:: 2016.3.0
+        '''
+        ip = self.floating_ip_show(floating_ip)
+        nt_ks = self.compute_conn
+        return nt_ks.floating_ips.delete(ip)
+
+    def floating_ip_associate(self, server_name, floating_ip):
+        '''
+        Associate floating IP address to server
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        server_ = self.server_by_name(server_name)
+        server = nt_ks.servers.get(server_.__dict__['id'])
+        server.add_floating_ip(floating_ip)
+        return self.floating_ip_list()[floating_ip]
+
+    def floating_ip_disassociate(self, server_name, floating_ip):
+        '''
+        Disassociate a floating IP from server
+        .. versionadded:: 2016.3.0
+        '''
+        nt_ks = self.compute_conn
+        server_ = self.server_by_name(server_name)
+        server = nt_ks.servers.get(server_.__dict__['id'])
+        server.remove_floating_ip(floating_ip)
+        return self.floating_ip_list()[floating_ip]
+
+#
+# Moved from salt.modules.nova until this works in upstream
+#
+
+def _auth(profile=None):
+    '''
+    Set up nova 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']
+        region_name = credentials.get('keystone.region_name', None)
+        api_key = credentials.get('keystone.api_key', None)
+        os_auth_system = credentials.get('keystone.os_auth_system', None)
+        use_keystoneauth = credentials.get('keystone.use_keystoneauth', False)
+        verify = credentials.get('keystone.verify', False)
+    else:
+        user = __salt__['config.option']('keystone.user')
+        password = __salt__['config.option']('keystone.password')
+        tenant = __salt__['config.option']('keystone.tenant')
+        auth_url = __salt__['config.option']('keystone.auth_url')
+        region_name = __salt__['config.option']('keystone.region_name')
+        api_key = __salt__['config.option']('keystone.api_key')
+        os_auth_system = __salt__['config.option']('keystone.os_auth_system')
+        use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth', False)
+        verify = __salt__['config.option']('keystone.verify', True)
+
+    kwargs = {
+        'username': user,
+        'password': password,
+        'api_key': api_key,
+        'project_id': tenant,
+        'auth_url': auth_url,
+        'region_name': region_name,
+        'os_auth_plugin': os_auth_system,
+        'use_keystoneauth': use_keystoneauth,
+        'verify': verify
+    }
+
+    return SaltNova(**kwargs)
+
+
+def boot(name, flavor_id=0, image_id=0, profile=None, timeout=300):
+    '''
+    Boot (create) a new instance
+    name
+        Name of the new instance (must be first)
+    flavor_id
+        Unique integer ID for the flavor
+    image_id
+        Unique integer ID for the image
+    timeout
+        How long to wait, after creating the instance, for the provider to
+        return information about it (default 300 seconds).
+        .. versionadded:: 2014.1.0
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.boot myinstance flavor_id=4596 image_id=2
+    The flavor_id and image_id are obtained from nova.flavor_list and
+    nova.image_list
+    .. code-block:: bash
+        salt '*' nova.flavor_list
+        salt '*' nova.image_list
+    '''
+    conn = _auth(profile)
+    return conn.boot(name, flavor_id, image_id, timeout)
+
+
+def volume_list(search_opts=None, profile=None):
+    '''
+    List storage volumes
+    search_opts
+        Dictionary of search options
+    profile
+        Profile to use
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_list \
+                search_opts='{"display_name": "myblock"}' \
+                profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_list(search_opts=search_opts)
+
+
+def volume_show(name, profile=None):
+    '''
+    Create a block storage volume
+    name
+        Name of the volume
+    profile
+        Profile to use
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_show myblock profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_show(name)
+
+
+def volume_create(name, size=100, snapshot=None, voltype=None,
+                  profile=None):
+    '''
+    Create a block storage volume
+    name
+        Name of the new volume (must be first)
+    size
+        Volume size
+    snapshot
+        Block storage snapshot id
+    voltype
+        Type of storage
+    profile
+        Profile to build on
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_create myblock size=300 profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_create(
+        name,
+        size,
+        snapshot,
+        voltype
+    )
+
+
+def volume_delete(name, profile=None):
+    '''
+    Destroy the volume
+    name
+        Name of the volume
+    profile
+        Profile to build on
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_delete myblock profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_delete(name)
+
+
+def volume_detach(name,
+                  profile=None,
+                  timeout=300):
+    '''
+    Attach a block storage volume
+    name
+        Name of the new volume to attach
+    server_name
+        Name of the server to detach from
+    profile
+        Profile to build on
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_detach myblock profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_detach(
+        name,
+        timeout
+    )
+
+
+def volume_attach(name,
+                  server_name,
+                  device='/dev/xvdb',
+                  profile=None,
+                  timeout=300):
+    '''
+    Attach a block storage volume
+    name
+        Name of the new volume to attach
+    server_name
+        Name of the server to attach to
+    device
+        Name of the device on the server
+    profile
+        Profile to build on
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.volume_attach myblock slice.example.com profile=openstack
+        salt '*' nova.volume_attach myblock server.example.com \
+                device='/dev/xvdb' profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.volume_attach(
+        name,
+        server_name,
+        device,
+        timeout
+    )
+
+
+def suspend(instance_id, profile=None):
+    '''
+    Suspend an instance
+    instance_id
+        ID of the instance to be suspended
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.suspend 1138
+    '''
+    conn = _auth(profile)
+    return conn.suspend(instance_id)
+
+
+def resume(instance_id, profile=None):
+    '''
+    Resume an instance
+    instance_id
+        ID of the instance to be resumed
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.resume 1138
+    '''
+    conn = _auth(profile)
+    return conn.resume(instance_id)
+
+
+def lock(instance_id, profile=None):
+    '''
+    Lock an instance
+    instance_id
+        ID of the instance to be locked
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.lock 1138
+    '''
+    conn = _auth(profile)
+    return conn.lock(instance_id)
+
+
+def delete(instance_id, profile=None):
+    '''
+    Delete an instance
+    instance_id
+        ID of the instance to be deleted
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.delete 1138
+    '''
+    conn = _auth(profile)
+    return conn.delete(instance_id)
+
+
+def flavor_list(profile=None):
+    '''
+    Return a list of available flavors (nova flavor-list)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.flavor_list
+    '''
+    conn = _auth(profile)
+    return conn.flavor_list()
+
+
+def flavor_create(name,      # pylint: disable=C0103
+                  flavor_id=0,      # pylint: disable=C0103
+                  ram=0,
+                  disk=0,
+                  vcpus=1,
+                  profile=None):
+    '''
+    Add a flavor to nova (nova flavor-create). The following parameters are
+    required:
+    name
+        Name of the new flavor (must be first)
+    flavor_id
+        Unique integer ID for the new flavor
+    ram
+        Memory size in MB
+    disk
+        Disk size in GB
+    vcpus
+        Number of vcpus
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.flavor_create myflavor flavor_id=6 \
+                ram=4096 disk=10 vcpus=1
+    '''
+    conn = _auth(profile)
+    return conn.flavor_create(
+        name,
+        flavor_id,
+        ram,
+        disk,
+        vcpus
+    )
+
+
+def flavor_delete(flavor_id, profile=None):  # pylint: disable=C0103
+    '''
+    Delete a flavor from nova by id (nova flavor-delete)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.flavor_delete 7
+    '''
+    conn = _auth(profile)
+    return conn.flavor_delete(flavor_id)
+
+
+def keypair_list(profile=None):
+    '''
+    Return a list of available keypairs (nova keypair-list)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.keypair_list
+    '''
+    conn = _auth(profile)
+    return conn.keypair_list()
+
+
+def keypair_add(name, pubfile=None, pubkey=None, profile=None):
+    '''
+    Add a keypair to nova (nova keypair-add)
+    CLI Examples:
+    .. code-block:: bash
+        salt '*' nova.keypair_add mykey pubfile='/home/myuser/.ssh/id_rsa.pub'
+        salt '*' nova.keypair_add mykey pubkey='ssh-rsa <key> myuser@mybox'
+    '''
+    conn = _auth(profile)
+    return conn.keypair_add(
+        name,
+        pubfile,
+        pubkey
+    )
+
+
+def keypair_delete(name, profile=None):
+    '''
+    Add a keypair to nova (nova keypair-delete)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.keypair_delete mykey'
+    '''
+    conn = _auth(profile)
+    return conn.keypair_delete(name)
+
+
+def image_list(name=None, profile=None):
+    '''
+    Return a list of available images (nova images-list + nova image-show)
+    If a name is provided, only that image will be displayed.
+    CLI Examples:
+    .. code-block:: bash
+        salt '*' nova.image_list
+        salt '*' nova.image_list myimage
+    '''
+    conn = _auth(profile)
+    return conn.image_list(name)
+
+
+def image_meta_set(image_id=None,
+                   name=None,
+                   profile=None,
+                   **kwargs):  # pylint: disable=C0103
+    '''
+    Sets a key=value pair in the metadata for an image (nova image-meta set)
+    CLI Examples:
+    .. code-block:: bash
+        salt '*' nova.image_meta_set 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 \
+                cheese=gruyere
+        salt '*' nova.image_meta_set name=myimage salad=pasta beans=baked
+    '''
+    conn = _auth(profile)
+    return conn.image_meta_set(
+        image_id,
+        name,
+        **kwargs
+    )
+
+
+def image_meta_delete(image_id=None,     # pylint: disable=C0103
+                      name=None,
+                      keys=None,
+                      profile=None):
+    '''
+    Delete a key=value pair from the metadata for an image
+    (nova image-meta set)
+    CLI Examples:
+    .. code-block:: bash
+        salt '*' nova.image_meta_delete \
+                6f52b2ff-0b31-4d84-8fd1-af45b84824f6 keys=cheese
+        salt '*' nova.image_meta_delete name=myimage keys=salad,beans
+    '''
+    conn = _auth(profile)
+    return conn.image_meta_delete(
+        image_id,
+        name,
+        keys
+    )
+
+
+def list_(profile=None):
+    '''
+    To maintain the feel of the nova command line, this function simply calls
+    the server_list function.
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.list
+    '''
+    return server_list(profile=profile)
+
+
+def server_list(profile=None):
+    '''
+    Return list of active servers
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.server_list
+    '''
+    conn = _auth(profile)
+    return conn.server_list()
+
+
+def show(server_id, profile=None):
+    '''
+    To maintain the feel of the nova command line, this function simply calls
+    the server_show function.
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.show
+    '''
+    return server_show(server_id, profile)
+
+
+def server_list_detailed(profile=None):
+    '''
+    Return detailed list of active servers
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.server_list_detailed
+    '''
+    conn = _auth(profile)
+    return conn.server_list_detailed()
+
+
+def server_show(server_id, profile=None):
+    '''
+    Return detailed information for an active server
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.server_show <server_id>
+    '''
+    conn = _auth(profile)
+    return conn.server_show(server_id)
+
+
+def secgroup_create(name, description, profile=None):
+    '''
+    Add a secgroup to nova (nova secgroup-create)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.secgroup_create mygroup 'This is my security group'
+    '''
+    conn = _auth(profile)
+    return conn.secgroup_create(name, description)
+
+
+def secgroup_delete(name, profile=None):
+    '''
+    Delete a secgroup to nova (nova secgroup-delete)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.secgroup_delete mygroup
+    '''
+    conn = _auth(profile)
+    return conn.secgroup_delete(name)
+
+
+def secgroup_list(profile=None):
+    '''
+    Return a list of available security groups (nova items-list)
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.secgroup_list
+    '''
+    conn = _auth(profile)
+    return conn.secgroup_list()
+
+
+def server_by_name(name, profile=None):
+    '''
+    Return information about a server
+    name
+        Server Name
+    CLI Example:
+    .. code-block:: bash
+        salt '*' nova.server_by_name myserver profile=openstack
+    '''
+    conn = _auth(profile)
+    return conn.server_by_name(name)
+
diff --git a/_states/novang.py b/_states/novang.py
index 17ba41c..a3376e7 100644
--- a/_states/novang.py
+++ b/_states/novang.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 '''
-Nova state that ensures that defined flavor is present
+Custom Nova state
 '''
 import logging
 import collections
@@ -12,7 +12,7 @@
     '''
     Only load if the nova module is in __salt__
     '''
-    return 'novang' if 'nova.flavor_list' in __salt__ else False
+    return 'novang'
 
 
 def flavor_present(name, flavor_id=0, ram=0, disk=0, vcpus=1, profile=None):
@@ -23,18 +23,129 @@
            'changes': {},
            'result': True,
            'comment': 'Flavor "{0}" already exists'.format(name)}
-    project = __salt__['nova.flavor_list'](profile)
+    project = __salt__['novang.flavor_list'](profile)
     if 'Error' in project:
         pass
     elif name in project:
         pass
     else:
-        __salt__['nova.flavor_create'](name, flavor_id, ram, disk, vcpus, profile)
+        __salt__['novang.flavor_create'](name, flavor_id, ram, disk, vcpus, profile)
         ret['comment'] = 'Flavor {0} has been created'.format(name)
         ret['changes']['Flavor'] = 'Created'
     return ret
 
 
+def map_instances(name='cell1'):
+    '''
+    Ensures that the nova instances are mapped to cell
+    '''
+    ret = {'name': name,
+           'changes': {},
+           'result': False,
+           'comment': 'Cell "{0}" does not exists'.format(name)}
+    cell_uuid = __salt__['cmd.shell']('nova-manage cell_v2 list_cells 2>&- | grep ' + name + ' | tr -d \"\n\" | awk \'{print $4}\'')
+    if cell_uuid:
+        try:
+            __salt__['cmd.shell']('nova-manage cell_v2 map_instances --cell_uuid ' + cell_uuid)
+            ret['result'] = True
+            ret['comment'] = 'Instances were mapped to cell named {0}'.format(name)
+            ret['changes']['Instances'] = 'Mapped to cell named {0}'.format(name)
+        except:
+            ret['result'] = False
+            ret['comment'] = 'Error while mapping instances to cell named {0}'.format(name)
+            ret['changes']['Instances'] = 'Failed to map to cell named {0}'.format(name)
+    return ret
+
+
+def api_db_version_present(name=None, version="20"):
+    '''
+    Ensures that specific api_db version is present
+    '''
+    ret = {'name': 'api_db --version',
+           'changes': {},
+           'result': True,
+           'comment': 'Current Api_db version is not < than "{0}".'.format(version)}
+    api_db_version = __salt__['cmd.shell']('nova-manage api_db version 2>/dev/null')
+    try:
+        api_db_version = int(api_db_version)
+        version = int(version)
+    except:
+        # nova is not installed
+        ret = _no_change('api_db --version', None, test=True)
+        return ret
+    if api_db_version < version:
+        try:
+            __salt__['cmd.shell']('nova-manage api_db sync --version ' + str(version))
+            ret['result'] = True
+            ret['comment'] = 'Nova-manage api_db sync --version {0} was successfuly executed'.format(version)
+            ret['changes']['api_db'] = 'api_db sync --version {0}'.format(version)
+        except:
+            ret['result'] = False
+            ret['comment'] = 'Error while executing nova-manage api_db sync --version {0}'.format(version)
+            ret['changes']['api_db'] = 'Failed to execute api_db sync --version {0}'.format(version)
+    return ret
+
+
+def db_version_present(name=None, version="334"):
+    '''
+    Ensures that specific api_db version is present
+    '''
+    ret = {'name': 'db --version',
+           'changes': {},
+           'result': True,
+           'comment': 'Current db version is not < than "{0}".'.format(version)}
+    db_version = __salt__['cmd.shell']('nova-manage db version 2>/dev/null')
+    try:
+        db_version = int(db_version)
+        version = int(version)
+    except:
+        # nova is not installed
+        ret = _no_change('db --version', None, test=True)
+        return ret
+
+    if db_version < version:
+        try:
+            __salt__['cmd.shell']('nova-manage db sync --version ' + str(version))
+            ret['result'] = True
+            ret['comment'] = 'Nova-manage db sync --version {0} was successfuly executed'.format(version)
+            ret['changes']['db'] = 'db sync --version {0}'.format(version)
+        except:
+            ret['result'] = False
+            ret['comment'] = 'Error while executing nova-manage db sync --version {0}'.format(version)
+            ret['changes']['db'] = 'Failed to execute db sync --version {0}'.format(version)
+    return ret
+
+def online_data_migrations_present(name=None, api_db_version="20", db_version="334"):
+    '''
+    Ensures that online_data_migrations are enforced if specific version of api_db and db is present
+    '''
+    ret = {'name': 'online_data_migrations',
+           'changes': {},
+           'result': True,
+           'comment': 'Current api_db version != {0} a db version != {1}.'.format(api_db_version, db_version)}
+    cur_api_db_version = __salt__['cmd.shell']('nova-manage api_db version 2>/dev/null')
+    cur_db_version = __salt__['cmd.shell']('nova-manage db version 2>/dev/null')
+    try:
+        cur_api_db_version = int(cur_api_db_version)
+        cur_db_version = int(cur_db_version)
+        api_db_version = int(api_db_version)
+        db_version = int(db_version)
+    except:
+        # nova is not installed
+        ret = _no_change('online_data_migrations', None, test=True)
+        return ret
+    if cur_api_db_version == api_db_version and cur_db_version == db_version:
+        try:
+            __salt__['cmd.shell']('nova-manage db online_data_migrations')
+            ret['result'] = True
+            ret['comment'] = 'nova-manage db online_data_migrations was successfuly executed'
+            ret['changes']['online_data_migrations'] = 'online_data_migrations on api_db version {0} and db version {1}'.format(api_db_version, db_version)
+        except:
+            ret['result'] = False
+            ret['comment'] = 'Error while executing nova-manage db online_data_migrations'
+            ret['changes']['online_data_migrations'] = 'Failed to execute online_data_migrations on api_db version {0} and db version {1}'.format(api_db_version, db_version)
+    return ret
+
 def quota_present(tenant_name, profile, name=None, **kwargs):
     '''
     Ensures that the nova quota exists
@@ -92,7 +203,7 @@
     existing_instances = __salt__['novang.server_list'](profile, tenant_name)
     if name in existing_instances:
         return ret
-    existing_flavors = __salt__['nova.flavor_list'](profile)
+    existing_flavors = __salt__['novang.flavor_list'](profile)
     if flavor in existing_flavors:
         flavor_id = existing_flavors[flavor]['id']
     else:
@@ -101,7 +212,7 @@
                 'result': False,
                 'comment': 'Flavor "{0}" doesn\'t exists'.format(flavor)}
 
-    existing_image = __salt__['nova.image_list'](image, profile)
+    existing_image = __salt__['novang.image_list'](image, profile)
     if not existing_image:
         return {'name': name,
                 'changes': {},
@@ -140,6 +251,23 @@
             'result': True,
             'comment': 'Instance "{0}" was successfuly created'.format(name)}
 
+
+def keypair_present(name, pub_file=None, pub_key=None, profile=None):
+    """
+    Ensures that the Nova key-pair exists
+    """
+
+    existing_keypairs = __salt__['novang.keypair_list'](profile)
+    if name in existing_keypairs:
+        return _already_exists(name, 'Keypair')
+    else:
+        res = __salt__['novang.keypair_add'](name, pubfile=pub_file,
+                                             pubkey=pub_key, profile=profile)
+        if res and res['name'] == name:
+            return _created(name, 'Keypair', res)
+        return _create_failed(name, 'Keypair')
+
+
 def _already_exists(name, resource):
     changes_dict = {'name': name,
                     'changes': {},
@@ -181,3 +309,12 @@
         changes_dict['comment'] = \
             '{0} {1} is in correct state'.format(resource, name)
     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
diff --git a/metadata/service/client/init.yml b/metadata/service/client/init.yml
new file mode 100644
index 0000000..46046d6
--- /dev/null
+++ b/metadata/service/client/init.yml
@@ -0,0 +1,6 @@
+applications:
+  - nova
+parameters:
+  nova:
+    client:
+      enabled: true
diff --git a/metadata/service/compute/ironic.yml b/metadata/service/compute/ironic.yml
new file mode 100644
index 0000000..c776dde
--- /dev/null
+++ b/metadata/service/compute/ironic.yml
@@ -0,0 +1,52 @@
+applications:
+- nova
+classes:
+- service.nova.support
+parameters:
+  nova:
+    compute:
+      version: ${_param:nova_version}
+      enabled: true
+      compute_driver: 'ironic.IronicDriver'
+      ram_allocation_ratio: 1.0
+      force_config_drive: True
+      config_drive_format: 'iso9660'
+      database:
+        engine: mysql
+        host: ${_param:cluster_vip_address}
+        port: 3306
+        name: nova
+        user: nova
+        password: ${_param:mysql_nova_password}
+      identity:
+        engine: keystone
+        region: RegionOne
+        host: ${_param:cluster_vip_address}
+        port: 35357
+        user: nova
+        password: ${_param:keystone_nova_password}
+        tenant: service
+      message_queue:
+        engine: rabbitmq
+        host: ${_param:cluster_vip_address}
+        port: 5672
+        user: openstack
+        password: ${_param:rabbitmq_openstack_password}
+        virtual_host: '/openstack'
+      image:
+        engine: glance
+        host: ${_param:cluster_vip_address}
+        port: 9292
+      network:
+        engine: neutron
+        region: RegionOne
+        host: ${_param:cluster_vip_address}
+        port: 9696
+      ironic:
+        region: RegionOne
+        host: ${_param:ironic_service_host}
+        port: 6385
+        user: ironic
+        tenant: service
+        password: ${_param:keystone_ironic_password}
+        auth_type: password
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index b1d9bcd..949d7cc 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -13,3 +13,7 @@
         enabled: true
       grafana:
         enabled: true
+      telegraf:
+        enabled: true
+      prometheus:
+        enabled: true
diff --git a/nova/client.sls b/nova/client.sls
index 57d62e5..87ae2cb 100644
--- a/nova/client.sls
+++ b/nova/client.sls
@@ -33,6 +33,23 @@
 
 {%- endif %}
 
+{%- if identity.keypair is defined %}
+
+{%- for keypair_name, keypair in identity.keypair.iteritems() %}
+nova_keypair_{{ keypair_name }}:
+  novang.keypair_present:
+    - name: {{ keypair_name }}
+    {%- if keypair.pub_file is defined %}
+    - pub_file: {{ keypair.pub_file }}
+    {%- endif %}
+    {%- if keypair.pub_key is defined %}
+    - pub_key: {{ keypair.pub_key }}
+    {%- endif %}
+    - profile: {{ identity_name }}
+{%- endfor %}
+
+{%- endif %}
+
 {%- if identity.availability_zones is defined %}
 
 {%- for availability_zone_name in identity.availability_zones %}
diff --git a/nova/compute.sls b/nova/compute.sls
index 584dbe4..641305e 100644
--- a/nova/compute.sls
+++ b/nova/compute.sls
@@ -1,5 +1,6 @@
-{%- from "nova/map.jinja" import compute with context %}
-{%- if compute.enabled %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
+
+{%- if compute.get('enabled') %}
 
 nova_compute_packages:
   pkg.installed:
@@ -29,47 +30,6 @@
     - service: nova_compute_services
 {%- endif %}
 
-{%- if not salt['user.info']('nova') %}
-# MOS9 libvirt fix to create group
-group_libvirtd:
-  group.present:
-    - name: libvirtd
-    - system: True
-    - require_in:
-      - user: user_nova_compute
-
-user_nova_compute:
-  user.present:
-  - name: nova
-  - home: /var/lib/nova
-  {%- if compute.user is defined %}
-  - shell: /bin/bash
-  {%- else %}
-  - shell: /bin/false
-  {%- endif %}
-  - uid: 303
-  - gid: 303
-  - system: True
-  - groups:
-    {%- if salt['group.info']('libvirtd') %}
-    - libvirtd
-    {%- endif %}
-    - nova
-  - require_in:
-    - pkg: nova_compute_packages
-    {%- if compute.user is defined %}
-    - file: /var/lib/nova/.ssh/id_rsa
-    {%- endif %}
-
-group_nova_compute:
-  group.present:
-    - name: nova
-    - gid: 303
-    - system: True
-    - require_in:
-      - user: user_nova_compute
-{%- endif %}
-
 {%- if compute.user is defined %}
 
 nova_auth_keys:
@@ -83,8 +43,12 @@
   - name: nova
   - home: /var/lib/nova
   - shell: /bin/bash
+{%- if compute.user.groups is defined %}
+  - groups: {{ compute.user.groups }}
+{%- else %}
   - groups:
     - libvirtd
+{%- endif %}
 
 /var/lib/nova/.ssh/id_rsa:
   file.managed:
@@ -104,21 +68,39 @@
 
 {%- endif %}
 
-{%- if pillar.nova.controller is not defined %}
+{%- if not pillar.nova.get('controller',{}).get('enabled') %}
 /etc/nova/nova.conf:
   file.managed:
   - source: salt://nova/files/{{ compute.version }}/nova-compute.conf.{{ grains.os_family }}
   - template: jinja
-  - watch_in:
-    - service: nova_compute_services
   - require:
     - pkg: nova_compute_packages
+
+{%- if compute.message_queue.get('ssl',{}).get('enabled',False)  %}
+rabbitmq_ca:
+{%- if compute.message_queue.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ compute.message_queue.ssl.cacert_file }}
+    - contents_pillar: nova:compute:message_queue:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+{%- else %}
+  file.exists:
+   - name: {{ compute.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
+
 {%- endif %}
 
 nova_compute_services:
   service.running:
   - enable: true
   - names: {{ compute.services }}
+  - watch:
+    - file: /etc/nova/nova.conf
+  {%- if compute.message_queue.get('ssl',{}).get('enabled',False) %}
+    - file: rabbitmq_ca
+  {%- endif %}
 
 {%- set ident = compute.identity %}
 
@@ -155,6 +137,49 @@
 
 {%- if compute.virtualization == 'kvm' %}
 
+{%- if not salt['user.info']('nova') %}
+# MOS9 libvirt fix to create group
+group_libvirtd:
+  group.present:
+    - name: libvirtd
+    - system: True
+    - require_in:
+      - user: user_nova_compute
+
+user_nova_compute:
+  user.present:
+  - name: nova
+  - home: /var/lib/nova
+  {%- if compute.user is defined %}
+  - shell: /bin/bash
+  {%- else %}
+  - shell: /bin/false
+  {%- endif %}
+  {# note: nova uid/gid values would not be evaluated after user is created. #}
+  - uid: {{ compute.get('nova_uid', 303) }}
+  - gid: {{ compute.get('nova_gid', 303) }}
+  - system: True
+  - groups:
+    {%- if salt['group.info']('libvirtd') %}
+    - libvirtd
+    {%- endif %}
+    - nova
+  - require_in:
+    - pkg: nova_compute_packages
+    {%- if compute.user is defined %}
+    - file: /var/lib/nova/.ssh/id_rsa
+    {%- endif %}
+
+group_nova_compute:
+  group.present:
+    - name: nova
+    {# note: nova gid value would not be evaluated after user is created. #}
+    - gid: {{ compute.get('nova_gid', 303) }}
+    - system: True
+    - require_in:
+      - user: user_nova_compute
+{%- endif %}
+
 {% if compute.ceph is defined %}
 
 ceph_package:
@@ -189,7 +214,6 @@
   - template: jinja
   - require:
     - pkg: nova_compute_packages
-{%- if not grains.get('noservices', False) %}
   - watch_in:
     - service: {{ compute.libvirt_service }}
 
@@ -205,7 +229,6 @@
 
 {%- endif %}
 {%- endif %}
-{%- endif %}
 
 /etc/libvirt/qemu.conf:
   file.managed:
@@ -228,17 +251,18 @@
     - pkg: nova_compute_packages
   - onlyif: "virsh net-list | grep default"
 
-{%- if not grains.get('noservices', False) %}
 {{ compute.libvirt_service }}:
   service.running:
   - enable: true
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
   - require:
     - pkg: nova_compute_packages
     - cmd: virsh net-undefine default
   - watch:
     - file: /etc/libvirt/{{ compute.libvirt_config }}
     - file: /etc/libvirt/qemu.conf
-{%- endif %}
 
 {%- if grains.get('init', None) == "upstart" %}
 # MOS9 libvirt fix for upstart
diff --git a/nova/controller.sls b/nova/controller.sls
index 2b2fe0f..94dda8b 100644
--- a/nova/controller.sls
+++ b/nova/controller.sls
@@ -1,6 +1,6 @@
-{% from "nova/map.jinja" import controller with context %}
+{% from "nova/map.jinja" import controller, system_cacerts_file with context %}
 
-{%- if controller.enabled %}
+{%- if controller.get('enabled') %}
 
 {%- if grains.os_family == 'Debian' %}
 debconf-set-prerequisite:
@@ -24,14 +24,29 @@
   pkg.installed:
   - names: {{ controller.pkgs }}
 
+{%- if controller.message_queue.get('ssl',{}).get('enabled',False)  %}
+rabbitmq_ca:
+{%- if controller.message_queue.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ controller.message_queue.ssl.cacert_file }}
+    - contents_pillar: nova:controller:message_queue:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+{%- else %}
+  file.exists:
+   - name: {{ controller.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
+
 {%- if not salt['user.info']('nova') %}
 user_nova:
   user.present:
   - name: nova
   - home: /var/lib/nova
   - shell: /bin/false
-  - uid: 303
-  - gid: 303
+  {# note: nova uid/gid values would not be evaluated after user is created. #}
+  - uid: {{ controller.get('nova_uid', 303) }}
+  - gid: {{ controller.get('nova_gid', 303) }}
   - system: True
   - require_in:
     - pkg: nova_controller_packages
@@ -39,7 +54,8 @@
 group_nova:
   group.present:
     - name: nova
-    - gid: 303
+    {# note: nova gid value would not be evaluated after user is created. #}
+    - gid: {{ controller.get('nova_gid', 303) }}
     - system: True
     - require_in:
       - user: user_nova
@@ -69,6 +85,20 @@
   - require:
     - pkg: nova_controller_packages
 
+{% if controller.get('policy', {}) and controller.version not in ['liberty', 'mitaka', 'newton'] %}
+{# nova no longer ships with a default policy.json #}
+
+/etc/nova/policy.json:
+  file.managed:
+    - contents: '{}'
+    - replace: False
+    - user: nova
+    - group: nova
+    - require:
+      - pkg: nova_controller_packages
+
+{% endif %}
+
 {%- for name, rule in controller.get('policy', {}).iteritems() %}
 
 {%- if rule != None %}
@@ -79,6 +109,9 @@
   - rule: {{ rule }}
   - require:
     - pkg: nova_controller_packages
+    {% if controller.version not in ['liberty', 'mitaka', 'newton'] %}
+    - file: /etc/nova/policy.json
+    {% endif%}
 
 {%- else %}
 
@@ -88,56 +121,65 @@
   - name: {{ name }}
   - require:
     - pkg: nova_controller_packages
+    {% if controller.version not in ['liberty', 'mitaka', 'newton'] %}
+    - file: /etc/nova/policy.json
+    {% endif%}
 
 {%- endif %}
 
 {%- endfor %}
 
-{%- if not grains.get('noservices', False) %}
-
-nova_controller_syncdb:
-  cmd.run:
-  - names:
-    - nova-manage db sync
-  - require:
-    - file: /etc/nova/nova.conf
-
-{%- if controller.version in ["mitaka", "newton", "ocata"] %}
-nova_controller_sync_apidb:
-  cmd.run:
-  - name: nova-manage api_db sync
-  - require:
-    - file: /etc/nova/nova.conf
-  - require_in:
-    - cmd: nova_controller_syncdb
-{%- endif %}
-
-{%- if controller.version in ["newton", "ocata"] %}
-nova_controller_online_data_migrations:
-  cmd.run:
-  - name: nova-manage db online_data_migrations
-  - require:
-    - cmd: nova_controller_syncdb
-{%- endif %}
-
 {%- if controller.version in ["ocata"] %}
 
+{#- the following api_db sync --version 20 happens only if the current api_db version is < 20 #}
+
+nova_controller_api_db_sync_version_20:
+  novang.api_db_version_present:
+  - version: "20"
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - file: /etc/nova/nova.conf
+
+{#- the following db sync --version 334 happens only if the current db version is < 334 #}
+
+nova_controller_db_sync_version_334:
+  novang.db_version_present:
+  - version: "334"
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - file: /etc/nova/nova.conf
+
+{#- the following db online_data_migrations executes only if the current db version == 334 && api_db version == 20 #}
+
+online_data_migrations_for_apidb20_and_db334:
+  novang.online_data_migrations_present:
+  - api_db_version: "20"
+  - db_version: "334"
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - novang: nova_controller_api_db_sync_version_20
+    - novang: nova_controller_db_sync_version_334
+
 nova_controller_map_cell0:
   cmd.run:
   - name: nova-manage cell_v2 map_cell0
-  - require:
-    - cmd: nova_controller_sync_apidb
-  - require_in:
-    - cmd: nova_controller_syncdb
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
 
 nova_cell1_create:
   cmd.run:
   - name: nova-manage cell_v2 create_cell --name=cell1
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
   - unless: 'nova-manage cell_v2 list_cells | grep cell1'
-  - require:
-    - cmd: nova_controller_sync_apidb
-  - require_in:
-    - cmd: nova_controller_syncdb
 
 nova_placement_service_mask:
   file.symlink:
@@ -163,16 +205,81 @@
      - name: /etc/apache2/sites-enabled/nova-placement-api.conf
      - target: /etc/apache2/sites-available/nova-placement-api.conf
 
+nova_controller_discover_hosts:
+  cmd.run:
+  - name: nova-manage cell_v2 discover_hosts
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - cmd: nova_controller_map_cell0
+    - cmd: nova_cell1_create
+
+nova_controller_map_instances:
+  novang.map_instances:
+  - name: 'cell1'
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - cmd: nova_controller_discover_hosts
+    - pkg: nova_controller_packages
+
+{%- endif %}
+
+{%- if controller.version in ["mitaka", "newton", "ocata"] %}
+nova_controller_sync_apidb:
+  cmd.run:
+  - name: nova-manage api_db sync
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - file: /etc/nova/nova.conf
+
+{%- endif %}
+
+nova_controller_syncdb:
+  cmd.run:
+  - names:
+    - nova-manage db sync
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - file: /etc/nova/nova.conf
+
+{%- if controller.version in ["mitaka", "newton", "ocata"] %}
+
+nova_controller_online_data_migrations:
+  cmd.run:
+  - name: nova-manage db online_data_migrations
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
+  - require:
+    - cmd: nova_controller_syncdb
+
+{%- endif %}
+
+{%- if controller.version in ["ocata"] %}
+
 nova_apache_restart:
   service.running:
   - enable: true
   - name: apache2
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
   - require:
     - cmd: nova_controller_syncdb
   - watch:
     - file: /etc/nova/nova.conf
     - file: /etc/nova/api-paste.ini
     - file: /etc/apache2/sites-available/nova-placement-api.conf
+    {%- if controller.database.get('ssl',{}).get('enabled',False)  %}
+    - file: mysql_ca
+    {% endif %}
 
 {%- endif %}
 
@@ -180,13 +287,20 @@
   service.running:
   - enable: true
   - names: {{ controller.services }}
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
   - require:
     - cmd: nova_controller_syncdb
   - watch:
     - file: /etc/nova/nova.conf
     - file: /etc/nova/api-paste.ini
-
-{%- endif %}
+    {%- if controller.message_queue.get('ssl',{}).get('enabled',False) %}
+    - file: rabbitmq_ca
+    {%- endif %}
+    {%- if controller.database.get('ssl',{}).get('enabled',False)  %}
+    - file: mysql_ca
+    {% endif %}
 
 {%- if grains.get('virtual_subtype', None) == "Docker" %}
 
@@ -199,4 +313,22 @@
 
 {%- endif %}
 
+{%- if controller.database.get('ssl',{}).get('enabled',False)  %}
+mysql_ca:
+{%- if controller.database.ssl.cacert is defined %}
+  file.managed:
+    - name: {{ controller.database.ssl.cacert_file }}
+    - contents_pillar: nova:controller:database:ssl:cacert
+    - mode: 0444
+    - makedirs: true
+    - require_in:
+      - file: /etc/nova/nova.conf
+{%- else %}
+  file.exists:
+   - name: {{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}
+   - require_in:
+     - file: /etc/nova/nova.conf
+{%- endif %}
+{%- endif %}
+
 {%- endif %}
diff --git a/nova/files/grafana_dashboards/hypervisor_prometheus.json b/nova/files/grafana_dashboards/hypervisor_prometheus.json
new file mode 100644
index 0000000..f564bd9
--- /dev/null
+++ b/nova/files/grafana_dashboards/hypervisor_prometheus.json
@@ -0,0 +1,1253 @@
+{% raw %}
+{
+  "description": "Monitors Hypervisor cluster using Prometheus. Shows overall cluster processes and usage.",
+  "editable": true,
+  "gnetId": 315,
+  "graphTooltip": 1,
+  "hideControls": false,
+  "id": null,
+  "links": [],
+  "refresh": "1m",
+  "rows": [
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 1,
+          "height": "",
+          "id": 1,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 5,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "cpu_usage_guest{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "guest",
+              "metric": "",
+              "refId": "A",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_guest_nice{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "guest_nice",
+              "metric": "",
+              "refId": "B",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_idle{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "idle",
+              "metric": "",
+              "refId": "C",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_iowait{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "iowait",
+              "metric": "",
+              "refId": "D",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_irq{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "irq",
+              "metric": "",
+              "refId": "E",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_nice{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "nice",
+              "metric": "",
+              "refId": "F",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_softirq{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "softirq",
+              "metric": "",
+              "refId": "G",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_steal{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "steal",
+              "metric": "",
+              "refId": "H",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_system{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "system",
+              "metric": "",
+              "refId": "I",
+              "step": 10
+            },
+            {
+              "expr": "cpu_usage_user{cpu=\"cpu-total\",host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "user",
+              "metric": "",
+              "refId": "J",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "CPU on $host",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "percent",
+              "label": null,
+              "logBase": 1,
+              "max": "100",
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 1,
+          "id": 2,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 5,
+          "stack": true,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "mem_used{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "used",
+              "metric": "",
+              "refId": "A",
+              "step": 10
+            },
+            {
+              "expr": "mem_buffered{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "buffered",
+              "metric": "",
+              "refId": "B",
+              "step": 10
+            },
+            {
+              "expr": "mem_cached{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "cached",
+              "metric": "",
+              "refId": "C",
+              "step": 10
+            },
+            {
+              "expr": "mem_free{host=\"$host\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "free",
+              "metric": "",
+              "refId": "D",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Memory on $host",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "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,
+          "format": "percent",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": true,
+            "thresholdLabels": false,
+            "thresholdMarkers": false
+          },
+          "hideTimeOverride": false,
+          "id": 3,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "disk_used_percent{host=\"$host\",path=\"$mount\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "timeFrom": null,
+          "title": "Used space on $host for $mount",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Host",
+      "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 4,
+          "interval": null,
+          "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": 3,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "max(openstack_nova_instances{state=\"active\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 20
+            }
+          ],
+          "thresholds": "",
+          "title": "Total Running",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 5,
+          "interval": null,
+          "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": 3,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "max(openstack_nova_running_instances{hostname=\"$host\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 20
+            }
+          ],
+          "thresholds": "",
+          "title": "Running on $host",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 6,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_free_vcpus{hostname=\"$host\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "Free vCPUs",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free Virtual CPUs on $host",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 7,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_free_ram{hostname=\"$host\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "Free vRAM",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free Virtual RAM on $host",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decmbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 8,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_free_disk{hostname=\"$host\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "Free vDisk",
+              "refId": "A",
+              "step": 2
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free Virtual Disk Space on $host",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decgbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Virtual Instances",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 1,
+          "height": "",
+          "id": 9,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": false,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "irate(libvirt_domain_info_cpu_time_seconds_total{host=\"$host\",domain=\"$instance\"}[1m])",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "metric": "",
+              "refId": "J",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "CPU on $instance",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "percent",
+              "label": null,
+              "logBase": 1,
+              "max": "100",
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 1,
+          "id": 10,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": true,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "libvirt_domain_info_memory_usage_bytes{host=\"$host\",domain=\"$instance\"}",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "used",
+              "metric": "",
+              "refId": "A",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Memory on $instance",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 1,
+          "id": 11,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "irate(libvirt_domain_block_stats_read_bytes_total{host=\"$host\",domain=\"$instance\",target_device=\"$disk\"}[1m])",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "read",
+              "metric": "",
+              "refId": "A",
+              "step": 10
+            },
+            {
+              "expr": "irate(libvirt_domain_block_stats_write_bytes_total{host=\"$host\",domain=\"$instance\",target_device=\"$disk\"}[1m])",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "write",
+              "metric": "",
+              "refId": "B",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Disk I/O on $instance / $disk",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "Bps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fill": 0,
+          "id": 12,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "irate(libvirt_domain_interface_stats_receive_bytes_total{host=\"$host\",domain=\"$instance\",target_device=\"$interface\"}[1m])",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "rx",
+              "metric": "",
+              "refId": "A",
+              "step": 20
+            },
+            {
+              "expr": "irate(libvirt_domain_interface_stats_transmit_bytes_total{host=\"$host\",domain=\"$instance\",target_device=\"$interface\"}[1m])",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "tx",
+              "metric": "",
+              "refId": "B",
+              "step": 20
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Network I/O on $instance / $interface",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "Bps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Virtual Resources",
+      "titleSize": "h6"
+    }
+  ],
+  "schemaVersion": 14,
+  "sharedCrosshair": true,
+  "style": "dark",
+  "tags": [
+    "hypervisor",
+    "nova"
+  ],
+  "templating": {
+    "list": [
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "host",
+        "options": [],
+        "query": "label_values(libvirt_up,host)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "mount",
+        "options": [],
+        "query": "label_values(disk_free{host=\"$host\"},path)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "instance",
+        "options": [],
+        "query": "label_values(libvirt_domain_info_cpu_time_seconds_total{host=~\"$host\"},domain)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "disk",
+        "options": [],
+        "query": "label_values(libvirt_domain_block_stats_read_requests_total{host=~\"$host\",domain=\"$instance\"},target_device)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": false,
+        "label": null,
+        "multi": false,
+        "name": "interface",
+        "options": [],
+        "query": "label_values(libvirt_domain_interface_stats_receive_bytes_total{host=~\"$host\",domain=\"$instance\"},target_device)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+     }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "browser",
+  "title": "Hypervisor",
+  "version": 1
+}
+{% endraw %}
diff --git a/nova/files/grafana_dashboards/nova_prometheus.json b/nova/files/grafana_dashboards/nova_prometheus.json
new file mode 100644
index 0000000..f42395b
--- /dev/null
+++ b/nova/files/grafana_dashboards/nova_prometheus.json
@@ -0,0 +1,3593 @@
+{% raw %}
+{
+  "annotations": {
+    "list": []
+  },
+  "description": "Monitors Nova cluster using Prometheus. Shows overall cluster processes and usage.",
+  "editable": true,
+  "gnetId": 315,
+  "graphTooltip": 1,
+  "hideControls": false,
+  "id": null,
+  "links": [],
+  "refresh": "1m",
+  "rows": [
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "cacheTimeout": null,
+          "colorBackground": false,
+          "colorValue": true,
+          "colors": [
+            "rgba(245, 54, 54, 0.9)",
+            "rgba(237, 129, 40, 0.89)",
+            "rgba(50, 172, 45, 0.97)"
+          ],
+          "datasource": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 1,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 1,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "min(openstack_api_check_status{service=\"nova\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ service }}",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "1,0",
+          "title": "API Availability",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OK",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "0"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": null,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 2,
+          "interval": null,
+          "links": [],
+          "mappingType": 1,
+          "mappingTypes": [
+            {
+              "name": "value to text",
+              "value": 1
+            },
+            {
+              "name": "range to text",
+              "value": 2
+            }
+          ],
+          "maxDataPoints": 100,
+          "nullPointMode": "connected",
+          "nullText": null,
+          "postfix": "/ sec",
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "sum(irate(haproxy_http_response_5xx{proxy=~\"nova.api\",sv=\"FRONTEND\"}[5m]))",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "per sec",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "HTTP 5xx errors",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 1,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 3,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "min(haproxy_active_servers{proxy=~\"nova.api\", sv=\"BACKEND\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ service }}",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "1,0",
+          "title": "Nova API backends",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OK",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "0"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 1,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 4,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "min(haproxy_active_servers{proxy=~\"nova.metadata.api\", sv=\"BACKEND\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ service }}",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "1,0",
+          "title": "Nova Metadata API backends",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OK",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "0"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 1,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 5,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "min(haproxy_active_servers{proxy=~\"nova.novnc\", sv=\"BACKEND\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ service }}",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "1,0",
+          "title": "Nova NoVNC API backends",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            },
+            {
+              "op": "=",
+              "text": "OK",
+              "value": "1"
+            },
+            {
+              "op": "=",
+              "text": "DOWN",
+              "value": "0"
+            }
+          ],
+          "valueName": "current"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Service Status",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250",
+      "panels": [
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "fill": 1,
+          "id": 6,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(openstack_nova_http_response_times_rate{host=~\"^$host$\"})  by (http_status)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ http_status }}",
+              "refId": "A",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Throughput",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "ops",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "fill": 1,
+          "id": 7,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 6,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_http_response_times_upper_90{host=~\"^$host$\"})  by (http_method)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ http_method }}",
+              "refId": "A",
+              "step": 10
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Latency",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "API Performances",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "content": "<br />\n<h3 align=\"center\"> Compute nodes </h3>",
+          "editable": true,
+          "error": false,
+          "id": 8,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 9,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.compute\", state=\"disabled\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Disabled",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 10,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.compute\", state=\"up\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Up",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 11,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.compute\", state=\"down\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Down",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 12,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 7,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.compute\"}) by (state)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ state }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "History",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Schedulers </h3>",
+          "editable": true,
+          "error": false,
+          "id": 13,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 14,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.scheduler\", state=\"disabled\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Disabled",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 15,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.scheduler\", state=\"up\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Up",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 16,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.scheduler\", state=\"down\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Down",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 17,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 7,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.scheduler\"}) by (state)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ state }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Conductors </h3>",
+          "editable": true,
+          "error": false,
+          "id": 18,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 19,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.conductor\", state=\"disabled\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Disabled",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 20,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.conductor\", state=\"up\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Up",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 21,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.conductor\", state=\"down\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Down",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 22,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 7,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.conductor\"}) by (state)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ state }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Cert servers </h3>",
+          "editable": true,
+          "error": false,
+          "id": 23,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 24,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.cert\", state=\"disabled\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Disabled",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 25,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.cert\", state=\"up\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Up",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 26,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.cert\", state=\"down\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Down",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 27,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 7,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.cert\"}) by (state)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ state }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "content": "<br />\n<h3 align=\"center\"> Consoleauth </h3>",
+          "editable": true,
+          "error": false,
+          "id": 28,
+          "links": [],
+          "mode": "html",
+          "span": 2,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 29,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.consoleauth\", state=\"disabled\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Disabled",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 30,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.consoleauth\", state=\"up\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Up",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "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": "prometheus",
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 31,
+          "interval": null,
+          "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": true
+          },
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.consoleauth\", state=\"down\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Down",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 32,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 7,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_services{service=~\"nova.consoleauth\"}) by (state)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ state }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Nova services",
+      "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 33,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "max(openstack_nova_instances{state=\"active\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Active instances",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 34,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_instances{state=\"active\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "active",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "History",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 35,
+          "interval": null,
+          "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
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "max(openstack_nova_instances{state=\"error\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Instances in error",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 36,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_instances{state=\"error\"})",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "error",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Instances",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "content": "<h3 align=\"center\">Virtual CPUs</h3>",
+          "editable": true,
+          "error": false,
+          "height": "10px",
+          "id": 37,
+          "links": [],
+          "mode": "html",
+          "span": 4,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "content": "<h3 align=\"center\">Virtual Disks</h3>",
+          "editable": true,
+          "error": false,
+          "height": "10px",
+          "id": 38,
+          "links": [],
+          "mode": "html",
+          "span": 4,
+          "style": {},
+          "title": "",
+          "type": "text"
+        },
+        {
+          "content": "<h3 align=\"center\">Virtual RAM</h3>",
+          "editable": true,
+          "error": false,
+          "height": "10px",
+          "id": 39,
+          "links": [],
+          "mode": "html",
+          "span": 4,
+          "title": "",
+          "type": "text"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 40,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_used_vcpus",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Used",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 2,
+          "format": "decgbytes",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 41,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_used_disk",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Used",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 2,
+          "format": "decmbytes",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 42,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_used_ram",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Used",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 0,
+          "format": "none",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 43,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_free_vcpus",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Free",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 2,
+          "format": "decgbytes",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 44,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_free_disk",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Free",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        },
+        {
+          "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": "prometheus",
+          "decimals": 2,
+          "format": "decmbytes",
+          "gauge": {
+            "maxValue": 100,
+            "minValue": 0,
+            "show": false,
+            "thresholdLabels": false,
+            "thresholdMarkers": true
+          },
+          "id": 45,
+          "interval": null,
+          "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": 4,
+          "sparkline": {
+            "fillColor": "rgba(31, 118, 189, 0.18)",
+            "full": false,
+            "lineColor": "rgb(31, 120, 193)",
+            "show": false
+          },
+          "tableColumn": "",
+          "targets": [
+            {
+              "expr": "openstack_nova_total_free_ram",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "",
+              "refId": "A",
+              "step": 60
+            }
+          ],
+          "thresholds": "",
+          "title": "Free",
+          "type": "singlestat",
+          "valueFontSize": "80%",
+          "valueMaps": [
+            {
+              "op": "=",
+              "text": "N/A",
+              "value": "null"
+            }
+          ],
+          "valueName": "current"
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Resources",
+      "titleSize": "h6"
+    },
+    {
+      "collapse": false,
+      "height": "250px",
+      "panels": [
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 46,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_free_vcpus) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free VCPUs",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 47,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_free_disk) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free Disk Space",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decgbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 48,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_free_ram) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Free RAM",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decmbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 0,
+          "fill": 1,
+          "id": 49,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_used_vcpus) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Used VCPUs",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 50,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_used_disk) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Used Disk Space",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decgbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": "prometheus",
+          "decimals": 2,
+          "fill": 1,
+          "id": 51,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "links": [],
+          "nullPointMode": "null",
+          "percentage": false,
+          "pointradius": 5,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "span": 4,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "max(openstack_nova_aggregate_used_ram) by (aggregate)",
+              "format": "time_series",
+              "intervalFactor": 2,
+              "legendFormat": "{{ aggregate }}",
+              "refId": "A",
+              "step": 4
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "Used RAM",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "decmbytes",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ]
+        }
+      ],
+      "repeat": null,
+      "repeatIteration": null,
+      "repeatRowId": null,
+      "showTitle": true,
+      "title": "Resources per Aggregates",
+      "titleSize": "h6"
+    }
+  ],
+  "schemaVersion": 14,
+  "sharedCrosshair": true,
+  "style": "dark",
+  "tags": [
+    "nova"
+  ],
+  "templating": {
+    "list": [
+      {
+        "allValue": null,
+        "current": {},
+        "datasource": "prometheus",
+        "hide": 0,
+        "includeAll": true,
+        "label": null,
+        "multi": true,
+        "name": "host",
+        "options": [],
+        "query": "label_values(openstack_nova_http_response_times_count,host)",
+        "refresh": 1,
+        "refresh_on_load": true,
+        "regex": "",
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tags": [],
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "browser",
+  "title": "Nova",
+  "version": 2
+}
+{% endraw %}
diff --git a/nova/files/juno/libvirtd.conf.Debian b/nova/files/juno/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/juno/libvirtd.conf.Debian
+++ b/nova/files/juno/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/juno/nova-compute.conf.Debian b/nova/files/juno/nova-compute.conf.Debian
index bb4dba5..de61288 100644
--- a/nova/files/juno/nova-compute.conf.Debian
+++ b/nova/files/juno/nova-compute.conf.Debian
@@ -15,6 +15,12 @@
 api_paste_config=/etc/nova/api-paste.ini
 volumes_path=/var/lib/nova/volumes
 enabled_apis=ec2,osapi_compute,metadata
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 allow_resize_to_same_host=True
 
@@ -107,6 +113,8 @@
 
 {% endif %}
 
+instances_path = {{ compute.instances_path }}
+
 {%- if compute.notification is defined %}
 notification_driver = {{ compute.notification.driver }}
 
@@ -146,3 +154,13 @@
 
 [cinder]
 catalog_info=volumev2:cinderv2:internalURL
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
+
+{%- if compute.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/juno/nova-controller.conf.Debian b/nova/files/juno/nova-controller.conf.Debian
index 263539b..7d4c04e 100644
--- a/nova/files/juno/nova-controller.conf.Debian
+++ b/nova/files/juno/nova-controller.conf.Debian
@@ -154,3 +154,13 @@
 
 [cinder]
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/kilo/libvirtd.conf.Debian b/nova/files/kilo/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/kilo/libvirtd.conf.Debian
+++ b/nova/files/kilo/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/kilo/nova-compute.conf.Debian b/nova/files/kilo/nova-compute.conf.Debian
index fcf3ae3..059ae78 100644
--- a/nova/files/kilo/nova-compute.conf.Debian
+++ b/nova/files/kilo/nova-compute.conf.Debian
@@ -17,7 +17,12 @@
 volumes_path=/var/lib/nova/volumes
 enabled_apis=ec2,osapi_compute,metadata
 vnc_keymap = {{ compute.get('vnc_keymap', 'en-us') }}
-
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 {%- if compute.image.use_cow is defined %}
 use_cow_images = {{ compute.image.use_cow }}
@@ -96,6 +101,8 @@
 
 {% endif %}
 
+instances_path = {{ compute.instances_path }}
+
 {%- if compute.notification is defined %}
 notification_driver = {{ compute.notification.driver }}
 
@@ -176,6 +183,9 @@
 [cinder]
 os_region_name = {{ compute.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
 
 {%- if compute.get('ceph', {}).ephemeral is defined %}
 [libvirt]
@@ -188,3 +198,10 @@
 libvirt_inject_key=false
 libvirt_inject_partition=-2
 {%- endif %}
+
+{%- if compute.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/kilo/nova-controller.conf.Debian b/nova/files/kilo/nova-controller.conf.Debian
index c86039a..228c358 100644
--- a/nova/files/kilo/nova-controller.conf.Debian
+++ b/nova/files/kilo/nova-controller.conf.Debian
@@ -210,3 +210,13 @@
 [cinder]
 os_region_name = {{ controller.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/liberty/libvirtd.conf.Debian b/nova/files/liberty/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/liberty/libvirtd.conf.Debian
+++ b/nova/files/liberty/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/liberty/nova-compute.conf.Debian b/nova/files/liberty/nova-compute.conf.Debian
index 283f9c4..90e56ee 100644
--- a/nova/files/liberty/nova-compute.conf.Debian
+++ b/nova/files/liberty/nova-compute.conf.Debian
@@ -24,6 +24,12 @@
 {%- if compute.image.use_cow is defined %}
 use_cow_images = {{ compute.image.use_cow }}
 {%- endif %}
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 reserved_host_memory_mb = {{ compute.get('reserved_host_memory_mb', '512') }}
 
@@ -100,6 +106,8 @@
 instance_usage_audit_period = hour
 {% endif %}
 
+instances_path = {{ compute.instances_path }}
+
 {%- if compute.get('notification', {}).notify_on is defined %}
 {%- for key, value in compute.notification.notify_on.iteritems() %}
 notify_on_{{ key }} = {{ value }}
@@ -190,10 +198,13 @@
 [cinder]
 os_region_name = {{ compute.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
 
 {%- if compute.get('ceph', {}).ephemeral is defined %}
 [libvirt]
-disk_cachemodes="network=writeback,block=none"
+disk_cachemodes="{{ compute.get('disk_cachemodes', 'network=writeback,block=none') }}"
 cpu_mode=host-passthrough
 virt_type=kvm
 live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST
@@ -209,3 +220,10 @@
 
 [vnc]
 keymap = {{ compute.get('vnc_keymap', 'en-us') }}
+
+{%- if compute.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/liberty/nova-controller.conf.Debian b/nova/files/liberty/nova-controller.conf.Debian
index e33c50e..4ceb955 100644
--- a/nova/files/liberty/nova-controller.conf.Debian
+++ b/nova/files/liberty/nova-controller.conf.Debian
@@ -221,6 +221,16 @@
 [cinder]
 os_region_name = {{ controller.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
 
 [vnc]
 keymap = {{ controller.get('vnc_keymap', 'en-us') }}
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/mitaka/libvirtd.conf.Debian b/nova/files/mitaka/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/mitaka/libvirtd.conf.Debian
+++ b/nova/files/mitaka/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/mitaka/nova-compute.conf.Debian b/nova/files/mitaka/nova-compute.conf.Debian
index ba9a0f0..bdd636c 100644
--- a/nova/files/mitaka/nova-compute.conf.Debian
+++ b/nova/files/mitaka/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 
 [DEFAULT]
 logdir=/var/log/nova
@@ -11,9 +11,10 @@
 compute_manager=nova.compute.manager.ComputeManager
 network_device_mtu=65000
 use_neutron = True
-config_drive_format=vfat
-force_config_drive=True
-allow_resize_to_same_host=True
+config_drive_format={{ compute.get('config_drive_format', compute.get('config_drive', {}).get('format', 'vfat')) }}
+config_drive_cdrom={{ compute.get('config_drive', {}).get('cdrom', False)|lower }}
+force_config_drive={{ compute.get('config_drive', {}).get('forced', True)|lower }}
+config_drive_inject_password={{ compute.get('config_drive', {}).get('inject_password', False)|lower }}
 security_group_api=neutron
 vif_plugging_is_fatal=True
 vif_plugging_timeout=300
@@ -26,6 +27,12 @@
 {%- if compute.image.use_cow is defined %}
 use_cow_images = {{ compute.image.use_cow }}
 {%- endif %}
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 remove_unused_original_minimum_age_seconds=86400
 image_service=nova.image.glance.GlanceImageService
@@ -64,6 +71,8 @@
 instance_usage_audit_period = hour
 {%- endif %}
 
+instances_path = {{ compute.instances_path }}
+
 {%- if compute.get('notification', {}).notify_on is defined %}
 {%- for key, value in compute.notification.notify_on.iteritems() %}
 notify_on_{{ key }} = {{ value }}
@@ -105,11 +114,11 @@
 {%- endif %}
 
 [libvirt]
-cpu_mode = host-passthrough
+cpu_mode = {{ compute.get('cpu_mode', 'host-passthrough') }}
 virt_type = kvm
 inject_partition=-2
 inject_password=False
-disk_cachemodes="network=writeback,block=none"
+disk_cachemodes="{{ compute.get('disk_cachemodes', 'network=writeback,block=none') }}"
 libvirt_inject_password=True
 block_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_NON_SHARED_INC
 live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST
@@ -125,6 +134,15 @@
 rbd_secret_uuid={{ compute.ceph.secret_uuid }}
 libvirt_inject_password=false
 libvirt_inject_key=false
+{%- elif compute.get('lvm', {}).ephemeral is defined %}
+images_type=lvm
+images_volume_group={{ compute.lvm.images_volume_group }}
+{%- if compute.lvm.volume_clear is defined %}
+volume_clear={{ compute.lvm.volume_clear }}
+{%- endif %}
+{%- if compute.lvm.volume_clear_size is defined %}
+volume_clear_size={{ compute.lvm.volume_clear_size }}
+{%- endif %}
 {%- endif %}
 {%- if compute.libvirt.hw_disk_discard is defined %}
 hw_disk_discard={{ compute.libvirt.hw_disk_discard }}
@@ -149,15 +167,17 @@
 memcached_servers={%- for member in compute.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
 {%- endif %}
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 [oslo_messaging_rabbit]
 {%- if compute.message_queue.members is defined %}
 rabbit_hosts = {% for member in compute.message_queue.members -%}
-                   {{ member.host }}:{{ member.get('port', 5672) }}
+                   {{ member.host }}:{{ member.get('port', rabbit_port) }}
                    {%- if not loop.last -%},{%- endif -%}
                {%- endfor -%}
 {%- else %}
 rabbit_host = {{ compute.message_queue.host }}
-rabbit_port = {{ compute.message_queue.port }}
+rabbit_port = {{ rabbit_port}}
 {%- endif %}
 
 rabbit_userid = {{ compute.message_queue.user }}
@@ -167,9 +187,26 @@
 rabbit_retry_interval = 1
 rabbit_retry_backoff = 2
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [glance]
-api_servers={{ compute.image.host }}:9292
-host={{ compute.image.host }}
+api_servers=http://{{ compute.image.host }}:9292
 
 [neutron]
 username={{ compute.network.user }}
@@ -187,4 +224,18 @@
 [cinder]
 os_region_name = {{ compute.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
 
+{%- if compute.workaround is defined %}
+[workarounds]
+disable_libvirt_livesnapshot={{ compute.workaround.get('disable_libvirt_livesnapshot', True)|lower }}
+{%- endif %}
+
+{%- if compute.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/mitaka/nova-controller.conf.Debian b/nova/files/mitaka/nova-controller.conf.Debian
index ea881bc..d6f6c23 100644
--- a/nova/files/mitaka/nova-controller.conf.Debian
+++ b/nova/files/mitaka/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 verbose = True
 log-dir = /var/log/nova
@@ -105,15 +105,17 @@
 driver=messagingv2
 {%- endif %}
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 [oslo_messaging_rabbit]
 {%- if controller.message_queue.members is defined %}
 rabbit_hosts = {% for member in controller.message_queue.members -%}
-                   {{ member.host }}:{{ member.get('port', 5672) }}
+                   {{ member.host }}:{{ member.get('port', rabbit_port) }}
                    {%- if not loop.last -%},{%- endif -%}
                {%- endfor -%}
 {%- else %}
 rabbit_host = {{ controller.message_queue.host }}
-rabbit_port = {{ controller.message_queue.port }}
+rabbit_port = {{ rabbit_port }}
 {%- endif %}
 
 rabbit_userid = {{ controller.message_queue.user }}
@@ -124,6 +126,24 @@
 rabbit_retry_backoff = 2
 rpc_conn_pool_size = 300
 
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [cache]
 {%- if controller.cache is defined %}
 enabled = true
@@ -160,7 +180,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}{%- if controller.database.get('ssl',{}).get('enabled',False) %}?ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 [api_database]
 idle_timeout = 180
@@ -173,7 +193,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api{%- if controller.database.get('ssl',{}).get('enabled',False) %}?ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 [glance]
 
@@ -184,7 +204,7 @@
 project_domain_name = default
 user_domain_name = default
 auth_url = http://{{ controller.identity.host }}:35357
-{% if pillar.neutron is defined %}
+{% if pillar.neutron is defined and pillar.neutron.server is defined %}
 password={{ pillar.neutron.server.identity.password }}
 project_name={{ pillar.neutron.server.identity.tenant }}
 username={{ pillar.neutron.server.identity.user }}
@@ -205,6 +225,9 @@
 [cinder]
 os_region_name = {{ controller.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
 
 [vnc]
 keymap = {{ controller.get('vnc_keymap', 'en-us') }}
@@ -254,3 +277,10 @@
 {% if controller.cors.allow_headers is defined %}
 allow_headers = {{ controller.cors.allow_headers }}
 {% endif %}
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/newton/libvirtd.conf.Debian b/nova/files/newton/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/newton/libvirtd.conf.Debian
+++ b/nova/files/newton/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/newton/nova-compute.conf.Debian b/nova/files/newton/nova-compute.conf.Debian
index f49529f..fb6fc85 100644
--- a/nova/files/newton/nova-compute.conf.Debian
+++ b/nova/files/newton/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 
 [DEFAULT]
 logdir=/var/log/nova
@@ -11,11 +11,12 @@
 compute_manager=nova.compute.manager.ComputeManager
 network_device_mtu=65000
 use_neutron = True
-config_drive_format=vfat
-force_config_drive=True
+config_drive_format={{ compute.get('config_drive_format', compute.get('config_drive', {}).get('format', 'vfat')) }}
+config_drive_cdrom={{ compute.get('config_drive', {}).get('cdrom', False)|lower }}
+force_config_drive={{ compute.get('config_drive', {}).get('forced', True)|lower }}
+config_drive_inject_password={{ compute.get('config_drive', {}).get('inject_password', False)|lower }}
 force_raw_images=True
 notify_api_faults=False
-allow_resize_to_same_host=True
 security_group_api=neutron
 vif_plugging_is_fatal=True
 vif_plugging_timeout=300
@@ -28,6 +29,12 @@
 {%- if compute.image.use_cow is defined %}
 use_cow_images = {{ compute.image.use_cow }}
 {%- endif %}
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 remove_unused_original_minimum_age_seconds=86400
 image_service=nova.image.glance.GlanceImageService
@@ -43,7 +50,10 @@
 auth_strategy = keystone
 
 neutron_url_timeout = 300
-compute_driver = libvirt.LibvirtDriver
+compute_driver = {{ compute.get('compute_driver', 'libvirt.LibvirtDriver') }}
+{%- if compute.ram_allocation_ratio is defined %}
+ram_allocation_ratio = {{ compute.ram_allocation_ratio }}
+{%- endif %}
 
 heal_instance_info_cache_interval = {{ compute.heal_instance_info_cache_interval }}
 
@@ -61,23 +71,25 @@
 resume_guests_state_on_host_boot = {{ compute.get('resume_guests_state_on_host_boot', True) }}
 service_down_time = 90
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if compute.message_queue.members is defined %}
 transport_url = rabbit://{% for member in compute.message_queue.members -%}
-                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ compute.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ controller.message_queue.port }}{{ compute.message_queue.virtual_host }}
+transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ rabbit_port }}/{{ compute.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
-
 {% if pillar.ceilometer is defined %}
 instance_usage_audit = True
 instance_usage_audit_period = hour
 {%- endif %}
 
+instances_path = {{ compute.instances_path }}
+
 {%- if compute.get('notification', {}).notify_on is defined %}
 {%- for key, value in compute.notification.notify_on.iteritems() %}
 notify_on_{{ key }} = {{ value }}
@@ -86,6 +98,24 @@
 notify_on_state_change = vm_and_task_state
 {%- endif %}
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 [oslo_concurrency]
 lock_path = /var/lib/nova/tmp
 
@@ -101,15 +131,23 @@
 
 [vnc]
 enabled = true
+{%- if compute.vncproxy_url is defined %}
 novncproxy_base_url={{ compute.vncproxy_url }}/vnc_auto.html
+{%- endif %}
+{%- if compute.get('bind', {}).get('vnc_port') %}
 novncproxy_port={{ compute.bind.vnc_port }}
+{%- endif %}
 vncserver_listen=0.0.0.0
+{%- if compute.get('bind', {}).get('vnc_address') %}
 vncserver_proxyclient_address={{ compute.bind.vnc_address }}
+{%- endif %}
 keymap = {{ compute.get('vnc_keymap', 'en-us') }}
 
 [spice]
 enabled = false
+{%- if compute.vncproxy_url is defined %}
 html5proxy_base_url = {{ compute.vncproxy_url }}/spice_auto.html
+{%- endif %}
 
 [cache]
 {%- if compute.cache is defined %}
@@ -119,11 +157,11 @@
 {%- endif %}
 
 [libvirt]
-cpu_mode = host-passthrough
+cpu_mode = {{ compute.get('cpu_mode', 'host-passthrough') }}
 virt_type = kvm
 inject_partition=-2
 inject_password=False
-disk_cachemodes="network=writeback,block=none"
+disk_cachemodes="{{ compute.get('disk_cachemodes', 'network=writeback,block=none') }}"
 libvirt_inject_password=True
 block_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_NON_SHARED_INC
 live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST
@@ -139,6 +177,15 @@
 rbd_secret_uuid={{ compute.ceph.secret_uuid }}
 libvirt_inject_password=false
 libvirt_inject_key=false
+{%- elif compute.get('lvm', {}).ephemeral is defined %}
+images_type=lvm
+images_volume_group={{ compute.lvm.images_volume_group }}
+{%- if compute.lvm.volume_clear is defined %}
+volume_clear={{ compute.lvm.volume_clear }}
+{%- endif %}
+{%- if compute.lvm.volume_clear_size is defined %}
+volume_clear_size={{ compute.lvm.volume_clear_size }}
+{%- endif %}
 {%- endif %}
 {%- if compute.libvirt.hw_disk_discard is defined %}
 hw_disk_discard={{ compute.libvirt.hw_disk_discard }}
@@ -167,8 +214,9 @@
 
 
 [glance]
-api_servers={{ compute.image.host }}:9292
-host={{ compute.image.host }}
+{%- if compute.image is defined %}
+api_servers=http://{{ compute.image.host }}:9292
+{%- endif %}
 
 [neutron]
 username={{ compute.network.user }}
@@ -186,4 +234,30 @@
 [cinder]
 os_region_name = {{ compute.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
 
+{%- if compute.ironic is defined %}
+[ironic]
+api_endpoint=http://{{ compute.ironic.host }}:{{ compute.ironic.get('port', 6385) }}
+auth_type={{ compute.ironic.auth_type }}
+auth_url=http://{{ compute.identity.host }}:{{ compute.identity.port }}/v3
+project_name={{ compute.identity.get('tenant', 'service') }}
+project_domain_name={{ compute.ironic.get('project_domain_name', 'Default') }}
+username={{ compute.ironic.user }}
+user_domain_name={{ compute.ironic.get('user_domain_name', 'Default') }}
+password={{ compute.ironic.password }}
+{%- endif %}
+
+{%- if compute.workaround is defined %}
+[workarounds]
+disable_libvirt_livesnapshot={{ compute.workaround.get('disable_libvirt_livesnapshot', True)|lower }}
+{%- endif %}
+
+{%- if compute.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/newton/nova-controller.conf.Debian b/nova/files/newton/nova-controller.conf.Debian
index 144fedc..c8e0bee 100644
--- a/nova/files/newton/nova-controller.conf.Debian
+++ b/nova/files/newton/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 verbose = True
 log-dir = /var/log/nova
@@ -40,7 +40,7 @@
 scheduler_host_manager={{ controller.get('scheduler_host_manager', 'host_manager') }}
 use_forwarded_for=False
 reservation_expire=86400
-compute_driver = nova.virt.libvirt.LibvirtDriver
+compute_driver = libvirt.LibvirtDriver
 rootwrap_config = /etc/nova/rootwrap.conf
 auth_strategy = keystone
 firewall_driver=nova.virt.firewall.NoopFirewallDriver
@@ -75,17 +75,35 @@
 block_device_allocate_retries=600
 block_device_allocate_retries_interval=10
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if controller.message_queue.members is defined %}
 transport_url = rabbit://{% for member in controller.message_queue.members -%}
-                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ controller.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ controller.message_queue.port }}/{{ controller.message_queue.virtual_host }}
+transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ rabbit_port }}/{{ controller.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+[oslo_messaging_rabbit]
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
 
 [vnc]
 enabled = true
@@ -166,7 +184,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}?charset=utf8
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}?charset=utf8{%- if controller.database.get('ssl',{}).get('enabled',False) %}&ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 [oslo_middleware]
 enable_proxy_headers_parsing=True
@@ -182,7 +200,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api?charset=utf8
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api?charset=utf8{%- if controller.database.get('ssl',{}).get('enabled',False) %}&ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 [glance]
 
@@ -193,7 +211,7 @@
 project_domain_name = Default
 user_domain_name = Default
 auth_url = http://{{ controller.identity.host }}:35357/v3
-{% if pillar.neutron is defined %}
+{% if pillar.neutron is defined and pillar.neutron.server is defined %}
 password={{ pillar.neutron.server.identity.password }}
 project_name={{ pillar.neutron.server.identity.tenant }}
 username={{ pillar.neutron.server.identity.user }}
@@ -214,6 +232,9 @@
 [cinder]
 os_region_name = {{ controller.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
 
 [wsgi]
 api_paste_config=/etc/nova/api-paste.ini
@@ -262,3 +283,10 @@
 {% if controller.cors.allow_headers is defined %}
 allow_headers = {{ controller.cors.allow_headers }}
 {% endif %}
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/newton/nova-controller.conf.RedHat b/nova/files/newton/nova-controller.conf.RedHat
index 6739f80..c3b92c3 100644
--- a/nova/files/newton/nova-controller.conf.RedHat
+++ b/nova/files/newton/nova-controller.conf.RedHat
@@ -38,7 +38,7 @@
 scheduler_host_manager=host_manager
 use_forwarded_for=False
 reservation_expire=86400
-compute_driver = nova.virt.libvirt.LibvirtDriver
+compute_driver = libvirt.LibvirtDriver
 rootwrap_config = /etc/nova/rootwrap.conf
 auth_strategy = keystone
 firewall_driver=nova.virt.firewall.NoopFirewallDriver
@@ -210,6 +210,16 @@
 [cinder]
 os_region_name = {{ controller.identity.region }}
 catalog_info=volumev2:cinderv2:internalURL
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
 
 [wsgi]
 api_paste_config=/etc/nova/api-paste.ini
+
+{%- if controller.upgrade_levels is defined %}
+[upgrade_levels]
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
diff --git a/nova/files/ocata/libvirtd.conf.Debian b/nova/files/ocata/libvirtd.conf.Debian
index 8333dcb..4fab737 100644
--- a/nova/files/ocata/libvirtd.conf.Debian
+++ b/nova/files/ocata/libvirtd.conf.Debian
@@ -1,3 +1,4 @@
+{%- from "nova/map.jinja" import compute with context %}
 # Master libvirt daemon configuration file
 #
 # For further information consult http://libvirt.org/format.html
@@ -84,7 +85,7 @@
 # without becoming root.
 #
 # This is restricted to 'root' by default.
-unix_sock_group = "libvirtd"
+unix_sock_group = "{{ compute.get('libvirt_service_group', 'libvirtd') }}"
 
 # Set the UNIX socket permissions for the R/O socket. This is used
 # for monitoring VM status only
diff --git a/nova/files/ocata/nova-compute.conf.Debian b/nova/files/ocata/nova-compute.conf.Debian
index b13d4a8..2e2d276 100644
--- a/nova/files/ocata/nova-compute.conf.Debian
+++ b/nova/files/ocata/nova-compute.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import compute, system_cacerts_file with context %}
 [DEFAULT]
 
 #
@@ -165,7 +165,7 @@
 # * ``hyperv.HyperVDriver``
 #  (string value)
 #compute_driver=<None>
-compute_driver = libvirt.LibvirtDriver
+compute_driver = {{ compute.get('compute_driver', 'libvirt.LibvirtDriver') }}
 
 #
 # Allow destination machine to match source for resize. Useful when
@@ -382,7 +382,7 @@
 # backing files will not be used.
 #  (boolean value)
 #use_cow_images=true
-{%- if compute.image.use_cow is defined %}
+{%- if compute.image is defined and compute.image.use_cow is defined %}
 use_cow_images = {{ compute.image.use_cow }}
 {%- endif %}
 
@@ -574,7 +574,11 @@
 # * Any valid positive integer or float value
 #  (floating point value)
 # Minimum value: 0
+{%- if compute.ram_allocation_ratio is defined %}
+ram_allocation_ratio = {{ compute.ram_allocation_ratio }}
+{%- else  %}
 #ram_allocation_ratio=0.0
+{%- endif %}
 
 #
 # This option helps you specify virtual disk to physical disk
@@ -642,6 +646,7 @@
 #   Any string representing directory path.
 #  (string value)
 #instances_path=$state_path/instances
+instances_path = {{ compute.instances_path }}
 
 #
 # This option enables periodic compute.instance.exists notifications. Each
@@ -1253,7 +1258,7 @@
 #  (string value)
 # Allowed values: iso9660, vfat
 #config_drive_format=iso9660
-config_drive_format=vfat
+config_drive_format={{ compute.get('config_drive_format', compute.get('config_drive', {}).get('format', 'vfat')) }}
 
 #
 # Force injection to take place on a config drive
@@ -1281,7 +1286,7 @@
 #   installation.
 #  (boolean value)
 #force_config_drive=false
-force_config_drive=true
+force_config_drive={{ compute.get('config_drive', {}).get('forced', True)|lower }}
 
 #
 # Name or path of the tool used for ISO image creation
@@ -1509,6 +1514,9 @@
 # * vpn_ip
 #  (string value)
 #my_ip=10.89.104.70
+{%- if compute.my_ip is defined %}
+my_ip={{ compute.my_ip }}
+{%- endif %}
 
 #
 # The IP address which is used to connect to the block storage network.
@@ -1531,6 +1539,9 @@
 # * String with hostname, FQDN or IP address. Default is hostname of this host.
 #  (string value)
 #host=lcy01-22
+{%- if compute.host is defined %}
+host={{ compute.host }}
+{%- endif %}
 
 #
 # Assign IPv6 and IPv4 addresses when creating instances.
@@ -3055,17 +3066,18 @@
 #rpc_response_timeout=60
 rpc_response_timeout = 3600
 
+{%- set rabbit_port = compute.message_queue.get('port', 5671 if compute.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if compute.message_queue.members is defined %}
 transport_url = rabbit://{% for member in compute.message_queue.members -%}
-                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ compute.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ compute.message_queue.port }}{{ compute.message_queue.virtual_host }}
+transport_url = rabbit://{{ compute.message_queue.user }}:{{ compute.message_queue.password }}@{{ compute.message_queue.host }}:{{ rabbit_port}}/{{ compute.message_queue.virtual_host }}
 {%- endif %}
 
-rpc_backend=rabbit
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
 # include amqp and zmq. (string value)
 # This option is deprecated for removal.
@@ -4108,6 +4120,9 @@
 # By default there is no availability zone restriction on volume attach.
 #  (boolean value)
 #cross_az_attach=true
+{%- if compute.cross_az_attach is defined %}
+cross_az_attach={{ compute.cross_az_attach }}
+{%- endif %}
 
 
 [cloudpipe]
@@ -5003,7 +5018,9 @@
 #   (i.e. "http://10.0.1.0:9292" or "https://my.glance.server/image").
 #  (list value)
 #api_servers=<None>
-api_servers={{ compute.image.host }}:9292
+{%- if compute.image is defined %}
+api_servers=http://{{ compute.image.host }}:9292
+{% endif %}
 
 #
 # Enable insecure SSL (https) requests to glance.
@@ -5340,6 +5357,7 @@
 #   drive by setting the force_config_drive option to 'True'.
 #  (boolean value)
 #config_drive_cdrom=false
+config_drive_cdrom={{ compute.get('config_drive', {}).get('cdrom', False)|lower }}
 
 #
 # Configuration drive inject password
@@ -5353,6 +5371,7 @@
 # * Currently, the only accepted config_drive_format is 'iso9660'.
 #  (boolean value)
 #config_drive_inject_password=false
+config_drive_inject_password={{ compute.get('config_drive', {}).get('inject_password', False)|lower }}
 
 #
 # Volume attach retry count
@@ -5454,6 +5473,7 @@
 #filesystems =
 
 
+{% if compute.ironic is defined -%}
 [ironic]
 #
 # Configuration options for Ironic driver (Bare Metal).
@@ -5471,7 +5491,7 @@
 #
 
 # URL override for the Ironic API endpoint. (string value)
-#api_endpoint=http://ironic.example.org:6385/
+api_endpoint=http://{{ compute.ironic.host }}:{{ compute.ironic.port }}
 
 #
 # The number of times to retry when a request conflicts.
@@ -5517,13 +5537,13 @@
 
 # Authentication type to load (string value)
 # Deprecated group/name - [ironic]/auth_plugin
-#auth_type=<None>
+auth_type={{ compute.ironic.auth_type }}
 
 # Config Section from which to load plugin specific options (string value)
 #auth_section=<None>
 
 # Authentication URL (string value)
-#auth_url=<None>
+auth_url=http://{{ compute.identity.host }}:{{ compute.identity.port }}/v3
 
 # Domain ID to scope to (string value)
 #domain_id=<None>
@@ -5535,13 +5555,13 @@
 #project_id=<None>
 
 # Project name to scope to (string value)
-#project_name=<None>
+project_name={{ compute.identity.tenant }}
 
 # Domain ID containing project (string value)
 #project_domain_id=<None>
 
 # Domain name containing project (string value)
-#project_domain_name=<None>
+project_domain_name={{ compute.ironic.project_domain_name }}
 
 # Trust ID (string value)
 #trust_id=<None>
@@ -5551,16 +5571,17 @@
 
 # Username (string value)
 # Deprecated group/name - [ironic]/user-name
-#username=<None>
+username={{ compute.ironic.user }}
 
 # User's domain id (string value)
 #user_domain_id=<None>
 
 # User's domain name (string value)
-#user_domain_name=<None>
+user_domain_name={{ compute.ironic.user_domain_name }}
 
 # User's password (string value)
-#password=<None>
+password={{ compute.ironic.password }}
+{%- endif %}
 
 
 [key_manager]
@@ -5878,11 +5899,11 @@
 #
 # From nova.conf
 #
-cpu_mode = host-passthrough
+cpu_mode = {{ compute.get('cpu_mode', 'host-passthrough') }}
 virt_type = kvm
 inject_partition=-2
 inject_password=True
-disk_cachemodes="network=writeback,block=none"
+disk_cachemodes="{{ compute.get('disk_cachemodes', 'network=writeback,block=none') }}"
 block_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_NON_SHARED_INC
 live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST
 inject_key=True
@@ -5896,6 +5917,15 @@
 rbd_secret_uuid={{ compute.ceph.secret_uuid }}
 inject_password=false
 inject_key=false
+{%- elif compute.get('lvm', {}).ephemeral is defined %}
+images_type=lvm
+images_volume_group={{ compute.lvm.images_volume_group }}
+{%- if compute.lvm.volume_clear is defined %}
+volume_clear={{ compute.lvm.volume_clear }}
+{%- endif %}
+{%- if compute.lvm.volume_clear_size is defined %}
+volume_clear_size={{ compute.lvm.volume_clear_size }}
+{%- endif %}
 {%- endif %}
 
 {%- if compute.get('libvirt', {}).uri is defined %}
@@ -7913,6 +7943,23 @@
 # message (floating point value)
 #rpc_retry_delay=0.25
 
+{# rabbitmq ssl configuration #}
+{%- if compute.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+
+{%- if compute.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ compute.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if compute.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ compute.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs = {{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
 
 [oslo_messaging_zmq]
 
@@ -9036,7 +9083,9 @@
 # * update html5proxy_base_url
 # * update server_proxyclient_address
 enabled = false
+{%- if compute.vncproxy_url is defined %}
 html5proxy_base_url = {{ compute.vncproxy_url }}/spice_auto.html
+{%- endif %}
 #
 # From nova.conf
 #
@@ -9377,6 +9426,11 @@
 
 
 [upgrade_levels]
+{%- if compute.upgrade_levels is defined %}
+{%- for key, value in compute.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
 #
 # upgrade_levels options are used to set version cap for RPC
 # messages sent between different nova services.
@@ -9817,10 +9871,16 @@
 # From nova.conf
 #
 enabled = true
+{%- if compute.vncproxy_url is defined %}
 novncproxy_base_url={{ compute.vncproxy_url }}/vnc_auto.html
+{%- endif %}
+{%- if compute.bind is defined and compute.bind.vnc_port is defined %}
 novncproxy_port={{ compute.bind.vnc_port }}
+{%- endif %}
 vncserver_listen=0.0.0.0
+{%- if compute.bind is defined and compute.bind.vnc_address is defined %}
 vncserver_proxyclient_address={{ compute.bind.vnc_address }}
+{%- endif %}
 keymap = {{ compute.get('vnc_keymap', 'en-us') }}
 
 #
@@ -10043,6 +10103,7 @@
 #   there is a new enough libvirt and the backend storage supports it)
 #  (boolean value)
 #disable_libvirt_livesnapshot=true
+disable_libvirt_livesnapshot={{ compute.get('workaround', {}).get('disable_libvirt_livesnapshot', True)|lower }}
 
 #
 # Enable handling of events emitted from compute drivers.
@@ -10503,6 +10564,9 @@
 # Time in secs to wait for a block device to be created (integer value)
 # Minimum value: 1
 #block_device_creation_timeout=10
+{%- if compute.block_device_creation_timeout is defined %}
+block_device_creation_timeout = {{ compute.block_device_creation_timeout }}
+{%- endif  %}
 
 #
 # Maximum size in bytes of kernel or ramdisk images.
diff --git a/nova/files/ocata/nova-controller.conf.Debian b/nova/files/ocata/nova-controller.conf.Debian
index eddcbb3..ee7bcc3 100644
--- a/nova/files/ocata/nova-controller.conf.Debian
+++ b/nova/files/ocata/nova-controller.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
+{%- from "nova/map.jinja" import controller, system_cacerts_file with context %}
 [DEFAULT]
 
 #
@@ -3035,16 +3035,19 @@
 #rpc_response_timeout=60
 rpc_response_timeout=3600
 
+{%- set rabbit_port = controller.message_queue.get('port', 5671 if controller.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
+
 {%- if controller.message_queue.members is defined %}
 transport_url = rabbit://{% for member in controller.message_queue.members -%}
-                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+                             {{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
                              /{{ controller.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ controller.message_queue.port }}/{{ controller.message_queue.virtual_host }}
+transport_url = rabbit://{{ controller.message_queue.user }}:{{ controller.message_queue.password }}@{{ controller.message_queue.host }}:{{ rabbit_port}}/{{ controller.message_queue.virtual_host }}
 {%- endif %}
 
+
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
 # include amqp and zmq. (string value)
 # This option is deprecated for removal.
@@ -3413,7 +3416,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api?charset=utf8
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}_api?charset=utf8{%- if controller.database.get('ssl',{}).get('enabled',False) %}&ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 # The SQLAlchemy connection string to use to connect to the database. (string
 # value)
@@ -4103,7 +4106,9 @@
 # By default there is no availability zone restriction on volume attach.
 #  (boolean value)
 #cross_az_attach=true
-
+{%- if controller.cross_az_attach is defined %}
+cross_az_attach={{ controller.cross_az_attach }}
+{%- endif %}
 
 [cloudpipe]
 
@@ -4453,7 +4458,7 @@
 db_retry_interval = 1
 connection_debug = 10
 pool_timeout = 120
-connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}?charset=utf8
+connection = {{ controller.database.engine }}+pymysql://{{ controller.database.user }}:{{ controller.database.password }}@{{ controller.database.host }}/{{ controller.database.name }}?charset=utf8{%- if controller.database.get('ssl',{}).get('enabled',False) %}&ssl_ca={{ controller.database.ssl.get('cacert_file', system_cacerts_file) }}{% endif %}
 
 # If True, SQLite uses synchronous mode. (boolean value)
 # Deprecated group/name - [DEFAULT]/sqlite_synchronous
@@ -7070,7 +7075,7 @@
 project_domain_name = Default
 user_domain_name = Default
 auth_url = http://{{ controller.identity.host }}:35357/v3
-{% if pillar.neutron is defined %}
+{% if pillar.neutron is defined and pillar.neutron.server is defined %}
 password={{ pillar.neutron.server.identity.password }}
 project_name={{ pillar.neutron.server.identity.tenant }}
 username={{ pillar.neutron.server.identity.user }}
@@ -7929,6 +7934,22 @@
 # message (floating point value)
 #rpc_retry_delay=0.25
 
+{# rabbitmq ssl configuration #}
+{%- if controller.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+
+{%- if controller.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ controller.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+
+{%- if controller.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ controller.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs = {{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
 
 [oslo_messaging_zmq]
 
@@ -9395,6 +9416,11 @@
 
 
 [upgrade_levels]
+{%- if controller.upgrade_levels is defined %}
+{%- for key, value in controller.upgrade_levels.iteritems() %}
+{{ key }}={{ value }}
+{%- endfor %}
+{%- endif %}
 #
 # upgrade_levels options are used to set version cap for RPC
 # messages sent between different nova services.
@@ -10527,6 +10553,9 @@
 # Time in secs to wait for a block device to be created (integer value)
 # Minimum value: 1
 #block_device_creation_timeout=10
+{%- if controller.block_device_creation_timeout is defined %}
+block_device_creation_timeout = {{ controller.block_device_creation_timeout }}
+{%- endif  %}
 
 #
 # Maximum size in bytes of kernel or ramdisk images.
diff --git a/nova/files/ocata/nova-placement-api.conf b/nova/files/ocata/nova-placement-api.conf
index 02e88cc..e7cde11 100644
--- a/nova/files/ocata/nova-placement-api.conf
+++ b/nova/files/ocata/nova-placement-api.conf
@@ -14,7 +14,7 @@
     </IfVersion>
 
     ErrorLog /var/log/apache2/nova_placement_error.log
-    CustomLog /var/log/apache2/nova_placement_access.log combined
+    CustomLog /var/log/apache2/nova_placement_access.log "%v:%p %h %l %u %t \"%r\" %>s %D %O \"%{Referer}i\" \"%{User-Agent}i\""
 
     <Directory /usr/bin>
         <IfVersion >= 2.4>
diff --git a/nova/map.jinja b/nova/map.jinja
index 4d56253..f725112 100644
--- a/nova/map.jinja
+++ b/nova/map.jinja
@@ -1,3 +1,8 @@
+{%- set system_cacerts_file = salt['grains.filter_by']({
+    'Debian': '/etc/ssl/certs/ca-certificates.crt',
+    'RedHat': '/etc/pki/tls/certs/ca-bundle.crt'
+})%}
+
 {% set compute_bind_defaults = {
     'vnc_address': '10.0.0.10',
     'vnc_port': '6080',
@@ -38,44 +43,76 @@
     },
 }, merge=pillar.nova.get('client', {})) %}
 
-{% set compute = salt['grains.filter_by']({
-    'Debian': {
-        'pkgs': ['nova-common', 'nova-compute-kvm', 'python-novaclient', 'pm-utils', 'sysfsutils', 'sg3-utils', 'libvirt-bin', 'python-memcache', 'qemu-kvm','python-guestfs', 'gettext-base'],
-        'services': ['nova-compute'],
-        'libvirt_config': 'libvirtd.conf',
-        'libvirt_bin': '/etc/default/libvirt-bin',
-        'libvirt_service': 'libvirt-bin',
-        'bind': compute_bind_defaults,
-        'debug': false,
-        'libvirt': [],
-        'notification': false,
-        'availability_zone': None,
-        'aggregates': [],
-        'identity': {
-            'region': 'RegionOne'
+{%- load_yaml as compute_defaults %}
+Debian:
+  pkgs:
+  - nova-common
+  - nova-compute-kvm
+  - python-novaclient
+  - pm-utils
+  - sysfsutils
+  - sg3-utils
+  - libvirt-bin
+  - python-memcache
+  - qemu-kvm
+  - python-guestfs
+  - gettext-base
+  {%- if pillar.nova.compute is defined and pillar.nova.compute.get('networking', 'default') == "contrail" and pillar.nova.compute.get('version', 'ocata') not in ["juno", "kilo", "liberty", "mitaka", "newton"] %}
+  - contrail-nova-driver
+  {%- endif %}
+  services:
+  - nova-compute
+  libvirt_config: libvirtd.conf
+  libvirt_bin: "/etc/default/libvirt-bin"
+  libvirt_service: libvirt-bin
+  bind: compute_bind_defaults
+  debug: false
+  libvirt: []
+  instances_path: "$state_path/instances"
+  notification: false
+  availability_zone:
+  aggregates: []
+  identity:
+    region: RegionOne
+  network:
+    region: RegionOne
+  heal_instance_info_cache_interval: '60'
+RedHat:
+  pkgs:
+  - openstack-nova-compute
+  - python-novaclient
+  - python-nova
+  - sysfsutils
+  - sg3_utils
+  services:
+  - messagebus
+  - openstack-nova-compute
+  - libvirtd
+  libvirt_config: libvirt.conf
+  libvirt_bin: "/etc/sysconfig/libvirtd"
+  libvirt_service: libvirtd
+  bind: compute_bind_defaults
+  debug: false
+  libvirt: []
+  notification: false
+  availability_zone:
+  identity:
+    region: RegionOne
+  network:
+    region: RegionOne
+  heal_instance_info_cache_interval: '60'
+{%- endload %}
+
+{% set compute = salt["grains.filter_by"](compute_defaults, merge=pillar.nova.get("compute", {})) %}
+
+{% set monitoring = salt['grains.filter_by']({
+    'default': {
+        'disk': {
+              'warn': '15%',
+              'crit': '5%',
         },
-        'network': {
-            'region': 'RegionOne'
+        'error_log_rate': {
+              'warn': 0.2,
         },
-        'heal_instance_info_cache_interval': '60',
     },
-    'RedHat': {
-        'pkgs': ['openstack-nova-compute', 'python-novaclient', 'python-nova', 'sysfsutils', 'sg3_utils'],
-        'services': ['messagebus', 'openstack-nova-compute', 'libvirtd'],
-        'libvirt_config': 'libvirt.conf',
-        'libvirt_bin': '/etc/sysconfig/libvirtd',
-        'libvirt_service': 'libvirtd',
-        'bind': compute_bind_defaults,
-        'debug': false,
-        'libvirt': [],
-        'notification': false,
-        'availability_zone': None,
-        'identity': {
-            'region': 'RegionOne'
-        },
-        'network': {
-            'region': 'RegionOne'
-        },
-        'heal_instance_info_cache_interval': '60',
-    },
-}, merge=pillar.nova.get('compute', {})) %}
+}, grain='os_family', merge=salt['pillar.get']('nova:monitoring')) %}
diff --git a/nova/meta/grafana.yml b/nova/meta/grafana.yml
index 212d2f3..7c352c3 100644
--- a/nova/meta/grafana.yml
+++ b/nova/meta/grafana.yml
@@ -1,11 +1,22 @@
 dashboard:
-  nova:
+  nova_prometheus:
+    datasource: prometheus
+    format: json
+    template: nova/files/grafana_dashboards/nova_prometheus.json
+  hypervisor_prometheus:
+    datasource: prometheus
+    format: json
+    template: nova/files/grafana_dashboards/hypervisor_prometheus.json
+  nova_influxdb:
+    datasource: influxdb
     format: json
     template: nova/files/grafana_dashboards/nova_influxdb.json
-  hypervisor:
+  hypervisor_influxdb:
+    datasource: influxdb
     format: json
     template: nova/files/grafana_dashboards/hypervisor_influxdb.json
   service_level:
+    datasource: influxdb
     row:
       nova-service-level:
         title: Nova Service Levels
@@ -67,6 +78,7 @@
                 rawQuery: true
                 query: SELECT count(max) FROM openstack_nova_http_response_times WHERE environment_label = '$environment' AND http_status = '5xx' AND $timeFilter
   main:
+    datasource: influxdb
     row:
       ost-control-plane:
         title: OpenStack Control Plane
diff --git a/nova/meta/heka.yml b/nova/meta/heka.yml
index 6121039..bf4f3c6 100644
--- a/nova/meta/heka.yml
+++ b/nova/meta/heka.yml
@@ -1,3 +1,5 @@
+{% from "nova/map.jinja" import controller with context %}
+{%- set apache_wsgi = controller.get('enabled') and controller.version not in ('juno', 'kilo', 'liberty', 'mitaka', 'newton') %}
 log_collector:
   decoder:
     nova:
@@ -11,6 +13,16 @@
       module_file: /usr/share/lma_collector/decoders/libvirt_log.lua
       module_dir: /usr/share/lma_collector/common;/usr/share/heka/lua_modules
     {%- endif %}
+    {%- if apache_wsgi %}
+    nova_placement_wsgi:
+      engine: sandbox
+      module_file: /usr/share/lma_collector/decoders/apache_wsgi_log.lua
+      module_dir: /usr/share/lma_collector/common;/usr/share/heka/lua_modules
+      config:
+        logger: openstack.nova
+        apache_log_pattern: >-
+          %v:%p %h %l %u %t \"%r\" %>s %D %O \"%{Referer}i\" \"%{User-Agent}i\"
+    {%- endif %}
   splitter:
     nova:
       engine: token
@@ -33,6 +45,16 @@
       decoder: "libvirt_decoder"
       splitter: "TokenSplitter"
     {%- endif %}
+    {%- if apache_wsgi %}
+    nova_placement_wsgi_log:
+      engine: logstreamer
+      log_directory: "/var/log/apache2"
+      file_match: 'nova_placement_access\.log'
+      differentiator: ['nova-placement-wsgi']
+      priority: ["^Seq"]
+      decoder: "nova_placement_wsgi_decoder"
+      splitter: "TokenSplitter"
+    {%- endif %}
 metric_collector:
   trigger:
     nova_logs_error:
diff --git a/nova/meta/prometheus.yml b/nova/meta/prometheus.yml
new file mode 100644
index 0000000..71d6ac9
--- /dev/null
+++ b/nova/meta/prometheus.yml
@@ -0,0 +1,235 @@
+{% from "nova/map.jinja" import controller, compute, monitoring with context %}
+
+{%- set is_controller = controller.get('enabled', False) %}
+{%- set is_compute = compute.get('enabled', False) %}
+
+{%- if is_controller or is_compute %}
+{%- if is_compute and
+       exporters is defined %}
+{%- set packages = exporters.get('libvirt', {}).get('packages', ('libvirt-exporter', )) %}
+  {%- load_yaml as new_exporters_cfg %}
+exporters:
+  libvirt:
+    enabled: true
+{%- if packages is defined %}
+    packages:
+    {% for pkg in packages %}
+    - {{ pkg }}
+    {% endfor %}
+{%- endif %}
+    services:
+      qemu:
+        enabled: true
+        bind:
+          address: 0.0.0.0
+          port: 9177
+  {%- endload %}
+{{ new_exporters_cfg|yaml(False) }}
+{%- endif %}
+
+server:
+  alert:
+{%- if is_controller %}
+{% raw %}
+    NovaAPIDown:
+      if: >-
+        openstack_api_check_status{service=~"nova.*|placement"} == 0
+      for: 2m
+      labels:
+        severity: down
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "Endpoint check for '{{ $labels.service }}' is down"
+        description: >-
+            Endpoint check for '{{ $labels.service }}' is down for 2 minutes
+    NovaSomeServicesDown:
+      if: >-
+        openstack_nova_services{state="down",service=~"nova-cert|nova-conductor|nova-consoleauth|nova-scheduler"} > 0 and ignoring(state) openstack_nova_services{state="up",service=~"nova-cert|nova-conductor|nova-consoleauth|nova-scheduler"} >= 2
+      for: 2m
+      labels:
+        severity: warning
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "Some {{ $labels.service }} services down"
+        description: >-
+            {{ $value }} '{{ $labels.service }}' service(s) is/are down for 2 minutes
+    NovaOnlyOneServiceUp:
+      if: >-
+        openstack_nova_services{state="up",service=~"nova-cert|nova-conductor|nova-consoleauth|nova-scheduler"} == 1 and ignoring(state) openstack_nova_services{state=~"down|disabled",service=~"nova-cert|nova-conductor|nova-consoleauth|nova-scheduler"} > 0
+      for: 2m
+      labels:
+        severity: critical
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "Only one {{ $labels.service }} service up"
+        description: >-
+            Only one '{{ $labels.service }}' service is up for 2 minutes
+    NovaAllServicesDown:
+      if: >-
+        openstack_nova_services{state="up",service=~"nova-cert|nova-conductor|nova-consoleauth|nova-scheduler"} == 0
+      for: 2m
+      labels:
+        severity: down
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "All {{ $labels.service }} services down"
+        description: >-
+            All '{{ $labels.service }}' services are down for 2 minutes
+    NovaSomeComputesDown:
+      if: >-
+        openstack_nova_services{state="down",service=~"nova-compute"} > 0
+      for: 2m
+      labels:
+        severity: warning
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "Some {{ $labels.service }} services down"
+        description: >-
+            {{ $value }} '{{ $labels.service }}' service(s) is/are down for 2 minutes
+    NovaMajorityComputesDown:
+      if: >-
+        openstack_nova_services_percent{state="down",service=~"nova-compute"} > 50
+      for: 2m
+      labels:
+        severity: critical
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "Only one {{ $labels.service }} service up"
+        description: >-
+            Only one '{{ $labels.service }}' service is up for 2 minutes
+    NovaAllComputesDown:
+      if: >-
+        openstack_nova_services{state="up",service=~"nova-compute"} == 0
+      for: 2m
+      labels:
+        severity: down
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: "All {{ $labels.service }} services down"
+        description: >-
+            All '{{ $labels.service }}' services are down for 2 minutes
+    NovaTotalFreeVCPUsLow:
+      if: >-
+        (100.0 * openstack_nova_total_free_vcpus) / (openstack_nova_total_free_vcpus + openstack_nova_total_used_vcpus) < 10.0
+      for: 1m
+      labels:
+        severity: warning
+        service: nova
+      annotations:
+        summary: "VCPU low limit for new instances"
+        description: >-
+            VPCU low limit for 1 minutes
+    NovaTotalFreeMemoryLow:
+      if: >-
+        (100.0 * openstack_nova_total_free_ram) / (openstack_nova_total_free_ram + openstack_nova_total_used_ram) < 10.0
+      for: 1m
+      labels:
+        severity: warning
+        service: nova
+      annotations:
+        summary: "Memory low limit for new instances"
+        description: >-
+            Memory low limit for 1 minutes
+    NovaTotalFreeVCPUsShortage:
+      if: >-
+        (100.0 * openstack_nova_total_free_vcpus) / (openstack_nova_total_free_vcpus + openstack_nova_total_used_vcpus) < 2.0
+      for: 1m
+      labels:
+        severity: critical
+        service: nova
+      annotations:
+        summary: "VCPU shortage for new instances"
+        description: >-
+            VPCU shortage for 1 minutes
+    NovaTotalFreeMemoryShortage:
+      if: >-
+        (100.0 * openstack_nova_total_free_ram) / (openstack_nova_total_free_ram + openstack_nova_total_used_ram) < 2.0
+      for: 1m
+      labels:
+        severity: critical
+        service: nova
+      annotations:
+        summary: "Memory shortage for new instances"
+        description: >-
+            Memory shortage for 1 minutes
+    NovaAggregatesFreeVCPUsLow:
+      if: >-
+        (100.0 * openstack_nova_aggregate_free_vcpus) / (openstack_nova_aggregate_free_vcpus + openstack_nova_aggregate_used_vcpus) < 10.0
+      for: 1m
+      labels:
+        severity: warning
+        service: nova
+        aggregate: "{{ $labels.aggregate }}"
+      annotations:
+        summary: "VCPU low limit for new instances on aggregate {{ $labels.aggregate }}"
+        description: >-
+            VPCU low limit for 1 minutes on aggregate {{ $labels.aggregate }}
+    NovaAggregatesFreeMemoryLow:
+      if: >-
+        (100.0 * openstack_nova_aggregate_free_ram) / (openstack_nova_aggregate_free_ram + openstack_nova_aggregate_used_ram) < 10.0
+      for: 1m
+      labels:
+        severity: warning
+        service: nova
+        aggregate: "{{ $labels.aggregate }}"
+      annotations:
+        summary: "Memory low limit for new instances on aggregate {{ $labels.aggregate }}"
+        description: >-
+            Memory low limit for 1 minutes on aggregate {{ $labels.aggregate }}
+    NovaAggregatesFreeVCPUsShortage:
+      if: >-
+        (100.0 * openstack_nova_aggregate_free_vcpus) / (openstack_nova_aggregate_free_vcpus + openstack_nova_aggregate_used_vcpus) < 2.0
+      for: 1m
+      labels:
+        severity: critical
+        service: nova
+        aggregate: "{{ $labels.aggregate }}"
+      annotations:
+        summary: "VCPU shortage for new instances on aggregate {{ $labels.aggregate }}"
+        description: >-
+            VPCU shortage for 1 minutes on aggregate {{ $labels.aggregate }}
+    NovaAggregatesFreeMemoryShortage:
+      if: >-
+        (100.0 * openstack_nova_aggregate_free_ram) / (openstack_nova_aggregate_free_ram + openstack_nova_aggregate_used_ram) < 2.0
+      for: 1m
+      labels:
+        severity: critical
+        service: nova
+        aggregate: "{{ $labels.aggregate }}"
+      annotations:
+        summary: "Memory shortage for new instances on aggregate {{ $labels.aggregate }}"
+        description: >-
+            Memory shortage for 1 minutes on aggregate {{ $labels.aggregate }}
+{%- endraw %}
+{%- endif %}
+    NovaErrorLogsTooHigh:
+      {%- set log_threshold = monitoring.error_log_rate.warn|float %}
+      if: >-
+        sum(rate(log_messages{service="nova",level=~"error|emergency|fatal"}[5m])) without (level) > {{ log_threshold }}
+{%- raw %}
+      labels:
+        severity: warning
+        service: "{{ $labels.service }}"
+      annotations:
+        summary: 'Too many errors in {{ $labels.service }} logs'
+        description: 'The rate of errors in {{ $labels.service }} logs over the last 5 minutes is too high on node {{ $labels.host }} (current value={{ $value }}, threshold={%- endraw %}{{ log_threshold }}).'
+
+{%- if is_compute %}
+{%- raw %}
+    NovaLibvirtDown:
+      if: >-
+        max(libvirt_up) by (host) == 0
+      for: 2m
+      labels:
+        severity: down
+        service: "libvirt"
+      annotations:
+        summary: "libvirt check on '{{ $labels.host }}' is down"
+        description: >-
+            libvirt check on '{{ $labels.host }}' is down for 2 minutes
+{%- endraw %}
+{%- if exporters is defined %}
+{%- include "prometheus/_exporters_config.sls" %}
+{%- endif %}
+{%- endif %}
+{%- endif %}
diff --git a/nova/meta/sensu.yml b/nova/meta/sensu.yml
index a92865e..5497b20 100644
--- a/nova/meta/sensu.yml
+++ b/nova/meta/sensu.yml
@@ -1,5 +1,4 @@
-{%- from "nova/map.jinja" import controller with context %}
-{%- from "nova/map.jinja" import compute with context %}
+{%- from "nova/map.jinja" import controller,compute,monitoring with context %}
 {%- if pillar.nova.controller is defined %}
 {%- set region = controller.identity.region %}
 {%- endif %}
@@ -51,7 +50,7 @@
     subscribers:
     - local-nova-compute
   local_linux_storage_nova_instances_usage:
-    command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_disk -w 15% -c 5% -p /var/lib/nova/instances"
+    command: "PATH=$PATH:/usr/lib64/nagios/plugins:/usr/lib/nagios/plugins check_disk -w {{ monitoring.disk.warn }} -c {{ monitoring.disk.crit }} -p /var/lib/nova/instances"
     interval: 60
     occurrences: 1
     subscribers:
diff --git a/nova/meta/sphinx.yml b/nova/meta/sphinx.yml
index 9ccaa84..6a3a4cb 100644
--- a/nova/meta/sphinx.yml
+++ b/nova/meta/sphinx.yml
@@ -13,9 +13,11 @@
         virtualization:
           name: "Virtualization type"
           value: {{ compute.virtualization }}
+        {%- if compute.vncproxy_url is defined %}
         vncproxy_url:
           name: "VNC proxy URL"
           value: {{ compute.vncproxy_url }}
+        {%- endif %}
         {%- if compute.reserved_host_memory_mb is defined %}
         reserved_host_memory_mb:
           name: "Reserved Host Memmory"
@@ -27,9 +29,11 @@
         network_host:
           name: "Network service"
           value: {{ compute.network.host }}:{{ compute.network.port }}
+        {%- if compute.image is defined %}
         glance_host:
           name: "Image service"
           value: {{ compute.image.host }}:{{ compute.image.port }}
+        {%- endif %}
         message_queue_ip:
           name: "Message queue"
           value: {{ compute.message_queue.user }}@{{ compute.message_queue.host }}:{{ compute.message_queue.port }}{{ compute.message_queue.virtual_host }}
diff --git a/nova/meta/telegraf.yml b/nova/meta/telegraf.yml
new file mode 100644
index 0000000..bda7082
--- /dev/null
+++ b/nova/meta/telegraf.yml
@@ -0,0 +1,15 @@
+{%- from "nova/map.jinja" import controller with context %}
+{%- if controller is defined and controller.get('enabled', False) %}
+{%- if controller.cpu_allocation_ratio is defined %}
+remote_agent:
+  input:
+    openstack:
+      cpu_ratio: "{{ controller.cpu_allocation_ratio }}"
+{%- endif %}
+agent:
+  input:
+    http_response:
+      nova-api:
+        address: "http://{{ controller.bind.private_address|replace('0.0.0.0', '127.0.0.1') }}:8774/"
+        expected_code: 200
+{%- endif %}
diff --git a/tests/pillar/compute_cluster.sls b/tests/pillar/compute_cluster.sls
index c3a2485..b027101 100644
--- a/tests/pillar/compute_cluster.sls
+++ b/tests/pillar/compute_cluster.sls
@@ -7,6 +7,7 @@
       mount_points:
       - path: /mnt/hugepages_1GB
     virtualization: kvm
+    disk_cachemodes: network=writeback,block=none
     heal_instance_info_cache_interval: 60
     vncproxy_url: openstack:6080
     report_interval: 60
@@ -66,3 +67,12 @@
         port: 11211
     libvirt:
       hw_disk_discard: unmap
+    upgrade_levels:
+      compute: liberty
+    libvirt_service_group: libvirtd
+    lvm:
+      ephemeral: yes
+      images_volume_group: nova_vg
+      volume_clear: zero
+      volume_clear_size: 0
+
diff --git a/tests/pillar/compute_single_config_drive_options.sls b/tests/pillar/compute_single_config_drive_options.sls
new file mode 100644
index 0000000..a181f8b
--- /dev/null
+++ b/tests/pillar/compute_single_config_drive_options.sls
@@ -0,0 +1,54 @@
+nova:
+  compute:
+    version: mitaka
+    enabled: true
+    virtualization: kvm
+    heal_instance_info_cache_interval: 60
+    vncproxy_url: openstack:6080
+    vnc_keymap: en-gb
+    resume_guests_state_on_host_boot: False
+    bind:
+      vnc_address: 127.0.0.1
+      vnc_port: 6080
+      vnc_name: 0.0.0.0
+    database:
+      engine: mysql
+      host: 127.0.0.1
+      port: 3306
+      name: nova
+      user: nova
+      password: password
+    identity:
+      engine: keystone
+      region: RegionOne
+      host: 127.0.0.1
+      port: 35357
+      user: nova
+      password: password
+      tenant: service
+    message_queue:
+      engine: rabbitmq
+      host: 127.0.0.1
+      port: 5672
+      user: openstack
+      password: password
+      virtual_host: '/openstack'
+    image:
+      engine: glance
+      host: 127.0.0.1
+      port: 9292
+    network:
+      engine: neutron
+      region: RegionOne
+      host: 127.0.0.1
+      port: 9696
+      password: password
+    cache:
+      engine: memcached
+      members:
+      - host: 127.0.0.1
+        port: 11211
+    config_drive:
+      cdrom: True
+      format: iso9660
+      inject_password: True
\ No newline at end of file
diff --git a/tests/pillar/control_cluster.sls b/tests/pillar/control_cluster.sls
index 84da744..07cb450 100644
--- a/tests/pillar/control_cluster.sls
+++ b/tests/pillar/control_cluster.sls
@@ -42,7 +42,7 @@
       password: password
       virtual_host: '/openstack'
     glance:
-      host: 
+      host:
       port: 9292
     network:
       engine: neutron
@@ -59,7 +59,8 @@
       filter_factory: 'keystonemiddleware.audit:filter_factory'
       map_file: '/etc/pycadf/nova_api_audit_map.conf'
     policy:
-      context_is_admin: 'role:admin or role:administrator'
+      'context_is_admin': 'role:admin or role:administrator'
       'compute:create': 'rule:admin_or_owner'
       'compute:create:attach_network':
-
+    upgrade_levels:
+      compute: liberty
diff --git a/tests/pillar/control_single.sls b/tests/pillar/control_single.sls
index ce33f8c..78eaa40 100644
--- a/tests/pillar/control_single.sls
+++ b/tests/pillar/control_single.sls
@@ -57,6 +57,6 @@
       - host: 127.0.0.1
         port: 11211
     policy:
-      context_is_admin: 'role:admin or role:administrator'
+      'context_is_admin': 'role:admin or role:administrator'
       'compute:create': 'rule:admin_or_owner'
       'compute:create:attach_network':
diff --git a/tests/pillar/ssl.sls b/tests/pillar/ssl.sls
new file mode 100644
index 0000000..07f4ad6
--- /dev/null
+++ b/tests/pillar/ssl.sls
@@ -0,0 +1,21 @@
+include:
+  - .compute_single
+  - .control_single
+
+nova:
+  controller:
+    database:
+      ssl:
+        enabled: True
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True
+  compute:
+    database:
+      ssl:
+        enabled: True
+    message_queue:
+      port: 5671
+      ssl:
+        enabled: True