This is an automated email from the ASF dual-hosted git repository.

av pushed a commit to branch ignite-ducktape
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-ducktape by this push:
     new 36ed2c3  Ignite ducktape control sh (#8127)
36ed2c3 is described below

commit 36ed2c381811cc05d568cbfe49baf33087568539
Author: Ivan Daschinskiy <ivanda...@gmail.com>
AuthorDate: Mon Aug 10 16:38:56 2020 +0300

    Ignite ducktape control sh (#8127)
---
 .gitignore                                         |   1 +
 .travis.yml                                        |   8 +
 bin/control.sh                                     |  13 +-
 bin/include/build-classpath.sh                     |   7 +-
 modules/ducktests/tests/check_style.sh             |  26 ---
 .../ignitetest/services/utils/config/ignite.xml.j2 |   4 +-
 .../ignitetest/services/utils/control_utility.py   | 167 +++++++++++++++
 .../ignitetest/services/utils/ignite_aware.py      |  21 +-
 .../tests/ignitetest/tests/control_utility_test.py | 235 +++++++++++++++++++++
 .../tests/ignitetest/tests/pme_free_switch_test.py |   4 +-
 modules/ducktests/tests/{.pylintrc => tox.ini}     |  23 ++
 11 files changed, 461 insertions(+), 48 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1a8b46a..151ad01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,3 +87,4 @@ packages
 *.pyc
 /tests/venv
 modules/ducktests/tests/docker/build/**
+modules/ducktests/tests/.tox
diff --git a/.travis.yml b/.travis.yml
index 73117e8..afb930f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -50,3 +50,11 @@ matrix:
       dotnet: 3.1.101
       script:
         - dotnet build modules/platforms/dotnet/Apache.Ignite.DotNetCore.sln
+
+    - language: python
+      python:
+        - "3.8"
+      install: pip install tox-travis
+      before_script: cd modules/ducktests/tests
+      script:
+        - tox
diff --git a/bin/control.sh b/bin/control.sh
index 5d615d4..4493c6c 100755
--- a/bin/control.sh
+++ b/bin/control.sh
@@ -1,12 +1,9 @@
 #!/usr/bin/env bash
-if [ ! -z "${IGNITE_SCRIPT_STRICT_MODE:-}" ]
-then
-    set -o nounset
-    set -o errexit
-    set -o pipefail
-    set -o errtrace
-    set -o functrace
-fi
+set -o nounset
+set -o errexit
+set -o pipefail
+set -o errtrace
+set -o functrace
 
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
diff --git a/bin/include/build-classpath.sh b/bin/include/build-classpath.sh
index 9a43683..0625f41 100644
--- a/bin/include/build-classpath.sh
+++ b/bin/include/build-classpath.sh
@@ -47,14 +47,13 @@ includeToClassPath() {
 
     for file in $1/*
     do
-        if [[ -z "${EXCLUDE_MODULES}" ]] || [[ ${EXCLUDE_MODULES} != 
*"`basename $file`"* ]]; then
-            echo "$file included"
+        if [[ -z "${EXCLUDE_MODULES:-}" ]] || [[ ${EXCLUDE_MODULES:-} != 
*"`basename $file`"* ]]; then
             if [ -d ${file} ] && [ -d "${file}/target" ]; then
                 if [ -d "${file}/target/classes" ]; then
                     IGNITE_LIBS=${IGNITE_LIBS}${SEP}${file}/target/classes
                 fi
 
-                if [[ -z "${EXCLUDE_TEST_CLASSES}" ]]; then
+                if [[ -z "${EXCLUDE_TEST_CLASSES:-}" ]]; then
                   if [ -d "${file}/target/test-classes" ]; then
                       
IGNITE_LIBS=${IGNITE_LIBS}${SEP}${file}/target/test-classes
                   fi
@@ -68,7 +67,7 @@ includeToClassPath() {
           echo "$file excluded by EXCLUDE_MODULES settings"
         fi
     done
-    
+
     IFS=$SAVEIFS
 }
 
diff --git a/modules/ducktests/tests/check_style.sh 
b/modules/ducktests/tests/check_style.sh
deleted file mode 100755
index 39405a7..0000000
--- a/modules/ducktests/tests/check_style.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-if ! command -v pylint &> /dev/null
-then
-    echo "Please, install pylint first"
-    exit 1
-fi
-
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-pylint --rcfile="$SCRIPT_DIR"/.pylintrc "$SCRIPT_DIR"/ignitetest
diff --git 
a/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2 
b/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2
index 5ceb5b6..50d2c5c 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2
+++ b/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2
@@ -30,7 +30,9 @@
         </property>
 
         <property name="clientMode" value="{{ client_mode or False | lower 
}}"/>
-
+        {% if consistent_id %}
+            <property name="consistentId" value="{{ consistent_id }}"/>
+        {% endif %}
         {{ properties }}
     </bean>
 </beans>
diff --git 
a/modules/ducktests/tests/ignitetest/services/utils/control_utility.py 
b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py
new file mode 100644
index 0000000..36ff5bb
--- /dev/null
+++ b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py
@@ -0,0 +1,167 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This module contains control utility wrapper.
+"""
+import random
+import re
+from collections import namedtuple
+
+from ducktape.cluster.remoteaccount import RemoteCommandError
+
+
+class ControlUtility:
+    """
+    Control utility (control.sh) wrapper.
+    """
+    BASE_COMMAND = "control.sh"
+
+    def __init__(self, cluster, text_context):
+        self._cluster = cluster
+        self.logger = text_context.logger
+
+    def baseline(self):
+        """
+        :return Baseline nodes.
+        """
+        return self.cluster_state().baseline
+
+    def cluster_state(self):
+        """
+        :return: Cluster state.
+        """
+        output = self.__run("--baseline")
+
+        return self.__parse_cluster_state(output)
+
+    def set_baseline(self, baseline):
+        """
+        :param baseline: Baseline nodes or topology version to set as baseline.
+        """
+        if isinstance(baseline, int):
+            result = self.__run("--baseline version %d --yes" % baseline)
+        else:
+            result = self.__run("--baseline set %s --yes" %
+                                ",".join([node.account.externally_routable_ip 
for node in baseline]))
+
+        return self.__parse_cluster_state(result)
+
+    def add_to_baseline(self, nodes):
+        """
+        :param nodes: Nodes that should be added to baseline.
+        """
+        result = self.__run("--baseline add %s --yes" %
+                            ",".join([node.account.externally_routable_ip for 
node in nodes]))
+
+        return self.__parse_cluster_state(result)
+
+    def remove_from_baseline(self, nodes):
+        """
+        :param nodes: Nodes that should be removed to baseline.
+        """
+        result = self.__run("--baseline remove %s --yes" %
+                            ",".join([node.account.externally_routable_ip for 
node in nodes]))
+
+        return self.__parse_cluster_state(result)
+
+    def disable_baseline_auto_adjust(self):
+        """
+        Disable baseline auto adjust.
+        """
+        return self.__run("--baseline auto_adjust disable --yes")
+
+    def enable_baseline_auto_adjust(self, timeout=None):
+        """
+        Enable baseline auto adjust.
+        :param timeout: Auto adjust timeout in millis.
+        """
+        timeout_str = "timeout %d" % timeout if timeout else ""
+        return self.__run("--baseline auto_adjust enable %s --yes" % 
timeout_str)
+
+    def activate(self):
+        """
+        Activate cluster.
+        """
+        return self.__run("--activate --yes")
+
+    def deactivate(self):
+        """
+        Deactivate cluster.
+        """
+        return self.__run("--deactivate --yes")
+
+    @staticmethod
+    def __parse_cluster_state(output):
+        state_pattern = re.compile("Cluster state: ([^\\s]+)")
+        topology_pattern = re.compile("Current topology version: (\\d+)")
+        baseline_pattern = 
re.compile("Consistent(Id|ID)=([^\\s]+),\\sS(tate|TATE)=([^\\s]+),?(\\sOrder=(\\d+))?")
+
+        match = state_pattern.search(output)
+        state = match.group(1) if match else None
+
+        match = topology_pattern.search(output)
+        topology = int(match.group(1)) if match else None
+
+        baseline = [BaselineNode(consistent_id=m[1], state=m[3], 
order=int(m[5]) if m[5] else None)
+                    for m in baseline_pattern.findall(output)]
+
+        return ClusterState(state=state, topology_version=topology, 
baseline=baseline)
+
+    def __run(self, cmd):
+        node = random.choice(self.__alives())
+
+        self.logger.debug("Run command %s on node %s", cmd, node.name)
+
+        raw_output = node.account.ssh_capture(self.__form_cmd(node, cmd), 
allow_fail=True)
+        code, output = self.__parse_output(raw_output)
+
+        self.logger.debug("Output of command %s on node %s, exited with code 
%d, is %s", cmd, node.name, code, output)
+
+        if code != 0:
+            raise ControlUtilityError(node.account, cmd, code, output)
+
+        return output
+
+    def __form_cmd(self, node, cmd):
+        return self._cluster.path.script("%s --host %s %s" % 
(self.BASE_COMMAND, node.account.externally_routable_ip,
+                                                              cmd))
+
+    @staticmethod
+    def __parse_output(raw_output):
+        exit_code = raw_output.channel_file.channel.recv_exit_status()
+        output = "".join(raw_output)
+
+        pattern = re.compile("Command \\[[^\\s]*\\] finished with code: 
(\\d+)")
+        match = pattern.search(output)
+
+        if match:
+            return int(match.group(1)), output
+        return exit_code, output
+
+    def __alives(self):
+        return [node for node in self._cluster.nodes if 
self._cluster.alive(node)]
+
+
+BaselineNode = namedtuple("BaselineNode", ["consistent_id", "state", "order"])
+ClusterState = namedtuple("ClusterState", ["state", "topology_version", 
"baseline"])
+
+
+class ControlUtilityError(RemoteCommandError):
+    """
+    Error is raised when control utility failed.
+    """
+    def __init__(self, account, cmd, exit_status, output):
+        super(ControlUtilityError, self).__init__(account, cmd, exit_status, 
"".join(output))
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py 
b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
index 7fe97a9..2e292a3 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
@@ -18,10 +18,11 @@ This module contains the base class to build services aware 
of Ignite.
 """
 
 import os
-from abc import abstractmethod
+from abc import abstractmethod, ABCMeta
 
 from ducktape.services.background_thread import BackgroundThreadService
 from ducktape.utils.util import wait_until
+from six import add_metaclass
 
 from ignitetest.services.utils.ignite_config import IgniteLoggerConfig, 
IgniteServerConfig, IgniteClientConfig
 from ignitetest.services.utils.ignite_path import IgnitePath
@@ -29,6 +30,7 @@ from ignitetest.services.utils.jmx_utils import 
ignite_jmx_mixin
 from ignitetest.tests.utils.version import IgniteVersion
 
 
+@add_metaclass(ABCMeta)
 class IgniteAwareService(BackgroundThreadService):
     """
     The base class to build services aware of Ignite.
@@ -85,8 +87,14 @@ class IgniteAwareService(BackgroundThreadService):
         logger_config = IgniteLoggerConfig().render(work_dir=self.WORK_DIR)
 
         node.account.mkdirs(self.PERSISTENT_ROOT)
-        node.account.create_file(self.CONFIG_FILE, self.config().render(
-            config_dir=self.PERSISTENT_ROOT, work_dir=self.WORK_DIR, 
properties=self.properties))
+
+        node_config = self.config().render(config_dir=self.PERSISTENT_ROOT,
+                                           work_dir=self.WORK_DIR,
+                                           properties=self.properties,
+                                           
consistent_id=node.account.externally_routable_ip)
+
+        setattr(node, "consistent_id", node.account.externally_routable_ip)
+        node.account.create_file(self.CONFIG_FILE, node_config)
         node.account.create_file(self.LOG4J_CONFIG_FILE, logger_config)
 
     @abstractmethod
@@ -156,10 +164,9 @@ class IgniteAwareService(BackgroundThreadService):
         :param backoff_sec: Number of seconds to back off between each failure 
to meet the condition
                 before checking again.
         """
-        assert len(self.nodes) == 1
-
-        self.await_event_on_node(evt_message, self.nodes[0], timeout_sec, 
from_the_beginning=from_the_beginning,
-                                 backoff_sec=backoff_sec)
+        for node in self.nodes:
+            self.await_event_on_node(evt_message, node, timeout_sec, 
from_the_beginning=from_the_beginning,
+                                     backoff_sec=backoff_sec)
 
     def execute(self, command):
         """
diff --git a/modules/ducktests/tests/ignitetest/tests/control_utility_test.py 
b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py
new file mode 100644
index 0000000..027cc3c
--- /dev/null
+++ b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py
@@ -0,0 +1,235 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+This module contains control.sh utility tests.
+"""
+from ducktape.mark import parametrize
+from ducktape.mark.resource import cluster
+from ducktape.utils.util import wait_until
+from jinja2 import Template
+
+from ignitetest.services.ignite import IgniteService
+from ignitetest.services.utils.control_utility import ControlUtility, 
ControlUtilityError
+from ignitetest.tests.utils.ignite_test import IgniteTest
+from ignitetest.tests.utils.version import DEV_BRANCH, LATEST_2_8, 
IgniteVersion, LATEST_2_7, V_2_8_0
+
+
+# pylint: disable=W0223
+class BaselineTests(IgniteTest):
+    """
+    Tests baseline command
+    """
+    NUM_NODES = 3
+
+    CONFIG_TEMPLATE = """
+        {% if version > "2.9.0" %}
+            <property name="clusterStateOnStart" value="INACTIVE"/>
+        {%  else %}
+            <property name="activeOnStart" value="false"/>
+        {% endif %}
+        <property name="dataStorageConfiguration">
+            <bean 
class="org.apache.ignite.configuration.DataStorageConfiguration">
+                <property name="defaultDataRegionConfiguration">
+                    <bean 
class="org.apache.ignite.configuration.DataRegionConfiguration">
+                        <property name="persistenceEnabled" value="true"/>
+                        <property name="maxSize" value="#{100L * 1024 * 
1024}"/>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    """
+
+    @staticmethod
+    def properties(version):
+        """
+        Render properties for ignite node configuration.
+        """
+        return Template(BaselineTests.CONFIG_TEMPLATE) \
+            .render(version=version)
+
+    def __init__(self, test_context):
+        super(BaselineTests, self).__init__(test_context)
+        self.servers = None
+
+    @cluster(num_nodes=NUM_NODES)
+    @parametrize(version=str(DEV_BRANCH))
+    @parametrize(version=str(LATEST_2_8))
+    @parametrize(version=str(LATEST_2_7))
+    def test_baseline_set(self, version):
+        """
+        Test baseline set.
+        """
+        blt_size = self.NUM_NODES - 2
+        self.servers = self.__start_ignite_nodes(version, blt_size)
+
+        control_utility = ControlUtility(self.servers, self.test_context)
+        control_utility.activate()
+
+        # Check baseline of activated cluster.
+        baseline = control_utility.baseline()
+        self.__check_baseline_size(baseline, blt_size)
+        self.__check_nodes_in_baseline(self.servers.nodes, baseline)
+
+        # Set baseline using list of conststent ids.
+        new_node = self.__start_ignite_nodes(version, 1)
+        control_utility.set_baseline(self.servers.nodes + new_node.nodes)
+        blt_size += 1
+
+        baseline = control_utility.baseline()
+        self.__check_baseline_size(baseline, blt_size)
+        self.__check_nodes_in_baseline(new_node.nodes, baseline)
+
+        # Set baseline using topology version.
+        new_node = self.__start_ignite_nodes(version, 1)
+        _, version, _ = control_utility.cluster_state()
+        control_utility.set_baseline(version)
+        blt_size += 1
+
+        baseline = control_utility.baseline()
+        self.__check_baseline_size(baseline, blt_size)
+        self.__check_nodes_in_baseline(new_node.nodes, baseline)
+
+    @cluster(num_nodes=NUM_NODES)
+    @parametrize(version=str(DEV_BRANCH))
+    @parametrize(version=str(LATEST_2_8))
+    @parametrize(version=str(LATEST_2_7))
+    def test_baseline_add_remove(self, version):
+        """
+        Test add and remove nodes from baseline.
+        """
+        blt_size = self.NUM_NODES - 1
+        self.servers = self.__start_ignite_nodes(version, blt_size)
+
+        control_utility = ControlUtility(self.servers, self.test_context)
+
+        control_utility.activate()
+
+        # Add node to baseline.
+        new_node = self.__start_ignite_nodes(version, 1)
+        control_utility.add_to_baseline(new_node.nodes)
+        blt_size += 1
+
+        baseline = control_utility.baseline()
+        self.__check_baseline_size(baseline, blt_size)
+        self.__check_nodes_in_baseline(new_node.nodes, baseline)
+
+        # Expected failure (remove of online node is not allowed).
+        try:
+            control_utility.remove_from_baseline(new_node.nodes)
+
+            assert False, "Remove of online node from baseline should fail!"
+        except ControlUtilityError:
+            pass
+
+        # Remove of offline node from baseline.
+        new_node.stop()
+
+        self.servers.await_event("Node left topology", timeout_sec=30, 
from_the_beginning=True)
+
+        control_utility.remove_from_baseline(new_node.nodes)
+        blt_size -= 1
+
+        baseline = control_utility.baseline()
+        self.__check_baseline_size(baseline, blt_size)
+        self.__check_nodes_not_in_baseline(new_node.nodes, baseline)
+
+    @cluster(num_nodes=NUM_NODES)
+    @parametrize(version=str(DEV_BRANCH))
+    @parametrize(version=str(LATEST_2_8))
+    @parametrize(version=str(LATEST_2_7))
+    def test_activate_deactivate(self, version):
+        """
+        Test activate and deactivate cluster.
+        """
+        self.servers = self.__start_ignite_nodes(version, self.NUM_NODES)
+
+        control_utility = ControlUtility(self.servers, self.test_context)
+
+        control_utility.activate()
+
+        state, _, _ = control_utility.cluster_state()
+
+        assert state.lower() == 'active', 'Unexpected state %s' % state
+
+        control_utility.deactivate()
+
+        state, _, _ = control_utility.cluster_state()
+
+        assert state.lower() == 'inactive', 'Unexpected state %s' % state
+
+    @cluster(num_nodes=NUM_NODES)
+    @parametrize(version=str(DEV_BRANCH))
+    @parametrize(version=str(LATEST_2_8))
+    def test_baseline_autoadjust(self, version):
+        """
+        Test activate and deactivate cluster.
+        """
+        if version < V_2_8_0:
+            self.logger.info("Skipping test because this feature is not 
supported for version %s" % version)
+            return
+
+        blt_size = self.NUM_NODES - 2
+        self.servers = self.__start_ignite_nodes(version, blt_size)
+
+        control_utility = ControlUtility(self.servers, self.test_context)
+        control_utility.activate()
+
+        # Add node.
+        control_utility.enable_baseline_auto_adjust(2000)
+        new_node = self.__start_ignite_nodes(version, 1)
+        blt_size += 1
+
+        wait_until(lambda: len(control_utility.baseline()) == blt_size, 
timeout_sec=5)
+
+        baseline = control_utility.baseline()
+        self.__check_nodes_in_baseline(new_node.nodes, baseline)
+
+        # Add node when auto adjust disabled.
+        control_utility.disable_baseline_auto_adjust()
+        old_topology = control_utility.cluster_state().topology_version
+        new_node = self.__start_ignite_nodes(version, 1)
+
+        wait_until(lambda: control_utility.cluster_state().topology_version != 
old_topology, timeout_sec=5)
+        baseline = control_utility.baseline()
+        self.__check_nodes_not_in_baseline(new_node.nodes, baseline)
+
+    @staticmethod
+    def __check_nodes_in_baseline(nodes, baseline):
+        blset = set(node.consistent_id for node in baseline)
+
+        for node in nodes:
+            assert node.consistent_id in blset
+
+    @staticmethod
+    def __check_nodes_not_in_baseline(nodes, baseline):
+        blset = set(node.consistent_id for node in baseline)
+
+        for node in nodes:
+            assert node.consistent_id not in blset
+
+    @staticmethod
+    def __check_baseline_size(baseline, size):
+        assert len(baseline) == size, 'Unexpected size of baseline %d, %d 
expected' % (len(baseline), size)
+
+    def __start_ignite_nodes(self, version, num_nodes, timeout_sec=180):
+        ignite_version = IgniteVersion(version)
+
+        servers = IgniteService(self.test_context, num_nodes=num_nodes, 
version=ignite_version,
+                                properties=self.properties(ignite_version))
+
+        servers.start(timeout_sec=timeout_sec)
+
+        return servers
diff --git a/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py 
b/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py
index cd9feec..108fa9d 100644
--- a/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py
+++ b/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py
@@ -24,6 +24,7 @@ from ducktape.mark.resource import cluster
 
 from ignitetest.services.ignite import IgniteService
 from ignitetest.services.ignite_app import IgniteApplicationService
+from ignitetest.services.utils.control_utility import ControlUtility
 from ignitetest.tests.utils.ignite_test import IgniteTest
 from ignitetest.tests.utils.version import DEV_BRANCH, LATEST_2_7, V_2_8_0, 
IgniteVersion
 
@@ -106,8 +107,7 @@ class PmeFreeSwitchTest(IgniteTest):
         single_key_tx_streamer.start()
 
         if ignite_version >= V_2_8_0:
-            long_tx_streamer.execute(
-                "control.sh --host %s --baseline auto_adjust disable --yes" % 
ignites.nodes[0].account.hostname)
+            ControlUtility(ignites, 
self.test_context).disable_baseline_auto_adjust()
 
         self.stage("Stopping server node")
 
diff --git a/modules/ducktests/tests/.pylintrc b/modules/ducktests/tests/tox.ini
similarity index 73%
rename from modules/ducktests/tests/.pylintrc
rename to modules/ducktests/tests/tox.ini
index 73536f3..8a7235b 100644
--- a/modules/ducktests/tests/.pylintrc
+++ b/modules/ducktests/tests/tox.ini
@@ -12,6 +12,29 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+[tox]
+envlist = linter
+skipsdist = True
+
+[travis]
+python =
+  3.8: linter
+
+[testenv]
+envdir = {homedir}/.virtualenvs/ignite-ducktests-{envname}
+deps =
+    ducktape==0.7.8
+    requests==2.20.0
+    monotonic
+    mock
+    pytest
+    pylint
+
+[testenv:linter]
+basepython = python3.8
+commands =
+    pylint --rcfile=tox.ini ./ignitetest
+
 [BASIC]
 min-public-methods=0
 # TODO: Remove after migrating to python 3

Reply via email to