Formulas testing revision 2019/10

Partial cherry-pick of fixes in c4b6ed93cbcbf8115d1887bd60048524c95f6d20

Related: PROD-32744
Related: PROD-33633
Related: PROD-33634
Related: PROD-33698
Related: PROD-33984

Change-Id: I8bcf38769b69d0677d97f6207a147b6b6786ee4b
diff --git a/.kitchen.yml b/.kitchen.yml
index 0e21280..6e0551b 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -8,7 +8,7 @@
   name: salt_solo
   salt_install: bootstrap
   salt_bootstrap_url: https://bootstrap.saltstack.com
-  salt_version: <%=ENV['SALT_VERSION'] || 'latest'%>
+  salt_version: latest
   require_chef: false
   log_level: error
   formula: keystone
@@ -17,77 +17,101 @@
   dependencies:
     - name: linux
       repo: git
-      source: https://github.com/salt-formulas/salt-formula-linux
-    - name: mysql
-      repo: git
-      source: https://github.com/salt-formulas/salt-formula-mysql
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/linux
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
     - name: apache
       repo: git
-      source: https://github.com/salt-formulas/salt-formula-apache
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/apache
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
     - name: memcached
       repo: git
-      source: https://github.com/salt-formulas/salt-formula-memcached
-    - name: cadf
-      repo: git
-      source: https://github.com/salt-formulas/salt-formula-cadf
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/memcached
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
     - name: rabbitmq
       repo: git
-      source: https://github.com/salt-formulas/salt-formula-rabbitmq
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/rabbitmq
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
+    - name: oslo_templates
+      repo: git
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/oslo-templates
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
+    - name: mysql
+      repo: git
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/mysql
+      branch: <%=ENV['GERRIT_BRANCH'] || 'release/2019.2.0' %>
   state_top:
     base:
       "*":
         - linux
-        - mysql
         - keystone
   pillars:
+    ssl.sls:
+      keystone:
+        server:
+          identity:
+            protocol: https
+          database:
+            x509:
+              enabled: True
+              ca_file: /etc/keystone/ssl/mysql/ca-cert.pem
+              key_file: /etc/keystone/ssl/mysql/client-key.pem
+              cert_file: /etc/keystone/ssl/mysql/client-cert.pem
+              cacert: |
+                -----BEGIN CERTIFICATE-----
+                MIIFzzCCA7egAwIBAgIIe7zZ8hCvkgowDQYJKoZIhvcNAQELBQAwSjELMAkGA1UE
+                -----END CERTIFICATE-----
+              cert: |
+                -----BEGIN CERTIFICATE-----
+                MIIGSjCCBDKgAwIBAgIJAIHRPs2rZbLvMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV
+                -----END CERTIFICATE-----
+              key: |
+                -----BEGIN RSA PRIVATE KEY-----
+                MIIJKQIBAAKCAgEAq0m4kOIITliYea07yJnlSRNY0o6NaykiteSfHGauiub4lNQJ
+                -----END RSA PRIVATE KEY-----
+            ssl:
+              enabled: True
+              cacert_file: /etc/keystone/ssl/mysql/ca-cert.pem
+              cacert: |
+                -----BEGIN CERTIFICATE-----
+                MIIFzzCCA7egAwIBAgIIe7zZ8hCvkgowDQYJKoZIhvcNAQELBQAwSjELMAkGA1UE
+                -----END CERTIFICATE-----
+          message_queue:
+            port: 5671
+            ssl:
+              enabled: True
+              cacert_file: /etc/keystone/ssl/mysql/ca-cert.pem
+              cacert: |
+                -----BEGIN CERTIFICATE-----
+                MIIFzzCCA7egAwIBAgIIe7zZ8hCvkgowDQYJKoZIhvcNAQELBQAwSjELMAkGA1UE
+                -----END CERTIFICATE-----
     top.sls:
       base:
         "*":
           - linux_repo_openstack
           - keystone
-          - mysql_platform_specific
-    mysql_platform_specific.sls: {}
-  pillars-from-files:
-    linux_repo_openstack.sls: tests/pillar/repo_mos9.sls
+          - release
 
 verifier:
   name: inspec
   sudo: true
 
+docker_images:
+  - &xenial-20177 <%=ENV['IMAGE_XENIAL_20177'] || 'docker-dev-local.docker.mirantis.net/mirantis/drivetrain/salt-formulas-ci/salt-formulas-ci-xenial-2017.7:latest'%>
+
 platforms:
-  - name: <%=ENV['PLATFORM'] ||  'saltstack-ubuntu-xenial-salt-stable' %>
+  - name: xenial-2017.7
     driver_config:
-      image: <%=ENV['PLATFORM'] || 'epcim/salt-formulas:saltstack-ubuntu-xenial-salt-stable'%>
+      image: *xenial-20177
       platform: ubuntu
-    provisioner:
-      pillars:
-        mysql_platform_specific.sls:
-          mysql:
-            server:
-              # workaround, formula not yet handle platform default version
-              version: "<%=ENV['MYSQL_VERSION'] || '5.7'%>"
 
 suites:
-
-  - name: single
+<% for os_version in ['pike','queens'] %>
+<% for mysql_version in ['5.7'] %>
+  - name: single_<%=mysql_version%>_<%=os_version%>
     provisioner:
       pillars-from-files:
         keystone.sls: tests/pillar/single.sls
-
-  - name: single_domain
-    provisioner:
-      pillars-from-files:
-        keystone.sls: tests/pillar/single_domain.sls
-
-  - name: single_fernet
-    provisioner:
-      pillars-from-files:
-        keystone.sls: tests/pillar/single_fernet.sls
-
-  - name: under-apache
-    provisioner:
-      pillars-from-files:
-        keystone.sls: tests/pillar/apache_wsgi.sls
+        linux_repo_openstack.sls: tests/pillar/repo_mcp_openstack_<%=os_version%>.sls
       state_top:
         base:
           "*":
@@ -95,5 +119,52 @@
             - mysql
             - apache
             - keystone
+      pillars:
+        mysql_platform_specific.sls:
+          mysql:
+            server:
+              version: "<%=mysql_version%>"
+        release.sls:
+            keystone:
+              server:
+                version: <%=os_version%>
+    verifier:
+      inspec_tests:
+        - tests/integration/<%=os_version%>/single
 
+  - name: single_ssl_<%=mysql_version%>_<%=os_version%>
+    provisioner:
+      pillars-from-files:
+        keystone.sls: tests/pillar/single.sls
+        linux_repo_openstack.sls: tests/pillar/repo_mcp_openstack_<%=os_version%>.sls
+      state_top:
+        base:
+          "*":
+            - linux
+            - mysql
+            - apache
+            - keystone
+      pillars:
+        mysql_platform_specific.sls:
+          mysql:
+            server:
+              version: "<%=mysql_version%>"
+        top.sls:
+          base:
+            "*":
+              - linux_repo_openstack
+              - keystone
+              - release
+              - ssl
+        release.sls:
+            keystone:
+              server:
+                version: <%=os_version%>
+    verifier:
+      inspec_tests:
+        - tests/integration/<%=os_version%>/single
+      attributes:
+        ssl: true
+<% end %>
+<% end %>
 # vim: ft=yaml sw=2 ts=2 sts=2 tw=125
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 501560c..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-language: python
-python:
-- "2.7.13"
-sudo: required
-services:
-  - docker
-
-install:
-  - pip install PyYAML
-  - pip install virtualenv
-  - |
-    if [ ! -e Gemfile ]; then
-       curl -s -o ./Gemfile 'https://gerrit.mcp.mirantis.com/gitweb?p=salt-formulas/salt-formulas-scripts.git;a=blob_plain;f=Gemfile;hb=refs/heads/master'
-    fi
-  - bundle install
-
-env:
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-trusty-salt-2016.3 MYSQL_VERSION='5.5' SUITE=single
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-trusty-salt-2016.3 MYSQL_VERSION='5.5' SUITE=single_domain
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-trusty-salt-2016.3 MYSQL_VERSION='5.5' SUITE=single_fernet
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-trusty-salt-2016.3 MYSQL_VERSION='5.5' SUITE=under-apache
-  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-xenial-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single
-  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-xenial-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single_domain
-  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-xenial-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single_fernet
-  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-xenial-salt-2017.7 MYSQL_VERSION='5.7' SUITE=under-apache
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-bionic-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-bionic-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single_domain
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-bionic-salt-2017.7 MYSQL_VERSION='5.7' SUITE=single_fernet
-#  - PLATFORM=epcim/salt-formulas:saltstack-ubuntu-bionic-salt-2017.7 MYSQL_VERSION='5.7' SUITE=under-apache
-
-before_script:
-  - set -o pipefail
-  - make test | tail
-
-script:
-  - bundle exec kitchen verify   single -t tests/integration
-  - bundle exec kitchen converge under-apache | tail
-  - bundle exec kitchen exec     under-apache -c "sudo service mysql start && sudo service apache2 start && keystone-manage db_sync && sleep 1"
-  - bundle exec kitchen verify   under-apache -t tests/integration
-
-notifications:
-  webhooks:
-    urls:
-      - https://webhooks.gitter.im/e/6123573504759330786b
-    on_success: change  # options: [always|never|change] default: always
-    on_failure: never  # options: [always|never|change] default: always
-    on_start: never     # options: [always|never|change] default: always
-    on_cancel: never    # options: [always|never|change] default: always
-    on_error: never    # options: [always|never|change] default: always
-  email: false
diff --git a/keystone/files/pike/keystone.conf.Debian b/keystone/files/pike/keystone.conf.Debian
index 38da89a..c44c569 100644
--- a/keystone/files/pike/keystone.conf.Debian
+++ b/keystone/files/pike/keystone.conf.Debian
@@ -107,7 +107,7 @@
 # in the P release. Use oslo.middleware.http_proxy_to_wsgi configuration
 # instead.
 #secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
-secure_proxy_ssl_header = "HTTP_X_FORWARDED_PROTO"
+secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
 
 # If set to true, then the server will return information in HTTP responses
 # that may allow an unauthenticated or authenticated user to get more
diff --git a/tests/integration/pike/single/config_spec.rb b/tests/integration/pike/single/config_spec.rb
new file mode 100644
index 0000000..156f002
--- /dev/null
+++ b/tests/integration/pike/single/config_spec.rb
@@ -0,0 +1,245 @@
+ssl_enabled = attribute("ssl", default: false)
+
+keystone_default_ssl = {
+  'transport_url' => 'rabbit://openstack:password@127.0.0.1:5671//openstack',
+  'log_config_append'         => '/etc/keystone/logging.conf',
+  'debug'                     => 'false',
+  'notification_format'       => 'cadf',
+  'admin_token'               => 'RANDOMSTRINGTOKEN',
+  'log_dir'                   => '/var/log/keystone',
+  'secure_proxy_ssl_header'   => 'HTTP_X_FORWARDED_PROTO',
+  'verbose'                   => 'true',
+}
+
+keystone_default = {
+  'transport_url' => 'rabbit://openstack:password@127.0.0.1:5672//openstack',
+  'log_config_append'         => '/etc/keystone/logging.conf',
+  'debug'                     => 'false',
+  'notification_format'       => 'cadf',
+  'admin_token'               => 'RANDOMSTRINGTOKEN',
+  'log_dir'                   => '/var/log/keystone',
+  'secure_proxy_ssl_header'   => 'HTTP_X_FORWARDED_PROTO',
+  'verbose'                   => 'true',
+}
+
+keystone_assignment = {
+  'driver' => 'sql'
+}
+
+keystone_auth = {
+  'methods' => 'password,token',
+  'oidc'    => 'keystone.auth.plugins.mapped.Mapped',
+  'saml2'   => 'keystone.auth.plugins.mapped.Mapped',
+}
+
+keystone_catalog = {
+  'template_file' => 'default_catalog.templates',
+  'driver'        => 'sql',
+}
+
+keystone_credential = {
+  'key_repository' => '/var/lib/keystone/credential-keys'
+}
+
+keystone_fernet_tokens = {
+  'key_repository'  => '/etc/keystone/fernet-keys/',
+  'max_active_keys' => '3',
+}
+
+keystone_identity = {
+  'driver'                          => 'sql',
+}
+
+keystone_token = {
+  'expiration'     => '86400',
+  'provider'       => 'fernet',
+  'caching'        => 'false',
+  'hash_algorithm' => 'sha256',
+  'driver'         => 'keystone.token.persistence.backends.memcache_pool.Token',
+  'revoke_by_id'   => 'False',
+}
+
+keystone_cache = {
+  'backend'          => 'oslo_cache.memcache_pool',
+  'enabled'          => 'True',
+  'memcache_servers' => '127.0.0.1:11211',
+}
+
+keystone_oslo_messaging_rabbit = {
+  'heartbeat_timeout_threshold' => '0',
+  'heartbeat_rate'              => '2',
+}
+
+keystone_oslo_messaging_rabbit_ssl = {
+  'rabbit_use_ssl'     => 'true',
+  'kombu_ssl_version'  => 'TLSv1_2',
+  'kombu_ssl_ca_certs' => '/etc/keystone/ssl/mysql/ca-cert.pem',
+}
+
+keystone_database = {
+  'connection'              => 'mysql+pymysql://keystone:passw0rd@127.0.0.1/keystone?charset=utf8',
+  'max_pool_size'           => '10',
+  'max_retries'             => '-1',
+  'max_overflow'            => '30',
+  'idle_timeout'            => '3600',
+}
+
+keystone_database_ssl = {
+  'connection'              => 'mysql+pymysql://keystone:passw0rd@127.0.0.1/keystone?charset=utf8&ssl_ca=/etc/keystone/ssl/mysql/ca-cert.pem&ssl_cert=/etc/keystone/ssl/mysql/client-cert.pem&ssl_key=/etc/keystone/ssl/mysql/client-key.pem',
+  'max_pool_size'           => '10',
+  'max_retries'             => '-1',
+  'max_overflow'            => '30',
+  'idle_timeout'            => '3600',
+}
+
+keystone_oslo_middleware = {
+  'max_request_body_size'        => '114688',
+  'enable_proxy_headers_parsing' => 'True',
+}
+
+keystone_cors = {
+  'allowed_origin'    => 'https://horizon.example.com',
+  'allow_credentials' => 'True',
+  'expose_headers'    => 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token',
+  'max_age'           => '3600',
+  'allow_methods'     => 'GET,PUT,POST,DELETE,PATCH',
+  'allow_headers'     => 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token,X-Project-Id,X-Project-Name,X-Project-Domain-Id,X-Project-Domain-Name,X-Domain-Id,X-Domain-Name',
+}
+
+keystone_profiler = {
+  'enabled' => 'True',
+}
+
+keystone_oidc = {
+  'remote_id_attribute' => 'HTTP_OIDC_ISS'
+}
+
+keystone_saml2 = {
+  'remote_id_attribute' => 'HTTP_OIDC_ISS'
+}
+
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '60',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '730',
+  'unique_last_password_count'         => '5',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^[a-zA-Z0-9]{32,}$$',
+  'password_regex_description'         => 'Your password could contains capital letters, lowercase letters, digits and have a minimum length of 32 characters',
+  'change_password_upon_first_use'     => 'False',
+}
+
+keystone_federation = {
+  'cache_group_membership_in_db' => 'True',
+}
+
+keystone_resource = {
+  'admin_project_domain_name' => 'project',
+  'admin_project_name'        => 'projectname',
+}
+
+keystone_extra_headers = {
+  'Distribution'          => 'Ubuntu',
+}
+
+control 'Keystone' do
+
+  describe parse_config_file('/etc/keystone/keystone.conf') do
+
+    describe 'Keystone messaging' do
+
+      if ssl_enabled
+        keystone_oslo_messaging_rabbit.merge!(keystone_oslo_messaging_rabbit_ssl)
+        keystone_default.merge!(keystone_default_ssl)
+        describe 'SSL' do
+          its('DEFAULT') {
+            should include(keystone_default)
+          }
+          its('oslo_messaging_rabbit') {
+            should include(keystone_oslo_messaging_rabbit)
+          }
+        end
+      else
+        describe 'non SSL' do
+          its('DEFAULT') {
+            should include(keystone_default)
+          }
+          its('oslo_messaging_rabbit') {
+            should include(keystone_oslo_messaging_rabbit)
+          }
+        end
+      end
+
+    end
+
+    describe 'Keystone database' do
+
+      if ssl_enabled
+        keystone_database.merge!(keystone_database_ssl)
+        describe 'SSL' do
+          its('database') {
+            should include(keystone_database)
+          }
+        end
+      else
+        describe 'non SSL' do
+          its('database') {
+            should include(keystone_database)
+          }
+        end
+      end
+
+    end
+
+    describe 'Keystone config' do
+
+      its('DEFAULT') {
+        should include(keystone_default)
+      }
+      its('assignment') {
+        should include(keystone_assignment)
+      }
+      its('auth') {
+        should include(keystone_auth)
+      }
+      its('catalog') {
+        should include(keystone_catalog)
+      }
+      its('credential') {
+        should include(keystone_credential)
+      }
+      its('fernet_tokens') {
+        should include(keystone_fernet_tokens)
+      }
+      its('identity') {
+        should include(keystone_identity)
+      }
+      its('token') {
+        should include(keystone_token)
+      }
+      its('cors') {
+        should include(keystone_cors)
+      }
+      its('oidc') {
+        should include(keystone_oidc)
+      }
+      its('saml2') {
+        should include(keystone_saml2)
+      }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
+      its('federation') {
+        should include(keystone_federation)
+      }
+      its('resource') {
+        should include(keystone_resource)
+      }
+      its('extra_headers') {
+        should include(keystone_extra_headers)
+      }
+
+    end
+  end
+end
diff --git a/tests/integration/queens/single/config_spec.rb b/tests/integration/queens/single/config_spec.rb
new file mode 100644
index 0000000..f82b08e
--- /dev/null
+++ b/tests/integration/queens/single/config_spec.rb
@@ -0,0 +1,300 @@
+ssl_enabled = attribute('ssl', default: false)
+
+keystone_default_ssl = {
+  'conn_pool_min_size'           => '2',
+  'conn_pool_ttl'                => '1200',
+  'control_exchange'             => 'openstack',
+  'debug'                        => 'True',
+  'executor_thread_pool_size'    => '64',
+  'log_config_append'            => '/etc/keystone/logging.conf',
+  'log_dir'                      => 'logdir',
+  'log_file'                     => 'logfile.log',
+  'notification_format'          => 'cadf',
+  'rpc_ack_timeout_base'         => '15',
+  'rpc_ack_timeout_multiplier'   => '2',
+  'rpc_conn_pool_size'           => '30',
+  'rpc_message_ttl'              => '300',
+  'rpc_poll_timeout'             => '1',
+  'rpc_response_timeout'         => '60',
+  'rpc_retry_attempts'           => '3',
+  'rpc_thread_pool_size'         => '100',
+  'rpc_use_acks'                 => 'False',
+  'syslog_log_facility'          => 'LOG_USER',
+  'transport_url'                => 'rabbit://openstack:password@127.0.0.1:5671//openstack',
+  'use_syslog'                   => 'True',
+}
+
+keystone_default = {
+  'conn_pool_min_size'           => '2',
+  'conn_pool_ttl'                => '1200',
+  'control_exchange'             => 'openstack',
+  'debug'                        => 'True',
+  'executor_thread_pool_size'    => '64',
+  'log_config_append'            => '/etc/keystone/logging.conf',
+  'log_dir'                      => 'logdir',
+  'log_file'                     => 'logfile.log',
+  'notification_format'          => 'cadf',
+  'rpc_ack_timeout_base'         => '15',
+  'rpc_ack_timeout_multiplier'   => '2',
+  'rpc_conn_pool_size'           => '30',
+  'rpc_message_ttl'              => '300',
+  'rpc_poll_timeout'             => '1',
+  'rpc_response_timeout'         => '60',
+  'rpc_retry_attempts'           => '3',
+  'rpc_thread_pool_size'         => '100',
+  'rpc_use_acks'                 => 'False',
+  'syslog_log_facility'          => 'LOG_USER',
+  'transport_url'                => 'rabbit://openstack:password@127.0.0.1:5672//openstack',
+  'use_syslog'                   => 'True',
+}
+
+keystone_assignment = {
+  'driver' => 'sql'
+}
+
+keystone_auth = {
+  'methods' => 'password,token',
+  'oidc'    => 'keystone.auth.plugins.mapped.Mapped',
+  'saml2'   => 'keystone.auth.plugins.mapped.Mapped',
+}
+
+keystone_catalog = {
+  'template_file' => 'default_catalog.templates',
+  'driver'        => 'sql',
+}
+
+keystone_credential = {
+  'key_repository' => '/var/lib/keystone/credential-keys'
+}
+
+keystone_fernet_tokens = {
+  'key_repository'  => '/etc/keystone/fernet-keys/',
+  'max_active_keys' => '3',
+}
+
+keystone_identity = {
+  'driver'                          => 'sql',
+}
+
+keystone_token = {
+  'expiration'     => '86400',
+  'provider'       => 'fernet',
+  'caching'        => 'false',
+  'hash_algorithm' => 'sha256',
+}
+
+keystone_cache = {
+  'backend'                      => 'oslo_cache.memcache_pool',
+  'enabled'                      => 'True',
+}
+
+keystone_oslo_messaging_rabbit = {
+  'heartbeat_rate'                       => '2',
+}
+
+keystone_oslo_messaging_rabbit_ssl = {
+  'channel_max'                          => '2',
+  'connection_factory'                   => 'single',
+  'default_notification_exchange'        => 'exchange',
+  'default_notification_retry_attempts'  => '1',
+  'default_rpc_exchange'                 => 'rpc_exchange',
+  'default_rpc_retry_attempts'           => '10',
+  'default_serializer_type'              => 'json',
+  'frame_max'                            => '2',
+  'heartbeat_interval'                   => '3',
+  'heartbeat_rate'                       => '2',
+  'heartbeat_timeout_threshold'          => '60',
+  'host_connection_reconnect_delay'      => '10',
+  'notification_listener_prefetch_count' => '100',
+  'notification_persistence'             => 'False',
+  'notification_retry_delay'             => '10',
+  'pool_max_overflow'                    => '0',
+  'pool_max_size'                        => '30',
+  'pool_recycle'                         => '600',
+  'pool_stale'                           => '60',
+  'pool_timeout'                         => '30',
+  'rabbit_ha_queues'                     => 'True',
+  'rabbit_interval_max'                  => '30',
+  'rabbit_qos_prefetch_count'            => '64',
+  'rabbit_retry_backoff'                 => '2',
+  'rabbit_retry_interval'                => '1',
+  'rabbit_transient_queues_ttl'          => '1800',
+  'rpc_listener_prefetch_count'          => '100',
+  'rpc_queue_expiration'                 => '60',
+  'rpc_reply_exchange'                   => 'rpc_reply_exchange',
+  'rpc_reply_listener_prefetch_count'    => '100',
+  'rpc_reply_retry_attempts'             => '10',
+  'rpc_reply_retry_delay'                => '10',
+  'rpc_retry_delay'                      => '10',
+  'socket_timeout'                       => '10',
+  'ssl'                                  => 'true',
+  'ssl_ca_file'                          => '/etc/keystone/ssl/mysql/ca-cert.pem',
+  'ssl_version'                          => 'TLSv1_2',
+  'tcp_user_timeout'                     => '10',
+}
+
+keystone_database = {
+  'connection'              => 'mysql+pymysql://keystone:passw0rd@127.0.0.1/keystone?charset=utf8',
+  'max_overflow'            => '30',
+  'max_pool_size'           => '10',
+  'max_retries'             => '-1',
+}
+
+keystone_database_ssl = {
+  'connection'              => 'mysql+pymysql://keystone:passw0rd@127.0.0.1/keystone?charset=utf8&ssl_ca=/etc/keystone/ssl/mysql/ca-cert.pem&ssl_cert=/etc/keystone/ssl/mysql/client-cert.pem&ssl_key=/etc/keystone/ssl/mysql/client-key.pem',
+  'max_overflow'            => '30',
+  'max_pool_size'           => '10',
+  'max_retries'             => '-1',
+  'connection_recycle_time' => '280',
+}
+
+keystone_oslo_middleware = {
+  'max_request_body_size'        => '114688',
+  'enable_proxy_headers_parsing' => 'True',
+}
+
+keystone_cors = {
+  'allowed_origin'    => 'https://horizon.example.com',
+  'allow_credentials' => 'True',
+  'expose_headers'    => 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token',
+  'max_age'           => '3600',
+  'allow_methods'     => 'GET,PUT,POST,DELETE,PATCH',
+  'allow_headers'     => 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token,X-Project-Id,X-Project-Name,X-Project-Domain-Id,X-Project-Domain-Name,X-Domain-Id,X-Domain-Name',
+}
+
+keystone_oidc = {
+  'remote_id_attribute' => 'HTTP_OIDC_ISS'
+}
+
+keystone_saml2 = {
+  'remote_id_attribute' => 'HTTP_OIDC_ISS'
+}
+
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '60',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '730',
+  'unique_last_password_count'         => '5',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^[a-zA-Z0-9]{32,}$$',
+  'password_regex_description'         => 'Your password could contains capital letters, lowercase letters, digits and have a minimum length of 32 characters',
+  'change_password_upon_first_use'     => 'False',
+}
+
+keystone_federation = {
+  'cache_group_membership_in_db' => 'True',
+}
+
+keystone_resource = {
+  'admin_project_domain_name' => 'project',
+  'admin_project_name'        => 'projectname',
+}
+
+keystone_oslo_middleware = {
+  'max_request_body_size'  => '114688',
+}
+
+control 'Keystone' do
+
+  describe parse_config_file('/etc/keystone/keystone.conf') do
+
+    describe 'Keystone messaging' do
+
+      if ssl_enabled
+        keystone_oslo_messaging_rabbit.merge!(keystone_oslo_messaging_rabbit_ssl)
+        keystone_default.merge!(keystone_default_ssl)
+        describe 'SSL' do
+          its('DEFAULT') {
+            should include(keystone_default)
+          }
+          its('oslo_messaging_rabbit') {
+            should include(keystone_oslo_messaging_rabbit)
+          }
+        end
+      else
+        describe 'non SSL' do
+          its('DEFAULT') {
+            should include(keystone_default)
+          }
+          its('oslo_messaging_rabbit') {
+            should include(keystone_oslo_messaging_rabbit)
+          }
+        end
+      end
+
+    end
+
+    describe 'Keystone database' do
+
+      if ssl_enabled
+        keystone_database.merge!(keystone_database_ssl)
+        describe 'SSL' do
+          its('database') {
+            should include(keystone_database)
+          }
+        end
+      else
+        describe 'non SSL' do
+          its('database') {
+            should include(keystone_database)
+          }
+        end
+      end
+
+    end
+
+    describe 'Keystone config' do
+
+      its('DEFAULT') {
+        should include(keystone_default)
+      }
+      its('assignment') {
+        should include(keystone_assignment)
+      }
+      its('auth') {
+        should include(keystone_auth)
+      }
+      its('catalog') {
+        should include(keystone_catalog)
+      }
+      its('credential') {
+        should include(keystone_credential)
+      }
+      its('fernet_tokens') {
+        should include(keystone_fernet_tokens)
+      }
+      its('identity') {
+        should include(keystone_identity)
+      }
+      its('token') {
+        should include(keystone_token)
+      }
+      its('cors') {
+        should include(keystone_cors)
+      }
+      its('oidc') {
+        should include(keystone_oidc)
+      }
+      its('saml2') {
+        should include(keystone_saml2)
+      }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
+      its('federation') {
+        should include(keystone_federation)
+      }
+      its('resource') {
+        should include(keystone_resource)
+      }
+      its('oslo_middleware') {
+        should include(keystone_oslo_middleware)
+      }
+      its('cache') {
+        should include(keystone_cache)
+      }
+
+    end
+  end
+end
diff --git a/tests/pillar/apache_wsgi.sls b/tests/pillar/apache_wsgi.sls
deleted file mode 100644
index 880b53d..0000000
--- a/tests/pillar/apache_wsgi.sls
+++ /dev/null
@@ -1,192 +0,0 @@
-
-keystone:
-# Server state
-  server:
-    enabled: true
-    version: liberty
-    service_name: apache2
-    service_token: RANDOMSTRINGTOKEN
-    service_tenant: service
-    admin_tenant: admin
-    admin_name: admin
-    admin_password: passw0rd
-    admin_email: root@localhost
-    enable_proxy_headers_parsing: True
-    bind:
-      address: 0.0.0.0
-      private_address: 127.0.0.1
-      private_port: 35357
-      public_address: 127.0.0.1
-      public_port: 5000
-    region: RegionOne
-    database:
-      engine: mysql
-      host: localhost
-      name: keystone
-      password: passw0rd
-      user: keystone
-    tokens:
-      engine: cache
-      expiration: 86400
-      location: /etc/keystone/fernet-keys/
-    notification: false
-    notification_format: cadf
-    logging:
-      log_appender: false
-      log_handlers:
-        watchedfile:
-          enabled: true
-        fluentd:
-          enabled: false
-        ossyslog:
-          enabled: false
-    #message_queue:
-      #engine: rabbitmq
-      #host: 127.0.0.1
-      #port: 5672
-      #user: openstack
-      #password: password
-      #virtual_host: '/openstack'
-      #ha_queues: true
-# Client state
-  client:
-    enabled: false
-    server:
-      identity:
-        admin:
-          host: localhost
-          port: 35357
-          token: RANDOMSTRINGTOKEN
-        roles:
-        - admin
-        - Member
-        project:
-          service:
-            description: "OpenStack Service tenant"
-          admin:
-            description: "OpenStack Admin tenant"
-            user:
-              admin:
-                is_admin: true
-                password: passw0rd
-                email: admin@localhost
-        service:
-          keystone3:
-            type: identity
-            description: OpenStack Identity Service v3
-            endpoints:
-            - region: RegionOne
-              public_address: keystone
-              public_protocol: http
-              public_port: 5000
-              public_path: '/v3'
-              internal_address: keystone
-              internal_port: 5000
-              internal_path: '/v3'
-              admin_address: keystone
-              admin_port: 35357
-              admin_path: '/v3'
-          keystone:
-            type: identity
-            description: OpenStack Identity Service
-            endpoints:
-            - region: RegionOne
-              public_address: keystone
-              public_protocol: http
-              public_port: 5000
-              public_path: '/v2.0'
-              internal_address: keystone
-              internal_port: 5000
-              internal_path: '/v2.0'
-              admin_address: keystone
-              admin_port: 35357
-              admin_path: '/v2.0'
-          #keystone3:
-            #name: keystone3
-            #type: identity
-            #description: OpenStack Identity Service v3
-            #endpoints:
-            #- region: RegionTwo
-              #public_address: keystone
-              #public_protocol: http
-              #public_port: 5000
-              #public_path: '/v3'
-              #internal_address: keystone
-              #internal_port: 5000
-              #internal_path: '/v3'
-              #admin_address: keystone
-              #admin_port: 35357
-              #admin_path: '/v3'
-          #keystone:
-            #name: keystone
-            #type: identity
-            #description: OpenStack Identity Service
-            #endpoints:
-            #- region: RegionTwo
-              #public_address: keystone
-              #public_protocol: http
-              #public_port: 5000
-              #public_path: '/v2.0'
-              #internal_address: keystone
-              #internal_port: 5000
-              #internal_path: '/v2.0'
-              #admin_address: keystone
-              #admin_port: 35357
-              #admin_path: '/v2.0'
-# CI related dependencies
-apache:
-  server:
-    enabled: true
-    default_mpm: event
-    mpm:
-      prefork:
-        enabled: true
-        servers:
-          start: 5
-          spare:
-            min: 2
-            max: 10
-        max_requests: 0
-        max_clients: 20
-        limit: 20
-    site:
-      keystone:
-        enabled: true
-        type: keystone
-        name: wsgi
-        host:
-          name: localhost
-    pkgs:
-      - apache2
-    modules:
-      - wsgi
-mysql:
-  client:
-    enabled: true
-    version: '5.7'
-    admin:
-      host: localhost
-      port: 3306
-      user: admin
-      password: password
-      encoding: utf8
-  server:
-    enabled: true
-    version: "5.7"
-    force_encoding: utf8
-    bind:
-      address: 0.0.0.0
-      port: 3306
-      protocol: tcp
-    database:
-      keystone:
-        encoding: utf8
-        users:
-        - host: '%'
-          name: keystone
-          password: passw0rd
-          rights: all
-        - host: 127.0.0.1
-          name: keystone
-          password: passw0rd
-          rights: all
diff --git a/tests/pillar/client_resources_v3.sls b/tests/pillar/client_resources_v3.sls
deleted file mode 100644
index f68ef70..0000000
--- a/tests/pillar/client_resources_v3.sls
+++ /dev/null
@@ -1,145 +0,0 @@
-include:
-  - single
-
-keystone:
-  client:
-    resources:
-      v3:
-        enabled: true
-        cloud_name: 'admin_identity'
-        domains:
-          'Default':
-            enabled: True
-            status: present
-            projects:
-              service:
-                status: present
-                description: "OpenStack Service tenant"
-              admin:
-                status: absent
-                description: "OpenStack Admin tenant"
-          'User_domain':
-            enabled: True
-            status: absent
-            projects:
-              user_domain_service:
-                status: present
-                description: "OpenStack Service tenant"
-              user_domain_admin:
-                status: absent
-                description: "OpenStack Admin tenant"
-          'User_domain_0':
-            enabled: True
-            status: absent
-            force_delete: True
-            projects:
-              user_domain_0_service:
-                status: present
-                description: "OpenStack Service tenant"
-              user_domain_0_admin:
-                status: absent
-                description: "OpenStack Admin tenant"
-          'User_domain_1':
-            enabled: False
-            status: absent
-            projects:
-              user_domain_1_service:
-                status: present
-                description: "OpenStack Service tenant"
-              user_domain_1_admin:
-                status: absent
-                description: "OpenStack Admin tenant"
-        roles:
-          service_admin:
-            name: admin
-            enabled: true
-            status: present
-          global_Member:
-            name: Member
-            enabled: true
-            status: absent
-          global_Member_0:
-            name: Member
-            enabled: False
-            status: absent
-        users:
-          admin:
-            enabled: true
-            status: present
-            password: passw0rd
-            email: root@localhost
-            roles:
-              service_admin:
-                status: assigned
-                name: admin
-                project_id: admin
-          user:
-            enabled: true
-            status: absent
-            password: passw0rd
-            email: root@localhost
-            roles:
-              global_Member:
-                status: unassigned
-                name: user
-                project_id: user
-          user0:
-            enabled: False
-            status: absent
-            password: passw0rd
-            email: root@localhost
-            roles:
-              global_Member:
-                status: unassigned
-                name: user
-                project_id: user
-        services:
-          keystone:
-            enabled: True
-            status: present
-            type: 'identity'
-            description: "OpenStack Identity Service"
-            endpoints:
-              keystone_public:
-                status: present
-                interface: 'public'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-              keystone_internal:
-                status: absent
-                interface: 'internal'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-          keystone_0:
-            enabled: True
-            status: absent
-            type: 'identity'
-            description: "OpenStack Identity Service"
-            endpoints:
-              keystone_0_public:
-                status: present
-                interface: 'public'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-              keystone_0_internal:
-                status: absent
-                interface: 'internal'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-          keystone_1:
-            enabled: False
-            status: absent
-            type: 'identity'
-            description: "OpenStack Identity Service"
-            endpoints:
-              keystone_1_public:
-                status: present
-                interface: 'public'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-              keystone_1_internal:
-                status: absent
-                interface: 'internal'
-                url: https://127.0.0.1:5000/
-                region: RegionOne
-
diff --git a/tests/pillar/repo_mcp_openstack_pike.sls b/tests/pillar/repo_mcp_openstack_pike.sls
new file mode 100644
index 0000000..fe6af3c
--- /dev/null
+++ b/tests/pillar/repo_mcp_openstack_pike.sls
@@ -0,0 +1,12 @@
+linux:
+  system:
+    enabled: true
+    repo:
+      mirantis_openstack_repo:
+        source: "deb http://mirror.mirantis.com/update/2019.2.0/openstack-pike/{{ grains.get('oscodename') }} {{ grains.get('oscodename') }} main"
+        architectures: amd64
+        key_url: "http://mirror.mirantis.com/update/2019.2.0/openstack-pike/{{ grains.get('oscodename') }}/archive-pike.key"
+        pin:
+        - pin: 'release l=pike'
+          priority: 1050
+          package: '*'
\ No newline at end of file
diff --git a/tests/pillar/repo_mcp_openstack_queens.sls b/tests/pillar/repo_mcp_openstack_queens.sls
new file mode 100644
index 0000000..ebe1964
--- /dev/null
+++ b/tests/pillar/repo_mcp_openstack_queens.sls
@@ -0,0 +1,12 @@
+linux:
+  system:
+    enabled: true
+    repo:
+      mirantis_openstack_repo:
+        source: "deb http://mirror.mirantis.com/update/2019.2.0/openstack-queens/{{ grains.get('oscodename') }} {{ grains.get('oscodename') }} main"
+        architectures: amd64
+        key_url: "http://mirror.mirantis.com/update/2019.2.0/openstack-queens/{{ grains.get('oscodename') }}/archive-queens.key"
+        pin:
+        - pin: 'release l=queens'
+          priority: 1050
+          package: '*'
\ No newline at end of file
diff --git a/tests/pillar/repo_mos9.sls b/tests/pillar/repo_mos9.sls
deleted file mode 100644
index 64d75b8..0000000
--- a/tests/pillar/repo_mos9.sls
+++ /dev/null
@@ -1,8 +0,0 @@
-linux:
-  system:
-    enabled: true
-    repo:
-      mirantis_openstack:
-        source: "deb [arch=amd64] http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/ mos9.0 main restricted"
-        architectures: amd64
-        key_url: "http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/archive-mos9.0.key"
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
index 7227af7..ba1ef77 100644
--- a/tests/pillar/single.sls
+++ b/tests/pillar/single.sls
@@ -3,12 +3,56 @@
   server:
     enabled: true
     version: liberty
