diff --git a/etc/rally.bash_completion b/etc/rally.bash_completion index f7c43aa977..220b8dcdad 100644 --- a/etc/rally.bash_completion +++ b/etc/rally.bash_completion @@ -49,6 +49,7 @@ _rally() OPTS["verify_compare"]="--uuid-1 --uuid-2 --csv --html --json --output-file --threshold" OPTS["verify_detailed"]="--uuid --sort-by" OPTS["verify_genconfig"]="--deployment --tempest-config --override" + OPTS["verify_import"]="--deployment --set-name --file --no-use" OPTS["verify_install"]="--deployment --source" OPTS["verify_list"]="" OPTS["verify_reinstall"]="--deployment --tempest-config --source" diff --git a/rally/api.py b/rally/api.py index 88c0fcf38c..5899e775bc 100644 --- a/rally/api.py +++ b/rally/api.py @@ -333,6 +333,29 @@ class Verification(object): return verifier + @classmethod + def import_file(cls, deployment, set_name, log_file=None): + """Import tempest log. + + :param deployment: UUID or name of a deployment. + :param log_file: User specified Tempest log file name. + :returns: Deployment and verification objects + """ + + # TODO(aplanas): Create an external deployment if this is + # missing, as required in the blueprint [1]. + # [1] https://blueprints.launchpad.net/rally/+spec/verification-import + deployment_uuid = objects.Deployment.get(deployment)["uuid"] + + verification = objects.Verification(deployment_uuid=deployment_uuid) + verifier = tempest.Tempest(deployment_uuid, verification=verification) + LOG.info("Importing verification of deployment: %s" % deployment_uuid) + + verification.set_running() + verifier.import_file(set_name=set_name, log_file=log_file) + + return deployment, verification + @classmethod def install_tempest(cls, deployment, source=None): """Install Tempest. diff --git a/rally/cli/commands/verify.py b/rally/cli/commands/verify.py index d40dd58dc7..e279e7422d 100644 --- a/rally/cli/commands/verify.py +++ b/rally/cli/commands/verify.py @@ -89,6 +89,36 @@ class VerifyCommands(object): if do_use: self.use(verification["uuid"]) + @cliutils.args("--deployment", dest="deployment", type=str, + required=False, help="UUID or name of a deployment.") + @cliutils.args("--set-name", dest="set_name", type=str, required=False, + help="Name of tempest test set. Available sets: %s" % ", ". + join(list(consts.TempestTestsSets) + + list(consts.TempestTestsAPI))) + @cliutils.args("--file", dest="log_file", type=str, + required=True, + help="User specified Tempest log file location") + @cliutils.args("--no-use", action="store_false", dest="do_use", + required=False, + help="Don't set new task as default for future operations") + @cliutils.alias("import") + def import_file(self, deployment=None, set_name=None, log_file=None, + do_use=True): + """Import a tempest result into rally. + + :param deployment: UUID or name of a deployment + :param set_name: Name of tempest test set + :param do_use: Use new task as default for future operations + :param log_file: User specified Tempest log file + """ + + deployment, verification = api.Verification.import_file(deployment, + set_name, + log_file) + + if do_use: + self.use(verification["uuid"]) + def list(self): """Display all verifications table, started and finished.""" diff --git a/rally/verification/tempest/tempest.py b/rally/verification/tempest/tempest.py index 9c4b9007bb..c419acda87 100644 --- a/rally/verification/tempest/tempest.py +++ b/rally/verification/tempest/tempest.py @@ -381,8 +381,8 @@ class Tempest(object): @utils.log_verification_wrapper( LOG.info, _("Saving verification results.")) - def _save_results(self): - total, test_cases = self.parse_results() + def _save_results(self, log_file=None): + total, test_cases = self.parse_results(log_file) if total and test_cases and self.verification: self.verification.finish_verification(total=total, test_cases=test_cases) @@ -392,3 +392,10 @@ class Tempest(object): def verify(self, set_name, regex): self._prepare_and_run(set_name, regex) self._save_results() + + def import_file(self, set_name, log_file): + if log_file: + self.verification.start_verifying(set_name) + self._save_results(log_file) + else: + LOG.error("No import file specified.") diff --git a/tests/unit/cli/commands/test_verify.py b/tests/unit/cli/commands/test_verify.py index 8c0ba08100..afbf07258b 100644 --- a/tests/unit/cli/commands/test_verify.py +++ b/tests/unit/cli/commands/test_verify.py @@ -94,6 +94,29 @@ class VerifyCommandsTestCase(test.TestCase): consts.TempestTestsAPI) self.assertFalse(mock_verification_verify.called) + @mock.patch("rally.api.Verification.import_file") + def test_import_file(self, mock_verification_import_file): + deployment_id = "fake_uuid" + mock_verification_import_file.return_value = (None, None) + self.verify.import_file(deployment=deployment_id, do_use=False) + default_set_name = None + default_log_file = None + + mock_verification_import_file.assert_called_once_with( + deployment_id, default_set_name, default_log_file) + + @mock.patch("rally.api.Verification.import_file") + def test_import_file_without_defaults(self, mock_verification_import_file): + deployment_id = "fake_uuid" + set_name = "fake_set_name" + log_file = "fake_log_file" + mock_verification_import_file.return_value = (None, None) + self.verify.import_file(deployment=deployment_id, set_name=set_name, + log_file=log_file, do_use=False) + + mock_verification_import_file.assert_called_once_with( + deployment_id, set_name, log_file) + @mock.patch("rally.cli.cliutils.print_list") @mock.patch("rally.common.db.verification_list") def test_list(self, mock_common_db_verification_list, mock_print_list): diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index e4e44b9cf3..82ed81652b 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -352,6 +352,20 @@ class VerificationAPITestCase(BaseDeploymentTestCase): self.tempest.verify.assert_called_once_with(set_name="smoke", regex=None) + @mock.patch("rally.common.objects.Deployment.get") + @mock.patch("rally.api.objects.Verification") + @mock.patch("rally.verification.tempest.tempest.Tempest") + def test_import_file(self, mock_tempest, mock_verification, + mock_deployment_get): + mock_deployment_get.return_value = {"uuid": self.deployment_uuid} + + mock_tempest.return_value = self.tempest + self.tempest.is_installed.return_value = True + api.Verification.import_file(self.deployment_uuid, "smoke", "log_file") + + self.tempest.import_file.assert_called_once_with( + set_name="smoke", log_file="log_file") + @mock.patch("rally.api.objects.Deployment.get") @mock.patch("rally.api.tempest.Tempest") def test_install_tempest(self, mock_tempest, mock_deployment_get): diff --git a/tests/unit/verification/test_tempest.py b/tests/unit/verification/test_tempest.py index 80100ce380..5838ca8bdd 100644 --- a/tests/unit/verification/test_tempest.py +++ b/tests/unit/verification/test_tempest.py @@ -362,7 +362,7 @@ class TempestVerifyTestCase(BaseTestCase): mock_subprocess.check_call.assert_called_once_with( fake_call, env=mock_tempest_env, cwd=self.verifier.path(), shell=True) - mock_tempest_parse_results.assert_called_once_with() + mock_tempest_parse_results.assert_called_once_with(None) @mock.patch(TEMPEST_PATH + ".tempest.Tempest.parse_results", return_value=(None, None)) @@ -388,7 +388,7 @@ class TempestVerifyTestCase(BaseTestCase): mock_subprocess.check_call.assert_called_once_with( fake_call, env=mock_tempest_env, cwd=self.verifier.path(), shell=True) - mock_tempest_parse_results.assert_called_once_with() + mock_tempest_parse_results.assert_called_once_with(None) @mock.patch(TEMPEST_PATH + ".tempest.Tempest.parse_results", return_value=(None, None)) @@ -417,3 +417,13 @@ class TempestVerifyTestCase(BaseTestCase): shell=True) self.assertTrue(mock_tempest_parse_results.called) self.verifier.verification.set_failed.assert_called_once_with() + + def test_import_file(self): + set_name = "identity" + log_file = "log_file" + + self.verifier._save_results = mock.Mock() + self.verifier.import_file(set_name, log_file) + mock_start_verifying = self.verifier.verification.start_verifying + mock_start_verifying.assert_called_once_with(set_name) + self.verifier._save_results.assert_called_once_with(log_file)