Skip to content
Snippets Groups Projects
Commit 25babf95 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch 'feat/195-opt-integrate-qkd-tests-in-tfs-ci-cd-pipeline' into 'develop'

Resolve "(OPT) Integrate QKD tests in TFS CI/CD pipeline"

See merge request !268
parents 459c6f14 5d2d4546
No related branches found
No related tags found
2 merge requests!294Release TeraFlowSDN 4.0,!268Resolve "(OPT) Integrate QKD tests in TFS CI/CD pipeline"
Showing
with 234 additions and 57 deletions
......@@ -669,6 +669,10 @@ if [[ "$TFS_COMPONENTS" == *"monitoring"* ]] && [[ "$TFS_COMPONENTS" == *"webui"
printf "\n\n"
fi
echo "Pruning Docker Images..."
docker image prune --force
printf "\n\n"
if [ "$DOCKER_BUILD" == "docker buildx build" ]; then
echo "Pruning Docker Buildx Cache..."
docker buildx prune --force
......
......@@ -62,10 +62,10 @@ spec:
name: nbiservice
port:
number: 8080
- path: /()(qkd_app/.*)
- pathType: Prefix
- path: /()(qkd_app/.*)
pathType: Prefix
backend:
service:
name: qkdappservice
name: qkd-appservice
port:
number: 8005
......@@ -15,16 +15,16 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: qkdappservice
name: qkd-appservice
spec:
selector:
matchLabels:
app: qkdappservice
app: qkd-appservice
#replicas: 1
template:
metadata:
labels:
app: qkdappservice
app: qkd-appservice
spec:
terminationGracePeriodSeconds: 5
containers:
......@@ -36,10 +36,8 @@ spec:
- containerPort: 9192
- containerPort: 8005
env:
- name: MB_BACKEND
value: "nats"
- name: LOG_LEVEL
value: "INFO"
value: "DEBUG"
- name: CRDB_DATABASE_APP
value: "qkd_app"
envFrom:
......@@ -64,13 +62,13 @@ spec:
apiVersion: v1
kind: Service
metadata:
name: qkdappservice
name: qkd-appservice
labels:
app: qkdappservice
app: qkd-appservice
spec:
type: ClusterIP
selector:
app: qkdappservice
app: qkd-appservice
ports:
- name: grpc
protocol: TCP
......
#!/bin/bash
# Copyright 2022-2024 ETSI OSG/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.
########################################################################################################################
# Define your deployment settings here
########################################################################################################################
# If not already set, set the name of the Kubernetes namespace to deploy to.
export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"}
########################################################################################################################
# Automated steps start here
########################################################################################################################
kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/qkd-appservice -c server
......@@ -38,6 +38,30 @@ build device:
- manifests/${IMAGE_NAME}service.yaml
- .gitlab-ci.yml
## Start Mock QKD Nodes before unit testing
#start_mock_nodes:
# stage: deploy
# script:
# - bash src/tests/tools/mock_qkd_nodes/start.sh &
# - sleep 10 # wait for nodes to spin up
# artifacts:
# paths:
# - mock_nodes.log
# rules:
# - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
# - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
## Prepare Scenario (Start NBI, mock services)
#prepare_scenario:
# stage: deploy
# script:
# - pytest src/tests/qkd/unit/PrepareScenario.py
# needs:
# - start_mock_nodes
# rules:
# - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
# - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
# Apply unit test to the component
unit_test device:
variables:
......@@ -46,6 +70,8 @@ unit_test device:
stage: unit_test
needs:
- build device
#- start_mock_nodes
#- prepare_scenario
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- >
......@@ -68,6 +94,7 @@ unit_test device:
- docker logs $IMAGE_NAME
- docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_emulated.py --junitxml=/opt/results/${IMAGE_NAME}_report_emulated.xml"
- docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_ietf_actn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_actn.xml"
#- docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/qkd/unit/test_*.py"
- docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
after_script:
......
{
"contexts": [
{"context_id": {"context_uuid": {"uuid": "admin"}}}
],
"topologies": [
{"topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}
],
"devices": [
{
"device_id": {"device_uuid": {"uuid": "QKD1"}}, "device_type": "qkd-node",
"device_operational_status": 0, "device_drivers": [12], "device_endpoints": [],
"device_config": {"config_rules": [
{"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
{"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "11111"}},
{"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
"scheme": "http"
}}}
]}
},
{
"device_id": {"device_uuid": {"uuid": "QKD2"}}, "device_type": "qkd-node",
"device_operational_status": 0, "device_drivers": [12], "device_endpoints": [],
"device_config": {"config_rules": [
{"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
{"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "22222"}},
{"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
"scheme": "http"
}}}
]}
},
{
"device_id": {"device_uuid": {"uuid": "QKD3"}}, "device_type": "qkd-node",
"device_operational_status": 0, "device_drivers": [12], "device_endpoints": [],
"device_config": {"config_rules": [
{"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
{"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "33333"}},
{"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
"scheme": "http"
}}}
]}
}
],
"links": [
{
"link_id": {"link_uuid": {"uuid": "QKD1/10.0.2.10:1001==QKD2/10.0.2.10:2001"}},
"link_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "QKD1"}}, "endpoint_uuid": {"uuid": "10.0.2.10:1001"}},
{"device_id": {"device_uuid": {"uuid": "QKD2"}}, "endpoint_uuid": {"uuid": "10.0.2.10:2001"}}
]
},
{
"link_id": {"link_uuid": {"uuid": "QKD2/10.0.2.10:2001==QKD1/10.0.2.10:1001"}},
"link_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "QKD2"}}, "endpoint_uuid": {"uuid": "10.0.2.10:2001"}},
{"device_id": {"device_uuid": {"uuid": "QKD1"}}, "endpoint_uuid": {"uuid": "10.0.2.10:1001"}}
]
},
{
"link_id": {"link_uuid": {"uuid": "QKD2/10.0.2.10:2002==QKD3/10.0.2.10:3001"}},
"link_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "QKD2"}}, "endpoint_uuid": {"uuid": "10.0.2.10:2002"}},
{"device_id": {"device_uuid": {"uuid": "QKD3"}}, "endpoint_uuid": {"uuid": "10.0.2.10:3001"}}
]
},
{
"link_id": {"link_uuid": {"uuid": "QKD3/10.0.2.10:3001==QKD2/10.0.2.10:2002"}},
"link_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "QKD3"}}, "endpoint_uuid": {"uuid": "10.0.2.10:3001"}},
{"device_id": {"device_uuid": {"uuid": "QKD2"}}, "endpoint_uuid": {"uuid": "10.0.2.10:2002"}}
]
}
]
}
......@@ -14,9 +14,11 @@
import pytest
import json
import os
os.environ['DEVICE_EMULATED_ONLY'] = 'YES'
from device.service.drivers.qkd.QKDDriver2 import QKDDriver
MOCK_QKD_ADDRRESS = '127.0.0.1'
MOCK_QKD_ADDRRESS = '10.0.2.10'
MOCK_PORT = 11111
@pytest.fixture
......
# Copyright 2022-2024 ETSI OSG/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.
import requests
QKD_ADDRESS = '10.0.2.10'
QKD_URL = 'http://{:s}/qkd_app/create_qkd_app'.format(QKD_ADDRESS)
QKD_REQUEST_1 = {
'app': {
'server_app_id': '1',
'client_app_id': [],
'app_status': 'ON',
'local_qkdn_id': '00000001-0000-0000-0000-0000000000',
'backing_qkdl_id': ['00000003-0002-0000-0000-0000000000']
}
}
print(requests.post(QKD_URL, json=QKD_REQUEST_1))
QKD_REQUEST_2 = {
'app': {
'server_app_id': '1',
'client_app_id': [],
'app_status': 'ON',
'local_qkdn_id': '00000003-0000-0000-0000-0000000000',
'backing_qkdl_id': ['00000003-0002-0000-0000-0000000000']
}
}
print(requests.post(QKD_URL, json=QKD_REQUEST_2))
......@@ -28,8 +28,8 @@ RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION,
class QKDAppClient:
def __init__(self, host=None, port=None):
if not host: host = get_service_host(ServiceNameEnum.APP)
if not port: port = get_service_port_grpc(ServiceNameEnum.APP)
if not host: host = get_service_host(ServiceNameEnum.QKD_APP)
if not port: port = get_service_port_grpc(ServiceNameEnum.QKD_APP)
self.endpoint = '{:s}:{:s}'.format(str(host), str(port))
LOGGER.debug('Creating channel to {:s}...'.format(self.endpoint))
self.channel = None
......
......@@ -29,7 +29,7 @@ class AppService(GenericGrpcService):
def __init__(
self, db_engine : sqlalchemy.engine.Engine, messagebroker : MessageBroker, cls_name: str = __name__
) -> None:
port = get_service_port_grpc(ServiceNameEnum.APP)
port = get_service_port_grpc(ServiceNameEnum.QKD_APP)
super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name)
self.app_servicer = AppServiceServicerImpl(db_engine, messagebroker)
......
......@@ -18,6 +18,6 @@ from common.tools.service.GenericRestServer import GenericRestServer
class RestServer(GenericRestServer):
def __init__(self, cls_name: str = __name__) -> None:
bind_port = get_service_port_http(ServiceNameEnum.APP)
base_url = get_service_baseurl_http(ServiceNameEnum.APP)
bind_port = get_service_port_http(ServiceNameEnum.QKD_APP)
base_url = get_service_baseurl_http(ServiceNameEnum.QKD_APP)
super().__init__(bind_port, base_url, cls_name=cls_name)
......@@ -23,8 +23,8 @@ killbg() {
trap killbg EXIT
pids=()
flask --app mock run --host 0.0.0.0 --port 11111 &
flask run --host 0.0.0.0 --port 11111 &
pids+=($!)
flask --app mock run --host 0.0.0.0 --port 22222 &
flask run --host 0.0.0.0 --port 22222 &
pids+=($!)
flask --app mock run --host 0.0.0.0 --port 33333
flask run --host 0.0.0.0 --port 33333
......@@ -23,7 +23,7 @@ yang_validator = YangValidator('etsi-qkd-sdn-node', ['etsi-qkd-node-types'])
nodes = {
'127.0.0.1:11111': {'node': {
'10.0.2.10:11111': {'node': {
'qkdn_id': '00000001-0000-0000-0000-000000000000',
},
'qkdn_capabilities': {
......@@ -54,7 +54,7 @@ nodes = {
{
'qkdi_id': '101',
'qkdi_att_point': {
'device':'127.0.0.1',
'device':'10.0.2.10',
'port':'1001'
},
'qkdi_capabilities': {
......@@ -69,7 +69,7 @@ nodes = {
}
},
'127.0.0.1:22222': {'node': {
'10.0.2.10:22222': {'node': {
'qkdn_id': '00000002-0000-0000-0000-000000000000',
},
'qkdn_capabilities': {
......@@ -100,7 +100,7 @@ nodes = {
{
'qkdi_id': '201',
'qkdi_att_point': {
'device':'127.0.0.1',
'device':'10.0.2.10',
'port':'2001'
},
'qkdi_capabilities': {
......@@ -109,7 +109,7 @@ nodes = {
{
'qkdi_id': '202',
'qkdi_att_point': {
'device':'127.0.0.1',
'device':'10.0.2.10',
'port':'2002'
},
'qkdi_capabilities': {
......@@ -124,7 +124,7 @@ nodes = {
}
},
'127.0.0.1:33333': {'node': {
'10.0.2.10:33333': {'node': {
'qkdn_id': '00000003-0000-0000-0000-000000000000',
},
'qkdn_capabilities': {
......@@ -155,7 +155,7 @@ nodes = {
{
'qkdi_id': '301',
'qkdi_att_point': {
'device':'127.0.0.1',
'device':'10.0.2.10',
'port':'3001'
},
'qkdi_capabilities': {
......
......@@ -84,11 +84,11 @@ COPY --chown=webui:webui src/service/__init__.py service/__init__.py
COPY --chown=webui:webui src/service/client/. service/client/
COPY --chown=webui:webui src/slice/__init__.py slice/__init__.py
COPY --chown=webui:webui src/slice/client/. slice/client/
COPY --chown=webui:webui src/app/__init__.py app/__init__.py
COPY --chown=webui:webui src/app/client/. app/client/
COPY --chown=webui:webui src/webui/. webui/
COPY --chown=webui:webui src/qkd_app/__init__.py qkd_app/__init__.py
COPY --chown=webui:webui src/qkd_app/client/. qkd_app/client/
COPY --chown=webui:webui src/bgpls_speaker/__init__.py bgpls_speaker/__init__.py
COPY --chown=webui:webui src/bgpls_speaker/client/. bgpls_speaker/client/
COPY --chown=webui:webui src/webui/. webui/
# Start the service
ENTRYPOINT ["python", "-m", "webui.service"]
......@@ -83,32 +83,32 @@ def create_app(use_config=None, web_app_root=None):
app.register_blueprint(healthz, url_prefix='/healthz')
from webui.service.js.routes import js # pylint: disable=import-outside-toplevel
from webui.service.js.routes import js # pylint: disable=import-outside-toplevel
app.register_blueprint(js)
from webui.service.main.routes import main # pylint: disable=import-outside-toplevel
from webui.service.main.routes import main # pylint: disable=import-outside-toplevel
app.register_blueprint(main)
from webui.service.load_gen.routes import load_gen # pylint: disable=import-outside-toplevel
from webui.service.load_gen.routes import load_gen # pylint: disable=import-outside-toplevel
app.register_blueprint(load_gen)
from webui.service.service.routes import service # pylint: disable=import-outside-toplevel
from webui.service.service.routes import service # pylint: disable=import-outside-toplevel
app.register_blueprint(service)
from webui.service.slice.routes import slice # pylint: disable=import-outside-toplevel,redefined-builtin
from webui.service.slice.routes import slice # pylint: disable=import-outside-toplevel,redefined-builtin
app.register_blueprint(slice)
from webui.service.device.routes import device # pylint: disable=import-outside-toplevel
from webui.service.device.routes import device # pylint: disable=import-outside-toplevel
app.register_blueprint(device)
from webui.service.bgpls.routes import bgpls # pylint: disable=import-outside-toplevel
from webui.service.bgpls.routes import bgpls # pylint: disable=import-outside-toplevel
app.register_blueprint(bgpls)
from webui.service.link.routes import link # pylint: disable=import-outside-toplevel
from webui.service.link.routes import link # pylint: disable=import-outside-toplevel
app.register_blueprint(link)
from webui.service.qkd_app.routes import qkd_app as _qkd_app # pylint: disable=import-outside-toplevel
app.register_blueprint(_qkd_app)
from webui.service.qkd_app.routes import qkd_app # pylint: disable=import-outside-toplevel
app.register_blueprint(qkd_app)
from webui.service.policy_rule.routes import policy_rule # pylint: disable=import-outside-toplevel
app.register_blueprint(policy_rule)
......
......@@ -43,8 +43,8 @@ def main():
get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
get_env_var_name(ServiceNameEnum.SLICE, ENVVAR_SUFIX_SERVICE_HOST ),
get_env_var_name(ServiceNameEnum.SLICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
get_env_var_name(ServiceNameEnum.APP, ENVVAR_SUFIX_SERVICE_HOST ),
get_env_var_name(ServiceNameEnum.APP, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
get_env_var_name(ServiceNameEnum.QKD_APP, ENVVAR_SUFIX_SERVICE_HOST ),
get_env_var_name(ServiceNameEnum.QKD_APP, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
])
logger.info('Starting...')
......
......@@ -25,12 +25,12 @@ from qkd_app.client.QKDAppClient import QKDAppClient
LOGGER = logging.getLogger(__name__)
app = Blueprint('qkd_app', __name__, url_prefix='/qkd_app')
qkd_app = Blueprint('qkd_app', __name__, url_prefix='/qkd_app')
qkd_app_client = QKDAppClient()
context_client = ContextClient()
@app.get('/')
@qkd_app.get('/')
def home():
if 'context_uuid' not in session or 'topology_uuid' not in session:
flash("Please select a context!", "warning")
......@@ -68,10 +68,10 @@ def home():
context_client.close()
return render_template(
'app/home.html', apps=apps, device_names=device_names, ate=QKDAppTypesEnum, ase=QKDAppStatusEnum)
'qkd_app/home.html', apps=apps, device_names=device_names, ate=QKDAppTypesEnum, ase=QKDAppStatusEnum)
@app.route('detail/<path:app_uuid>', methods=('GET', 'POST'))
@qkd_app.route('detail/<path:app_uuid>', methods=('GET', 'POST'))
def detail(app_uuid: str):
'''
context_client.connect()
......@@ -87,7 +87,7 @@ def detail(app_uuid: str):
'''
pass
@app.get('<path:app_uuid>/delete')
@qkd_app.get('<path:app_uuid>/delete')
def delete(app_uuid):
'''
try:
......
......@@ -105,9 +105,9 @@
</li>
<li class="nav-item">
{% if '/qkd_app/' in request.path %}
<a class="nav-link active" aria-current="page" href="{{ url_for('app.home') }}">App</a>
<a class="nav-link active" aria-current="page" href="{{ url_for('qkd_app.home') }}">QKD Apps</a>
{% else %}
<a class="nav-link" href="{{ url_for('app.home') }}">App</a>
<a class="nav-link" href="{{ url_for('qkd_app.home') }}">QKD Apps</a>
{% endif %}
</li>
<li class="nav-item">
......
......@@ -41,7 +41,7 @@
{% for app in apps %}
<tr>
<td>
{{ app.app_id.app_uuid.uuid }}
{{ app.app_id.app_uuid.uuid }}
</td>
<td>
{{ ase.Name(app.app_status).replace('QKDAPPSTATUS_', '') }}
......@@ -74,12 +74,14 @@
{% endif %}
</td>
<td>
<a href="{{ url_for('app.detail', app_uuid=app.app_id.app_uuid.uuid) }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
</svg>
</a>
<!--
<a href="{{ url_for('qkd_app.detail', app_uuid=app.app_id.app_uuid.uuid) }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
</svg>
</a>
-->
</td>
</tr>
{% endfor %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment