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 bb5453f Ducktape parallel (#8192) bb5453f is described below commit bb5453ffcc2098ff389a1054e0926f4616670987 Author: Ivan Daschinskiy <ivanda...@gmail.com> AuthorDate: Mon Aug 31 17:25:11 2020 +0300 Ducktape parallel (#8192) --- modules/ducktests/tests/docker/run_tests.sh | 18 +- .../ducktests/tests/ignitetest/services/ignite.py | 9 +- .../tests/ignitetest/services/ignite_app.py | 17 +- .../utils/{ignite_config.py => config_template.py} | 10 +- .../ignitetest/services/utils/ignite_aware.py | 31 ++- .../utils/ignite_configuration/__init__.py | 46 ++++ .../services/utils/ignite_configuration/cache.py | 29 +++ .../utils/ignite_configuration/data_storage.py | 38 ++++ .../utils/ignite_configuration/discovery.py | 141 ++++++++++++ .../services/utils/ignite_persistence.py | 4 +- .../tests/ignitetest/services/utils/ignite_spec.py | 59 +++-- .../services/utils/templates/cache_macro.j2 | 34 +++ .../services/utils/templates/datastorage_macro.j2 | 46 ++++ .../services/utils/templates/discovery_macro.j2 | 57 +++++ .../utils/{config => templates}/ignite.xml.j2 | 29 ++- .../utils/{config => templates}/log4j.xml.j2 | 0 .../services/utils/templates/misc_macro.j2 | 24 ++ .../ignitetest/tests/add_node_rebalance_test.py | 18 +- .../ignitetest/tests/cellular_affinity_test.py | 35 ++- .../tests/ignitetest/tests/control_utility_test.py | 85 +++---- .../tests/ignitetest/tests/discovery_test.py | 251 ++++++++++----------- .../tests/ignitetest/tests/pme_free_switch_test.py | 52 ++--- .../ducktests/tests/ignitetest/tests/smoke_test.py | 31 ++- .../tests/suites/fast_suite.yml} | 28 +-- .../tests/ignitetest/tests/suites/slow_suite.yml | 17 ++ modules/ducktests/tests/setup.py | 5 +- 26 files changed, 753 insertions(+), 361 deletions(-) diff --git a/modules/ducktests/tests/docker/run_tests.sh b/modules/ducktests/tests/docker/run_tests.sh index e5dc561..b9ee488 100755 --- a/modules/ducktests/tests/docker/run_tests.sh +++ b/modules/ducktests/tests/docker/run_tests.sh @@ -52,13 +52,22 @@ Usage: ${0} [options] The options are as follows: -h|--help - Display this help message + Display this help message. + +-n|--num-nodes + Specify how many nodes to start. Default number of nodes to start: 11. + +-j|--max-parallel + Specify max number of tests that can be run in parallel. -p|--param Use specified param to inject in tests. Could be used multiple times. ./run_tests.sh --param version=2.8.1 +-pj|--params-json + Use specified json as parameters to inject in tests. Can be extended with -p|--param. + -g|--global Use specified global param to pass to test context. Could be used multiple times. @@ -108,8 +117,11 @@ while [[ $# -ge 1 ]]; do case "$1" in -h|--help) usage;; -p|--param) duck_add_param "$2"; shift 2;; + -pj|--params-json) PARAMETERS="$2"; shift 2;; -g|--global) duck_add_global "$2"; shift 2;; -t|--tc-paths) TC_PATHS="$2"; shift 2;; + -n|--num-nodes) IGNITE_NUM_CONTAINERS="$2"; shift 2;; + -j|--max-parallel) MAX_PARALLEL="$2"; shift 2;; -f|--force) FORCE=$1; shift;; *) break;; esac @@ -139,5 +151,9 @@ if [[ "$PARAMETERS" != "{}" ]]; then DUCKTAPE_OPTIONS="$DUCKTAPE_OPTIONS --parameters '$PARAMETERS'" fi +if [[ -n "$MAX_PARALLEL" ]]; then + DUCKTAPE_OPTIONS="$DUCKTAPE_OPTIONS --max-parallel $MAX_PARALLEL" +fi + "$SCRIPT_DIR"/ducker-ignite test "$TC_PATHS" "$DUCKTAPE_OPTIONS" \ || die "ducker-ignite test failed" diff --git a/modules/ducktests/tests/ignitetest/services/ignite.py b/modules/ducktests/tests/ignitetest/services/ignite.py index 2fb64b3..f50365d 100644 --- a/modules/ducktests/tests/ignitetest/services/ignite.py +++ b/modules/ducktests/tests/ignitetest/services/ignite.py @@ -31,7 +31,6 @@ from ducktape.utils.util import wait_until from ignitetest.services.utils.concurrent import CountDownLatch, AtomicValue from ignitetest.services.utils.ignite_aware import IgniteAwareService -from ignitetest.utils.version import DEV_BRANCH class IgniteService(IgniteAwareService): @@ -42,10 +41,8 @@ class IgniteService(IgniteAwareService): HEAP_DUMP_FILE = os.path.join(IgniteAwareService.PERSISTENT_ROOT, "ignite-heap.bin") # pylint: disable=R0913 - def __init__(self, context, num_nodes, jvm_opts=None, properties="", client_mode=False, modules=None, - version=DEV_BRANCH): - super().__init__(context, num_nodes, properties, client_mode=client_mode, modules=modules, version=version, - jvm_opts=jvm_opts) + def __init__(self, context, config, num_nodes, jvm_opts=None, modules=None): + super().__init__(context, config, num_nodes, modules=modules, jvm_opts=jvm_opts) # pylint: disable=W0221 def start(self, timeout_sec=180): @@ -125,7 +122,7 @@ class IgniteService(IgniteAwareService): start_waiter.wait() if delay_ms > 0: - time.sleep(delay_ms/1000.0) + time.sleep(delay_ms / 1000.0) if time_holder: mono = monotonic() diff --git a/modules/ducktests/tests/ignitetest/services/ignite_app.py b/modules/ducktests/tests/ignitetest/services/ignite_app.py index 84871d8..b40d01a 100644 --- a/modules/ducktests/tests/ignitetest/services/ignite_app.py +++ b/modules/ducktests/tests/ignitetest/services/ignite_app.py @@ -20,7 +20,6 @@ This module contains the base class to build Ignite aware application written on import re from ignitetest.services.utils.ignite_aware import IgniteAwareService -from ignitetest.utils.version import DEV_BRANCH class IgniteApplicationService(IgniteAwareService): @@ -31,18 +30,10 @@ class IgniteApplicationService(IgniteAwareService): SERVICE_JAVA_CLASS_NAME = "org.apache.ignite.internal.ducktest.utils.IgniteAwareApplicationService" # pylint: disable=R0913 - def __init__(self, context, java_class_name, params="", properties="", timeout_sec=60, modules=None, - client_mode=True, version=DEV_BRANCH, servicejava_class_name=SERVICE_JAVA_CLASS_NAME, - jvm_opts=None, start_ignite=True): - super().__init__(context, 1, properties, - client_mode=client_mode, - version=version, - modules=modules, - servicejava_class_name=servicejava_class_name, - java_class_name=java_class_name, - params=params, - jvm_opts=jvm_opts, - start_ignite=start_ignite) + def __init__(self, context, config, java_class_name, params="", timeout_sec=60, modules=None, + servicejava_class_name=SERVICE_JAVA_CLASS_NAME, jvm_opts=None, start_ignite=True): + super().__init__(context, config, 1, modules=modules, servicejava_class_name=servicejava_class_name, + java_class_name=java_class_name, params=params, jvm_opts=jvm_opts, start_ignite=start_ignite) self.servicejava_class_name = servicejava_class_name self.java_class_name = java_class_name diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py b/modules/ducktests/tests/ignitetest/services/utils/config_template.py similarity index 91% rename from modules/ducktests/tests/ignitetest/services/utils/ignite_config.py rename to modules/ducktests/tests/ignitetest/services/utils/config_template.py index 6123198..5170c4d 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py +++ b/modules/ducktests/tests/ignitetest/services/utils/config_template.py @@ -20,11 +20,11 @@ import os from jinja2 import FileSystemLoader, Environment -DEFAULT_CONFIG_PATH = os.path.dirname(os.path.abspath(__file__)) + "/config" +DEFAULT_CONFIG_PATH = os.path.dirname(os.path.abspath(__file__)) + "/templates" DEFAULT_IGNITE_CONF = DEFAULT_CONFIG_PATH + "/ignite.xml.j2" -class Config: +class ConfigTemplate: """ Basic configuration. """ @@ -47,7 +47,7 @@ class Config: return res -class IgniteServerConfig(Config): +class IgniteServerConfigTemplate(ConfigTemplate): """ Ignite server node configuration. """ @@ -55,7 +55,7 @@ class IgniteServerConfig(Config): super().__init__(path) -class IgniteClientConfig(Config): +class IgniteClientConfigTemplate(ConfigTemplate): """ Ignite client node configuration. """ @@ -64,7 +64,7 @@ class IgniteClientConfig(Config): self.default_params.update(client_mode=True) -class IgniteLoggerConfig(Config): +class IgniteLoggerConfigTemplate(ConfigTemplate): """ Ignite logger configuration. """ diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py index 9f374bb..fadbbe6 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py @@ -33,7 +33,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl """ # pylint: disable=R0913 - def __init__(self, context, num_nodes, properties, **kwargs): + def __init__(self, context, config, num_nodes, **kwargs): """ **kwargs are params that passed to IgniteSpec """ @@ -43,9 +43,9 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl # IgniteAwareService uses IgnitePersistenceAware mixin to override default Service 'log' definition. self.log_level = "DEBUG" - self.properties = properties + self.config = config - self.spec = resolve_spec(self, context, **kwargs) + self.spec = resolve_spec(self, context, config, **kwargs) def start_node(self, node): self.init_persistent(node) @@ -63,14 +63,27 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl """ super().init_persistent(node) - node_config = self.spec.config().render(config_dir=self.PERSISTENT_ROOT, - work_dir=self.WORK_DIR, - properties=self.properties, - consistent_id=node.account.externally_routable_ip) + node_config = self._prepare_config(node) - setattr(node, "consistent_id", node.account.externally_routable_ip) node.account.create_file(self.CONFIG_FILE, node_config) + def _prepare_config(self, node): + if not self.config.consistent_id: + config = self.config._replace(consistent_id=node.account.externally_routable_ip) + else: + config = self.config + + 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, + config=config) + + setattr(node, "consistent_id", node.account.externally_routable_ip) + + self.logger.debug("Config for node %s: %s" % (node.account.hostname, node_config)) + + return node_config + @abstractmethod def pids(self, node): """ @@ -81,7 +94,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware, metacl # pylint: disable=W0613 def _worker(self, idx, node): - cmd = self.spec.command() + cmd = self.spec.command self.logger.debug("Attempting to start Application Service on %s with command: %s" % (str(node.account), cmd)) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py new file mode 100644 index 0000000..b9602e4 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py @@ -0,0 +1,46 @@ +# 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 IgniteConfiguration classes and utilities. +""" + +from typing import NamedTuple + +from ignitetest.services.utils.ignite_configuration.data_storage import DataStorageConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import DiscoverySpi, TcpDiscoverySpi +from ignitetest.utils.version import IgniteVersion, DEV_BRANCH + + +class IgniteConfiguration(NamedTuple): + """ + Ignite configuration. + """ + discovery_spi: DiscoverySpi = TcpDiscoverySpi() + version: IgniteVersion = DEV_BRANCH + cluster_state: str = 'ACTIVE' + client_mode: bool = False + consistent_id: str = None + failure_detection_timeout: int = 10000 + properties: str = None + data_storage: DataStorageConfiguration = None + caches: list = [] + + +class IgniteClientConfiguration(IgniteConfiguration): + """ + Ignite client configuration. + """ + client_mode = True diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/cache.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/cache.py new file mode 100644 index 0000000..cc58a69 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/cache.py @@ -0,0 +1,29 @@ +# 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 and utilities for Ignite Cache configuration. +""" +from typing import NamedTuple + + +class CacheConfiguration(NamedTuple): + """ + Ignite Cache configuration. + """ + name: str + cache_mode: str = 'PARTITIONED' + atomicity_mode: str = 'ATOMIC' + backups: int = 0 diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/data_storage.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/data_storage.py new file mode 100644 index 0000000..7b2999d --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/data_storage.py @@ -0,0 +1,38 @@ +# 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 and utilities for Ignite DataStorage configuration. +""" + +from typing import NamedTuple + + +class DataRegionConfiguration(NamedTuple): + """ + Ignite DataRegion Configuration + """ + name: str = "default" + persistent: bool = False + init_size: int = 100 * 1024 * 1024 + max_size: int = 512 * 1024 * 1024 + + +class DataStorageConfiguration(NamedTuple): + """ + Ignite DataStorage configuration + """ + default: DataRegionConfiguration = DataRegionConfiguration() + regions: list = [] diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/discovery.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/discovery.py new file mode 100644 index 0000000..9f38da5 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/discovery.py @@ -0,0 +1,141 @@ +# 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 + +""" +Module contains classes and utility methods to create discovery configuration for ignite nodes. +""" + +from abc import ABCMeta, abstractmethod + +from ignitetest.services.utils.ignite_aware import IgniteAwareService +from ignitetest.services.zk.zookeeper import ZookeeperService + + +class DiscoverySpi(metaclass=ABCMeta): + """ + Abstract class for DiscoverySpi. + """ + @property + @abstractmethod + def type(self): + """ + Type of DiscoverySPI. + """ + + @abstractmethod + def prepare_on_start(self, **kwargs): + """ + Call if update before start is needed. + """ + + +class ZookeeperDiscoverySpi(DiscoverySpi): + """ + ZookeeperDiscoverySpi. + """ + def __init__(self, connection_string, root_path): + self.connection_string = connection_string + self.root_path = root_path + + @property + def type(self): + return "ZOOKEEPER" + + def prepare_on_start(self, **kwargs): + pass + + +class TcpDiscoveryIpFinder(metaclass=ABCMeta): + """ + Abstract class for TcpDiscoveryIpFinder. + """ + @property + @abstractmethod + def type(self): + """ + Type of TcpDiscoveryIpFinder. + """ + + @abstractmethod + def prepare_on_start(self, **kwargs): + """ + Call if update before start is needed. + """ + + +class TcpDiscoveryVmIpFinder(TcpDiscoveryIpFinder): + """ + IpFinder with static ips, obtained from cluster nodes. + """ + def __init__(self, nodes=None): + self.addresses = TcpDiscoveryVmIpFinder.__get_addresses(nodes) if nodes else None + + @property + def type(self): + return 'VM' + + def prepare_on_start(self, **kwargs): + if not self.addresses: + cluster = kwargs.get('cluster') + self.addresses = TcpDiscoveryVmIpFinder.__get_addresses(cluster.nodes) + + @staticmethod + def __get_addresses(nodes): + return [node.account.externally_routable_ip for node in nodes] + + +class TcpDiscoverySpi(DiscoverySpi): + """ + TcpDiscoverySpi. + """ + def __init__(self, ip_finder=TcpDiscoveryVmIpFinder()): + self.ip_finder = ip_finder + + @property + def type(self): + return 'TCP' + + def prepare_on_start(self, **kwargs): + self.ip_finder.prepare_on_start(**kwargs) + + +def from_ignite_cluster(cluster, subset=None): + """ + Form TcpDiscoverySpi from cluster or its subset. + :param cluster: IgniteService cluster + :param subset: slice object (optional). + :return: TcpDiscoverySpi with static ip addresses. + """ + assert isinstance(cluster, IgniteAwareService) + + if subset: + assert isinstance(subset, slice) + nodes = cluster.nodes[subset] + else: + nodes = cluster.nodes + + return TcpDiscoverySpi(ip_finder=TcpDiscoveryVmIpFinder(nodes)) + + +def from_zookeeper_cluster(cluster, root_path="/apacheIgnite"): + """ + Form ZookeeperDiscoverySpi from zookeeper service cluster. + :param cluster: ZookeeperService cluster. + :param root_path: root ZNode path. + :return: ZookeeperDiscoverySpi. + """ + assert isinstance(cluster, ZookeeperService) + + return ZookeeperDiscoverySpi(cluster.connection_string(), root_path=root_path) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py index 27c3161..589b535 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py @@ -19,7 +19,7 @@ This module contains classes that represent persistent artifacts of tests import os -from ignitetest.services.utils.ignite_config import IgniteLoggerConfig +from ignitetest.services.utils.config_template import IgniteLoggerConfigTemplate class PersistenceAware: @@ -66,5 +66,5 @@ class IgnitePersistenceAware(PersistenceAware): """ super().init_persistent(node) - logger_config = IgniteLoggerConfig().render(work_dir=self.WORK_DIR) + 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 9cbfcc0..124fefb 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py @@ -20,16 +20,16 @@ This module contains Spec classes that describes config and command line to star import base64 import importlib import json +from abc import ABCMeta, abstractmethod from ignitetest.services.utils.ignite_path import IgnitePath -from ignitetest.services.utils.ignite_config import IgniteClientConfig, IgniteServerConfig -from ignitetest.utils.version import DEV_BRANCH, IgniteVersion +from ignitetest.services.utils.config_template import IgniteClientConfigTemplate, IgniteServerConfigTemplate +from ignitetest.utils.version import DEV_BRANCH from ignitetest.services.utils.ignite_persistence import IgnitePersistenceAware -# pylint: disable=no-else-return -def resolve_spec(service, context, **kwargs): +def resolve_spec(service, context, config, **kwargs): """ Resolve Spec classes for IgniteService and IgniteApplicationService """ @@ -47,41 +47,40 @@ def resolve_spec(service, context, **kwargs): return len(impl_filter) > 0 if is_impl("IgniteService"): - return _resolve_spec("NodeSpec", ApacheIgniteNodeSpec)(**kwargs) - elif is_impl("IgniteApplicationService"): - return _resolve_spec("AppSpec", ApacheIgniteApplicationSpec)(context=context, **kwargs) - else: - raise Exception("There is no specification for class %s" % type(service)) + return _resolve_spec("NodeSpec", ApacheIgniteNodeSpec)(config=config, **kwargs) + if is_impl("IgniteApplicationService"): + return _resolve_spec("AppSpec", ApacheIgniteApplicationSpec)(context=context, config=config, **kwargs) -class IgniteSpec: + raise Exception("There is no specification for class %s" % type(service)) + + +class IgniteSpec(metaclass=ABCMeta): """ This class is a basic Spec """ - def __init__(self, version, project, client_mode, jvm_opts): - if isinstance(version, IgniteVersion): - self.version = version - else: - self.version = IgniteVersion(version) - + def __init__(self, config, project, jvm_opts): + self.version = config.version self.path = IgnitePath(self.version, project) self.envs = {} self.jvm_opts = jvm_opts or [] - self.client_mode = client_mode + self.config = config - def config(self): + @property + def config_template(self): """ :return: config that service will use to start on a node """ - if self.client_mode: - return IgniteClientConfig() - return IgniteServerConfig() + if self.config.client_mode: + return IgniteClientConfigTemplate() + return IgniteServerConfigTemplate() + @property + @abstractmethod def command(self): """ :return: string that represents command to run service on a node """ - raise NotImplementedError() def _envs(self): """ @@ -102,14 +101,13 @@ class IgniteNodeSpec(IgniteSpec, IgnitePersistenceAware): """ Spec to run ignite node """ - + @property def command(self): - cmd = "%s %s %s %s 1>> %s 2>> %s &" % \ + cmd = "%s %s %s %s 2>&1 | tee -a %s &" % \ (self._envs(), self.path.script("ignite.sh"), self._jvm_opts(), self.CONFIG_FILE, - self.STDOUT_STDERR_CAPTURE, self.STDOUT_STDERR_CAPTURE) return cmd @@ -126,21 +124,18 @@ class IgniteApplicationSpec(IgniteSpec, IgnitePersistenceAware): def _app_args(self): return ",".join(self.args) + @property def command(self): - cmd = "%s %s %s %s 1>> %s 2>> %s &" % \ + cmd = "%s %s %s %s 2>&1 | tee -a %s &" % \ (self._envs(), self.path.script("ignite.sh"), self._jvm_opts(), self._app_args(), - self.STDOUT_STDERR_CAPTURE, self.STDOUT_STDERR_CAPTURE) return cmd -### - - class ApacheIgniteNodeSpec(IgniteNodeSpec, IgnitePersistenceAware): """ Implementation IgniteNodeSpec for Apache Ignite project @@ -208,5 +203,5 @@ class ApacheIgniteApplicationSpec(IgniteApplicationSpec, IgnitePersistenceAware) aws = self.path.module("aws") return self.context.cluster.nodes[0].account.ssh_capture( "ls -d %s/* | grep jackson | tr '\n' ':' | sed 's/.$//'" % aws) - else: - return [] + + return [] diff --git a/modules/ducktests/tests/ignitetest/services/utils/templates/cache_macro.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/cache_macro.j2 new file mode 100644 index 0000000..d227369 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/templates/cache_macro.j2 @@ -0,0 +1,34 @@ +{# + 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. +#} + +{% macro cache_configs(caches) %} + {% if caches %} + <property name="cacheConfiguration"> + <list> + {% for cache in caches %} + <bean class="org.apache.ignite.configuration.CacheConfiguration"> + <property name="name" value="{{ cache.name }}"/> + {% if cache.cache_mode == 'PARTITIONED' %} + <property name="backups" value="{{ cache.backups or 0 }}"/> + {% endif %} + <property name="atomicityMode" value="{{ cache.atomicity_mode or 'ATOMIC' }}"/> + </bean> + {% endfor %} + </list> + </property> + {% endif %} +{% endmacro %} diff --git a/modules/ducktests/tests/ignitetest/services/utils/templates/datastorage_macro.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/datastorage_macro.j2 new file mode 100644 index 0000000..1c2b462 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/templates/datastorage_macro.j2 @@ -0,0 +1,46 @@ +{# + 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. +#} + +{% macro data_storage(config) %} + {% if config %} + <property name="dataStorageConfiguration"> + <bean class="org.apache.ignite.configuration.DataStorageConfiguration"> + <property name="defaultDataRegionConfiguration"> + {{ data_region(config.default) }} + </property> + {% if config.regions %} + <property name="dataRegionConfigurations"> + <list> + {% for region in config.regions %} + {{ data_region(region) }} + {% endfor %} + </list> + </property> + {% endif %} + </bean> + </property> + {% endif %} +{% endmacro %} + +{% macro data_region(config) %} + <bean class="org.apache.ignite.configuration.DataRegionConfiguration"> + <property name="name" value="{{ config.name }}"/> + <property name="persistenceEnabled" value="{{ config.persistent }}"/> + <property name="initialSize" value="{{ config.init_size }}"/> + <property name="maxSize" value="{{ config.max_size }}"/> + </bean> +{% endmacro %} diff --git a/modules/ducktests/tests/ignitetest/services/utils/templates/discovery_macro.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/discovery_macro.j2 new file mode 100644 index 0000000..fdd632a --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/templates/discovery_macro.j2 @@ -0,0 +1,57 @@ +{# + 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. +#} + +{% macro ip_finder(finder) %} + {% if finder %} + <property name="ipFinder"> + {% if finder.type == 'VM' %} + <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"> + <property name="addresses"> + <list> + {% for address in finder.addresses %} + <value>{{ address }}</value> + {% endfor %} + </list> + </property> + </bean> + {% endif %} + </property> + {% endif %} +{% endmacro %} + +{% macro zookeeper_discovery_spi(spi) %} + <bean class="org.apache.ignite.spi.discovery.zk.ZookeeperDiscoverySpi"> + <property name="zkConnectionString" value="{{ spi.connection_string }}"/> + <property name="zkRootPath" value="{{ spi.root_path }}"/> + </bean> +{% endmacro %} + +{% macro tcp_discovery_spi(spi) %} + <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi"> + {{ ip_finder(spi.ip_finder) }} + </bean> +{% endmacro %} + +{% macro discovery_spi(spi) %} + <property name="discoverySpi"> + {% if spi.type == 'TCP' %} + {{ tcp_discovery_spi(spi) }} + {% elif spi.type == 'ZOOKEEPER' %} + {{ zookeeper_discovery_spi(spi) }} + {% endif %} + </property> +{% endmacro %} diff --git a/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/ignite.xml.j2 similarity index 65% rename from modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2 rename to modules/ducktests/tests/ignitetest/services/utils/templates/ignite.xml.j2 index f03ba17..86abd35 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/config/ignite.xml.j2 +++ b/modules/ducktests/tests/ignitetest/services/utils/templates/ignite.xml.j2 @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- +{# 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. @@ -15,7 +15,12 @@ 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. ---> +#} + +{% import 'discovery_macro.j2' as disco_utils %} +{% import 'cache_macro.j2' as cache_utils %} +{% import 'datastorage_macro.j2' as datastorage_utils %} +{% import 'misc_macro.j2' as misc_utils %} <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -29,12 +34,20 @@ </bean> </property> - <property name="clientMode" value="{{ client_mode or False | lower }}"/> - {% if consistent_id %} - <property name="consistentId" value="{{ consistent_id }}"/> - {% endif %} - {% if properties %} - {{ properties }} + <property name="clientMode" value="{{ config.client_mode or False | lower }}"/> + <property name="consistentId" value="{{ config.consistent_id }}"/> + <property name="failureDetectionTimeout" value="{{ config.failure_detection_timeout }}"/> + + {{ misc_utils.cluster_state(config.cluster_state, config.version) }} + + {{ disco_utils.discovery_spi(config.discovery_spi) }} + + {{ datastorage_utils.data_storage(config.data_storage) }} + + {{ cache_utils.cache_configs(config.caches) }} + + {% if config.properties %} + {{ config.properties }} {% endif %} </bean> </beans> diff --git a/modules/ducktests/tests/ignitetest/services/utils/config/log4j.xml.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/log4j.xml.j2 similarity index 100% rename from modules/ducktests/tests/ignitetest/services/utils/config/log4j.xml.j2 rename to modules/ducktests/tests/ignitetest/services/utils/templates/log4j.xml.j2 diff --git a/modules/ducktests/tests/ignitetest/services/utils/templates/misc_macro.j2 b/modules/ducktests/tests/ignitetest/services/utils/templates/misc_macro.j2 new file mode 100644 index 0000000..892453b --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/templates/misc_macro.j2 @@ -0,0 +1,24 @@ +{# + 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. +#} + +{% macro cluster_state(state, version) %} + {% if version > "2.9.0" %} + <property name="clusterStateOnStart" value="{{ state }}"/> + {% else %} + <property name="activeOnStart" value="{{ 'false' if state == 'INACTIVE' else 'true'}}"/> + {% endif %} +{% endmacro %} diff --git a/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py b/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py index a1d90b8..b162d2e 100644 --- a/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py +++ b/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py @@ -22,6 +22,8 @@ from ducktape.mark.resource import cluster from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService +from ignitetest.services.utils.ignite_configuration import IgniteConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import from_ignite_cluster from ignitetest.utils.ignite_test import IgniteTest from ignitetest.utils.version import DEV_BRANCH, IgniteVersion, LATEST @@ -36,12 +38,6 @@ class AddNodeRebalanceTest(IgniteTest): DATA_AMOUNT = 1000000 REBALANCE_TIMEOUT = 60 - def setUp(self): - pass - - def teardown(self): - pass - @cluster(num_nodes=NUM_NODES + 1) @parametrize(version=str(DEV_BRANCH)) @parametrize(version=str(LATEST)) @@ -56,20 +52,22 @@ class AddNodeRebalanceTest(IgniteTest): self.stage("Start Ignite nodes") - ignites = IgniteService(self.test_context, num_nodes=AddNodeRebalanceTest.NUM_NODES - 1, version=ignite_version) + node_config = IgniteConfiguration(version=ignite_version) + ignites = IgniteService(self.test_context, config=node_config, num_nodes=self.NUM_NODES - 1) ignites.start() self.stage("Starting DataGenerationApplication") # This client just put some data to the cache. - IgniteApplicationService(self.test_context, + app_config = node_config._replace(client_mode=True, discovery_spi=from_ignite_cluster(ignites)) + IgniteApplicationService(self.test_context, config=app_config, java_class_name="org.apache.ignite.internal.ducktest.tests.DataGenerationApplication", - version=ignite_version, params={"cacheName": "test-cache", "range": self.DATA_AMOUNT}, timeout_sec=self.PRELOAD_TIMEOUT).run() - ignite = IgniteService(self.test_context, num_nodes=1, version=ignite_version) + ignite = IgniteService(self.test_context, node_config._replace(discovery_spi=from_ignite_cluster(ignites)), + num_nodes=1) self.stage("Starting Ignite node") diff --git a/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py b/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py index 78d3eba..6b1e664 100644 --- a/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py +++ b/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py @@ -23,8 +23,10 @@ from jinja2 import Template from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService +from ignitetest.services.utils.ignite_configuration import IgniteConfiguration, IgniteClientConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import from_ignite_cluster from ignitetest.utils.ignite_test import IgniteTest -from ignitetest.utils.version import DEV_BRANCH +from ignitetest.utils.version import DEV_BRANCH, IgniteVersion # pylint: disable=W0223 @@ -68,41 +70,36 @@ class CellularAffinity(IgniteTest): attr=CellularAffinity.ATTRIBUTE, cacheName=CellularAffinity.CACHE_NAME) - def setUp(self): - pass - - def teardown(self): - pass - @cluster(num_nodes=NUM_NODES * 3 + 1) @parametrize(version=str(DEV_BRANCH)) def test(self, version): """ Test Cellular Affinity scenario (partition distribution). """ - self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=1']) - self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=2']) - self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=XXX', '-DRANDOM=42']) + cell1 = self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=1']) + self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=2'], joined_cluster=cell1) + self.start_cell(version, ['-D' + CellularAffinity.ATTRIBUTE + '=XXX', '-DRANDOM=42'], joined_cluster=cell1) checker = IgniteApplicationService( self.test_context, + IgniteClientConfiguration(version=IgniteVersion(version), discovery_spi=from_ignite_cluster(cell1)), java_class_name="org.apache.ignite.internal.ducktest.tests.cellular_affinity_test.DistributionChecker", params={"cacheName": CellularAffinity.CACHE_NAME, "attr": CellularAffinity.ATTRIBUTE, - "nodesPerCell": self.NUM_NODES}, - version=version) + "nodesPerCell": self.NUM_NODES}) checker.run() - def start_cell(self, ignite_version, jvm_opts): + def start_cell(self, version, jvm_opts, joined_cluster=None): """ Starts cell. """ - ignites = IgniteService( - self.test_context, - num_nodes=CellularAffinity.NUM_NODES, - version=ignite_version, - properties=self.properties(), - jvm_opts=jvm_opts) + config = IgniteConfiguration(version=IgniteVersion(version), properties=self.properties()) + if joined_cluster: + config = config._replace(discovery_spi=from_ignite_cluster(joined_cluster)) + + ignites = IgniteService(self.test_context, config, num_nodes=CellularAffinity.NUM_NODES, jvm_opts=jvm_opts) ignites.start() + + return ignites diff --git a/modules/ducktests/tests/ignitetest/tests/control_utility_test.py b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py index 63c87ac..a4afd09 100644 --- a/modules/ducktests/tests/ignitetest/tests/control_utility_test.py +++ b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py @@ -19,10 +19,12 @@ 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.services.utils.ignite_configuration import IgniteConfiguration, DataStorageConfiguration +from ignitetest.services.utils.ignite_configuration.data_storage import DataRegionConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import from_ignite_cluster from ignitetest.utils import version_if from ignitetest.utils.ignite_test import IgniteTest from ignitetest.utils.version import DEV_BRANCH, LATEST_2_8, IgniteVersion, LATEST_2_7, V_2_8_0 @@ -35,36 +37,6 @@ class BaselineTests(IgniteTest): """ 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().__init__(test_context) - self.servers = None - @cluster(num_nodes=NUM_NODES) @parametrize(version=str(DEV_BRANCH)) @parametrize(version=str(LATEST_2_8)) @@ -74,19 +46,19 @@ class BaselineTests(IgniteTest): Test baseline set. """ blt_size = self.NUM_NODES - 2 - self.servers = self.__start_ignite_nodes(version, blt_size) + servers = self.__start_ignite_nodes(version, blt_size) - control_utility = ControlUtility(self.servers, self.test_context) + control_utility = ControlUtility(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) + self.__check_nodes_in_baseline(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) + # Set baseline using list of consisttent ids. + new_node = self.__start_ignite_nodes(version, 1, join_cluster=servers) + control_utility.set_baseline(servers.nodes + new_node.nodes) blt_size += 1 baseline = control_utility.baseline() @@ -94,7 +66,7 @@ class BaselineTests(IgniteTest): self.__check_nodes_in_baseline(new_node.nodes, baseline) # Set baseline using topology version. - new_node = self.__start_ignite_nodes(version, 1) + new_node = self.__start_ignite_nodes(version, 1, join_cluster=servers) _, version, _ = control_utility.cluster_state() control_utility.set_baseline(version) blt_size += 1 @@ -112,14 +84,14 @@ class BaselineTests(IgniteTest): Test add and remove nodes from baseline. """ blt_size = self.NUM_NODES - 1 - self.servers = self.__start_ignite_nodes(version, blt_size) + servers = self.__start_ignite_nodes(version, blt_size) - control_utility = ControlUtility(self.servers, self.test_context) + control_utility = ControlUtility(servers, self.test_context) control_utility.activate() # Add node to baseline. - new_node = self.__start_ignite_nodes(version, 1) + new_node = self.__start_ignite_nodes(version, 1, join_cluster=servers) control_utility.add_to_baseline(new_node.nodes) blt_size += 1 @@ -138,7 +110,7 @@ class BaselineTests(IgniteTest): # Remove of offline node from baseline. new_node.stop() - self.servers.await_event("Node left topology", timeout_sec=30, from_the_beginning=True) + servers.await_event("Node left topology", timeout_sec=30, from_the_beginning=True) control_utility.remove_from_baseline(new_node.nodes) blt_size -= 1 @@ -155,9 +127,9 @@ class BaselineTests(IgniteTest): """ Test activate and deactivate cluster. """ - self.servers = self.__start_ignite_nodes(version, self.NUM_NODES) + servers = self.__start_ignite_nodes(version, self.NUM_NODES) - control_utility = ControlUtility(self.servers, self.test_context) + control_utility = ControlUtility(servers, self.test_context) control_utility.activate() @@ -180,14 +152,14 @@ class BaselineTests(IgniteTest): Test activate and deactivate cluster. """ blt_size = self.NUM_NODES - 2 - self.servers = self.__start_ignite_nodes(version, blt_size) + servers = self.__start_ignite_nodes(version, blt_size) - control_utility = ControlUtility(self.servers, self.test_context) + control_utility = ControlUtility(servers, self.test_context) control_utility.activate() # Add node. control_utility.enable_baseline_auto_adjust(2000) - new_node = self.__start_ignite_nodes(version, 1) + new_node = self.__start_ignite_nodes(version, 1, join_cluster=servers) blt_size += 1 wait_until(lambda: len(control_utility.baseline()) == blt_size, timeout_sec=5) @@ -198,7 +170,7 @@ class BaselineTests(IgniteTest): # 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) + new_node = self.__start_ignite_nodes(version, 1, join_cluster=servers) wait_until(lambda: control_utility.cluster_state().topology_version != old_topology, timeout_sec=5) baseline = control_utility.baseline() @@ -222,11 +194,20 @@ class BaselineTests(IgniteTest): 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) + def __start_ignite_nodes(self, version, num_nodes, timeout_sec=60, join_cluster=None): + config = IgniteConfiguration( + cluster_state="INACTIVE", + version=IgniteVersion(version), + data_storage=DataStorageConfiguration( + default=DataRegionConfiguration(name='persistent', persistent=True), + regions=[DataRegionConfiguration(name='in-memory', persistent=False, max_size=100 * 1024 * 1024)] + ) + ) + + if join_cluster: + config._replace(discovery_spi=from_ignite_cluster(join_cluster)) - servers = IgniteService(self.test_context, num_nodes=num_nodes, version=ignite_version, - properties=self.properties(ignite_version)) + servers = IgniteService(self.test_context, config=config, num_nodes=num_nodes) servers.start(timeout_sec=timeout_sec) diff --git a/modules/ducktests/tests/ignitetest/tests/discovery_test.py b/modules/ducktests/tests/ignitetest/tests/discovery_test.py index 63f6ef8..6daaa2d 100644 --- a/modules/ducktests/tests/ignitetest/tests/discovery_test.py +++ b/modules/ducktests/tests/ignitetest/tests/discovery_test.py @@ -20,18 +20,33 @@ Module contains discovery tests. import random import re from datetime import datetime +from time import monotonic +from typing import NamedTuple from ducktape.mark import matrix from ducktape.mark.resource import cluster -from jinja2 import Template from ignitetest.services.ignite import IgniteAwareService from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService +from ignitetest.services.utils.ignite_configuration import IgniteConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import from_zookeeper_cluster, from_ignite_cluster, \ + TcpDiscoverySpi from ignitetest.services.utils.time_utils import epoch_mills from ignitetest.services.zk.zookeeper import ZookeeperService from ignitetest.utils.ignite_test import IgniteTest -from ignitetest.utils.version import DEV_BRANCH, LATEST_2_8 +from ignitetest.utils.version import DEV_BRANCH, LATEST_2_8, IgniteVersion + + +class DiscoveryTestConfig(NamedTuple): + """ + Configuration for DiscoveryTest. + """ + version: IgniteVersion + nodes_to_kill: int = 1 + kill_coordinator: bool = False + with_load: bool = False + with_zk: bool = False # pylint: disable=W0223 @@ -42,191 +57,167 @@ class DiscoveryTest(IgniteTest): 2. Kill random node. 3. Wait that survived node detects node failure. """ - class Config: - """ - Configuration for DiscoveryTest. - """ - def __init__(self, nodes_to_kill=1, kill_coordinator=False, with_load=False): - self.nodes_to_kill = nodes_to_kill - self.kill_coordinator = kill_coordinator - self.with_load = with_load - NUM_NODES = 7 FAILURE_DETECTION_TIMEOUT = 2000 DATA_AMOUNT = 100000 - CONFIG_TEMPLATE = """ - <property name="failureDetectionTimeout" value="{{ failure_detection_timeout }}"/> - {% if zookeeper_settings %} - {% with zk = zookeeper_settings %} - <property name="discoverySpi"> - <bean class="org.apache.ignite.spi.discovery.zk.ZookeeperDiscoverySpi"> - <property name="zkConnectionString" value="{{ zk.connection_string }}"/> - <property name="zkRootPath" value="{{ zk.root_path or '/apacheIgnite' }}"/> - </bean> - </property> - {% endwith %} - {% endif %} - """ - - def __init__(self, test_context): - super().__init__(test_context=test_context) - self.zk_quorum = None - self.servers = None - self.loader = None - @cluster(num_nodes=NUM_NODES) - @matrix(ignite_version=[str(DEV_BRANCH), str(LATEST_2_8)], + @matrix(version=[str(DEV_BRANCH), str(LATEST_2_8)], kill_coordinator=[False, True], nodes_to_kill=[1, 2], with_load=[False, True]) - def test_tcp(self, ignite_version, kill_coordinator, nodes_to_kill, with_load): + def test_node_fail_tcp(self, version, kill_coordinator, nodes_to_kill, with_load): """ Test nodes failure scenario with TcpDiscoverySpi. """ - config = DiscoveryTest.Config(nodes_to_kill, kill_coordinator, with_load) + test_config = DiscoveryTestConfig(version=IgniteVersion(version), kill_coordinator=kill_coordinator, + nodes_to_kill=nodes_to_kill, with_load=with_load, with_zk=False) - return self.__simulate_nodes_failure(ignite_version, self.__properties(), None, config) + return self._perform_node_fail_scenario(test_config) @cluster(num_nodes=NUM_NODES + 3) - @matrix(ignite_version=[str(DEV_BRANCH), str(LATEST_2_8)], + @matrix(version=[str(DEV_BRANCH), str(LATEST_2_8)], kill_coordinator=[False, True], nodes_to_kill=[1, 2], with_load=[False, True]) - def test_zk(self, ignite_version, kill_coordinator, nodes_to_kill, with_load): + def test_node_fail_zk(self, version, kill_coordinator, nodes_to_kill, with_load): """ Test node failure scenario with ZooKeeperSpi. """ - config = DiscoveryTest.Config(nodes_to_kill, kill_coordinator, with_load) + test_config = DiscoveryTestConfig(version=IgniteVersion(version), kill_coordinator=kill_coordinator, + nodes_to_kill=nodes_to_kill, with_load=with_load, with_zk=True) - self.__start_zk_quorum() + return self._perform_node_fail_scenario(test_config) - properties = self.__zk_properties(self.zk_quorum.connection_string()) - modules = ["zookeeper"] + def _perform_node_fail_scenario(self, test_config): + modules = ['zookeeper'] if test_config.with_zk else None - return self.__simulate_nodes_failure(ignite_version, properties, modules, config) + if test_config.with_zk: + zk_quorum = start_zookeeper(self.test_context, 3) - def setUp(self): - pass + discovery_spi = from_zookeeper_cluster(zk_quorum) + else: + discovery_spi = TcpDiscoverySpi() - def teardown(self): - if self.loader: - self.loader.stop() + ignite_config = IgniteConfiguration( + version=test_config.version, + discovery_spi=discovery_spi, + failure_detection_timeout=self.FAILURE_DETECTION_TIMEOUT + ) - if self.servers: - self.servers.stop() + servers, start_servers_sec = start_servers(self.test_context, self.NUM_NODES - 1, ignite_config, modules) - if self.zk_quorum: - self.zk_quorum.stop() + if test_config.with_load: + load_config = ignite_config._replace(client_mode=True) if test_config.with_zk else \ + ignite_config._replace(client_mode=True, discovery_spi=from_ignite_cluster(servers)) - def __simulate_nodes_failure(self, version, properties, modules, config): - if config.nodes_to_kill < 1: - return {"No nodes to kill": "Nothing to do"} + start_load_app(self.test_context, ignite_config=load_config, data_amount=self.DATA_AMOUNT, modules=modules) - self.servers = IgniteService( - self.test_context, - num_nodes=self.NUM_NODES - 1, - modules=modules, - properties=properties, - version=version) - - time_holder = self.monotonic() - - self.servers.start() + data = simulate_nodes_failure(servers, test_config.kill_coordinator, test_config.nodes_to_kill) + data['Ignite cluster start time (s)'] = start_servers_sec + return data - data = {'Ignite cluster start time (s)': round(self.monotonic() - time_holder, 1)} - failed_nodes, survived_node = self.__choose_node_to_kill(config.kill_coordinator, config.nodes_to_kill) +def start_zookeeper(test_context, num_nodes): + """ + Start zookeeper cluster. + """ + zk_quorum = ZookeeperService(test_context, num_nodes) + zk_quorum.start() + return zk_quorum - ids_to_wait = [node.discovery_info().node_id for node in failed_nodes] - if config.with_load: - self.__start_loading(version, properties, modules) +def start_servers(test_context, num_nodes, ignite_config, modules=None): + """ + Start ignite servers. + """ + servers = IgniteService(test_context, config=ignite_config, num_nodes=num_nodes, modules=modules, + # mute spam in log. + jvm_opts=["-DIGNITE_DUMP_THREADS_ON_FAILURE=false"]) - first_terminated = self.servers.stop_nodes_async(failed_nodes, clean_shutdown=False, wait_for_stop=False) + start = monotonic() + servers.start() + return servers, round(monotonic() - start, 1) - # Keeps dates of logged node failures. - logged_timestamps = [] - for failed_id in ids_to_wait: - self.servers.await_event_on_node(self.__failed_pattern(failed_id), survived_node, 20, - from_the_beginning=True, backoff_sec=0.1) +def start_load_app(test_context, ignite_config, data_amount, modules=None): + """ + Start loader application. + """ + loader = IgniteApplicationService( + test_context, + config=ignite_config, + java_class_name="org.apache.ignite.internal.ducktest.tests.ContinuousDataLoadApplication", + modules=modules, + # mute spam in log. + jvm_opts=["-DIGNITE_DUMP_THREADS_ON_FAILURE=false"], + params={"cacheName": "test-cache", "range": data_amount}) - _, stdout, _ = survived_node.account.ssh_client.exec_command( - "grep '%s' %s" % (self.__failed_pattern(failed_id), IgniteAwareService.STDOUT_STDERR_CAPTURE)) + loader.start() - logged_timestamps.append( - datetime.strptime(re.match("^\\[[^\\[]+\\]", stdout.read().decode("utf-8")).group(), - "[%Y-%m-%d %H:%M:%S,%f]")) - logged_timestamps.sort(reverse=True) +def failed_pattern(failed_node_id): + """ + Failed node pattern in log + """ + return "Node FAILED: .\\{1,\\}Node \\[id=" + failed_node_id - self.__store_results(data, logged_timestamps, first_terminated[1]) - data['Nodes failed'] = len(failed_nodes) +def choose_node_to_kill(servers, kill_coordinator, nodes_to_kill): + """Choose node to kill during test""" + assert nodes_to_kill > 0, " No nodes to kill passed. Check the parameters." - return data + nodes = servers.nodes + coordinator = nodes[0].discovery_info().coordinator + to_kill = [] - @staticmethod - def __store_results(data, logged_timestamps, first_kill_time): - first_kill_time = epoch_mills(first_kill_time) + if kill_coordinator: + to_kill.append(next(node for node in nodes if node.discovery_info().node_id == coordinator)) + nodes_to_kill -= 1 - detection_delay = epoch_mills(logged_timestamps[0]) - first_kill_time + if nodes_to_kill > 0: + choice = random.sample([n for n in nodes if n.discovery_info().node_id != coordinator], nodes_to_kill) + to_kill.extend([choice] if not isinstance(choice, list) else choice) - data['Detection of node(s) failure (ms)'] = detection_delay - data['All detection delays (ms):'] = str([epoch_mills(ts) - first_kill_time for ts in logged_timestamps]) + survive = random.choice([node for node in servers.nodes if node not in to_kill]) - @staticmethod - def __failed_pattern(failed_node_id): - return "Node FAILED: .\\{1,\\}Node \\[id=" + failed_node_id + return to_kill, survive - def __choose_node_to_kill(self, kill_coordinator, nodes_to_kill): - assert nodes_to_kill > 0, "No nodes to kill passed. Check the parameters." - nodes = self.servers.nodes - coordinator = nodes[0].discovery_info().coordinator - to_kill = [] +def simulate_nodes_failure(servers, kill_coordinator, nodes_to_kill): + """ + Perform node failure scenario + """ + failed_nodes, survived_node = choose_node_to_kill(servers, kill_coordinator, nodes_to_kill) - if kill_coordinator: - to_kill.append(next(node for node in nodes if node.discovery_info().node_id == coordinator)) - nodes_to_kill -= 1 + ids_to_wait = [node.discovery_info().node_id for node in failed_nodes] - if nodes_to_kill > 0: - choice = random.sample([n for n in nodes if n.discovery_info().node_id != coordinator], nodes_to_kill) - to_kill.extend([choice] if not isinstance(choice, list) else choice) + _, first_terminated = servers.stop_nodes_async(failed_nodes, clean_shutdown=False, wait_for_stop=False) - survive = random.choice([node for node in self.servers.nodes if node not in to_kill]) + # Keeps dates of logged node failures. + logged_timestamps = [] + data = {} - return to_kill, survive + for failed_id in ids_to_wait: + servers.await_event_on_node(failed_pattern(failed_id), survived_node, 20, + from_the_beginning=True, backoff_sec=0.1) - def __start_loading(self, ignite_version, properties, modules): - self.loader = IgniteApplicationService( - self.test_context, - java_class_name="org.apache.ignite.internal.ducktest.tests.ContinuousDataLoadApplication", - version=ignite_version, - modules=modules, - properties=properties, - params={"cacheName": "test-cache", "range": self.DATA_AMOUNT}) + _, stdout, _ = survived_node.account.ssh_client.exec_command( + "grep '%s' %s" % (failed_pattern(failed_id), IgniteAwareService.STDOUT_STDERR_CAPTURE)) - self.loader.start() + logged_timestamps.append( + datetime.strptime(re.match("^\\[[^\\[]+\\]", stdout.read().decode("utf-8")).group(), + "[%Y-%m-%d %H:%M:%S,%f]")) - def __start_zk_quorum(self): - self.zk_quorum = ZookeeperService(self.test_context, 3) + logged_timestamps.sort(reverse=True) - self.zk_quorum.start() + first_kill_time = epoch_mills(first_terminated) + detection_delay = epoch_mills(logged_timestamps[0]) - first_kill_time - @staticmethod - def __properties(zookeeper_settings=None): - """ - :param zookeeper_settings: ZooKeeperDiscoverySpi settings. If None, TcpDiscoverySpi will be used. - :return: Rendered node's properties. - """ - return Template(DiscoveryTest.CONFIG_TEMPLATE) \ - .render(failure_detection_timeout=DiscoveryTest.FAILURE_DETECTION_TIMEOUT, - zookeeper_settings=zookeeper_settings) + data['Detection of node(s) failure (ms)'] = detection_delay + data['All detection delays (ms):'] = str([epoch_mills(ts) - first_kill_time for ts in logged_timestamps]) + data['Nodes failed'] = len(failed_nodes) - @staticmethod - def __zk_properties(connection_string): - return DiscoveryTest.__properties(zookeeper_settings={'connection_string': connection_string}) + return data 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 ea266c3..8ec2daa 100644 --- a/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py +++ b/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py @@ -25,6 +25,9 @@ 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.services.utils.ignite_configuration import IgniteConfiguration +from ignitetest.services.utils.ignite_configuration.cache import CacheConfiguration +from ignitetest.services.utils.ignite_configuration.discovery import from_ignite_cluster from ignitetest.utils.ignite_test import IgniteTest from ignitetest.utils.version import DEV_BRANCH, LATEST_2_7, V_2_8_0, IgniteVersion @@ -36,29 +39,6 @@ class PmeFreeSwitchTest(IgniteTest): """ NUM_NODES = 3 - @staticmethod - def properties(): - """ - :return: Rendered configuration properties. - """ - return """ - <property name="cacheConfiguration"> - <list> - <bean class="org.apache.ignite.configuration.CacheConfiguration"> - <property name="name" value="test-cache"/> - <property name="backups" value="2"/> - <property name="atomicityMode" value="TRANSACTIONAL"/> - </bean> - </list> - </property> - """ - - def setUp(self): - pass - - def teardown(self): - pass - @cluster(num_nodes=NUM_NODES + 2) @parametrize(version=str(DEV_BRANCH)) @parametrize(version=str(LATEST_2_7)) @@ -72,22 +52,25 @@ class PmeFreeSwitchTest(IgniteTest): ignite_version = IgniteVersion(version) - ignites = IgniteService( - self.test_context, - num_nodes=self.NUM_NODES, - properties=self.properties(), - version=ignite_version) + config = IgniteConfiguration( + version=ignite_version, + caches=[CacheConfiguration(name='test-cache', backups=2, atomicity_mode='TRANSACTIONAL')] + ) + + ignites = IgniteService(self.test_context, config, num_nodes=self.NUM_NODES) ignites.start() self.stage("Starting long_tx_streamer") + client_config = config._replace(client_mode=True, + discovery_spi=from_ignite_cluster(ignites, slice(0, self.NUM_NODES - 1))) + long_tx_streamer = IgniteApplicationService( self.test_context, + client_config, java_class_name="org.apache.ignite.internal.ducktest.tests.pme_free_switch_test.LongTxStreamerApplication", - properties=self.properties(), - params={"cacheName": "test-cache"}, - version=ignite_version) + params={"cacheName": "test-cache"}) long_tx_streamer.start() @@ -95,11 +78,10 @@ class PmeFreeSwitchTest(IgniteTest): single_key_tx_streamer = IgniteApplicationService( self.test_context, + client_config, java_class_name="org.apache.ignite.internal.ducktest.tests.pme_free_switch_test." "SingleKeyTxStreamerApplication", - properties=self.properties(), - params={"cacheName": "test-cache", "warmup": 1000}, - version=ignite_version) + params={"cacheName": "test-cache", "warmup": 1000}) single_key_tx_streamer.start() @@ -108,7 +90,7 @@ class PmeFreeSwitchTest(IgniteTest): self.stage("Stopping server node") - ignites.stop_node(ignites.nodes[1]) + ignites.stop_node(ignites.nodes[self.NUM_NODES - 1]) long_tx_streamer.await_event("Node left topology", 60, from_the_beginning=True) diff --git a/modules/ducktests/tests/ignitetest/tests/smoke_test.py b/modules/ducktests/tests/ignitetest/tests/smoke_test.py index 48965e3..d7207a2 100644 --- a/modules/ducktests/tests/ignitetest/tests/smoke_test.py +++ b/modules/ducktests/tests/ignitetest/tests/smoke_test.py @@ -18,13 +18,16 @@ This module contains smoke tests that checks that services work """ from ducktape.mark import parametrize +from ducktape.mark.resource import cluster from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService from ignitetest.services.spark import SparkService +from ignitetest.services.utils.ignite_configuration.discovery import from_ignite_cluster +from ignitetest.services.utils.ignite_configuration import IgniteConfiguration, IgniteClientConfiguration from ignitetest.services.zk.zookeeper import ZookeeperService from ignitetest.utils.ignite_test import IgniteTest -from ignitetest.utils.version import DEV_BRANCH +from ignitetest.utils.version import DEV_BRANCH, IgniteVersion # pylint: disable=W0223 @@ -33,44 +36,35 @@ class SmokeServicesTest(IgniteTest): Tests services implementations """ - def setUp(self): - pass - - def teardown(self): - pass - + @cluster(num_nodes=1) @parametrize(version=str(DEV_BRANCH)) def test_ignite_start_stop(self, version): """ Test that IgniteService correctly start and stop """ - ignite = IgniteService( - self.test_context, - num_nodes=1, - version=version) + ignite = IgniteService(self.test_context, IgniteConfiguration(version=IgniteVersion(version)), num_nodes=1) ignite.start() ignite.stop() + @cluster(num_nodes=2) @parametrize(version=str(DEV_BRANCH)) def test_ignite_app_start_stop(self, version): """ Test that IgniteService and IgniteApplicationService correctly start and stop """ - ignite = IgniteService( - self.test_context, - num_nodes=1, - version=version) + ignite = IgniteService(self.test_context, IgniteConfiguration(version=IgniteVersion(version)), num_nodes=1) app = IgniteApplicationService( self.test_context, - java_class_name="org.apache.ignite.internal.ducktest.tests.smoke_test.SimpleApplication", - version=version) + IgniteClientConfiguration(version=IgniteVersion(version), discovery_spi=from_ignite_cluster(ignite)), + java_class_name="org.apache.ignite.internal.ducktest.tests.smoke_test.SimpleApplication") ignite.start() app.start() app.stop() ignite.stop() + @cluster(num_nodes=2) def test_spark_start_stop(self): """ Test that SparkService correctly start and stop @@ -79,10 +73,11 @@ class SmokeServicesTest(IgniteTest): spark.start() spark.stop() + @cluster(num_nodes=3) def test_zk_start_stop(self): """ Test that ZookeeperService correctly start and stop """ - zookeeper = ZookeeperService(self.test_context, num_nodes=2) + zookeeper = ZookeeperService(self.test_context, num_nodes=3) zookeeper.start() zookeeper.stop() diff --git a/modules/ducktests/tests/setup.py b/modules/ducktests/tests/ignitetest/tests/suites/fast_suite.yml similarity index 51% copy from modules/ducktests/tests/setup.py copy to modules/ducktests/tests/ignitetest/tests/suites/fast_suite.yml index 76dd815..4811574 100644 --- a/modules/ducktests/tests/setup.py +++ b/modules/ducktests/tests/ignitetest/tests/suites/fast_suite.yml @@ -13,25 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -import re -from setuptools import find_packages, setup +smoke: + - ../smoke_test.py +control_utility: + - ../control_utility_test.py -with open('ignitetest/__init__.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) +pme_free_switch: + - ../pme_free_switch_test.py +cellular_affinity: + - ../cellular_affinity_test.py -# Note: when changing the version of ducktape, also revise tests/docker/Dockerfile -setup(name="ignitetest", - version=version, - description="Apache Ignite System Tests", - author="Apache Ignite", - platforms=["any"], - license="apache2.0", - packages=find_packages(exclude=["ignitetest.tests", "ignitetest.tests.*"]), - include_package_data=True, - install_requires=['ducktape==0.8.0'], - dependency_links = [ - 'https://github.com/confluentinc/ducktape/tarball/master#egg=ducktape-0.8.0' - ] -) +rebalance: + - ../add_node_rebalance_test.py diff --git a/modules/ducktests/tests/ignitetest/tests/suites/slow_suite.yml b/modules/ducktests/tests/ignitetest/tests/suites/slow_suite.yml new file mode 100644 index 0000000..6f066d5 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/tests/suites/slow_suite.yml @@ -0,0 +1,17 @@ +# 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 + +discovery: + - ../discovery_test.py diff --git a/modules/ducktests/tests/setup.py b/modules/ducktests/tests/setup.py index 76dd815..5b89d58 100644 --- a/modules/ducktests/tests/setup.py +++ b/modules/ducktests/tests/setup.py @@ -31,7 +31,6 @@ setup(name="ignitetest", packages=find_packages(exclude=["ignitetest.tests", "ignitetest.tests.*"]), include_package_data=True, install_requires=['ducktape==0.8.0'], - dependency_links = [ + dependency_links=[ 'https://github.com/confluentinc/ducktape/tarball/master#egg=ducktape-0.8.0' - ] -) + ])