Skip to content
Snippets Groups Projects
Commit 5c2de56d authored by trantzas's avatar trantzas
Browse files

fix for #9

- adding code block annotations
- minor changes in deploymentK8s.md and deploymentCompose.md
parent 45b10439
No related branches found
No related tags found
1 merge request!14Resolve "Creating Release 2024Q4 documentation"
Pipeline #10906 passed
...@@ -37,7 +37,7 @@ sudo nano /etc/docker/daemon.json ...@@ -37,7 +37,7 @@ sudo nano /etc/docker/daemon.json
and add: and add:
``` ```json
{ {
"dns": ["8.8.8.8", "8.8.4.4"] "dns": ["8.8.8.8", "8.8.4.4"]
} }
...@@ -140,7 +140,7 @@ If you want to utilise the Bugzilla connector: ...@@ -140,7 +140,7 @@ If you want to utilise the Bugzilla connector:
In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml`
``` ```json
SPRING_APPLICATION_JSON: '{ SPRING_APPLICATION_JSON: '{
"spring.activemq.brokerUrl": "tcp://anartemis:61616?jms.watchTopicAdvisories=false", "spring.activemq.brokerUrl": "tcp://anartemis:61616?jms.watchTopicAdvisories=false",
"spring.activemq.user": "artemis", "spring.activemq.user": "artemis",
...@@ -227,7 +227,7 @@ Change the respective fields: ...@@ -227,7 +227,7 @@ Change the respective fields:
In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml`
``` ```json
SPRING_APPLICATION_JSON: '{ SPRING_APPLICATION_JSON: '{
"spring.datasource.username":"root", "spring.datasource.username":"root",
"spring.datasource.password":"letmein", "spring.datasource.password":"letmein",
...@@ -251,7 +251,7 @@ Change the respective fields: ...@@ -251,7 +251,7 @@ Change the respective fields:
In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml`
``` ```json
SPRING_APPLICATION_JSON: '{ SPRING_APPLICATION_JSON: '{
"spring.datasource.username":"root", "spring.datasource.username":"root",
"spring.datasource.password":"letmein", "spring.datasource.password":"letmein",
...@@ -297,14 +297,14 @@ Edit the `config.js` file with the information of your domain. `ROOTURL` will au ...@@ -297,14 +297,14 @@ Edit the `config.js` file with the information of your domain. `ROOTURL` will au
Example file: Example file:
``` ```json
{ {
BUGZILLA: "ROOTURL/bugzilla/", "BUGZILLA": "ROOTURL/bugzilla/",
STATUS: "ROOTURL/status/", "STATUS": "ROOTURL/status/",
APIURL: "http://localhost", "APIURL": "http://localhost",
WEBURL: "ROOTURL/nfvportal", "WEBURL": "ROOTURL/nfvportal",
APIOAUTHURL: "ROOTURL/auth/realms/openslice", "APIOAUTHURL": "ROOTURL/auth/realms/openslice",
APITMFURL: "ROOTURL/tmf-api/serviceCatalogManagement/v4" "APITMFURL": "ROOTURL/tmf-api/serviceCatalogManagement/v4"
} }
``` ```
...@@ -330,7 +330,7 @@ E.g. You may edit "TITLE", "WIKI", etc properties with your domain title. Also c ...@@ -330,7 +330,7 @@ E.g. You may edit "TITLE", "WIKI", etc properties with your domain title. Also c
Example file: Example file:
``` ```json
{ {
"TITLE": "OpenSlice by ETSI", "TITLE": "OpenSlice by ETSI",
"PORTALVERSION":"2024Q2", "PORTALVERSION":"2024Q2",
...@@ -479,9 +479,7 @@ Hosts File Location: ...@@ -479,9 +479,7 @@ Hosts File Location:
2 - Replace http://localhost/auth/ with http://keycloak:8080/auth/ in your Keycloak config for AngularJS and Angular (see examples below). 2 - Replace http://localhost/auth/ with http://keycloak:8080/auth/ in your Keycloak config for AngularJS and Angular (see examples below).
> Explanation > **Explanation**: Nginx uses the http://keycloak:8080 URL, which is accessible via the internal docker system's network.
Nginx uses the http://keycloak:8080 URL, which is accessible via the internal docker system's network.
The Front-end (TS/Angular) shall also use the http://keycloak:8080. The Front-end (TS/Angular) shall also use the http://keycloak:8080.
This way, you will not get the invalid token error, as the API is acquiring the token from http://keycloak:8080 (internally) and the Front-end is getting verified by an issuer at the same URL, as well. This way, you will not get the invalid token error, as the API is acquiring the token from http://keycloak:8080 (internally) and the Front-end is getting verified by an issuer at the same URL, as well.
...@@ -500,7 +498,7 @@ nano config.prod.json ...@@ -500,7 +498,7 @@ nano config.prod.json
After editing, the displayed properties should look like the example below: After editing, the displayed properties should look like the example below:
```yaml ```json
{ {
"OAUTH_CONFIG" : { "OAUTH_CONFIG" : {
"issuer": "http://keycloak:8080/auth/realms/openslice", "issuer": "http://keycloak:8080/auth/realms/openslice",
...@@ -531,7 +529,7 @@ nano config.js ...@@ -531,7 +529,7 @@ nano config.js
After editing, the displayed properties should look like the example below: After editing, the displayed properties should look like the example below:
``` ```js
var appConfig = angular.module('portalwebapp.config',[]); var appConfig = angular.module('portalwebapp.config',[]);
......
...@@ -20,16 +20,17 @@ ...@@ -20,16 +20,17 @@
* **Helm:** For managing the deployment of OpenSlice. * **Helm:** For managing the deployment of OpenSlice.
* **Ingress Controller:** Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource. An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name-based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic. You must have an Ingress controller to satisfy an Ingress. * **Ingress Controller:** Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource. An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name-based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic. You must have an Ingress controller to satisfy an Ingress.
* **Nginx Ingress Controller (Kubernetes Community Edition):** The ingress resource is configured to use an Nginx type ingress controller. * **Nginx Ingress Controller (Kubernetes Community Edition):** The ingress resource is configured to use an Nginx type ingress controller.
* If you need to expose the message bus service (Artemis), which communicates using the TCP protocol, you must use version **>= 1.9.13** of the Nginx Ingress Controller (a prerequisite for [managing multiple kubernetes clusters](#management-of-multiple-kubernetes-clusters)). This version or higher includes the required functionality to handle TCP services. Otherwise, earlier versions may suffice depending on your configuration. * If you need to expose the message bus service (Artemis), which communicates using the TCP protocol, you must use version **>= 1.9.13** of the Nginx Ingress Controller (a prerequisite for [managing multiple kubernetes clusters](#management-of-multiple-kubernetes-clusters)). This version or higher includes the required functionality to handle TCP services. Otherwise, earlier versions may suffice depending on your configuration.
* To install or upgrade to the required version, run the following command: * To install or upgrade to the required version, run the following command:
``` bash ``` bash
helm upgrade nginx-ingress ingress-nginx/ingress-nginx --namespace ingress \ helm upgrade nginx-ingress ingress-nginx/ingress-nginx --namespace ingress \
--set tcp.61616="<openslice-namespace>/<openslice-helm-release-name>-artemis:61616" --set tcp.61616="<openslice-namespace>/<openslice-helm-release-name>-artemis:61616"
``` ```
Replace `<helm-release-name>` with the name of your OpenSlice Helm release.
Replace `<helm-release-name>` with the name of your OpenSlice Helm release.
* More details regarding the Nginx Ingress Controller (Kubernetes Community Edition) can be found [here](https://kubernetes.github.io/ingress-nginx/deploy/). * More details regarding the Nginx Ingress Controller (Kubernetes Community Edition) can be found [here](https://kubernetes.github.io/ingress-nginx/deploy/).
* **Other Ingress Controller:** For non-Nginx ingress controllers, modify `[repo-root]/kubernetes/helm/openslice/templates/openslice-ingress.yaml` to meet your controller’s requirements. * **Other Ingress Controller:** For non-Nginx ingress controllers, modify `[repo-root]/kubernetes/helm/openslice/templates/openslice-ingress.yaml` to meet your controller’s requirements.
### Exposure ### Exposure
...@@ -52,7 +53,8 @@ rooturl: http://<master-node-ip>:<nodeport> ...@@ -52,7 +53,8 @@ rooturl: http://<master-node-ip>:<nodeport>
### Additional Configuration ### Additional Configuration
* **Storage Class:** In a production environment, specify your `storageClass` in `[repo-root]/kubernetes/helm/openslice/values.yaml` under `storageClass`. If not defined, PVs will be created and managed manually. * **Storage Class:** In a production environment, specify your `storageClass` in `[repo-root]/kubernetes/helm/openslice/values.yaml` under `storageClass`. If not defined, PVs will be created and managed manually.
* **Disclaimer:** Before deploying, confirm that your storage system supports claims of one 10G and two 1G volumes.
> **Disclaimer:** Before deploying, confirm that your storage system supports claims of one 10G and two 1G volumes.
## Preparing the environment ## Preparing the environment
...@@ -94,9 +96,7 @@ We recommend: ...@@ -94,9 +96,7 @@ We recommend:
When deploying OpenSlice with Helm, service configurations are handled through the `values.yaml` file. This file allows you to define all necessary configurations for your deployment, including database credentials, service URLs, and logging levels. Below are examples of how to configure your services in Helm based on your provided values. When deploying OpenSlice with Helm, service configurations are handled through the `values.yaml` file. This file allows you to define all necessary configurations for your deployment, including database credentials, service URLs, and logging levels. Below are examples of how to configure your services in Helm based on your provided values.
### Configuring Services ### Database
#### 1. Database Configuration
To configure MySQL and other related services, you can directly set the values in your `values.yaml` file under the `oscreds` and `mysql` sections. For example: To configure MySQL and other related services, you can directly set the values in your `values.yaml` file under the `oscreds` and `mysql` sections. For example:
...@@ -117,7 +117,7 @@ oscreds: ...@@ -117,7 +117,7 @@ oscreds:
password: "12345" password: "12345"
``` ```
#### 2. Keycloak Configuration ### Keycloak
Keycloak settings, including the database and admin password, are part of the `oscreds.mysql.keycloak` section. If you need to adjust Keycloak-specific settings like realms or client configurations, you'll likely need to customize your Helm chart further or manage these settings directly within Keycloak after deployment. The Keycloak realm configuration that is imported by default can be found under `kubernetes/helm/openslice/files/keycloak-init/realm-export.json`. Keycloak settings, including the database and admin password, are part of the `oscreds.mysql.keycloak` section. If you need to adjust Keycloak-specific settings like realms or client configurations, you'll likely need to customize your Helm chart further or manage these settings directly within Keycloak after deployment. The Keycloak realm configuration that is imported by default can be found under `kubernetes/helm/openslice/files/keycloak-init/realm-export.json`.
...@@ -131,7 +131,7 @@ oscreds: ...@@ -131,7 +131,7 @@ oscreds:
adminpassword: "Pa55w0rd" adminpassword: "Pa55w0rd"
``` ```
#### 3. CRIDGE Configuration ### CRIDGE
To create and manage Kubernetes Custom Resources (CRs), you have to install and configure the CRIDGE component. To create and manage Kubernetes Custom Resources (CRs), you have to install and configure the CRIDGE component.
...@@ -139,7 +139,7 @@ For CRIDGE to work properly, you need to provide a **cluster-wide scope kubeconf ...@@ -139,7 +139,7 @@ For CRIDGE to work properly, you need to provide a **cluster-wide scope kubeconf
There are two ways to install CRIDGE: There are two ways to install CRIDGE:
##### 3.1 **Bundled CRIDGE deployment with the OpenSlice Helm chart (same cluster environment)** #### **Bundled CRIDGE deployment with the OpenSlice Helm chart (same cluster environment)**
By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. To configure CRIDGE, there are three different ways to provide this kubeconfig file during deployment: By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. To configure CRIDGE, there are three different ways to provide this kubeconfig file during deployment:
...@@ -154,9 +154,9 @@ By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. T ...@@ -154,9 +154,9 @@ By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. T
- If you do not wish to manually copy the file, you can pass it directly during the Helm installation using the `--set-file` option, at the final [deployment process](#deploy-the-helm-chart): - If you do not wish to manually copy the file, you can pass it directly during the Helm installation using the `--set-file` option, at the final [deployment process](#deploy-the-helm-chart):
```bash ```bash
--set-file cridge.kubeconfig.raw=path/to/kubeconfig.yaml --set-file cridge.kubeconfig.raw=path/to/kubeconfig.yaml
``` ```
- This method reads the specified kubeconfig file and mounts it into the CRIDGE container during deployment. - This method reads the specified kubeconfig file and mounts it into the CRIDGE container during deployment.
...@@ -164,15 +164,15 @@ By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. T ...@@ -164,15 +164,15 @@ By default, the OpenSlice Helm chart also deploys CRIDGE alongside the bundle. T
- Alternatively, you can pass the kubeconfig as a base64-encoded string, during the Helm installation using the `--set` option, at the final [deployment process](#deploy-the-helm-chart): - Alternatively, you can pass the kubeconfig as a base64-encoded string, during the Helm installation using the `--set` option, at the final [deployment process](#deploy-the-helm-chart):
```bash ```bash
--set cridge.kubeconfig.base64="$(base64 path/to/kubeconfig.yaml)" --set cridge.kubeconfig.base64="$(base64 path/to/kubeconfig.yaml)"
``` ```
- This method encodes the kubeconfig content and passes it directly to the CRIDGE container. - This method encodes the kubeconfig content and passes it directly to the CRIDGE container.
> **Note:** Regardless of the method you choose, if you're using a non-standard kubeconfig file name, make sure to adjust the references or rename the file as needed. > **Note:** Regardless of the method you choose, if you're using a non-standard kubeconfig file name, make sure to adjust the references or rename the file as needed.
##### 3.2 **Standalone CRIDGE deployment** #### **Standalone CRIDGE deployment**
There can be cases where a separate deployment of CRIDGE, apart from the bundled OpenSlice deployment, may be needed. These cases comprise: There can be cases where a separate deployment of CRIDGE, apart from the bundled OpenSlice deployment, may be needed. These cases comprise:
...@@ -204,18 +204,18 @@ Similarly, to configure CRIDGE, there are three different ways to provide this k ...@@ -204,18 +204,18 @@ Similarly, to configure CRIDGE, there are three different ways to provide this k
2. **Passing the Kubeconfig File Using Helm (`--set-file`)**: 2. **Passing the Kubeconfig File Using Helm (`--set-file`)**:
- If you do not wish to manually copy the file, you can pass it directly during the Helm installation using the `--set-file` option: - If you do not wish to manually copy the file, you can pass it directly during the Helm installation using the `--set-file` option:
```bash ```bash
helm install cridge-release . --set-file kubeconfig.raw=path/to/kubeconfig.yaml helm install cridge-release . --set-file kubeconfig.raw=path/to/kubeconfig.yaml
``` ```
- This method reads the specified kubeconfig file and mounts it into the CRIDGE container during deployment. - This method reads the specified kubeconfig file and mounts it into the CRIDGE container during deployment.
3. **Passing a Base64-Encoded Kubeconfig Using Helm (`--set`)**: 3. **Passing a Base64-Encoded Kubeconfig Using Helm (`--set`)**:
- Alternatively, you can pass the kubeconfig as a base64-encoded string: - Alternatively, you can pass the kubeconfig as a base64-encoded string:
```bash ```bash
helm install cridge-release . --set kubeconfig.base64="$(base64 path/to/kubeconfig.yaml)" helm install cridge-release . --set kubeconfig.base64="$(base64 path/to/kubeconfig.yaml)"
``` ```
- This method encodes the kubeconfig content and passes it directly to the CRIDGE container. - This method encodes the kubeconfig content and passes it directly to the CRIDGE container.
...@@ -231,14 +231,14 @@ oscreds: ...@@ -231,14 +231,14 @@ oscreds:
brokerUrl: "tcp://<openslice-rootURL>:61616?jms.watchTopicAdvisories=false" brokerUrl: "tcp://<openslice-rootURL>:61616?jms.watchTopicAdvisories=false"
``` ```
##### Management of multiple Kubernetes Clusters #### Management of multiple Kubernetes Clusters
OpenSlice also offers management support of *multiple Kubernetes Clusters* simultaneously. OpenSlice also offers management support of *multiple Kubernetes Clusters* simultaneously.
For this, you will have to replicate the steps in [Standalone CRIDGE deployment](#32-standalone-cridge-deployment) for every Cluster. Each CRIDGE instance will be in charged with the management of one Kubernetes Cluster. For this, you will have to replicate the steps in [Standalone CRIDGE deployment](#32-standalone-cridge-deployment) for every Cluster. Each CRIDGE instance will be in charged with the management of one Kubernetes Cluster.
#### 4. External Services Configuration ### External Services (optional)
For configuring optional external services like Bugzilla and CentralLog, specify their URLs and credentials in the `values.yaml` file: For configuring optional external services like Bugzilla and CentralLog, specify their URLs and credentials in the `values.yaml` file:
...@@ -259,7 +259,7 @@ Bugzilla should have the following components under the specified product: ...@@ -259,7 +259,7 @@ Bugzilla should have the following components under the specified product:
Also in the 'Main Site Operations' product, a version named 'unspecified' must be created. Also in the 'Main Site Operations' product, a version named 'unspecified' must be created.
#### 5. Application and Logging Configuration ### Application and Logging
Application-specific configurations, such as OAuth client secrets, can be set in the `spring` section: Application-specific configurations, such as OAuth client secrets, can be set in the `spring` section:
...@@ -268,7 +268,7 @@ spring: ...@@ -268,7 +268,7 @@ spring:
oauthClientSecret: "secret" oauthClientSecret: "secret"
``` ```
#### 6. Ingress and Root URL ### Ingress and Root URL
To configure the ingress controller and root URL for OpenSlice, update the rooturl field with your ingress load balancer IP or domain. This setting is crucial for external access to your application: To configure the ingress controller and root URL for OpenSlice, update the rooturl field with your ingress load balancer IP or domain. This setting is crucial for external access to your application:
...@@ -278,7 +278,7 @@ rooturl: "http://openslice.com" # Example domain ...@@ -278,7 +278,7 @@ rooturl: "http://openslice.com" # Example domain
rooturl: "http://3.15.198.35:8080" # Example IP with port rooturl: "http://3.15.198.35:8080" # Example IP with port
``` ```
#### 7. Persistent Volume for MySQL ### Persistent Volume for MySQL
For persistent storage, especially for MySQL, define the storage size under the `mysql` section. This ensures that your database retains data across pod restarts and deployments. For persistent storage, especially for MySQL, define the storage size under the `mysql` section. This ensures that your database retains data across pod restarts and deployments.
...@@ -287,7 +287,7 @@ mysql: ...@@ -287,7 +287,7 @@ mysql:
storage: "10Gi" storage: "10Gi"
``` ```
#### 8. Configuring TCP Forwarding for Artemis ### TCP Forwarding for Artemis
To expose the message bus service (Artemis) via the ingress controller, it’s essential to configure TCP traffic forwarding. Artemis listens on port `61616`, and this traffic needs to be directed to the Artemis service within your Kubernetes cluster. To expose the message bus service (Artemis) via the ingress controller, it’s essential to configure TCP traffic forwarding. Artemis listens on port `61616`, and this traffic needs to be directed to the Artemis service within your Kubernetes cluster.
...@@ -296,7 +296,7 @@ In the [Ingress Controller Setup](#software-requirements) section, you already c ...@@ -296,7 +296,7 @@ In the [Ingress Controller Setup](#software-requirements) section, you already c
This setup ensures that the message bus service is accessible externally via the ingress controller, completing the necessary configuration for Artemis. This setup ensures that the message bus service is accessible externally via the ingress controller, completing the necessary configuration for Artemis.
### Configure Web UI ### Web UI
In folder `kubernetes/helm/openslice/files/org.etsi.osl.portal.web/src/js` you must make a copy of `config.js.default` file and rename it to `config.js`. In folder `kubernetes/helm/openslice/files/org.etsi.osl.portal.web/src/js` you must make a copy of `config.js.default` file and rename it to `config.js`.
...@@ -304,7 +304,7 @@ This is **mandatory** for the configuration file to be discoverable. ...@@ -304,7 +304,7 @@ This is **mandatory** for the configuration file to be discoverable.
Edit the `config.js` configuration file with your static configuration, if needed. Edit the `config.js` configuration file with your static configuration, if needed.
``` ```js
{ {
TITLE: "OpenSlice by ETSI", TITLE: "OpenSlice by ETSI",
WIKI: "https://osl.etsi.org/documentation/", WIKI: "https://osl.etsi.org/documentation/",
...@@ -319,7 +319,7 @@ Edit the `config.js` configuration file with your static configuration, if neede ...@@ -319,7 +319,7 @@ Edit the `config.js` configuration file with your static configuration, if neede
### Configure TMF Web UI ### TMF Web UI
In the folder `kubernetes/helm/openslice/files/org.etsi.osl.tmf.web/src/assets/config` there are 3 files available for configuration: In the folder `kubernetes/helm/openslice/files/org.etsi.osl.tmf.web/src/assets/config` there are 3 files available for configuration:
...@@ -343,7 +343,7 @@ cd kubernetes/helm/openslice/files/org.etsi.osl.tmf.web/src/assets/config ...@@ -343,7 +343,7 @@ cd kubernetes/helm/openslice/files/org.etsi.osl.tmf.web/src/assets/config
E.g. You may edit "TITLE", "WIKI", etc properties with your domain title. Also configure TMF's API and Keycloak's location for the web application, if needed. E.g. You may edit "TITLE", "WIKI", etc properties with your domain title. Also configure TMF's API and Keycloak's location for the web application, if needed.
``` ```json
{ {
"TITLE": "OpenSlice by ETSI", "TITLE": "OpenSlice by ETSI",
"PORTALVERSION":"2024Q2", "PORTALVERSION":"2024Q2",
...@@ -475,9 +475,9 @@ If a pod is not in the expected state, you can access its logs for troubleshooti ...@@ -475,9 +475,9 @@ If a pod is not in the expected state, you can access its logs for troubleshooti
kubectl logs <pod-name> -n openslice kubectl logs <pod-name> -n openslice
``` ```
## Post installation steps ## Post installation steps (mandatory)
After the successful deployment of OpenSlice, to ensure the E2E user experience, **this section is mandatory**. It contains crucial configuration in regard of authentication and user creation. After the successful deployment of OpenSlice, to ensure the end-to-end user experience, **this section is mandatory**. It contains crucial configuration in regard of authentication and user creation.
### Configure Keycloak server ### Configure Keycloak server
......
...@@ -37,6 +37,7 @@ theme: ...@@ -37,6 +37,7 @@ theme:
- navigation.path - navigation.path
- search - search
- search.highlight - search.highlight
- content.code.copy
palette: palette:
primary: 'cyan' primary: 'cyan'
...@@ -48,6 +49,15 @@ theme: ...@@ -48,6 +49,15 @@ theme:
icon: icon:
repo: fontawesome/brands/gitlab repo: fontawesome/brands/gitlab
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
# Copyright # Copyright
copyright: "Copyright &copy; 2024 ETSI OSL" copyright: "Copyright &copy; 2024 ETSI OSL"
......
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