diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index b34d7d2c915f8f0472be3feb67b93bc395ee252c..ef69a304e589ea26e3582074fc3890aaec06a2c5 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -69,7 +69,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ] routers = {} - for router_id, router_description in get_active_router_subscriptions(includes=["subscription_id", "description"]): + for router in get_active_router_subscriptions(includes=["subscription_id", "description"]): + router_id = router["subscription_id"] if router_id not in current_routers: current_router_site = Router.from_subscription(router_id).router.router_site.subscription old_side_site = Router.from_subscription(migrate_form_input.replace_side).router.router_site @@ -78,7 +79,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: and current_router_site.subscription_id == old_side_site.owner_subscription_id ): continue - routers[str(router_id)] = router_description + routers[str(router_id)] = router["description"] new_router_enum = Choice("Select a new router", zip(routers.keys(), routers.items())) # type: ignore[arg-type] @@ -403,8 +404,6 @@ def delete_old_config_real( @step("Update IPAM") def update_ipam(subscription: Iptrunk) -> State: - pass - return {"subscription": subscription} diff --git a/test/services/conftest.py b/test/services/conftest.py index 4501963708fd8ebbbed1eceac3ed3fc2283c76ea..282ae9edc8521e77cc6b8688670ad5218fa676c2 100644 --- a/test/services/conftest.py +++ b/test/services/conftest.py @@ -38,3 +38,6 @@ class MockedNetboxClient: def detach_interfaces_from_lag(self): return None + + def delete_interface(self): + return None diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py new file mode 100644 index 0000000000000000000000000000000000000000..89eb44f55cb9ea5776487e760d8335101b3980f4 --- /dev/null +++ b/test/workflows/iptrunk/test_migrate_iptrunk.py @@ -0,0 +1,133 @@ +from unittest.mock import patch + +import pytest + +from gso.products import Iptrunk +from gso.utils.helpers import LAGMember +from test.workflows import ( + assert_complete, + assert_suspended, + extract_state, + resume_workflow, + run_workflow, + user_accept_and_assert_suspended, +) +from test.workflows.iptrunk.test_create_iptrunk import MockedNetboxClient + + +@pytest.mark.workflow +@patch("gso.workflows.iptrunk.migrate_iptrunk.provisioning_proxy.migrate_ip_trunk") +@patch("gso.workflows.iptrunk.migrate_iptrunk.provisioning_proxy.provision_ip_trunk") +@patch("gso.services.netbox_client.NetboxClient.get_device_by_name") +@patch("gso.services.netbox_client.NetboxClient.get_available_interfaces") +@patch("gso.services.netbox_client.NetboxClient.get_available_lags") +@patch("gso.services.netbox_client.NetboxClient.create_interface") +@patch("gso.services.netbox_client.NetboxClient.attach_interface_to_lag") +@patch("gso.services.netbox_client.NetboxClient.reserve_interface") +@patch("gso.services.netbox_client.NetboxClient.allocate_interface") +@patch("gso.services.netbox_client.NetboxClient.free_interface") +@patch("gso.services.netbox_client.NetboxClient.delete_interface") +def test_migrate_iptrunk_success( + mocked_delete_interface, + mocked_free_interface, + mocked_allocate_interface, + mocked_reserve_interface, + mocked_attach_interface_to_lag, + mocked_create_interface, + mocked_get_available_lags, + mocked_get_available_interfaces, + mocked_get_device_by_name, + mock_provision_ip_trunk, + mock_migrate_ip_trunk, + iptrunk_subscription_factory, + router_subscription_factory, + faker, +): + # Set up mock return values + mocked_netbox = MockedNetboxClient() + mocked_get_device_by_name.return_value = mocked_netbox.get_device_by_name() + mocked_get_available_interfaces.return_value = mocked_netbox.get_available_interfaces() + mocked_attach_interface_to_lag.return_value = mocked_netbox.attach_interface_to_lag() + mocked_reserve_interface.return_value = mocked_netbox.reserve_interface() + mocked_allocate_interface.return_value = mocked_netbox.allocate_interface() + mocked_free_interface.return_value = mocked_netbox.free_interface() + mocked_create_interface.return_value = mocked_netbox.create_interface() + mocked_get_available_lags.return_value = mocked_netbox.get_available_lags() + mocked_delete_interface.return_value = mocked_netbox.delete_interface() + + product_id = iptrunk_subscription_factory() + old_subscription = Iptrunk.from_subscription(product_id) + new_router = router_subscription_factory() + + # Run workflow + migrate_form_input = [ + {"subscription_id": product_id}, + { + "tt_number": faker.tt_number(), + "replace_side": str( + old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.subscription.subscription_id + ), + }, + { + "new_node": new_router, + }, + { + "new_lag_interface": "LAG1", + "new_lag_member_interfaces": [ + LAGMember(interface_name=f"Interface{interface}", interface_description=faker.sentence()) + for interface in range(2) + ], + }, + ] + + result, process_stat, step_log = run_workflow("migrate_iptrunk", migrate_form_input) + assert_suspended(result) + + lso_return = { + "pp_run_results": { + "status": "ok", + "job_id": faker.uuid4(), + "output": "parsed_output", + "return_code": 0, + }, + "confirm": "ACCEPTED", + } + # Resume steps + for _ in range(5): + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, lso_return) + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, [{}, {}]) + for _ in range(2): + result, step_log = user_accept_and_assert_suspended(process_stat, step_log) + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, lso_return) + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, [{}, {}]) + + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, lso_return) + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, [{}, {}]) + result, step_log = user_accept_and_assert_suspended(process_stat, step_log, lso_return) + result, step_log = resume_workflow(process_stat, step_log, [{}, {}]) + + assert_complete(result) + + state = extract_state(result) + subscription_id = state["subscription_id"] + subscription = Iptrunk.from_subscription(subscription_id) + + assert "active" == subscription.status + assert mock_provision_ip_trunk.call_count == 2 + assert mock_migrate_ip_trunk.call_count == 7 + # Assert all Netbox calls have been made + # This test case is only for migrating Nokia to Nokia. + # For Juniper to Nokia and Nokia to Juniper, the workflow is different. + assert mocked_create_interface.call_count == 1 # once for creating the LAG on the newly replaced side + assert mocked_reserve_interface.call_count == 2 # Twice for the new interfaces + assert mocked_attach_interface_to_lag.call_count == 2 # Twice for the new interfaces + assert mocked_allocate_interface.call_count == 2 # Twice for the new interfaces + assert mocked_free_interface.call_count == 2 # Twice for the old interfaces + assert mocked_delete_interface.call_count == 1 # once for deleting the LAG on the old replaced side + + # Assert the new side is replaced + assert str(subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.subscription.subscription_id) == new_router + assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface == "LAG1" + assert len(subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members) == 2 + assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members[0].interface_name == "Interface0" + assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members[1].interface_name == "Interface1"