Merge remote-tracking branch 'target/master'
diff --git a/README.rst b/README.rst
index 65ad8cf..e3a8576 100644
--- a/README.rst
+++ b/README.rst
@@ -745,6 +745,28 @@
         override_rpf_default_by: 'disable'
       ...
 
+Cassandra GC logging
+--------------------
+
+From Contrail version 3 you can set a way you want to handle Cassandra GC logs.
+The behavior is controlled by `cassandra_gc_logging`. Valid values are
+'rotation' (default), 'legacy' and false.
+
+- 'rotation' is supported by JDK 6u34 7u2 or later and handles rotation of log
+files automatically.
+- 'legacy' is a way to support older JDKs and you will need to handle logs by
+other means. This can be handled for example by using
+`- service.opencontrail.database.cassandra_log_cleanup` in your reclass model.
+- false will disable the cassandra gc logging
+
+.. code-block:: yaml
+
+    opencontrail:
+      ...
+      database:
+        cassandra_gc_logging: false
+      ...
+
 
 Disable Contrail API authentication
 -----------------------------------
diff --git a/metadata/service/database/cassandra_log_cleanup.yml b/metadata/service/database/cassandra_log_cleanup.yml
new file mode 100644
index 0000000..d73980e
--- /dev/null
+++ b/metadata/service/database/cassandra_log_cleanup.yml
@@ -0,0 +1,12 @@
+parameters:
+  _param:
+    opencontrail_cassandra_gc_log_cleanup_mtime: '+7'
+    opencontrail_cassandra_gc_log_cleanup_dir: '/var/log/cassandra/'
+  linux:
+    system:
+      job:
+        cassandra_gc_logs_cleanup:
+          command: "find ${_param:opencontrail_cassandra_gc_log_cleanup_dir} -name 'gc-*.log' -type f -mtime ${_param:opencontrail_cassandra_gc_log_cleanup_mtime} -exec rm {} \\;"
+          identifier: cassandra_gc_logs_cleanup
+          hour: 0
+          minute: 0
diff --git a/metadata/service/test/single.yml b/metadata/service/test/single.yml
new file mode 100644
index 0000000..efb8d59
--- /dev/null
+++ b/metadata/service/test/single.yml
@@ -0,0 +1,44 @@
+applications:
+- opencontrail
+parameters:
+  opencontrail:
+    test:
+      enabled: True
+  #working dir full path to the dir where we want to have needed files
+      working_dir: '/opt/opencontrail_test'
+      url:
+        api: ${_param:opencontrail_control_address}
+        analytics: ${_param:opencontrail_analytics_address}
+      control:
+        host:
+          members:
+          - ${_param:opencontrail_control_node01_hostname}.${_param:cluster_domain}
+          - ${_param:opencontrail_control_node02_hostname}.${_param:cluster_domain}
+          - ${_param:opencontrail_control_node03_hostname}.${_param:cluster_domain}
+      analytics:
+        host:
+          members:
+          - ${_param:opencontrail_analytics_node01_hostname}.${_param:cluster_domain}
+          - ${_param:opencontrail_analytics_node02_hostname}.${_param:cluster_domain}
+          - ${_param:opencontrail_analytics_node03_hostname}.${_param:cluster_domain}
+      compute:
+        host:
+          members:
+          - ${_param:openstack_compute_node01_hostname}.${_param:cluster_domain}
+          - ${_param:openstack_compute_node02_hostname}.${_param:cluster_domain}
+      identity:
+        version: ${_param:keystone_version}
+        service_token: ${_param:keystone_service_token}
+        service_tenant: service
+        admin_tenant: admin
+        admin_name: admin
+        admin_password: ${_param:keystone_admin_password}
+        password: ${_param:keystone_admin_password}
+        cacert: '/etc/ssl/certs/ca-certificates.crt'
+        bind:
+          address: 0.0.0.0
+          private_address: ${_param:keystone_service_host}
+          private_port: 35357
+          public_address: ${_param:keystone_service_host}
+          public_port: 5000
+        region: RegionOne
diff --git a/opencontrail/files/3.0/database/cassandra-env.sh b/opencontrail/files/3.0/database/cassandra-env.sh
index ba438ec..2eb5fbd 100644
--- a/opencontrail/files/3.0/database/cassandra-env.sh
+++ b/opencontrail/files/3.0/database/cassandra-env.sh
@@ -237,6 +237,8 @@
     JVM_OPTS="$JVM_OPTS -XX:+UseCondCardMark"
 fi
 
+{% set cassandra_gc_logging = database.get('cassandra_gc_logging', 'rotation') %}
+{% if  cassandra_gc_logging %}
 # GC logging options -- uncomment to enable
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCDetails"
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCDateStamps"
@@ -245,13 +247,19 @@
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCApplicationStoppedTime"
 JVM_OPTS="$JVM_OPTS -XX:+PrintPromotionFailure"
 JVM_OPTS="$JVM_OPTS -XX:PrintFLSStatistics=1"
+{% if cassandra_gc_logging == 'legacy' %}
 JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc-`date +%s`.log"
+{% elif cassandra_gc_logging == 'rotation' %}
+#JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc-`date +%s`.log"
+
 # If you are using JDK 6u34 7u2 or later you can enable GC log rotation
 # don't stick the date in the log name if rotation is on.
-# JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc.log"
-# JVM_OPTS="$JVM_OPTS -XX:+UseGCLogFileRotation"
-# JVM_OPTS="$JVM_OPTS -XX:NumberOfGCLogFiles=10"
-# JVM_OPTS="$JVM_OPTS -XX:GCLogFileSize=10M"
+JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc.log"
+JVM_OPTS="$JVM_OPTS -XX:+UseGCLogFileRotation"
+JVM_OPTS="$JVM_OPTS -XX:NumberOfGCLogFiles=10"
+JVM_OPTS="$JVM_OPTS -XX:GCLogFileSize=10M"
+{% endif %}
+{% endif %}
 
 # Configure the following for JEMallocAllocator and if jemalloc is not available in the system 
 # library path (Example: /usr/local/lib/). Usually "make install" will do the right thing. 
diff --git a/opencontrail/files/3.0/server.properties b/opencontrail/files/3.0/server.properties
index 5560f5c..98b8c71 100644
--- a/opencontrail/files/3.0/server.properties
+++ b/opencontrail/files/3.0/server.properties
@@ -98,7 +98,7 @@
 
 # A size-based retention policy for logs. Segments are pruned from the log as long as the remaining
 # segments don't drop below log.retention.bytes.
-#log.retention.bytes=1073741824
+log.retention.bytes=268435456
 
 # The maximum size of a log segment file. When this size is reached a new log segment will be created.
 log.segment.bytes=268435456
diff --git a/opencontrail/files/4.0/database/cassandra-env.sh b/opencontrail/files/4.0/database/cassandra-env.sh
index ba438ec..2eb5fbd 100644
--- a/opencontrail/files/4.0/database/cassandra-env.sh
+++ b/opencontrail/files/4.0/database/cassandra-env.sh
@@ -237,6 +237,8 @@
     JVM_OPTS="$JVM_OPTS -XX:+UseCondCardMark"
 fi
 
+{% set cassandra_gc_logging = database.get('cassandra_gc_logging', 'rotation') %}
+{% if  cassandra_gc_logging %}
 # GC logging options -- uncomment to enable
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCDetails"
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCDateStamps"
@@ -245,13 +247,19 @@
 JVM_OPTS="$JVM_OPTS -XX:+PrintGCApplicationStoppedTime"
 JVM_OPTS="$JVM_OPTS -XX:+PrintPromotionFailure"
 JVM_OPTS="$JVM_OPTS -XX:PrintFLSStatistics=1"
+{% if cassandra_gc_logging == 'legacy' %}
 JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc-`date +%s`.log"
+{% elif cassandra_gc_logging == 'rotation' %}
+#JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc-`date +%s`.log"
+
 # If you are using JDK 6u34 7u2 or later you can enable GC log rotation
 # don't stick the date in the log name if rotation is on.
-# JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc.log"
-# JVM_OPTS="$JVM_OPTS -XX:+UseGCLogFileRotation"
-# JVM_OPTS="$JVM_OPTS -XX:NumberOfGCLogFiles=10"
-# JVM_OPTS="$JVM_OPTS -XX:GCLogFileSize=10M"
+JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc.log"
+JVM_OPTS="$JVM_OPTS -XX:+UseGCLogFileRotation"
+JVM_OPTS="$JVM_OPTS -XX:NumberOfGCLogFiles=10"
+JVM_OPTS="$JVM_OPTS -XX:GCLogFileSize=10M"
+{% endif %}
+{% endif %}
 
 # Configure the following for JEMallocAllocator and if jemalloc is not available in the system 
 # library path (Example: /usr/local/lib/). Usually "make install" will do the right thing. 
diff --git a/opencontrail/files/4.0/server.properties b/opencontrail/files/4.0/server.properties
index 5560f5c..98b8c71 100644
--- a/opencontrail/files/4.0/server.properties
+++ b/opencontrail/files/4.0/server.properties
@@ -98,7 +98,7 @@
 
 # A size-based retention policy for logs. Segments are pruned from the log as long as the remaining
 # segments don't drop below log.retention.bytes.
-#log.retention.bytes=1073741824
+log.retention.bytes=268435456
 
 # The maximum size of a log segment file. When this size is reached a new log segment will be created.
 log.segment.bytes=268435456
diff --git a/opencontrail/files/test_files/conftest.py b/opencontrail/files/test_files/conftest.py
new file mode 100644
index 0000000..6f39e4f
--- /dev/null
+++ b/opencontrail/files/test_files/conftest.py
@@ -0,0 +1,31 @@
+# pytest settings and fixtures
+from stepler import *
+from stepler.conftest import *  # noqa
+from stepler.conftest import __all__
+from stepler.conftest import pytest_plugins
+from stepler.glance.fixtures import *  # noqa
+from stepler.keystone.fixtures import *  # noqa
+from stepler.neutron.fixtures import *  # noqa
+from stepler.nova.fixtures import *  # noqa
+
+from vapor.fixtures.contrail import *  # noqa
+from vapor.fixtures.contrail_resources import *  # noqa
+from vapor.fixtures.different_tenants_resources import *  # noqa
+from vapor.fixtures.dns import *  # noqa
+from vapor.fixtures.images import *  # noqa
+from vapor.fixtures.instance_ip import *  # noqa
+from vapor.fixtures.ipams import *  # noqa
+from vapor.fixtures.lbaas import *  # noqa
+from vapor.fixtures.networks import *  # noqa
+from vapor.fixtures.nodes import *  # noqa
+from vapor.fixtures.policies import *  # noqa
+from vapor.fixtures.security_groups import *  # noqa
+from vapor.fixtures.service_chain import *  # noqa
+from vapor.fixtures.skip import *  # noqa
+from vapor.fixtures.subnets import *  # noqa
+from vapor.fixtures.system_services import *  # noqa
+from vapor.fixtures.virtual_interface import *  # noqa
+
+pytest_plugins = [
+    'vapor.plugins.xfail',
+]
diff --git a/opencontrail/files/test_files/exports.sh b/opencontrail/files/test_files/exports.sh
new file mode 100644
index 0000000..c23b652
--- /dev/null
+++ b/opencontrail/files/test_files/exports.sh
@@ -0,0 +1,18 @@
+{%- from "opencontrail/map.jinja" import test with context %}
+#!/bin/bash
+export OS_USERNAME=admin
+export OS_PASSWORD={{ test.identity.admin_password }}
+export OS_PROJECT_NAME=admin
+export OS_PROJECT_DOMAIN_NAME=Default
+export OS_AUTH_URL=http://{{ test.identity.bind.private_address }}:{{ test.identity.bind.private_port}}/
+export VIRTUAL_DISPLAY=1
+
+export OS_FAULTS_CLOUD_DRIVER=tcpcloud
+
+export CONTRAIL_ROLES_DISTRIBUTION_YAML={{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/roles.yaml
+export CONTRAIL_API_URL=http://{{ test.url.api }}:8082/
+export CONTRAIL_ANALYTICS_URL=http://{{ test.url.analytics}}:8081/
+
+export OS_FAULTS_CONFIG={{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/os_faults.json
+export K_PARAM='not destructive'
+export OPENRC_ACTIVATE_CMD='source {{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/keystonerc; source {{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/keystonercv3'
diff --git a/opencontrail/files/test_files/keystonerc b/opencontrail/files/test_files/keystonerc
new file mode 100644
index 0000000..4b148be
--- /dev/null
+++ b/opencontrail/files/test_files/keystonerc
@@ -0,0 +1,10 @@
+{% from 'opencontrail/map.jinja' import  test with context %}
+export OS_USERNAME={{ test.identity.admin_name }}
+export OS_PASSWORD={{ test.identity.admin_password }}
+export OS_TENANT_NAME={{ test.identity.admin_tenant }}
+export OS_AUTH_URL=http://{{ test.identity.bind.private_address }}:{{ test.identity.bind.private_port }}/v2.0
+export OS_REGION_NAME={{ test.identity.region }}
+export OS_SERVICE_TOKEN={{ test.identity.service_token }}
+export OS_SERVICE_ENDPOINT="http://{{ test.identity.bind.private_address }}:{{ test.identity.bind.private_port }}/v2.0/"
+export OS_ENDPOINT_TYPE="internal"
+export OS_CACERT="{{ test.identity.cacert }}"
diff --git a/opencontrail/files/test_files/keystonercv3 b/opencontrail/files/test_files/keystonercv3
new file mode 100644
index 0000000..fa37973
--- /dev/null
+++ b/opencontrail/files/test_files/keystonercv3
@@ -0,0 +1,13 @@
+{%- from "opencontrail/map.jinja" import test with context %}
+  export OS_IDENTITY_API_VERSION=3
+  export OS_AUTH_URL=http://{{ test.identity.bind.private_address }}:{{ test.identity.bind.private_port }}/v3
+  export OS_PROJECT_DOMAIN_NAME=Default
+  export OS_USER_DOMAIN_NAME=Default
+  export OS_PROJECT_NAME={{ test.identity.admin_tenant }}
+  export OS_TENANT_NAME={{ test.identity.admin_tenant }}
+  export OS_USERNAME={{ test.identity.admin_name }}
+  export OS_PASSWORD={{ test.identity.admin_password }}
+  export OS_REGION_NAME={{ test.identity.region }}
+  export OS_INTERFACE=internal
+  export OS_ENDPOINT_TYPE="internal"
+  export OS_CACERT="{{ test.identity.cacert }}"
diff --git a/opencontrail/files/test_files/os_faults.json b/opencontrail/files/test_files/os_faults.json
new file mode 100644
index 0000000..e607502
--- /dev/null
+++ b/opencontrail/files/test_files/os_faults.json
@@ -0,0 +1,20 @@
+{
+  'cloud_management': {
+    'driver': 'tcpcloud',
+    'args': {
+      'address': '127.0.0.1',
+      'username': 'root',
+      'master_sudo': True,
+      'slave_username': 'root',
+      'slave_name_regexp': '^(?!cfg)',
+    }
+},
+  'power_managements': [
+    {
+       'driver': 'libvirt',
+       'args': {
+          'connection_uri': "qemu+unix:///system"
+       }
+    }
+  ]
+}
diff --git a/opencontrail/files/test_files/pytest.ini b/opencontrail/files/test_files/pytest.ini
new file mode 100644
index 0000000..9ee6bdc
--- /dev/null
+++ b/opencontrail/files/test_files/pytest.ini
@@ -0,0 +1,9 @@
+#content of pytest.ini
+[pytest]
+markers =
+    smoke_test: mark test as Smoke.
+    performance_test: mark test as Performance.
+
+#addopts = -vv --color=yes --junit-xml=report.xml
+#Single line of addopts
+addopts = -vv --color=yes -ra -p stepler.third_party.destructive_dispatcher -p stepler.third_party.idempotent_id -p stepler.third_party.default_project --use-default-project
diff --git a/opencontrail/files/test_files/requirements.txt b/opencontrail/files/test_files/requirements.txt
new file mode 100644
index 0000000..8382052
--- /dev/null
+++ b/opencontrail/files/test_files/requirements.txt
@@ -0,0 +1,13 @@
+# Now it's better to use only gerrit repo because of bad syncing between gerrit and git
+git+https://review.gerrithub.io/Mirantis/stepler
+git+https://github.com/morganfainberg/positional.git
+git+https://github.com/gdyuldin/contrail-python-api@R3.0
+ansible==2.3.2.0
+dpath
+jmespath==0.9.0
+contextlib2==0.5.4; python_version < '3.2'
+kazoo==2.2.1
+logbook
+xmltodict
+pytest-html
+ansible==2.3.2.0
diff --git a/opencontrail/files/test_files/roles.yaml b/opencontrail/files/test_files/roles.yaml
new file mode 100644
index 0000000..f5ffb53
--- /dev/null
+++ b/opencontrail/files/test_files/roles.yaml
@@ -0,0 +1,24 @@
+{%- from "opencontrail/map.jinja" import test with context %}
+{%- set host_controls = test.control.host.members %}
+{%- set host_analytics = test.analytics.host.members %}
+{%- set host_computes = test.compute.host.members %}
+contrail-controller:
+{%- for host in host_controls %}
+  - {{ host }}
+{%- endfor %}
+contrail-config:
+{%- for host in host_controls %}
+  - {{ host }}
+{%- endfor %}
+contrail-analytics:
+{%- for host in host_analytics %}
+  - {{ host }}
+{%- endfor %}
+contrail-db:
+{%- for host in host_analytics %}
+  - {{ host }}
+{%- endfor %}
+contrail-compute:
+{%- for host in host_computes %}
+  - {{ host }}
+{%- endfor %}
diff --git a/opencontrail/map.jinja b/opencontrail/map.jinja
index c492828..c3edaef 100644
--- a/opencontrail/map.jinja
+++ b/opencontrail/map.jinja
@@ -145,7 +145,9 @@
   RedHat:
     pkgs:
          []
-
+test:
+  Debian:
+    pkgs: ['libssl-dev', 'python-dev', 'libvirt-dev', 'python-pip']
 {%- elif vendor == 'juniper' -%}
 
 common:
@@ -289,7 +291,9 @@
   RedHat:
     pkgs:
          []
-
+test:
+  Debian:
+    pkgs: ['libssl-dev', 'python-dev', 'libvirt-dev', 'python-pip']
 {%- endif %}
 {%- endload %}
 
@@ -301,6 +305,7 @@
 {% set database  = salt['grains.filter_by'](base_defaults['database'], merge=salt['pillar.get']('opencontrail:database', {}), base='database') %}
 {% set web       = salt['grains.filter_by'](base_defaults['web'], merge=salt['pillar.get']('opencontrail:web', {}), base='web') %}
 {% set client    = salt['grains.filter_by'](base_defaults['client'], merge=salt['pillar.get']('opencontrail:client', {}), base='client') %}
+{% set test      = salt['grains.filter_by'](base_defaults['test'], merge=salt['pillar.get']('opencontrail:test', {}), base='test') %}
 
 {% set monitoring = salt['grains.filter_by']({
     'default': {
diff --git a/opencontrail/test.sls b/opencontrail/test.sls
new file mode 100644
index 0000000..a36cf6c
--- /dev/null
+++ b/opencontrail/test.sls
@@ -0,0 +1,107 @@
+{%- from "opencontrail/map.jinja" import test with context  %}
+{%- if test.enabled %}
+
+opencontrail_test_packages:
+  pkg.installed:
+    - names: {{ test.pkgs }}
+    - force_yes: True
+
+install_python_packages:
+  pip.installed:
+    - names:
+      - pip
+      - tox
+      - setuptools
+    - require:
+      - pkg: opencontrail_test_packages
+
+install_vapor_and_dependencies:
+  pip.installed:
+    - requirements: salt://opencontrail/files/test_files/requirements.txt
+    - require:
+      - pip: install_python_packages
+
+{{ test.working_dir }}:
+  file.directory:
+    - user: root
+    - group: root
+    - dir_mode: 755
+    - require:
+      - pip: install_vapor_and_dependencies
+
+clone_fuel_plugin_contrail:
+  git.latest:
+    - name: https://github.com/openstack/fuel-plugin-contrail.git
+    - target: {{ test.working_dir }}/fuel-plugin-contrail
+    - require:
+      - file: {{ test.working_dir }}
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/roles.yaml:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/roles.yaml
+    - user: root
+    - group: root
+    - template: jinja
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/exports.sh:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/exports.sh
+    - user: root
+    - group: root
+    - template: jinja
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/os_faults.json:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/os_faults.json
+    - user: root
+    - group: root
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/pytest.ini:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/pytest.ini
+    - user: root
+    - group: root
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/requirements.txt:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/requirements.txt
+    - user: root
+    - group: root
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/vapor/conftest.py:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/conftest.py
+    - user: root
+    - group: root
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/keystonerc:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/keystonerc
+    - user: root
+    - group: root
+    - template: jinja
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{{ test.working_dir }}/fuel-plugin-contrail/plugin_test/vapor/keystonercv3:
+  file.managed:
+    - source: salt://opencontrail/files/test_files/keystonercv3
+    - user: root
+    - group: root
+    - template: jinja
+    - require:
+      - git: clone_fuel_plugin_contrail
+
+{%- endif %}