[Verify] Adding possibility to specify concurrency for tests
The `rally verify start` command doesn't allow us to specify concurrency for Tempest tests. The tests always are run in parallel mode and use concurrency equal to the count of CPU cores. However, users may want to use specific concurrency for running tests based on their deployments and available resources. This patch adds possibility to specify desired concurrency for tests. Closes-Bug: #1524968 Change-Id: I3ba2c987c739b13bb03005b1988ee6bd01e8a08d
This commit is contained in:
parent
180b57acc3
commit
1f3851f409
@ -56,7 +56,7 @@ _rally()
|
|||||||
OPTS["verify_results"]="--uuid --html --json --output-file"
|
OPTS["verify_results"]="--uuid --html --json --output-file"
|
||||||
OPTS["verify_show"]="--uuid --sort-by --detailed"
|
OPTS["verify_show"]="--uuid --sort-by --detailed"
|
||||||
OPTS["verify_showconfig"]="--deployment"
|
OPTS["verify_showconfig"]="--deployment"
|
||||||
OPTS["verify_start"]="--deployment --set --regex --tests-file --tempest-config --no-use --system-wide-install"
|
OPTS["verify_start"]="--deployment --set --regex --tests-file --tempest-config --no-use --system-wide-install --concurrency"
|
||||||
OPTS["verify_uninstall"]="--deployment"
|
OPTS["verify_uninstall"]="--deployment"
|
||||||
OPTS["verify_use"]="--verification"
|
OPTS["verify_use"]="--verification"
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ class Verification(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def verify(cls, deployment, set_name, regex, tests_file,
|
def verify(cls, deployment, set_name, regex, tests_file,
|
||||||
tempest_config, system_wide_install=False):
|
tempest_config, system_wide_install=False, concur=0):
|
||||||
"""Start verifying.
|
"""Start verifying.
|
||||||
|
|
||||||
:param deployment: UUID or name of a deployment
|
:param deployment: UUID or name of a deployment
|
||||||
@ -370,9 +370,10 @@ class Verification(object):
|
|||||||
when installing Tempest; whether or not to
|
when installing Tempest; whether or not to
|
||||||
use the local env instead of the Tempest
|
use the local env instead of the Tempest
|
||||||
virtual env when running the tests
|
virtual env when running the tests
|
||||||
|
:param concur: How many processes to use to run Tempest tests.
|
||||||
|
The default value (0) auto-detects CPU count
|
||||||
:returns: Verification object
|
:returns: Verification object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
deployment_uuid = objects.Deployment.get(deployment)["uuid"]
|
deployment_uuid = objects.Deployment.get(deployment)["uuid"]
|
||||||
verification = objects.Verification(deployment_uuid=deployment_uuid)
|
verification = objects.Verification(deployment_uuid=deployment_uuid)
|
||||||
verifier = tempest.Tempest(deployment_uuid, verification=verification,
|
verifier = tempest.Tempest(deployment_uuid, verification=verification,
|
||||||
@ -389,7 +390,7 @@ class Verification(object):
|
|||||||
LOG.info("Starting verification of deployment: %s" % deployment_uuid)
|
LOG.info("Starting verification of deployment: %s" % deployment_uuid)
|
||||||
verification.set_running()
|
verification.set_running()
|
||||||
verifier.verify(set_name=set_name, regex=regex,
|
verifier.verify(set_name=set_name, regex=regex,
|
||||||
tests_file=tests_file)
|
tests_file=tests_file, concur=concur)
|
||||||
|
|
||||||
return verification
|
return verification
|
||||||
|
|
||||||
|
@ -63,10 +63,13 @@ class VerifyCommands(object):
|
|||||||
"requirements have to be already installed in "
|
"requirements have to be already installed in "
|
||||||
"the local env!",
|
"the local env!",
|
||||||
required=False, action="store_true")
|
required=False, action="store_true")
|
||||||
|
@cliutils.args("--concurrency", dest="concur", type=int, required=False,
|
||||||
|
help="How many processes to use to run Tempest tests. "
|
||||||
|
"The default value (0) auto-detects your CPU count")
|
||||||
@envutils.with_default_deployment(cli_arg_name="deployment")
|
@envutils.with_default_deployment(cli_arg_name="deployment")
|
||||||
def start(self, set_name="", deployment=None, regex=None,
|
def start(self, set_name="", deployment=None, regex=None,
|
||||||
tests_file=None, tempest_config=None, do_use=True,
|
tests_file=None, tempest_config=None, do_use=True,
|
||||||
system_wide_install=False):
|
system_wide_install=False, concur=0):
|
||||||
"""Start set of tests.
|
"""Start set of tests.
|
||||||
|
|
||||||
:param set_name: Name of tempest test set
|
:param set_name: Name of tempest test set
|
||||||
@ -79,6 +82,8 @@ class VerifyCommands(object):
|
|||||||
when installing Tempest; whether or not to
|
when installing Tempest; whether or not to
|
||||||
use the local env instead of the Tempest
|
use the local env instead of the Tempest
|
||||||
virtual env when running the tests
|
virtual env when running the tests
|
||||||
|
:param concur: How many processes to use to run Tempest tests.
|
||||||
|
The default value (0) auto-detects CPU count
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if regex and set_name:
|
if regex and set_name:
|
||||||
@ -109,7 +114,7 @@ class VerifyCommands(object):
|
|||||||
|
|
||||||
verification = api.Verification.verify(deployment, set_name, regex,
|
verification = api.Verification.verify(deployment, set_name, regex,
|
||||||
tests_file, tempest_config,
|
tests_file, tempest_config,
|
||||||
system_wide_install)
|
system_wide_install, concur)
|
||||||
if do_use:
|
if do_use:
|
||||||
self.use(verification["uuid"])
|
self.use(verification["uuid"])
|
||||||
|
|
||||||
|
@ -297,54 +297,48 @@ class Tempest(object):
|
|||||||
shutil.rmtree(self.path())
|
shutil.rmtree(self.path())
|
||||||
|
|
||||||
@logging.log_verification_wrapper(LOG.info, _("Run verification."))
|
@logging.log_verification_wrapper(LOG.info, _("Run verification."))
|
||||||
def _prepare_and_run(self, set_name, regex, tests_file):
|
def _prepare_and_run(self, set_name, regex, tests_file, concur):
|
||||||
if not self.is_configured():
|
if not self.is_configured():
|
||||||
self.generate_config_file()
|
self.generate_config_file()
|
||||||
|
|
||||||
|
testr_args = "--concurrency %d" % concur
|
||||||
|
|
||||||
if set_name == "full":
|
if set_name == "full":
|
||||||
testr_arg = ""
|
pass
|
||||||
|
elif set_name in consts.TempestTestsAPI:
|
||||||
|
testr_args += " tempest.api.%s" % set_name
|
||||||
|
elif regex:
|
||||||
|
testr_args += " %s" % regex
|
||||||
else:
|
else:
|
||||||
if set_name in consts.TempestTestsAPI:
|
testr_args += " --load-list %s" % os.path.abspath(tests_file)
|
||||||
testr_arg = "tempest.api.%s" % set_name
|
|
||||||
elif tests_file:
|
|
||||||
testr_arg = "--load-list %s" % os.path.abspath(tests_file)
|
|
||||||
else:
|
|
||||||
testr_arg = set_name or regex
|
|
||||||
|
|
||||||
self.verification.start_verifying(set_name)
|
self.verification.start_verifying(set_name)
|
||||||
try:
|
try:
|
||||||
self.run(testr_arg)
|
self.run(testr_args)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
LOG.info(_("Test set '%s' has been finished with errors. "
|
LOG.info(_("Test run has been finished with errors. "
|
||||||
"Check logs for details.") % set_name)
|
"Check logs for details."))
|
||||||
|
|
||||||
def run(self, testr_arg=None, log_file=None, tempest_conf=None):
|
def run(self, testr_args="", log_file=None, tempest_conf=None):
|
||||||
"""Launch tempest with given arguments
|
"""Run Tempest.
|
||||||
|
|
||||||
:param testr_arg: argument which will be transmitted into testr
|
:param testr_args: Arguments which will be passed to testr
|
||||||
:type testr_arg: str
|
:param log_file: Path to a file for raw subunit stream logs.
|
||||||
:param log_file: file name for raw subunit results of tests. If not
|
If not specified, the value from "self.log_file_raw"
|
||||||
specified, value from "self.log_file_raw"
|
will be used as the path to the file
|
||||||
will be chosen.
|
:param tempest_conf: User specified Tempest config file location
|
||||||
:type log_file: str
|
|
||||||
:param tempest_conf: User specified tempest.conf location
|
|
||||||
:type tempest_conf: str
|
|
||||||
|
|
||||||
:raises subprocess.CalledProcessError: if tests has been finished
|
|
||||||
with error.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if tempest_conf and os.path.isfile(tempest_conf):
|
if tempest_conf and os.path.isfile(tempest_conf):
|
||||||
self.config_file = tempest_conf
|
self.config_file = tempest_conf
|
||||||
LOG.info(_("Tempest config file: %s") % self.config_file)
|
LOG.info(_("Tempest config file: %s") % self.config_file)
|
||||||
|
|
||||||
test_cmd = (
|
test_cmd = (
|
||||||
"%(venv)s testr run --parallel --subunit %(arg)s "
|
"%(venv)s testr run --subunit --parallel %(testr_args)s "
|
||||||
"| tee %(log_file)s "
|
"| tee %(log_file)s "
|
||||||
"| %(venv)s subunit-trace -f -n" %
|
"| %(venv)s subunit-trace -f -n" %
|
||||||
{
|
{
|
||||||
"venv": self.venv_wrapper,
|
"venv": self.venv_wrapper,
|
||||||
"arg": testr_arg,
|
"testr_args": testr_args,
|
||||||
"log_file": log_file or self.log_file_raw
|
"log_file": log_file or self.log_file_raw
|
||||||
})
|
})
|
||||||
LOG.debug("Test(s) started by the command: %s" % test_cmd)
|
LOG.debug("Test(s) started by the command: %s" % test_cmd)
|
||||||
@ -393,8 +387,8 @@ class Tempest(object):
|
|||||||
else:
|
else:
|
||||||
self.verification.set_failed()
|
self.verification.set_failed()
|
||||||
|
|
||||||
def verify(self, set_name, regex, tests_file):
|
def verify(self, set_name, regex, tests_file, concur):
|
||||||
self._prepare_and_run(set_name, regex, tests_file)
|
self._prepare_and_run(set_name, regex, tests_file, concur)
|
||||||
self._save_results()
|
self._save_results()
|
||||||
|
|
||||||
def import_results(self, set_name, log_file):
|
def import_results(self, set_name, log_file):
|
||||||
|
@ -60,7 +60,7 @@ class VerifyCommandsTestCase(test.TestCase):
|
|||||||
|
|
||||||
mock_verification_verify.assert_called_once_with(
|
mock_verification_verify.assert_called_once_with(
|
||||||
deployment_id, default_set_name, default_regex,
|
deployment_id, default_set_name, default_regex,
|
||||||
default_tests_file, None, False)
|
default_tests_file, None, False, 0)
|
||||||
|
|
||||||
@mock.patch("rally.osclients.Clients")
|
@mock.patch("rally.osclients.Clients")
|
||||||
@mock.patch("rally.api.Verification.verify")
|
@mock.patch("rally.api.Verification.verify")
|
||||||
@ -80,7 +80,7 @@ class VerifyCommandsTestCase(test.TestCase):
|
|||||||
|
|
||||||
mock_verification_verify.assert_called_once_with(
|
mock_verification_verify.assert_called_once_with(
|
||||||
deployment_id, default_set_name, default_regex,
|
deployment_id, default_set_name, default_regex,
|
||||||
default_tests_file, tempest_config.name, False)
|
default_tests_file, tempest_config.name, False, 0)
|
||||||
tempest_config.close()
|
tempest_config.close()
|
||||||
|
|
||||||
@mock.patch("rally.api.Verification.verify")
|
@mock.patch("rally.api.Verification.verify")
|
||||||
@ -93,7 +93,7 @@ class VerifyCommandsTestCase(test.TestCase):
|
|||||||
tests_file=tests_file, do_use=False)
|
tests_file=tests_file, do_use=False)
|
||||||
|
|
||||||
mock_verification_verify.assert_called_once_with(
|
mock_verification_verify.assert_called_once_with(
|
||||||
deployment_id, "", None, tests_file, None, False)
|
deployment_id, "", None, tests_file, None, False, 0)
|
||||||
|
|
||||||
@mock.patch("rally.api.Verification.verify")
|
@mock.patch("rally.api.Verification.verify")
|
||||||
def test_start_with_wrong_set_name(self, mock_verification_verify):
|
def test_start_with_wrong_set_name(self, mock_verification_verify):
|
||||||
|
@ -40,7 +40,7 @@ class TempestScenarioTestCase(test.TestCase):
|
|||||||
self.scenario._add_atomic_actions = mock.MagicMock()
|
self.scenario._add_atomic_actions = mock.MagicMock()
|
||||||
|
|
||||||
def get_tests_launcher_cmd(self, tests):
|
def get_tests_launcher_cmd(self, tests):
|
||||||
return ("%(venv)s testr run --parallel --subunit %(tests)s "
|
return ("%(venv)s testr run --subunit --parallel %(tests)s "
|
||||||
"| tee /dev/null "
|
"| tee /dev/null "
|
||||||
"| %(venv)s subunit-trace -f -n" %
|
"| %(venv)s subunit-trace -f -n" %
|
||||||
{
|
{
|
||||||
|
@ -408,7 +408,7 @@ class VerificationAPITestCase(BaseDeploymentTestCase):
|
|||||||
|
|
||||||
self.tempest.is_installed.assert_called_once_with()
|
self.tempest.is_installed.assert_called_once_with()
|
||||||
self.tempest.verify.assert_called_once_with(
|
self.tempest.verify.assert_called_once_with(
|
||||||
set_name="smoke", regex=None, tests_file=None)
|
set_name="smoke", regex=None, tests_file=None, concur=0)
|
||||||
|
|
||||||
@mock.patch("rally.api.objects.Deployment.get")
|
@mock.patch("rally.api.objects.Deployment.get")
|
||||||
@mock.patch("rally.api.objects.Verification")
|
@mock.patch("rally.api.objects.Verification")
|
||||||
@ -425,7 +425,7 @@ class VerificationAPITestCase(BaseDeploymentTestCase):
|
|||||||
self.tempest.is_installed.assert_called_once_with()
|
self.tempest.is_installed.assert_called_once_with()
|
||||||
self.tempest.install.assert_called_once_with()
|
self.tempest.install.assert_called_once_with()
|
||||||
self.tempest.verify.assert_called_once_with(
|
self.tempest.verify.assert_called_once_with(
|
||||||
set_name="smoke", regex=None, tests_file=None)
|
set_name="smoke", regex=None, tests_file=None, concur=0)
|
||||||
|
|
||||||
@mock.patch("os.path.exists", return_value=True)
|
@mock.patch("os.path.exists", return_value=True)
|
||||||
@mock.patch("rally.api.objects.Deployment.get")
|
@mock.patch("rally.api.objects.Deployment.get")
|
||||||
@ -441,7 +441,7 @@ class VerificationAPITestCase(BaseDeploymentTestCase):
|
|||||||
self.deployment_uuid, "", None, tests_file, None)
|
self.deployment_uuid, "", None, tests_file, None)
|
||||||
|
|
||||||
self.tempest.verify.assert_called_once_with(
|
self.tempest.verify.assert_called_once_with(
|
||||||
set_name="", regex=None, tests_file=tests_file)
|
set_name="", regex=None, tests_file=tests_file, concur=0)
|
||||||
|
|
||||||
@mock.patch("rally.common.objects.Deployment.get")
|
@mock.patch("rally.common.objects.Deployment.get")
|
||||||
@mock.patch("rally.api.objects.Verification")
|
@mock.patch("rally.api.objects.Verification")
|
||||||
|
@ -332,15 +332,15 @@ class TempestInstallAndUninstallTestCase(BaseTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TempestVerifyTestCase(BaseTestCase):
|
class TempestVerifyTestCase(BaseTestCase):
|
||||||
def _get_fake_call(self, testr_arg, is_set=True):
|
def _get_fake_call(self, testr_args):
|
||||||
return (
|
return (
|
||||||
"%(venv)s testr run --parallel --subunit %(testr_arg)s"
|
"%(venv)s testr run --subunit --parallel"
|
||||||
" | tee %(tempest_path)s/subunit.stream"
|
" --concurrency 0 %(testr_args)s"
|
||||||
|
" | tee %(log_file)s"
|
||||||
" | %(venv)s subunit-trace -f -n" % {
|
" | %(venv)s subunit-trace -f -n" % {
|
||||||
"venv": self.verifier.venv_wrapper,
|
"venv": self.verifier.venv_wrapper,
|
||||||
"testr_arg": ("tempest.api." if is_set
|
"testr_args": testr_args,
|
||||||
else "--load-list ") + testr_arg,
|
"log_file": self.verifier.path("subunit.stream")})
|
||||||
"tempest_path": self.verifier.path()})
|
|
||||||
|
|
||||||
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.parse_results",
|
@mock.patch(TEMPEST_PATH + ".tempest.Tempest.parse_results",
|
||||||
return_value=None)
|
return_value=None)
|
||||||
@ -356,9 +356,9 @@ class TempestVerifyTestCase(BaseTestCase):
|
|||||||
mock_tempest_parse_results):
|
mock_tempest_parse_results):
|
||||||
|
|
||||||
set_name = "compute"
|
set_name = "compute"
|
||||||
fake_call = self._get_fake_call(set_name)
|
fake_call = self._get_fake_call("tempest.api.%s" % set_name)
|
||||||
|
|
||||||
self.verifier.verify(set_name, None, None)
|
self.verifier.verify(set_name, None, None, 0)
|
||||||
|
|
||||||
self.assertEqual(2, mock_tempest_is_configured.call_count)
|
self.assertEqual(2, mock_tempest_is_configured.call_count)
|
||||||
mock_tempest_config.assert_called_once_with(self.verifier.deployment)
|
mock_tempest_config.assert_called_once_with(self.verifier.deployment)
|
||||||
@ -386,9 +386,9 @@ class TempestVerifyTestCase(BaseTestCase):
|
|||||||
mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
|
mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
|
||||||
mock_tempest_parse_results):
|
mock_tempest_parse_results):
|
||||||
set_name = "identity"
|
set_name = "identity"
|
||||||
fake_call = self._get_fake_call(set_name)
|
fake_call = self._get_fake_call("tempest.api.%s" % set_name)
|
||||||
|
|
||||||
self.verifier.verify(set_name, None, None)
|
self.verifier.verify(set_name, None, None, 0)
|
||||||
|
|
||||||
mock_tempest_is_configured.assert_called_once_with()
|
mock_tempest_is_configured.assert_called_once_with()
|
||||||
self.assertFalse(mock_tempest_config.called)
|
self.assertFalse(mock_tempest_config.called)
|
||||||
@ -414,10 +414,10 @@ class TempestVerifyTestCase(BaseTestCase):
|
|||||||
mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
|
mock_tempest_resources_context, mock_subprocess, mock_tempest_env,
|
||||||
mock_tempest_parse_results):
|
mock_tempest_parse_results):
|
||||||
set_name = "identity"
|
set_name = "identity"
|
||||||
fake_call = self._get_fake_call(set_name)
|
fake_call = self._get_fake_call("tempest.api.%s" % set_name)
|
||||||
mock_subprocess.side_effect = subprocess.CalledProcessError
|
mock_subprocess.side_effect = subprocess.CalledProcessError
|
||||||
|
|
||||||
self.verifier.verify(set_name, None, None)
|
self.verifier.verify(set_name, None, None, 0)
|
||||||
|
|
||||||
mock_tempest_is_configured.assert_called_once_with()
|
mock_tempest_is_configured.assert_called_once_with()
|
||||||
self.assertFalse(mock_tempest_config.called)
|
self.assertFalse(mock_tempest_config.called)
|
||||||
@ -442,9 +442,9 @@ class TempestVerifyTestCase(BaseTestCase):
|
|||||||
self, mock_tempest_is_configured, mock_tempest_resources_context,
|
self, mock_tempest_is_configured, mock_tempest_resources_context,
|
||||||
mock_subprocess, mock_tempest_env, mock_tempest_parse_results):
|
mock_subprocess, mock_tempest_env, mock_tempest_parse_results):
|
||||||
tests_file = "/path/to/tests/file"
|
tests_file = "/path/to/tests/file"
|
||||||
fake_call = self._get_fake_call(tests_file, is_set=False)
|
fake_call = self._get_fake_call("--load-list %s" % tests_file)
|
||||||
|
|
||||||
self.verifier.verify("", None, tests_file)
|
self.verifier.verify("", None, tests_file, 0)
|
||||||
self.verifier.verification.start_verifying.assert_called_once_with("")
|
self.verifier.verification.start_verifying.assert_called_once_with("")
|
||||||
|
|
||||||
mock_subprocess.check_call.assert_called_once_with(
|
mock_subprocess.check_call.assert_called_once_with(
|
||||||
|
Loading…
Reference in New Issue
Block a user