diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..70bfc27e17dce4bdd2df10ee86b2490ec7789d98 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.15 + hooks: + # Run the linter. + - id: ruff + args: + - --fix + - --preview + - --ignore=PLR0917,PLR0914 + # Run the formatter. + - id: ruff-format + args: + - --preview diff --git a/Changelog.md b/Changelog.md index ae5a2918b153c7bb9a469b3d1f1231309c80cc79..e5624c2a65a0a385f73ba3774b9734479c6db862 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. +## [0.6] - 2024-02-07 +- Removed the import workflows from migrations. + +## [0.5] - 2024-01-30 +- Changed the router access ts field to FALSE in import router workfolw. + +## [0.4] - 2024-01-26 +- Added Token authentication for LSO callbacks. + +## [0.3] - 2024-01-23 +- Fixed related to the Authentication and some small improvments. + ## [0.2] - 2024-01-16 - Initial release diff --git a/data/routers.json b/data/routers.json deleted file mode 100644 index a68e7fe83737c5e893d63f068ef9d24d95a35892..0000000000000000000000000000000000000000 --- a/data/routers.json +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - "router_site": "AMS", - "hostname": "rt1", - "ts_port": 22111, - "router_vendor": "juniper", - "router_role": "pe", - "router_lo_ipv4_address": "62.40.119.2", - "router_lo_ipv6_address": "2001:798:1ab::2", - "router_lo_iso_address": "49.51e5.0001.0620.4011.9002.00", - "is_ias_connected": true - }, - { - "router_site": "ATH", - "hostname": "rt1", - "ts_port": 22111, - "router_vendor": "juniper", - "router_role": "pe", - "router_lo_ipv4_address": "62.40.119.1", - "router_lo_ipv6_address": "2001:798:1ab::1", - "router_lo_iso_address": "49.51e5.0001.0620.4011.9001.00", - "is_ias_connected": false - }, - { - "router_site": "BIL", - "hostname": "rt1", - "ts_port": 22111, - "router_vendor": "juniper", - "router_role": "pe", - "router_lo_ipv4_address": "62.40.119.3", - "router_lo_ipv6_address": "2001:798:1ab::3", - "router_lo_iso_address": "49.51e5.0001.0620.4011.9003.00", - "is_ias_connected": false - }, - { - "router_site": "DUB", - "hostname": "rt1", - "ts_port": 22111, - "router_vendor": "juniper", - "router_role": "pe", - "router_lo_ipv4_address": "62.40.119.4", - "router_lo_ipv6_address": "2001:798:1ab::4", - "router_lo_iso_address": "49.51e5.0001.0620.4011.9004.00", - "is_ias_connected": false - }, - { - "router_site": "LON", - "hostname": "rt1", - "ts_port": 22111, - "router_vendor": "juniper", - "router_role": "pe", - "router_lo_ipv4_address": "62.40.119.5", - "router_lo_ipv6_address": "2001:798:1ab::5", - "router_lo_iso_address": "49.51e5.0001.0620.4011.9005.00", - "is_ias_connected": true - } -] diff --git a/data/sites.json b/data/sites.json deleted file mode 100644 index 1ce7427645053e6c6774534e895293178e04097f..0000000000000000000000000000000000000000 --- a/data/sites.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "site_name": "AMS", - "site_city": "Amsterdam", - "site_country": "The Netherlands", - "site_country_code": "NL", - "site_latitude": 52.35640299542275, - "site_longitude": 4.952931412236851, - "site_bgp_community_id": 13, - "site_internal_id": 2, - "site_tier": "1", - "site_ts_address": "62.40.111.195" - }, - { - "site_name": "ATH", - "site_city": "Athens", - "site_country": "Greece", - "site_country_code": "GR", - "site_latitude": 37.973573902105514, - "site_longitude": 23.74551842723506, - "site_bgp_community_id": 14, - "site_internal_id": 4, - "site_tier": "1", - "site_ts_address": "62.40.111.196" - }, - { - "site_name": "BIL", - "site_city": "Bilbao", - "site_country": "Spain", - "site_country_code": "ES", - "site_latitude": 43.32311388825037, - "site_longitude": -2.996764830318336, - "site_bgp_community_id": 47, - "site_internal_id": 3, - "site_tier": "1", - "site_ts_address": "62.40.111.197" - }, - { - "site_name": "DUB", - "site_city": "Dublin", - "site_country": "Ireland", - "site_country_code": "IE", - "site_latitude": 53.29025463849949, - "site_longitude": -6.4207320574310165, - "site_bgp_community_id": 20, - "site_internal_id": 5, - "site_tier": "1", - "site_ts_address": "62.40.111.198" - }, - { - "site_name": "LON", - "site_city": "London", - "site_country": "United Kingdom", - "site_country_code": "UK", - "site_latitude": 51.49821912962843, - "site_longitude": -0.015228819041376851, - "site_bgp_community_id": 28, - "site_internal_id": 1, - "site_tier": "1", - "site_ts_address": "62.40.111.199" - } -] \ No newline at end of file diff --git a/data/trunks.json b/data/trunks.json deleted file mode 100644 index f1c2b8720358dc0d63aa9863af417f3818427eac..0000000000000000000000000000000000000000 --- a/data/trunks.json +++ /dev/null @@ -1,300 +0,0 @@ -[ - { - "id": "LGS-00001", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Leased" - }, - "nodeA": { - "name": "rt1.ath.gr.lab.office.geant.net", - "ae_name": "ae0", - "port_sid": "LGA-00001", - "members": [ - { - "interface_name": "et-0/0/2", - "interface_description": "et-0/0/2" - } - ], - "ipv4_address": "62.40.98.0/31", - "ipv6_address": "2001:798:cc::1/126" - }, - "nodeB": { - "name": "rt1.ams.nl.lab.office.geant.net", - "ae_name": "ae0", - "port_sid": "LGA-00002", - "members": [ - { - "interface_name": "et-9/0/2", - "interface_description": "et-9/0/2" - } - ], - "ipv4_address": "62.40.98.1/31", - "ipv6_address": "2001:798:cc::2/126" - } - } - }, - { - "id": "LGS-00003", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Leased" - }, - "nodeA": { - "name": "rt1.ath.gr.lab.office.geant.net", - "ae_name": "ae1", - "port_sid": "LGA-00005", - "members": [ - { - "interface_name": "et-0/0/1", - "interface_description": "et-0/0/1" - } - ], - "ipv4_address": "62.40.98.2/31", - "ipv6_address": "2001:798:cc::9/126" - }, - "nodeB": { - "name": "rt1.lon.uk.lab.office.geant.net", - "ae_name": "ae1", - "port_sid": "LGA-00006", - "members": [ - { - "interface_name": "et-2/0/0", - "interface_description": "et-2/0/0" - } - ], - "ipv4_address": "62.40.98.3/31", - "ipv6_address": "2001:798:cc::a/126" - } - } - }, - { - "id": "LGS-00004", - "config": { - "common": { - "link_speed": "400G", - "minimum_links": 1, - "isis_metric": 100, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.lon.uk.lab.office.geant.net", - "ae_name": "ae6", - "port_sid": "LGA-00004", - "members": [ - { - "interface_name": "et-1/0/4", - "interface_description": "et-1/0/4" - } - ], - "ipv4_address": "62.40.98.55/31", - "ipv6_address": "2001:798:cc::6e/126" - }, - "nodeB": { - "name": "rt1.ams.nl.lab.office.geant.net", - "ae_name": "ae6", - "port_sid": "LGA-00004", - "members": [ - { - "interface_name": "et-3/0/4", - "interface_description": "et-3/0/4" - } - ], - "ipv4_address": "62.40.98.54/31", - "ipv6_address": "2001:798:cc::6d/126" - } - } - }, - { - "id": "LGS-00006", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.ams.nl.lab.office.geant.net", - "ae_name": "ae1", - "port_sid": "LGA-00011", - "members": [ - { - "interface_name": "et-9/0/5", - "interface_description": "et-9/0/5" - } - ], - "ipv4_address": "62.40.98.4/31", - "ipv6_address": "2001:798:cc::d/126" - }, - "nodeB": { - "name": "rt1.bil.es.lab.office.geant.net", - "ae_name": "ae1", - "port_sid": "LGA-00012", - "members": [ - { - "interface_name": "et-0/0/0", - "interface_description": "et-0/0/0" - } - ], - "ipv4_address": "62.40.98.5/31", - "ipv6_address": "2001:798:cc::e/126" - } - } - }, - { - "id": "LGS-00007", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.lon.uk.lab.office.geant.net", - "ae_name": "ae0", - "port_sid": "LGA-00013", - "members": [ - { - "interface_name": "et-5/2/0", - "interface_description": "et-5/2/0" - } - ], - "ipv4_address": "62.40.98.17/31", - "ipv6_address": "2001:798:cc::22/126" - }, - "nodeB": { - "name": "rt1.dub.ie.lab.office.geant.net", - "ae_name": "ae0", - "port_sid": "LGA-00014", - "members": [ - { - "interface_name": "et-0/0/1", - "interface_description": "et-0/0/1" - } - ], - "ipv4_address": "62.40.98.16/31", - "ipv6_address": "2001:798:cc::21/126" - } - } - }, - { - "id": "LGS-00008", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.bil.es.lab.office.geant.net", - "ae_name": "ae2", - "port_sid": "LGA-00016", - "members": [ - { - "interface_name": "et-0/0/1", - "interface_description": "et-0/0/1" - } - ], - "ipv4_address": "62.40.98.8/31", - "ipv6_address": "2001:798:cc::15/126" - }, - "nodeB": { - "name": "rt1.dub.ie.lab.office.geant.net", - "ae_name": "ae2", - "port_sid": "LGA-00015", - "members": [ - { - "interface_name": "et-0/0/0", - "interface_description": "et-0/0/0" - } - ], - "ipv4_address": "62.40.98.9/31", - "ipv6_address": "2001:798:cc::16/126" - } - } - }, - { - "id": "LGS-00012", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.ams.nl.lab.office.geant.net", - "ae_name": "ae5", - "port_sid": "LGA-00023", - "members": [ - { - "interface_name": "et-1/0/2", - "interface_description": "et-1/0/2" - } - ], - "ipv4_address": "62.40.98.52/31", - "ipv6_address": "2001:798:cc::69/126" - }, - "nodeB": { - "name": "rt2.lon.uk.lab.office.geant.net", - "ae_name": "ae1", - "port_sid": "LGA-00024", - "members": [ - { - "interface_name": "et-0/0/0", - "interface_description": "et-0/0/0" - } - ], - "ipv4_address": "62.40.98.53/31", - "ipv6_address": "2001:798:cc::6a/126" - } - } - }, - { - "id": "LGS-00013", - "config": { - "common": { - "link_speed": "100G", - "minimum_links": 1, - "isis_metric": 500, - "type": "Dark_fiber" - }, - "nodeA": { - "name": "rt1.lon.uk.lab.office.geant.net", - "ae_name": "ae3", - "port_sid": "LGA-00025", - "members": [ - { - "interface_name": "et-0/0/2", - "interface_description": "et-0/0/2" - } - ], - "ipv4_address": "62.40.98.36/31", - "ipv6_address": "2001:798:cc::65/126" - }, - "nodeB": { - "name": "rt2.lon.uk.lab.office.geant.net", - "ae_name": "ae2", - "port_sid": "LGA-00026", - "members": [ - { - "interface_name": "et-0/0/1", - "interface_description": "et-0/0/1" - } - ], - "ipv4_address": "62.40.98.37/31", - "ipv6_address": "2001:798:cc::66/126" - } - } - } -] - - diff --git a/gso/api/v1/subscriptions.py b/gso/api/v1/subscriptions.py index 24c9307f1ce67371ea3f89c6f0888380ad7ea09c..9cc6075ac8bb4858e0354afb691b645a0bdcc1dd 100644 --- a/gso/api/v1/subscriptions.py +++ b/gso/api/v1/subscriptions.py @@ -8,13 +8,13 @@ from orchestrator.domain import SubscriptionModel from orchestrator.schemas import SubscriptionDomainModelSchema from orchestrator.services.subscriptions import build_extended_domain_model -from gso.auth.security import opa_security_default +from gso.auth.api_key_auth import get_api_key from gso.services.subscriptions import get_active_router_subscriptions router = APIRouter( prefix="/subscriptions", tags=["Subscriptions"], - dependencies=[Depends(opa_security_default)], + dependencies=[Depends(get_api_key)], ) diff --git a/gso/auth/api_key_auth.py b/gso/auth/api_key_auth.py new file mode 100644 index 0000000000000000000000000000000000000000..f66e7b8535e553d49a060528fc024bde15222be6 --- /dev/null +++ b/gso/auth/api_key_auth.py @@ -0,0 +1,22 @@ +"""Manage API key validation for FastAPI routes.""" + +from fastapi import Depends, HTTPException, status +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer + +from gso.settings import load_oss_params + +security = HTTPBearer() + + +async def get_api_key( + credentials: HTTPAuthorizationCredentials = Depends(security), # noqa: B008 +) -> str: + """Validate the provided API key against known third-party keys and returns it if valid, else raises HTTP 403.""" + settings = load_oss_params() + api_key = credentials.credentials + + # TODO: This is a simulated database of API keys which should be replace with a real one + if api_key in settings.THIRD_PARTY_API_KEYS.values(): + return api_key + + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid API Key") diff --git a/gso/auth/oidc_policy_helper.py b/gso/auth/oidc_policy_helper.py index 005e6cd4a5e6b292613f9612d350ddc73f5f0ba1..04e2fc8e5ec419fb5f80a6a5646bb3429512cad9 100644 --- a/gso/auth/oidc_policy_helper.py +++ b/gso/auth/oidc_policy_helper.py @@ -411,6 +411,9 @@ def opa_decision( if not (oauth2lib_settings.OAUTH2_ACTIVE and oauth2lib_settings.OAUTH2_AUTHORIZATION_ACTIVE): return None + if _is_callback_step_endpoint(request): + return None + try: json = await request.json() # Silencing the Decode error or Type error when request.json() does not return anything sane. diff --git a/gso/migrations/versions/2023-11-21_0c31b60487c8_add_site_workflows.py b/gso/migrations/versions/2023-11-21_0c31b60487c8_add_site_workflows.py index 3373f1d4dbe86d2ab93dbe785983fcdc76f0d929..d0c4980fa521248fce89230095e8e11a1dd4b00b 100644 --- a/gso/migrations/versions/2023-11-21_0c31b60487c8_add_site_workflows.py +++ b/gso/migrations/versions/2023-11-21_0c31b60487c8_add_site_workflows.py @@ -36,12 +36,6 @@ new_workflows = [ "description": "Modify Site", "product_type": "Site" }, - { - "name": "import_site", - "target": "CREATE", - "description": "Import Site", - "product_type": "Site" - } ] diff --git a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py index 14165a6f66f6c24189abfd5e970a0ceb28b1b423..b2ae1da22f4836940059267a31ed4be661dfd300 100644 --- a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py +++ b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py @@ -30,12 +30,6 @@ new_workflows = [ "description": "Terminate IPtrunk", "product_type": "Iptrunk" }, - { - "name": "import_iptrunk", - "target": "CREATE", - "description": "Import iptrunk", - "product_type": "Iptrunk" - }, { "name": "migrate_iptrunk", "target": "MODIFY", diff --git a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py index 02cb440d4f234d7298c787f13340baa0f3902c21..1d996616d410081c01938ade185fc45455e48046 100644 --- a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py +++ b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py @@ -30,12 +30,6 @@ new_workflows = [ "description": "Terminate router", "product_type": "Router" }, - { - "name": "import_router", - "target": "CREATE", - "description": "Import router", - "product_type": "Router" - } ] diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index fa8616e4151a50175db13e52b48ed28513861133..931367ea48746610b29646651e99a68f89d1288e 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -73,5 +73,9 @@ "broker_url": "redis://localhost:6379/0", "result_backend": "rpc://localhost:6379/0", "result_expires": 3600 + }, + "THIRD_PARTY_API_KEYS": { + "AnsibleDynamicInventoryGenerator": "REALLY_random_AND_secure_T0keN", + "Application_2": "another_REALY_random_AND_3cure_T0keN" } } diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py index d93bbb7220a718ce72d40a97cc7a6711b50c27cd..7aba02a4c01b93c476773c0d56f4d86445ce2cf0 100644 --- a/gso/services/provisioning_proxy.py +++ b/gso/services/provisioning_proxy.py @@ -62,8 +62,8 @@ def execute_playbook( .. code-block:: json "inventory": { - "_meta": { - "vars": { + "all": { + "hosts": { "host1.local": { "foo": "bar" }, @@ -71,12 +71,6 @@ def execute_playbook( "key": "value" } } - }, - "all": { - "hosts": { - "host1.local": null, - "host2.local": null - } } } diff --git a/gso/settings.py b/gso/settings.py index f05632ed3968d8f727c6d108618c0c80a5065ca8..ca0da591c00e829585db55c5a68731b3c44aac90 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -161,6 +161,7 @@ class OSSParams(BaseSettings): MONITORING: MonitoringParams PROVISIONING_PROXY: ProvisioningProxyParams CELERY: CeleryParams + THIRD_PARTY_API_KEYS: dict[str, str] def load_oss_params() -> OSSParams: diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py index 6927da89a9d8162c885af6de4c56dc1cbd63721e..164007b183335c423fac59cb7ed1278c7ff93508 100644 --- a/gso/utils/helpers.py +++ b/gso/utils/helpers.py @@ -47,7 +47,7 @@ def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None: if get_router_vendor(router_id) != RouterVendor.NOKIA: return None interfaces = { - interface["name"]: f"{interface['name']} - {interface['description']}" + interface["name"]: f"{interface['name']} {interface['description']}" for interface in NetboxClient().get_available_interfaces(router_id, speed) } return Choice("ae member", zip(interfaces.keys(), interfaces.items(), strict=True)) # type: ignore[arg-type] diff --git a/gso/utils/workflow_steps.py b/gso/utils/workflow_steps.py index 23126f26c7fac70e57c2a9e1dfef060fa5aec11e..5890d62b76d042eb8d6ff9c95383ab8cbbedf733 100644 --- a/gso/utils/workflow_steps.py +++ b/gso/utils/workflow_steps.py @@ -77,7 +77,7 @@ def set_isis_to_90000(subscription: Iptrunk, process_id: UUIDstr, callback_route } execute_playbook( - playbook_name="playbooks.yaml", + playbook_name="iptrunks.yaml", callback_route=callback_route, inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index 880f62f99dff23942a00644c758ad80cb9153aaf..810041cd89a7a5c7104ddd8a8ab5f368ac5ac068 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -7,7 +7,6 @@ configured to run from A to C. B is then no longer associated with this IP trunk import copy import json import re -from typing import NoReturn from uuid import uuid4 from orchestrator import step, workflow @@ -85,10 +84,12 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: 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 if ( - migrate_form_input.migrate_to_different_site - and current_router_site.subscription_id == old_side_site.owner_subscription_id + not migrate_form_input.migrate_to_different_site + and current_router_site.subscription_id != old_side_site.owner_subscription_id ): + # We want to stay on the same site, so all routers that are in different sites get skipped. continue + # If migrate_to_different_site is true, we can add ALL routers to the result map routers[str(router_id)] = router["description"] new_router_enum = Choice("Select a new router", zip(routers.keys(), routers.items(), strict=True)) # type: ignore[arg-type] @@ -148,7 +149,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: new_lag_member_interfaces: ae_members # type: ignore[valid-type] @validator("new_lag_interface", allow_reuse=True, pre=True, always=True) - def lag_interface_proper_name(cls, new_lag_interface: str) -> str | NoReturn: + def lag_interface_proper_name(cls, new_lag_interface: str) -> str: if get_router_vendor(new_router) == RouterVendor.JUNIPER: juniper_lag_re = re.compile("^ae\\d{1,2}$") if not juniper_lag_re.match(new_lag_interface): diff --git a/gso/workflows/router/update_ibgp_mesh.py b/gso/workflows/router/update_ibgp_mesh.py index b93ed30bbe91bd5f91bffdd2560235b9d3325394..d2f6d8d66b4dcd95250c6bcbc87f15815f3d15a6 100644 --- a/gso/workflows/router/update_ibgp_mesh.py +++ b/gso/workflows/router/update_ibgp_mesh.py @@ -59,23 +59,22 @@ def calculate_pe_router_list() -> State: def _generate_pe_inventory(pe_router_list: list[Router]) -> dict[str, Any]: """Generate an Ansible-compatible inventory for executing playbooks. Contains all active PE routers.""" return { - "_meta": { - "vars": { + "all": { + "hosts": { router.router.router_fqdn: { - "lo4": router.router.router_lo_ipv4_address, - "lo6": router.router.router_lo_ipv6_address, - "vendor": router.router.vendor, + "lo4": str(router.router.router_lo_ipv4_address), + "lo6": str(router.router.router_lo_ipv6_address), + "vendor": str(router.router.vendor), } for router in pe_router_list } }, - "all": {"hosts": {router.router.router_fqdn: None for router in pe_router_list}}, } @step("[DRY RUN] Add P router to iBGP mesh") def add_p_to_mesh_dry( - subscription: Router, callback_route: str, pe_router_list: list[Router], tt_number: str, process_id: UUIDstr + subscription: dict[str, Any], callback_route: str, pe_router_list: list[Router], tt_number: str, process_id: UUIDstr ) -> None: """Perform a dry run of adding the new P router to the PE router mesh.""" extra_vars = { @@ -95,7 +94,7 @@ def add_p_to_mesh_dry( @step("[FOR REAL] Add P router to iBGP mesh") def add_p_to_mesh_real( - subscription: Router, callback_route: str, pe_router_list: list[Router], tt_number: str, process_id: UUIDstr + subscription: dict[str, Any], callback_route: str, pe_router_list: list[Router], tt_number: str, process_id: UUIDstr ) -> None: """Add the P router to the mesh of PE routers.""" extra_vars = { @@ -115,7 +114,7 @@ def add_p_to_mesh_real( @step("[DRY RUN] Add all PE routers to P router iBGP table") def add_all_pe_to_p_dry( - subscription: Router, pe_router_list: list[Router], callback_route: str, tt_number: str, process_id: UUIDstr + subscription: dict[str, Any], pe_router_list: list[Router], callback_route: str, tt_number: str, process_id: UUIDstr ) -> None: """Perform a dry run of adding the list of all PE routers to the new P router.""" extra_vars = { @@ -123,8 +122,8 @@ def add_all_pe_to_p_dry( "subscription": subscription, "pe_router_list": { router.router.router_fqdn: { - "lo4": router.router.router_lo_ipv4_address, - "lo6": router.router.router_lo_ipv6_address, + "lo4": str(router.router.router_lo_ipv4_address), + "lo6": str(router.router.router_lo_ipv6_address), "vendor": router.router.vendor, } for router in pe_router_list @@ -136,14 +135,14 @@ def add_all_pe_to_p_dry( provisioning_proxy.execute_playbook( playbook_name="update_ibgp_mesh.yaml", callback_route=callback_route, - inventory=subscription.router.router_fqdn, + inventory=subscription["router"]["router_fqdn"], extra_vars=extra_vars, ) @step("[FOR REAL] Add all PE routers to P router iBGP table") def add_all_pe_to_p_real( - subscription: Router, pe_router_list: list[Router], callback_route: str, tt_number: str, process_id: UUIDstr + subscription: dict[str, Any], pe_router_list: list[Router], callback_route: str, tt_number: str, process_id: UUIDstr ) -> None: """Add the list of all PE routers to the new P router.""" extra_vars = { @@ -151,8 +150,8 @@ def add_all_pe_to_p_real( "subscription": subscription, "pe_router_list": { router.router.router_fqdn: { - "lo4": router.router.router_lo_ipv4_address, - "lo6": router.router.router_lo_ipv6_address, + "lo4": str(router.router.router_lo_ipv4_address), + "lo6": str(router.router.router_lo_ipv6_address), "vendor": router.router.vendor, } for router in pe_router_list @@ -164,7 +163,7 @@ def add_all_pe_to_p_real( provisioning_proxy.execute_playbook( playbook_name="update_ibgp_mesh.yaml", callback_route=callback_route, - inventory=subscription.router.router_fqdn, + inventory=subscription["router"]["router_fqdn"], extra_vars=extra_vars, ) diff --git a/gso/workflows/tasks/import_router.py b/gso/workflows/tasks/import_router.py index f2498d8adbbfcb469dd73af43851b0426f6e8922..878b538a40b0cf326380863d7008ef44b8acaab3 100644 --- a/gso/workflows/tasks/import_router.py +++ b/gso/workflows/tasks/import_router.py @@ -87,7 +87,7 @@ def initialize_subscription( fqdn = generate_fqdn(hostname, router_site_obj.site_name, router_site_obj.site_country_code) subscription.router.router_fqdn = fqdn subscription.router.router_role = router_role - subscription.router.router_access_via_ts = True + subscription.router.router_access_via_ts = False subscription.description = f"Router {fqdn}" subscription.router.router_lo_ipv4_address = router_lo_ipv4_address subscription.router.router_lo_ipv6_address = router_lo_ipv6_address diff --git a/pyproject.toml b/pyproject.toml index 5a2ca81c14941c381684b2b8d626f7b1c8323a3e..5162b2ca226c9a3181ee6942ec7153a57d909b25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ ignore = [ "COM812", "D203", "D213", + "ISC001", "N805", "PLR0913", "PLR0904", diff --git a/requirements.txt b/requirements.txt index 87dc706a5986c5833967ea7fae184a5228dad1ca..73bb46ff0e83e703d78bca7c58a728f95e7fa696 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ sphinx==7.2.6 sphinx-rtd-theme==1.3.0 urllib3_mock==0.3.3 pytest-asyncio==0.23.3 +pre-commit~=3.6.0 diff --git a/setup.py b/setup.py index ecb402620217e9355290ad5f0ff3d325bffe7faa..cf34f158746312524a27348396cf48a46812080d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import find_packages, setup setup( name="geant-service-orchestrator", - version="0.3", + version="0.7", author="GÉANT", author_email="swd@geant.org", description="GÉANT Service Orchestrator", diff --git a/test/api/test_subscriptions.py b/test/api/test_subscriptions.py index d56d2d582c21a89aa255e417a19dc04b38bff6e4..b4bad4e091301ccd644a417f9cf7391779d1d32a 100644 --- a/test/api/test_subscriptions.py +++ b/test/api/test_subscriptions.py @@ -3,14 +3,30 @@ from orchestrator.types import SubscriptionLifecycle ROUTER_SUBSCRIPTION_ENDPOINT = "/api/v1/subscriptions/routers" -def test_router_subscriptions_endpoint(test_client, nokia_router_subscription_factory): +def test_router_subscriptions_endpoint_with_valid_api_key(test_client, nokia_router_subscription_factory): nokia_router_subscription_factory() nokia_router_subscription_factory() nokia_router_subscription_factory() nokia_router_subscription_factory(status=SubscriptionLifecycle.TERMINATED) nokia_router_subscription_factory(status=SubscriptionLifecycle.INITIAL) - response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT) + response = test_client.get( + ROUTER_SUBSCRIPTION_ENDPOINT, headers={"Authorization": "Bearer REALY_random_AND_3cure_T0keN"} + ) assert response.status_code == 200 assert len(response.json()) == 3 + + +def test_router_subscriptions_endpoint_with_invalid_api_key(test_client, nokia_router_subscription_factory): + response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT, headers={"Authorization": "Bearer fake_invalid_api_key"}) + + assert response.status_code == 403 + assert response.json() == {"detail": "Invalid API Key"} + + +def test_router_subscriptions_endpoint_without_api_key(test_client, nokia_router_subscription_factory): + response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT) + + assert response.status_code == 403 + assert response.json() == {"detail": "Not authenticated"} diff --git a/test/conftest.py b/test/conftest.py index edb1dffba401f2f57b5c140d171570468c7ee8e7..dd6e9d6d3fc3429f0ce0df33213de8dc1f195523 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -204,6 +204,10 @@ def configuration_data() -> dict: "result_backend": "rpc://localhost:6379/0", "result_expires": 3600, }, + "THIRD_PARTY_API_KEYS": { + "AnsibleDynamicInventoryGenerator": "REALY_random_AND_3cure_T0keN", + "Application_2": "another_REALY_random_AND_3cure_T0keN", + }, } diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py index 8dc3acf02b5cf854ab8de257bed30647acdf8311..c7ca62bf11a89dbde7aa9d3bf2297eef2c5651cd 100644 --- a/test/workflows/iptrunk/test_migrate_iptrunk.py +++ b/test/workflows/iptrunk/test_migrate_iptrunk.py @@ -76,6 +76,7 @@ def migrate_form_input( { "tt_number": faker.tt_number(), "replace_side": replace_side, + "migrate_to_different_site": True, }, { "new_node": new_router,