Loading .dockerignore +14 −0 Original line number Diff line number Diff line # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __pycache__/ *.pyc *.db Loading .gitignore +15 −0 Original line number Diff line number Diff line # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __pycache__/ swagger/__pycache__/ src/__pycache__/ venv/ .env slice.db .python-version .gitlab-ci.yml +1 −1 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading Dockerfile +67 −10 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -16,23 +16,80 @@ FROM python:3.12-slim # Stablish woking directory # Set working directory WORKDIR /app # Install system dependencies # Install system and build dependencies RUN apt-get update -qq && \ apt-get install -y -qq git python3-dev && \ apt-get install -y -qq \ git \ python3-dev \ build-essential \ cmake \ pkg-config \ libssl-dev \ libpcre2-dev \ libssh-dev \ libcurl4-openssl-dev \ zlib1g-dev \ ca-certificates && \ apt-get clean && rm -rf /var/lib/apt/lists/* # Copy project content COPY . /app ################################ # Build & install libyang ################################ RUN git clone https://github.com/CESNET/libyang.git /tmp/libyang && \ cd /tmp/libyang && \ git checkout v3.13.6 && \ mkdir build && cd build && \ cmake -D CMAKE_BUILD_TYPE=Release .. && \ make -j$(nproc) && \ make install && \ ldconfig && \ rm -rf /tmp/libyang ################################ # Build & install sysrepo ################################ RUN git clone https://github.com/sysrepo/sysrepo.git /tmp/sysrepo && \ cd /tmp/sysrepo && \ git checkout v3.7.11 && \ mkdir build && cd build && \ cmake .. && \ make -j$(nproc) && \ make install && \ ldconfig && \ rm -rf /tmp/sysrepo ################################ # Python dependencies ################################ COPY requirements.txt /app/requirements.txt # Intall python dependencies RUN pip install --upgrade pip && \ pip install -r requirements.txt pip install --no-cache-dir -r requirements.txt ################################ # Application ################################ COPY . /app # Expose port EXPOSE 8081 EXPOSE 8085 RUN sysrepoctl -i /app/yang-modules/ietf-routing-types@2017-12-04.yang \ -i /app/yang-modules/ietf-geo-location@2022-02-11.yang \ -i /app/yang-modules/ietf-ethertypes@2019-03-04.yang \ -i /app/yang-modules/ietf-packet-fields@2019-03-04.yang \ -i /app/yang-modules/ietf-vpn-common@2022-02-11.yang \ -i /app/yang-modules/ietf-network@2018-02-26.yang \ -i /app/yang-modules/ietf-network-topology@2018-02-26.yang \ -i /app/yang-modules/ietf-key-chain@2017-06-15.yang \ -i /app/yang-modules/ietf-ac-common@2025-09-29.yang \ -i /app/yang-modules/ietf-ac-svc@2025-09-29.yang \ -i /app/yang-modules/ietf-te-types@2025-10-17.yang \ -i /app/yang-modules/ietf-te-packet-types@2025-01-24.yang \ -i /app/yang-modules/ietf-network-slice-service@2025-05-09.yang # Init command ENTRYPOINT ["python3", "app.py"] No newline at end of file app.py +45 −4 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,17 +15,44 @@ # This file is an original contribution from Telefonica Innovación Digital S.L. import logging from flask import Flask from flask import Flask, request, Response from flask_restx import Api from flask_cors import CORS from swagger.tfs_namespace import tfs_ns from swagger.ixia_namespace import ixia_ns from swagger.E2E_namespace import e2e_ns from swagger.restconf_namespace import restconf_ns from src.config.constants import NSC_PORT from src.webui.gui import gui_bp from src.config.config import create_config from src.database.db import init_db # Paths that do not require authentication (Swagger UI and its static assets) # /nsc → Swagger UI # /swaggerui → Swagger UI static assets (flask-restx) # /swagger → swagger.json spec file (flask-restx) UNAUTHENTICATED_PREFIXES = ("/nsc", "/swaggerui", "/swagger") def _unauthorized_response(): """Return a 401 response without WWW-Authenticate header to avoid browser popup.""" return Response( response="Unauthorized: valid credentials required.", status=401, headers={} ) def _check_basic_auth(app): """Validate Basic Auth credentials against the configured username and password.""" credentials = request.authorization if not credentials: return False expected_username = app.config["API_USERNAME"] expected_password = app.config["API_PASSWORD"] return credentials.username == expected_username and credentials.password == expected_password def create_app(): """Create Flask application with configured API and namespaces.""" init_db() Loading @@ -45,20 +72,34 @@ def create_app(): version="1.0", title="Network Slice Controller (NSC) API", description="API for orchestrating and realizing transport network slice requests", doc="/nsc" # Swagger UI URL doc="/nsc", # Swagger UI URL authorizations={"basicAuth": {"type": "basic"}}, security="basicAuth" ) # Register namespaces api.add_namespace(tfs_ns, path="/tfs") api.add_namespace(ixia_ns, path="/ixia") api.add_namespace(e2e_ns, path="/e2e") api.add_namespace(restconf_ns, path="/restconf") if app.config["WEBUI_DEPLOY"]: app.secret_key = "clave-secreta-dev" app.register_blueprint(gui_bp) # Registered after Api and namespaces to ensure flask-restx does not override this hook @app.before_request def enforce_basic_auth(): """Reject any request that does not include valid Basic Auth credentials.""" # Allow unauthenticated access to Swagger UI and its static assets if request.path.startswith(UNAUTHENTICATED_PREFIXES): return None if not _check_basic_auth(app): return _unauthorized_response() return app if __name__ == "__main__": app = create_app() app.run(host="0.0.0.0", port=NSC_PORT, debug=True) No newline at end of file Loading
.dockerignore +14 −0 Original line number Diff line number Diff line # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __pycache__/ *.pyc *.db Loading
.gitignore +15 −0 Original line number Diff line number Diff line # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __pycache__/ swagger/__pycache__/ src/__pycache__/ venv/ .env slice.db .python-version
.gitlab-ci.yml +1 −1 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading
Dockerfile +67 −10 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -16,23 +16,80 @@ FROM python:3.12-slim # Stablish woking directory # Set working directory WORKDIR /app # Install system dependencies # Install system and build dependencies RUN apt-get update -qq && \ apt-get install -y -qq git python3-dev && \ apt-get install -y -qq \ git \ python3-dev \ build-essential \ cmake \ pkg-config \ libssl-dev \ libpcre2-dev \ libssh-dev \ libcurl4-openssl-dev \ zlib1g-dev \ ca-certificates && \ apt-get clean && rm -rf /var/lib/apt/lists/* # Copy project content COPY . /app ################################ # Build & install libyang ################################ RUN git clone https://github.com/CESNET/libyang.git /tmp/libyang && \ cd /tmp/libyang && \ git checkout v3.13.6 && \ mkdir build && cd build && \ cmake -D CMAKE_BUILD_TYPE=Release .. && \ make -j$(nproc) && \ make install && \ ldconfig && \ rm -rf /tmp/libyang ################################ # Build & install sysrepo ################################ RUN git clone https://github.com/sysrepo/sysrepo.git /tmp/sysrepo && \ cd /tmp/sysrepo && \ git checkout v3.7.11 && \ mkdir build && cd build && \ cmake .. && \ make -j$(nproc) && \ make install && \ ldconfig && \ rm -rf /tmp/sysrepo ################################ # Python dependencies ################################ COPY requirements.txt /app/requirements.txt # Intall python dependencies RUN pip install --upgrade pip && \ pip install -r requirements.txt pip install --no-cache-dir -r requirements.txt ################################ # Application ################################ COPY . /app # Expose port EXPOSE 8081 EXPOSE 8085 RUN sysrepoctl -i /app/yang-modules/ietf-routing-types@2017-12-04.yang \ -i /app/yang-modules/ietf-geo-location@2022-02-11.yang \ -i /app/yang-modules/ietf-ethertypes@2019-03-04.yang \ -i /app/yang-modules/ietf-packet-fields@2019-03-04.yang \ -i /app/yang-modules/ietf-vpn-common@2022-02-11.yang \ -i /app/yang-modules/ietf-network@2018-02-26.yang \ -i /app/yang-modules/ietf-network-topology@2018-02-26.yang \ -i /app/yang-modules/ietf-key-chain@2017-06-15.yang \ -i /app/yang-modules/ietf-ac-common@2025-09-29.yang \ -i /app/yang-modules/ietf-ac-svc@2025-09-29.yang \ -i /app/yang-modules/ietf-te-types@2025-10-17.yang \ -i /app/yang-modules/ietf-te-packet-types@2025-01-24.yang \ -i /app/yang-modules/ietf-network-slice-service@2025-05-09.yang # Init command ENTRYPOINT ["python3", "app.py"] No newline at end of file
app.py +45 −4 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,17 +15,44 @@ # This file is an original contribution from Telefonica Innovación Digital S.L. import logging from flask import Flask from flask import Flask, request, Response from flask_restx import Api from flask_cors import CORS from swagger.tfs_namespace import tfs_ns from swagger.ixia_namespace import ixia_ns from swagger.E2E_namespace import e2e_ns from swagger.restconf_namespace import restconf_ns from src.config.constants import NSC_PORT from src.webui.gui import gui_bp from src.config.config import create_config from src.database.db import init_db # Paths that do not require authentication (Swagger UI and its static assets) # /nsc → Swagger UI # /swaggerui → Swagger UI static assets (flask-restx) # /swagger → swagger.json spec file (flask-restx) UNAUTHENTICATED_PREFIXES = ("/nsc", "/swaggerui", "/swagger") def _unauthorized_response(): """Return a 401 response without WWW-Authenticate header to avoid browser popup.""" return Response( response="Unauthorized: valid credentials required.", status=401, headers={} ) def _check_basic_auth(app): """Validate Basic Auth credentials against the configured username and password.""" credentials = request.authorization if not credentials: return False expected_username = app.config["API_USERNAME"] expected_password = app.config["API_PASSWORD"] return credentials.username == expected_username and credentials.password == expected_password def create_app(): """Create Flask application with configured API and namespaces.""" init_db() Loading @@ -45,20 +72,34 @@ def create_app(): version="1.0", title="Network Slice Controller (NSC) API", description="API for orchestrating and realizing transport network slice requests", doc="/nsc" # Swagger UI URL doc="/nsc", # Swagger UI URL authorizations={"basicAuth": {"type": "basic"}}, security="basicAuth" ) # Register namespaces api.add_namespace(tfs_ns, path="/tfs") api.add_namespace(ixia_ns, path="/ixia") api.add_namespace(e2e_ns, path="/e2e") api.add_namespace(restconf_ns, path="/restconf") if app.config["WEBUI_DEPLOY"]: app.secret_key = "clave-secreta-dev" app.register_blueprint(gui_bp) # Registered after Api and namespaces to ensure flask-restx does not override this hook @app.before_request def enforce_basic_auth(): """Reject any request that does not include valid Basic Auth credentials.""" # Allow unauthenticated access to Swagger UI and its static assets if request.path.startswith(UNAUTHENTICATED_PREFIXES): return None if not _check_basic_auth(app): return _unauthorized_response() return app if __name__ == "__main__": app = create_app() app.run(host="0.0.0.0", port=NSC_PORT, debug=True) No newline at end of file