You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by av...@apache.org on 2020/12/25 08:27:50 UTC
[ignite] branch ignite-ducktape updated: IGNITE-13882 Add
parametrization to ducktape install_root and persistence_root (#8600)
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 cbf8bdf IGNITE-13882 Add parametrization to ducktape install_root and persistence_root (#8600)
cbf8bdf is described below
commit cbf8bdfd05c316495d8e437e80d8fa92fd1088b3
Author: Ivan Daschinskiy <iv...@gmail.com>
AuthorDate: Fri Dec 25 11:27:21 2020 +0300
IGNITE-13882 Add parametrization to ducktape install_root and persistence_root (#8600)
---
.../ducktests/tests/ignitetest/services/ignite.py | 4 +-
.../tests/ignitetest/services/ignite_app.py | 2 +-
.../ducktests/tests/ignitetest/services/spark.py | 68 ++++----
.../ignitetest/services/utils/config_template.py | 6 +-
.../ignitetest/services/utils/control_utility.py | 2 +-
.../ignitetest/services/utils/ignite_aware.py | 54 +++++--
.../tests/ignitetest/services/utils/ignite_path.py | 56 -------
.../services/utils/ignite_persistence.py | 74 ---------
.../tests/ignitetest/services/utils/ignite_spec.py | 82 ++++++----
.../tests/ignitetest/services/utils/jmx_utils.py | 15 +-
.../tests/ignitetest/services/utils/path.py | 173 +++++++++++++++++++++
.../services/zk/templates/log4j.properties.j2 | 4 +-
.../services/zk/templates/zookeeper.properties.j2 | 2 +-
.../tests/ignitetest/services/zk/zookeeper.py | 78 ++++++----
.../ducktests/tests/ignitetest/tests/self_test.py | 39 +++++
modules/ducktests/tests/tox.ini | 1 -
16 files changed, 410 insertions(+), 250 deletions(-)
diff --git a/modules/ducktests/tests/ignitetest/services/ignite.py b/modules/ducktests/tests/ignitetest/services/ignite.py
index 7923dc7..af866b2 100644
--- a/modules/ducktests/tests/ignitetest/services/ignite.py
+++ b/modules/ducktests/tests/ignitetest/services/ignite.py
@@ -17,7 +17,6 @@
This module contains class to start ignite cluster node.
"""
-import os
import re
import signal
from datetime import datetime
@@ -32,7 +31,6 @@ class IgniteService(IgniteAwareService):
Ignite node service.
"""
APP_SERVICE_CLASS = "org.apache.ignite.startup.cmdline.CommandLineStartup"
- HEAP_DUMP_FILE = os.path.join(IgniteAwareService.PERSISTENT_ROOT, "ignite-heap.bin")
# pylint: disable=R0913
def __init__(self, context, config, num_nodes, jvm_opts=None, startup_timeout_sec=60, shutdown_timeout_sec=10,
@@ -42,7 +40,7 @@ class IgniteService(IgniteAwareService):
def clean_node(self, node):
node.account.kill_java_processes(self.APP_SERVICE_CLASS, clean_shutdown=False, allow_fail=True)
- node.account.ssh("sudo rm -rf -- %s" % self.PERSISTENT_ROOT, allow_fail=False)
+ node.account.ssh("rm -rf -- %s" % self.persistent_root, allow_fail=False)
def thread_dump(self, node):
"""
diff --git a/modules/ducktests/tests/ignitetest/services/ignite_app.py b/modules/ducktests/tests/ignitetest/services/ignite_app.py
index 8833582..502eb56 100644
--- a/modules/ducktests/tests/ignitetest/services/ignite_app.py
+++ b/modules/ducktests/tests/ignitetest/services/ignite_app.py
@@ -76,7 +76,7 @@ class IgniteApplicationService(IgniteAwareService):
node.account.kill_java_processes(self.servicejava_class_name, clean_shutdown=False, allow_fail=True)
- node.account.ssh("rm -rf %s" % self.PERSISTENT_ROOT, allow_fail=False)
+ node.account.ssh("rm -rf -- %s" % self.persistent_root, allow_fail=False)
def pids(self, node):
return node.account.java_pids(self.servicejava_class_name)
diff --git a/modules/ducktests/tests/ignitetest/services/spark.py b/modules/ducktests/tests/ignitetest/services/spark.py
index 09e99ac..39ec199 100644
--- a/modules/ducktests/tests/ignitetest/services/spark.py
+++ b/modules/ducktests/tests/ignitetest/services/spark.py
@@ -18,25 +18,22 @@ This module contains spark service class.
"""
import os.path
+from distutils.version import LooseVersion
from ducktape.cluster.remoteaccount import RemoteCommandError
from ducktape.services.background_thread import BackgroundThreadService
-from ignitetest.services.utils.ignite_persistence import PersistenceAware
+from ignitetest.services.utils.path import PathAware
from ignitetest.services.utils.log_utils import monitor_log
-class SparkService(BackgroundThreadService, PersistenceAware):
+# pylint: disable=abstract-method
+class SparkService(BackgroundThreadService, PathAware):
"""
Start a spark node.
"""
- INSTALL_DIR = "/opt/spark-{version}".format(version="2.3.4")
- SPARK_PERSISTENT_ROOT = "/mnt/spark"
-
- logs = {}
-
# pylint: disable=R0913
- def __init__(self, context, num_nodes=3):
+ def __init__(self, context, num_nodes=3, version=LooseVersion("2.3.4")):
"""
:param context: test context
:param num_nodes: number of Ignite nodes.
@@ -44,16 +41,20 @@ class SparkService(BackgroundThreadService, PersistenceAware):
super().__init__(context, num_nodes)
self.log_level = "DEBUG"
+ self._version = version
+ self.init_logs_attribute()
- for node in self.nodes:
- self.logs["master_logs" + node.account.hostname] = {
- "path": self.master_log_path(node),
- "collect_default": True
- }
- self.logs["worker_logs" + node.account.hostname] = {
- "path": self.slave_log_path(node),
- "collect_default": True
- }
+ @property
+ def project(self):
+ return "spark"
+
+ @property
+ def version(self):
+ return self._version
+
+ @property
+ def globals(self):
+ return self.context.globals
def start(self, clean=True):
BackgroundThreadService.start(self, clean=clean)
@@ -69,14 +70,25 @@ class SparkService(BackgroundThreadService, PersistenceAware):
else:
script = "start-slave.sh spark://{spark_master}:7077".format(spark_master=self.nodes[0].account.hostname)
- start_script = os.path.join(SparkService.INSTALL_DIR, "sbin", script)
+ start_script = os.path.join(self.home_dir, "sbin", script)
- cmd = "export SPARK_LOG_DIR={spark_dir}; ".format(spark_dir=SparkService.SPARK_PERSISTENT_ROOT)
- cmd += "export SPARK_WORKER_DIR={spark_dir}; ".format(spark_dir=SparkService.SPARK_PERSISTENT_ROOT)
+ cmd = "export SPARK_LOG_DIR={spark_dir}; ".format(spark_dir=self.persistent_root)
+ cmd += "export SPARK_WORKER_DIR={spark_dir}; ".format(spark_dir=self.persistent_root)
cmd += "{start_script} &".format(start_script=start_script)
return cmd
+ def init_logs_attribute(self):
+ for node in self.nodes:
+ self.logs["master_logs" + node.account.hostname] = {
+ "path": self.master_log_path(node),
+ "collect_default": True
+ }
+ self.logs["worker_logs" + node.account.hostname] = {
+ "path": self.slave_log_path(node),
+ "collect_default": True
+ }
+
def start_node(self, node):
self.init_persistent(node)
@@ -103,9 +115,9 @@ class SparkService(BackgroundThreadService, PersistenceAware):
def stop_node(self, node):
if node == self.nodes[0]:
- node.account.ssh(os.path.join(SparkService.INSTALL_DIR, "sbin", "stop-master.sh"))
+ node.account.ssh(os.path.join(self.home_dir, "sbin", "stop-master.sh"))
else:
- node.account.ssh(os.path.join(SparkService.INSTALL_DIR, "sbin", "stop-slave.sh"))
+ node.account.ssh(os.path.join(self.home_dir, "sbin", "stop-slave.sh"))
def clean_node(self, node):
"""
@@ -113,7 +125,7 @@ class SparkService(BackgroundThreadService, PersistenceAware):
"""
node.account.kill_java_processes(self.java_class_name(node),
clean_shutdown=False, allow_fail=True)
- node.account.ssh("sudo rm -rf -- %s" % SparkService.SPARK_PERSISTENT_ROOT, allow_fail=False)
+ node.account.ssh("rm -rf -- %s" % self.persistent_root, allow_fail=False)
def pids(self, node):
"""
@@ -135,26 +147,24 @@ class SparkService(BackgroundThreadService, PersistenceAware):
return "org.apache.spark.deploy.worker.Worker"
- @staticmethod
- def master_log_path(node):
+ def master_log_path(self, node):
"""
:param node: Spark master node.
:return: Path to log file.
"""
return "{SPARK_LOG_DIR}/spark-{userID}-org.apache.spark.deploy.master.Master-{instance}-{host}.out".format(
- SPARK_LOG_DIR=SparkService.SPARK_PERSISTENT_ROOT,
+ SPARK_LOG_DIR=self.persistent_root,
userID=node.account.user,
instance=1,
host=node.account.hostname)
- @staticmethod
- def slave_log_path(node):
+ def slave_log_path(self, node):
"""
:param node: Spark slave node.
:return: Path to log file.
"""
return "{SPARK_LOG_DIR}/spark-{userID}-org.apache.spark.deploy.worker.Worker-{instance}-{host}.out".format(
- SPARK_LOG_DIR=SparkService.SPARK_PERSISTENT_ROOT,
+ SPARK_LOG_DIR=self.persistent_root,
userID=node.account.user,
instance=1,
host=node.account.hostname)
diff --git a/modules/ducktests/tests/ignitetest/services/utils/config_template.py b/modules/ducktests/tests/ignitetest/services/utils/config_template.py
index 875f12b..91c279f 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/config_template.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/config_template.py
@@ -20,8 +20,8 @@ import os
from jinja2 import FileSystemLoader, Environment
-DEFAULT_CONFIG_PATH = os.path.dirname(os.path.abspath(__file__)) + "/templates"
-DEFAULT_IGNITE_CONF = DEFAULT_CONFIG_PATH + "/ignite.xml.j2"
+DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
+DEFAULT_IGNITE_CONF = os.path.join(DEFAULT_CONFIG_PATH, "ignite.xml.j2")
class ConfigTemplate:
@@ -69,4 +69,4 @@ class IgniteLoggerConfigTemplate(ConfigTemplate):
Ignite logger configuration.
"""
def __init__(self):
- super().__init__(DEFAULT_CONFIG_PATH + "/log4j.xml.j2")
+ super().__init__(os.path.join(DEFAULT_CONFIG_PATH, "log4j.xml.j2"))
diff --git a/modules/ducktests/tests/ignitetest/services/utils/control_utility.py b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py
index 90b6549..9b1a9ea 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/control_utility.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py
@@ -264,7 +264,7 @@ class ControlUtility:
return output
def __form_cmd(self, node, cmd):
- return self._cluster.spec.path.script(f"{self.BASE_COMMAND} --host {node.account.externally_routable_ip} {cmd}")
+ return self._cluster.script(f"{self.BASE_COMMAND} --host {node.account.externally_routable_ip} {cmd}")
@staticmethod
def __parse_output(raw_output):
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
index 06fbe32..cf0e9c7 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
@@ -29,19 +29,18 @@ from ducktape.services.background_thread import BackgroundThreadService
from ducktape.utils.util import wait_until
from ignitetest.services.utils.concurrent import CountDownLatch, AtomicValue
-from ignitetest.services.utils.ignite_persistence import IgnitePersistenceAware
+from ignitetest.services.utils.path import IgnitePathAware
from ignitetest.services.utils.ignite_spec import resolve_spec
from ignitetest.services.utils.jmx_utils import ignite_jmx_mixin
from ignitetest.services.utils.log_utils import monitor_log
-class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metaclass=ABCMeta):
+# pylint: disable=too-many-public-methods
+class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABCMeta):
"""
The base class to build services aware of Ignite.
"""
- NETFILTER_STORE_PATH = os.path.join(IgnitePersistenceAware.TEMP_DIR, "iptables.bak")
-
# pylint: disable=R0913
def __init__(self, context, config, num_nodes, startup_timeout_sec, shutdown_timeout_sec, **kwargs):
"""
@@ -58,10 +57,23 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
self.shutdown_timeout_sec = shutdown_timeout_sec
self.spec = resolve_spec(self, context, config, **kwargs)
+ self.init_logs_attribute()
self.disconnected_nodes = []
self.killed = False
+ @property
+ def version(self):
+ return self.config.version
+
+ @property
+ def project(self):
+ return self.spec.project
+
+ @property
+ def globals(self):
+ return self.context.globals
+
def start_async(self, clean=True):
"""
Starts in async way.
@@ -89,7 +101,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
wait_until(lambda: self.alive(node), timeout_sec=10)
- ignite_jmx_mixin(node, self.pids(node))
+ ignite_jmx_mixin(node, self.spec, self.pids(node))
def stop_async(self):
"""
@@ -159,7 +171,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
node_config = self._prepare_config(node)
- node.account.create_file(self.CONFIG_FILE, node_config)
+ node.account.create_file(self.config_file, node_config)
def _prepare_config(self, node):
if not self.config.consistent_id:
@@ -171,7 +183,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
config.discovery_spi.prepare_on_start(cluster=self)
- node_config = self.spec.config_template.render(config_dir=self.PERSISTENT_ROOT, work_dir=self.WORK_DIR,
+ node_config = self.spec.config_template.render(config_dir=self.persistent_root, work_dir=self.work_dir,
config=config)
setattr(node, "consistent_id", node.account.externally_routable_ip)
@@ -190,7 +202,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
# pylint: disable=W0613
def _worker(self, idx, node):
- cmd = self.spec.command(node.log_file)
+ cmd = self.spec.command(node)
self.logger.debug("Attempting to start Application Service on %s with command: %s" % (str(node.account), cmd))
@@ -277,6 +289,13 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
task(node)
+ @property
+ def netfilter_store_path(self):
+ """
+ :return: path to store backup of iptables filter
+ """
+ return os.path.join(self.temp_dir, "iptables.bak")
+
def drop_network(self, nodes=None):
"""
Disconnects node from cluster.
@@ -311,12 +330,12 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
def __backup_iptables(self, nodes):
# Store current network filter settings.
for node in nodes:
- cmd = "sudo iptables-save | tee " + IgniteAwareService.NETFILTER_STORE_PATH
+ cmd = f"sudo iptables-save | tee {self.netfilter_store_path}"
exec_error = str(node.account.ssh_client.exec_command(cmd)[2].read(), sys.getdefaultencoding())
if "Warning: iptables-legacy tables present" in exec_error:
- cmd = "sudo iptables-legacy-save | tee " + IgniteAwareService.NETFILTER_STORE_PATH
+ cmd = f"sudo iptables-legacy-save | tee {self.netfilter_store_path}"
exec_error = str(node.account.ssh_client.exec_command(cmd)[2].read(), sys.getdefaultencoding())
@@ -330,7 +349,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
def __restore_iptables(self):
# Restore previous network filter settings.
- cmd = "sudo iptables-restore < " + IgniteAwareService.NETFILTER_STORE_PATH
+ cmd = f"sudo iptables-restore < {self.netfilter_store_path}"
errors = []
@@ -359,8 +378,13 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl
"""
Update the node log file.
"""
- cnt = list(node.account.ssh_capture(f'ls {self.LOGS_DIR} | '
- f'grep -E "^console_[0-9]*.log$" | '
- f'wc -l', callback=int))[0]
+ if not hasattr(node, 'log_file'):
+ node.log_file = os.path.join(self.log_dir, "console.log")
- node.log_file = self.STDOUT_STDERR_CAPTURE.replace('.log', f'_{cnt + 1}.log')
+ cnt = list(node.account.ssh_capture(f'ls {self.log_dir} | '
+ f'grep -E "^console.log(.[0-9]+)?$" | '
+ f'wc -l', callback=int))[0]
+ if cnt > 0:
+ rotated_log = os.path.join(self.log_dir, f"console.log.{cnt}")
+ self.logger.debug(f"rotating {node.log_file} to {rotated_log} on {node.name}")
+ node.account.ssh(f"mv {node.log_file} {rotated_log}")
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py
deleted file mode 100644
index 4089112..0000000
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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 ignite path resolve utilities.
-"""
-
-import os
-
-
-class IgnitePath:
- """Path resolver for Ignite system tests which assumes the following layout:
-
- /opt/ignite-dev # Current version of Ignite under test
- /opt/ignite-2.7.6 # Example of an older version of Ignite installed from tarball
- /opt/ignite-<version> # Other previous versions of Ignite
- ...
- """
- SCRATCH_ROOT = "/mnt"
- IGNITE_INSTALL_ROOT = "/opt"
-
- def __init__(self, version, project="ignite"):
- self.version = version
- home_dir = "%s-%s" % (project, str(self.version))
- self.home = os.path.join(IgnitePath.IGNITE_INSTALL_ROOT, home_dir)
-
- def module(self, module_name):
- """
- :param module_name: name of Ignite optional lib
- :return: absolute path to the specified module
- """
- if self.version.is_dev:
- module_path = os.path.join("modules", module_name, "target")
- else:
- module_path = os.path.join("libs", "optional", "ignite-%s" % module_name)
-
- return os.path.join(self.home, module_path)
-
- def script(self, script_name):
- """
- :param script_name: name of Ignite script
- :return: absolute path to the specified script
- """
- return os.path.join(self.home, "bin", script_name)
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py
deleted file mode 100644
index 4e018cc..0000000
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# 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 classes that represent persistent artifacts of tests
-"""
-
-import os
-
-from ignitetest.services.utils.config_template import IgniteLoggerConfigTemplate
-
-
-class PersistenceAware:
- """
- This class contains basic persistence artifacts
- """
- # Root directory for persistent output
- PERSISTENT_ROOT = "/mnt/service"
- TEMP_DIR = os.path.join(PERSISTENT_ROOT, "tmp")
- LOGS_DIR = os.path.join(PERSISTENT_ROOT, "logs")
- STDOUT_STDERR_CAPTURE = os.path.join(LOGS_DIR, "console.log")
-
- logs = {
- "console_log": {
- "path": LOGS_DIR,
- "collect_default": True
- }
- }
-
- def init_persistent(self, node):
- """
- Init persistent directory.
- :param node: Service node.
- """
- node.account.mkdirs(self.PERSISTENT_ROOT)
- node.account.mkdirs(self.TEMP_DIR)
- node.account.mkdirs(self.LOGS_DIR)
-
-
-class IgnitePersistenceAware(PersistenceAware):
- """
- This class contains Ignite persistence artifacts
- """
- WORK_DIR = os.path.join(PersistenceAware.PERSISTENT_ROOT, "work")
- CONFIG_FILE = os.path.join(PersistenceAware.PERSISTENT_ROOT, "ignite-config.xml")
- LOG4J_CONFIG_FILE = os.path.join(PersistenceAware.PERSISTENT_ROOT, "ignite-log4j.xml")
-
- def __getattribute__(self, item):
- if item == 'logs':
- return PersistenceAware.logs
-
- return super().__getattribute__(item)
-
- def init_persistent(self, node):
- """
- Init persistent directory.
- :param node: Ignite service node.
- """
- super().init_persistent(node)
-
- logger_config = IgniteLoggerConfigTemplate().render(work_dir=self.WORK_DIR)
- node.account.create_file(self.LOG4J_CONFIG_FILE, logger_config)
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
index 6b9ba9f..489c887 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
@@ -20,11 +20,11 @@ This module contains Spec classes that describes config and command line to star
import base64
import importlib
import json
+import os
from abc import ABCMeta, abstractmethod
from ignitetest.services.utils.config_template import IgniteClientConfigTemplate, IgniteServerConfigTemplate
-from ignitetest.services.utils.ignite_path import IgnitePath
-from ignitetest.services.utils.ignite_persistence import IgnitePersistenceAware
+from ignitetest.services.utils.path import get_home_dir, get_module_path
from ignitetest.utils.version import DEV_BRANCH
@@ -46,10 +46,11 @@ def resolve_spec(service, context, config, **kwargs):
return len(impl_filter) > 0
if is_impl("IgniteService"):
- return _resolve_spec("NodeSpec", ApacheIgniteNodeSpec)(config=config, **kwargs)
+ return _resolve_spec("NodeSpec", ApacheIgniteNodeSpec)(path_aware=service, config=config, **kwargs)
if is_impl("IgniteApplicationService"):
- return _resolve_spec("AppSpec", ApacheIgniteApplicationSpec)(context=context, config=config, **kwargs)
+ return _resolve_spec("AppSpec", ApacheIgniteApplicationSpec)(path_aware=service, context=context,
+ config=config, **kwargs)
raise Exception("There is no specification for class %s" % type(service))
@@ -58,12 +59,13 @@ class IgniteSpec(metaclass=ABCMeta):
"""
This class is a basic Spec
"""
- def __init__(self, config, project, jvm_opts):
- self.version = config.version
- self.path = IgnitePath(self.version, project)
+ def __init__(self, path_aware, config, project, jvm_opts):
+ self.project = project
+ self.path_aware = path_aware
self.envs = {}
self.jvm_opts = jvm_opts or []
self.config = config
+ self.version = config.version
@property
def config_template(self):
@@ -74,8 +76,22 @@ class IgniteSpec(metaclass=ABCMeta):
return IgniteClientConfigTemplate()
return IgniteServerConfigTemplate()
+ def __home(self, version=None):
+ """
+ Get home directory for current spec.
+ """
+ version = version if version else self.version
+ return get_home_dir(self.path_aware.install_root, self.project, version)
+
+ def _module(self, name):
+ """
+ Get module path for current spec.
+ """
+ version = DEV_BRANCH if name == "ducktests" else self.version
+ return get_module_path(self.__home(version), name, version)
+
@abstractmethod
- def command(self):
+ def command(self, node):
"""
:return: string that represents command to run service on a node
"""
@@ -95,23 +111,22 @@ class IgniteSpec(metaclass=ABCMeta):
return " ".join(opts)
-class IgniteNodeSpec(IgniteSpec, IgnitePersistenceAware):
+class IgniteNodeSpec(IgniteSpec):
"""
Spec to run ignite node
"""
- # pylint: disable=W0221
- def command(self, stdout_stderr):
+ def command(self, node):
cmd = "%s %s %s %s 2>&1 | tee -a %s &" % \
(self._envs(),
- self.path.script("ignite.sh"),
+ self.path_aware.script("ignite.sh"),
self._jvm_opts(),
- self.CONFIG_FILE,
- stdout_stderr)
+ self.path_aware.config_file,
+ node.log_file)
return cmd
-class IgniteApplicationSpec(IgniteSpec, IgnitePersistenceAware):
+class IgniteApplicationSpec(IgniteSpec):
"""
Spec to run ignite application
"""
@@ -123,18 +138,18 @@ class IgniteApplicationSpec(IgniteSpec, IgnitePersistenceAware):
return ",".join(self.args)
# pylint: disable=W0221
- def command(self, stdout_stderr):
+ def command(self, node):
cmd = "%s %s %s %s 2>&1 | tee -a %s &" % \
(self._envs(),
- self.path.script("ignite.sh"),
+ self.path_aware.script("ignite.sh"),
self._jvm_opts(),
self._app_args(),
- stdout_stderr)
+ node.log_file)
return cmd
-class ApacheIgniteNodeSpec(IgniteNodeSpec, IgnitePersistenceAware):
+class ApacheIgniteNodeSpec(IgniteNodeSpec):
"""
Implementation IgniteNodeSpec for Apache Ignite project
"""
@@ -143,24 +158,24 @@ class ApacheIgniteNodeSpec(IgniteNodeSpec, IgnitePersistenceAware):
libs = (modules or [])
libs.append("log4j")
- libs = list(map(lambda m: self.path.module(m) + "/*", libs))
+ libs = list(map(lambda m: os.path.join(self._module(m), "*"), libs))
- libs.append(IgnitePath(DEV_BRANCH).module("ducktests") + "/*")
+ libs.append(os.path.join(self._module("ducktests"), "*"))
self.envs = {
'EXCLUDE_TEST_CLASSES': 'true',
- 'IGNITE_LOG_DIR': self.PERSISTENT_ROOT,
+ 'IGNITE_LOG_DIR': self.path_aware.persistent_root,
'USER_LIBS': ":".join(libs)
}
self.jvm_opts.extend([
- "-DIGNITE_SUCCESS_FILE=" + self.PERSISTENT_ROOT + "/success_file",
- "-Dlog4j.configuration=file:" + self.LOG4J_CONFIG_FILE,
+ "-DIGNITE_SUCCESS_FILE=" + os.path.join(self.path_aware.persistent_root, "success_file"),
+ "-Dlog4j.configuration=file:" + self.path_aware.log_config_file,
"-Dlog4j.configDebug=true"
])
-class ApacheIgniteApplicationSpec(IgniteApplicationSpec, IgnitePersistenceAware):
+class ApacheIgniteApplicationSpec(IgniteApplicationSpec):
"""
Implementation IgniteApplicationSpec for Apache Ignite project
"""
@@ -172,20 +187,20 @@ class ApacheIgniteApplicationSpec(IgniteApplicationSpec, IgnitePersistenceAware)
libs = modules or []
libs.extend(["log4j"])
- libs = [self.path.module(m) + "/*" for m in libs]
- libs.append(IgnitePath(DEV_BRANCH).module("ducktests") + "/*")
+ libs = list(map(lambda m: os.path.join(self._module(m), "*"), libs))
+ libs.append(os.path.join(self._module("ducktests"), "*"))
libs.extend(self.__jackson())
self.envs = {
"MAIN_CLASS": servicejava_class_name,
"EXCLUDE_TEST_CLASSES": "true",
- "IGNITE_LOG_DIR": self.PERSISTENT_ROOT,
+ "IGNITE_LOG_DIR": self.path_aware.persistent_root,
"USER_LIBS": ":".join(libs)
}
self.jvm_opts.extend([
- "-DIGNITE_SUCCESS_FILE=" + self.PERSISTENT_ROOT + "/success_file",
- "-Dlog4j.configuration=file:" + self.LOG4J_CONFIG_FILE,
+ "-DIGNITE_SUCCESS_FILE=" + os.path.join(self.path_aware.persistent_root, "success_file"),
+ "-Dlog4j.configuration=file:" + self.path_aware.log_config_file,
"-Dlog4j.configDebug=true",
"-DIGNITE_NO_SHUTDOWN_HOOK=true", # allows to perform operations on app termination.
"-Xmx1G",
@@ -196,13 +211,14 @@ class ApacheIgniteApplicationSpec(IgniteApplicationSpec, IgnitePersistenceAware)
self.args = [
str(start_ignite),
java_class_name,
- self.CONFIG_FILE,
+ self.path_aware.config_file,
str(base64.b64encode(json.dumps(params).encode('utf-8')), 'utf-8')
]
def __jackson(self):
- if not self.version.is_dev:
- aws = self.path.module("aws")
+ version = self.version
+ if not version.is_dev:
+ aws = self._module("aws")
return self.context.cluster.nodes[0].account.ssh_capture(
"ls -d %s/* | grep jackson | tr '\n' ':' | sed 's/.$//'" % aws)
diff --git a/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py b/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
index 925034b..a14d96d 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
@@ -17,19 +17,20 @@
This module contains JMX Console client and different utilities and mixins to retrieve ignite node parameters
and attributes.
"""
-
+import os
import re
from ignitetest.services.utils.decorators import memoize
-def ignite_jmx_mixin(node, pids):
+def ignite_jmx_mixin(node, spec, pids):
"""
Dynamically mixin JMX attributes to Ignite service node.
:param node: Ignite service node.
:param pids: Ignite service node pids.
"""
setattr(node, 'pids', pids)
+ setattr(node, 'spec', spec)
base_cls = node.__class__
base_cls_name = node.__class__.__name__
node.__class__ = type(base_cls_name, (base_cls, IgniteJmxMixin), {})
@@ -55,12 +56,18 @@ class JmxMBean:
class JmxClient:
"""JMX client, invokes jmxterm on node locally.
"""
- jmx_util_cmd = 'java -jar /opt/jmxterm.jar -v silent -n'
-
def __init__(self, node):
self.node = node
+ self.install_root = node.spec.path_aware.install_root
self.pid = node.pids[0]
+ @property
+ def jmx_util_cmd(self):
+ """
+ :return: jmxterm prepared command line invocation.
+ """
+ return os.path.join(f"java -jar {self.install_root}/jmxterm.jar -v silent -n")
+
@memoize
def find_mbean(self, pattern, domain='org.apache'):
"""
diff --git a/modules/ducktests/tests/ignitetest/services/utils/path.py b/modules/ducktests/tests/ignitetest/services/utils/path.py
new file mode 100644
index 0000000..9cab69d
--- /dev/null
+++ b/modules/ducktests/tests/ignitetest/services/utils/path.py
@@ -0,0 +1,173 @@
+# 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 classes that represent persistent artifacts of tests
+"""
+
+import os
+from abc import abstractmethod, ABCMeta
+
+from ignitetest.services.utils.config_template import IgniteLoggerConfigTemplate
+
+
+def get_home_dir(install_root, project, version):
+ """
+ Get path to binary release (home) directory depending on version.
+ """
+ return os.path.join(install_root, f"{project}-{version}")
+
+
+def get_module_path(project_dir, module_name, version):
+ """
+ Get absolute path to the specified module.
+ """
+ if version.is_dev:
+ module_path = os.path.join("modules", module_name, "target")
+ else:
+ module_path = os.path.join("libs", "optional", "ignite-%s" % module_name)
+
+ return os.path.join(project_dir, module_path)
+
+
+class PathAware:
+ """
+ Basic class for path configs.
+ """
+ def init_persistent(self, node):
+ """
+ Init persistent directory.
+ :param node: Service node.
+ """
+ node.account.mkdirs(f"{self.persistent_root} {self.temp_dir} {self.work_dir} {self.log_dir}")
+
+ def init_logs_attribute(self):
+ """
+ Initialize logs attribute for collecting logs by ducktape.
+ After changing to property based logs, will be removed.
+ """
+ setattr(self, 'logs', {
+ "logs": {
+ "path": self.log_dir,
+ "collect_default": True
+ }
+ })
+
+ @property
+ @abstractmethod
+ def config_file(self):
+ """
+ :return: path to project configuration file
+ """
+
+ @property
+ @abstractmethod
+ def log_config_file(self):
+ """
+ :return: path to logger configuration file
+ """
+
+ @property
+ def work_dir(self):
+ """
+ :return: path to work directory
+ """
+ return os.path.join(self.persistent_root, "work")
+
+ @property
+ def log_dir(self):
+ """
+ :return: path to log directory
+ """
+ return os.path.join(self.persistent_root, "logs")
+
+ @property
+ @abstractmethod
+ def project(self):
+ """
+ :return: project name, for example 'zookeeper' for Apache Zookeeper.
+ """
+
+ @property
+ @abstractmethod
+ def version(self):
+ """
+ :return: version of project.
+ """
+
+ @property
+ @abstractmethod
+ def globals(self):
+ """
+ :return: dictionary of globals variable (usually from test context).
+ """
+
+ @property
+ def home_dir(self):
+ """
+ :return: path to binary release (home) directory
+ """
+ return get_home_dir(self.install_root, self.project, self.version)
+
+ @property
+ def temp_dir(self):
+ """
+ :return: path to temp directory
+ """
+ return os.path.join(self.persistent_root, "tmp")
+
+ @property
+ def persistent_root(self):
+ """
+ :return: path to persistent root
+ """
+ return self.globals.get("persistent_root", "/mnt/service")
+
+ @property
+ def install_root(self):
+ """
+ :return: path to distributive installation root
+ """
+ return self.globals.get("install_root", "/opt")
+
+
+class IgnitePathAware(PathAware, metaclass=ABCMeta):
+ """
+ This class contains Ignite path configs.
+ """
+ def init_persistent(self, node):
+ """
+ Init persistent directory.
+ :param node: Ignite service node.
+ """
+ super().init_persistent(node)
+
+ logger_config = IgniteLoggerConfigTemplate().render(work_dir=self.work_dir)
+ node.account.create_file(self.log_config_file, logger_config)
+
+ @property
+ def config_file(self):
+ return os.path.join(self.persistent_root, "ignite-config.xml")
+
+ @property
+ def log_config_file(self):
+ return os.path.join(self.persistent_root, "ignite-log4j.xml")
+
+ def script(self, script_name):
+ """
+ :param script_name: name of Ignite script
+ :return: absolute path to the specified script
+ """
+ return os.path.join(self.home_dir, "bin", script_name)
diff --git a/modules/ducktests/tests/ignitetest/services/zk/templates/log4j.properties.j2 b/modules/ducktests/tests/ignitetest/services/zk/templates/log4j.properties.j2
index 507ec98..1f51c94 100644
--- a/modules/ducktests/tests/ignitetest/services/zk/templates/log4j.properties.j2
+++ b/modules/ducktests/tests/ignitetest/services/zk/templates/log4j.properties.j2
@@ -18,8 +18,8 @@
zookeeper.root.logger=INFO, FILE
zookeeper.console.threshold=INFO
-zookeeper.log.dir={{ PERSISTENT_ROOT }}
-zookeeper.log.file=zookeeper.log
+zookeeper.log.dir={{ log_dir }}
+zookeeper.log.file={{ LOG_FILENAME }}
zookeeper.log.threshold=INFO
zookeeper.log.maxfilesize=256MB
zookeeper.log.maxbackupindex=20
diff --git a/modules/ducktests/tests/ignitetest/services/zk/templates/zookeeper.properties.j2 b/modules/ducktests/tests/ignitetest/services/zk/templates/zookeeper.properties.j2
index 2ea8c3d..8a4e7b8 100644
--- a/modules/ducktests/tests/ignitetest/services/zk/templates/zookeeper.properties.j2
+++ b/modules/ducktests/tests/ignitetest/services/zk/templates/zookeeper.properties.j2
@@ -19,7 +19,7 @@ tickTime={{ settings.tick_time }}
minSessionTimeout={{ settings.min_session_timeout }}
initLimit={{ settings.init_limit }}
syncLimit={{ settings.sync_limit }}
-dataDir={{ DATA_DIR }}
+dataDir={{ data_dir }}
clientPort={{ settings.client_port }}
{% for node in nodes %}
server.{{ loop.index }}={{ node.account.hostname }}:2888:3888
diff --git a/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py b/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
index baf8848..b76ea99 100644
--- a/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
+++ b/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
@@ -18,11 +18,13 @@ This module contains classes and utilities to start zookeeper cluster for testin
"""
import os.path
+from distutils.version import LooseVersion
from ducktape.services.service import Service
from ducktape.utils.util import wait_until
from ignitetest.services.utils.log_utils import monitor_log
+from ignitetest.services.utils.path import PathAware
class ZookeeperSettings:
@@ -36,32 +38,48 @@ class ZookeeperSettings:
self.sync_limit = kwargs.get('sync_limit', 5)
self.client_port = kwargs.get('client_port', 2181)
+ version = kwargs.get("version")
+ if version:
+ if isinstance(version, str):
+ version = LooseVersion(version)
+ self.version = version
+ else:
+ self.version = LooseVersion("3.5.8")
+
assert self.tick_time <= self.min_session_timeout // 2, "'tick_time' must be <= 'min_session_timeout' / 2"
-class ZookeeperService(Service):
+class ZookeeperService(Service, PathAware):
"""
Zookeeper service.
"""
- PERSISTENT_ROOT = "/mnt/zookeeper"
- CONFIG_ROOT = os.path.join(PERSISTENT_ROOT, "conf")
- LOG_FILE = os.path.join(PERSISTENT_ROOT, "zookeeper.log")
- DATA_DIR = os.path.join(PERSISTENT_ROOT, "data")
- CONFIG_FILE = os.path.join(CONFIG_ROOT, "zookeeper.properties")
- LOG_CONFIG_FILE = os.path.join(CONFIG_ROOT, "log4j.properties")
- ZK_LIB_DIR = "/opt/zookeeper-3.5.8/lib"
-
- logs = {
- "zk_log": {
- "path": LOG_FILE,
- "collect_default": True
- }
- }
+ LOG_FILENAME = "zookeeper.log"
def __init__(self, context, num_nodes, settings=ZookeeperSettings(), start_timeout_sec=60):
super().__init__(context, num_nodes)
self.settings = settings
self.start_timeout_sec = start_timeout_sec
+ self.init_logs_attribute()
+
+ @property
+ def version(self):
+ return self.settings.version
+
+ @property
+ def globals(self):
+ return self.context.globals
+
+ @property
+ def log_config_file(self):
+ return os.path.join(self.persistent_root, "log4j.properties")
+
+ @property
+ def config_file(self):
+ return os.path.join(self.persistent_root, "zookeeper.properties")
+
+ @property
+ def project(self):
+ return "zookeeper"
def start(self, clean=True):
super().start(clean=clean)
@@ -77,19 +95,18 @@ class ZookeeperService(Service):
self.logger.info("Starting Zookeeper node %d on %s", idx, node.account.hostname)
- node.account.ssh("mkdir -p %s" % self.DATA_DIR)
- node.account.ssh("mkdir -p %s" % self.CONFIG_ROOT)
- node.account.ssh("echo %d > %s/myid" % (idx, self.DATA_DIR))
+ self.init_persistent(node)
+ node.account.ssh(f"echo {idx} > {self.work_dir}/myid")
- config_file = self.render('zookeeper.properties.j2', settings=self.settings)
- node.account.create_file(self.CONFIG_FILE, config_file)
+ config_file = self.render('zookeeper.properties.j2', settings=self.settings, data_dir=self.work_dir)
+ node.account.create_file(self.config_file, config_file)
self.logger.info("ZK config %s", config_file)
- log_config_file = self.render('log4j.properties.j2')
- node.account.create_file(self.LOG_CONFIG_FILE, log_config_file)
+ log_config_file = self.render('log4j.properties.j2', log_dir=self.log_dir)
+ node.account.create_file(self.log_config_file, log_config_file)
- start_cmd = "nohup java -cp %s/*:%s org.apache.zookeeper.server.quorum.QuorumPeerMain %s >/dev/null 2>&1 &" % \
- (self.ZK_LIB_DIR, self.CONFIG_ROOT, self.CONFIG_FILE)
+ start_cmd = f"nohup java -cp {os.path.join(self.home_dir, 'lib')}/*:{self.persistent_root} " \
+ f"org.apache.zookeeper.server.quorum.QuorumPeerMain {self.config_file} >/dev/null 2>&1 &"
node.account.ssh(start_cmd)
@@ -104,13 +121,20 @@ class ZookeeperService(Service):
:param node: Zookeeper service node.
:param timeout: Wait timeout.
"""
- with monitor_log(node, self.LOG_FILE, from_the_beginning=True) as monitor:
+ with monitor_log(node, self.log_file, from_the_beginning=True) as monitor:
monitor.wait_until(
"LEADER ELECTION TOOK",
timeout_sec=timeout,
- err_msg="Zookeeper quorum was not formed on %s" % node.account.hostname
+ err_msg=f"Zookeeper quorum was not formed on {node.account.hostname}"
)
+ @property
+ def log_file(self):
+ """
+ :return: current log file of node.
+ """
+ return os.path.join(self.log_dir, self.LOG_FILENAME)
+
@staticmethod
def java_class_name():
""" The class name of the Zookeeper quorum peers. """
@@ -150,7 +174,7 @@ class ZookeeperService(Service):
self.logger.warn("%s %s was still alive at cleanup time. Killing forcefully..." %
(self.__class__.__name__, node.account))
node.account.kill_process("zookeeper", clean_shutdown=False, allow_fail=True)
- node.account.ssh("rm -rf %s %s %s" % (self.CONFIG_ROOT, self.DATA_DIR, self.LOG_FILE), allow_fail=False)
+ node.account.ssh(f"rm -rf -- {self.persistent_root}", allow_fail=False)
def kill(self):
"""
diff --git a/modules/ducktests/tests/ignitetest/tests/self_test.py b/modules/ducktests/tests/ignitetest/tests/self_test.py
index e5f91e9..1d6e6ea 100644
--- a/modules/ducktests/tests/ignitetest/tests/self_test.py
+++ b/modules/ducktests/tests/ignitetest/tests/self_test.py
@@ -16,6 +16,7 @@
"""
This module contains smoke tests that checks that ducktape works as expected
"""
+import os
from ignitetest.services.ignite import IgniteService
from ignitetest.services.ignite_app import IgniteApplicationService
@@ -93,3 +94,41 @@ class SelfTest(IgniteTest):
client.stop()
ignites.stop()
+
+ @cluster(num_nodes=1)
+ @ignite_versions(str(DEV_BRANCH))
+ def test_logs_rotation(self, ignite_version):
+ """
+ Test logs rotation after ignite service restart.
+ """
+ def get_log_lines_count(service, filename):
+ node = service.nodes[0]
+ log_file = os.path.join(service.log_dir, filename)
+ log_cnt = list(node.account.ssh_capture(f'cat {log_file} | wc -l', callback=int))[0]
+ return log_cnt
+
+ def get_logs_count(service):
+ node = service.nodes[0]
+ return list(node.account.ssh_capture(f'ls {service.log_dir} | wc -l', callback=int))[0]
+
+ ignites = IgniteService(self.test_context, IgniteConfiguration(version=IgniteVersion(ignite_version)),
+ num_nodes=1)
+
+ ignites.start()
+
+ num_restarts = 6
+ for i in range(num_restarts - 1):
+ ignites.stop()
+
+ old_cnt = get_log_lines_count(ignites, "console.log")
+ assert old_cnt > 0
+
+ ignites.start(clean=False)
+
+ new_cnt = get_log_lines_count(ignites, "console.log")
+ assert new_cnt > 0
+
+ # check that there is no new entry in rotated file
+ assert old_cnt == get_log_lines_count(ignites, f"console.log.{i + 1}")
+
+ assert get_logs_count(ignites) == num_restarts
diff --git a/modules/ducktests/tests/tox.ini b/modules/ducktests/tests/tox.ini
index f74d685..02b773b 100644
--- a/modules/ducktests/tests/tox.ini
+++ b/modules/ducktests/tests/tox.ini
@@ -26,7 +26,6 @@ python =
envdir = {homedir}/.virtualenvs/ignite-ducktests-{envname}
deps =
-r ./docker/requirements-dev.txt
-recreate = True
usedevelop = True
commands =
pytest {env:PYTESTARGS:} {posargs}