+    service_name: apache2
     service_token: RANDOMSTRINGTOKEN
     service_tenant: service
+    admin_project:
+      name: projectname
+      domain: project
     admin_tenant: admin
     admin_name: admin
     admin_password: passw0rd
     admin_email: root@localhost
+    enable_proxy_headers_parsing: True
+    cors:
+      allowed_origin: 'https://horizon.example.com'
+      allow_credentials: True
+      expose_headers: 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token'
+      allow_headers: 'X-Auth-Token,X-Openstack-Request-Id,X-Subject-Token,X-Project-Id,X-Project-Name,X-Project-Domain-Id,X-Project-Domain-Name,X-Domain-Id,X-Domain-Name'
+      max_age: 3600
+      allow_methods: 'GET,PUT,POST,DELETE,PATCH'
+    auth_methods:
+    - password
+    - token
+    federation:
+      oidc:
+        remote_id_attribute: HTTP_OIDC_ISS
+        remote_id_attribute_value: remote_id_attribute_value
+        oidc_claim_prefix: oidc_claim_prefix
+        oidc_client_id: oidc_client_id
+        oidc_client_secret: oidc_client_secret
+        oidc_crypto_passphrase: oidc_crypto_passphrase
+        oidc_redirect_uri: oidc_redirect_uri
+        oidc_provider_metadata_url: oidc_provider_metadata_url
+        oidc_response_type: oidc_response_type
+        oidc_scope: oidc_scope
+        oidc_ssl_validate_server: oidc_ssl_validate_server
+        oidc_oauth_ssl_validate_server: oidc_oauth_ssl_validate_server
+        oidc_oauth_introspection_endpoint: oidc_oauth_introspection_endpoint
+        oidc_oauth_introspection_token_param_name: oidc_oauth_introspection_token_param_name
+        oidc_oauth_remote_user_claim: oidc_oauth_remote_user_claim
+        oidc_oauth_verify_jwks_uri: oidc_oauth_verify_jwks_uri
+        odic_token_iat_slack: odic_token_iat_slack
+        oidc_provider_issuer: oidc_provider_issuer
+        oidc_provider_authorization_endpoint: oidc_provider_authorization_endpoint
+        oidc_provider_token_endpoint: oidc_provider_token_endpoint
+        oidc_provider_token_endpoint_auth: oidc_provider_token_endpoint_auth
+        oidc_provider_user_info_endpoint: oidc_provider_user_info_endpoint
+        oidc_provider_jwks_uri: oidc_provider_jwks_uri
+        protocol: oidc
+      saml2:
+        remote_id_attribute: HTTP_OIDC_ISS
+        protocol: saml2
     bind:
       address: 0.0.0.0
       private_address: 127.0.0.1
