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], "")