Skip to content

Commit

Permalink
Change: Extend list-tasks with average and last scan duration time.
Browse files Browse the repository at this point in the history
  • Loading branch information
a-h-abdelsalam committed Sep 3, 2024
1 parent 51e3f44 commit 9892b86
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 5 deletions.
27 changes: 22 additions & 5 deletions scripts/list-tasks.gmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")

Check warning on line 56 in scripts/list-tasks.gmp.py

View check run for this annotation

Codecov / codecov/patch

scripts/list-tasks.gmp.py#L56

Added line #L56 was not covered by tests

list_tasks(gmp, args)

Check warning on line 58 in scripts/list-tasks.gmp.py

View check run for this annotation

Codecov / codecov/patch

scripts/list-tasks.gmp.py#L58

Added line #L58 was not covered by tests

if __name__ == "__gmp__":
main(gmp, args)
119 changes: 119 additions & 0 deletions tests/scripts/test_list_tasks.py
Original file line number Diff line number Diff line change
@@ -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<http://www.gnu.org/licenses/>


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",
'<get_tasks_response status="200" status_text="OK">'
'<task id="7f32c1f4-c5c8-4035-b4fb-5b1e220bc052">'
"<name>Test</name>"
"<average_duration>4115</average_duration>"
"<last_report>"
'<report id="cbf8c159-d3f8-4adf-b17d-fd53abf20e52">'
"<scan_start>2024-03-12T16:35:01-07:00</scan_start>"
"<scan_end>2024-03-12T17:43:36-07:00</scan_end>"
"</report>"
"</last_report>"
"</task>"
"</get_tasks_response>",
)
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",
'<get_tasks_response status="200" status_text="OK">'
'<task id="7f32c1f4-c5c8-4035-b4fb-5b1e220bc052">'
"<name>Test</name>"
"<average_duration>14444</average_duration>"
"<last_report>"
'<report id="cbf8c159-d3f8-4adf-b17d-fd53abf20e52">'
"<scan_start>2024-09-27T14:30:01Z</scan_start>"
"<scan_end>2024-09-27T18:30:45Z</scan_end>"
"</report>"
"</last_report>"
"</task>"
"</get_tasks_response>",
)
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",
'<get_tasks_response status="200" status_text="OK">'
'<task id="7f32c1f4-c5c8-4035-b4fb-5b1e220bc052">'
"<name>Test</name>"
"<average_duration>0</average_duration>"
"</task>"
"</get_tasks_response>",
)
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], "")

0 comments on commit 9892b86

Please sign in to comment.