@@ -18,16 +62,16 @@
     region: RegionOne
     database:
       engine: mysql
-      host: localhost
+      host: 127.0.0.1
       name: keystone
       password: passw0rd
       user: keystone
     tokens:
-      engine: cache
+      engine: fernet
       expiration: 86400
       location: /etc/keystone/fernet-keys/
       allow_expired_window: 86400
-    notification: false
+    notification: true
     notification_format: cadf
     security_compliance:
       disable_user_account_days_inactive: 90
@@ -41,7 +85,12 @@
         Your password could contains capital letters, lowercase letters, digits and have a minimum length of 32 characters
       change_password_upon_first_use: False
     logging:
-      log_appender: false
+      debug: true
+      log_file: 'logfile.log'
+      log_dir: logdir
+      use_syslog: true
+      syslog_log_facility: LOG_USER
+      log_appender: true
       log_handlers:
         watchedfile:
           enabled: true
@@ -52,18 +101,139 @@
     extra_config:
       federation:
         cache_group_membership_in_db: true
-    #message_queue:
-      #engine: rabbitmq
-      #host: 127.0.0.1
-      #port: 5672
-      #user: openstack
-      #password: password
-      #virtual_host: '/openstack'
-      #ha_queues: true
+    message_queue:
+      engine: rabbitmq
+      host: 127.0.0.1
+      port: 5672
+      user: openstack
+      password: password
+      virtual_host: '/openstack'
+      ha_queues: true
+      rabbit_ha_queues: true
+      rpc_conn_pool_size: 30
+      conn_pool_min_size: 2
+      conn_pool_ttl: 1200
+      rpc_poll_timeout: 1
+      rpc_thread_pool_size: 100
+      rpc_message_ttl: 300
+      rpc_use_acks: false
+      rpc_ack_timeout_base: 15
+      rpc_ack_timeout_multiplier: 2
+      rpc_retry_attempts: 3
+      executor_thread_pool_size: 64
+      rpc_response_timeout: 60
+      control_exchange: openstack
+      ssl:
+        version: TLSv1_2
+        cacert_file: ssl_ca_certs
+      x509:
+        key_file: kombu_ssl_keyfile
+        cert_file: kombu_ssl_certfile
+      rabbit_retry_interval: 1
+      rabbit_retry_backoff: 2
+      rabbit_interval_max: 30
+      rabbit_transient_queues_ttl: 1800
+      heartbeat_timeout_threshold: 60
+      heartbeat_rate: 2
+      channel_max: 2
+      frame_max: 2
+      heartbeat_interval: 3
+      socket_timeout: 10
+      tcp_user_timeout: 10
+      host_connection_reconnect_delay: 10
+      connection_factory: single
+      pool_max_size: 30
+      pool_max_overflow: 0
+      pool_timeout: 30
+      pool_recycle: 600
+      pool_stale: 60
+      default_serializer_type: json
+      notification_persistence: false
+      default_notification_exchange: exchange
+      notification_listener_prefetch_count: 100
+      default_notification_retry_attempts: 1
+      notification_retry_delay: 10
+      rpc_queue_expiration: 60
+      default_rpc_exchange: rpc_exchange
+      rpc_reply_exchange: rpc_reply_exchange
+      rpc_listener_prefetch_count: 100
+      rpc_reply_listener_prefetch_count: 100
+      rpc_reply_retry_attempts: 10
+      rpc_reply_retry_delay: 10
+      default_rpc_retry_attempts: 10
+      rpc_retry_delay: 10
+      rabbit_qos_prefetch_count: 64
+    healthcheck:
+      path: '/healthcheck'
+    max_request_body_size: 114688
+    profiler:
+      enabled: True
+    cache:
+      enabled: True
+      host: 127.0.0.1
+      port: 11211
+    policy:
+      policy_file: 'policy.json'
+    domain:
+      testing:
+        description: "Test domain"
+        backend: ldap
+        identity:
+          backend: ldap
+          driver: ldap
+        assignment:
+          backend: sql
+          driver: keystone.assignment.backends.sql.Assignment
+        ldap:
+          group_mapping: False
+          url: "ldaps://idm.domain.com"
+          suffix: "dc=cloud,dc=domain,dc=com"
+          uid: keystone
+          password: password
+          query_scope: "sub"
+          bind_user: "CN=lab,CN=users,${keystone:server:domain:testing:ldap:suffix}"
+          filter:
+            user: "(memberOf=CN=Grp-atm-admins,CN=Users,${keystone:server:domain:testing:ldap:suffix})"
+          user_tree_dn: "CN=users,${keystone:server:domain:testing:ldap:suffix}"
+          user_id_attribute: "sAMAccountName"
+          user_name_attribute: "sAMAccountName"
+          user_pass_attribute: ""
+          user_enabled_default: 512
+          user_enabled_mask: 2
+          user_enabled_attribute: "userAccountControl"
+          user_attribute_ignore: "password,tenant_id,tenants"
+
 # Client state
   client:
     enabled: false
