From b41f0991a9a74fac89bc869ee1340bcfb4ef31fd Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 15 Oct 2024 12:01:30 +0200 Subject: [PATCH 01/20] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b5122..d1dcf80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ For unreleased changes see [WHATSNEW.md](WHATSNEW.md) ### Added -- FortiManager Methods for handling addresses and services (get and delete) +- FortiManager methods for handling addresses and services (get and delete) ### Changed From 9fc4c7156707e801f5002418b939e614b2ecc662 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 15 Oct 2024 15:40:46 +0200 Subject: [PATCH 02/20] =?UTF-8?q?=E2=9C=A8=20Add=20new=20CLI=20commands=20?= =?UTF-8?q?to=20get=20cmdb=20configuration=20from=20a=20FortiGate.=20Busin?= =?UTF-8?q?ess=20Logic=20not=20yet=20implemented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/__init__.py | 4 + fotoobo/cli/ems/__init__.py | 4 + fotoobo/cli/ems/{main.py => ems.py} | 4 +- fotoobo/cli/ems/{get_commands.py => get.py} | 0 .../ems/{monitor_commands.py => monitor.py} | 0 fotoobo/cli/faz/__init__.py | 4 + fotoobo/cli/faz/{main.py => faz.py} | 3 +- fotoobo/cli/faz/{get_commands.py => get.py} | 0 fotoobo/cli/fgt/__init__.py | 4 + .../cli/fgt/{config_commands.py => config.py} | 0 fotoobo/cli/fgt/{main.py => fgt.py} | 17 ++-- fotoobo/cli/fgt/get/__init__.py | 7 ++ fotoobo/cli/fgt/get/cmdb/__init__.py | 7 ++ fotoobo/cli/fgt/get/cmdb/cmdb.py | 29 +++++++ fotoobo/cli/fgt/get/cmdb/firewall/__init__.py | 7 ++ fotoobo/cli/fgt/get/cmdb/firewall/firewall.py | 82 +++++++++++++++++++ .../fgt/get/cmdb/firewall/service/__init__.py | 7 ++ .../fgt/get/cmdb/firewall/service/service.py | 77 +++++++++++++++++ .../cli/fgt/{get_commands.py => get/get.py} | 7 +- .../fgt/{monitor_commands.py => monitor.py} | 0 fotoobo/cli/fmg/__init__.py | 4 + fotoobo/cli/fmg/{main.py => fmg.py} | 3 +- fotoobo/cli/fmg/{get_commands.py => get.py} | 0 fotoobo/cli/main.py | 11 +-- fotoobo/helpers/cli.py | 2 +- tests/cli/fgt/test_cli_fgt_get.py | 11 ++- tests/cli/fgt/test_cli_fgt_get_cmdb.py | 29 +++++++ .../cli/fgt/test_cli_fgt_get_cmdb_firewall.py | 55 +++++++++++++ .../test_cli_fgt_get_cmdb_firewall_service.py | 59 +++++++++++++ 29 files changed, 417 insertions(+), 20 deletions(-) rename fotoobo/cli/ems/{main.py => ems.py} (86%) rename fotoobo/cli/ems/{get_commands.py => get.py} (100%) rename fotoobo/cli/ems/{monitor_commands.py => monitor.py} (100%) rename fotoobo/cli/faz/{main.py => faz.py} (92%) rename fotoobo/cli/faz/{get_commands.py => get.py} (100%) rename fotoobo/cli/fgt/{config_commands.py => config.py} (100%) rename fotoobo/cli/fgt/{main.py => fgt.py} (95%) create mode 100644 fotoobo/cli/fgt/get/__init__.py create mode 100644 fotoobo/cli/fgt/get/cmdb/__init__.py create mode 100644 fotoobo/cli/fgt/get/cmdb/cmdb.py create mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/__init__.py create mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/firewall.py create mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py create mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/service/service.py rename fotoobo/cli/fgt/{get_commands.py => get/get.py} (90%) rename fotoobo/cli/fgt/{monitor_commands.py => monitor.py} (100%) rename fotoobo/cli/fmg/{main.py => fmg.py} (98%) rename fotoobo/cli/fmg/{get_commands.py => get.py} (100%) create mode 100644 tests/cli/fgt/test_cli_fgt_get_cmdb.py create mode 100644 tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py create mode 100644 tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py diff --git a/fotoobo/cli/__init__.py b/fotoobo/cli/__init__.py index c58cff1..d7f84a3 100644 --- a/fotoobo/cli/__init__.py +++ b/fotoobo/cli/__init__.py @@ -4,3 +4,7 @@ Do not add too much logic into the cli commands. Just add the tools module and add the logic there. Therefore, we can segregate the duties. """ + +from . import convert, get + +__all__ = ["convert", "get"] diff --git a/fotoobo/cli/ems/__init__.py b/fotoobo/cli/ems/__init__.py index ae73455..40e3c2f 100644 --- a/fotoobo/cli/ems/__init__.py +++ b/fotoobo/cli/ems/__init__.py @@ -1,3 +1,7 @@ """ __init__.py """ + +from . import ems, get, monitor + +__all__ = ["ems", "get", "monitor"] diff --git a/fotoobo/cli/ems/main.py b/fotoobo/cli/ems/ems.py similarity index 86% rename from fotoobo/cli/ems/main.py rename to fotoobo/cli/ems/ems.py index 31e671e..a6fbeb6 100644 --- a/fotoobo/cli/ems/main.py +++ b/fotoobo/cli/ems/ems.py @@ -6,10 +6,10 @@ import typer -from fotoobo.cli.ems import get_commands as get -from fotoobo.cli.ems import monitor_commands as monitor from fotoobo.helpers import cli_path +from . import get, monitor + app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") diff --git a/fotoobo/cli/ems/get_commands.py b/fotoobo/cli/ems/get.py similarity index 100% rename from fotoobo/cli/ems/get_commands.py rename to fotoobo/cli/ems/get.py diff --git a/fotoobo/cli/ems/monitor_commands.py b/fotoobo/cli/ems/monitor.py similarity index 100% rename from fotoobo/cli/ems/monitor_commands.py rename to fotoobo/cli/ems/monitor.py diff --git a/fotoobo/cli/faz/__init__.py b/fotoobo/cli/faz/__init__.py index ae73455..bef10a0 100644 --- a/fotoobo/cli/faz/__init__.py +++ b/fotoobo/cli/faz/__init__.py @@ -1,3 +1,7 @@ """ __init__.py """ + +from . import faz + +__all__ = ["faz"] diff --git a/fotoobo/cli/faz/main.py b/fotoobo/cli/faz/faz.py similarity index 92% rename from fotoobo/cli/faz/main.py rename to fotoobo/cli/faz/faz.py index fa9f989..054e9a5 100644 --- a/fotoobo/cli/faz/main.py +++ b/fotoobo/cli/faz/faz.py @@ -6,9 +6,10 @@ import typer -from fotoobo.cli.faz import get_commands as get from fotoobo.helpers import cli_path +from . import get + app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") diff --git a/fotoobo/cli/faz/get_commands.py b/fotoobo/cli/faz/get.py similarity index 100% rename from fotoobo/cli/faz/get_commands.py rename to fotoobo/cli/faz/get.py diff --git a/fotoobo/cli/fgt/__init__.py b/fotoobo/cli/fgt/__init__.py index ae73455..3d915e5 100644 --- a/fotoobo/cli/fgt/__init__.py +++ b/fotoobo/cli/fgt/__init__.py @@ -1,3 +1,7 @@ """ __init__.py """ + +from . import fgt + +__all__ = ["fgt"] diff --git a/fotoobo/cli/fgt/config_commands.py b/fotoobo/cli/fgt/config.py similarity index 100% rename from fotoobo/cli/fgt/config_commands.py rename to fotoobo/cli/fgt/config.py diff --git a/fotoobo/cli/fgt/main.py b/fotoobo/cli/fgt/fgt.py similarity index 95% rename from fotoobo/cli/fgt/main.py rename to fotoobo/cli/fgt/fgt.py index 20ecfa4..8eba681 100644 --- a/fotoobo/cli/fgt/main.py +++ b/fotoobo/cli/fgt/fgt.py @@ -10,19 +10,25 @@ import typer from fotoobo import tools -from fotoobo.cli.fgt import config_commands as config -from fotoobo.cli.fgt import get_commands as get -from fotoobo.cli.fgt import monitor_commands as monitor from fotoobo.exceptions import GeneralWarning from fotoobo.helpers import cli_path from fotoobo.helpers.config import config as fotoobo_config from fotoobo.helpers.files import create_dir, file_to_ftp, file_to_zip from fotoobo.inventory import Inventory +from . import config, monitor +from .get import get + app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") +# app.add_typer(get_commands.app, name="get", help="FortiGate get commands.") +app.add_typer(config.app, name="config", help="FortiGate config file commands.") +app.add_typer(get.app, name="get", help="FortiGate get commands.") +app.add_typer(monitor.app, name="monitor", help="FortiGate monitor commands.") + + @app.callback() def callback(context: typer.Context) -> None: """ @@ -35,11 +41,6 @@ def callback(context: typer.Context) -> None: log.debug("About to execute command: '%s'", context.invoked_subcommand) -app.add_typer(get.app, name="get", help="FortiGate get commands.") -app.add_typer(monitor.app, name="monitor", help="FortiGate monitor commands.") -app.add_typer(config.app, name="config", help="FortiGate config file commands.") - - @app.command() def backup( host: str = typer.Argument( diff --git a/fotoobo/cli/fgt/get/__init__.py b/fotoobo/cli/fgt/get/__init__.py new file mode 100644 index 0000000..6feabb3 --- /dev/null +++ b/fotoobo/cli/fgt/get/__init__.py @@ -0,0 +1,7 @@ +""" +__init__.py +""" + +from . import get + +__all__ = ["get"] diff --git a/fotoobo/cli/fgt/get/cmdb/__init__.py b/fotoobo/cli/fgt/get/cmdb/__init__.py new file mode 100644 index 0000000..ec55029 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/__init__.py @@ -0,0 +1,7 @@ +""" +__init__.py +""" + +from . import cmdb + +__all__ = ["cmdb"] diff --git a/fotoobo/cli/fgt/get/cmdb/cmdb.py b/fotoobo/cli/fgt/get/cmdb/cmdb.py new file mode 100644 index 0000000..0147315 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/cmdb.py @@ -0,0 +1,29 @@ +""" +The FortiGate commands +""" + +import logging + +import typer + +from fotoobo.helpers import cli_path + +from .firewall import firewall + +app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") +log = logging.getLogger("fotoobo") + + +app.add_typer(firewall.app, name="firewall", help="FortiGate get cmdb firewall commands.") + + +@app.callback() +def callback(context: typer.Context) -> None: + """ + The fgt get cmdb subcommand callback + + Args: + context: The context object of the typer app + """ + cli_path.append(str(context.invoked_subcommand)) + log.debug("About to execute command: '%s'", context.invoked_subcommand) diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/__init__.py b/fotoobo/cli/fgt/get/cmdb/firewall/__init__.py new file mode 100644 index 0000000..4939503 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/firewall/__init__.py @@ -0,0 +1,7 @@ +""" +__init__.py +""" + +from . import firewall + +__all__ = ["firewall"] diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py new file mode 100644 index 0000000..dc2dc85 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py @@ -0,0 +1,82 @@ +""" +The FortiGate commands +""" + +# pylint: disable=anomalous-backslash-in-string +import logging + +import typer + +from fotoobo.helpers import cli_path + +from .service import service + +app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") +log = logging.getLogger("fotoobo") + + +app.add_typer(service.app, name="service", help="FortiGate get cmdb firewall service commands.") + + +@app.callback() +def callback(context: typer.Context) -> None: + """ + The fgt get cmdb get firewall subcommand callback + + Args: + context: The context object of the typer app + """ + cli_path.append(str(context.invoked_subcommand)) + log.debug("About to execute command: '%s'", context.invoked_subcommand) + + +@app.command() +def address( + name: str = typer.Argument( + "", + help="The firewall address object to get \[default: ]", + show_default=False, + metavar="[name]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """ + Get FortiGate cmdb firewall address. + + The FortiGate api endpoint is: /cmdb/firewall/address + """ + # TODO: Here to add the address cli code + print("ADDRESS") + + +@app.command() +def addrgrp( + name: str = typer.Argument( + "", + help="The firewall address group object to get \[default: ]", + show_default=False, + metavar="[name]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """ + Get FortiGate cmdb firewall address group. + + The FortiGate api endpoint is: /cmdb/firewall/addrgrp + """ + # TODO: Here to add the address group cli code + print("ADDRGRP") diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py b/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py new file mode 100644 index 0000000..8cb1b11 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py @@ -0,0 +1,7 @@ +""" +__init__.py +""" + +from . import service + +__all__ = ["service"] diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py b/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py new file mode 100644 index 0000000..bb62809 --- /dev/null +++ b/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py @@ -0,0 +1,77 @@ +""" +The FortiGate commands +""" + +# pylint: disable=anomalous-backslash-in-string +import logging + +import typer + +from fotoobo.helpers import cli_path + +app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") +log = logging.getLogger("fotoobo") + + +@app.callback() +def callback(context: typer.Context) -> None: + """ + The fgt get cmdb get firewall service subcommand callback + + Args: + context: The context object of the typer app + """ + cli_path.append(str(context.invoked_subcommand)) + log.debug("About to execute command: '%s'", context.invoked_subcommand) + + +@app.command() +def custom( + name: str = typer.Argument( + "", + help="The firewall address object to get \[default: ]", + show_default=False, + metavar="[name]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """ + Get FortiGate cmdb firewall service custom. + + The FortiGate api endpoint is: /cmdb/firewall.service/custom + """ + # TODO: Here to add the service custom cli code + print("SERVICE CUSTOM") + + +@app.command() +def group( + name: str = typer.Argument( + "", + help="The firewall address object to get \[default: ]", + show_default=False, + metavar="[name]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """ + Get FortiGate cmdb firewall service group. + + The FortiGate api endpoint is: /cmdb/firewall.service/group + """ + # TODO: Here to add the service group cli code + print("SERVICE GROUP") diff --git a/fotoobo/cli/fgt/get_commands.py b/fotoobo/cli/fgt/get/get.py similarity index 90% rename from fotoobo/cli/fgt/get_commands.py rename to fotoobo/cli/fgt/get/get.py index d55e0ff..e25a6df 100644 --- a/fotoobo/cli/fgt/get_commands.py +++ b/fotoobo/cli/fgt/get/get.py @@ -1,5 +1,5 @@ """ -The FortiGate get commands +The FortiGate commands """ # pylint: disable=anomalous-backslash-in-string @@ -10,10 +10,15 @@ from fotoobo.helpers import cli_path from fotoobo.tools import fgt +from .cmdb import cmdb + app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") +app.add_typer(cmdb.app, name="cmdb", help="FortiGate get cmdb commands.") + + @app.callback() def callback(context: typer.Context) -> None: """ diff --git a/fotoobo/cli/fgt/monitor_commands.py b/fotoobo/cli/fgt/monitor.py similarity index 100% rename from fotoobo/cli/fgt/monitor_commands.py rename to fotoobo/cli/fgt/monitor.py diff --git a/fotoobo/cli/fmg/__init__.py b/fotoobo/cli/fmg/__init__.py index ae73455..0af5377 100644 --- a/fotoobo/cli/fmg/__init__.py +++ b/fotoobo/cli/fmg/__init__.py @@ -1,3 +1,7 @@ """ __init__.py """ + +from . import fmg + +__all__ = ["fmg"] diff --git a/fotoobo/cli/fmg/main.py b/fotoobo/cli/fmg/fmg.py similarity index 98% rename from fotoobo/cli/fmg/main.py rename to fotoobo/cli/fmg/fmg.py index 4bfd2c4..5f9f1a1 100644 --- a/fotoobo/cli/fmg/main.py +++ b/fotoobo/cli/fmg/fmg.py @@ -7,12 +7,13 @@ import typer -from fotoobo.cli.fmg import get_commands as get from fotoobo.helpers import cli_path from fotoobo.helpers.config import config as fotoobo_config from fotoobo.inventory import Inventory from fotoobo.tools import fmg +from . import get + app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") diff --git a/fotoobo/cli/fmg/get_commands.py b/fotoobo/cli/fmg/get.py similarity index 100% rename from fotoobo/cli/fmg/get_commands.py rename to fotoobo/cli/fmg/get.py diff --git a/fotoobo/cli/main.py b/fotoobo/cli/main.py index 62ef0ae..bf7b4aa 100644 --- a/fotoobo/cli/main.py +++ b/fotoobo/cli/main.py @@ -17,16 +17,17 @@ import typer from fotoobo import tools -from fotoobo.cli import convert, get -from fotoobo.cli.ems import main as ems -from fotoobo.cli.faz import main as faz -from fotoobo.cli.fgt import main as fgt -from fotoobo.cli.fmg import main as fmg from fotoobo.helpers import cli_path from fotoobo.helpers.config import config from fotoobo.helpers.log import Log from fotoobo.helpers.output import print_logo +from . import convert, get +from .ems import ems +from .faz import faz +from .fgt import fgt +from .fmg import fmg + app = typer.Typer( context_settings={"help_option_names": ["-h", "--help"]}, no_args_is_help=True, diff --git a/fotoobo/helpers/cli.py b/fotoobo/helpers/cli.py index 99defca..92ce8d1 100644 --- a/fotoobo/helpers/cli.py +++ b/fotoobo/helpers/cli.py @@ -31,7 +31,7 @@ def walk_cli_info( text = Text(overflow="ellipsis", no_wrap=True) text.append(commands[command]["name"], style="bold cyan") - text.append(" " * max((20 - len(command_path) * 4 - len(command)), 2)) + text.append(" " * max((25 - len(command_path) * 4 - len(command)), 2)) text.append(commands[command]["help"].split("\n")[0], style="grey53") branch = tree.add(text) diff --git a/tests/cli/fgt/test_cli_fgt_get.py b/tests/cli/fgt/test_cli_fgt_get.py index 241d1d6..d8f650e 100644 --- a/tests/cli/fgt/test_cli_fgt_get.py +++ b/tests/cli/fgt/test_cli_fgt_get.py @@ -20,7 +20,16 @@ def test_cli_app_fgt_get_help() -> None: arguments, options, commands = parse_help_output(result.stdout) assert not arguments assert options == {"-h", "--help"} - assert set(commands) == {"version"} + assert set(commands) == {"cmdb", "version"} + + +def test_cli_app_fgt_get_no_args() -> None: + """Test fgt get with no arguments""" + result = runner.invoke(app, ["-c", "tests/fotoobo.yaml", "fgt", "get"]) + assert result.exit_code == 0 + assert "Usage: callback fgt get [OPTIONS] COMMAND" in result.stdout + assert "--help" in result.stdout + assert "FortiGate get cmdb commands." in result.stdout def test_cli_app_fgt_get_version_help() -> None: diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb.py b/tests/cli/fgt/test_cli_fgt_get_cmdb.py new file mode 100644 index 0000000..c89ade3 --- /dev/null +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb.py @@ -0,0 +1,29 @@ +""" +Testing the cli fgt get cmdb +""" + +from typer.testing import CliRunner + +from fotoobo.cli.main import app +from tests.helper import parse_help_output + +runner = CliRunner() + + +def test_cli_app_fgt_get_cmdb_help() -> None: + """Test cli help for fgt get cmdb""" + result = runner.invoke(app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "-h"]) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert not arguments + assert options == {"-h", "--help"} + assert set(commands) == {"firewall"} + + +def test_cli_app_fgt_get_cmdb_no_args() -> None: + """Test fgt get cmdb with no arguments""" + result = runner.invoke(app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb"]) + assert result.exit_code == 0 + assert "Usage: callback fgt get cmdb [OPTIONS] COMMAND" in result.stdout + assert "--help" in result.stdout + assert "FortiGate get cmdb firewall commands." in result.stdout diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py new file mode 100644 index 0000000..476ff1a --- /dev/null +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py @@ -0,0 +1,55 @@ +""" +Testing the cli fgt get cmdb firewall +""" + +from typer.testing import CliRunner + +from fotoobo.cli.main import app +from tests.helper import parse_help_output + +runner = CliRunner() + + +def test_cli_app_fgt_get_cmdb_firewall_help() -> None: + """Test cli help for fgt get cmdb firewall""" + result = runner.invoke( + app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "-h"] + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert not arguments + assert options == {"-h", "--help"} + assert set(commands) == {"address", "addrgrp", "service"} + + +def test_cli_app_fgt_get_cmdb_firewall_no_args() -> None: + """Test fgt get cmdb firewall with no arguments""" + result = runner.invoke(app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall"]) + assert result.exit_code == 0 + assert "Usage: callback fgt get cmdb firewall [OPTIONS] COMMAND" in result.stdout + assert "--help" in result.stdout + assert "FortiGate get cmdb firewall service commands." in result.stdout + + +def test_cli_app_fgt_get_cmdb_firewall_address_help() -> None: + """Test cli help for fgt get cmdb firewall address""" + result = runner.invoke( + app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "address", "-h"] + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"name"} + assert options == {"-h", "--help", "-o", "--output"} + assert not commands + + +def test_cli_app_fgt_get_cmdb_firewall_addrgrp_help() -> None: + """Test cli help for fgt get cmdb firewall address group""" + result = runner.invoke( + app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "addrgrp", "-h"] + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"name"} + assert options == {"-h", "--help", "-o", "--output"} + assert not commands diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py new file mode 100644 index 0000000..99b3200 --- /dev/null +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py @@ -0,0 +1,59 @@ +""" +Testing the cli fgt get cmdb firewall service +""" + +from typer.testing import CliRunner + +from fotoobo.cli.main import app +from tests.helper import parse_help_output + +runner = CliRunner() + + +def test_cli_app_fgt_get_cmdb_firewall_service_help() -> None: + """Test cli help for fgt get cmdb firewall service""" + result = runner.invoke( + app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "-h"] + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert not arguments + assert options == {"-h", "--help"} + assert set(commands) == {"custom", "group"} + + +def test_cli_app_fgt_get_cmdb_firewall_service_no_args() -> None: + """Test fgt get cmdb firewall service with no arguments""" + result = runner.invoke( + app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service"] + ) + assert result.exit_code == 0 + assert "Usage: callback fgt get cmdb firewall service [OPTIONS] COMMAND" in result.stdout + assert "--help" in result.stdout + assert "Get FortiGate cmdb firewall service custom." in result.stdout + + +def test_cli_app_fgt_get_cmdb_firewall_service_custom_help() -> None: + """Test cli help for fgt get cmdb firewall service custom""" + result = runner.invoke( + app, + ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "custom", "-h"], + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"name"} + assert options == {"-h", "--help", "-o", "--output"} + assert not commands + + +def test_cli_app_fgt_get_cmdb_firewall_service_group_help() -> None: + """Test cli help for fgt get cmdb firewall service group""" + result = runner.invoke( + app, + ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "group", "-h"], + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"name"} + assert options == {"-h", "--help", "-o", "--output"} + assert not commands From 1dc54fce6745457d4fccc1d6c849c7d3d0cc4a99 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 15 Oct 2024 15:44:51 +0200 Subject: [PATCH 03/20] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Missing=20dot=20(.)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fgt/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fotoobo/cli/fgt/config.py b/fotoobo/cli/fgt/config.py index 47f5dfd..11ea352 100644 --- a/fotoobo/cli/fgt/config.py +++ b/fotoobo/cli/fgt/config.py @@ -70,7 +70,7 @@ def get( ), path: str = typer.Argument("/", help="Configuration path", metavar="[path]"), ) -> None: - """Get configuration or parts of it from one or more FortiGate configuration files""" + """Get configuration or parts of it from one or more FortiGate configuration files.""" result = fgt.config.get(configuration, scope, path) result.print_raw() From beb2b7a2a1472545e4b8361ee905389a81aaefc3 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 15 Oct 2024 15:47:04 +0200 Subject: [PATCH 04/20] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20No=20need=20to=20wri?= =?UTF-8?q?te=20ot=20capitalized?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fmg/fmg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fotoobo/cli/fmg/fmg.py b/fotoobo/cli/fmg/fmg.py index 5f9f1a1..552a142 100644 --- a/fotoobo/cli/fmg/fmg.py +++ b/fotoobo/cli/fmg/fmg.py @@ -101,7 +101,7 @@ def post( ), ) -> None: """ - POST any valid JSON request to the FortiManager. + Post any valid JSON request to the FortiManager. Configure the FortiManager with any valid API call(s) given within the JSON file. """ From 5a17a625ea5bf6506515f98bc078496991b852b9 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Wed, 16 Oct 2024 15:00:02 +0200 Subject: [PATCH 05/20] =?UTF-8?q?=F0=9F=91=94=20Add=20API=20endpoint=20for?= =?UTF-8?q?=20'fgt=20get=20cmdb=20firewall=20address'.=20Tests=20not=20yet?= =?UTF-8?q?=20implemented.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fgt/get/cmdb/firewall/firewall.py | 67 ++++++++++++++++++- fotoobo/helpers/files.py | 20 +++++- fotoobo/helpers/result.py | 35 ++++++++++ fotoobo/tools/fgt/get.py | 29 +++++++- .../cli/fgt/test_cli_fgt_get_cmdb_firewall.py | 4 +- 5 files changed, 149 insertions(+), 6 deletions(-) diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py index dc2dc85..7ea0334 100644 --- a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py +++ b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py @@ -4,10 +4,13 @@ # pylint: disable=anomalous-backslash-in-string import logging +from pathlib import Path import typer +from fotoobo.exceptions import GeneralError from fotoobo.helpers import cli_path +from fotoobo.tools.fgt.get import api from .service import service @@ -32,12 +35,26 @@ def callback(context: typer.Context) -> None: @app.command() def address( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), name: str = typer.Argument( "", help="The firewall address object to get \[default: ]", show_default=False, metavar="[name]", ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), output_file: str = typer.Option( None, "--output", @@ -52,8 +69,54 @@ def address( The FortiGate api endpoint is: /cmdb/firewall/address """ - # TODO: Here to add the address cli code - print("ADDRESS") + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") + + result = api(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") + + if output_file: + result.save_raw(file=Path(output_file), key=host) + + else: + assets = [] + if result.get_result(host): + for vd in result.get_result(host): + for asset in vd["results"]: + + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "type": asset["type"], + } + + if asset["type"] == "fqdn": + data["content"] = asset["fqdn"] + + elif asset["type"] == "geography": + data["content"] = asset["country"] + + elif asset["type"] == "ipmask": + data["content"] = "/".join( + [asset["subnet"].split(" ")[0], asset["subnet"].split(" ")[1]] + ) + + elif asset["type"] == "iprange": + data["content"] = " - ".join([asset["start-ip"], asset["end-ip"]]) + + else: + data["content"] = "" + + assets.append(data) + + result.push_result(host, assets) + + if result.results[host]: + result.print_table_raw( + result.results[host], headers=["name", "vdom", "type", "content"], title=host + ) + + else: + print("No data found") @app.command() diff --git a/fotoobo/helpers/files.py b/fotoobo/helpers/files.py index 5ca581c..c2a1dd7 100644 --- a/fotoobo/helpers/files.py +++ b/fotoobo/helpers/files.py @@ -158,7 +158,7 @@ def save_json_file(json_file: Path, data: Union[List[Any], Dict[Any, Any]]) -> b json_file: The file to write the data into data: The data to save - Returns: + Returns: True if data was valid """ status = True @@ -172,6 +172,24 @@ def save_json_file(json_file: Path, data: Union[List[Any], Dict[Any, Any]]) -> b return status +def save_txt_file(file: Path, data: str) -> bool: + """ + Saves the content of any data object to a text file. + + Args: + file: The file to write the data into + data: The data to save + + Returns: + True if data was valid + """ + status = True + with file.open("w", encoding="UTF-8") as out_file: + out_file.write(data) + + return status + + def save_yaml_file(yaml_file: Path, data: Union[List[Any], Dict[str, Any]]) -> bool: """ Saves the content of a list or dict to a yaml file. diff --git a/fotoobo/helpers/result.py b/fotoobo/helpers/result.py index 79405ed..8c5ce5e 100644 --- a/fotoobo/helpers/result.py +++ b/fotoobo/helpers/result.py @@ -3,6 +3,7 @@ """ import json +import logging import re import smtplib from pathlib import Path @@ -16,7 +17,9 @@ from fotoobo.exceptions import GeneralWarning from fotoobo.helpers import cli_path +from fotoobo.helpers.files import save_json_file, save_txt_file +log = logging.getLogger("fotoobo") ftb_theme = Theme({"var": "white", "ftb": "#FF33BB bold", "chk": "green"}) T = TypeVar("T") @@ -30,6 +33,8 @@ class Result(Generic[T]): It can then be rendered to some command line output (CLI) or JSON response (REST API). """ + OUTPUT_FORMAT_MAPPING = {".json": "json", ".txt": "text"} + def __init__(self) -> None: """ Create the FotooboResult object @@ -62,6 +67,7 @@ def push_result(self, key: str, data: T, successful: bool = True) -> None: successful: Whether the call has been successful or not [default: True] """ self.results[key] = data + if successful: self.successful.append(key) @@ -241,6 +247,35 @@ def print_raw(self, key: Optional[str] = None) -> None: pprint(data, expand_all=True) + def save_raw(self, file: Path, key: Optional[str] = None) -> None: + """ + Save the raw data in defined format. + + Args: + key: Print only the result for the host given + (default: print all results) + """ + output_format = Result.OUTPUT_FORMAT_MAPPING.get(file.suffix, None) + + data: Any = None + if key: + data = self.get_result(key) + + else: + data = {} + for host in self.all_results(): + data[host] = self.results[host] + + if output_format == "json": + save_json_file(json_file=Path(file), data=data) + + elif output_format == "text": + save_txt_file(file=Path(file), data=str(data)) + + else: + log.warning("No output format defined for this file extension. Saving as text") + save_txt_file(file=Path(file), data=str(data)) + def save_with_template(self, host: str, template_file: Path, output_file: Path) -> None: """ Saves a data structure to a file with a given Jinja2 template. The data structure and the diff --git a/fotoobo/tools/fgt/get.py b/fotoobo/tools/fgt/get.py index f911ec6..0a6558d 100644 --- a/fotoobo/tools/fgt/get.py +++ b/fotoobo/tools/fgt/get.py @@ -4,7 +4,7 @@ import concurrent.futures import logging -from typing import Optional, Tuple +from typing import Any, Optional, Tuple, Union from rich.progress import Progress @@ -17,6 +17,33 @@ log = logging.getLogger("fotoobo") +def api( + host: str, url: str = "", vdom: str = "*", timeout: Optional[float] = None +) -> Result[list[Any]]: + """Native GET request to a FortiGate. + + This gets the response from a single API request to a FortiGate and returns it as a fotoobo + Results object. + + Args: + host: The host from the inventory to send the GET requests to + url: The API endpoint to access + vdom: The VDOM to access ("vdom1" or "vdom1,vdom2" or "*") + + Returns: + The Result object with all the results as list (even if only one result is returned) + """ + inventory = Inventory(config.inventory_file) + fgt: FortiGate = inventory.get_item(host, "fortigate") + result = Result[list[Any]]() + params = {"vdom": vdom} + response = fgt.api(method="get", url=url, params=params, timeout=timeout) + data = [response.json()] if isinstance(response.json(), dict) else response.json() + result.push_result(host, data=data) + + return result + + def version(host: Optional[str] = None) -> Result[str]: """FortiGate get version. diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py index 476ff1a..8b85e26 100644 --- a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py @@ -38,8 +38,8 @@ def test_cli_app_fgt_get_cmdb_firewall_address_help() -> None: ) assert result.exit_code == 0 arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"name"} - assert options == {"-h", "--help", "-o", "--output"} + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} assert not commands From c50e3b2e2e91633c8d6c96371f28d00e6d6af6f2 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Wed, 16 Oct 2024 15:46:02 +0200 Subject: [PATCH 06/20] =?UTF-8?q?=F0=9F=91=94=20Add=20API=20endpoint=20for?= =?UTF-8?q?=20'fgt=20get=20cmdb=20firewall=20addrgrp'.=20Tests=20not=20yet?= =?UTF-8?q?=20implemented.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fgt/get/cmdb/firewall/firewall.py | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py index 7ea0334..15fe14c 100644 --- a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py +++ b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py @@ -34,7 +34,7 @@ def callback(context: typer.Context) -> None: @app.command() -def address( +def address( # pylint: disable=too-many-branches host: str = typer.Argument( "", help="The FortiGate hostname to access (must be defined in the inventory). " @@ -121,12 +121,26 @@ def address( @app.command() def addrgrp( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), name: str = typer.Argument( "", help="The firewall address group object to get \[default: ]", show_default=False, metavar="[name]", ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), output_file: str = typer.Option( None, "--output", @@ -141,5 +155,34 @@ def addrgrp( The FortiGate api endpoint is: /cmdb/firewall/addrgrp """ - # TODO: Here to add the address group cli code - print("ADDRGRP") + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") + + result = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + + if output_file: + result.save_raw(file=Path(output_file), key=host) + + else: + assets = [] + if result.get_result(host): + for vd in result.get_result(host): + for asset in vd["results"]: + # print(asset) + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "content": "\n".join(_["name"] for _ in asset["member"]), + } + + assets.append(data) + + result.push_result(host, assets) + + if result.results[host]: + result.print_table_raw( + result.results[host], headers=["name", "vdom", "content"], title=host + ) + + else: + print("No data found") From 24b199c70033cf1bc6a4d814cb57ea639b5b5346 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Wed, 16 Oct 2024 15:48:49 +0200 Subject: [PATCH 07/20] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20Unused=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/tools/fgt/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fotoobo/tools/fgt/get.py b/fotoobo/tools/fgt/get.py index 0a6558d..0460740 100644 --- a/fotoobo/tools/fgt/get.py +++ b/fotoobo/tools/fgt/get.py @@ -4,7 +4,7 @@ import concurrent.futures import logging -from typing import Any, Optional, Tuple, Union +from typing import Any, Optional, Tuple from rich.progress import Progress From 85fc74b7c0f7a6938f131fbc354e322ec2e3f7c2 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Wed, 16 Oct 2024 15:49:16 +0200 Subject: [PATCH 08/20] =?UTF-8?q?=E2=9C=85=20Fix=20test=20for=20cli=20fgt?= =?UTF-8?q?=20get=20cmdb=20firewall=20addrgrp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py index 8b85e26..f8c9316 100644 --- a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py @@ -50,6 +50,6 @@ def test_cli_app_fgt_get_cmdb_firewall_addrgrp_help() -> None: ) assert result.exit_code == 0 arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"name"} - assert options == {"-h", "--help", "-o", "--output"} + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} assert not commands From 53caadc80d54b7f9890a3411d60bef31198f3ceb Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Wed, 16 Oct 2024 15:51:46 +0200 Subject: [PATCH 09/20] =?UTF-8?q?=F0=9F=93=9D=20Update=20WHATSNEW?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WHATSNEW.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WHATSNEW.md b/WHATSNEW.md index 6b5dca8..8f5a177 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -1,6 +1,11 @@ ### Added +- Add CLI command fgt get cmdb firewall addrgrp +- Add CLI command fgt get cmdb firewall address + ### Changed +- Optimize imports in CLI module + ### Removed From 8271fb37c8561d36b623b9df7e472f6cf8bffab9 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Mon, 21 Oct 2024 11:55:13 +0200 Subject: [PATCH 10/20] =?UTF-8?q?=F0=9F=93=9D=20Add=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/tools/fgt/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fotoobo/tools/fgt/get.py b/fotoobo/tools/fgt/get.py index 0460740..a343117 100644 --- a/fotoobo/tools/fgt/get.py +++ b/fotoobo/tools/fgt/get.py @@ -38,7 +38,7 @@ def api( result = Result[list[Any]]() params = {"vdom": vdom} response = fgt.api(method="get", url=url, params=params, timeout=timeout) - data = [response.json()] if isinstance(response.json(), dict) else response.json() + data = [response.json()] if isinstance(response.json(), dict) else response.json() # listify result.push_result(host, data=data) return result From 56cd072d849f220088fbc054c3a6c52a96b06b4a Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Mon, 21 Oct 2024 13:31:34 +0200 Subject: [PATCH 11/20] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Add=20API=20endpo?= =?UTF-8?q?ints=20for=20fgt=20cmdb=20services=20and=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fgt/get/cmdb/firewall/firewall.py | 97 +++---------------- .../fgt/get/cmdb/firewall/service/service.py | 59 ++++++++--- fotoobo/tools/fgt/cmdb/__init__.py | 3 + fotoobo/tools/fgt/cmdb/firewall/__init__.py | 3 + fotoobo/tools/fgt/cmdb/firewall/address.py | 53 ++++++++++ fotoobo/tools/fgt/cmdb/firewall/addrgrp.py | 36 +++++++ .../tools/fgt/cmdb/firewall/service_custom.py | 54 +++++++++++ .../tools/fgt/cmdb/firewall/service_group.py | 38 ++++++++ .../test_cli_fgt_get_cmdb_firewall_service.py | 8 +- 9 files changed, 247 insertions(+), 104 deletions(-) create mode 100644 fotoobo/tools/fgt/cmdb/__init__.py create mode 100644 fotoobo/tools/fgt/cmdb/firewall/__init__.py create mode 100644 fotoobo/tools/fgt/cmdb/firewall/address.py create mode 100644 fotoobo/tools/fgt/cmdb/firewall/addrgrp.py create mode 100644 fotoobo/tools/fgt/cmdb/firewall/service_custom.py create mode 100644 fotoobo/tools/fgt/cmdb/firewall/service_group.py diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py index 15fe14c..b61133a 100644 --- a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py +++ b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py @@ -4,13 +4,13 @@ # pylint: disable=anomalous-backslash-in-string import logging -from pathlib import Path import typer from fotoobo.exceptions import GeneralError from fotoobo.helpers import cli_path -from fotoobo.tools.fgt.get import api +from fotoobo.tools.fgt.cmdb.firewall.address import get_firewall_address +from fotoobo.tools.fgt.cmdb.firewall.addrgrp import get_firewall_addrgrp from .service import service @@ -34,7 +34,7 @@ def callback(context: typer.Context) -> None: @app.command() -def address( # pylint: disable=too-many-branches +def address( host: str = typer.Argument( "", help="The FortiGate hostname to access (must be defined in the inventory). " @@ -64,59 +64,13 @@ def address( # pylint: disable=too-many-branches show_default=False, ), ) -> None: - """ - Get FortiGate cmdb firewall address. - - The FortiGate api endpoint is: /cmdb/firewall/address - """ + """Get FortiGate cmdb firewall address.""" if name and ("*" in vdom or "," in vdom): raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - result = api(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") - - if output_file: - result.save_raw(file=Path(output_file), key=host) - - else: - assets = [] - if result.get_result(host): - for vd in result.get_result(host): - for asset in vd["results"]: - - data: dict[str, str] = { - "name": asset["name"], - "vdom": vd["vdom"], - "type": asset["type"], - } - - if asset["type"] == "fqdn": - data["content"] = asset["fqdn"] - - elif asset["type"] == "geography": - data["content"] = asset["country"] - - elif asset["type"] == "ipmask": - data["content"] = "/".join( - [asset["subnet"].split(" ")[0], asset["subnet"].split(" ")[1]] - ) - - elif asset["type"] == "iprange": - data["content"] = " - ".join([asset["start-ip"], asset["end-ip"]]) - - else: - data["content"] = "" - - assets.append(data) - - result.push_result(host, assets) - - if result.results[host]: - result.print_table_raw( - result.results[host], headers=["name", "vdom", "type", "content"], title=host - ) - - else: - print("No data found") + result = get_firewall_address(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) @app.command() @@ -150,39 +104,10 @@ def addrgrp( show_default=False, ), ) -> None: - """ - Get FortiGate cmdb firewall address group. - - The FortiGate api endpoint is: /cmdb/firewall/addrgrp - """ + """Get FortiGate cmdb firewall address group.""" if name and ("*" in vdom or "," in vdom): raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - result = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") - - if output_file: - result.save_raw(file=Path(output_file), key=host) - - else: - assets = [] - if result.get_result(host): - for vd in result.get_result(host): - for asset in vd["results"]: - # print(asset) - data: dict[str, str] = { - "name": asset["name"], - "vdom": vd["vdom"], - "content": "\n".join(_["name"] for _ in asset["member"]), - } - - assets.append(data) - - result.push_result(host, assets) - - if result.results[host]: - result.print_table_raw( - result.results[host], headers=["name", "vdom", "content"], title=host - ) - - else: - print("No data found") + result = get_firewall_addrgrp(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py b/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py index bb62809..8b0eaef 100644 --- a/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py +++ b/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py @@ -7,7 +7,10 @@ import typer +from fotoobo.exceptions import GeneralError from fotoobo.helpers import cli_path +from fotoobo.tools.fgt.cmdb.firewall.service_custom import get_firewall_service_custom +from fotoobo.tools.fgt.cmdb.firewall.service_group import get_firewall_service_group app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") @@ -27,12 +30,26 @@ def callback(context: typer.Context) -> None: @app.command() def custom( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), name: str = typer.Argument( "", - help="The firewall address object to get \[default: ]", + help="The firewall service object to get \[default: ]", show_default=False, metavar="[name]", ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), output_file: str = typer.Option( None, "--output", @@ -42,23 +59,37 @@ def custom( show_default=False, ), ) -> None: - """ - Get FortiGate cmdb firewall service custom. + """Get FortiGate cmdb firewall service custom.""" + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - The FortiGate api endpoint is: /cmdb/firewall.service/custom - """ - # TODO: Here to add the service custom cli code - print("SERVICE CUSTOM") + result = get_firewall_service_custom(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) @app.command() def group( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), name: str = typer.Argument( "", - help="The firewall address object to get \[default: ]", + help="The firewall service group to get \[default: ]", show_default=False, metavar="[name]", ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), output_file: str = typer.Option( None, "--output", @@ -68,10 +99,10 @@ def group( show_default=False, ), ) -> None: - """ - Get FortiGate cmdb firewall service group. + """Get FortiGate cmdb firewall service group.""" + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - The FortiGate api endpoint is: /cmdb/firewall.service/group - """ - # TODO: Here to add the service group cli code - print("SERVICE GROUP") + result = get_firewall_service_group(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) diff --git a/fotoobo/tools/fgt/cmdb/__init__.py b/fotoobo/tools/fgt/cmdb/__init__.py new file mode 100644 index 0000000..4836b3a --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/__init__.py @@ -0,0 +1,3 @@ +""" +fgt tools cmdb +""" diff --git a/fotoobo/tools/fgt/cmdb/firewall/__init__.py b/fotoobo/tools/fgt/cmdb/firewall/__init__.py new file mode 100644 index 0000000..9ca4f7f --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/firewall/__init__.py @@ -0,0 +1,3 @@ +""" +fgt tools cmdb firewall +""" diff --git a/fotoobo/tools/fgt/cmdb/firewall/address.py b/fotoobo/tools/fgt/cmdb/firewall/address.py new file mode 100644 index 0000000..db5bf5c --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/firewall/address.py @@ -0,0 +1,53 @@ +"""FortiGate CMDB firewall address module""" + +from pathlib import Path +from typing import Any + +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.get import api + + +def get_firewall_address(host: str, name: str, vdom: str, output_file: str) -> Result[list[Any]]: + """Get the firewall address object(s) + + The FortiGate api endpoint is: /cmdb/firewall/address + """ + result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") + + if output_file: + result_raw.save_raw(file=Path(output_file), key=host) + + result = Result[list[Any]]() + assets = [] + if result_raw.get_result(host): + for vd in result_raw.get_result(host): + for asset in vd["results"]: + + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "type": asset["type"], + } + + if asset["type"] == "fqdn": + data["content"] = asset["fqdn"] + + elif asset["type"] == "geography": + data["content"] = asset["country"] + + elif asset["type"] == "ipmask": + data["content"] = "/".join( + [asset["subnet"].split(" ")[0], asset["subnet"].split(" ")[1]] + ) + + elif asset["type"] == "iprange": + data["content"] = " - ".join([asset["start-ip"], asset["end-ip"]]) + + else: + data["content"] = "" + + assets.append(data) + + result.push_result(host, assets) + + return result diff --git a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py new file mode 100644 index 0000000..6a5e75d --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py @@ -0,0 +1,36 @@ +"""FortiGate CMDB firewall addrgrp module""" + +from pathlib import Path +from typing import Any + +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.get import api + + +def get_firewall_addrgrp(host: str, name: str, vdom: str, output_file: str) -> Result[list[Any]]: + """Get the firewall address group object(s) + + The FortiGate api endpoint is: /cmdb/firewall/addrgrp + """ + result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + + if output_file: + result_raw.save_raw(file=Path(output_file), key=host) + + result = Result[list[Any]]() + assets = [] + if result_raw.get_result(host): + for vd in result_raw.get_result(host): + for asset in vd["results"]: + # print(asset) + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "content": "\n".join(_["name"] for _ in asset["member"]), + } + + assets.append(data) + + result.push_result(host, assets) + + return result diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py new file mode 100644 index 0000000..b93e4f4 --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py @@ -0,0 +1,54 @@ +"""FortiGate CMDB firewall service custom module""" + +from pathlib import Path +from typing import Any + +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.get import api + + +def get_firewall_service_custom( + host: str, name: str, vdom: str, output_file: str +) -> Result[list[Any]]: + """Get the firewall service custom object(s) + + The FortiGate api endpoint is: /cmdb/firewall.service/custom + """ + result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall.service/custom/{name}") + + if output_file: + result_raw.save_raw(file=Path(output_file), key=host) + + result = Result[list[Any]]() + assets = [] + if result_raw.get_result(host): + for vd in result_raw.get_result(host): + for asset in vd["results"]: + + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "protocol": asset["protocol"], + } + + if asset["protocol"] == "TCP/UDP/SCTP": + data["data_1"] = asset.get("tcp-portrange", "") + data["data_2"] = asset.get("udp-portrange", "") + + elif asset["protocol"] in ["ICMP", "ICMP6"]: + data["data_1"] = asset.get("icmptype", "") + data["data_2"] = asset.get("icmpcode", "") + + elif asset["protocol"] == "IP": + data["data_1"] = asset.get("protocol-number", "") + data["data_2"] = "" + + else: + data["data_1"] = "" + data["data_2"] = "" + + assets.append(data) + + result.push_result(host, assets) + + return result diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_group.py b/fotoobo/tools/fgt/cmdb/firewall/service_group.py new file mode 100644 index 0000000..94e1303 --- /dev/null +++ b/fotoobo/tools/fgt/cmdb/firewall/service_group.py @@ -0,0 +1,38 @@ +"""FortiGate CMDB firewall service group module""" + +from pathlib import Path +from typing import Any + +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.get import api + + +def get_firewall_service_group( + host: str, name: str, vdom: str, output_file: str +) -> Result[list[Any]]: + """Get the firewall service group object(s) + + The FortiGate api endpoint is: /cmdb/firewall.service/group + """ + result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + + if output_file: + result_raw.save_raw(file=Path(output_file), key=host) + + result = Result[list[Any]]() + assets = [] + if result_raw.get_result(host): + for vd in result_raw.get_result(host): + for asset in vd["results"]: + print(asset) + data: dict[str, str] = { + "name": asset["name"], + "vdom": vd["vdom"], + "content": "\n".join(_["name"] for _ in asset["member"]), + } + + assets.append(data) + + result.push_result(host, assets) + + return result diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py index 99b3200..21909af 100644 --- a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py @@ -41,8 +41,8 @@ def test_cli_app_fgt_get_cmdb_firewall_service_custom_help() -> None: ) assert result.exit_code == 0 arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"name"} - assert options == {"-h", "--help", "-o", "--output"} + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} assert not commands @@ -54,6 +54,6 @@ def test_cli_app_fgt_get_cmdb_firewall_service_group_help() -> None: ) assert result.exit_code == 0 arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"name"} - assert options == {"-h", "--help", "-o", "--output"} + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} assert not commands From 10066ff7004680f23678a9cd28e81aae6fe65ffc Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 08:22:08 +0200 Subject: [PATCH 12/20] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20CLI=20com?= =?UTF-8?q?mand=20structure=20for=20fgt=20get=20cmd=20firewall=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/cli/fgt/get/cmdb/firewall/firewall.py | 92 +++++++++++++-- .../fgt/get/cmdb/firewall/service/__init__.py | 7 -- .../fgt/get/cmdb/firewall/service/service.py | 108 ------------------ fotoobo/tools/fgt/cmdb/firewall/__init__.py | 12 ++ fotoobo/tools/fgt/cmdb/firewall/address.py | 4 +- fotoobo/tools/fgt/cmdb/firewall/addrgrp.py | 4 +- .../tools/fgt/cmdb/firewall/service_custom.py | 2 +- .../tools/fgt/cmdb/firewall/service_group.py | 2 +- .../cli/fgt/test_cli_fgt_get_cmdb_firewall.py | 30 ++++- .../test_cli_fgt_get_cmdb_firewall_service.py | 59 ---------- 10 files changed, 131 insertions(+), 189 deletions(-) delete mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py delete mode 100644 fotoobo/cli/fgt/get/cmdb/firewall/service/service.py delete mode 100644 tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py index b61133a..3e3c118 100644 --- a/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py +++ b/fotoobo/cli/fgt/get/cmdb/firewall/firewall.py @@ -9,18 +9,12 @@ from fotoobo.exceptions import GeneralError from fotoobo.helpers import cli_path -from fotoobo.tools.fgt.cmdb.firewall.address import get_firewall_address -from fotoobo.tools.fgt.cmdb.firewall.addrgrp import get_firewall_addrgrp - -from .service import service +from fotoobo.tools.fgt.cmdb.firewall import * # pylint: disable=wildcard-import app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") log = logging.getLogger("fotoobo") -app.add_typer(service.app, name="service", help="FortiGate get cmdb firewall service commands.") - - @app.callback() def callback(context: typer.Context) -> None: """ @@ -68,7 +62,7 @@ def address( if name and ("*" in vdom or "," in vdom): raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - result = get_firewall_address(host, name, vdom, output_file) + result = get_cmdb_firewall_address(host, name, vdom, output_file) if not output_file: result.print_table_raw(result.results[host], auto_header=True, title=host) @@ -108,6 +102,86 @@ def addrgrp( if name and ("*" in vdom or "," in vdom): raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - result = get_firewall_addrgrp(host, name, vdom, output_file) + result = get_cmdb_firewall_addrgrp(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) + + +@app.command() +def service_custom( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), + name: str = typer.Argument( + "", + help="The firewall service object to get \[default: ]", + show_default=False, + metavar="[name]", + ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """Get FortiGate cmdb firewall service custom.""" + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") + + result = get_cmdb_firewall_service_custom(host, name, vdom, output_file) + if not output_file: + result.print_table_raw(result.results[host], auto_header=True, title=host) + + +@app.command() +def service_group( + host: str = typer.Argument( + "", + help="The FortiGate hostname to access (must be defined in the inventory). " + "\[default: ]", + show_default=False, + metavar="[host]", + ), + name: str = typer.Argument( + "", + help="The firewall service group to get \[default: ]", + show_default=False, + metavar="[name]", + ), + vdom: str = typer.Option( + "*", + "--vdom", + help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", + show_default=False, + metavar="[vdom]", + ), + output_file: str = typer.Option( + None, + "--output", + "-o", + help="Output file (format is specified by extension)", + metavar="[file]", + show_default=False, + ), +) -> None: + """Get FortiGate cmdb firewall service group.""" + if name and ("*" in vdom or "," in vdom): + raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") + + result = get_cmdb_firewall_service_group(host, name, vdom, output_file) if not output_file: result.print_table_raw(result.results[host], auto_header=True, title=host) diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py b/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py deleted file mode 100644 index 8cb1b11..0000000 --- a/fotoobo/cli/fgt/get/cmdb/firewall/service/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -__init__.py -""" - -from . import service - -__all__ = ["service"] diff --git a/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py b/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py deleted file mode 100644 index 8b0eaef..0000000 --- a/fotoobo/cli/fgt/get/cmdb/firewall/service/service.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -The FortiGate commands -""" - -# pylint: disable=anomalous-backslash-in-string -import logging - -import typer - -from fotoobo.exceptions import GeneralError -from fotoobo.helpers import cli_path -from fotoobo.tools.fgt.cmdb.firewall.service_custom import get_firewall_service_custom -from fotoobo.tools.fgt.cmdb.firewall.service_group import get_firewall_service_group - -app = typer.Typer(no_args_is_help=True, rich_markup_mode="rich") -log = logging.getLogger("fotoobo") - - -@app.callback() -def callback(context: typer.Context) -> None: - """ - The fgt get cmdb get firewall service subcommand callback - - Args: - context: The context object of the typer app - """ - cli_path.append(str(context.invoked_subcommand)) - log.debug("About to execute command: '%s'", context.invoked_subcommand) - - -@app.command() -def custom( - host: str = typer.Argument( - "", - help="The FortiGate hostname to access (must be defined in the inventory). " - "\[default: ]", - show_default=False, - metavar="[host]", - ), - name: str = typer.Argument( - "", - help="The firewall service object to get \[default: ]", - show_default=False, - metavar="[name]", - ), - vdom: str = typer.Option( - "*", - "--vdom", - help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", - show_default=False, - metavar="[vdom]", - ), - output_file: str = typer.Option( - None, - "--output", - "-o", - help="Output file (format is specified by extension)", - metavar="[file]", - show_default=False, - ), -) -> None: - """Get FortiGate cmdb firewall service custom.""" - if name and ("*" in vdom or "," in vdom): - raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - - result = get_firewall_service_custom(host, name, vdom, output_file) - if not output_file: - result.print_table_raw(result.results[host], auto_header=True, title=host) - - -@app.command() -def group( - host: str = typer.Argument( - "", - help="The FortiGate hostname to access (must be defined in the inventory). " - "\[default: ]", - show_default=False, - metavar="[host]", - ), - name: str = typer.Argument( - "", - help="The firewall service group to get \[default: ]", - show_default=False, - metavar="[name]", - ), - vdom: str = typer.Option( - "*", - "--vdom", - help="The vdom to query ('vdom1' or 'vdom1,vdom2') \[default: ]", - show_default=False, - metavar="[vdom]", - ), - output_file: str = typer.Option( - None, - "--output", - "-o", - help="Output file (format is specified by extension)", - metavar="[file]", - show_default=False, - ), -) -> None: - """Get FortiGate cmdb firewall service group.""" - if name and ("*" in vdom or "," in vdom): - raise GeneralError("With name argument you have to specify one single VDOM (with --vdom)") - - result = get_firewall_service_group(host, name, vdom, output_file) - if not output_file: - result.print_table_raw(result.results[host], auto_header=True, title=host) diff --git a/fotoobo/tools/fgt/cmdb/firewall/__init__.py b/fotoobo/tools/fgt/cmdb/firewall/__init__.py index 9ca4f7f..801e87d 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/__init__.py +++ b/fotoobo/tools/fgt/cmdb/firewall/__init__.py @@ -1,3 +1,15 @@ """ fgt tools cmdb firewall """ + +from .address import get_cmdb_firewall_address +from .addrgrp import get_cmdb_firewall_addrgrp +from .service_custom import get_cmdb_firewall_service_custom +from .service_group import get_cmdb_firewall_service_group + +__all__ = [ + "get_cmdb_firewall_address", + "get_cmdb_firewall_addrgrp", + "get_cmdb_firewall_service_custom", + "get_cmdb_firewall_service_group", +] diff --git a/fotoobo/tools/fgt/cmdb/firewall/address.py b/fotoobo/tools/fgt/cmdb/firewall/address.py index db5bf5c..03412af 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/address.py +++ b/fotoobo/tools/fgt/cmdb/firewall/address.py @@ -7,7 +7,9 @@ from fotoobo.tools.fgt.get import api -def get_firewall_address(host: str, name: str, vdom: str, output_file: str) -> Result[list[Any]]: +def get_cmdb_firewall_address( + host: str, name: str, vdom: str, output_file: str +) -> Result[list[Any]]: """Get the firewall address object(s) The FortiGate api endpoint is: /cmdb/firewall/address diff --git a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py index 6a5e75d..7bde333 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py +++ b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py @@ -7,7 +7,9 @@ from fotoobo.tools.fgt.get import api -def get_firewall_addrgrp(host: str, name: str, vdom: str, output_file: str) -> Result[list[Any]]: +def get_cmdb_firewall_addrgrp( + host: str, name: str, vdom: str, output_file: str +) -> Result[list[Any]]: """Get the firewall address group object(s) The FortiGate api endpoint is: /cmdb/firewall/addrgrp diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py index b93e4f4..df8bfe7 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py @@ -7,7 +7,7 @@ from fotoobo.tools.fgt.get import api -def get_firewall_service_custom( +def get_cmdb_firewall_service_custom( host: str, name: str, vdom: str, output_file: str ) -> Result[list[Any]]: """Get the firewall service custom object(s) diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_group.py b/fotoobo/tools/fgt/cmdb/firewall/service_group.py index 94e1303..43d811e 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_group.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_group.py @@ -7,7 +7,7 @@ from fotoobo.tools.fgt.get import api -def get_firewall_service_group( +def get_cmdb_firewall_service_group( host: str, name: str, vdom: str, output_file: str ) -> Result[list[Any]]: """Get the firewall service group object(s) diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py index f8c9316..6fa063b 100644 --- a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py +++ b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall.py @@ -19,7 +19,7 @@ def test_cli_app_fgt_get_cmdb_firewall_help() -> None: arguments, options, commands = parse_help_output(result.stdout) assert not arguments assert options == {"-h", "--help"} - assert set(commands) == {"address", "addrgrp", "service"} + assert set(commands) == {"address", "addrgrp", "service-custom", "service-group"} def test_cli_app_fgt_get_cmdb_firewall_no_args() -> None: @@ -28,7 +28,7 @@ def test_cli_app_fgt_get_cmdb_firewall_no_args() -> None: assert result.exit_code == 0 assert "Usage: callback fgt get cmdb firewall [OPTIONS] COMMAND" in result.stdout assert "--help" in result.stdout - assert "FortiGate get cmdb firewall service commands." in result.stdout + assert "Get FortiGate cmdb firewall address." in result.stdout def test_cli_app_fgt_get_cmdb_firewall_address_help() -> None: @@ -53,3 +53,29 @@ def test_cli_app_fgt_get_cmdb_firewall_addrgrp_help() -> None: assert set(arguments) == {"host", "name"} assert options == {"-h", "--help", "-o", "--output", "--vdom"} assert not commands + + +def test_cli_app_fgt_get_cmdb_firewall_service_custom_help() -> None: + """Test cli help for fgt get cmdb firewall service-custom""" + result = runner.invoke( + app, + ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service-custom", "-h"], + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} + assert not commands + + +def test_cli_app_fgt_get_cmdb_firewall_service_group_help() -> None: + """Test cli help for fgt get cmdb firewall service-group""" + result = runner.invoke( + app, + ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service-group", "-h"], + ) + assert result.exit_code == 0 + arguments, options, commands = parse_help_output(result.stdout) + assert set(arguments) == {"host", "name"} + assert options == {"-h", "--help", "-o", "--output", "--vdom"} + assert not commands diff --git a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py b/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py deleted file mode 100644 index 21909af..0000000 --- a/tests/cli/fgt/test_cli_fgt_get_cmdb_firewall_service.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Testing the cli fgt get cmdb firewall service -""" - -from typer.testing import CliRunner - -from fotoobo.cli.main import app -from tests.helper import parse_help_output - -runner = CliRunner() - - -def test_cli_app_fgt_get_cmdb_firewall_service_help() -> None: - """Test cli help for fgt get cmdb firewall service""" - result = runner.invoke( - app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "-h"] - ) - assert result.exit_code == 0 - arguments, options, commands = parse_help_output(result.stdout) - assert not arguments - assert options == {"-h", "--help"} - assert set(commands) == {"custom", "group"} - - -def test_cli_app_fgt_get_cmdb_firewall_service_no_args() -> None: - """Test fgt get cmdb firewall service with no arguments""" - result = runner.invoke( - app, ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service"] - ) - assert result.exit_code == 0 - assert "Usage: callback fgt get cmdb firewall service [OPTIONS] COMMAND" in result.stdout - assert "--help" in result.stdout - assert "Get FortiGate cmdb firewall service custom." in result.stdout - - -def test_cli_app_fgt_get_cmdb_firewall_service_custom_help() -> None: - """Test cli help for fgt get cmdb firewall service custom""" - result = runner.invoke( - app, - ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "custom", "-h"], - ) - assert result.exit_code == 0 - arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"host", "name"} - assert options == {"-h", "--help", "-o", "--output", "--vdom"} - assert not commands - - -def test_cli_app_fgt_get_cmdb_firewall_service_group_help() -> None: - """Test cli help for fgt get cmdb firewall service group""" - result = runner.invoke( - app, - ["-c", "tests/fotoobo.yaml", "fgt", "get", "cmdb", "firewall", "service", "group", "-h"], - ) - assert result.exit_code == 0 - arguments, options, commands = parse_help_output(result.stdout) - assert set(arguments) == {"host", "name"} - assert options == {"-h", "--help", "-o", "--output", "--vdom"} - assert not commands From 91fe25195ef255a7ef444783dd5f9e9046918f8d Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 08:25:42 +0200 Subject: [PATCH 13/20] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20Remove=20unused=20pr?= =?UTF-8?q?int()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/tools/fgt/cmdb/firewall/service_group.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_group.py b/fotoobo/tools/fgt/cmdb/firewall/service_group.py index 43d811e..ae0f44e 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_group.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_group.py @@ -24,7 +24,6 @@ def get_cmdb_firewall_service_group( if result_raw.get_result(host): for vd in result_raw.get_result(host): for asset in vd["results"]: - print(asset) data: dict[str, str] = { "name": asset["name"], "vdom": vd["vdom"], From c5ef1843d879561f3fc395b9eaa09529e203e01b Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 08:47:48 +0200 Subject: [PATCH 14/20] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rename=20native=20ap?= =?UTF-8?q?i()=20method=20to=20api=5Fget()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/tools/fgt/cmdb/firewall/address.py | 4 ++-- fotoobo/tools/fgt/cmdb/firewall/addrgrp.py | 4 ++-- fotoobo/tools/fgt/cmdb/firewall/service_custom.py | 4 ++-- fotoobo/tools/fgt/cmdb/firewall/service_group.py | 4 ++-- fotoobo/tools/fgt/get.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fotoobo/tools/fgt/cmdb/firewall/address.py b/fotoobo/tools/fgt/cmdb/firewall/address.py index 03412af..a23d281 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/address.py +++ b/fotoobo/tools/fgt/cmdb/firewall/address.py @@ -4,7 +4,7 @@ from typing import Any from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api +from fotoobo.tools.fgt.get import api_get def get_cmdb_firewall_address( @@ -14,7 +14,7 @@ def get_cmdb_firewall_address( The FortiGate api endpoint is: /cmdb/firewall/address """ - result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") + result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") if output_file: result_raw.save_raw(file=Path(output_file), key=host) diff --git a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py index 7bde333..7d881cd 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py +++ b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py @@ -4,7 +4,7 @@ from typing import Any from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api +from fotoobo.tools.fgt.get import api_get def get_cmdb_firewall_addrgrp( @@ -14,7 +14,7 @@ def get_cmdb_firewall_addrgrp( The FortiGate api endpoint is: /cmdb/firewall/addrgrp """ - result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") if output_file: result_raw.save_raw(file=Path(output_file), key=host) diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py index df8bfe7..2d6723b 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py @@ -4,7 +4,7 @@ from typing import Any from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api +from fotoobo.tools.fgt.get import api_get def get_cmdb_firewall_service_custom( @@ -14,7 +14,7 @@ def get_cmdb_firewall_service_custom( The FortiGate api endpoint is: /cmdb/firewall.service/custom """ - result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall.service/custom/{name}") + result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall.service/custom/{name}") if output_file: result_raw.save_raw(file=Path(output_file), key=host) diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_group.py b/fotoobo/tools/fgt/cmdb/firewall/service_group.py index ae0f44e..81f3453 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_group.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_group.py @@ -4,7 +4,7 @@ from typing import Any from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api +from fotoobo.tools.fgt.get import api_get def get_cmdb_firewall_service_group( @@ -14,7 +14,7 @@ def get_cmdb_firewall_service_group( The FortiGate api endpoint is: /cmdb/firewall.service/group """ - result_raw = api(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") if output_file: result_raw.save_raw(file=Path(output_file), key=host) diff --git a/fotoobo/tools/fgt/get.py b/fotoobo/tools/fgt/get.py index a343117..65752a8 100644 --- a/fotoobo/tools/fgt/get.py +++ b/fotoobo/tools/fgt/get.py @@ -17,7 +17,7 @@ log = logging.getLogger("fotoobo") -def api( +def api_get( host: str, url: str = "", vdom: str = "*", timeout: Optional[float] = None ) -> Result[list[Any]]: """Native GET request to a FortiGate. From f71e27c15a62ed9eb36803ddb11e0cb4785252cd Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 09:34:38 +0200 Subject: [PATCH 15/20] =?UTF-8?q?=E2=9C=85=20Test=20save=5Ftxt=5Ffile()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/helpers/test_files.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/helpers/test_files.py b/tests/helpers/test_files.py index ed98177..3f2b8cd 100644 --- a/tests/helpers/test_files.py +++ b/tests/helpers/test_files.py @@ -20,6 +20,7 @@ load_json_file, load_yaml_file, save_json_file, + save_txt_file, save_yaml_file, ) from fotoobo.inventory.generic import GenericDevice @@ -46,6 +47,12 @@ def json_test_file(temp_dir: Path) -> Path: return temp_dir / "test_file.json" +@pytest.fixture +def txt_test_file(temp_dir: Path) -> Path: + """Returns the filename of a txt test file""" + return temp_dir / "test_file.txt" + + @pytest.fixture def yaml_test_file(temp_dir: Path) -> Path: """Returns the filename of a yaml test file""" @@ -289,6 +296,28 @@ def test_load_yaml_file_non_exist(yaml_test_file: Path) -> None: assert not load_yaml_file(yaml_test_file) +# Start testing the txt file_helper functions + + +@pytest.mark.parametrize( + "content", + ( + pytest.param("LINE_1", id="file with one line"), + pytest.param("", id="empty file"), + pytest.param("LINE_1\nLINE_2\nLINE_3", id="file with multiple lines"), + ), +) +def test_save_txt_file(txt_test_file: Path, content: str) -> None: + """Test the save_json_file function""" + assert not txt_test_file.is_file() + save_txt_file(txt_test_file, content) + assert txt_test_file.is_file() + new_content = txt_test_file.read_text(encoding="UTF-8") + assert new_content == content + txt_test_file.unlink() + assert not txt_test_file.is_file() + + # Start testing the create_dir function From b1033afe7f836619ba1f828c50bfb384c659d7ce Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 10:25:26 +0200 Subject: [PATCH 16/20] =?UTF-8?q?=E2=9C=85=20Test=20save=5Fraw()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/helpers/test_results.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/helpers/test_results.py b/tests/helpers/test_results.py index 005a027..e746206 100644 --- a/tests/helpers/test_results.py +++ b/tests/helpers/test_results.py @@ -276,6 +276,29 @@ def test_print_raw(input_data: Any, expect: str, capsys: Any) -> None: assert "test_host_2" not in out assert expect in out + @staticmethod + @pytest.mark.parametrize( + "file_name, key", + ( + pytest.param("test_save_raw.json", None, id="json all"), + pytest.param("test_save_raw.json", "dummy", id="json single"), + pytest.param("test_save_raw.txt", None, id="txt all"), + pytest.param("test_save_raw.txt", "dummy", id="txt single"), + pytest.param("test_save_raw.dummy", None, id="unknown all"), + pytest.param("test_save_raw.dummy", "dummy", id="unknown single"), + ), + ) + def test_save_raw(temp_dir: Path, file_name: str, key: str) -> None: + """Test save_raw() method""" + result = Result[Any]() + result.push_result("dummy", [1, 2, 3]) + output_file = temp_dir / file_name + assert not output_file.is_file() + result.save_raw(output_file, key) + assert output_file.is_file() + output_file.unlink() + assert not output_file.is_file() + @staticmethod def test_save_with_template(temp_dir: Path) -> None: """Test save_with_template""" From da3cf351454dd6fddd7d6b19e38637eca34e4c04 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 13:42:27 +0200 Subject: [PATCH 17/20] =?UTF-8?q?=E2=9C=85=20Test=20the=20new=20FortiGate?= =?UTF-8?q?=20get...=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/tools/fgt/cmdb/__init__.py | 3 + tests/tools/fgt/cmdb/firewall/__init__.py | 3 + tests/tools/fgt/cmdb/firewall/test_address.py | 82 ++++++++++++++++++ tests/tools/fgt/cmdb/firewall/test_addrgrp.py | 56 ++++++++++++ .../fgt/cmdb/firewall/test_service_custom.py | 85 +++++++++++++++++++ .../fgt/cmdb/firewall/test_service_group.py | 56 ++++++++++++ 6 files changed, 285 insertions(+) create mode 100644 tests/tools/fgt/cmdb/__init__.py create mode 100644 tests/tools/fgt/cmdb/firewall/__init__.py create mode 100644 tests/tools/fgt/cmdb/firewall/test_address.py create mode 100644 tests/tools/fgt/cmdb/firewall/test_addrgrp.py create mode 100644 tests/tools/fgt/cmdb/firewall/test_service_custom.py create mode 100644 tests/tools/fgt/cmdb/firewall/test_service_group.py diff --git a/tests/tools/fgt/cmdb/__init__.py b/tests/tools/fgt/cmdb/__init__.py new file mode 100644 index 0000000..60c5496 --- /dev/null +++ b/tests/tools/fgt/cmdb/__init__.py @@ -0,0 +1,3 @@ +""" +Here we test the fgt cmdb utility +""" diff --git a/tests/tools/fgt/cmdb/firewall/__init__.py b/tests/tools/fgt/cmdb/firewall/__init__.py new file mode 100644 index 0000000..862c692 --- /dev/null +++ b/tests/tools/fgt/cmdb/firewall/__init__.py @@ -0,0 +1,3 @@ +""" +Here we test the fgt cmdb firewall utility +""" diff --git a/tests/tools/fgt/cmdb/firewall/test_address.py b/tests/tools/fgt/cmdb/firewall/test_address.py new file mode 100644 index 0000000..689745c --- /dev/null +++ b/tests/tools/fgt/cmdb/firewall/test_address.py @@ -0,0 +1,82 @@ +""" +Test fgt cmdb firewall address +""" + +# pylint: disable=no-member +# mypy: disable-error-code=attr-defined +from pathlib import Path +from typing import Any +from unittest.mock import MagicMock + +import pytest +from _pytest.monkeypatch import MonkeyPatch + +import fotoobo +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_address + + +@pytest.fixture(autouse=True) +def inventory_file(monkeypatch: MonkeyPatch) -> None: + """Change inventory file in config to test inventory""" + monkeypatch.setattr( + "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") + ) + + +def test_get_cmdb_firewall_address(monkeypatch: MonkeyPatch) -> None: + """Test the get cmdb firewall address method""" + result_mock = Result[list[Any]]() + result_mock.push_result( + "test_fgt_1", + [ + { + "results": [ + { + "name": "dummy_1", + "type": "fqdn", + "fqdn": "dummy.local", + }, + { + "name": "dummy_2", + "type": "geography", + "country": "dummy-country", + }, + { + "name": "dummy_3", + "type": "ipmask", + "subnet": "1.1.1.1 2.2.2.2", + }, + { + "name": "dummy_4", + "type": "iprange", + "start-ip": "1.1.1.1", + "end-ip": "2.2.2.2", + }, + { + "name": "dummy_5", + "type": "dummy-type", + }, + ], + "vdom": "vdom_1", + } + ], + ) + monkeypatch.setattr( + "fotoobo.tools.fgt.cmdb.firewall.address.api_get", MagicMock(return_value=result_mock) + ) + monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) + result = get_cmdb_firewall_address("test_fgt_1", "", "", "test.json") + data = result.get_result("test_fgt_1") + assert len(data) == 5 + assert data[0]["content"] == "dummy.local" + assert data[1]["content"] == "dummy-country" + assert data[2]["content"] == "1.1.1.1/2.2.2.2" + assert data[3]["content"] == "1.1.1.1 - 2.2.2.2" + assert data[4]["content"] == "" + fotoobo.tools.fgt.cmdb.firewall.address.api_get.assert_called_with( + host="test_fgt_1", vdom="", url="/cmdb/firewall/address/" + ) + fotoobo.helpers.result.Result.save_raw.assert_called_with( + file=Path("test.json"), key="test_fgt_1" + ) diff --git a/tests/tools/fgt/cmdb/firewall/test_addrgrp.py b/tests/tools/fgt/cmdb/firewall/test_addrgrp.py new file mode 100644 index 0000000..8094d73 --- /dev/null +++ b/tests/tools/fgt/cmdb/firewall/test_addrgrp.py @@ -0,0 +1,56 @@ +""" +Test fgt cmdb firewall address group +""" + +# pylint: disable=no-member +# mypy: disable-error-code=attr-defined +from pathlib import Path +from typing import Any +from unittest.mock import MagicMock + +import pytest +from _pytest.monkeypatch import MonkeyPatch + +import fotoobo +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_addrgrp + + +@pytest.fixture(autouse=True) +def inventory_file(monkeypatch: MonkeyPatch) -> None: + """Change inventory file in config to test inventory""" + monkeypatch.setattr( + "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") + ) + + +def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: + """Test the get cmdb firewall address group method""" + result_mock = Result[list[Any]]() + result_mock.push_result( + "test_fgt_1", + [ + { + "results": [ + {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, + {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, + ], + "vdom": "vdom_1", + } + ], + ) + monkeypatch.setattr( + "fotoobo.tools.fgt.cmdb.firewall.addrgrp.api_get", MagicMock(return_value=result_mock) + ) + monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) + result = get_cmdb_firewall_addrgrp("test_fgt_1", "", "", "test.json") + data = result.get_result("test_fgt_1") + assert len(data) == 2 + assert data[0]["content"] == "member_1\nmember_2" + assert data[1]["content"] == "member_3\nmember_4" + fotoobo.tools.fgt.cmdb.firewall.addrgrp.api_get.assert_called_with( + host="test_fgt_1", vdom="", url="/cmdb/firewall/addrgrp/" + ) + fotoobo.helpers.result.Result.save_raw.assert_called_with( + file=Path("test.json"), key="test_fgt_1" + ) diff --git a/tests/tools/fgt/cmdb/firewall/test_service_custom.py b/tests/tools/fgt/cmdb/firewall/test_service_custom.py new file mode 100644 index 0000000..084c015 --- /dev/null +++ b/tests/tools/fgt/cmdb/firewall/test_service_custom.py @@ -0,0 +1,85 @@ +""" +Test fgt cmdb firewall service custom +""" + +# pylint: disable=no-member +# mypy: disable-error-code=attr-defined +from pathlib import Path +from typing import Any +from unittest.mock import MagicMock + +import pytest +from _pytest.monkeypatch import MonkeyPatch + +import fotoobo +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_service_custom + + +@pytest.fixture(autouse=True) +def inventory_file(monkeypatch: MonkeyPatch) -> None: + """Change inventory file in config to test inventory""" + monkeypatch.setattr( + "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") + ) + + +def test_get_cmdb_firewall_service_custom(monkeypatch: MonkeyPatch) -> None: + """Test the get cmdb firewall service custom method""" + result_mock = Result[list[Any]]() + result_mock.push_result( + "test_fgt_1", + [ + { + "results": [ + { + "name": "dummy_1", + "protocol": "TCP/UDP/SCTP", + "tcp-portrange": "88", + "udp-portrange": "99", + }, + { + "name": "dummy_2", + "protocol": "ICMP", + "icmptype": "8", + "icmpcode": "9", + }, + { + "name": "dummy_3", + "protocol": "ICMP6", + "icmptype": "8", + "icmpcode": "9", + }, + { + "name": "dummy_4", + "protocol": "IP", + "protocol-number": "89", + }, + { + "name": "dummy_5", + "protocol": "dummy-type", + }, + ], + "vdom": "vdom_1", + } + ], + ) + monkeypatch.setattr( + "fotoobo.tools.fgt.cmdb.firewall.service_custom.api_get", + MagicMock(return_value=result_mock), + ) + monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) + result = get_cmdb_firewall_service_custom("test_fgt_1", "", "", "test.json") + data = result.get_result("test_fgt_1") + assert len(data) == 5 + assert data[0]["data_1"] == "88" + assert data[1]["data_1"] == "8" + assert data[2]["data_1"] == "8" + assert data[3]["data_1"] == "89" + assert data[4]["data_1"] == "" + fotoobo.tools.fgt.cmdb.firewall.service_custom.api_get.assert_called_with( + host="test_fgt_1", vdom="", url="/cmdb/firewall.service/custom/" + ) + fotoobo.helpers.result.Result.save_raw.assert_called_with( + file=Path("test.json"), key="test_fgt_1" + ) diff --git a/tests/tools/fgt/cmdb/firewall/test_service_group.py b/tests/tools/fgt/cmdb/firewall/test_service_group.py new file mode 100644 index 0000000..1937356 --- /dev/null +++ b/tests/tools/fgt/cmdb/firewall/test_service_group.py @@ -0,0 +1,56 @@ +""" +Test fgt cmdb firewall service group +""" + +# pylint: disable=no-member +# mypy: disable-error-code=attr-defined +from pathlib import Path +from typing import Any +from unittest.mock import MagicMock + +import pytest +from _pytest.monkeypatch import MonkeyPatch + +import fotoobo +from fotoobo.helpers.result import Result +from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_service_group + + +@pytest.fixture(autouse=True) +def inventory_file(monkeypatch: MonkeyPatch) -> None: + """Change inventory file in config to test inventory""" + monkeypatch.setattr( + "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") + ) + + +def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: + """Test the get cmdb firewall service group method""" + result_mock = Result[list[Any]]() + result_mock.push_result( + "test_fgt_1", + [ + { + "results": [ + {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, + {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, + ], + "vdom": "vdom_1", + } + ], + ) + monkeypatch.setattr( + "fotoobo.tools.fgt.cmdb.firewall.service_group.api_get", MagicMock(return_value=result_mock) + ) + monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) + result = get_cmdb_firewall_service_group("test_fgt_1", "", "", "test.json") + data = result.get_result("test_fgt_1") + assert len(data) == 2 + assert data[0]["content"] == "member_1\nmember_2" + assert data[1]["content"] == "member_3\nmember_4" + fotoobo.tools.fgt.cmdb.firewall.service_group.api_get.assert_called_with( + host="test_fgt_1", vdom="", url="/cmdb/firewall/addrgrp/" + ) + fotoobo.helpers.result.Result.save_raw.assert_called_with( + file=Path("test.json"), key="test_fgt_1" + ) From 3e7448bfc09475e1d284dde5f1920c3c28a3ca47 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 14:24:49 +0200 Subject: [PATCH 18/20] =?UTF-8?q?=E2=9C=85=20Add=20test=20for=20tools.fgt.?= =?UTF-8?q?get.api=5Fget()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/tools/fgt/get/test_api_get.py | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/tools/fgt/get/test_api_get.py diff --git a/tests/tools/fgt/get/test_api_get.py b/tests/tools/fgt/get/test_api_get.py new file mode 100644 index 0000000..55f5e40 --- /dev/null +++ b/tests/tools/fgt/get/test_api_get.py @@ -0,0 +1,38 @@ +""" +Test fgt tools api_get +""" + +# pylint: disable=no-member +# mypy: disable-error-code=attr-defined +from pathlib import Path +from unittest.mock import MagicMock + +import pytest +from _pytest.monkeypatch import MonkeyPatch + +import fotoobo +from fotoobo.tools.fgt.get import api_get +from tests.helper import ResponseMock + + +@pytest.fixture(autouse=True) +def inventory_file(monkeypatch: MonkeyPatch) -> None: + """Change inventory file in config to test inventory""" + monkeypatch.setattr( + "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") + ) + + +def test_api_get(monkeypatch: MonkeyPatch) -> None: + """Test api_get method""" + response_mock = ResponseMock(json=[{"http_method": "GET", "results": []}], status_code=200) + monkeypatch.setattr( + "fotoobo.fortinet.fortigate.FortiGate.api", MagicMock(return_value=response_mock) + ) + result = api_get("test_fgt_1", "/test/dummy/fake") + data = result.get_result("test_fgt_1") + assert data + assert data == [{"http_method": "GET", "results": []}] + fotoobo.fortinet.fortigate.FortiGate.api.assert_called_with( + method="get", url="/test/dummy/fake", params={"vdom": "*"}, timeout=None + ) From bae463e6642db619b0ccedd0e004d4a32b87f142 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Tue, 22 Oct 2024 14:30:10 +0200 Subject: [PATCH 19/20] =?UTF-8?q?=F0=9F=93=9D=20Update=20WHATSNEW?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WHATSNEW.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WHATSNEW.md b/WHATSNEW.md index 8f5a177..820c4a1 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -1,7 +1,10 @@ ### Added -- Add CLI command fgt get cmdb firewall addrgrp +- Add generic api_get() method in tools/fgt/get - Add CLI command fgt get cmdb firewall address +- Add CLI command fgt get cmdb firewall addrgrp +- Add CLI command fgt get cmdb firewall service-custom +- Add CLI command fgt get cmdb firewall service-group ### Changed From ff66437c4af0102fd3688c5c91e6ac058194b5b0 Mon Sep 17 00:00:00 2001 From: Patrik Spiess Date: Mon, 28 Oct 2024 16:26:55 +0100 Subject: [PATCH 20/20] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20the=20fortiga?= =?UTF-8?q?te=20api=5Fget=20method=20to=20a=20low=20level=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fotoobo/fortinet/fortigate.py | 26 +++++- fotoobo/tools/fgt/cmdb/firewall/address.py | 19 +++-- fotoobo/tools/fgt/cmdb/firewall/addrgrp.py | 18 +++-- .../tools/fgt/cmdb/firewall/service_custom.py | 18 +++-- .../tools/fgt/cmdb/firewall/service_group.py | 18 +++-- fotoobo/tools/fgt/get.py | 45 ++++++----- tests/fortinet/test_fortigate.py | 13 +++ tests/tools/fgt/cmdb/firewall/test_address.py | 76 ++++++++---------- tests/tools/fgt/cmdb/firewall/test_addrgrp.py | 30 +++---- .../fgt/cmdb/firewall/test_service_custom.py | 80 +++++++++---------- .../fgt/cmdb/firewall/test_service_group.py | 30 +++---- tests/tools/fgt/get/test_api_get.py | 20 ----- 12 files changed, 205 insertions(+), 188 deletions(-) diff --git a/fotoobo/fortinet/fortigate.py b/fotoobo/fortinet/fortigate.py index 91be6c4..957fffd 100644 --- a/fotoobo/fortinet/fortigate.py +++ b/fotoobo/fortinet/fortigate.py @@ -50,8 +50,8 @@ def api( # pylint: disable=too-many-arguments payload: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, ) -> requests.models.Response: - """ - API request to a FortiGate device. + """Native API request to a FortiGate. + It uses the super.api method but it has to enrich the payload in post requests with the needed session key. @@ -70,6 +70,28 @@ def api( # pylint: disable=too-many-arguments method, url, payload=payload, params=params, timeout=timeout, headers=headers ) + def api_get(self, url: str, vdom: str = "*", timeout: Optional[float] = None) -> list[Any]: + """Low level GET request to a FortiGate. + + This gets the response from a single API request to a FortiGate and returns it as a fotoobo + Results object. + + Args: + url: The API endpoint to access + vdom: The VDOM to access ("vdom1" or "vdom1,vdom2" or "*") + timeout: The time to wait for a response from the FortiGate + + Returns: + The Result object with all the results as list (even if only one result is returned) + """ + params = {"vdom": vdom} + response = self.api(method="get", url=url, params=params, timeout=timeout) + data: list[Any] = ( + [response.json()] if isinstance(response.json(), dict) else response.json() + ) # this is to listify the data from the response + + return data + def backup(self, timeout: int = 10) -> str: """ Get the configuration backup from a FortiGate. diff --git a/fotoobo/tools/fgt/cmdb/firewall/address.py b/fotoobo/tools/fgt/cmdb/firewall/address.py index a23d281..2af439a 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/address.py +++ b/fotoobo/tools/fgt/cmdb/firewall/address.py @@ -3,8 +3,10 @@ from pathlib import Path from typing import Any +from fotoobo.fortinet.fortigate import FortiGate +from fotoobo.helpers.config import config from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api_get +from fotoobo.inventory import Inventory def get_cmdb_firewall_address( @@ -14,15 +16,20 @@ def get_cmdb_firewall_address( The FortiGate api endpoint is: /cmdb/firewall/address """ - result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/address/{name}") + inventory = Inventory(config.inventory_file) + fgt: FortiGate = inventory.get_item(host, "fortigate") + result = Result[list[Any]]() + + address_list = fgt.api_get(url=f"/cmdb/firewall/address/{name}", vdom=vdom) + result.push_result(key=host, data=address_list) if output_file: - result_raw.save_raw(file=Path(output_file), key=host) + result.push_result(key=host, data=address_list) + result.save_raw(file=Path(output_file), key=host) - result = Result[list[Any]]() assets = [] - if result_raw.get_result(host): - for vd in result_raw.get_result(host): + if address_list: + for vd in address_list: for asset in vd["results"]: data: dict[str, str] = { diff --git a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py index 7d881cd..d94465f 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py +++ b/fotoobo/tools/fgt/cmdb/firewall/addrgrp.py @@ -3,8 +3,10 @@ from pathlib import Path from typing import Any +from fotoobo.fortinet.fortigate import FortiGate +from fotoobo.helpers.config import config from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api_get +from fotoobo.inventory import Inventory def get_cmdb_firewall_addrgrp( @@ -14,15 +16,19 @@ def get_cmdb_firewall_addrgrp( The FortiGate api endpoint is: /cmdb/firewall/addrgrp """ - result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + inventory = Inventory(config.inventory_file) + fgt: FortiGate = inventory.get_item(host, "fortigate") + result = Result[list[Any]]() + + addrgrp_list = fgt.api_get(url=f"/cmdb/firewall/addrgrp/{name}", vdom=vdom) if output_file: - result_raw.save_raw(file=Path(output_file), key=host) + result.push_result(key=host, data=addrgrp_list) + result.save_raw(file=Path(output_file), key=host) - result = Result[list[Any]]() assets = [] - if result_raw.get_result(host): - for vd in result_raw.get_result(host): + if addrgrp_list: + for vd in addrgrp_list: for asset in vd["results"]: # print(asset) data: dict[str, str] = { diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py index 2d6723b..3a08467 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_custom.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_custom.py @@ -3,8 +3,10 @@ from pathlib import Path from typing import Any +from fotoobo.fortinet.fortigate import FortiGate +from fotoobo.helpers.config import config from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api_get +from fotoobo.inventory import Inventory def get_cmdb_firewall_service_custom( @@ -14,15 +16,19 @@ def get_cmdb_firewall_service_custom( The FortiGate api endpoint is: /cmdb/firewall.service/custom """ - result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall.service/custom/{name}") + inventory = Inventory(config.inventory_file) + fgt: FortiGate = inventory.get_item(host, "fortigate") + result = Result[list[Any]]() + + service_custom_list = fgt.api_get(url=f"/cmdb/firewall.service/custom/{name}", vdom=vdom) if output_file: - result_raw.save_raw(file=Path(output_file), key=host) + result.push_result(key=host, data=service_custom_list) + result.save_raw(file=Path(output_file), key=host) - result = Result[list[Any]]() assets = [] - if result_raw.get_result(host): - for vd in result_raw.get_result(host): + if service_custom_list: + for vd in service_custom_list: for asset in vd["results"]: data: dict[str, str] = { diff --git a/fotoobo/tools/fgt/cmdb/firewall/service_group.py b/fotoobo/tools/fgt/cmdb/firewall/service_group.py index 81f3453..24a8cc8 100644 --- a/fotoobo/tools/fgt/cmdb/firewall/service_group.py +++ b/fotoobo/tools/fgt/cmdb/firewall/service_group.py @@ -3,8 +3,10 @@ from pathlib import Path from typing import Any +from fotoobo.fortinet.fortigate import FortiGate +from fotoobo.helpers.config import config from fotoobo.helpers.result import Result -from fotoobo.tools.fgt.get import api_get +from fotoobo.inventory import Inventory def get_cmdb_firewall_service_group( @@ -14,15 +16,19 @@ def get_cmdb_firewall_service_group( The FortiGate api endpoint is: /cmdb/firewall.service/group """ - result_raw = api_get(host=host, vdom=vdom, url=f"/cmdb/firewall/addrgrp/{name}") + inventory = Inventory(config.inventory_file) + fgt: FortiGate = inventory.get_item(host, "fortigate") + result = Result[list[Any]]() + + service_group_list = fgt.api_get(url=f"/cmdb/firewall/addrgrp/{name}", vdom=vdom) if output_file: - result_raw.save_raw(file=Path(output_file), key=host) + result.push_result(key=host, data=service_group_list) + result.save_raw(file=Path(output_file), key=host) - result = Result[list[Any]]() assets = [] - if result_raw.get_result(host): - for vd in result_raw.get_result(host): + if service_group_list: + for vd in service_group_list: for asset in vd["results"]: data: dict[str, str] = { "name": asset["name"], diff --git a/fotoobo/tools/fgt/get.py b/fotoobo/tools/fgt/get.py index 65752a8..fdb7140 100644 --- a/fotoobo/tools/fgt/get.py +++ b/fotoobo/tools/fgt/get.py @@ -4,7 +4,7 @@ import concurrent.futures import logging -from typing import Any, Optional, Tuple +from typing import Optional, Tuple from rich.progress import Progress @@ -17,31 +17,32 @@ log = logging.getLogger("fotoobo") -def api_get( - host: str, url: str = "", vdom: str = "*", timeout: Optional[float] = None -) -> Result[list[Any]]: - """Native GET request to a FortiGate. +# def api_get( +# host: str, url: str = "", vdom: str = "*", timeout: Optional[float] = None +# ) -> Result[list[Any]]: +# """Native GET request to a FortiGate. - This gets the response from a single API request to a FortiGate and returns it as a fotoobo - Results object. +# This gets the response from a single API request to a FortiGate and returns it as a fotoobo +# Results object. - Args: - host: The host from the inventory to send the GET requests to - url: The API endpoint to access - vdom: The VDOM to access ("vdom1" or "vdom1,vdom2" or "*") +# Args: +# host: The host from the inventory to send the GET requests to +# url: The API endpoint to access +# vdom: The VDOM to access ("vdom1" or "vdom1,vdom2" or "*") - Returns: - The Result object with all the results as list (even if only one result is returned) - """ - inventory = Inventory(config.inventory_file) - fgt: FortiGate = inventory.get_item(host, "fortigate") - result = Result[list[Any]]() - params = {"vdom": vdom} - response = fgt.api(method="get", url=url, params=params, timeout=timeout) - data = [response.json()] if isinstance(response.json(), dict) else response.json() # listify - result.push_result(host, data=data) +# Returns: +# The Result object with all the results as list (even if only one result is returned) +# """ +# inventory = Inventory(config.inventory_file) +# fgt: FortiGate = inventory.get_item(host, "fortigate") +# result = Result[list[Any]]() - return result +# params = {"vdom": vdom} +# response = fgt.api(method="get", url=url, params=params, timeout=timeout) +# data = [response.json()] if isinstance(response.json(), dict) else response.json() # listify +# result.push_result(host, data=data) + +# return result def version(host: Optional[str] = None) -> Result[str]: diff --git a/tests/fortinet/test_fortigate.py b/tests/fortinet/test_fortigate.py index 6c5f792..f779803 100644 --- a/tests/fortinet/test_fortigate.py +++ b/tests/fortinet/test_fortigate.py @@ -44,6 +44,19 @@ def test_api(monkeypatch: MonkeyPatch) -> None: "get", "dummy", payload=None, params=None, timeout=None, headers=None ) + def test_api_get(self, monkeypatch: MonkeyPatch) -> None: + """Test the FortiGate api_get method""" + response_mock = ResponseMock(json=[{"http_method": "GET", "results": []}], status_code=200) + monkeypatch.setattr( + "fotoobo.fortinet.fortigate.FortiGate.api", MagicMock(return_value=response_mock) + ) + fortigate = FortiGate("dummy_hostname", "token") + result = fortigate.api_get("/test/dummy/fake") + assert result == [{"http_method": "GET", "results": []}] + FortiGate.api.assert_called_with( + method="get", url="/test/dummy/fake", params={"vdom": "*"}, timeout=None + ) + @staticmethod def test_backup(monkeypatch: MonkeyPatch) -> None: """Test the FortiGate backup method""" diff --git a/tests/tools/fgt/cmdb/firewall/test_address.py b/tests/tools/fgt/cmdb/firewall/test_address.py index 689745c..8ecb186 100644 --- a/tests/tools/fgt/cmdb/firewall/test_address.py +++ b/tests/tools/fgt/cmdb/firewall/test_address.py @@ -5,14 +5,12 @@ # pylint: disable=no-member # mypy: disable-error-code=attr-defined from pathlib import Path -from typing import Any from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch import fotoobo -from fotoobo.helpers.result import Result from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_address @@ -26,44 +24,40 @@ def inventory_file(monkeypatch: MonkeyPatch) -> None: def test_get_cmdb_firewall_address(monkeypatch: MonkeyPatch) -> None: """Test the get cmdb firewall address method""" - result_mock = Result[list[Any]]() - result_mock.push_result( - "test_fgt_1", - [ - { - "results": [ - { - "name": "dummy_1", - "type": "fqdn", - "fqdn": "dummy.local", - }, - { - "name": "dummy_2", - "type": "geography", - "country": "dummy-country", - }, - { - "name": "dummy_3", - "type": "ipmask", - "subnet": "1.1.1.1 2.2.2.2", - }, - { - "name": "dummy_4", - "type": "iprange", - "start-ip": "1.1.1.1", - "end-ip": "2.2.2.2", - }, - { - "name": "dummy_5", - "type": "dummy-type", - }, - ], - "vdom": "vdom_1", - } - ], - ) + result_mock = [ + { + "results": [ + { + "name": "dummy_1", + "type": "fqdn", + "fqdn": "dummy.local", + }, + { + "name": "dummy_2", + "type": "geography", + "country": "dummy-country", + }, + { + "name": "dummy_3", + "type": "ipmask", + "subnet": "1.1.1.1 2.2.2.2", + }, + { + "name": "dummy_4", + "type": "iprange", + "start-ip": "1.1.1.1", + "end-ip": "2.2.2.2", + }, + { + "name": "dummy_5", + "type": "dummy-type", + }, + ], + "vdom": "vdom_1", + } + ] monkeypatch.setattr( - "fotoobo.tools.fgt.cmdb.firewall.address.api_get", MagicMock(return_value=result_mock) + "fotoobo.fortinet.fortigate.FortiGate.api_get", MagicMock(return_value=result_mock) ) monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) result = get_cmdb_firewall_address("test_fgt_1", "", "", "test.json") @@ -74,8 +68,8 @@ def test_get_cmdb_firewall_address(monkeypatch: MonkeyPatch) -> None: assert data[2]["content"] == "1.1.1.1/2.2.2.2" assert data[3]["content"] == "1.1.1.1 - 2.2.2.2" assert data[4]["content"] == "" - fotoobo.tools.fgt.cmdb.firewall.address.api_get.assert_called_with( - host="test_fgt_1", vdom="", url="/cmdb/firewall/address/" + fotoobo.fortinet.fortigate.FortiGate.api_get.assert_called_with( + url="/cmdb/firewall/address/", vdom="" ) fotoobo.helpers.result.Result.save_raw.assert_called_with( file=Path("test.json"), key="test_fgt_1" diff --git a/tests/tools/fgt/cmdb/firewall/test_addrgrp.py b/tests/tools/fgt/cmdb/firewall/test_addrgrp.py index 8094d73..bb8d755 100644 --- a/tests/tools/fgt/cmdb/firewall/test_addrgrp.py +++ b/tests/tools/fgt/cmdb/firewall/test_addrgrp.py @@ -5,14 +5,12 @@ # pylint: disable=no-member # mypy: disable-error-code=attr-defined from pathlib import Path -from typing import Any from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch import fotoobo -from fotoobo.helpers.result import Result from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_addrgrp @@ -26,21 +24,17 @@ def inventory_file(monkeypatch: MonkeyPatch) -> None: def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: """Test the get cmdb firewall address group method""" - result_mock = Result[list[Any]]() - result_mock.push_result( - "test_fgt_1", - [ - { - "results": [ - {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, - {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, - ], - "vdom": "vdom_1", - } - ], - ) + result_mock = [ + { + "results": [ + {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, + {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, + ], + "vdom": "vdom_1", + } + ] monkeypatch.setattr( - "fotoobo.tools.fgt.cmdb.firewall.addrgrp.api_get", MagicMock(return_value=result_mock) + "fotoobo.fortinet.fortigate.FortiGate.api_get", MagicMock(return_value=result_mock) ) monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) result = get_cmdb_firewall_addrgrp("test_fgt_1", "", "", "test.json") @@ -48,8 +42,8 @@ def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: assert len(data) == 2 assert data[0]["content"] == "member_1\nmember_2" assert data[1]["content"] == "member_3\nmember_4" - fotoobo.tools.fgt.cmdb.firewall.addrgrp.api_get.assert_called_with( - host="test_fgt_1", vdom="", url="/cmdb/firewall/addrgrp/" + fotoobo.fortinet.fortigate.FortiGate.api_get.assert_called_with( + url="/cmdb/firewall/addrgrp/", vdom="" ) fotoobo.helpers.result.Result.save_raw.assert_called_with( file=Path("test.json"), key="test_fgt_1" diff --git a/tests/tools/fgt/cmdb/firewall/test_service_custom.py b/tests/tools/fgt/cmdb/firewall/test_service_custom.py index 084c015..ff58bac 100644 --- a/tests/tools/fgt/cmdb/firewall/test_service_custom.py +++ b/tests/tools/fgt/cmdb/firewall/test_service_custom.py @@ -5,14 +5,12 @@ # pylint: disable=no-member # mypy: disable-error-code=attr-defined from pathlib import Path -from typing import Any from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch import fotoobo -from fotoobo.helpers.result import Result from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_service_custom @@ -26,46 +24,42 @@ def inventory_file(monkeypatch: MonkeyPatch) -> None: def test_get_cmdb_firewall_service_custom(monkeypatch: MonkeyPatch) -> None: """Test the get cmdb firewall service custom method""" - result_mock = Result[list[Any]]() - result_mock.push_result( - "test_fgt_1", - [ - { - "results": [ - { - "name": "dummy_1", - "protocol": "TCP/UDP/SCTP", - "tcp-portrange": "88", - "udp-portrange": "99", - }, - { - "name": "dummy_2", - "protocol": "ICMP", - "icmptype": "8", - "icmpcode": "9", - }, - { - "name": "dummy_3", - "protocol": "ICMP6", - "icmptype": "8", - "icmpcode": "9", - }, - { - "name": "dummy_4", - "protocol": "IP", - "protocol-number": "89", - }, - { - "name": "dummy_5", - "protocol": "dummy-type", - }, - ], - "vdom": "vdom_1", - } - ], - ) + result_mock = [ + { + "results": [ + { + "name": "dummy_1", + "protocol": "TCP/UDP/SCTP", + "tcp-portrange": "88", + "udp-portrange": "99", + }, + { + "name": "dummy_2", + "protocol": "ICMP", + "icmptype": "8", + "icmpcode": "9", + }, + { + "name": "dummy_3", + "protocol": "ICMP6", + "icmptype": "8", + "icmpcode": "9", + }, + { + "name": "dummy_4", + "protocol": "IP", + "protocol-number": "89", + }, + { + "name": "dummy_5", + "protocol": "dummy-type", + }, + ], + "vdom": "vdom_1", + } + ] monkeypatch.setattr( - "fotoobo.tools.fgt.cmdb.firewall.service_custom.api_get", + "fotoobo.fortinet.fortigate.FortiGate.api_get", MagicMock(return_value=result_mock), ) monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) @@ -77,8 +71,8 @@ def test_get_cmdb_firewall_service_custom(monkeypatch: MonkeyPatch) -> None: assert data[2]["data_1"] == "8" assert data[3]["data_1"] == "89" assert data[4]["data_1"] == "" - fotoobo.tools.fgt.cmdb.firewall.service_custom.api_get.assert_called_with( - host="test_fgt_1", vdom="", url="/cmdb/firewall.service/custom/" + fotoobo.fortinet.fortigate.FortiGate.api_get.assert_called_with( + url="/cmdb/firewall.service/custom/", vdom="" ) fotoobo.helpers.result.Result.save_raw.assert_called_with( file=Path("test.json"), key="test_fgt_1" diff --git a/tests/tools/fgt/cmdb/firewall/test_service_group.py b/tests/tools/fgt/cmdb/firewall/test_service_group.py index 1937356..5328c64 100644 --- a/tests/tools/fgt/cmdb/firewall/test_service_group.py +++ b/tests/tools/fgt/cmdb/firewall/test_service_group.py @@ -5,14 +5,12 @@ # pylint: disable=no-member # mypy: disable-error-code=attr-defined from pathlib import Path -from typing import Any from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch import fotoobo -from fotoobo.helpers.result import Result from fotoobo.tools.fgt.cmdb.firewall import get_cmdb_firewall_service_group @@ -26,21 +24,17 @@ def inventory_file(monkeypatch: MonkeyPatch) -> None: def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: """Test the get cmdb firewall service group method""" - result_mock = Result[list[Any]]() - result_mock.push_result( - "test_fgt_1", - [ - { - "results": [ - {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, - {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, - ], - "vdom": "vdom_1", - } - ], - ) + result_mock = [ + { + "results": [ + {"name": "group_1", "member": [{"name": "member_1"}, {"name": "member_2"}]}, + {"name": "group_2", "member": [{"name": "member_3"}, {"name": "member_4"}]}, + ], + "vdom": "vdom_1", + } + ] monkeypatch.setattr( - "fotoobo.tools.fgt.cmdb.firewall.service_group.api_get", MagicMock(return_value=result_mock) + "fotoobo.fortinet.fortigate.FortiGate.api_get", MagicMock(return_value=result_mock) ) monkeypatch.setattr("fotoobo.helpers.result.Result.save_raw", MagicMock(return_value=True)) result = get_cmdb_firewall_service_group("test_fgt_1", "", "", "test.json") @@ -48,8 +42,8 @@ def test_get_cmdb_firewall_addrgrp(monkeypatch: MonkeyPatch) -> None: assert len(data) == 2 assert data[0]["content"] == "member_1\nmember_2" assert data[1]["content"] == "member_3\nmember_4" - fotoobo.tools.fgt.cmdb.firewall.service_group.api_get.assert_called_with( - host="test_fgt_1", vdom="", url="/cmdb/firewall/addrgrp/" + fotoobo.fortinet.fortigate.FortiGate.api_get.assert_called_with( + url="/cmdb/firewall/addrgrp/", vdom="" ) fotoobo.helpers.result.Result.save_raw.assert_called_with( file=Path("test.json"), key="test_fgt_1" diff --git a/tests/tools/fgt/get/test_api_get.py b/tests/tools/fgt/get/test_api_get.py index 55f5e40..2bd9a3e 100644 --- a/tests/tools/fgt/get/test_api_get.py +++ b/tests/tools/fgt/get/test_api_get.py @@ -5,15 +5,10 @@ # pylint: disable=no-member # mypy: disable-error-code=attr-defined from pathlib import Path -from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -import fotoobo -from fotoobo.tools.fgt.get import api_get -from tests.helper import ResponseMock - @pytest.fixture(autouse=True) def inventory_file(monkeypatch: MonkeyPatch) -> None: @@ -21,18 +16,3 @@ def inventory_file(monkeypatch: MonkeyPatch) -> None: monkeypatch.setattr( "fotoobo.helpers.config.config.inventory_file", Path("tests/data/inventory.yaml") ) - - -def test_api_get(monkeypatch: MonkeyPatch) -> None: - """Test api_get method""" - response_mock = ResponseMock(json=[{"http_method": "GET", "results": []}], status_code=200) - monkeypatch.setattr( - "fotoobo.fortinet.fortigate.FortiGate.api", MagicMock(return_value=response_mock) - ) - result = api_get("test_fgt_1", "/test/dummy/fake") - data = result.get_result("test_fgt_1") - assert data - assert data == [{"http_method": "GET", "results": []}] - fotoobo.fortinet.fortigate.FortiGate.api.assert_called_with( - method="get", url="/test/dummy/fake", params={"vdom": "*"}, timeout=None - )