Browse Source

Fix rally task verification log

- Fix rally task verification log for FAILED tasks.
- Fix inconsistent output of "rally task detailed"
  for non-FINISHED / non-FAILED tasks.
- Fix unit tests for rally.cli.commands.task

Change-Id: I97787b211aca0872251fab3b80cc213338ac2657
Closes-Bug: #1562713
tags/0.4.0
Piyush Raman Srivastava 3 years ago
parent
commit
bc819cb728

+ 18
- 5
rally/cli/commands/task.py View File

@@ -19,6 +19,7 @@ from __future__ import print_function
19 19
 import json
20 20
 import os
21 21
 import sys
22
+import traceback
22 23
 import webbrowser
23 24
 
24 25
 import jsonschema
@@ -131,10 +132,14 @@ class TaskCommands(object):
131 132
 
132 133
     def _load_and_validate_task(self, task, task_args, task_args_file,
133 134
                                 deployment, task_instance=None):
134
-        if not os.path.exists(task) or os.path.isdir(task):
135
+        if not os.path.isfile(task):
136
+            err_cls = IOError
137
+            msg = "No such file '%s'" % task
135 138
             if task_instance:
136
-                task_instance.set_failed(log="No such file '%s'" % task)
137
-            raise IOError("File '%s' is not found." % task)
139
+                task_instance.set_failed(err_cls.__name__,
140
+                                         msg,
141
+                                         json.dumps(traceback.format_stack()))
142
+            raise err_cls(msg)
138 143
         input_task = self._load_task(task, task_args, task_args_file)
139 144
         api.Task.validate(deployment, input_task, task_instance)
140 145
         print(_("Task config is valid :)"))
@@ -252,7 +257,9 @@ class TaskCommands(object):
252 257
             self.detailed(task_id=task_instance["uuid"])
253 258
 
254 259
         except (exceptions.InvalidTaskException, FailedToLoadTask) as e:
255
-            task_instance.set_failed(log=e.format_message())
260
+            task_instance.set_failed(type(e).__name__,
261
+                                     str(e),
262
+                                     json.dumps(traceback.format_exc()))
256 263
             print(e, file=sys.stderr)
257 264
             return(1)
258 265
 
@@ -318,7 +325,6 @@ class TaskCommands(object):
318 325
         if task["status"] == consts.TaskStatus.FAILED:
319 326
             print("-" * 80)
320 327
             verification = yaml.safe_load(task["verification_log"])
321
-
322 328
             if logging.is_debug():
323 329
                 print(yaml.safe_load(verification[2]))
324 330
             else:
@@ -327,6 +333,13 @@ class TaskCommands(object):
327 333
                 print(_("\nFor more details run:\nrally -vd task detailed %s")
328 334
                       % task["uuid"])
329 335
             return 0
336
+        elif task["status"] not in [consts.TaskStatus.FINISHED,
337
+                                    consts.TaskStatus.ABORTED]:
338
+            print("-" * 80)
339
+            print(_("\nThe task %s marked as '%s'. Results "
340
+                    "available when it is '%s'.") % (
341
+                task_id, task["status"], consts.TaskStatus.FINISHED))
342
+            return 0
330 343
         for result in task["results"]:
331 344
             key = result["key"]
332 345
             print("-" * 80)

+ 5
- 2
rally/common/objects/task.py View File

@@ -346,9 +346,12 @@ class Task(object):
346 346
     def update_verification_log(self, log):
347 347
         self._update({"verification_log": json.dumps(log)})
348 348
 
349
-    def set_failed(self, log=""):
349
+    def set_failed(self, etype, msg, etraceback):
350 350
         self._update({"status": consts.TaskStatus.FAILED,
351
-                      "verification_log": json.dumps(log)})
351
+                      "verification_log": json.dumps([etype,
352
+                                                      msg,
353
+                                                      etraceback
354
+                                                      ])})
352 355
 
353 356
     def get_results(self):
354 357
         return db.task_result_get_all_by_uuid(self.task["uuid"])

+ 6
- 4
rally/task/engine.py View File

@@ -188,8 +188,9 @@ class TaskEngine(object):
188 188
         try:
189 189
             self.config = TaskConfig(config)
190 190
         except Exception as e:
191
-            log = [str(type(e)), str(e), json.dumps(traceback.format_exc())]
192
-            task.set_failed(log=log)
191
+            task.set_failed(type(e).__name__,
192
+                            str(e),
193
+                            json.dumps(traceback.format_exc()))
193 194
             raise exceptions.InvalidTaskException(str(e))
194 195
 
195 196
         self.task = task
@@ -285,8 +286,9 @@ class TaskEngine(object):
285 286
             self._validate_config_syntax(self.config)
286 287
             self._validate_config_semantic(self.config)
287 288
         except Exception as e:
288
-            log = [str(type(e)), str(e), json.dumps(traceback.format_exc())]
289
-            self.task.set_failed(log=log)
289
+            self.task.set_failed(type(e).__name__,
290
+                                 str(e),
291
+                                 json.dumps(traceback.format_exc()))
290 292
             raise exceptions.InvalidTaskException(str(e))
291 293
 
292 294
     def _get_runner(self, config):

+ 72
- 35
tests/unit/cli/commands/test_task.py View File

@@ -15,10 +15,12 @@
15 15
 
16 16
 import copy
17 17
 import datetime as dt
18
+import json
18 19
 import os.path
19 20
 
20 21
 import ddt
21 22
 import mock
23
+import yaml
22 24
 
23 25
 from rally.cli.commands import task
24 26
 from rally import consts
@@ -121,13 +123,13 @@ class TaskCommandsTestCase(test.TestCase):
121 123
             actual = self.task._load_task(input_task_file)
122 124
         self.assertEqual(expect, actual)
123 125
 
124
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
126
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
125 127
     @mock.patch("rally.cli.commands.task.api.Task.validate",
126 128
                 return_value=fakes.FakeTask())
127 129
     @mock.patch("rally.cli.commands.task.TaskCommands._load_task",
128 130
                 return_value={"uuid": "some_uuid"})
129 131
     def test__load_and_validate_task(self, mock__load_task,
130
-                                     mock_task_validate, mock_os_path_exists):
132
+                                     mock_task_validate, mock_os_path_isfile):
131 133
         deployment = "some_deployment_uuid"
132 134
         self.task._load_and_validate_task("some_task", "task_args",
133 135
                                           "task_args_file", deployment)
@@ -136,20 +138,17 @@ class TaskCommandsTestCase(test.TestCase):
136 138
         mock_task_validate.assert_called_once_with(
137 139
             deployment, mock__load_task.return_value, None)
138 140
 
139
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
140
-    @mock.patch("rally.cli.commands.task.os.path.isdir", return_value=True)
141
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=False)
141 142
     @mock.patch("rally.cli.commands.task.TaskCommands._load_task")
142 143
     @mock.patch("rally.api.Task.validate")
143
-    def test__load_and_validate_directory(self, mock_task_validate,
144
-                                          mock__load_task, mock_os_path_isdir,
145
-                                          mock_os_path_exists):
144
+    def test__load_and_validate_file(self, mock_task_validate, mock__load_task,
145
+                                     mock_os_path_isfile):
146 146
         deployment = "some_deployment_uuid"
147 147
         self.assertRaises(IOError, self.task._load_and_validate_task,
148 148
                           "some_task", "task_args",
149 149
                           "task_args_file", deployment)
150 150
 
151
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
152
-    @mock.patch("rally.cli.commands.task.os.path.isdir", return_value=False)
151
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
153 152
     @mock.patch("rally.cli.commands.task.api.Task.create",
154 153
                 return_value=fakes.FakeTask(uuid="some_new_uuid", tag="tag"))
155 154
     @mock.patch("rally.cli.commands.task.TaskCommands.use")
@@ -162,7 +161,7 @@ class TaskCommandsTestCase(test.TestCase):
162 161
     @mock.patch("rally.cli.commands.task.api.Task.start")
163 162
     def test_start(self, mock_task_start, mock_task_validate, mock__load_task,
164 163
                    mock_detailed, mock_use, mock_task_create,
165
-                   mock_os_path_isdir, mock_os_path_exists):
164
+                   mock_os_path_isfile):
166 165
         deployment_id = "e0617de9-77d1-4875-9b49-9d5789e29f20"
167 166
         task_path = "path_to_config.json"
168 167
         self.task.start(task_path, deployment_id, do_use=True)
@@ -175,8 +174,7 @@ class TaskCommandsTestCase(test.TestCase):
175 174
         mock_use.assert_called_once_with("some_new_uuid")
176 175
         mock_detailed.assert_called_once_with(task_id="some_new_uuid")
177 176
 
178
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
179
-    @mock.patch("rally.cli.commands.task.os.path.isdir", return_value=False)
177
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
180 178
     @mock.patch("rally.cli.commands.task.api.Task.create",
181 179
                 return_value=fakes.FakeTask(uuid="new_uuid", tag="some_tag"))
182 180
     @mock.patch("rally.cli.commands.task.TaskCommands.detailed")
@@ -187,8 +185,7 @@ class TaskCommandsTestCase(test.TestCase):
187 185
                 return_value=fakes.FakeTask(uuid="some_id"))
188 186
     def test_start_with_task_args(self, mock_task_validate, mock__load_task,
189 187
                                   mock_task_start, mock_detailed,
190
-                                  mock_task_create, mock_os_path_isdir,
191
-                                  mock_os_path_exists):
188
+                                  mock_task_create, mock_os_path_isfile):
192 189
         task_path = mock.MagicMock()