+    os_client_config:
+      enabled: true
+      cfgs:
+        root:
+          content:
+            clouds:
+              admin_identity:
+                region_name: RegionOne
+                identity_api_version: '3'
+                interface: 'internal'
+                auth:
+                  username: 'admin'
+                  password: passw0rd
+                  user_domain_name: 'Default'
+                  project_name: 'admin'
+                  project_domain_name: 'Default'
+                  auth_url: 'http://127.0.0.1:5000'
     server:
+      admin_identity:
+        admin:
+          user: admin
+          password: passw0rd
+          project: admin
+          host: localhost
+          port: 5000
+          region_name: RegionOne
+          use_keystoneauth: true
+          protocol: http
       identity:
         admin:
           host: localhost
@@ -113,23 +283,32 @@
               admin_address: keystone
               admin_port: 35357
               admin_path: '/v2.0'
-          # TODO: enable once salt keystone module/states are fixed
-          #keystoneR2:
-            #service: keystone
-            #type: identity
-            #description: OpenStack Identity Service
-            #endpoints:
-            #- region: RegionTwo
-              #public_address: keystone
-              #public_protocol: http
-              #public_port: 5000
-              #public_path: '/v2.0'
-              #internal_address: keystone
-              #internal_port: 5000
-              #internal_path: '/v2.0'
-              #admin_address: keystone
-              #admin_port: 35357
-              #admin_path: '/v2.0'
+apache:
+  server:
+    enabled: true
+    default_mpm: event
+    mpm:
+      prefork:
+        enabled: true
+        servers:
+          start: 5
+          spare:
+            min: 2
+            max: 10
+        max_requests: 0
+        max_clients: 20
+        limit: 20
+    site:
+      keystone:
+        enabled: true
+        type: keystone
+        name: wsgi
+        host:
+          name: localhost
+    pkgs:
+      - apache2
+    modules:
+      - wsgi
 # CI related dependencies
 mysql:
   client:
