diff --git a/scripts/list-tasks.gmp.py b/scripts/list-tasks.gmp.py
index 1c9c9cb4..5e66cd76 100644
--- a/scripts/list-tasks.gmp.py
+++ b/scripts/list-tasks.gmp.py
@@ -6,21 +6,20 @@
from gvm.protocols.gmp import Gmp
from gvmtools.helper import Table
+from datetime import datetime
-def main(gmp: Gmp, args: Namespace) -> None:
+def list_tasks(gmp: Gmp, args: Namespace) -> None:
# pylint: disable=unused-argument
response_xml = gmp.get_tasks(details=True, filter_string="rows=-1")
tasks_xml = response_xml.xpath("task")
- heading = ["#", "Name", "Id", "Target", "Scanner", "Scan Order", "Severity"]
+ heading = ["#", "Name", "Id", "Target", "Scanner", "Scan Order", "Severity", "Average Duration", "Last Scan Duration (hours)"]
rows = []
numberRows = 0
- print("Listing tasks.\n")
-
for task in tasks_xml:
# Count number of reports
numberRows = numberRows + 1
@@ -33,12 +32,30 @@ def main(gmp: Gmp, args: Namespace) -> None:
scanner = "".join(task.xpath("scanner/name/text()"))
severity = "".join(task.xpath("last_report/report/severity/text()"))
order = "".join(task.xpath("hosts_ordering/text()"))
+ average_duration = "".join(task.xpath("average_duration/text()"))
+ average_duration_int = 0 if not average_duration else int(average_duration)
+ average_duration_hours = f"{average_duration_int / 3600:.2f}"
+ scan_start_iso = "".join(task.xpath("last_report/report/scan_start/text()"))
+ scan_end_iso = "".join(task.xpath("last_report/report/scan_end/text()"))
+ if not scan_start_iso or not scan_end_iso:
+ duration_hours = ""
+ else:
+ scan_start_time = datetime.fromisoformat(scan_start_iso.replace('Z', '+00:00'))
+ scan_end_time = datetime.fromisoformat(scan_end_iso.replace('Z', '+00:00'))
+ duration = scan_end_time - scan_start_time
+ duration_hours = f"{duration.total_seconds() / 3600:.2f}"
rows.append(
- [rowNumber, name, task_id, targetname, scanner, order, severity]
+ [rowNumber, name, task_id, targetname, scanner, order, severity, average_duration_hours, duration_hours]
)
print(Table(heading=heading, rows=rows))
+def main(gmp: Gmp, args: Namespace) -> None:
+
+ print("Listing tasks.\n")
+
+ list_tasks(gmp, args)
+
if __name__ == "__gmp__":
main(gmp, args)
diff --git a/tests/scripts/test_list_tasks.py b/tests/scripts/test_list_tasks.py
new file mode 100644
index 00000000..0b145db6
--- /dev/null
+++ b/tests/scripts/test_list_tasks.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# SPDX-FileCopyrightText: 2020-2024 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+
+
+import unittest
+from argparse import Namespace
+from pathlib import Path
+from unittest.mock import patch
+
+from gvmtools.helper import Table
+
+from . import GmpMockFactory, load_script
+
+CWD = Path(__file__).absolute().parent
+
+
+class ListTasksTestCase(unittest.TestCase):
+ def setUp(self):
+ self.list_tasks = load_script(
+ (CWD.parent.parent / "scripts"), "list-tasks"
+ )
+
+ @patch("gvm.protocols.latest.Gmp", new_callable=GmpMockFactory)
+ @patch("builtins.print")
+ def test_duration_with_timezone_offset(
+ self, mock_print, mock_gmp: GmpMockFactory
+ ):
+ args = Namespace(script=["foo"])
+
+ mock_gmp.mock_response(
+ "get_tasks",
+ ''
+ ''
+ "Test"
+ "4115"
+ ""
+ ''
+ "2024-03-12T16:35:01-07:00"
+ "2024-03-12T17:43:36-07:00"
+ ""
+ ""
+ ""
+ "",
+ )
+ self.list_tasks.list_tasks(mock_gmp.gmp_protocol, args)
+
+ table = mock_print.call_args[0][0]
+ self.assertIsInstance(table, Table)
+ self.assertEqual(len(table.rows), 1)
+ self.assertEqual(table.rows[0][1], "Test")
+ self.assertEqual(table.rows[0][7], "1.14")
+ self.assertEqual(table.rows[0][8], "1.14")
+
+ @patch("gvm.protocols.latest.Gmp", new_callable=GmpMockFactory)
+ @patch("builtins.print")
+ def test_duration_in_UTC(self, mock_print, mock_gmp: GmpMockFactory):
+ args = Namespace(script=["foo"])
+
+ mock_gmp.mock_response(
+ "get_tasks",
+ ''
+ ''
+ "Test"
+ "14444"
+ ""
+ ''
+ "2024-09-27T14:30:01Z"
+ "2024-09-27T18:30:45Z"
+ ""
+ ""
+ ""
+ "",
+ )
+ self.list_tasks.list_tasks(mock_gmp.gmp_protocol, args)
+
+ table = mock_print.call_args[0][0]
+ self.assertIsInstance(table, Table)
+ self.assertEqual(len(table.rows), 1)
+ self.assertEqual(table.rows[0][1], "Test")
+ self.assertEqual(table.rows[0][7], "4.01")
+ self.assertEqual(table.rows[0][8], "4.01")
+
+ @patch("gvm.protocols.latest.Gmp", new_callable=GmpMockFactory)
+ @patch("builtins.print")
+ def test_missing_last_report(self, mock_print, mock_gmp: GmpMockFactory):
+ args = Namespace(script=["foo"])
+
+ mock_gmp.mock_response(
+ "get_tasks",
+ ''
+ ''
+ "Test"
+ "0"
+ ""
+ "",
+ )
+ self.list_tasks.list_tasks(mock_gmp.gmp_protocol, args)
+
+ table = mock_print.call_args[0][0]
+ self.assertIsInstance(table, Table)
+ self.assertEqual(len(table.rows), 1)
+ self.assertEqual(table.rows[0][1], "Test")
+ self.assertEqual(table.rows[0][7], "0.00")
+ self.assertEqual(table.rows[0][8], "")