diff --git a/.gitignore b/.gitignore index 754ad9d40c81f85a801f4f4aed6b26a497cb9e59..6cf2af7fc3ecf1db4faea388c628f740a2e7d000 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ coverage.xml docs/build .vscode + +config.json diff --git a/docs/source/_static/openapi.json b/docs/source/_static/openapi.json new file mode 100644 index 0000000000000000000000000000000000000000..51b197cb2a0a68455fa436e0c119cc8df122a407 --- /dev/null +++ b/docs/source/_static/openapi.json @@ -0,0 +1,390 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/api/version": { + "get": { + "summary": "Version", + "operationId": "version_api_version_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Version" + } + } + } + } + } + } + }, + "/api/interfaces/initialize-router/{fqdn}": { + "post": { + "summary": "Load New Router Interfaces", + "operationId": "load_new_router_interfaces_api_interfaces_initialize_router__fqdn__post", + "parameters": [ + { + "required": true, + "schema": { + "title": "Fqdn", + "type": "string" + }, + "name": "fqdn", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InterfacesSummary" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/interfaces/reconcile-router/{fqdn}": { + "post": { + "summary": "Reconcile Current Router Interfaces", + "operationId": "reconcile_current_router_interfaces_api_interfaces_reconcile_router__fqdn__post", + "parameters": [ + { + "required": true, + "schema": { + "title": "Fqdn", + "type": "string" + }, + "name": "fqdn", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/interfaces/routers": { + "get": { + "summary": "Get Known Routers", + "operationId": "get_known_routers_api_interfaces_routers_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListOfRouters" + } + } + } + } + } + } + }, + "/api/interfaces/next-lag/{fqdn}": { + "post": { + "summary": "Reserve Next Lag", + "description": "compute the next available lag name for the given router\n\nTODO: _next_lag_name is a placeholder for\n whatever logic turns out to be right", + "operationId": "reserve_next_lag_api_interfaces_next_lag__fqdn__post", + "parameters": [ + { + "required": true, + "schema": { + "title": "Fqdn", + "type": "string" + }, + "name": "fqdn", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NextLAG" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/interfaces/next-physical/{fqdn}/{lag_name}": { + "post": { + "summary": "Reserve Physical Bundle Member", + "description": "compute the next available lag name for the given router\n\nTODO: _find_available_physical is a placeholder for\n whatever logic turns out to be right (e.g. speeds, etc)", + "operationId": "reserve_physical_bundle_member_api_interfaces_next_physical__fqdn___lag_name__post", + "parameters": [ + { + "required": true, + "schema": { + "title": "Fqdn", + "type": "string" + }, + "name": "fqdn", + "in": "path" + }, + { + "required": true, + "schema": { + "title": "Lag Name", + "type": "string" + }, + "name": "lag_name", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NextPhysicalInterface" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + }, + "InterfaceCounts": { + "title": "InterfaceCounts", + "required": [ + "total", + "available" + ], + "type": "object", + "properties": { + "total": { + "title": "Total", + "type": "integer" + }, + "available": { + "title": "Available", + "type": "integer" + } + } + }, + "InterfacesSummary": { + "title": "InterfacesSummary", + "required": [ + "fqdn", + "lag", + "physical" + ], + "type": "object", + "properties": { + "fqdn": { + "title": "Fqdn", + "type": "string" + }, + "lag": { + "title": "Lag", + "type": "integer" + }, + "physical": { + "$ref": "#/components/schemas/InterfaceCounts" + } + } + }, + "ListOfRouters": { + "title": "ListOfRouters", + "required": [ + "routers" + ], + "type": "object", + "properties": { + "routers": { + "title": "Routers", + "type": "array", + "items": { + "$ref": "#/components/schemas/RouterDetails" + } + } + } + }, + "NextLAG": { + "title": "NextLAG", + "required": [ + "fqdn", + "name" + ], + "type": "object", + "properties": { + "fqdn": { + "title": "Fqdn", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + } + }, + "NextPhysicalInterface": { + "title": "NextPhysicalInterface", + "required": [ + "fqdn", + "lag", + "name" + ], + "type": "object", + "properties": { + "fqdn": { + "title": "Fqdn", + "type": "string" + }, + "lag": { + "title": "Lag", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + } + } + }, + "RouterDetails": { + "title": "RouterDetails", + "required": [ + "fqdn" + ], + "type": "object", + "properties": { + "fqdn": { + "title": "Fqdn", + "type": "string" + } + } + }, + "ValidationError": { + "title": "ValidationError", + "required": [ + "loc", + "msg", + "type" + ], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + }, + "Version": { + "title": "Version", + "required": [ + "api", + "module" + ], + "type": "object", + "properties": { + "api": { + "title": "Api", + "pattern": "\\d+\\.\\d+", + "type": "string" + }, + "module": { + "title": "Module", + "pattern": "\\d+\\.\\d+", + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/resource_management/alembic.ini b/resource_management/alembic.ini index 22d9b90d7b87f658aeb5b38a234376d320a13e4c..64ca481182a337975db9c0233c7fe8d475565abd 100644 --- a/resource_management/alembic.ini +++ b/resource_management/alembic.ini @@ -3,6 +3,6 @@ # make sure the right line is un / commented depending on which schema you want # a migration for script_location = migrations -sqlalchemy.url = sqlite:////absolute/path/to/foo.db +sqlalchemy.url = %(DB_URI)s # or here is an example of a mysql dsn # sqlalchemy.url = mysql://dummy:dummy-pass@localhost/resources_db_name diff --git a/resource_management/migrations/env.py b/resource_management/migrations/env.py index dd3e8510b6d78a29a524d681c7788f5a4ee6c272..dd251e81e0761c6a58037ff36c5435ae751ee158 100644 --- a/resource_management/migrations/env.py +++ b/resource_management/migrations/env.py @@ -5,6 +5,8 @@ from sqlalchemy import pool from alembic import context +from resource_management import config as _app_config + # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config @@ -25,6 +27,11 @@ target_metadata = None # my_important_option = config.get_main_option("my_important_option") # ... etc. +app_config = _app_config.load() +db_uri = app_config["db"] +section = config.config_ini_section +config.set_section_option(section, "DB_URI", db_uri) + def run_migrations_offline() -> None: """Run migrations in 'offline' mode. diff --git a/resource_management/migrations/versions/1ec562888fd6_initial_tables.py b/resource_management/migrations/versions/1ec562888fd6_initial_tables.py index 34fabf1e874ca1b34843a80e0ba8191231373b9e..50249f435c8ab1aa1af2dbb90ddab53930d4a5d1 100644 --- a/resource_management/migrations/versions/1ec562888fd6_initial_tables.py +++ b/resource_management/migrations/versions/1ec562888fd6_initial_tables.py @@ -26,7 +26,7 @@ def upgrade() -> None: op.create_table( 'lags', sa.Column('id', sa.Integer, primary_key=True), - sa.Column('name', sa.String, nullable=False), + sa.Column('name', sa.String(256), nullable=False), sa.Column( 'availability', sa.Enum('AVAILABLE', 'USED', 'RESERVED', name='lag_availability'), @@ -40,7 +40,7 @@ def upgrade() -> None: op.create_table( 'physical_interfaces', sa.Column('id', sa.Integer, primary_key=True), - sa.Column('name', sa.String, nullable=False), + sa.Column('name', sa.String(256), nullable=False), sa.Column( 'availability', sa.Enum('AVAILABLE', 'USED', 'RESERVED', name='lag_availability'), @@ -59,7 +59,7 @@ def upgrade() -> None: op.create_table( 'logical_interfaces', sa.Column('id', sa.Integer, primary_key=True), - sa.Column('name', sa.String, nullable=False), + sa.Column('name', sa.String(256), nullable=False), sa.Column( 'physical_id', sa.Integer, diff --git a/resource_management/routes/interfaces.py b/resource_management/routes/interfaces.py index dd0b4425330cf81b2f3fbc76252c9014a13846bc..019b8be12bc9296b3dbd5914cd2f7dfa8ebbded6 100644 --- a/resource_management/routes/interfaces.py +++ b/resource_management/routes/interfaces.py @@ -86,6 +86,10 @@ async def reconcile_current_router_interfaces(fqdn: str): @router.get('/routers') async def get_known_routers() -> ListOfRouters: + + params = config.load() + db.init_db_model(params['db']) + with db.session_scope() as session: rows = session.query(model.Router.fqdn).all() return {'routers': [{'fqdn': r[0]} for r in rows]}