diff --git a/tests/pillar/single_domain.sls b/tests/pillar/single_domain.sls
deleted file mode 100644
index ec86a26..0000000
--- a/tests/pillar/single_domain.sls
+++ /dev/null
@@ -1,85 +0,0 @@
-keystone:
-# Server state
-  server:
-    enabled: true
-    version: liberty
-    service_token: RANDOMSTRINGTOKEN
-    service_tenant: service
-    admin_tenant: admin
-    admin_name: admin
-    admin_password: passw0rd
-    admin_email: root@localhost
-    bind:
-      address: 0.0.0.0
-      private_address: 127.0.0.1
-      private_port: 35357
-      public_address: 127.0.0.1
-      public_port: 5000
-    region: RegionOne
-    database:
-      engine: mysql
-      host: localhost
-      name: keystone
-      password: passw0rd
-      user: keystone
-    tokens:
-      engine: cache
-      expiration: 86400
-      location: /etc/keystone/fernet-keys/
-    notification: false
-    notification_format: cadf
-    logging:
-      log_appender: false
-      log_handlers:
-        watchedfile:
-          enabled: true
-        fluentd:
-          enabled: false
-        ossyslog:
-          enabled: false
-    domain:
-      testing:
-        description: "Test domain"
-        backend: ldap
-        identity:
-          backend: ldap
-          driver: ldap
-        assignment:
-          backend: sql
-          driver: keystone.assignment.backends.sql.Assignment
-        ldap:
-          url: "ldaps://idm.domain.com"
-          suffix: "dc=cloud,dc=domain,dc=com"
-          uid: keystone
-          password: password
-# CI related dependencies
-mysql:
-  client:
-    enabled: true
-    version: '5.7'
-    admin:
-      host: localhost
-      port: 3306
-      user: admin
-      password: password
-      encoding: utf8
-  server:
-    enabled: true
-    version: "5.7"
-    force_encoding: utf8
-    bind:
-      address: 0.0.0.0
-      port: 3306
-      protocol: tcp
-    database:
-      keystone:
-        encoding: utf8
-        users:
-        - host: '%'
-          name: keystone
-          password: passw0rd
-          rights: all
-        - host: 127.0.0.1
-          name: keystone
-          password: passw0rd
-          rights: all
diff --git a/tests/pillar/single_fernet.sls b/tests/pillar/single_fernet.sls
deleted file mode 100644
index d87b63c..0000000
--- a/tests/pillar/single_fernet.sls
+++ /dev/null
@@ -1,70 +0,0 @@
-keystone:
-  server:
-    enabled: true
-    version: liberty
-    service_token: token
-    service_tenant: service
-    admin_tenant: admin
-    admin_name: admin
-    admin_password: passw0rd
-    admin_email: root@localhost
-    bind:
-      address: 0.0.0.0
-      private_address: 127.0.0.1
-      private_port: 35357
-      public_address: 127.0.0.1
-      public_port: 5000
-    region: RegionOne
-    database:
-      engine: mysql
-      host: localhost
-      name: keystone
-      password: passw0rd
-      user: keystone
-    tokens:
-      engine: fernet
-      expiration: 86400
-      location: /etc/keystone/fernet-keys/
-      max_active_keys: 4
-    notification: false
-    notification_format: cadf
-    logging:
-      log_appender: false
-      log_handlers:
-        watchedfile:
-          enabled: true
-        fluentd:
-          enabled: false
-        ossyslog:
-          enabled: false
-# CI related dependencies
-mysql:
-  client:
-    enabled: false
-    version: '5.7'
-    admin:
-      host: localhost
-      port: 3306
-      user: admin
-      password: password
-      encoding: utf8
-  server:
-    enabled: true
-    version: "5.7"
-    force_encoding: utf8
-    bind:
-      address: 0.0.0.0
-      port: 3306
-      protocol: tcp
-    database:
-      keystone:
-        encoding: utf8
-        users:
-        - host: '%'
-          name: keystone
-          password: passw0rd
-          rights: all
-        - host: 127.0.0.1
-          name: keystone
-          password: passw0rd
-          rights: all
diff --git a/tests/pillar/ssl.sls b/tests/pillar/ssl.sls
deleted file mode 100644
index 8e42d8e..0000000
--- a/tests/pillar/ssl.sls
+++ /dev/null
@@ -1,62 +0,0 @@
-# Test case with enabled SSL of the following communication paths:
-# - messaging (rabbitmq)
-
-keystone:
-  server:
-    enabled: true
-    version: liberty
-    service_token: token
-    service_tenant: service
-    admin_tenant: admin
-    admin_name: admin
-    admin_password: passw0rd
-    admin_email: root@localhost
-    bind:
-      address: 0.0.0.0
-      private_address: 127.0.0.1
-      private_port: 35357
-      public_address: 127.0.0.1
-      public_port: 5000
-    region: RegionOne
-    database:
-      engine: mysql
-      host: 127.0.0.1
-      name: keystone
-      password: passw0rd
-      user: keystone
-      ssl:
-        enabled: True
-    tokens:
-      engine: cache
-      expiration: 86400
-      location: /etc/keystone/fernet-keys/
-    notification: true
-    notification_format: cadf
-    logging:
-      log_appender: false
-      log_handlers:
-        watchedfile:
-          enabled: true
-        fluentd:
-          enabled: false
-        ossyslog:
-          enabled: false
-    message_queue:
-      engine: rabbitmq
-      host: 127.0.0.1
-      port: 5671
-      user: openstack
-      password: passw0rd
-      virtual_host: '/openstack'
-      ha_queues: true
-      ssl:
-        enabled: True
-    cache:
-      engine: memcached
-      members:
-      - host: 127.0.0.1
-        port: 11211
-      - host: 127.0.0.1
-        port: 11211
-      - host: 127.0.0.1
-        port: 11211
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 9761585..4665d34 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1,5 +1,15 @@
 #!/usr/bin/env bash
 