193 190
         task_args = mock.MagicMock()
194 191
         task_args_file = mock.MagicMock()
@@ -211,8 +208,7 @@ class TaskCommandsTestCase(test.TestCase):
211 208
         self.assertRaises(exceptions.InvalidArgumentsException,
212 209
                           self.task.start, "path_to_config.json", None)
213 210
 
214
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
215
-    @mock.patch("rally.cli.commands.task.os.path.isdir", return_value=False)
211
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
216 212
     @mock.patch("rally.cli.commands.task.api.Task.create",
217 213
                 return_value=fakes.FakeTask(temporary=False, tag="tag",
218 214
                                             uuid="uuid"))
@@ -223,7 +219,7 @@ class TaskCommandsTestCase(test.TestCase):
223 219
                 side_effect=exceptions.InvalidTaskException)
224 220
     def test_start_invalid_task(self, mock_task_start, mock_task_validate,
225 221
                                 mock__load_task, mock_task_create,
226
-                                mock_os_path_isdir, mock_os_path_exists):
222
+                                mock_os_path_isfile):
227 223
         result = self.task.start("task_path", "deployment", tag="tag")
228 224
         self.assertEqual(1, result)
229 225
 
@@ -317,23 +313,59 @@ class TaskCommandsTestCase(test.TestCase):
317 313
                                                        extended_results=True)
318 314
         self.task.detailed(test_uuid, iterations_data=True)
319 315
 
316
+    @mock.patch("rally.cli.commands.task.sys.stdout")
320 317
     @mock.patch("rally.cli.commands.task.api.Task")
321 318
     @mock.patch("rally.cli.commands.task.logging")
322
-    def test_detailed_task_failed(self, mock_logging, mock_task):
319
+    @ddt.data({"debug": True},
320
+              {"debug": False})
321
+    @ddt.unpack
322
+    def test_detailed_task_failed(self, mock_logging, mock_task,
323
+                                  mock_stdout, debug):
324
+        test_uuid = "test_task_id"
323 325
         value = {
324 326
             "id": "task",
325
-            "uuid": "task_uuid",
327
+            "uuid": test_uuid,
326 328
             "status": consts.TaskStatus.FAILED,
327 329
             "results": [],
328
-            "verification_log": "['1', '2', '3']"
330
+            "verification_log": json.dumps(["error_type", "error_message",
331
+                                            "error_traceback"])
329 332
         }
330 333
         mock_task.get_detailed = mock.MagicMock(return_value=value)
331 334
 
332
-        mock_logging.is_debug.return_value = False
333
-        self.task.detailed("task_uuid")
335
+        mock_logging.is_debug.return_value = debug
336
+        self.task.detailed(test_uuid)
337
+        verification = yaml.safe_load(value["verification_log"])
338
+        if debug:
339
+            expected_calls = [mock.call("Task test_task_id: failed"),
340
+                              mock.call("%s" % verification[2])]
341
+            mock_stdout.write.assert_has_calls(expected_calls, any_order=True)
342
+        else:
343
+            expected_calls = [mock.call("Task test_task_id: failed"),
344
+                              mock.call("%s" % verification[0]),
345
+                              mock.call("%s" % verification[1]),
346
+                              mock.call("\nFor more details run:\nrally "
347
+                                        "-vd task detailed %s" % test_uuid)]
348
+            mock_stdout.write.assert_has_calls(expected_calls, any_order=True)
334 349
 
335
-        mock_logging.is_debug.return_value = True
336
-        self.task.detailed("task_uuid")
350
+    @mock.patch("rally.cli.commands.task.api.Task")
351
+    @mock.patch("rally.cli.commands.task.sys.stdout")
352
+    def test_detailed_task_status_not_in_finished_abort(self,
353
+                                                        mock_stdout,
354
+                                                        mock_task):
355
+        test_uuid = "test_task_id"
356
+        value = {
357
+            "id": "task",
358
+            "uuid": test_uuid,
359
+            "status": consts.TaskStatus.INIT,
360
+            "results": []
361
+        }
362
+        mock_task.get_detailed = mock.MagicMock(return_value=value)
363
+        self.task.detailed(test_uuid)
364
+        expected_calls = [mock.call("Task test_task_id: init"),
365
+                          mock.call("\nThe task test_task_id marked as "
366
+                                    "'init'. Results available when it "
367
+                                    "is 'finished'.")]
368
+        mock_stdout.write.assert_has_calls(expected_calls, any_order=True)
337 369
 
338 370
     @mock.patch("rally.cli.commands.task.envutils.get_global")
339 371
     def test_detailed_no_task_id(self, mock_get_global):
