diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80eb9a6f4d025d393e67a6ab30ea2c646d1f8a5d..05fe9af7cf0863703bde6341001f27280b339b05 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,7 +15,7 @@ run-tox-pipeline: image: python:3.12 services: - - postgres:15.4 + - postgres:14 # Change pip's cache directory to be inside the project directory since we can # only cache local items. diff --git a/Changelog.md b/Changelog.md index a5131efa7c62f51f243fb36b935dd50ee8028323..4eea690ff4933b4dbe3b6c6a59754217f9252c5f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Changelog +## [2.24] - 2024-11-07 +- Add support for Moodi dashboard in Edge Port creation workflow +- Fix a bug where ISIS metric restoration did not work correctly + ## [2.23] - 2024-11-05 - Added new workflows and updated the products of Swich and LAN Swith Interconnect - Upgraded orchestrator-core to 2.8.0 diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst index 59b699e5f3bffd1a16fc523d6e08e3c635b1de05..b646c999f9fd9e0d3434ef0386247d881456271c 100644 --- a/docs/source/glossary.rst +++ b/docs/source/glossary.rst @@ -116,3 +116,6 @@ Glossary of terms WFO `Workflow Orchestrator <https://workfloworchestrator.org/>`_ + + Moodi + A service that does monitoring on demand during a workflow execution. \ No newline at end of file diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index 95ad8c021835e62c43107c193da8b2b135ee95b2..63de6f892954672074a3327987572626ed724c16 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -125,6 +125,7 @@ "DSN": "https://sentry-dsn-url" }, "MOODI": { - "host": "moodi.test.gap.geant.org" + "host": "moodi.test.gap.geant.org", + "moodi_enabled": false } } diff --git a/gso/settings.py b/gso/settings.py index 3596eb1cfcd6855d38c077f10f2babb1bd10ade5..eaeeada908de6f8525311da1db7031e394f747da 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -209,6 +209,7 @@ class MoodiParams(BaseSettings): """Settings for Moodi.""" host: str + moodi_enabled: bool = False class OSSParams(BaseSettings): diff --git a/gso/utils/workflow_steps.py b/gso/utils/workflow_steps.py index 6c53f2fcead3f1f3743828f55975197bdb7ac626..6ca9aa80536a01655b81f95bf57ffbe4ff62dbc5 100644 --- a/gso/utils/workflow_steps.py +++ b/gso/utils/workflow_steps.py @@ -7,6 +7,7 @@ from orchestrator import inputstep, step from orchestrator.config.assignee import Assignee from orchestrator.types import State, UUIDstr from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, conditional from pydantic import ConfigDict from pydantic_forms.core import FormPage from pydantic_forms.types import FormGenerator @@ -14,7 +15,7 @@ from pydantic_forms.validators import Label from gso.products.product_blocks.router import RouterRole from gso.products.product_types.iptrunk import Iptrunk -from gso.services.lso_client import LSOState +from gso.services.lso_client import LSOState, anonymous_lso_interaction from gso.settings import load_oss_params from gso.utils.helpers import generate_inventory_for_active_routers from gso.utils.shared_enums import Vendor @@ -391,25 +392,34 @@ def prompt_sharepoint_checklist_url(checklist_url: str) -> FormGenerator: return {} -@step("Start Moodi") -def start_moodi(subscription: dict[str, Any]) -> LSOState: - """Start monitoring on demand using Moodi Telemetry stack.""" - params = load_oss_params() +_is_moodi_enabled = conditional(lambda _: load_oss_params().MOODI.moodi_enabled) - return { - "playbook_name": "moodi_telemetry/playbooks/start_moodi.yaml", - "inventory": {"all": {"hosts": {params.MOODI.host: None}}}, - "extra_vars": {"subscription": subscription}, - } +def start_moodi() -> StepList: + """Start monitoring on demand using :term:`Moodi` Telemetry stack.""" + host = load_oss_params().MOODI.host -@step("Stop Moodi") -def stop_moodi() -> LSOState: - """Stop monitoring on demand.""" - params = load_oss_params() + @step("Start Moodi") + def _start_moodi(subscription: dict[str, Any]) -> LSOState: + return { + "playbook_name": "moodi_telemetry/playbooks/start_moodi.yaml", + "inventory": {"all": {"hosts": {host: None}}}, + "extra_vars": {"subscription": subscription}, + } - return { - "playbook_name": "moodi_telemetry/playbooks/stop_moodi.yaml", - "inventory": {"all": {"hosts": {params.MOODI.host: None}}}, - "extra_vars": None, - } + return _is_moodi_enabled(anonymous_lso_interaction(_start_moodi)) + + +def stop_moodi() -> StepList: + """Stop :term:`Moodi` Telemetry monitoring on demand.""" + host = load_oss_params().MOODI.host + + @step("Stop Moodi") + def _stop_moodi() -> LSOState: + return { + "playbook_name": "moodi_telemetry/playbooks/stop_moodi.yaml", + "inventory": {"all": {"hosts": {host: None}}}, + "extra_vars": None, + } + + return _is_moodi_enabled(anonymous_lso_interaction(_stop_moodi)) diff --git a/gso/workflows/edge_port/create_edge_port.py b/gso/workflows/edge_port/create_edge_port.py index 5977206dfaa57ce4bf271c69464e440aa0fdc722..3782800b426fc0b4f08fd70a6dfa2a72947c11fb 100644 --- a/gso/workflows/edge_port/create_edge_port.py +++ b/gso/workflows/edge_port/create_edge_port.py @@ -31,6 +31,7 @@ from gso.utils.helpers import ( ) from gso.utils.types.interfaces import LAGMember, PhysicalPortCapacity from gso.utils.types.tt_number import TTNumber +from gso.utils.workflow_steps import start_moodi, stop_moodi from gso.workflows.shared import create_summary_form @@ -267,11 +268,13 @@ def create_edge_port() -> StepList: >> create_subscription >> store_process_subscription(Target.CREATE) >> initialize_subscription + >> start_moodi() >> reserve_interfaces_in_netbox >> lso_interaction(create_edge_port_dry) >> lso_interaction(create_edge_port_real) >> allocate_interfaces_in_netbox >> set_status(SubscriptionLifecycle.ACTIVE) >> resync + >> stop_moodi() >> done ) diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index 43b43e8c454a2dba2b419149f3dceffe59974025..8c782fa04fca3437fed4185ab3423c5fc6e68285 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -631,6 +631,7 @@ def restore_isis_metric( } return { + "subscription": subscription, "playbook_name": "gap_ansible/playbooks/iptrunks.yaml", "inventory": { "all": { diff --git a/setup.py b/setup.py index bf6eb267a561df5a72e8c986af8d7981bb40a616..d5ad047d11eb6f79bc81be43fd7dce51972152ab 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import find_packages, setup setup( name="geant-service-orchestrator", - version="2.23", + version="2.24", author="GÉANT Orchestration and Automation Team", author_email="goat@geant.org", description="GÉANT Service Orchestrator", diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py index 525ce49bf67833f33eb1f62530efdf40d0bef3ad..74c80af387544fc686ea69451d14ff22d008da01 100644 --- a/test/workflows/iptrunk/test_migrate_iptrunk.py +++ b/test/workflows/iptrunk/test_migrate_iptrunk.py @@ -109,6 +109,7 @@ def interface_lists_are_equal(list1, list2): [UseJuniperSide.NONE, UseJuniperSide.SIDE_A, UseJuniperSide.SIDE_B, UseJuniperSide.SIDE_BOTH], indirect=True, ) +@pytest.mark.parametrize("restore_isis_metric", [True, False]) @pytest.mark.workflow() @patch("gso.services.infoblox.create_host_by_ip") @patch("gso.services.infoblox.delete_host_by_ip") @@ -122,7 +123,7 @@ def interface_lists_are_equal(list1, list2): @patch("gso.services.netbox_client.NetboxClient.free_interface") @patch("gso.services.netbox_client.NetboxClient.delete_interface") @patch("gso.workflows.iptrunk.migrate_iptrunk.SharePointClient") -def test_migrate_iptrunk_success( +def test_migrate_iptrunk_success( # noqa: PLR0915 mock_sharepoint_client, mocked_delete_interface, mocked_free_interface, @@ -136,6 +137,7 @@ def test_migrate_iptrunk_success( mock_delete_host_by_ip, mock_create_host_by_ip, migrate_form_input, + restore_isis_metric, data_config_filename: PathLike, ): # Set up mock return values @@ -150,6 +152,7 @@ def test_migrate_iptrunk_success( mocked_delete_interface.return_value = mocked_netbox.delete_interface() mock_sharepoint_client.return_value = MockedSharePointClient + migrate_form_input[1]["restore_isis_metric"] = restore_isis_metric result, process_stat, step_log = run_workflow("migrate_iptrunk", migrate_form_input) for _ in range(8): @@ -161,10 +164,10 @@ def test_migrate_iptrunk_success( for _ in range(8): result, step_log = assert_lso_interaction_success(result, process_stat, step_log) - assert_suspended(result) - result, step_log = resume_workflow(process_stat, step_log, input_data=USER_CONFIRM_EMPTY_FORM) + if restore_isis_metric: + assert_suspended(result) + result, step_log = resume_workflow(process_stat, step_log, input_data=USER_CONFIRM_EMPTY_FORM) - for _ in range(1): result, step_log = assert_lso_interaction_success(result, process_stat, step_log) # Continue workflow after it has displayed a checklist URL. @@ -178,7 +181,7 @@ def test_migrate_iptrunk_success( subscription = Iptrunk.from_subscription(subscription_id) assert subscription.status == "active" - assert mock_execute_playbook.call_count == 17 + assert mock_execute_playbook.call_count == 17 if restore_isis_metric else 16 assert mock_create_host_by_ip.call_count == 1 assert mock_delete_host_by_ip.call_count == 1 @@ -216,3 +219,4 @@ def test_migrate_iptrunk_success( assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface == new_lag_interface existing_members = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members assert interface_lists_are_equal(new_lag_member_interfaces, existing_members) + assert (subscription.iptrunk.iptrunk_isis_metric == 999999) != restore_isis_metric