+###
+# Script source: https://gerrit.mcp.mirantis.com/#/admin/projects/salt-formulas/cookiecutter-salt-formula
+# Script requirments:
+#apt-get install -y python-yaml virtualenv git
+
+__ScriptVersion="2019.01.07"
+__ScriptName="run_tests.sh"
+__ScriptFullName="$0"
+__ScriptArgs="$*"
+
 set -e
 [ -n "$DEBUG" ] && set -x
 
@@ -14,32 +24,40 @@
 VENV_DIR=${VENV_DIR:-${BUILDDIR}/virtualenv}
 MOCK_BIN_DIR=${MOCK_BIN_DIR:-${CURDIR}/mock_bin}
 DEPSDIR=${BUILDDIR}/deps
+SCHEMARDIR=${SCHEMARDIR:-"${CURDIR}/../${FORMULA_NAME}/schemas/"}
 
 SALT_FILE_DIR=${SALT_FILE_DIR:-${BUILDDIR}/file_root}
 SALT_PILLAR_DIR=${SALT_PILLAR_DIR:-${BUILDDIR}/pillar_root}
 SALT_CONFIG_DIR=${SALT_CONFIG_DIR:-${BUILDDIR}/salt}
 SALT_CACHE_DIR=${SALT_CACHE_DIR:-${SALT_CONFIG_DIR}/cache}
+SALT_CACHE_EXTMODS_DIR=${SALT_CACHE_EXTMODS_DIR:-${SALT_CONFIG_DIR}/cache_master_extmods}
 
 SALT_OPTS="${SALT_OPTS} --retcode-passthrough --local -c ${SALT_CONFIG_DIR} --log-file=/dev/null"
 