@@ -694,22 +726,22 @@ class TaskCommandsTestCase(test.TestCase):
694 726
         result = self.task.sla_check(task_id="fake_task_id", tojson=True)
695 727
         self.assertEqual(0, result)
696 728
 
697
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
729
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
698 730
     @mock.patch("rally.api.Task.validate")
699 731
     @mock.patch("rally.cli.commands.task.open",
700 732
                 side_effect=mock.mock_open(read_data="{\"some\": \"json\"}"),
701 733
                 create=True)
702 734
     def test_validate(self, mock_open, mock_task_validate,
703
-                      mock_os_path_exists):
735
+                      mock_os_path_isfile):
704 736
         self.task.validate("path_to_config.json", "fake_id")
705 737
         mock_task_validate.assert_called_once_with("fake_id", {"some": "json"},
706 738
                                                    None)
707 739
 
708
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
740
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
709 741
     @mock.patch("rally.cli.commands.task.TaskCommands._load_task",
710 742
                 side_effect=task.FailedToLoadTask)
711 743
     def test_validate_failed_to_load_task(self, mock__load_task,
712
-                                          mock_os_path_exists):
744
+                                          mock_os_path_isfile):
713 745
         args = mock.MagicMock()
714 746
         args_file = mock.MagicMock()
715 747
 
@@ -719,11 +751,11 @@ class TaskCommandsTestCase(test.TestCase):
719 751
         mock__load_task.assert_called_once_with(
720 752
             "path_to_task", args, args_file)
721 753
 
722
-    @mock.patch("rally.cli.commands.task.os.path.exists", return_value=True)
754
+    @mock.patch("rally.cli.commands.task.os.path.isfile", return_value=True)
723 755
     @mock.patch("rally.cli.commands.task.TaskCommands._load_task")
724 756
     @mock.patch("rally.api.Task.validate")
725 757
     def test_validate_invalid(self, mock_task_validate, mock__load_task,
726
-                              mock_os_path_exists):
758
+                              mock_os_path_isfile):
727 759
         mock_task_validate.side_effect = exceptions.InvalidTaskException
728 760
         result = self.task.validate("path_to_task", "deployment")
729 761
         self.assertEqual(1, result)
@@ -782,10 +814,12 @@ class TaskCommandsTestCase(test.TestCase):
782 814
     @mock.patch("rally.cli.commands.task.api.Task")
783 815
     @ddt.data({"error_type": "test_no_trace_type",
784 816
                "error_message": "no_trace_error_message",
785
-               "error_traceback": None},
817
+               "error_traceback": None,
818
+               },
786 819
               {"error_type": "test_error_type",
787 820
                "error_message": "test_error_message",
788
-               "error_traceback": "test\nerror\ntraceback"})
821
+               "error_traceback": "test\nerror\ntraceback",
822
+               })
789 823
     @ddt.unpack
790 824
     def test_show_task_errors_no_trace(self, mock_task, mock_stdout,
791 825
                                        error_type, error_message,
@@ -798,7 +832,7 @@ class TaskCommandsTestCase(test.TestCase):
798 832
         mock_task.get_detailed.return_value = {
799 833
             "id": "task",
800 834
             "uuid": test_uuid,
801
-            "status": "status",
835
+            "status": "finished",
802 836
             "results": [{
803 837
                 "key": {
804 838
                     "name": "fake_name",
@@ -819,8 +853,11 @@ class TaskCommandsTestCase(test.TestCase):
819 853
                      "atomic_actions": {"foo": 0.6, "bar": 0.7},
820 854
                      "error": error_data
821 855
                      },
822
-                ]}
823
-            ]}
856
+                ]},
857
+            ],
858
+            "verification_log": json.dumps([error_type, error_message,
859
+                                            error_traceback])
860
+        }
824 861
         self.task.detailed(test_uuid)
825 862
         mock_task.get_detailed.assert_called_once_with(test_uuid,
826 863
                                                        extended_results=True)

+ 4
- 2
tests/unit/common/objects/test_task.py View File

@@ -240,10 +240,12 @@ class TaskTestCase(test.TestCase):
240 240
     def test_set_failed(self, mock_task_update):
241 241
         mock_task_update.return_value = self.task
242 242
         task = objects.Task(task=self.task)
243
-        task.set_failed()
243
+        task.set_failed("foo_type", "foo_error_message", "foo_trace")
244 244
         mock_task_update.assert_called_once_with(
245 245
             self.task["uuid"],
246
-            {"status": consts.TaskStatus.FAILED, "verification_log": "\"\""},
246
+            {"status": consts.TaskStatus.FAILED,
247
+             "verification_log": "[\"foo_type\", \"foo_error_message\", "
248
+                                 "\"foo_trace\"]"},
247 249
         )
248 250
 
249 251
     @ddt.data(

Loading…
Cancel
Save