diff --git a/resource_management/router_interfaces.py b/resource_management/router_interfaces.py index 77a08c7eb94e5fa9fd787cb74855becc47f52d81..d0cbc8811520ea0083b8c91e3b4c4bd40f7ba2a0 100644 --- a/resource_management/router_interfaces.py +++ b/resource_management/router_interfaces.py @@ -15,7 +15,6 @@ def load_router_interfaces(fqdn: str): """ params = config.load() - db.init_db_model(params['db']) with juniper.router( hostname=fqdn, diff --git a/resource_management/routes/interfaces.py b/resource_management/routes/interfaces.py index 771bc2a0047238e701e8a8cad9a96c0bf75e20c6..ca15f20d7de0cd47e7f560ce4d632057a477d6ae 100644 --- a/resource_management/routes/interfaces.py +++ b/resource_management/routes/interfaces.py @@ -2,6 +2,7 @@ from fastapi import APIRouter, HTTPException import pydantic from resource_management import db +from resource_management import config from resource_management.db import model from resource_management import router_interfaces @@ -19,9 +20,23 @@ class InterfacesSummary(pydantic.BaseModel): physical: InterfaceCounts +class NextLAG(pydantic.BaseModel): + fqdn: str + name: str + + +class NextPhysicalInterface(pydantic.BaseModel): + fqdn: str + lag: str + name: str + + @router.post('/import/{fqdn}') async def load_router_interfaces(fqdn: str) -> InterfacesSummary: + params = config.load() + db.init_db_model(params['db']) + router_interfaces.load_router_interfaces(fqdn) with db.session_scope() as session: @@ -38,3 +53,63 @@ async def load_router_interfaces(fqdn: str) -> InterfacesSummary: 'available': num_available_physical } } + +@router.post('/next-lag/{fqdn}') +async def reserve_next_lag(fqdn: str) -> NextLAG: + """ + compute the next available lag name for the given router + + TODO: _next_lag_name is a placeholder for + whatever logic turns out to be right + """ + + params = config.load() + db.init_db_model(params['db']) + + with db.session_scope() as session: + records = session.query(model.LAG.name) \ + .join(model.Router).filter_by(fqdn=fqdn).all() + names = set(r[0] for r in records) + + def _next_lag_name(): + index = 1 + while True: + candidate = f'ae{index}' + if candidate not in names: + return candidate + index += 1 + + return { + 'fqdn': fqdn, + 'name': _next_lag_name() + } + + +@router.post('/next-physical/{fqdn}/{lag_name}') +async def reserve_physical_bundle_member(fqdn: str, lag_name: str) -> NextPhysicalInterface: + """ + compute the next available lag name for the given router + + TODO: _find_available_physical is a placeholder for + whatever logic turns out to be right (e.g. speeds, etc) + """ + + params = config.load() + db.init_db_model(params['db']) + + with db.session_scope() as session: + router = session.query(model.Router).filter_by(fqdn=fqdn).one() + + def _find_available_physical(): + for ifc in router.physical: + if not ifc.lag: + return ifc.name + raise HTTPException( + status_code=404, + detail=f'no available physical ports for "{lag_name}"') + + return { + 'fqdn': fqdn, + 'lag': lag_name, + 'name': _find_available_physical() + } diff --git a/test/test_interfaces_routes.py b/test/test_interfaces_routes.py index 09e1f933e8a4632ee47b5672d15cb5a57fb4c92e..4b5ece456010c0c419c3c040815323f6d03cb2af 100644 --- a/test/test_interfaces_routes.py +++ b/test/test_interfaces_routes.py @@ -2,6 +2,8 @@ import jsonschema from resource_management.routes.default import Version from resource_management.routes import interfaces +from resource_management import router_interfaces + def test_bad_method(client): rv = client.post('/api/version') @@ -20,3 +22,19 @@ def test_update_router_interfaces(client, resources_db, mocked_router, router_na jsonschema.validate(rv.json(), interfaces.InterfacesSummary.schema()) +def test_next_lag(client, resources_db, mocked_router, router_name): + + router_interfaces.load_router_interfaces(router_name) + + rv = client.post(f'/api/interfaces/next-lag/{router_name}') + assert rv.status_code == 200 + jsonschema.validate(rv.json(), interfaces.NextLAG.schema()) + + +def test_next_physical(client, resources_db, mocked_router, router_name): + + router_interfaces.load_router_interfaces(router_name) + + rv = client.post(f'/api/interfaces/next-physical/{router_name}/ae123123') + assert rv.status_code == 200 + jsonschema.validate(rv.json(), interfaces.NextPhysicalInterface.schema())