+IGNORE_MODELVALIDATE_MASK=${IGNORE_MODELVALIDATE_MASK:-"novalidate"}
+
 if [ "x${SALT_VERSION}" != "x" ]; then
     PIP_SALT_VERSION="==${SALT_VERSION}"
 fi
 
 ## Functions
 log_info() {
-    echo "[INFO] $*"
+    echo -e "[INFO] $*"
 }
 
 log_err() {
-    echo "[ERROR] $*" >&2
+    echo -e "[ERROR] $*" >&2
 }
 
 setup_virtualenv() {
     log_info "Setting up Python virtualenv"
+    dependency_check virtualenv
     virtualenv $VENV_DIR
     source ${VENV_DIR}/bin/activate
     python -m pip install salt${PIP_SALT_VERSION}
+    if [[ -f ${CURDIR}/test-requirements.txt ]]; then
+       python -m pip install -r ${CURDIR}/test-requirements.txt
+    fi
 }
 
 setup_mock_bin() {
@@ -59,12 +77,19 @@
         state_name=$(basename ${pillar%.sls})
         echo -e "  ${state_name}:\n    - ${state_name}" >> ${SALT_PILLAR_DIR}/top.sls
     done
+    for pillar in $(find $PILLARDIR  -mindepth 2 -type f -iname *.sls); do
+        state_name=$(basename "${pillar%*.sls}")
+        os_release=$(echo $pillar | rev | cut -d'/' -f2 | rev)
+        grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
+        echo -e "  ${os_release}_${state_name}:\n    - ${os_release}.${state_name}" >> ${SALT_PILLAR_DIR}/top.sls
+    done
 }
 
 setup_salt() {
     [ ! -d ${SALT_FILE_DIR} ] && mkdir -p ${SALT_FILE_DIR}
     [ ! -d ${SALT_CONFIG_DIR} ] && mkdir -p ${SALT_CONFIG_DIR}
     [ ! -d ${SALT_CACHE_DIR} ] && mkdir -p ${SALT_CACHE_DIR}
+    [ ! -d ${SALT_CACHE_EXTMODS_DIR} ] && mkdir -p ${SALT_CACHE_EXTMODS_DIR}
 
     echo "base:" > ${SALT_FILE_DIR}/top.sls
     for pillar in ${PILLARDIR}/*.sls; do
@@ -72,10 +97,17 @@
         state_name=$(basename ${pillar%.sls})
         echo -e "  ${state_name}:\n    - ${FORMULA_NAME}" >> ${SALT_FILE_DIR}/top.sls
     done
+    for pillar in $(find $PILLARDIR  -mindepth 2 -type f -iname *.sls); do
+        state_name=$(basename "${pillar%*.sls}")
+        os_release=$(echo $pillar | rev | cut -d'/' -f2 | rev)
+        grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
+        echo -e "  ${os_release}_${state_name}:\n    - ${FORMULA_NAME}" >> ${SALT_FILE_DIR}/top.sls
+    done
 
     cat << EOF > ${SALT_CONFIG_DIR}/minion
 file_client: local
 cachedir: ${SALT_CACHE_DIR}
+extension_modules:  ${SALT_CACHE_EXTMODS_DIR}
 verify_env: False
 minion_id_caching: False
 
@@ -83,7 +115,6 @@
   base:
   - ${SALT_FILE_DIR}
   - ${CURDIR}/..
-  - /usr/share/salt-formulas/env
 
 pillar_roots:
   base:
@@ -93,13 +124,14 @@
 }
 
 fetch_dependency() {
+    # example: fetch_dependency "linux:https://github.com/salt-formulas/salt-formula-linux"
     dep_name="$(echo $1|cut -d : -f 1)"
     dep_source="$(echo $1|cut -d : -f 2-)"
     dep_root="${DEPSDIR}/$(basename $dep_source .git)"
     dep_metadata="${dep_root}/metadata.yml"
 
-    [ -d /usr/share/salt-formulas/env/${dep_name} ] && log_info "Dependency $dep_name already present in system-wide salt env" && return 0
-    [ -d $dep_root ] && log_info "Dependency $dep_name already fetched" && return 0
+    dependency_check git
+    [ -d $dep_root ] && { log_info "Dependency $dep_name already fetched"; return 0; }
 
     log_info "Fetching dependency $dep_name"
     [ ! -d ${DEPSDIR} ] && mkdir -p ${DEPSDIR}
@@ -109,6 +141,19 @@
     METADATA="${dep_metadata}" install_dependencies
 }
 
+link_modules(){
+    # Link modules *.py files to temporary salt-root
+    local SALT_ROOT=${1:-$SALT_FILE_DIR}
+    local SALT_ENV=${2:-$DEPSDIR}
+
+    mkdir -p "${SALT_ROOT}/_modules/"
+    # from git, development versions
+    find ${SALT_ENV} -maxdepth 3 -mindepth 3 -path '*_modules*' -iname "*.py" -type f -print0 | while read -d $'\0' file; do
+      ln -fs $(readlink -e ${file}) "$SALT_ROOT"/_modules/$(basename ${file}) ;
+    done
+    salt_run saltutil.sync_all
+}
+
 install_dependencies() {
     grep -E "^dependencies:" ${METADATA} >/dev/null || return 0
     (python - | while read dep; do fetch_dependency "$dep"; done) << EOF
@@ -129,13 +174,30 @@
 }
 
 prepare() {
-    [ -d ${BUILDDIR} ] && mkdir -p ${BUILDDIR}
+    if [[ -f ${BUILDDIR}/.prepare_done ]]; then
+      log_info "${BUILDDIR}/.prepare_done exist, not rebuilding BUILDDIR"
+      return
+    fi
+    [[ -d ${BUILDDIR} ]] && mkdir -p ${BUILDDIR}
 
-    which salt-call || setup_virtualenv
+    [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
     setup_mock_bin
     setup_pillar
     setup_salt
     install_dependencies
+    link_modules
+    touch ${BUILDDIR}/.prepare_done
+}
+
+lint_releasenotes() {
+    [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
+    source ${VENV_DIR}/bin/activate
+    reno lint ${CURDIR}/../
+}
+
+lint() {
+#    lint_releasenotes
+    log_err "TODO: lint_releasenotes"
 }
 
 run() {
@@ -152,7 +214,7 @@
             meta_name=$(basename ${meta})
             echo "Checking meta ${meta_name} ..."
             salt_run --out=quiet --id=${state_name} cp.get_template ${meta} ${SALT_CACHE_DIR}/${meta_name} \
-              || (log_err "Failed to render meta ${meta} using pillar ${FORMULA_NAME}.${state_name}"; exit 1)
+              || { log_err "Failed to render meta ${meta} using pillar ${FORMULA_NAME}.${state_name}"; exit 1; }
             cat ${SALT_CACHE_DIR}/${meta_name}
         done
     done
@@ -161,10 +223,52 @@
 real_run() {
     for pillar in ${PILLARDIR}/*.sls; do
         state_name=$(basename ${pillar%.sls})
-        salt_run --id=${state_name} state.sls ${FORMULA_NAME} || (log_err "Execution of ${FORMULA_NAME}.${state_name} failed"; exit 1)
+        salt_run --id=${state_name} state.sls ${FORMULA_NAME} || { log_err "Execution of ${FORMULA_NAME}.${state_name} failed"; exit 1; }
     done
 }
 
+run_model_validate(){
+  # Run modelschema.model_validate validation.
+  # TEST iterateble, run for `each formula ROLE against each ROLE_PILLARNAME`
+  # Pillars should be named in conviend ROLE_XXX.sls or ROLE.sls
+  # Example:
+  # client.sls  client_auth.sls  server.sls  server_auth.sls
+  if [ -d ${SCHEMARDIR} ]; then
+    # model validator require py modules
+    fetch_dependency "salt:https://github.com/salt-formulas/salt-formula-salt"
+    link_modules
+    salt_run saltutil.clear_cache; salt_run saltutil.refresh_pillar; salt_run saltutil.sync_all;
+    for role in $(find $SCHEMARDIR/* -maxdepth 0 -type f -iname *.yaml); do
+      role_name=$(basename "${role%*.yaml}")
+      for pillar in $(ls pillar/${role_name}*.sls | grep -v ${IGNORE_MODELVALIDATE_MASK} ); do
+        pillar_name=$(basename "${pillar%*.sls}")
+        local _message="FORMULA:${FORMULA_NAME} ROLE:${role_name} against PILLAR:${pillar_name}"
+        log_info "model_validate ${_message}"
+        # Rendered Example:
+        # python $(which salt-call) --local -c /test1/maas/tests/build/salt --id=maas_cluster modelschema.model_validate maas cluster
+        salt_run -m ${DEPSDIR}/salt-formula-salt --id=${pillar_name} modelschema.model_validate ${FORMULA_NAME} ${role_name} || { log_err "Execution of model_validate ${_message} failed"; exit 1 ; }
+      done
+    done
+    for schema in $(find $SCHEMARDIR -mindepth 2 -type f -iname *.yaml); do
+        role_name=$(basename "${schema%*.yaml}")
+        os_release=$(echo $schema | rev | cut -d'/' -f2 | rev)
+        local _message="FORMULA:${FORMULA_NAME} ROLE:${role_name} against PILLAR:${role_name}"
+        log_info "model_validate ${_message}"
+        salt_run -m ${DEPSDIR}/salt-formula-salt --id=${os_release}_${role_name} modelschema.model_validate ${FORMULA_NAME} ${role_name} ${os_release} || { log_err "Execution of model_validate ${_message} failed"; exit 1 ; }
+    done
+  else
+    log_info "${SCHEMARDIR} not found!";
+  fi
+}
+
+dependency_check() {
+  local DEPENDENCY_COMMANDS=$*
+
+  for DEPENDENCY_COMMAND in $DEPENDENCY_COMMANDS; do
+    which $DEPENDENCY_COMMAND > /dev/null || ( log_err "Command \"$DEPENDENCY_COMMAND\" can not be found in default path."; exit 1; )
+  done
+}
+
 _atexit() {
     RETVAL=$?
     trap true INT TERM EXIT
@@ -178,6 +282,10 @@
 }
 
 ## Main
+
+log_info "Running version: ${__ScriptVersion}"
+log_info "Command line: '${__ScriptFullName} ${__ScriptArgs}'"
+
 trap _atexit INT TERM EXIT
 
 case $1 in
@@ -187,14 +295,23 @@
     prepare)
         prepare
         ;;
+    lint)
+        lint
+        ;;
     run)
         run
         ;;
     real-run)
         real_run
         ;;
+    model-validate)
+       prepare
+       run_model_validate
+        ;;
     *)
         prepare
+#        lint
         run
+        run_model_validate
         ;;
 esac
diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt
new file mode 100644
index 0000000..a0f561a
--- /dev/null
+++ b/tests/test-requirements.txt
@@ -0,0 +1,2 @@
+jsonschema
+reno