diff --git a/.gitignore b/.gitignore index 92cc944781b7320d9389d251d193666af0323dfe..5b3ea2538f9c16bf9c4f49d2090e2007e8880869 100644 --- a/.gitignore +++ b/.gitignore @@ -161,16 +161,17 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -logs +*/logs config -test/logs - *.vscode .flake8 *.DS_store -netapp-samples/netapp-provider-sample/node_modules \ No newline at end of file +*/__pycache__ + +*.capif_sdk_config_sample_test +test/capif_sdk_config_sample_test.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ba00662d298726cbd83893822bfe43cb4b88adf0..0000000000000000000000000000000000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "cSpell.words": [ - "AEF's", - "aefs", - "APF's", - "apfs", - "CAPIF", - "detailspath", - "libintl", - "localeconv", - "localemodule", - "Offboard", - "offboarding", - "onboarded", - "pesp", - "pyenv", - "textdomain", - "Unpublish", - "virtualenv" - ] -} \ No newline at end of file diff --git a/README.md b/README.md index d36d464c4d3ffac0ffd55041ecb09b497f6956e9..c00abc8200d25a17b36c429acfd4bf06d2570258 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,72 @@ - - # OpenCAPIF SDK - + -This repository develops a Python Software Development Kit(SDK) which focuses on connecting to OpenCAPIF (Common API Framework for 3GPP Northbound APIs) in a simple way, lowering integration complexity and allowing developers to focus on Network Applications (NetApps) or services development. +This repository develops a Python Software Development Kit(SDK) which focuses on connecting to OpenCAPIF (Common API Framework for 3GPP Northbound APIs) in a simple way, lowering integration complexity and allowing developers to focus on Network Applications (Network Apps) or services development. OpenCAPIF SDK provides a set of libraries to enable either CAPIF provider and invoker roles, and other functions to simplify procedures calls towards OpenCAPIF entity. Current version of OpenCAPIF SDK is compatible with following publicly available releases: - [OpenCAPIF Release 1.0](https://ocf.etsi.org/documentation/v1.0.0-release/) -This document serves as the [main bootstrap reference](#netapp-developer-path) to start working with OpenCAPIF SDK. For advanced users, refer to [OpenCAPIF full documentation](./doc/sdk-full-documentation.md) section to dig into all available features. +This document serves as the [main bootstrap reference](#networkapp-developer-path) to start working with OpenCAPIF SDK. For advanced users, refer to [OpenCAPIF full documentation](./doc/sdk_full_documentation.md) section to dig into all available features. # Table of Contents - 1. [NetApp developers](#netapp-developers) + 1. [Network App developers](#network-app-developers) 2. [OpenCAPIF SDK summary](#opencapif-sdk-summary) 3. [OpenCAPIF SDK requirements](#opencapif-sdk-requirements) - 4. [NetApp developer path](#netapp-developer-path) - 5. [OpenCAPIF SDK Configuration](./doc/sdk-configuration.md) - 6. [**OpenCAPIF SDK full documentation**](./doc/sdk-full-documentation.md) - 7. [OpenCAPIF SDK known issues](./doc/sdk-issues.md) - 8. [OpenCAPIF SDK developers](./doc/sdk-installation.md) + 4. [Network App developer path](#network-app-developer-path) + 5. [OpenCAPIF SDK Configuration](./doc/sdk_configuration.md) + 6. [**OpenCAPIF SDK full documentation**](./doc/sdk_full_documentation.md) + 7. [OpenCAPIF SDK known issues](#opencapif-sdk-known-issues) + 8. [OpenCAPIF SDK developers](./doc/sdk_developers.md) + +# Repository structure + + pesp_capif_sdk + ├── config + ├── doc + │  └── images + ├── installation + ├── network_app_samples + │  ├── network_app_invoker_sample + │  │  └── postman + │  └── network_app_provider_sample + ├── samples + ├── scripts + ├── sdk + └── test + +- [config](./config/): contains OpenCAPIF SDK configuration files samples. These samples illustrate the structure of the configuration files ir order to use SDK properly. Go to the [configuration section](./doc/sdk_configuration.md) for more details, +- [doc](./doc/): contains documentation related files to this README, +- [installation](./installation/): this folder stores the Pyhton [requeriments.txt](./installation/requirements.txt) file that is required to complete the [SDK developers section](./doc/sdk_developers.md), +- [network_app_samples](./network_app_samples/): this folder contains both provider and invoker Network App samples explained further in this document at [network app developer path](#network-app-developer-path), +- [samples](./samples/): contains sample files related to SDK configuration, API definitions and SDK configuration via environment variables, +- [scripts](./scripts/): single scripts to individually test functionality though command line. For more information on how to use these go to the [full documentation section](./doc/sdk_full_documentation.md), +- [sdk](./sdk/): where SDK code is stored, +- [test](./test/): contains a file named test.py containing tests to ensure all SDK flows work properly. + +# Network App developers -# NetApp developers +In the scope of CAPIF, a Network App (Network Application) refers to an external application or service that interacts with the 3GPP network via standardized APIs. These Network Apps typically leverage the capabilities and services provided by the underlying mobile network infrastructure, such as network slicing, quality of service (QoS), or location services. -In the scope of CAPIF, a NetApp (Network Application) refers to an external application or service that interacts with the 3GPP network via standardized APIs. These NetApps typically leverage the capabilities and services provided by the underlying mobile network infrastructure, such as network slicing, quality of service (QoS), or location services. +Network Apps can be developed by third-party service providers, network operators, or other stakeholders to offer a wide range of services, including enhanced communication features, IoT solutions, or content delivery, and they use CAPIF as the unified framework for securely discovering, accessing, and utilizing 3GPP network APIs. -NetApps can be developed by third-party service providers, network operators, or other stakeholders to offer a wide range of services, including enhanced communication features, IoT solutions, or content delivery, and they use CAPIF as the unified framework for securely discovering, accessing, and utilizing 3GPP network APIs. +Next image illustrates how CAPIF works and where the SDK provides means to integrate with it: -For that purpose NetApps play 2 different roles when interacting with CAPIF: -- **Invoker**: a NetApp acting as an Invoker is responsible for consuming APIs exposed by other services. This role represents an external application or service that calls the 3GPP northbound APIs to utilize the network’s functionalities. + -- **Provider**: a NetApp acting as a Provider is responsible for exposing its own APIs//services for use by Invokers. This role represents an entity that offers services through APIs, making them available to other external applications or Invokers. +For that purpose Network Apps play 2 different roles when interacting with CAPIF: +- **Invoker**: a Network App acting as an Invoker is responsible for consuming APIs exposed by other services. This role represents an external application or service that calls the 3GPP northbound APIs to utilize the network’s functionalities. -**NetApps developers are the target users of OpenCAPIF SDK.** +- **Provider**: a Network App acting as a Provider is responsible for exposing its own APIs//services for use by Invokers. This role represents an entity that offers services through APIs, making them available to other external applications or Invokers.A provider also is distinguished for having three parts. + - The AMF, supplies the API provider domain with administrative capabilities. Some of these capabilities include, auditing the service API invocation logs received from the CCF, on-boarding/off-boarding new API invokers and monitoring the status of the service APIs.One provider can have only one AMF. + + - The APF (API Publishing Function), is responsible for the publication of the service APIs to CCF in order to enable the discovery capability to the API Invokers.One provider can have multiple APFs. + + - AEF(API Exposing Function), is responsible for the exposure of the service APIs. Assuming that API Invokers are authorized by the CCF, AEF validates the authorization and subsequently provides the direct communication entry points to the service APIs. AEF may also authorize API invokers and record the invocations in log files.One provider can have multiple AEFs + +**Network Apps developers are the target users of OpenCAPIF SDK.** ## OpenCAPIF SDK summary @@ -42,20 +74,20 @@ OpenCAPIF SDK brings a set of functions to integrate with the 5G Core's function | **3GPP CAPIF API** | **OpenCAPIF SDK function** | **Description** | |-------------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------| -| /onboardedInvokers (POST) | onboard_invoker() | Registers a new invoker. | -| /onboardedInvokers/{onboardingId} (PUT) | update_invoker() | Updates an existing invoker for a specific `onboardingId`. | -| /onboardedInvokers/{onboardingId} (DELETE) | offboard_invoker() | Deletes an invoker for a specific `onboardingId`. | -| registrations (POST) | onboard_provider() | Registers a new service provider. | -| /registrations/{registrationId} (PUT) | update_provider() | Updates a service provider's registration for a specific `registrationId`. | -| /registrations/{registrationId} (DELETE) | offboard_provider() | Deletes a service provider's registration for a specific `registrationId`. | -| /allServiceAPIs (GET) | discover() | Retrieves a list of all available service APIs. | -| /trustedInvokers (PUT//POST) | discover() | Registers or updates trusted invokers. | -| /securities/{securityId}/token (GET) | get_tokens() | Retrieves a security token for a specific `securityId`. This JWT token is used to query the targeted services. | -| /{apfId}/service-apis(POST) | publish_services() | Registers a new service API into the system for a specific `apfId` | -| /{apfId}/service-apis/{serviceApiId} (DELETE) | unpublish_service() | Deletes a service API from the system for a specific `apfId`and `serviceApiId` | -| /{apfId}/service-apis/{serviceApiId} (PUT) | update_service() | Updates the details of an existing service API for a specific `apfId`and `serviceApiId` | -| /{apfId}/service-apis/{serviceApiId} (GET) | get_service() | Retrieves the details of a specific service API for a specific `apfId` and `serviceApiId` | -| /{apfId}/service-apis (GET) | get_all_services() | Retrieves a list of all available service APIs for a specific `apfId` | +| /onboardedInvokers (POST) | [onboard_invoker()](./doc/sdk_full_documentation.md#invoker-onboarding) | Registers a new invoker. | +| /onboardedInvokers/{onboardingId} (PUT) | [update_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Updates an existing invoker for a specific `onboardingId`. | +| /onboardedInvokers/{onboardingId} (DELETE) | [offboard_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Deletes an invoker for a specific `onboardingId`. | +| registrations (POST) | [onboard_provider()](./doc/sdk_full_documentation.md#provider-onboarding) | Registers a new service provider. | +| /registrations/{registrationId} (PUT) | [update_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Updates a service provider's registration for a specific `registrationId`. | +| /registrations/{registrationId} (DELETE) | [offboard_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Deletes a service provider's registration for a specific `registrationId`. | +| /allServiceAPIs (GET) | [discover()](./doc/sdk_full_documentation.md#discover-process) | Retrieves a list of all available service APIs. | +| /trustedInvokers (PUT//POST) | [discover()](./doc/sdk_full_documentation.md#discover-process) | Registers or updates trusted invokers. | +| /securities/{securityId}/token (GET) | [get_tokens()](./doc/sdk_full_documentation.md#obtain-invoker-tokens) | Retrieves a security token for a specific `securityId`. This JWT token is used to query the targeted services. | +| /{apfId}/service-apis(POST) | [publish_services()](./doc/sdk_full_documentation.md#services-publishing) | Registers a new service API into the system for a specific `apfId` | +| /{apfId}/service-apis/{serviceApiId} (DELETE) | [unpublish_service()](./doc/sdk_full_documentation.md#services-deletion) | Deletes a service API from the system for a specific `apfId`and `serviceApiId` | +| /{apfId}/service-apis/{serviceApiId} (PUT) | [update_service()](./doc/sdk_full_documentation.md#services-update) | Updates the details of an existing service API for a specific `apfId`and `serviceApiId` | +| /{apfId}/service-apis/{serviceApiId} (GET) | [get_service()](./doc/sdk_full_documentation.md#get-services) | Retrieves the details of a specific service API for a specific `apfId` and `serviceApiId` | +| /{apfId}/service-apis (GET) | [get_all_services()](./doc/sdk_full_documentation.md#get-all-services) | Retrieves a list of all available service APIs for a specific `apfId` | NOTE: Above mentioned CAPIF APIs are defined in these 3GPP references: @@ -72,126 +104,228 @@ NOTE: In the [3GPP Technical Specification (TS) 29.222 V18.5.0 Common API Framew To leverage the OpenCAPIF SDK, it must have a registered user in the target CAPIF instance. **Please contact the administrator to obtain the necessary predefined credentials (username and password).** -# Netapp developer path +# Network App developer path -The NetApp Developer Path guides the programmer through building and integrating NetApps using CAPIF. This path is divided into two key sections: [Invoker NetApp](#invoker-netapp-1) and Provider NetApp. Each section covers the essential flow and functions for developing NetApps interaction with CAPIF, whether the user is acting as an invoker consuming services or a provider offering them. By following this path, developers will gain a comprehensive understanding of how to effectively use the SDK within the CAPIF ecosystem. +The Network App Developer Path guides the programmer through building and integrating Network Apps using CAPIF. This path is divided into two key sections: [Invoker Network App](#invoker-network-app) and [Provider Network App](#provider-network-app). Each section covers the essential flow and functions for developing Network Apps interaction with CAPIF, whether the user is acting as an invoker consuming services or a provider offering them. By following this path, developers will gain a comprehensive understanding of how to effectively use the SDK within the CAPIF ecosystem. -Here is a good illustration about how a usual flow of a Netapp should work +Here is a good explanation about how a usual flow of a Network App should work: [usual flow example](https://ocf.etsi.org/documentation/latest/testing/postman/) -[EXAMPLE](https://ocf.etsi.org/documentation/latest/testing/postman/) +## Provider Network App -## Invoker NetApp +A Network App development running as a Provider would typically follow this process step by step, making use of the SDK: -Here is a code sample of the implementation of this hole [Invoker-Netapp](./netapp-samples/netapp-invoker-sample/netapp-invoker.py) sample. + -1. **Create an Invoker object:** \ - Initialize the invoker by creating an instance of the `CAPIFInvokerConnector` class, passing the required configuration file: +Now, it is described in 4 simple steps how a Provider can be developed in just some code lines, below snippet. It describes the usual flow a Provider would follow to publish an API service. + +```python + provider = capif_provider_connector(config_file="path/to/the/capif_sdk_config.json") + provider.onboard_provider() + capif_connector.api_description_path = "./nef-upf-partner-1.json" + provider_folder = capif_connector.provider_folder + + APF = capif_connector.api_prov_funcs["APF-1"] + + AEF1 = capif_connector.api_prov_funcs["AEF-1"] + AEF2 = capif_connector.api_prov_funcs["AEF-2"] + + capif_connector.publish_req['publisher_apf_id'] = APF + capif_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2] + provider.publish_services() +``` + +1. **Create a Provider object:** \ + Initialize the provider by creating an instance of the `capif_provider_connector` class, passing the required [configuration](./doc/sdk_configuration.md) file: + + Make sure that the configuration file is filled before creating the instance. + + +2. **Onboard the Provider:** \ + Register the provider with the CAPIF system to enable the publication of APIs: + + In this phase, the SDK creates and stores all the necessary files for using CAPIF as a provider, such as the authorization certificate, the server certificate and each of the APFs and AEFs certificates .Furthermore creates a file named `capif_provider_details.json`, which stores important information about the provider. + +3. **Prepare API details:** \ + In the `provider_folder`, more specifically in the `capif_username` folder, it will be located the provider API details file.\ + This file contains all the APFs and AEFs ids that have already onboarded with this `capif_username`.\ + Also it is very important to have previously prepared the API description of the API is going to be published.\ + This file needs to follow the [CAPIF_Publish_Service_API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml). + Choose one APF and the AEFs identifiers, and fulfill the publish_req structure and the api_description_path: + + <p align="center"> + <img src="./doc/images/capif_provider_details_example.png" alt="example" width="400"/> + <img src="./doc/images/publish_req_example.png" alt="config-example" width="400"/> + </p> + + The api_prov_funcs variable is a dictionary which contains key-values of all the APFs and AEFs stored as name: ID. + + This publish_req field can also be filled with object variables already stored at capif_connector. + +4. **Publish the services:** \ + Use the publish_services() method to register the APIs with the CAPIF framework(don't forget to update the capif_provider_connector constructor in order to use the new APF and AEFs): + + In this phase, the SDK does the publishing of the provided API specification. + + **At the end of this step, the API will be available for Invokers to be consumed.** + +Now, Provider Network App is ready to receive requests from Invokers. + +### Provider Network App sample + +This repository provides an implementation sample of a [Provider-Network App](./network_app_samples/network_app_provider_sample/network_app_provider.py). + +In this sample, the provider publishes two APIs and starts running the servers of each API on local environment. + +### Important information for Provider consumers + +Within the `provider_folder`, the SDK stores the created folders named with prefix of the provided `capif_username` that has been registered from administrator. At each folder, there will be found the following files: + +- `capif_provider_details.json`: contains all the APFs and AEFs ids that have already onboarded with this `capif_username`, +- `capif_<api_name>_<api_id>.json`: if it is already published or updated an API, it will contain a copy of the last payload, +- `service_received.json`: if it is already used to get an API or get all APIs functionality, it will contain the response of last request, +- `published-apis.json`: contains the currently published APIs with their `api_id`. + +All the configuration values are available within the object capif_provider_connector, also the api_prov_funcs variable is a dict which contains as a key all the APFs and AEFs, as a value contains all APFs and AEFs ids. + + +## Invoker Network App + +A Network App development running as an Invoker would typically follow this process step by step, making use of the SDK: + + + +Now, it is described in some simple steps how an Invoker can be developed in just some code lines. Find below the code snippet. It describes the usual flow an Invoker would follow to consume APIs from CAPIF. - ```python - invoker = CAPIFInvokerConnector(config_file=utilities.get_config_file()) - ``` +```python + invoker = capif_invoker_connector(config_file="path/to/the/capif_sdk_config.json") + invoker.onboard_invoker() + service_discoverer = ServiceDiscoverer(config_file=utilities.get_config_file()) + service_discoverer.discover() + service_discoverer.get_tokens() +``` + +Code is explained next step by step: + +1. **Create an Invoker object:** \ + Initialize the invoker by creating an instance of the `capif_invoker_connector` class, passing the required [configuration](./doc/sdk_configuration.md) file. + + Make sure that the configuration file is filled out before creating the instance. 2. **Onboard the Invoker**: \ - Register the invoker with the CAPIF system to enable access to APIs: + Register the target invoker with the CAPIF system to enable access to APIs. - ```python - invoker.onboard_invoker() - ``` + In this phase, the SDK creates and stores all the necessary files for using CAPIF as a invoker, such as the authorization certificate and the server certificate.Furthermore,it creates a file named `capif_api_security_context_details.json` , which stores important information about the invoker. -3. **Create a Service Discoverer object:** \ - Initialize the service discovery mechanism to search for available services: +3. **Create a Service Discoverer object:** \ + Initialize the service discovery mechanism to search for available services(APIs) in CAPIF. - ```python - service_discoverer = ServiceDiscoverer(config_file=utilities.get_config_file()) - ``` +4. **Discover available services:** \ + Use the `discover()` method to retrieve a list of available APIs. In this phase, the SDK finds all the available APIs for the invoker. Consequently, it saves the most important information and stores it within the `capif_api_security_context_details.json`. -4. **Discover available services:** \ - Use the discover() method to retrieve a list of available APIs: + **DISCLAIMER:** If it is the first time the user runs `discover()`, it will show a warning alert like following: - DISCLAIMER: if it's the first time the user runs discover() it will appear a warning alert like this + WARNING - Received 404 error, redirecting to register security service - WARNING - Received 404 error, redirecting to register security service + This alert is expected because the SDK tries to update the security context first. If a 404 error is received, it means the security context is not created yet, so the next step for the SDK is to register a new security service. - ```python - service_discoverer.discover() - ``` +5. **Retrieve security tokens:** \ + Use the get_tokens() method to obtain the necessary tokens for authenticating API requests. - -5. **Retrieve security tokens:** \ - Use the get_tokens() method to obtain the necessary tokens for authenticating API requests: - - ```python - invoker.get_tokens() - ``` -Then all information for using the available APIs would be at Capif_api_security_context_details.json +**At the end of this flow, the invoker has been onboarded and it is ready to use target APIs.** All required information, including the access_token to use the available APIs, is stored at `capif_api_security_context_details.json` file. This file is placed in the invoker_folder path, specifically in the folder that corresponds to the capif_username used in the capif_sdk_config.json. A sample of the [capif_api_security_context_details](./samples/capif_api_security_context_details_sample.json) is also available. -This file would be placed in the invoker_folder path, more specifically in the folder that corresponds of the capif_username used in the config_file. +Now, Invoker Network App can use access tokens to consume real services. -Here is a sample of this Capif_api_security_context_details [file](./samples/capif_api_security_context_details-sample.json) +### Invoker Network App sample -This image highlights Invoker path using SDK code: +Here is a code sample of the implementation of an [Invoker-Network App](./network_app_samples/network_app_invoker_sample/network_app_invoker.py). - +In this sample, the invoker will discover the APIs published by the sample provider shown in this document and will return the access token for querying the APIs. This sample is prepared to run after the [Provider-Network App](./network_app_samples/network_app_provider_sample/network_app_provider.py). -### Important information for Invoker consumer +Make sure that the [Provider-Network App](./network_app_samples/network_app_provider_sample/network_app_provider.py) is running before following this implementation. -In the `invoker_folder`, it will be located several folders with each `capif_username` it has been onboarded as a provider. For each folder, it could be found: +For testing APIs availability, after running both samples([Provider-Network App](./network_app_samples/network_app_provider_sample/network_app_provider.py) and [Invoker-Network App](./network_app_samples/network_app_invoker_sample/network_app_invoker.py)) the invoker app will return the access token. -- `Capif_api_security_context_details.json`: This file contains the information of the invoker. It will contain: - - 1. The `api_invoker_id`. - 2. If the Service Discovery Functionality has already been used , it will be found all the available APIs with their information. - 3. If the Service Get Token functionality has already been used , it will be found the access token for using the APIs that has already been discovered. +Also, in the same Invoker-Network folder is available a [Postman structure](./network_app_samples/network_app_invoker_sample/postman/).In order to test these APIs, the access token returned in the Invoker-Network App must be placed in the Postman environment, more specifically in the access_token variable. -## Provider NetApp +Another alternative is to import the [Postman structure](./network_app_samples/network_app_invoker_sample/postman/) in your own postman account and fill the `postman_api_key` and the `environment_id` fields within the [Invoker-Network App](./network_app_samples/network_app_invoker_sample/network_app_invoker.py). Here is an example of these two fields that need to be fulfilled. -A NetApp development running as a Provider would typically follow this process step by step, making use of the SDK: +```python + # Your Postman API Key + postman_api_key = "AAAA-your-apikey" + + # Postman Environment ID + environment_id = "your-environment-id-must-be-here" +``` -Here is a sample of the implementation of this hole [Provider-Netapp](./netapp-samples/netapp-provider-sample/netapp-provider.py) +Now, in the next steps is going to be illustrated the usual flow of an Invoker that wants to use obtain the access token to use an API service: -1. **Create a Provider object:** \ - Initialize the provider by creating an instance of the `CAPIFProviderConnector` class, passing the required configuration file: +1. **Create an Invoker object:** \ + Initialize the invoker by creating an instance of the `capif_invoker_connector` class, passing the required [configuration](./doc/sdk_configuration.md) file: + + Make sure that the configuration file is filled before creating the instance. - ```python - provider = CAPIFProviderConnector(config_file=utilities.get_config_file()) - ``` + +2. **Onboard the Invoker**: \ + Register the invoker with the CAPIF system to enable access to APIs: -2. **Onboard the Provider:** \ - Register the provider with the CAPIF system to enable the publication of APIs: + In this phase, the SDK creates and stores all the necessary files for using CAPIF as a invoker, such as the authorization certificate and the server certificate .Furthermore,it creates a file named `capif_api_security_context_details.json` , which stores important information about the invoker. - ```python - provider.onboard_provider() - ``` +3. **Create a Service Discoverer object:** \ + Initialize the service discovery mechanism to search for available services -3. **Prepare API details:** \ - Locate the provider API details in the provider_folder path, more specifically in the username folder. Choose the APF (API Publishing Function) and AEFs (API Exposing Function) identifiers, and fulfil the publish_req structure and the api_description_path: +4. **Discover available services:** \ + Use the `discover()` method to retrieve a list of available APIs.\ + **DISCLAIMER:** If it's the first time the user runs `discover()`, it will show a warning alert like this: -<p align="center"> - <img src="./doc/images/capif-provider-details-example.png" alt="example" width="400"/> - <img src="./doc/images/publish_req-example.png" alt="config-example" width="400"/> -</p> + **WARNING** - Received 404 error, redirecting to register security service -4. **Publish the services:** \ - Use the publish_services() method to register the APIs with the CAPIF framework(don't forget to update the CAPIFProviderConnector constructor in order to use the new APF and AEFs): + This alert is expected because the SDK tries to update the security context first. If a 404 error is received, it means the security context is not created yet, so the SDK's next step is to register a new security service. - ```python - provider = CAPIFProviderConnector(config_file=utilities.get_config_file()) - provider.publish_services() - ``` + In this phase, the SDK will find all the available APIs for the invoker.Consequently,it will save the most important information and will store it within the `capif_api_security_context_details.json`. -Here is a sample of the implementation of this whole [functionality](./netapp-samples/netapp-provider-sample/netapp-provider.py). -This image highlights Provider path using SDK code: +5. **Retrieve security tokens:** \ + Use the get_tokens() method to obtain the necessary tokens for authenticating API requests: + - +```python + invoker = capif_invoker_connector(config_file="path/to/the/capif_sdk_config.json") + invoker.onboard_invoker() + service_discoverer = ServiceDiscoverer(config_file=utilities.get_config_file()) + service_discoverer.discover() + invoker.get_tokens() +``` +Then all information,including the access_token for using the available APIs, will be at `capif_api_security_context_details.json` file -### Important information for Provider consumer +This file would be placed in the invoker_folder path, more specifically in the folder that corresponds of the capif_username used in the config_file. -Within the `provider_folder`, the SDK will store some folders named with the provided `capif_username` it has been registered from administrator. At each folder, there will be found the following files: +Here is a sample of this capif_api_security_context_details [file](./samples/capif_api_security_context_details_sample.json) -- `capif_provider_details.json`: contains all the APFs and AEFs ids that have already onboarded with this `capif_username`, -- `CAPIF_<api_name><API_id>.json`: if it is already published or updated an API, it will contain a copy of the last payload, -- `service_received.json`: if it is already used to get an API or get all APIs functionality, it will contain the response of last request, -- `published-apis.json`: Contains the currently published APIs with their `ApiId`. +This image highlights Invoker path using SDK code: + + + +### Important information for Invoker consumer + +In the `invoker_folder`, it will be located several folders with each `capif_username` it has been onboarded as a provider. For each folder, it will be found: + +- `capif_api_security_context_details.json`: This file contains the information of the invoker. It will contain: + + 1. The `api_invoker_id`, + 2. If the Service Discovery Functionality has already been used , it will be found all the available APIs with their information, + 3. If the Service Get Token functionality has already been used , it will be found the access token for using the APIs that has already been discovered. + + +# OpenCAPIF SDK known issues +There are some features which **are not currently available at latest OpenCAPIF SDK release**. Those are assumed to be technical debt and might be available in future releases: + - [CAPIF Access control policy management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Access_Control_Policy_API.yaml) + - [CAPIF Auditing API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Auditing_API.yaml) + - [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) + - [CAPIF Logging API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Logging_API_Invocation_API.yaml) + - [CAPIF Routing info API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Routing_Info_API.yaml) + - [CAPIF Security API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Security_API.yaml) + - /trustedInvokers/{apiInvokerId}/delete (POST) + - /trustedInvokers/{apiInvokerId} (GET) + - /trustedInvokers/{apiInvokerId} (DELETE) diff --git a/config/capif-sdk-config.json b/config/capif-sdk-config.json deleted file mode 100644 index 9e96b920d790e9097b78ba7bd89791bd4fceeec7..0000000000000000000000000000000000000000 --- a/config/capif-sdk-config.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", - "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", - "capif_host": "capif-prev.mobilesandbox.cloud", - "register_host": "registercapif-prev.mobilesandbox.cloud", - "capif_https_port": "36212", - "capif_register_port": "36211", - "capif_callback_url": "http://localhost:5000", - "csr_common_name": "test03", - "csr_organizational_unit": "test_app_ou", - "csr_organization": "test_app_o", - "crs_locality": "Madrid", - "csr_state_or_province_name": "Madrid", - "csr_country_name": "ES", - "csr_email_address": "test@example.com", - "capif_username": "echeva_0", - "capif_password": "echevapass", - "apfs": "1", - "aefs": "2", - "debug_mode": "True", - "discover_filter" : { - "api-name": "", - "api-version": "", - "comm-type": "", - "protocol": "", - "aef-id": "", - "data-format": "", - "api-cat": "", - "preferred-aef-loc": "", - "req-api-prov-name": "", - "supported-features": "", - "api-supported-features": "", - "ue-ip-addr": "", - "service-kpis": "" - }, - "publish_req" : { - "service_api_id":"80dbdd52ee766d4ad4494264e4289c", - "publisher_apf_id":"APF73e3458fb483c3c65f2f7e126ec851", - "publisher_aefs_ids":["AEF07a01ccd74a160c195e69b4f116d66","AEFb5c206b46fc68c192aed6870899ea1"] - }, - "api_description_path":"/Users/IDB0128/git_repos/pesp_capif_sdk/samples/provider_api_description_sample.json" -} \ No newline at end of file diff --git a/config/capif_sdk_config.json b/config/capif_sdk_config.json new file mode 100644 index 0000000000000000000000000000000000000000..2e16e6d90d401c7043cac5d4580f24b420606808 --- /dev/null +++ b/config/capif_sdk_config.json @@ -0,0 +1,60 @@ +{ + "capif_host": "capif-prev.mobilesandbox.cloud", + "register_host": "registercapif-prev.mobilesandbox.cloud", + "capif_https_port": "36212", + "capif_register_port": "36211", + "capif_username": "echeva_0", + "capif_password": "echevapass", + "debug_mode": "False", + "invoker":{ + "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", + "capif_callback_url": "http://localhost:5000", + "cert_generation":{ + "csr_common_name": "invoker", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + } + }, + "provider":{ + "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", + "apfs": "1", + "aefs": "2", + "publish_req": { + "service_api_id": "", + "publisher_apf_id": "", + "publisher_aefs_ids": [ + "", + "" + ] + }, + "cert_generation":{ + "csr_common_name": "provider", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "api_description_path": "/Users/IDB0128/git_repos/pesp_capif_sdk/samples/provider_api_description_sample.json" + } +} \ No newline at end of file diff --git a/config/capif-sdk-register.json b/config/capif_sdk_register.json similarity index 100% rename from config/capif-sdk-register.json rename to config/capif_sdk_register.json diff --git a/doc/images/Flujo completo-INVOKER PATH.jpg b/doc/images/Flujo completo-INVOKER PATH.jpg deleted file mode 100644 index 6866f19958296a19379bebd2ad7902b04045ebe8..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-INVOKER PATH.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-OPENCAPIF ACTUAL.jpg b/doc/images/Flujo completo-OPENCAPIF ACTUAL.jpg deleted file mode 100644 index 9a610b5143d004809318da06a3d24b0070fe83bb..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-OPENCAPIF ACTUAL.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-PROVIDER PATH.jpg b/doc/images/Flujo completo-PROVIDER PATH.jpg deleted file mode 100644 index 0071bed417968e52a707b193af0d0d1733e0d30d..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-PROVIDER PATH.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-SDK ACTUAL CON REGISTER.jpg b/doc/images/Flujo completo-SDK ACTUAL CON REGISTER.jpg deleted file mode 100644 index 6672270064499194267ad1103921e75ee1038166..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-SDK ACTUAL CON REGISTER.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-invoker_discover.jpg b/doc/images/Flujo completo-invoker_discover.jpg deleted file mode 100644 index 8e39b8754ad8b5f0605c041dc679dd88c6d5e6ca..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-invoker_discover.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-invoker_get_tokens.jpg b/doc/images/Flujo completo-invoker_get_tokens.jpg deleted file mode 100644 index 994e9f99501c9c451ac8019edef78ee7205edab1..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-invoker_get_tokens.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-invoker_onboard.jpg b/doc/images/Flujo completo-invoker_onboard.jpg deleted file mode 100644 index ffd4a761144549e72a9091be24515dbc8947cd1a..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-invoker_onboard.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-invoker_update_offboard.jpg b/doc/images/Flujo completo-invoker_update_offboard.jpg deleted file mode 100644 index dcc3a6d6e8cf3b134a1562d4ed379a1508221f42..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-invoker_update_offboard.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-provider_onboard.jpg b/doc/images/Flujo completo-provider_onboard.jpg deleted file mode 100644 index 450e0d5d665d224ce6bd851cd395745eedf8f5a6..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-provider_onboard.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-provider_publish_functions.jpg b/doc/images/Flujo completo-provider_publish_functions.jpg deleted file mode 100644 index 135034ea3f250cd34c83a4b110725a5a3e4d6cc1..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-provider_publish_functions.jpg and /dev/null differ diff --git a/doc/images/Flujo completo-provider_update_offboard.jpg b/doc/images/Flujo completo-provider_update_offboard.jpg deleted file mode 100644 index 93a7fa5267fe74b396dadb17883b166f72427a5a..0000000000000000000000000000000000000000 Binary files a/doc/images/Flujo completo-provider_update_offboard.jpg and /dev/null differ diff --git a/doc/images/capif-provider-details-example.png b/doc/images/capif_provider_details_example.png similarity index 100% rename from doc/images/capif-provider-details-example.png rename to doc/images/capif_provider_details_example.png diff --git a/doc/images/flows_capif_illustration.jpg b/doc/images/flows_capif_illustration.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a1c1302980d9386586246b789198fbf0a3da6ec Binary files /dev/null and b/doc/images/flows_capif_illustration.jpg differ diff --git a/doc/images/flows_invoker_discover.jpg b/doc/images/flows_invoker_discover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e490c6a914a295e068fc598c29c7bf47d038ce0a Binary files /dev/null and b/doc/images/flows_invoker_discover.jpg differ diff --git a/doc/images/flows_invoker_get_tokens.jpg b/doc/images/flows_invoker_get_tokens.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc2aaec46a4bd3247059fddda0fcf33f4039be4e Binary files /dev/null and b/doc/images/flows_invoker_get_tokens.jpg differ diff --git a/doc/images/flows_invoker_onboard.jpg b/doc/images/flows_invoker_onboard.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49c20dc026669a91159897a85d86c546eb1f80de Binary files /dev/null and b/doc/images/flows_invoker_onboard.jpg differ diff --git a/doc/images/flows_invoker_path.jpg b/doc/images/flows_invoker_path.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff7457f06a5d41c7a5b7657fdf924f555afc165f Binary files /dev/null and b/doc/images/flows_invoker_path.jpg differ diff --git a/doc/images/flows_invoker_update_offboard.jpg b/doc/images/flows_invoker_update_offboard.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9b092fff8d8736a5c08f7c1d1a664cfaa4034d06 Binary files /dev/null and b/doc/images/flows_invoker_update_offboard.jpg differ diff --git a/doc/images/flows_provider_onboard.jpg b/doc/images/flows_provider_onboard.jpg new file mode 100644 index 0000000000000000000000000000000000000000..70c942a75120b9eb1e05bb9305699d8b79fc5584 Binary files /dev/null and b/doc/images/flows_provider_onboard.jpg differ diff --git a/doc/images/flows_provider_path.jpg b/doc/images/flows_provider_path.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1d5dbc536bb344e7dcbdcd524a02412fdc4eb2c2 Binary files /dev/null and b/doc/images/flows_provider_path.jpg differ diff --git a/doc/images/flows_provider_publish_functions.jpg b/doc/images/flows_provider_publish_functions.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5d0020355a72d612f7a46887514d8c91cb21b414 Binary files /dev/null and b/doc/images/flows_provider_publish_functions.jpg differ diff --git a/doc/images/flows_provider_update_offboard.jpg b/doc/images/flows_provider_update_offboard.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f592642aa175c03217c5544b4102beb3a8d23e1 Binary files /dev/null and b/doc/images/flows_provider_update_offboard.jpg differ diff --git a/doc/images/flows_sdk_with_register.jpg b/doc/images/flows_sdk_with_register.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ece6ce2087e4bdef23b3991e41409a2a39bb379 Binary files /dev/null and b/doc/images/flows_sdk_with_register.jpg differ diff --git a/doc/images/flows_updated_opencapif.jpg b/doc/images/flows_updated_opencapif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1bef5f1da3faca689919311e173d1a66e742187e Binary files /dev/null and b/doc/images/flows_updated_opencapif.jpg differ diff --git a/doc/images/OpenCAPIF-icon.jpg b/doc/images/opencapif_icon.jpg similarity index 100% rename from doc/images/OpenCAPIF-icon.jpg rename to doc/images/opencapif_icon.jpg diff --git a/doc/images/publish_req-example.png b/doc/images/publish_req_example.png similarity index 100% rename from doc/images/publish_req-example.png rename to doc/images/publish_req_example.png diff --git a/doc/sdk-configuration.md b/doc/sdk-configuration.md deleted file mode 100644 index 67407ec6e50b4512464af77841ea6a0fd39332e9..0000000000000000000000000000000000000000 --- a/doc/sdk-configuration.md +++ /dev/null @@ -1,132 +0,0 @@ -# OpenCAPIF SDK configuration - -Before configuration it's needed to have completed the [requirements](./sdk-prerequirements.md) section and the [installation](./sdk-installation.md) section. - -- [Configuration via capif-sdk-config.json](#1-configuration-via-capif-sdk-configjson) - - [As a netapp invoker](#netapp-invoker) - - [As a netapp provider](#netapp-provider) - - [Descriptions of capif-sdk-config fields](#descriptions-of-capif-sdk-config-fields) -- [Configuration via capif-sdk-register.json](#configuration-via-capif-sdk-registerjson) - -# **1. Configuration via capif-sdk-config.json** - -Mandatory fields no matter the target role to be onboarded, either invoker or provider: - -- capif_host -- register_host -- capif_https_port -- capif_register_port -- capif_username -- capif_password -- debug_mode - -## Netapp invoker - -If it is wanted to use SDK as an **Invoker** is mandatory to fill out these fields - -- invoker_folder -- capif_callback_url -- csr_information(csr_common_name,csr_country_name...) - -OPTIONAL: -- [discover_filter](#2configuration-via-discover_filterjson) - -## Netapp provider - -If it is wanted to use SDK as a **Provider** is mandatory to fill out these fields - -- provider_folder -- APFs -- AEFs - -Mandatory field for using [CAPIF Publish API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) : - -- [publish_req](#3-configuration-via-publishjson) - -- api_description_path - - -## **Configuration of discover_filter** -This file follows the parameters schema from the GET petition of [Discover Services API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Discover_Service_API.yaml) - -To use this feature it has to be completed `discover_filter` with the parameters it is wanted to be filtered in order to run the Invoker Service Discovery Functionality. - -To run the Invoker Service Discovery Functionality it has to be onboarded as an Invoker before the CAPIF user. - - -## **Configuration of publish_req** - -This configuration fields is only mandatory if we want to use the [CAPIF_Publish_Service_API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) - - - service_api_id: The Api id we want to use Example "02eff6e1b3a8f7c8044a92ee8a30bd" - - publisher_apf_id: APF id we chose to use Example : "APFa165364a379035d14311deadc04332" - - publisher_aefs_ids: Array of strings filled out of AEFs ids we want to use Example: ["AEFfa38f0e855bffb420e4994ecbc8fb9","AEFe8bfa711f4f0c95ba0b382508e6382"] - -For using the Publish Api function or the Update function the api_description_path **must** have been modified with the path to the Publish API that is wanted to share following the standard schema for [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) - -**AefIds fields won't need to fill out the from aefProfiles** array because it would already be configuring this fields by completing publisherAEFsids parameter - -**If the publisherAEFsids parameter don't match with the aefProfiles in the [Api description](../api-samples/provider_api_description_sample.json) SDK will raise an error** - -# Descriptions of capif-sdk-config fields - -This file could also be fulfilled by [environment variables](../samples/enviroment-variables-sample.txt) - -- invoker_folder: The path (relative or absolute) of the folder it is wanted to store the invoker information(Certifications,keys and important information for using CAPIF) - -- provider_folder:The path (relative or absolute) of the folder it is want to store it is provider information(Certifications,keys and important information for using CAPIF) - -- capif_host:The domain name of the capif host - -- register_host:The domain name of the register host - -- capif_https_port: The port of the capif host - -- capif_register_port:The port of the register host - -- capif_callback_url:The Url it is wanted to receive CAPIF notifications(This functionality is not currently available) - -- csr fields: Seven information fields for generating the invoker certificate(csr_country_name must be only two letters) - -- capif_username: CAPIF username - -- capif_password: CAPIF password - -- apfs: Number of APF's it is wanted to onboard as a provider Example:5 - - The [APF](https://www.3gpp.org/technologies/rnaa) is what connects to the Capif Core Function to publish the service -- aefs: Number of AEF's it is wanted to onboard as a provider Example:2 - - The [AEF](https://www.3gpp.org/technologies/rnaa) is what enables to Invokers to connect to API's - -- debug_mode: Boolean | If it is wanted to receive logs from SDK Example:True/False - -- [discover_filter](#2configuration-via-discover_filterjson) - -- [publish_req](#3-configuration-via-publishjson) - -- api_description_path : The path to the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) json - -# **Configuration via capif-sdk-register.json** - -In order to use this SDK in a Local environment and creating and removing users, its mandatory to fulfil this fields. - -This Feature is not included in the SDK. - -[Here](https://labs.etsi.org/rep/ocf/capif/-/tree/REL1?ref_type=heads) is the link to the CAPIF official repository if it's wanted to create the Local environment - -- register_host:The domain name of the register host -- capif_register_port: The port of the register host -- capif_register_username: CAPIF admin username -- capif_register_password: CAPIF admin password -- capif_username: CAPIF user username -- capif_password: CAPIF user password -- config_path: Absolute path to the Config_files folder -- uuid: UUID for Deregister the user - - -This file is only used for the Functionalities of : -- Register and login -- Deregister and login - -Each field is obligatory to complete except UUID, which is only obligatory in case of Deregister the user. - -Although this field is not obligatory we recommend to store the UUID parameter received by the Register and login functionality in this field. diff --git a/doc/sdk-full-documentation.md b/doc/sdk-full-documentation.md deleted file mode 100644 index e8046031ba2044aae9bd843349fdc2d2fc3e3b43..0000000000000000000000000000000000000000 --- a/doc/sdk-full-documentation.md +++ /dev/null @@ -1,242 +0,0 @@ -# OpenCAPIF SDK full documentation - -OpenCAPIF SDK implements this set of features to easily integrate an application with CAPIF NF either manually, using scripting, or integrating SDK library directly within app code. - -Before using it, it is required to have fulfilled the [requirements](./sdk-prerequirements.md) section, the [installation](./sdk-installation.md) section and the selected parts of the [configuration](./sdk-configuration.md) depending on the CAPIF role the NetApp is going to play. - - - -This repository includes 2 different modes to test OpenCAPIF SDK: - -- **Development usage**: import SDK in the code and start ... Within [nf-sample folder](../netapp-samples/), it is provided a sample application leveraging - -- **Manual usage**: use a set of python [scripts](../scripts/) to test step by step all implemented procedures. All of them are provided at scripts folder - - - For Manual usage it is required to complete the utilities file with the absolute paths of target environment in order to complete the configuration of the SDK: - -**NOTE**:register file is not needed for the use of the SDK - -**IMPORTANT**:It is needed to fill out config files depending on the features required to be used from the SDK.Please if it is not fulfilled this file, go to the [Configuration Section](./sdk-configuration.md) - -# Table of contents - -As explained before, OpenCAPIF SDK is designed to play two different roles: - -- [Provider NetApp](#provider-netapp) - - [Important information for Providers](#important-information-for-providers) - - [Onboard Provider](#provider-onboarding) - - [Publish services](#services-publishing) - - [Unpublish services](#services-deletion) - - [Update services](#services-update) - - [Get published services](#get-services) - - [Get all published services](#get-all-services) - - [Update and offboard Provider](#update-and-offboard-provider) -- [Invoker NetApp](#invoker-netapp) - - [Important information for Invokers](#important-information-for-invokers) - - [Onboard process](#onboarding-process) - - [Discover process](#discover-process) - - [Get JWT tokens](#obtain-invoker-tokens) - - [Update and offboarding process](#update-and-offboard-invoker) -- [Other Features](#other-features) - - [Register and Login](#script-register_and_loginpy) - - [Deregister and Login](#script-deregister_and_loginpy) - -# Provider NetApp - -CAPIF SDK allows developers to quickly implement NetApp provider flow in just some code lines. This section contains the full guide of all available features in SDK related to CAPIF providers. - -### Important information for Providers - -Within the `provider_folder`, the SDK will store some folders named with the provided `capif_username` it has been previously registered from CAPIF administrator. At each folder, there will be found the following files: - -- `capif_provider_details.json` : Contains all the APFs and AEFs ids that have already onboarded with this capif_username -- `CAPIF_<api_name><_API_id>.json` : If it's already published or updated an API, It will be available a copy of the last payload. -- `service_received.json` : If it's already used the get an api or get all apis functionality, It will be available the response to the request. -- `published-apis.json` : Contains the currently published APIs with their ApiId - - -### Provider onboarding - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>onboard_provider()</mark> -* **CAPIF SDK script**: <mark>provider_capif_connector.py</mark> - -SDK simplifies the onboarding process for Provider developers. It also provides the capability to register several APFs and AEFs if necessary. - - - -[CAPIF_Publish_Service_API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) - -Using Publish Service it is required to fulfill certain fields of the [capif-sdk-config.json](./sdk-configuration.md) file. - -### Services publishing - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>publish_services()</mark> -* **CAPIF SDK script**: <mark>provider_publish_api.py</mark> - -SDK simplifies the process of publishing an API in CAPIF. Besides, it also provides the ability to chose which APF and AEFs will be used to publish the API. - -It is mandatory to be [onboarded as a provider](#onboard_provider--script-provider_capif_connectorpy): - - Mandatory fields: - - PublisherAPFid - - PublisherAEFsids - -### Services deletion - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>unpublish_service()</mark> -* **CAPIF SDK script**: <mark>provider_unpublish_api.py</mark> - -SDK simplifies the process of deleting an API available in CAPIF. - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - - PublisherAEFsids - -### Services update - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>update_service()</mark> -* **CAPIF SDK script**: <mark>provider_update_api.py</mark> - -SDK simplifies the process of updating an API already registered at CAPIF. It also supports selecting which APF and AEFs will be used to update the API. - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - - PublisherAEFsids - -### Get services - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>get_service()</mark> -* **CAPIF SDK script**: <mark>provider_get_published_api.py</mark> - -SDK simplifies the process of receiving the information of one service published previously in published-apis.json - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - -### Get all services - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>get_all_services()</mark> -* **CAPIF SDK script**: <mark>provider_get_all_published_api.py</mark> - -SDK simplifies the process of receiving the information of all available services published previously in published-apis.json - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - PublisherAPFid - - - -### Update and Offboard provider - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>update_provider()</mark> and <mark>offboard_provider()</mark> -* **CAPIF SDK script**: <mark>provider_capif_connector_update.py</mark> and <mark>provider_capif_connector_offboarding.py</mark> - -For using this features we must be previously onboarded as a provider. - - - -# Invoker NetApp - -CAPIF SDK allows developers to quickly implement NetApp invokers flow in just some code lines. This section contains the full guide of all available features in SDK related to CAPIF invokers. - -### Important information for Invokers - -Within the `invoker_folder`, the SDK will store some folders named with the provided `capif_username` it has been previously registered from CAPIF administrator. At each folder, there will be found the following files: - -- `capif_api_security_context_details.json`: This file contains the information of the invoker. It will contain: - - 1. The `api_invoker_id`, - 2. If it has already used the Service Discovery Functionality, it will be found all the available APIs with their information, - 3. If it has already used the Service Get Token functionality, it will be found the access token for using the APIs that has already been discovered. - -### Onboarding process - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>onboard_invoker()</mark> -* **CAPIF SDK script**: <mark>invoker_capif_connector.py</mark> - -Simplifies the process of onboarding for Invoker users - - - -### Discover process - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>discover()</mark> -* **CAPIF SDK script**: <mark>invoker_service_discover.py</mark> - -In this functionality, [discover_filter](./sdk-configuration.md) is used to retrieve the access of target API specified in discover filter. - -It is mandatory to be onboarded as a [invoker](#onboard_invoker--script-invoker_capif_connectorpy). - -DISCLAIMER: if it's the first time the user runs discover() it will appear a warning alert like this - - WARNING - Received 404 error, redirecting to register security service - - - -### Obtain Invoker tokens - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>get_tokens()</mark> -* **CAPIF SDK script**: <mark>invoker_service_get_token.py</mark> - -This functionality simplifies the process of creating a security context based on JWT tokens in CAPIF, allowing to access the target APIs. - -This functionality **requires** to to be onboarded as a [invoker](#onboard_invoker--script-invoker_capif_connectorpy) before and run the [discover](#discover--script-invoker_service_discoverpy) function at least once before. - - - -### Update and offboard invoker - -CAPIF SDK references: -* **CAPIF SDK function**: <mark>update_invoker()</mark> & <mark>offboard_and_deregister_Invoker()</mark> -* **CAPIF SDK script**: <mark>invoker_capif_connector_update.py</mark> & <mark>invoker_capif_connector_offboarding.py</mark> - -To use these features, the NetApp must be previously [onboarded as an invoker](#onboard_invoker--script-invoker_capif_connectorpy). - - - -# Other features - -Apart from the OpenCAPIF SDK core features shown above, there are available different functionalities for SDK development purposes. - -### CAPIF registration and login - -CAPIF SDK reference: -* **CAPIF SDK script**: <mark>register_and_login.py</mark> - -Eases the logging process for admin users and creates a CAPIF user, - -### CAPIF deregistration and logout - -CAPIF SDK reference: -* **CAPIF SDK script**: <mark>deregister_and_login.py</mark> - -Eases the logging process for admin users and removes a CAPIF user. - - - - - - - - - diff --git a/doc/sdk-issues.md b/doc/sdk-issues.md deleted file mode 100644 index 3d97ae8b0fc60fecc90d0949994edde7b8aa4f2f..0000000000000000000000000000000000000000 --- a/doc/sdk-issues.md +++ /dev/null @@ -1,9 +0,0 @@ -# OpenCAPIF SDK known issues - -1. There are some features which are not currently available latest CAPIF-SDK release, which are assumed to be technical debt and might be available in future releases: - - [CAPIF Access control policy management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Access_Control_Policy_API.yaml) - - [CAPIF Auditing API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Auditing_API.yaml) - - [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) - - [CAPIF Logging API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Logging_API_Invocation_API.yaml) - - [CAPIF Routing info API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Routing_Info_API.yaml) - - [Part of CAPIF Security API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Security_API.yaml) \ No newline at end of file diff --git a/doc/sdk-usage.md b/doc/sdk-usage.md deleted file mode 100644 index 9e56871b111cf12bbfd2f9c1767c60c4c550527e..0000000000000000000000000000000000000000 --- a/doc/sdk-usage.md +++ /dev/null @@ -1,200 +0,0 @@ -# OpenCAPIF SDK usage - -OpenCAPIF SDK implements this set of features to easily integrate an application with CAPIF NF either manually or integrating SDK library directly within app code. - -Before using it, it is required to have fulfilled the [requirements](./sdk-prerequirements.md) section, the [installation](./sdk-installation.md) section and the selected parts of the [configuration](./sdk-configuration.md) depending on the CAPIF role the NetApp is going to play. - - - - -This repository includes 2 different modes to test OpenCAPIF SDK: - -- **Development usage**: import SDK in the code and start ... Within [nf-sample folder](../netapp-samples/), it is provided a sample application leveraging - -- **Manual usage**: use a set of python [scripts](../scripts/) to test step by step all implemented procedures. All of them are provided at scripts folder - - - For Manual usage it is required to complete the utilities file with the absolute paths of target environment in order to complete the configuration of the SDK: - -**NOTE**:register file is not needed for the use of the SDK - -**IMPORTANT**:It is needed to fill out config files depending on the features required to be used from the SDK.Please if it is not fulfilled this file, go to the [Configuration Section](./sdk-configuration.md) - -# Table of contents - -CAPIF is designed to use using two different roles: - - - -- [As an Invoker](#as-an-invoker) - - [Important Information for Invoker](#important-information-for-invoker-consumer) - - [Onboard Invoker](#onboard_invoker--script-invoker_capif_connectorpy) - - [Discover API](#discover--script-invoker_service_discoverpy) - - [Get Tokens](#get_tokens--script-invoker_service_get_tokenpy) - - [Update and Offboard Invoker](#update_invoker-and-offboard_and_deregister_invoker--invoker_capif_connector_updatepy-and-invoker_capif_connector_offboardingpy) -- [As a Provider](#as-a-proxvider) - - [Important Information for Provider](#important-information-for-provider-consumer) - - [Onboard Provider](#onboard_provider--script-provider_capif_connectorpy) - - [Publish Services](#publish_services--script-provider_publish_apipy) - - [Unpublish Services](#unpublish_service--script-provider_unpublish_apipy) - - [Update Services](#update_service--script-provider_update_apipy) - - [Get Published Services](#get_service--script-provider_get_published_apipy) - - [Get All Published Services](#get_all_services--script-provider_get_all_published_apipy) - - [Update and Offboard Provider](#update_provider-and-offboard_provider--provider_capif_connector_updatepy-and-provider_capif_connector_offboardingpy) -- [Other Features](#other-features) - - [Register and Login](#script-register_and_loginpy) - - [Deregister and Login](#script-deregister_and_loginpy) - -# As an invoker - -The common path to follow using CAPIF in order to get an API token of the service it's wanted to use is by following this steps: -## Important information for Invoker consumer - -In the `invoker_folder`, it will be located several folders with each `capif_username` that has onboarded as a provider. For each folder, it could be found: - -- `Capif_api_security_context_details.json`: This file contains the information of the invoker. It will contain: - - 1. The `api_invoker_id`. - 2. If it has already used the Service Discovery Functionality, it will be found all the available APIs with their information. - 3. If it has already used the Service Get Token functionality, it will be found the access token for using the APIs that has already been discovered. - - -### onboard_invoker() // Script invoker_capif_connector.py - -Simplifies the process of onboarding for Invoker users - - - -### discover() // Script invoker_service_discover.py -In this functionality it could be used [discover_filter](./sdk-configuration.md) to retrieve only the access of the API's it's wanted to query - -It is mandatory to have onboarded as a [invoker](#onboard_invoker--script-invoker_capif_connectorpy) before - -DISCLAIMER: if it's the first time the user runs discover() it will appear a warning alert like this - - WARNING - Received 404 error, redirecting to register security service - - - - -### get_tokens() // Script invoker_service_get_token.py - -This functionality simplifies the way of getting created their properly security context for each of the services and acquiring the access token to use the final APIs. - -This functionality **requires** to to have onboarded as a [invoker](#onboard_invoker--script-invoker_capif_connectorpy) before and run the [discover](#discover--script-invoker_service_discoverpy) function at least once before. - - - -### update_invoker() and offboard_and_deregister_Invoker() // invoker_capif_connector_update.py and invoker_capif_connector_offboarding.py - -For using this features we must have [onboarded as an invoker](#onboard_invoker--script-invoker_capif_connectorpy) previously. - - - - -# As a provider -The common path to follow using CAPIF in order to publish an API is by following this steps: - -## Important information for Provider consumer - -In the `provider_folder`, it will be located several folders with each `capif_username` that has onboarded as a provider, for each folder it is created by SDK this files: - -- `Capif_provider_details.json` : Contains all the APFs and AEFs ids that have already onboarded with this capif_username -- `CAPIF_<api_name><_API_id>.json` : If it's already published or updated an API, It will be available a copy of the last payload. -- `Service_received.json` : If it's already used the get an api or get all apis functionality, It will be available the response to the request. -- `Published-Apis.json` : Contains the currently published APIs with their ApiId - - -### onboard_provider() // Script provider_capif_connector.py - -Simplifies the process of onboarding for Provider users,also has the capability to register several APF's and AEF's if its necessary - - - -[CAPIF_Publish_Service_API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) - -For using the Publish Service is mandatory to fulfil certain fields of the [Publish.json](./sdk-configuration.md) file - -### publish_services() // Script provider_publish_api.py - -Simplifies the process of publishing an API. Also has the capability to chose which APF and AEF's will be used to publish the API. - -It is mandatory to have [onboarded as a provider](#onboard_provider--script-provider_capif_connectorpy) before - - Mandatory fields: - - PublisherAPFid - - PublisherAEFsids - -### unpublish_service() // Script provider_unpublish_api.py - -Simplifies the process of deleting an API - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - - PublisherAEFsids - -### update_service() // Script provider_update_api.py - -Simplifies the process of updating an API. Also has the capability to chose which APF and AEF's will be used to update the API. - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - - PublisherAEFsids - -### get_service() // Script provider_get_published_api.py - -Simplifies the process of receiving the information of One service published previously in Published-Apis.json - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - ServiceApiId - - PublisherAPFid - -### get_all_services() // Script provider_get_all_published_api.py - -Simplifies the process of receiving the information of all available services published previously in Published-Apis.json - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before and to have [published any service](#publish_services--script-provider_publish_apipy) before - - Mandatory fields: - - PublisherAPFid - - - -### update_provider() and offboard_provider() // provider_capif_connector_update.py and provider_capif_connector_offboarding.py - -For using this features we must have onboard as a provider previously. - -It is mandatory to have onboarded as a [provider](#onboard_provider--script-provider_capif_connectorpy) before - - - - - -# Other features - -Apart from the SDK core, there are available different functionalities for development purposes: - -### Script register_and_login.py - -Facilitates the logging process for admin users and creates a CAPIF user, - - -### Script deregister_and_login.py - -Facilitates the logging process for admin users and eliminates a CAPIF user. - - - - - - - - - diff --git a/doc/sdk_configuration.md b/doc/sdk_configuration.md new file mode 100644 index 0000000000000000000000000000000000000000..b90e80a798aaf823f020bb398b7917eaf9364f50 --- /dev/null +++ b/doc/sdk_configuration.md @@ -0,0 +1,108 @@ +# OpenCAPIF SDK Configuration + +Before starting the configuration process, it is required that both the [requirements](../README.md) and the [installation](./sdk_developers.md) sections are completed. + +## Table of Contents +- [Configuration via capif_sdk_config.json](#configuration-via-capif_sdk_configjson) + - [As a Network App Invoker](#network-app-invoker) + - [As a Network App Provider](#network-app-provider) + - [Descriptions of capif_sdk_config Fields](#descriptions-of-capif_sdk_config-fields) +- [Configuration via capif-sdk-register.json](#configuration-via-capif-sdk-registerjson) + +## Configuration via `capif_sdk_config.json` + +A sample configuration file can be found [here](../samples/config_sample.json). + +### Common Fields for Invoker and Provider + +Regardless of the role (Invoker or Provider), the following fields are mandatory: + +- `capif_host` +- `register_host` +- `capif_https_port` +- `capif_register_port` +- `capif_username` +- `capif_password` +- `debug_mode` + +### Network App Invoker + +When configuring the SDK as a **Network App Invoker**, the following fields must be provided: + +- `invoker_folder` +- `capif_callback_url` +- `cert_generation` (fields such as `csr_common_name`, `csr_country_name`, etc.) + +**Optional:** +To enable the discovery of specific APIs, the fields under [`discover_filter`](#configuration-of-discover_filter) can be configured. + +### Network App Provider + +For SDK configuration as a **Network App Provider**, the following fields are required: + +- `provider_folder` +- `cert_generation` (fields such as `csr_common_name`, `csr_country_name`, etc.) +- `APFs` +- `AEFs` +- [`publish_req`](#configuration-of-publish_req) +- `api_description_path` + +## Configuration of `discover_filter` + +The `discover_filter` section adheres to the parameters defined in the GET request schema of the [Discover Services API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Discover_Service_API.yaml). + +To utilize the service discovery functionality, the `discover_filter` fields should be populated with the desired filters. It is important to note that fields such as `api-name` must contain only one object of each type (i.e., no lists are allowed). + +Before running the Invoker Service Discovery Functionality, the Invoker must be onboarded to CAPIF. + +## Configuration of `publish_req` + +This section is mandatory when using the [CAPIF Publish Service API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml). The following fields are required: + +- `service_api_id`: Example: `"02eff6e1b3a8f7c8044a92ee8a30bd"` +- `publisher_apf_id`: Example: `"APFa165364a379035d14311deadc04332"` +- `publisher_aefs_ids`: An array of selected AEF IDs. Example: `["AEFfa38f0e855bffb420e4994ecbc8fb9", "AEFe8bfa711f4f0c95ba0b382508e6382"]` + +The `api_description_path` must point to the Publish API to be shared, and it should follow the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) schema. + +If the `publisherAEFsids` do not match the `aefProfiles` in the API description, an error will be raised by the SDK. + +## Descriptions of `capif_sdk_config` Fields + +This file can also be populated using [environment variables](../samples/enviroment_variables_sample.txt). + +- `invoker_folder`: The path (relative or absolute) where invoker information (certificates, keys, etc.) is stored. +- `provider_folder`: The path (relative or absolute) where provider information is stored. +- `capif_host`: The domain name of the CAPIF host. +- `register_host`: The domain name of the register host. +- `capif_https_port`: The CAPIF host port number. +- `capif_register_port`: The register host port number. +- `capif_callback_url`: The URL used by CAPIF to send invoker notifications ([currently unavailable](sdk-issues.md)). +- `cert_generation`: Fields for certificate generation, with `csr_country_name` requiring a two-letter country code. +- `capif_username`: The CAPIF username. +- `capif_password`: The CAPIF password. +- `apfs`: The number of APFs to be onboarded as a provider (e.g., `5`). +- `aefs`: The number of AEFs to be onboarded as a provider (e.g., `2`). +- `debug_mode`: A boolean value to enable or disable SDK logs (e.g., `True` or `False`). +- [`discover_filter`](#configuration-of-discover_filter): Fields for configuring invoker service discovery. +- [`publish_req`](#configuration-of-publish_req): Fields required for API publishing. +- `api_description_path`: The path to the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) JSON file. + +## Configuration via `capif_sdk_register.json` + +To use this SDK in a local environment for creating and removing users, the following fields must be populated. Note that this feature is not included in the SDK, but instructions can be found in the [CAPIF official repository](https://labs.etsi.org/rep/ocf/capif/-/tree/REL1?ref_type=heads). + +- `register_host`: The domain name of the register host. +- `capif_register_port`: The port number of the register host. +- `capif_register_username`: The CAPIF admin username. +- `capif_register_password`: The CAPIF admin password. +- `capif_username`: The CAPIF user username. +- `capif_password`: The CAPIF user password. +- `config_path`: The absolute path to the configuration files folder. +- `uuid`: The UUID required for deregistering a user (only mandatory for deregistration). + +This file is used for the following functionalities: +- Register and login. +- Deregister and login. + +All fields are required except for `uuid`, which is only mandatory for deregistration. It is recommended to store the `uuid` returned from the registration process for future use. diff --git a/doc/sdk-installation.md b/doc/sdk_developers.md similarity index 67% rename from doc/sdk-installation.md rename to doc/sdk_developers.md index 50c46de690ae08b13737bef1420629b803fc1592..32820cc0ac6cc48ef7d066f628b73a9c64215a63 100644 --- a/doc/sdk-installation.md +++ b/doc/sdk_developers.md @@ -1,20 +1,31 @@ -# OpenCAPIF SDK development environment installation +# OpenCAPIF SDK Development Environment Installation -Before installation it's needed to have completed the [requirements](../README.md) section. +Currently, the OpenCAPIF SDK is only available by downloading this repository and performing a manual installation. In the future, an OpenCAPIF SDK package will be released for easier installation. -Follow next steps to perform OpenCAPIF SDK installation: +Before proceeding, ensure you have fulfilled the necessary [requirements](../README.md). -- For developers installation is mandatory to have already installed: - - pyenv +Follow the steps below to install the OpenCAPIF SDK for development purposes: -1. Create an environment with pyenv +## Prerequisites -```console -#Commands to install the Python environment +- Developers must have the following tools installed: + - **pyenv** + +## Installation Steps + +### 1. Set Up a Python Environment with `pyenv` + +Follow these steps to create and activate a virtual environment using `pyenv`: + +```bash +# Install Python 3.12 using pyenv pyenv install 3.12 + +# Create a virtual environment for the SDK pyenv virtualenv 3.12 pesp_sdk_env -#Activate the environment + +# Activate the virtual environment source the/path/to/.pyenv/versions/pesp_sdk_env/bin/activate ``` diff --git a/doc/sdk_full_documentation.md b/doc/sdk_full_documentation.md new file mode 100644 index 0000000000000000000000000000000000000000..016cf4f97acf5eac1b90b7c568e64d3f997ac3da --- /dev/null +++ b/doc/sdk_full_documentation.md @@ -0,0 +1,218 @@ +# OpenCAPIF SDK full documentation + +The OpenCAPIF SDK simplifies the integration of applications with the CAPIF NF, offering several features for both manual use and automated scripting, as well as direct integration into application code. + +This documentation provides a step-by-step guide on how to use the SDK, explaining each functionality in detail. Before diving into the specifics, ensure the necessary prerequisites are met and learn about the available testing modes for the SDK. + +## Getting Started + +Before using the SDK, make sure the following steps have been completed: +- Fulfill the [requirements](../README.md) section, +- Follow the [installation instructions](./sdk_developers.md), +- Configure the SDK by completing the relevant parts of the [configuration](./sdk_configuration.md) section, based on the CAPIF role your NetApp will perform. + +## Available SDK Usage Modes + + + +This repository provides 2 modes for using OpenCAPIF SDK: + +1. **Development Mode**: Import the SDK into your code and start developing. Sample applications using the SDK are provided in the [network_app_samples](../netapp-samples/) folder. + +2. **Manual Mode**: Use a set of Python [scripts](../scripts/) to test each step of the integration process manually. For manual usage, it is essential to complete the utilities file with absolute paths of the target environment to finalize SDK configuration. + +**IMPORTANT**: all SDK configuration files must be filled out depending on the role and features planned to be used. For further details, refer to the [Configuration Section](./sdk_configuration.md). + +**NOTE**: register file is not required for SDK usage, just for SDK developers. + +## Table of Contents + +As outlined in the [Network App developers section](../README.md), OpenCAPIF SDK supports two primary roles: + +- [Provider NetApp](#provider-netapp) + - [Important Information for Providers](#important-information-for-providers) + - [Provider Onboarding](#provider-onboarding) + - [Service Publishing](#services-publishing) + - [Service Deletion](#services-deletion) + - [Service Updates](#services-update) + - [Get Published Services](#get-services) + - [Get All Published Services](#get-all-services) + - [Update and Offboard Provider](#update-and-offboard-provider) +- [Invoker NetApp](#invoker-netapp) + - [Important Information for Invokers](#important-information-for-invokers) + - [Invoker Onboarding](#invoker-onboarding) + - [Service Discovery](#discover-process) + - [Obtain JWT Tokens](#obtain-invoker-tokens) + - [Update and Offboard Invoker](#update-and-offboard-invoker) +- [Other Features](#other-features) + - [CAPIF Registration and Login](#capif-registration-and-login) + - [CAPIF Deregistration and Logout](#capif-registration-and-login) + +## Provider NetApp + +The OpenCAPIF SDK allows developers to quickly implement the provider flow for NetApp using just a few lines of code. This section provides a comprehensive guide to the SDK features related to CAPIF providers. + +### Important Information for Providers + +Inside the `provider_folder`, the SDK stores directories named after the registered `capif_username`. Each folder contains: + +- `capif_provider_details.json`: Contains all APFs and AEFs IDs that have been onboarded with the associated username. +- `capif_<api_name>_<api_id>.json`: Stores a copy of the last payload for any published or updated API. +- `service_received.json`: Stores responses for the Get API or Get All APIs functionality. +- `published-apis.json`: Contains a list of currently published APIs with their respective IDs. + +All configuration values are available within the `capif_provider_connector` object. Additionally, the `api_prov_funcs` variable is a dictionary where APFs and AEFs are the keys, and their respective IDs are the values. + +### Provider Onboarding + +OpenCAPIF SDK references: +- **Function**: `onboard_provider()` +- **Script**: `provider_capif_connector.py` + +The SDK streamlines the onboarding process for providers, allowing them to register multiple APFs and AEFs as needed. + + + +### Service Publishing + +OpenCAPIF SDK references: +- **Function**: `publish_services()` +- **Script**: `provider_publish_api.py` + +The SDK simplifies the process of publishing an API in CAPIF, with the option to select specific APFs and AEFs. Before publishing, ensure you’ve [onboarded as a provider](#provider-onboarding). + +**Required SDK inputs**: +- PublisherAPFid +- PublisherAEFsids + +### Service Deletion + +OpenCAPIF SDK references: +- **Function**: `unpublish_service()` +- **Script**: `provider_unpublish_api.py` + +The SDK simplifies API deletion in CAPIF. To delete a service, you must be [onboarded as a provider](#provider-onboarding) and have [published a service](#services-publishing) beforehand. + +**Required SDK inputs**: +- ServiceApiId +- PublisherAPFid +- PublisherAEFsids + +### Service Updates + +OpenCAPIF SDK references: +- **Function**: `update_service()` +- **Script**: `provider_update_api.py` + +The SDK facilitates updating a previously registered API and allows selecting the APFs and AEFs used for the update. Ensure you’ve [onboarded as a provider](#provider-onboarding) and have [published a service](#services-publishing) beforehand. + +**Required SDK inputs**: +- ServiceApiId +- PublisherAPFid +- PublisherAEFsids + +### Get Services + +OpenCAPIF SDK references: +- **Function**: `get_service()` +- **Script**: `provider_get_published_api.py` + +Retrieve information about a previously published service in `service_received.json`. Before retrieving services, you must be [onboarded as a provider](#provider-onboarding) and have [published a service](#services-publishing). + +**Required SDK inputs**: +- ServiceApiId +- PublisherAPFid + +### Get All Services + +OpenCAPIF SDK references: +- **Function**: `get_all_services()` +- **Script**: `provider_get_all_published_api.py` + +Retrieve information about all previously published services in `service_received.json`. Ensure you are [onboarded as a provider](#provider-onboarding) and have [published services](#services-publishing). + +**Mandatory Field**: +- PublisherAPFid + + + +### Update and Offboard Provider + +OpenCAPIF SDK references: +- **Functions**: `update_provider()` and `offboard_provider()` +- **Scripts**: `provider_capif_connector_update.py` and `provider_capif_connector_offboarding.py` + +You must be onboarded as a provider before using these features. + + + +## Invoker NetApp + +The OpenCAPIF SDK enables developers to implement invoker functionality for NetApp efficiently. This section provides a complete guide to SDK features related to CAPIF invokers. + +### Important Information for Invokers + +Inside the `invoker_folder`, the SDK stores directories named after the registered `capif_username`. Each folder contains: + +- `capif_api_security_context_details.json`: Stores details about the invoker, including: + 1. `api_invoker_id` + 2. Discovered APIs and their information (if Service Discovery has been used) + 3. JWT access tokens for discovered APIs (if Service Get Token functionality has been used) + +### Invoker Onboarding + +OpenCAPIF SDK references: +- **Function**: `onboard_invoker()` +- **Script**: `invoker_capif_connector.py` + +The SDK simplifies the invoker onboarding process. + + + +### Service Discovery + +OpenCAPIF SDK references: +- **Function**: `discover()` +- **Script**: `invoker_service_discover.py` + +Use the [discover_filter](./sdk_configuration.md) to retrieve access to target APIs. Ensure you are [onboarded as an invoker](#invoker-onboarding) before using this feature. + +**Note**: If this is the first time running `discover()`, you may receive a 404 warning, prompting registration for the security service. + + + +### Obtain JWT Tokens + +OpenCAPIF SDK references: +- **Function**: `get_tokens()` +- **Script**: `invoker_service_get_token.py` + +The SDK streamlines the creation of a security context using JWT tokens, which allows access to target APIs. Ensure you are [onboarded as an invoker](#invoker-onboarding) and have used the [discover](#discover-process) function beforehand. + + + +### Update and Offboard Invoker + +OpenCAPIF SDK references: +- **Functions**: `update_invoker()` & `offboard_and_deregister_invoker()` +- **Scripts**: `invoker_capif_connector_update.py` & `invoker_capif_connector_offboarding.py` + +Ensure you are [onboarded as an invoker](#invoker-onboarding) before using these features. + + + +## Other Features + +OpenCAPIF SDK reference: +* **OpenCAPIF SDK script**: <mark>register_and_login.py</mark> + +Eases the logging process for admin users and creates a CAPIF user, + +### CAPIF deregistration and logout + +OpenCAPIF SDK reference: +* **OpenCAPIF SDK script**: <mark>deregister_and_login.py</mark> + +Eases the logging process for admin users and removes a CAPIF user. + + diff --git a/installation/requirements.txt b/installation/requirements.txt index fd7410e6aa876c1abb93dd8c8bfc222a77be781f..00b4a1504821d537bba345fa7ba117f74c101068 100644 --- a/installation/requirements.txt +++ b/installation/requirements.txt @@ -1,27 +1,33 @@ argh==0.31.3 arrow==1.3.0 binaryornot==0.4.4 +blinker==1.8.2 certifi==2024.7.4 cffi==1.16.0 chardet==5.2.0 charset-normalizer==3.3.2 -click==8.1.3 +click==8.1.7 cookiecutter==2.1.1 coverage==4.5.4 cryptography==38.0.4 flake8==3.9.2 +Flask==3.0.3 +Flask-JWT-Extended==4.6.0 idna==3.7 iniconfig==2.0.0 invoke==1.6.0 +itsdangerous==2.2.0 Jinja2==3.1.4 jinja2-time==0.2.0 MarkupSafe==2.1.5 mccabe==0.6.1 packaging==24.1 +pip==24.2 pluggy==1.5.0 pycodestyle==2.7.0 pycparser==2.22 pyflakes==2.3.1 +PyJWT==2.9.0 pyOpenSSL==22.1.0 pytest==8.3.2 python-dateutil==2.9.0.post0 @@ -32,4 +38,5 @@ six==1.16.0 text-unidecode==1.3 types-python-dateutil==2.9.0.20240316 typing-extensions==3.10.0.2 -urllib3==2.2.2 \ No newline at end of file +urllib3==2.2.2 +Werkzeug==3.0.4 diff --git a/netapp-samples/netapp-invoker-sample/capif-sdk-config-sample.json b/netapp-samples/netapp-invoker-sample/capif-sdk-config-sample.json deleted file mode 100644 index d2b17798a181bc124cd90d0a40735c38b4c10d1d..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-invoker-sample/capif-sdk-config-sample.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", - "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", - "capif_host": "capif-prev.mobilesandbox.cloud", - "register_host": "registercapif-prev.mobilesandbox.cloud", - "capif_https_port": "36212", - "capif_register_port": "36211", - "capif_callback_url": "http://localhost:5000", - "csr_common_name": "test03", - "csr_organizational_unit": "test_app_ou", - "csr_organization": "test_app_o", - "crs_locality": "Madrid", - "csr_state_or_province_name": "Madrid", - "csr_country_name": "ES", - "csr_email_address": "test@example.com", - "capif_username": "echeva_0", - "capif_password": "echevapass", - "apfs": "1", - "aefs": "2", - "debug_mode": "False", - "discover_filter": { - "api-name": "", - "api-version": "", - "comm-type": "", - "protocol": "", - "aef-id": "", - "data-format": "", - "api-cat": "", - "preferred-aef-loc": "", - "req-api-prov-name": "", - "supported-features": "", - "api-supported-features": "", - "ue-ip-addr": "", - "service-kpis": "" - }, - "publish_req": { - "service_api_id": "", - "publisher_apf_id": "", - "publisher_aefs_ids": ["", ""] - }, - "api_description_path": "/Users/IDB0128/git_repos/pesp_capif_sdk/samples/provider_api_description_sample.json" -} diff --git a/netapp-samples/netapp-invoker-sample/netapp-invoker.py b/netapp-samples/netapp-invoker-sample/netapp-invoker.py deleted file mode 100644 index 63326f97d8d26bd359d66b6f0eeac44ebd72b3ec..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-invoker-sample/netapp-invoker.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -import os - -# Add the SDK directory to PYTHONPATH using a relative path -script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory -sdk_path = os.path.join(script_dir, '..', '..', 'sdk') # Go up two levels and point to 'sdk' -sys.path.insert(0, sdk_path) -from sdk import CAPIFInvokerConnector, ServiceDiscoverer - -capif_sdk_config_path = "./capif-sdk-config-sample.json" - -if __name__ == "__main__": - - capif_connector = CAPIFInvokerConnector(config_file=capif_sdk_config_path) - - capif_connector.onboard_invoker() - print("INVOKER ONBOARDING COMPLETED") - - # Now I have certificates, I can proceed with discovery (get token for an API or several, depending on your choice) - - # As a developer, I already know which APIs I want to access - # DISCOVER filter configureCmcAmfSet - # Without DISCOVER filter, the entire list of available APIs in CAPIF is obtained - - discoverer = ServiceDiscoverer(config_file=capif_sdk_config_path) - - discoverer.discover() - - discoverer.get_tokens() diff --git a/netapp-samples/netapp-provider-sample/5g-server.py b/netapp-samples/netapp-provider-sample/5g-server.py deleted file mode 100644 index 6ccff0d346d741755a4168f83bc527e1bbdf920e..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/5g-server.py +++ /dev/null @@ -1,140 +0,0 @@ -from flask import Flask, jsonify, request -from flask_jwt_extended import jwt_required, JWTManager -from werkzeug import serving - -app = Flask(__name__) - -# JWT Configuration -app.config['JWT_ALGORITHM'] = 'RS256' -app.config['JWT_PUBLIC_KEY'] = "YourPublicKeyHere" -jwt_flask = JWTManager(app) - -# Dummy data for QoS, TSN, and Slicing management -qos_profiles = { - "1": {"profileId": 1, "level": "high"}, - "2": {"profileId": 2, "level": "medium"} -} - -tsn_profiles = { - "profile1": {"name": "profile1", "config": "default"}, - "profile2": {"name": "profile2", "config": "custom"} -} - -network_slices = { - "slice1": {"sliceId": "slice1", "status": "active"}, - "slice2": {"sliceId": "slice2", "status": "inactive"} -} - -### QoS Management Endpoints ### - - -@app.route("/<scsAsId>/qos", methods=["GET", "POST"]) -@jwt_required() -def manage_qos(scsAsId): - """Endpoint to manage QoS levels for network traffic""" - if request.method == "GET": - return jsonify({"message": "GET request for QoS management", "scsAsId": scsAsId}) - - if request.method == "POST": - data = request.get_json() - return jsonify({"message": "POST request to manage QoS", "scsAsId": scsAsId, "data": data}) - - -@app.route("/<scsAsId>/qos/<profileId>", methods=["GET", "PUT", "DELETE"]) -@jwt_required() -def manage_single_qos_profile(scsAsId, profileId): - """Endpoint to manage single QoS profile""" - if request.method == "GET": - profile = qos_profiles.get(profileId) - if profile: - return jsonify({"profile": profile, "scsAsId": scsAsId}) - return jsonify({"message": f"Profile {profileId} not found"}), 404 - - if request.method == "PUT": - data = request.get_json() - qos_profiles[profileId] = data - return jsonify({"message": f"Profile {profileId} updated", "scsAsId": scsAsId, "data": data}) - - if request.method == "DELETE": - if profileId in qos_profiles: - del qos_profiles[profileId] - return jsonify({"message": f"Profile {profileId} deleted"}) - return jsonify({"message": f"Profile {profileId} not found"}), 404 - -### TSN Management Endpoints ### - - -@app.route("/profile", methods=["GET"]) -@jwt_required() -def list_tsn_profiles(): - """Endpoint for retrieving the list of available TSN profiles""" - return jsonify({"profiles": tsn_profiles}) - - -@app.route("/profile", methods=["GET"]) -@jwt_required() -def tsn_profile_by_name(): - """Endpoint for retrieving information about a single TSN profile""" - profile_name = request.args.get('name') - profile = tsn_profiles.get(profile_name) - if profile: - return jsonify({"profile": profile}) - return jsonify({"message": f"Profile {profile_name} not found"}), 404 - - -@app.route("/apply", methods=["POST"]) -@jwt_required() -def apply_tsn_configuration(): - """Endpoint for configuring TSN connection parameters""" - data = request.get_json() - return jsonify({"message": "TSN configuration applied", "data": data}) - - -@app.route("/clear", methods=["POST"]) -@jwt_required() -def clear_tsn_configuration(): - """Endpoint for removing a previous TSN connection configuration""" - return jsonify({"message": "TSN configuration cleared"}) - -### Network Slicing Management Endpoints ### - - -@app.route("/slice", methods=["GET", "POST"]) -@jwt_required() -def manage_slices(): - """Endpoint for managing network slices""" - if request.method == "GET": - return jsonify({"slices": network_slices}) - - if request.method == "POST": - data = request.get_json() - slice_id = f"slice{len(network_slices) + 1}" - network_slices[slice_id] = data - return jsonify({"message": "Network slice added", "sliceId": slice_id, "data": data}) - - -@app.route("/slice/<sliceId>", methods=["GET", "PUT", "DELETE"]) -@jwt_required() -def manage_single_slice(sliceId): - """Endpoint for managing a single network slice""" - if request.method == "GET": - slice_ = network_slices.get(sliceId) - if slice_: - return jsonify({"slice": slice_}) - return jsonify({"message": f"Slice {sliceId} not found"}), 404 - - if request.method == "PUT": - data = request.get_json() - network_slices[sliceId] = data - return jsonify({"message": f"Slice {sliceId} updated", "data": data}) - - if request.method == "DELETE": - if sliceId in network_slices: - del network_slices[sliceId] - return jsonify({"message": f"Slice {sliceId} deleted"}) - return jsonify({"message": f"Slice {sliceId} not found"}), 404 - - -# Starting the server -if __name__ == '__main__': - serving.run_simple("0.0.0.0", 8888, app) diff --git a/netapp-samples/netapp-provider-sample/capif-sdk-config-sample.json b/netapp-samples/netapp-provider-sample/capif-sdk-config-sample.json deleted file mode 100644 index b9451c30150e26245296d4d21ad5fbe5b21edda6..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/capif-sdk-config-sample.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", - "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", - "capif_host": "capif-prev.mobilesandbox.cloud", - "register_host": "registercapif-prev.mobilesandbox.cloud", - "capif_https_port": "36212", - "capif_register_port": "36211", - "capif_callback_url": "http://localhost:5000", - "csr_common_name": "test03", - "csr_organizational_unit": "test_app_ou", - "csr_organization": "test_app_o", - "crs_locality": "Madrid", - "csr_state_or_province_name": "Madrid", - "csr_country_name": "ES", - "csr_email_address": "test@example.com", - "capif_username": "echeva_0", - "capif_password": "echevapass", - "apfs": "1", - "aefs": "3", - "debug_mode": "False", - "discover_filter": { - "api-name": "", - "api-version": "", - "comm-type": "", - "protocol": "", - "aef-id": "", - "data-format": "", - "api-cat": "", - "preferred-aef-loc": "", - "req-api-prov-name": "", - "supported-features": "", - "api-supported-features": "", - "ue-ip-addr": "", - "service-kpis": "" - }, - "publish_req": { - "service_api_id": "", - "publisher_apf_id": "", - "publisher_aefs_ids": [ - "", - "" - ] - }, - "api_description_path": "./nef-upf-partner-1.json" -} \ No newline at end of file diff --git a/netapp-samples/netapp-provider-sample/netapp-provider.py b/netapp-samples/netapp-provider-sample/netapp-provider.py deleted file mode 100644 index ce9516b88ec2cc820aa8bb50e9ba335e5b0350b5..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/netapp-provider.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys -import os -import json - -# Add the SDK directory to PYTHONPATH using a relative path -script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory -sdk_path = os.path.join(script_dir, '..', '..', 'sdk') # Go up two levels and point to 'sdk' -sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector - -capif_sdk_config_path = "./capif-sdk-config-sample.json" - -if __name__ == "__main__": - try: - # Initialize the connector - capif_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) - capif_connector.onboard_provider() - print("PROVIDER ONBOARDING COMPLETED") - - provider_folder = capif_connector.provider_folder - - detailspath = os.path.join(provider_folder, "capif_provider_details.json") - - with open(detailspath, 'r') as file: - details = json.load(file) - - APF = details.get('APF-1_api_prov_func_id') - AEF1 = details.get('AEF-1_api_prov_func_id') - AEF2 = details.get('AEF-2_api_prov_func_id') - AEF3 = details.get('AEF-3_api_prov_func_id') - - capif_connector.publish_req['publisher_apf_id'] = APF - capif_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2] - - capif_connector.publish_services() - - capif_connector.api_description_path = "./nef-upf-partner-2.json" - - capif_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2, AEF3] - - capif_connector.publish_services() - - except FileNotFoundError as e: - print(f"Error: {e}") - except json.JSONDecodeError as e: - print(f"Error reading the JSON file: {e}") - except Exception as e: - print(f"Unexpected error: {e}") diff --git a/netapp-samples/netapp-provider-sample/package-lock.json b/netapp-samples/netapp-provider-sample/package-lock.json deleted file mode 100644 index aeaed07c0aca1f7148fbba7d0c3c301a7f3c5791..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/package-lock.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "netapp-provider-sample", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "flask": "^0.2.10" - } - }, - "node_modules/flask": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/flask/-/flask-0.2.10.tgz", - "integrity": "sha512-Zy69KIuFsoX0o2W5uDHHk1GHMwRT8QT0AboPQY7b1soCfJ2pfwKxHF2AGVwJmDUjzawgOavoY/UsXnZ7fk3lVQ==", - "license": "MIT" - } - } -} diff --git a/netapp-samples/netapp-provider-sample/package.json b/netapp-samples/netapp-provider-sample/package.json deleted file mode 100644 index 9b61d0260c0d2219542833adbcbcd0b2d2c1d649..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "flask": "^0.2.10" - } -} diff --git a/netapp-samples/netapp-provider-sample/resilience-server.py b/netapp-samples/netapp-provider-sample/resilience-server.py deleted file mode 100644 index 2a7974b6b3b5edb5a73932662d5db24a985dc77e..0000000000000000000000000000000000000000 --- a/netapp-samples/netapp-provider-sample/resilience-server.py +++ /dev/null @@ -1,97 +0,0 @@ -from flask import Flask, jsonify, request -from flask_jwt_extended import jwt_required, JWTManager -import ssl -from werkzeug import serving -from OpenSSL import crypto - -app = Flask(__name__) - -# JWT Configuration -app.config['JWT_ALGORITHM'] = 'RS256' -app.config['JWT_PUBLIC_KEY'] = "YourPublicKeyHere" -jwt_flask = JWTManager(app) - -with open("/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder/echeva_0/capif_cert_server.pem", "rb") as cert_file: - cert = cert_file.read() - -crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert) -pubKeyObject = crtObj.get_pubkey() -pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject) - -app.config['JWT_ALGORITHM'] = 'RS256' -app.config['JWT_PUBLIC_KEY'] = pubKeyString - - -# Dummy data for resilience management (In practice, this would connect to a database or service) -resilience_profiles = { - "1": {"profileId": 1, "status": "active"}, - "2": {"profileId": 2, "status": "inactive"} -} - - -@app.route("/resilience", methods=["GET", "POST"]) -@jwt_required() -def manage_resilience(): - """Endpoint to manage resilience functionalities in 6G networks""" - if request.method == "GET": - return jsonify({"message": "GET request for resilience management received."}) - - if request.method == "POST": - data = request.get_json() - return jsonify({"message": "Resilience management POST request received", "data": data}) - - -@app.route("/resilience/<profileId>", methods=["GET", "PUT", "DELETE"]) -@jwt_required() -def manage_single_resilience(profileId): - """Endpoint to manage a single resilience profile""" - if request.method == "GET": - profile = resilience_profiles.get(profileId, None) - if profile: - return jsonify({"profile": profile}) - else: - return jsonify({"message": f"Profile {profileId} not found"}), 404 - - if request.method == "PUT": - data = request.get_json() - resilience_profiles[profileId] = data - return jsonify({"message": f"Profile {profileId} updated", "data": data}) - - if request.method == "DELETE": - if profileId in resilience_profiles: - del resilience_profiles[profileId] - return jsonify({"message": f"Profile {profileId} deleted"}) - else: - return jsonify({"message": f"Profile {profileId} not found"}), 404 - - -@app.route("/slice/resilience", methods=["GET", "POST"]) -@jwt_required() -def manage_slice_resilience(): - """Endpoint for managing resilience in network slices""" - if request.method == "GET": - return jsonify({"message": "GET request for slice resilience received."}) - - if request.method == "POST": - data = request.get_json() - return jsonify({"message": "POST request for slice resilience management received", "data": data}) - - -@app.route("/slice/<sliceId>/resilience", methods=["GET", "PUT", "DELETE"]) -@jwt_required() -def manage_single_slice_resilience(sliceId): - """Endpoint for managing a single slice's resilience""" - if request.method == "GET": - return jsonify({"message": f"GET request for slice {sliceId} resilience received"}) - - if request.method == "PUT": - data = request.get_json() - return jsonify({"message": f"PUT request to update slice {sliceId}'s resilience", "data": data}) - - if request.method == "DELETE": - return jsonify({"message": f"DELETE request to remove slice {sliceId}'s resilience"}) - - -# Starting the server -if __name__ == '__main__': - serving.run_simple("0.0.0.0", 8088, app) diff --git a/network_app_samples/network_app_invoker_sample/capif_sdk_config_sample.json b/network_app_samples/network_app_invoker_sample/capif_sdk_config_sample.json new file mode 100644 index 0000000000000000000000000000000000000000..441d8d831018f27456f0e604fd98f4c66ea6f8e0 --- /dev/null +++ b/network_app_samples/network_app_invoker_sample/capif_sdk_config_sample.json @@ -0,0 +1,60 @@ +{ + "capif_host": "capif-prev.mobilesandbox.cloud", + "register_host": "registercapif-prev.mobilesandbox.cloud", + "capif_https_port": "36212", + "capif_register_port": "36211", + "capif_username": "echeva_0", + "capif_password": "echevapass", + "debug_mode": "False", + "invoker":{ + "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", + "capif_callback_url": "http://localhost:5000", + "csr_generation":{ + "csr_common_name": "invoker", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + } + }, + "provider":{ + "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", + "apfs": 1, + "aefs": 3, + "publish_req": { + "service_api_id": "80dbdd52ee766d4ad4494264e4289c", + "publisher_apf_id": "APF73e3458fb483c3c65f2f7e126ec851", + "publisher_aefs_ids": [ + "AEF07a01ccd74a160c195e69b4f116d66", + "AEFb5c206b46fc68c192aed6870899ea1" + ] + }, + "cert_generation":{ + "csr_common_name": "provider", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "api_description_path": "/Users/IDB0128/git_repos/pesp_capif_sdk/samples/provider_api_description_sample.json" + } +} diff --git a/network_app_samples/network_app_invoker_sample/network_app_invoker.py b/network_app_samples/network_app_invoker_sample/network_app_invoker.py new file mode 100644 index 0000000000000000000000000000000000000000..17407be230c64daf484a12c225c3aeef79123e7d --- /dev/null +++ b/network_app_samples/network_app_invoker_sample/network_app_invoker.py @@ -0,0 +1,112 @@ +import json +import sys +import os +import time +import requests + +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', '..', 'sdk') # Go up two levels and point to 'sdk' +sys.path.insert(0, sdk_path) + +# Import necessary classes from the SDK +from sdk import capif_invoker_connector, service_discoverer + +# Path to the OpenCAPIF SDK configuration file +capif_sdk_config_path = "./capif_sdk_config_sample.json" + +import requests +import json + +def update_token(access_token): + """ + Update the Postman environment with the new access token. + """ + # Your Postman API Key + postman_api_key = "PMAK-66fd117ac0a4eb0001e7881b-ecf2b89675a6a1bfd0856838e3b1aa552a" + + # Postman Environment ID + environment_id = "36540500-bfaa8442-180f-4247-892d-b0605581b6a6" + + # Postman API URL to modify environments + url = f"https://api.getpostman.com/environments/{environment_id}" + + # Headers for authentication and content type + headers = { + "X-Api-Key": postman_api_key, + "Content-Type": "application/json" + } + + # Get the current environment details + response = requests.get(url, headers=headers) + + # Check if the request was successful + if response.status_code == 200: + environment_data = response.json() + + # Check if the variable already exists + variables = environment_data['environment']['values'] + variable_found = False + for var in variables: + if var['key'] == "ACCESS TOKEN": + + # Update both initial and current value + var['value'] = access_token # Set initial value + var['current_value'] = access_token # Set current value + variable_found = True + break + + if not variable_found: + # If the variable doesn't exist, append it + variables.append({ + "key": "ACCESS TOKEN", + "value": access_token, # Initial value + "current_value": access_token, # Current value + "enabled": True + }) + + # Send the PUT request to update the environment + updated_data = { + "environment": { + "name": environment_data['environment']['name'], + "values": variables + } + } + + # Update the environment in Postman + put_response = requests.put(url, headers=headers, data=json.dumps(updated_data)) + + if put_response.status_code == 200: + print("Access token updated successfully.") + else: + print(f"Error updating the variable: {put_response.status_code}") + else: + print(f"Error getting the environment: {response.status_code}") + + +if __name__ == "__main__": + # Initialize the CAPIF invoker + capif_connector = capif_invoker_connector(config_file=capif_sdk_config_path) + capif_connector.onboard_invoker() + print("INVOKER ONBOARDING COMPLETED") + + # Now, with the certificates available, proceed with discovery + discoverer = service_discoverer(config_file=capif_sdk_config_path) + discoverer.discover() + + while True: + discoverer.get_tokens() + + # Load API details from the JSON file + apidetailspath = os.path.join(capif_connector.invoker_folder, + f"capif_api_security_context_details-{capif_connector.capif_username}.json") + with open(apidetailspath, 'r') as file: + apidetails = json.load(file) + + # Update the Postman token with the retrieved access token + update_token(apidetails['access_token']) + + print(apidetails['access_token']) + + # Wait for 599 seconds before the next update + time.sleep(599) diff --git a/network_app_samples/network_app_invoker_sample/postman/Demo-SDK.postman_collection.json b/network_app_samples/network_app_invoker_sample/postman/Demo-SDK.postman_collection.json new file mode 100644 index 0000000000000000000000000000000000000000..cb8f1d1a49df6864db637cfbb14c6fdea79d6de5 --- /dev/null +++ b/network_app_samples/network_app_invoker_sample/postman/Demo-SDK.postman_collection.json @@ -0,0 +1,537 @@ +{ + "info": { + "_postman_id": "4bc02799-96ca-4e10-a1fa-bb880f37ffb4", + "name": "Demo-SDK", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "36540500", + "_collection_link": "https://crimson-escape-958452.postman.co/workspace/My-Workspace~446d9f89-afac-41d1-869c-90d989948481/collection/36540500-4bc02799-96ca-4e10-a1fa-bb880f37ffb4?action=share&source=collection_link&creator=36540500" + }, + "item": [ + { + "name": "Resilience", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/resilience", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "resilience" + ] + } + }, + "response": [] + }, + { + "name": "Resilience", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"resilience_level\": \"high\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/resilience", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "resilience" + ] + } + }, + "response": [] + }, + { + "name": "Specific Resilience", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/resilience/1", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "resilience", + "1" + ], + "query": [ + { + "key": "", + "value": null, + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Specific Resilience", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"level\": \"high\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/resilience/1", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "resilience", + "1" + ] + } + }, + "response": [] + }, + { + "name": "Specific Resilience", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n\"name\": \"echeva_0\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/resilience/1", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "resilience", + "1" + ] + } + }, + "response": [] + }, + { + "name": "Resilience slice", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/slice/resilience", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "slice", + "resilience" + ] + } + }, + "response": [] + }, + { + "name": "Resilience slice", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"resilience_config\": \"custom\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/slice/resilience", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "slice", + "resilience" + ] + } + }, + "response": [] + }, + { + "name": "Qos", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8888/1/qos", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8888", + "path": [ + "1", + "qos" + ] + } + }, + "response": [] + }, + { + "name": "Qos", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"level\": \"high\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8088/slice/resilience", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8088", + "path": [ + "slice", + "resilience" + ] + } + }, + "response": [] + }, + { + "name": "Tsn", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8888/profile", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8888", + "path": [ + "profile" + ] + } + }, + "response": [] + }, + { + "name": "Tsn", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"parameter\": \"value\"\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8888/Network_apply", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8888", + "path": [ + "Network_apply" + ] + } + }, + "response": [] + }, + { + "name": "Slice", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8888/slice", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8888", + "path": [ + "slice" + ] + } + }, + "response": [] + }, + { + "name": "Slice", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{ACCESS TOKEN}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"status\": \"active\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8888/slice", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8888", + "path": [ + "slice" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/network_app_samples/network_app_invoker_sample/postman/SDK-DEMO.postman_environment.json b/network_app_samples/network_app_invoker_sample/postman/SDK-DEMO.postman_environment.json new file mode 100644 index 0000000000000000000000000000000000000000..d543aa8a70e6f05c597c742665434ae093adad06 --- /dev/null +++ b/network_app_samples/network_app_invoker_sample/postman/SDK-DEMO.postman_environment.json @@ -0,0 +1,14 @@ +{ + "id": "bfaa8442-180f-4247-892d-b0605581b6a6", + "name": "SDK-DEMO", + "values": [ + { + "key": "ACCESS TOKEN", + "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcyNzg2MzI1MSwianRpIjoiMDhlM2FhMmItNTI3ZC00NDFiLTljOWYtNWQ3Yjk1NDUyZGQ1IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IklOVjc2NzFiMDEwNWNhNWUzZjM2MGJlYTQ3NDY5NzI5YiIsIm5iZiI6MTcyNzg2MzI1MSwiZXhwIjoxNzI3ODYzODUxLCJpc3MiOiJJTlY3NjcxYjAxMDVjYTVlM2YzNjBiZWE0NzQ2OTcyOWIiLCJzY29wZSI6IjNncHAjQUVGNzJlN2Y5NDRhMjBmZTA3MmU5ZGIyNjM1MzNiNjQ2OlRlc3Q2NjtBRUY1NjA4YjYwNmFiMDhjZmQ3MTExMDE4ZTM5MzY2NjY6VGVzdDc3O0FFRmQ4MzZlOWI0OTE2MTY3M2UzZWU2NjMyYzQwOTUyMDpEZXBsb3ltZW50O0FFRjQ2MTBjNjNmY2I5NDA1MmViODg0N2FiZDJjNWVkNzpEZXBsb3ltZW50MTtBRUZlNGQyOTA2MTczOWM4MGFjZWIyNjkwNDM1NDIwM2E6a3M4NTAwX2dhdGV3YXk7QUVGNGNhMmNmYjk3MDBmOTE3YjhjY2UxMzY4NTk5YzYwOmtzODUwMF9nYXRld2F5X2RldmVsb3BtZW50O0FFRmY5OTExMTY2ZGFkZDcyZDlmMjMwNWU5MTIxNzc2ZDpUZXN0LTI7QUVGODViZWYwNWIzMGIxZTlkMzJjNWI3Y2I2ZWM3ZDAwOlRlc3QtMjtBRUZjNjhiYjA5MDU0NmRmYzExZDM5ODZhZGJjY2JiMTI6VGVzdC0yO0FFRmExMDBiMWE3NmNlMGE1ZTg0OTVjNmViZjQzYmY0YzpkZW1vX2FwaV9PQ0Y7QUVGZjVlMzA0YWU5YjM3NDk2N2Y1NDdhMDk2MmJkZmQyOjZHLXJlc2lsaWVuY2U7QUVGZDhmZmI2ZTJlMjFmZDQ5N2JlZDExZGE0MzNiYjA5OjZHLXJlc2lsaWVuY2U7QUVGZjVlMzA0YWU5YjM3NDk2N2Y1NDdhMDk2MmJkZmQyOjVHLU5ldEFwcC1BUEk7QUVGZDhmZmI2ZTJlMjFmZDQ5N2JlZDExZGE0MzNiYjA5OjVHLU5ldEFwcC1BUEk7QUVGZWU1YjU5ZGYyMzFlZjFjZDQwMjRkNmQ2Y2Q1NWU0OjVHLU5ldEFwcC1BUEkifQ.SrPqTUSvzVnO8LWvpfWNIpyFrTOJlK7cTcjxbc8_tDUEC1Ot1sujCJLdXBd0RjatnqTNWnGw063wON7gxFkwMcr3AxTw7Lnf4K7r31VWCfZB5odTP5prRrTpSZPhuTdEsMCdSL-vh56VbJJU0IAqG71cm08BXcrfBF08zYJpVBcTvNAi1J96-bo0QlbH2WYfHXEkcUirJ_DSosTCMyQIS2tTwoUd6GBDQvnKorjgGGyogP4QDZQYjiNOPUFmWmDmDVjAwncplqxfppukanQgBU_M1c9JeQYiNrkz3X8dhRlVqdwGXpqOBntWyt8mrQAf7GkWjYcMhYm17dyb5tpcPg", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2024-10-02T11:52:57.845Z", + "_postman_exported_using": "Postman/11.14.1" +} \ No newline at end of file diff --git a/network_app_samples/network_app_provider_sample/capif_sdk_config_sample.json b/network_app_samples/network_app_provider_sample/capif_sdk_config_sample.json new file mode 100644 index 0000000000000000000000000000000000000000..b9613dfd5f035fceb5f5cabf65ff6c6825526327 --- /dev/null +++ b/network_app_samples/network_app_provider_sample/capif_sdk_config_sample.json @@ -0,0 +1,60 @@ +{ + "capif_host": "capif-prev.mobilesandbox.cloud", + "register_host": "registercapif-prev.mobilesandbox.cloud", + "capif_https_port": "36212", + "capif_register_port": "36211", + "capif_username": "echeva_0", + "capif_password": "echevapass", + "debug_mode": "True", + "invoker":{ + "invoker_folder": "", + "capif_callback_url": "", + "csr_generation":{ + "csr_common_name": "", + "csr_organizational_unit": "", + "csr_organization": "", + "crs_locality": "", + "csr_state_or_province_name": "", + "csr_country_name": "", + "csr_email_address": "" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + } + }, + "provider":{ + "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", + "cert_generation":{ + "csr_common_name": "provider", + "csr_organizational_unit": "discovery", + "csr_organization": "telefonica", + "crs_locality": "madrid", + "csr_state_or_province_name": "madrid", + "csr_country_name": "ES", + "csr_email_address": "hola@gmail.com" + }, + "apfs": "1", + "aefs": "3", + "publish_req": { + "service_api_id": "", + "publisher_apf_id": "", + "publisher_aefs_ids": [ + "", + "" + ] + }, + "api_description_path": "" + } +} diff --git a/netapp-samples/netapp-provider-sample/nef-upf-partner-1.json b/network_app_samples/network_app_provider_sample/nef_upf_vendor_1.json similarity index 97% rename from netapp-samples/netapp-provider-sample/nef-upf-partner-1.json rename to network_app_samples/network_app_provider_sample/nef_upf_vendor_1.json index 064fdf115a9da83d4c9ac5273de09615374d31d2..38d1f8d22fede06591595d57465379090c4ad13a 100755 --- a/netapp-samples/netapp-provider-sample/nef-upf-partner-1.json +++ b/network_app_samples/network_app_provider_sample/nef_upf_vendor_1.json @@ -2,7 +2,7 @@ "apiName": "6G-resilience", "aefProfiles": [ { - "aefId": "AEF63e3d024de52005e452d03d73f9755", + "aefId": "AEFce66355108e807345dc5cf4bc88258", "versions": [ { "apiVersion": "6G_Resilience_v1", @@ -60,7 +60,7 @@ ] }, { - "aefId": "AEF3988e5fec8a0bcff1ee4ec64453292", + "aefId": "AEF322d6efae0e509ed0c04c4126fd738", "versions": [ { "apiVersion": "6G_Resilience_v1", diff --git a/netapp-samples/netapp-provider-sample/nef-upf-partner-2.json b/network_app_samples/network_app_provider_sample/nef_upf_vendor_2.json similarity index 97% rename from netapp-samples/netapp-provider-sample/nef-upf-partner-2.json rename to network_app_samples/network_app_provider_sample/nef_upf_vendor_2.json index bdb6e411e03d451cb561d58ad58ed1637c420de7..c98178e0a4b62be05412c5dcc36ffb9154144699 100755 --- a/netapp-samples/netapp-provider-sample/nef-upf-partner-2.json +++ b/network_app_samples/network_app_provider_sample/nef_upf_vendor_2.json @@ -2,7 +2,7 @@ "apiName": "5G-NetApp-API", "aefProfiles": [ { - "aefId": "AEF63e3d024de52005e452d03d73f9755", + "aefId": "AEFce66355108e807345dc5cf4bc88258", "versions": [ { "apiVersion": "QoS_v1", @@ -61,7 +61,7 @@ ] }, { - "aefId": "AEF3988e5fec8a0bcff1ee4ec64453292", + "aefId": "AEF322d6efae0e509ed0c04c4126fd738", "versions": [ { "apiVersion": "TSN_v1", @@ -136,7 +136,7 @@ ] }, { - "aefId": "AEFe21f6b078ecc8ba89630ab8f90f656", + "aefId": "AEFd7ade775c592bc3be4440d1656c084", "versions": [ { "apiVersion": "NetworkSlicing_v1", diff --git a/network_app_samples/network_app_provider_sample/network_app_provider.py b/network_app_samples/network_app_provider_sample/network_app_provider.py new file mode 100644 index 0000000000000000000000000000000000000000..a2038d52374564fa113eb48b3497f81ad2bafdc5 --- /dev/null +++ b/network_app_samples/network_app_provider_sample/network_app_provider.py @@ -0,0 +1,307 @@ +import threading +from OpenSSL import crypto +from flask_jwt_extended import jwt_required, JWTManager +from flask import Flask, jsonify, request +import sys +import os +import json +from werkzeug import serving + +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', '..', 'sdk') # Go up two levels and point to 'sdk' +sys.path.insert(0, sdk_path) +from sdk import capif_provider_connector + +capif_sdk_config_path = "./capif_sdk_config_sample.json" + +resilience_app = Flask("resilience_app") +jwt_flask = JWTManager(resilience_app) + + +def config_resilience(): + # JWT Configuration + resilience_app.config['JWT_ALGORITHM'] = 'RS256' + resilience_app.config['JWT_PUBLIC_KEY'] = "YourPublicKeyHere" + + with open("/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder/echeva_0/capif_cert_server.pem", "rb") as cert_file: + cert = cert_file.read() + + crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + pubKeyObject = crtObj.get_pubkey() + pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject) + + resilience_app.config['JWT_ALGORITHM'] = 'RS256' + resilience_app.config['JWT_PUBLIC_KEY'] = pubKeyString + + +# Dummy data for resilience management (In practice, this would connect to a database or service) +resilience_profiles = { + "1": {"profileId": 1, "status": "active"}, + "2": {"profileId": 2, "status": "inactive"} +} + + +@resilience_app.route("/resilience", methods=["GET", "POST"]) +@jwt_required() +def manage_resilience(): + """Endpoint to manage resilience functionalities in 6G networks""" + if request.method == "GET": + return jsonify({"message": "GET request for resilience management received."}) + + if request.method == "POST": + data = request.get_json() + return jsonify({"message": "Resilience management POST request received", "data": data}) + + +@resilience_app.route("/resilience/<profileId>", methods=["GET", "PUT", "DELETE"]) +@jwt_required() +def manage_single_resilience(profileId): + """Endpoint to manage a single resilience profile""" + if request.method == "GET": + profile = resilience_profiles.get(profileId, None) + if profile: + return jsonify({"profile": profile}) + else: + return jsonify({"message": f"Profile {profileId} not found"}), 404 + + if request.method == "PUT": + data = request.get_json() + resilience_profiles[profileId] = data + return jsonify({"message": f"Profile {profileId} updated", "data": data}) + + if request.method == "DELETE": + if profileId in resilience_profiles: + del resilience_profiles[profileId] + return jsonify({"message": f"Profile {profileId} deleted"}) + else: + return jsonify({"message": f"Profile {profileId} not found"}), 404 + + +@resilience_app.route("/slice/resilience", methods=["GET", "POST"]) +@jwt_required() +def manage_slice_resilience(): + """Endpoint for managing resilience in network slices""" + if request.method == "GET": + return jsonify({"message": "GET request for slice resilience received."}) + + if request.method == "POST": + data = request.get_json() + return jsonify({"message": "POST request for slice resilience management received", "data": data}) + + +@resilience_app.route("/slice/<sliceId>/resilience", methods=["GET", "PUT", "DELETE"]) +@jwt_required() +def manage_single_slice_resilience(sliceId): + """Endpoint for managing a single slice's resilience""" + if request.method == "GET": + return jsonify({"message": f"GET request for slice {sliceId} resilience received"}) + + if request.method == "PUT": + data = request.get_json() + return jsonify({"message": f"PUT request to update slice {sliceId}'s resilience", "data": data}) + + if request.method == "DELETE": + return jsonify({"message": f"DELETE request to remove slice {sliceId}'s resilience"}) + + +Network_app = Flask("Network_app") + +# JWT Configuration +jwt_flask = JWTManager(Network_app) + + +def config_network(): + with open("/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder/echeva_0/capif_cert_server.pem", "rb") as cert_file: + cert = cert_file.read() + + crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + pubKeyObject = crtObj.get_pubkey() + pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject) + + Network_app.config['JWT_ALGORITHM'] = 'RS256' + Network_app.config['JWT_PUBLIC_KEY'] = pubKeyString + +# Dummy data for QoS, TSN, and Slicing management + + +qos_profiles = { + "1": {"profileId": 1, "level": "high"}, + "2": {"profileId": 2, "level": "medium"} +} + +tsn_profiles = { + "profile1": {"name": "profile1", "config": "default"}, + "profile2": {"name": "profile2", "config": "custom"} +} + +network_slices = { + "slice1": {"sliceId": "slice1", "status": "active"}, + "slice2": {"sliceId": "slice2", "status": "inactive"} +} + +# QoS Management Endpoints ### + + +@Network_app.route("/<scsAsId>/qos", methods=["GET", "POST"]) +@jwt_required() +def manage_qos(scsAsId): + """Endpoint to manage QoS levels for network traffic""" + if request.method == "GET": + return jsonify({"message": "GET request for QoS management", "scsAsId": scsAsId}) + + if request.method == "POST": + data = request.get_json() + return jsonify({"message": "POST request to manage QoS", "scsAsId": scsAsId, "data": data}) + + +@Network_app.route("/<scsAsId>/qos/<profileId>", methods=["GET", "PUT", "DELETE"]) +@jwt_required() +def manage_single_qos_profile(scsAsId, profileId): + """Endpoint to manage single QoS profile""" + if request.method == "GET": + profile = qos_profiles.get(profileId) + if profile: + return jsonify({"profile": profile, "scsAsId": scsAsId}) + return jsonify({"message": f"Profile {profileId} not found"}), 404 + + if request.method == "PUT": + data = request.get_json() + qos_profiles[profileId] = data + return jsonify({"message": f"Profile {profileId} updated", "scsAsId": scsAsId, "data": data}) + + if request.method == "DELETE": + if profileId in qos_profiles: + del qos_profiles[profileId] + return jsonify({"message": f"Profile {profileId} deleted"}) + return jsonify({"message": f"Profile {profileId} not found"}), 404 + +# TSN Management Endpoints ### + + +@Network_app.route("/profile", methods=["GET"]) +@jwt_required() +def list_tsn_profiles(): + """Endpoint for retrieving the list of available TSN profiles""" + return jsonify({"profiles": tsn_profiles}) + + +@Network_app.route("/profile", methods=["GET"]) +@jwt_required() +def tsn_profile_by_name(): + """Endpoint for retrieving information about a single TSN profile""" + profile_name = request.args.get('name') + profile = tsn_profiles.get(profile_name) + if profile: + return jsonify({"profile": profile}) + return jsonify({"message": f"Profile {profile_name} not found"}), 404 + + +@Network_app.route("/Network_apply", methods=["POST"]) +@jwt_required() +def Network_apply_tsn_configuration(): + """Endpoint for configuring TSN connection parameters""" + data = request.get_json() + return jsonify({"message": "TSN configuration Network_applied", "data": data}) + + +@Network_app.route("/clear", methods=["POST"]) +@jwt_required() +def clear_tsn_configuration(): + """Endpoint for removing a previous TSN connection configuration""" + return jsonify({"message": "TSN configuration cleared"}) + +# Network Slicing Management Endpoints ### + + +@Network_app.route("/slice", methods=["GET", "POST"]) +@jwt_required() +def manage_slices(): + """Endpoint for managing network slices""" + if request.method == "GET": + return jsonify({"slices": network_slices}) + + if request.method == "POST": + data = request.get_json() + slice_id = f"slice{len(network_slices) + 1}" + network_slices[slice_id] = data + return jsonify({"message": "Network slice added", "sliceId": slice_id, "data": data}) + + +@Network_app.route("/slice/<sliceId>", methods=["GET", "PUT", "DELETE"]) +@jwt_required() +def manage_single_slice(sliceId): + """Endpoint for managing a single network slice""" + if request.method == "GET": + slice_ = network_slices.get(sliceId) + if slice_: + return jsonify({"slice": slice_}) + return jsonify({"message": f"Slice {sliceId} not found"}), 404 + + if request.method == "PUT": + data = request.get_json() + network_slices[sliceId] = data + return jsonify({"message": f"Slice {sliceId} updated", "data": data}) + + if request.method == "DELETE": + if sliceId in network_slices: + del network_slices[sliceId] + return jsonify({"message": f"Slice {sliceId} deleted"}) + return jsonify({"message": f"Slice {sliceId} not found"}), 404 + + +def run_resilience_app(): + serving.run_simple("0.0.0.0", 8088, resilience_app) + + +def run_network_app(): + serving.run_simple("0.0.0.0", 8888, Network_app) + + +if __name__ == "__main__": + try: + # Initialize the connector + capif_connector = capif_provider_connector(config_file=capif_sdk_config_path) + capif_connector.onboard_provider() + print("PROVIDER ONBOARDING COMPLETED") + + capif_connector.api_description_path = "./nef_upf_vendor_1.json" + + APF = capif_connector.api_prov_funcs["APF-1"] + + AEF1 = capif_connector.api_prov_funcs["AEF-1"] + AEF2 = capif_connector.api_prov_funcs["AEF-2"] + AEF3 = capif_connector.api_prov_funcs["AEF-3"] + + capif_connector.publish_req['publisher_apf_id'] = APF + capif_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2] + + capif_connector.publish_services() + + capif_connector.api_description_path = "./nef_upf_vendor_2.json" + + capif_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2, AEF3] + + capif_connector.publish_services() + + print("APIS PUBLISHED") + + config_network() + + config_resilience() + + t1 = threading.Thread(target=run_resilience_app) + t2 = threading.Thread(target=run_network_app) + t1.start() + t2.start() + t1.join() + t2.join() + + print("SERVERS WORKING") + + except FileNotFoundError as e: + print(f"Error: {e}") + except json.JSONDecodeError as e: + print(f"Error reading the JSON file: {e}") + except Exception as e: + print(f"Unexpected error: {e}") diff --git a/samples/capif_api_security_context_details-sample.json b/samples/capif_api_security_context_details_sample.json similarity index 100% rename from samples/capif_api_security_context_details-sample.json rename to samples/capif_api_security_context_details_sample.json diff --git a/samples/config_sample.json b/samples/config_sample.json new file mode 100644 index 0000000000000000000000000000000000000000..a358ca4cc7daa0dc73ef770f0cb2d8f3a42ff49e --- /dev/null +++ b/samples/config_sample.json @@ -0,0 +1,61 @@ +{ + "capif_host": "", + "register_host": "", + "capif_https_port": "", + "capif_register_port": "", + "capif_username": "", + "capif_password": "", + "debug_mode": "", + "invoker": { + "invoker_folder": "", + "capif_callback_url": "", + "cert_generation": { + "csr_common_name": "", + "csr_organizational_unit": "", + "csr_organization": "", + "crs_locality": "", + "csr_state_or_province_name": "", + "csr_country_name": "", + "csr_email_address": "" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + } + }, + "provider": { + "provider_folder": "", + "apfs": "", + "aefs": "", + "publish_req": { + "service_api_id": "", + "publisher_apf_id": "", + "publisher_aefs_ids": [ + "", + "" + ] + }, + "cert_generation": { + "csr_common_name": "", + "csr_organizational_unit": "", + "csr_organization": "", + "crs_locality": "", + "csr_state_or_province_name": "", + "csr_country_name": "", + "csr_email_address": "" + }, + "api_description_path": "" + } + } + \ No newline at end of file diff --git a/samples/enviroment-variables-sample.txt b/samples/enviroment-variables-sample.txt deleted file mode 100644 index cd3e361fa5b14fcf90ba178b013f824d8108d303..0000000000000000000000000000000000000000 --- a/samples/enviroment-variables-sample.txt +++ /dev/null @@ -1,39 +0,0 @@ -export INVOKER_FOLDER="/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder" -export PROVIDER_FOLDER="/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder" -export CAPIF_HOST="capif-prev.mobilesandbox.cloud" -export REGISTER_HOST="registercapif-prev.mobilesandbox.cloud" -export CAPIF_HTTPS_PORT="36212" -export CAPIF_REGISTER_PORT="36211" -export CAPIF_CALLBACK_URL="http://localhost:5000" -export CSR_COMMON_NAME="test03" -export CSR_ORGANIZATIONAL_UNIT="test_app_ou" -export CSR_ORGANIZATION="test_app_o" -export CRS_LOCALITY="Madrid" -export CSR_STATE_OR_PROVINCE_NAME="Madrid" -export CSR_COUNTRY_NAME="ES" -export CSR_EMAIL_ADDRESS="test@example.com" -export CAPIF_USERNAME="echeva_0" -export CAPIF_PASSWORD="echevapass" -export APFS="1" -export AEFS="2" -export DEBUG_MODE="True" - -export DISCOVER_FILTER_API_NAME="Deployment" -export DISCOVER_FILTER_API_VERSION="" -export DISCOVER_FILTER_COMM_TYPE="" -export DISCOVER_FILTER_PROTOCOL="" -export DISCOVER_FILTER_AEF_ID="" -export DISCOVER_FILTER_DATA_FORMAT="" -export DISCOVER_FILTER_API_CAT="" -export DISCOVER_FILTER_PREFERRED_AEF_LOC="" -export DISCOVER_FILTER_REQ_API_PROV_NAME="" -export DISCOVER_FILTER_SUPPORTED_FEATURES="" -export DISCOVER_FILTER_API_SUPPORTED_FEATURES="" -export DISCOVER_FILTER_UE_IP_ADDR="" -export DISCOVER_FILTER_SERVICE_KPIS="" - -export PUBLISH_REQ_SERVICE_API_ID="6d3fbdcc138f81b36c7b1595229377" -export PUBLISH_REQ_PUBLISHER_APF_ID="APFdf22044ce6988b136b253a8c5b26f1" -export PUBLISH_REQ_PUBLISHER_AEFS_IDS="AEF0f66526ff2fd8a1be721ace3506adb,AEF212b55c43b1b16116192043bab9e05" - -export API_DESCRIPTION_PATH="/Users/IDB0128/git_repos/pesp_capif_sdk/samples/provider_api_description_sample.json" diff --git a/samples/enviroment_variables_sample.txt b/samples/enviroment_variables_sample.txt new file mode 100644 index 0000000000000000000000000000000000000000..79d2ee03ffdbd596f383a75def3e6d48a7f79577 --- /dev/null +++ b/samples/enviroment_variables_sample.txt @@ -0,0 +1,60 @@ +#GENERAL CONFIGURATION + +export CAPIF_HOST=your_capif_host +export REGISTER_HOST=your_register_host +export CAPIF_HTTPS_PORT=443 +export CAPIF_REGISTER_PORT=8084 +export CAPIF_USERNAME=your_capif_username +export CAPIF_PASSWORD=your_capif_password +export DEBUG_MODE=False + +# INVOKER +export CAPIF_CALLBACK_URL=https://your_callback_url + +# CSR (Certificate Signing Request) Configuration INVOKER +export INVOKER_CSR_COMMON_NAME=your_csr_common_name +export INVOKER_CSR_ORGANIZATIONAL_UNIT=your_organizational_unit +export INVOKER_CSR_ORGANIZATION=your_organization +export INVOKER_CRS_LOCALITY=your_locality +export INVOKER_CSR_STATE_OR_PROVINCE_NAME=your_state_or_province +export INVOKER_CSR_COUNTRY_NAME=your_country_name +export INVOKER_CSR_EMAIL_ADDRESS=your_email_address + +#DISCOVER FILTER +export DISCOVER_FILTER_API_NAME="" +export DISCOVER_FILTER_API_VERSION="" +export DISCOVER_FILTER_COMM_TYPE="" +export DISCOVER_FILTER_PROTOCOL="" +export DISCOVER_FILTER_AEF_ID="" +export DISCOVER_FILTER_DATA_FORMAT="" +export DISCOVER_FILTER_API_CAT="" +export DISCOVER_FILTER_PREFERRED_AEF_LOC="" +export DISCOVER_FILTER_REQ_API_PROV_NAME="" +export DISCOVER_FILTER_SUPPORTED_FEATURES="" +export DISCOVER_FILTER_API_SUPPORTED_FEATURES="" +export DISCOVER_FILTER_UE_IP_ADDR="" +export DISCOVER_FILTER_SERVICE_KPIS="" + + +# Provider Configuration +export PROVIDER_FOLDER=/path/to/provider/folder + +# CSR (Certificate Signing Request) Configuration +export PROVIDER_CSR_COMMON_NAME=your_csr_common_name +export PROVIDER_CSR_ORGANIZATIONAL_UNIT=your_organizational_unit +export PROVIDER_CSR_ORGANIZATION=your_organization +export PROVIDER_CRS_LOCALITY=your_locality +export PROVIDER_CSR_STATE_OR_PROVINCE_NAME=your_state_or_province +export PROVIDER_CSR_COUNTRY_NAME=your_country_name +export PROVIDER_CSR_EMAIL_ADDRESS=your_email_address + +# Provider Specific Values +export APFS=your_apfs_value +export AEFS=your_aefs_value +export API_DESCRIPTION_PATH=/path/to/api_description + +# Publish Request Configuration +export PUBLISH_REQ_SERVICE_API_ID=your_service_api_id +export PUBLISH_REQ_PUBLISHER_APF_ID=your_publisher_apf_id +export PUBLISH_REQ_PUBLISHER_AEFS_IDS=your_publisher_aefs_ids + diff --git a/scripts/deregister_and_login.py b/scripts/deregister_and_login.py index 1e63f9d5bc7fe4267c89b9aacb3650086512ba28..d44d914cf9e2ee8a842a9be027f050e8065838a3 100644 --- a/scripts/deregister_and_login.py +++ b/scripts/deregister_and_login.py @@ -8,11 +8,11 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) logging.basicConfig( - level=logging.INFO, # Nivel mÃnimo de severidad a registrar - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # Formato del mensaje de log + level=logging.INFO, # Minimum severity level to log + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # Log message format handlers=[ - logging.FileHandler("logs/register_logs.log"), # Registra en un archivo - logging.StreamHandler() # También muestra en la consola + logging.FileHandler("logs/register_logs.log"), # Logs to a file + logging.StreamHandler() # Also shows in the console ] ) @@ -24,7 +24,7 @@ def main(): admintoken = log_result["access_token"] de_register_from_capif(admintoken, variables) - logger.info("User eliminated succesfully") + logger.info("User eliminated successfully") def __log_to_capif(variables): @@ -71,7 +71,7 @@ def de_register_from_capif(admin_token, variables): def __load_config_file(config_file: str): - """Carga el archivo de configuración.""" + """Loads the configuration file.""" try: with open(config_file, 'r') as file: return json.load(file) diff --git a/scripts/invoker_capif_connector.py b/scripts/invoker_capif_connector.py index 141a403f9862ee96a5fa3ce24247e8a78a43ab8f..bcac3b581127d017bd1aaec38d9fe83637e33804 100755 --- a/scripts/invoker_capif_connector.py +++ b/scripts/invoker_capif_connector.py @@ -1,27 +1,24 @@ - - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFInvokerConnector +from sdk import capif_invoker_connector def showcase_capif_connector(): """ This method showcases how one can use the CAPIFConnector class. - """ - capif_connector = CAPIFInvokerConnector(config_file=utilities.get_config_file()) + capif_connector = capif_invoker_connector(config_file=utilities.get_config_file()) capif_connector.onboard_invoker() print("COMPLETED") if __name__ == "__main__": - # Let's register invoker to CAPIF. This should happen exactly once + # Register invoker to CAPIF. This should happen exactly once showcase_capif_connector() diff --git a/scripts/invoker_capif_connector_offboarding.py b/scripts/invoker_capif_connector_offboarding.py index 125a7c2035590808903840e874e26029eab8834c..2e59cd39b39154304b494891d399b8a776c09dd8 100755 --- a/scripts/invoker_capif_connector_offboarding.py +++ b/scripts/invoker_capif_connector_offboarding.py @@ -2,14 +2,14 @@ import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFInvokerConnector +from sdk import capif_invoker_connector def showcase_offboard_and_deregister_invoker(): - capif_connector = CAPIFInvokerConnector(config_file=utilities.get_config_file()) + capif_connector = capif_invoker_connector(config_file=utilities.get_config_file()) capif_connector.offboard_invoker() print("COMPLETED") diff --git a/scripts/invoker_capif_connector_update.py b/scripts/invoker_capif_connector_update.py index 34c993ae76b41f784ff32b389e9ad413a3bed83b..ba0665b22c0622ceb0cdfd8daad7526efd7f5ee3 100644 --- a/scripts/invoker_capif_connector_update.py +++ b/scripts/invoker_capif_connector_update.py @@ -1,13 +1,12 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFInvokerConnector +from sdk import capif_invoker_connector def showcase_capif_connector(): """ @@ -16,12 +15,12 @@ def showcase_capif_connector(): It is a low level class part of the SDK that is not required to use while creating invokers """ - capif_connector = CAPIFInvokerConnector(config_file=utilities.get_config_file()) + capif_connector = capif_invoker_connector(config_file=utilities.get_config_file()) capif_connector.update_invoker() print("COMPLETED") if __name__ == "__main__": - # Let's register invoker to CAPIF. This should happen exactly once + # Register invoker to CAPIF. This should happen exactly once showcase_capif_connector() diff --git a/scripts/invoker_service_discovery.py b/scripts/invoker_service_discovery.py index 54cd586ba60d4c2de329f03c1df6a8bda67cb9be..e9c035e79231ba026e4db2a59ea82b5215465f4f 100755 --- a/scripts/invoker_service_discovery.py +++ b/scripts/invoker_service_discovery.py @@ -1,23 +1,22 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py +# Now import the classes from your sdk.py file -from sdk import ServiceDiscoverer +from sdk import service_discoverer def showcase_access_token_retrieval_from_capif(): - service_discoverer = ServiceDiscoverer(config_file=utilities.get_config_file()) + service_discoverer = service_discoverer(config_file=utilities.get_config_file()) service_discoverer.discover() if __name__ == "__main__": - # The following code assumes that you have already registered the net app to capif. + # The following code assumes that you have already registered the net app to CAPIF. # showcase_service_discovery() # showcase_retrieve_endpoint_url_from_tsn() showcase_access_token_retrieval_from_capif() diff --git a/scripts/invoker_service_get_token.py b/scripts/invoker_service_get_token.py index 404469822e530da62278f623223e90fbd1bd753d..f6095bb352b00883a82a00674bf914b3128cb886 100755 --- a/scripts/invoker_service_get_token.py +++ b/scripts/invoker_service_get_token.py @@ -1,23 +1,22 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py +# Now import the classes from your sdk.py file -from sdk import ServiceDiscoverer +from sdk import service_discoverer def showcase_access_token_retrieval_from_capif(): - service_discoverer = ServiceDiscoverer(config_file=utilities.get_config_file()) + service_discoverer = service_discoverer(config_file=utilities.get_config_file()) service_discoverer.get_tokens() if __name__ == "__main__": - # The following code assumes that you have already registered the net app to capif. + # The following code assumes that you have already registered the net app to CAPIF. # showcase_service_discovery() # showcase_retrieve_endpoint_url_from_tsn() showcase_access_token_retrieval_from_capif() diff --git a/scripts/provider_capif_connector.py b/scripts/provider_capif_connector.py index 843d256ab9283be87c97e102055e77fb16b716ee..4c464828277896990bfcf9b7d2684a54a30215f4 100755 --- a/scripts/provider_capif_connector.py +++ b/scripts/provider_capif_connector.py @@ -1,21 +1,20 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector -# Ahora importa las clases desde tu archivo sdk.py +from sdk import capif_provider_connector +# Now import the classes from your sdk.py file def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.onboard_provider() @@ -23,5 +22,5 @@ def showcase_capif_nef_connector(): if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_capif_connector_offboarding.py b/scripts/provider_capif_connector_offboarding.py index a274cc8e09f23f6c377722b4dceb3ce8c58d443b..642fb2709e268715c4fc1086c9e772b5b9720e59 100755 --- a/scripts/provider_capif_connector_offboarding.py +++ b/scripts/provider_capif_connector_offboarding.py @@ -1,16 +1,15 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector +from sdk import capif_provider_connector def offboard_capif_nef_connector(): - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.offboard_provider() print("COMPLETED") diff --git a/scripts/provider_capif_connector_update.py b/scripts/provider_capif_connector_update.py index e4fa912717b98374cb1d8b25e7d67d28365a4e6c..53b3e8ad911f1dbabf2cc60131fdcb3a147e65fb 100644 --- a/scripts/provider_capif_connector_update.py +++ b/scripts/provider_capif_connector_update.py @@ -1,20 +1,19 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector +from sdk import capif_provider_connector def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.update_provider() @@ -22,5 +21,5 @@ def showcase_capif_nef_connector(): if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_get_all_published_api.py b/scripts/provider_get_all_published_api.py index 1448a5782df24fa15bfe8531b7eb386e74748bba..f9cbb0c71a64caa8b342154c532ba59e97013e42 100644 --- a/scripts/provider_get_all_published_api.py +++ b/scripts/provider_get_all_published_api.py @@ -1,26 +1,25 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py -from sdk import CAPIFProviderConnector +# Now import the classes from your sdk.py file +from sdk import capif_provider_connector def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.get_all_services() print("COMPLETED") if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_get_published_api.py b/scripts/provider_get_published_api.py index 5128481f4a60b60a20515575f67bb2f063eea3d4..5ffd0bb32cffb804dc3ccf819e44d708e56f1b9a 100644 --- a/scripts/provider_get_published_api.py +++ b/scripts/provider_get_published_api.py @@ -1,26 +1,26 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py +# Now import the classes from your sdk.py file + +from sdk import capif_provider_connector -from sdk import CAPIFProviderConnector def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.get_service() print("COMPLETED") if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_publish_api.py b/scripts/provider_publish_api.py index 86c6774489c624b41faeda8f3290ab6885137eab..646e86fd71a6e5bfd75604419ff97ba9d99b6024 100644 --- a/scripts/provider_publish_api.py +++ b/scripts/provider_publish_api.py @@ -1,26 +1,26 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py +# Now import the classes from your sdk.py file + +from sdk import capif_provider_connector -from sdk import CAPIFProviderConnector def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.publish_services() print("COMPLETED") if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_unpublish_api.py b/scripts/provider_unpublish_api.py index f118ca7eab059f7672f75684990f39274f5c5952..81ab9e21d09fb999cffa3ae32801c2783e79a00f 100644 --- a/scripts/provider_unpublish_api.py +++ b/scripts/provider_unpublish_api.py @@ -1,26 +1,24 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector -# Ahora importa las clases desde tu archivo sdk.py - +from sdk import capif_provider_connector +# Now import the classes from your sdk.py file def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.unpublish_service() print("COMPLETED") if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/provider_update_api.py b/scripts/provider_update_api.py index dd48209fe920b3b7c6611cb1e6aae6a8139aede1..8ac9548df052d76b43ff76fe11ff0a3ec3484cb5 100644 --- a/scripts/provider_update_api.py +++ b/scripts/provider_update_api.py @@ -1,26 +1,26 @@ - import sys import os import utilities -# Añadir el directorio del SDK al PYTHONPATH usando una ruta relativa -script_dir = os.path.dirname(os.path.abspath(__file__)) # Directorio actual del script -sdk_path = os.path.join(script_dir, '..', 'sdk') # Subir un nivel y apuntar a 'sdk' +# Add the SDK directory to PYTHONPATH using a relative path +script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory +sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up one level and point to 'sdk' sys.path.insert(0, sdk_path) -# Ahora importa las clases desde tu archivo sdk.py +# Now import the classes from your sdk.py file + +from sdk import capif_provider_connector -from sdk import CAPIFProviderConnector def showcase_capif_nef_connector(): """ """ - capif_connector = CAPIFProviderConnector(config_file=utilities.get_config_file()) + capif_connector = capif_provider_connector(config_file=utilities.get_config_file()) capif_connector.update_service() print("COMPLETED") if __name__ == "__main__": - # Let's register a NEF to CAPIF. This should happen exactly once + # Register a NEF to CAPIF. This should happen exactly once showcase_capif_nef_connector() diff --git a/scripts/register_and_login.py b/scripts/register_and_login.py index 0659754e3298cdc895926ac5a7c99189588ee6c0..7fd26913fcd9d1feda2d97e6ea1c2ceed0ae390e 100644 --- a/scripts/register_and_login.py +++ b/scripts/register_and_login.py @@ -6,19 +6,16 @@ import utilities from requests.auth import HTTPBasicAuth urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - logging.basicConfig( - level=logging.INFO, # Nivel mÃnimo de severidad a registrar - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # Formato del mensaje de log + level=logging.INFO, # Minimum severity level to log + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # Log message format handlers=[ - logging.FileHandler("logs/register_logs.log"), # Registra en un archivo - logging.StreamHandler() # También muestra en la consola + logging.FileHandler("logs/register_logs.log"), # Log to a file + logging.StreamHandler() # Also display in the console ] ) - def main(): - variables = __load_config_file(config_file=utilities.get_register_file()) log_result = __log_to_capif(variables) admintoken = log_result["access_token"] @@ -26,7 +23,6 @@ def main(): uuid = postcreation["uuid"] logger.info(uuid) - def __log_to_capif(variables): logger.info("Logging in to CAPIF") capif_register_url = "https://" + variables["register_host"].strip() + ":" + variables["capif_register_port"] + "/" @@ -48,7 +44,6 @@ def __log_to_capif(variables): logger.error(f"Error during login to CAPIF: {e}") raise - def __create_user(admin_token, variables): logger.info("Creating user in CAPIF") capif_register_url = "https://" + variables["register_host"].strip() + ":" + variables["capif_register_port"] + "/" @@ -79,9 +74,8 @@ def __create_user(admin_token, variables): logger.error(f"Error during user creation in CAPIF: {e}") raise - def __load_config_file(config_file: str): - """Carga el archivo de configuración.""" + """Load the configuration file.""" try: with open(config_file, 'r') as file: return json.load(file) @@ -89,7 +83,6 @@ def __load_config_file(config_file: str): logger.warning(f"Configuration file {config_file} not found. Using defaults or environment variables.") return {} - if __name__ == "__main__": logger = logging.getLogger("CAPIF Register") logger.info("Initializing CAPIF Register") diff --git a/scripts/utilities.py b/scripts/utilities.py index 89759504341c0fa8cf368c84b2b21cc7c8b45516..238f969ea14f4fefe6ce480a856f4c94046c5c1d 100755 --- a/scripts/utilities.py +++ b/scripts/utilities.py @@ -1,8 +1,8 @@ def get_config_file() -> str: - return "../config/capif-sdk-config.json" + return "../config/capif_sdk_config.json" def get_register_file() -> str: - return "../config/capif-sdk-register.json" \ No newline at end of file + return "../config/capif_sdk_register.json" \ No newline at end of file diff --git a/sdk/CAPIFInvokerConnector.py b/sdk/capif_invoker_connector.py similarity index 80% rename from sdk/CAPIFInvokerConnector.py rename to sdk/capif_invoker_connector.py index 029a9a971d8ed280316f243bc07ad196fa0a5d4c..664dbf4becc9536ae2f1882f588f306369f332c3 100644 --- a/sdk/CAPIFInvokerConnector.py +++ b/sdk/capif_invoker_connector.py @@ -18,7 +18,7 @@ from requests.exceptions import RequestsDependencyWarning urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) warnings.filterwarnings("ignore", category=RequestsDependencyWarning) # noqa: E501 -# Configuración básica del logger +# Basic configuration of the logger functionality log_path = 'logs/sdk_logs.log' @@ -28,106 +28,90 @@ if not os.path.exists(log_dir): os.makedirs(log_dir) logging.basicConfig( - level=logging.NOTSET, # Nivel mÃnimo de severidad a registrar - # Formato del mensaje de log + level=logging.NOTSET, # Minimum severity level to log + # Log message format format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ - logging.FileHandler(log_path), # Registra en un archivo - logging.StreamHandler() # También muestra en la consola + logging.FileHandler(log_path), # Log to a file + logging.StreamHandler() # Also display in the console ] ) -class CAPIFInvokerConnector: +class capif_invoker_connector: """ Τhis class is responsbile for onboarding an Invoker (ex. a Invoker) to CAPIF """ - - def __init__(self , config_file: str): + def __init__(self, config_file: str): config_file = os.path.abspath(config_file) - # Cargar configuración desde archivo si es necesario + # Load configuration from file if necessary config = self.__load_config_file(config_file) - debug_mode = os.getenv('DEBUG_MODE', config.get( - 'debug_mode', 'False')).strip().lower() + debug_mode = os.getenv('DEBUG_MODE', config.get('debug_mode', 'False')).strip().lower() if debug_mode == "false": debug_mode = False + else: + debug_mode = True - # Inicializar logger + # Initialize logger for this class self.logger = logging.getLogger(self.__class__.__name__) if debug_mode: self.logger.setLevel(logging.DEBUG) else: self.logger.setLevel(logging.WARNING) + # Set logging level for urllib based on debug_mode urllib_logger = logging.getLogger("urllib3") if not debug_mode: urllib_logger.setLevel(logging.WARNING) else: urllib_logger.setLevel(logging.DEBUG) - self.logger.info("Initializing CAPIFInvokerConnector") - - # Asignar valores desde variables de entorno o desde configuración - - invoker_general_folder = os.path.abspath( - os.getenv('invoker_folder', config.get('invoker_folder', '')).strip()) - - capif_host = os.getenv( - 'CAPIF_HOST', config.get('capif_host', '')).strip() - register_host = os.getenv( - 'REGISTER_HOST', config.get('register_host', '')).strip() - capif_https_port = str( - os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) - capif_register_port = str(os.getenv( - 'CAPIF_REGISTER_PORT', config.get('capif_register_port', '')).strip()) - capif_invoker_username = os.getenv( - 'CAPIF_USERNAME', config.get('capif_username', '')).strip() - capif_invoker_password = os.getenv( - 'CAPIF_PASSWORD', config.get('capif_password', '')).strip() - capif_callback_url = os.getenv( - 'CAPIF_CALLBACK_URL', config.get('capif_callback_url', '')).strip() - - csr_common_name = os.getenv( - 'CSR_COMMON_NAME', config.get('csr_common_name', '')).strip() - csr_organizational_unit = os.getenv( - 'CSR_ORGANIZATIONAL_UNIT', config.get('csr_organizational_unit', '')).strip() - csr_organization = os.getenv( - 'CSR_ORGANIZATION', config.get('csr_organization', '')).strip() - crs_locality = os.getenv( - 'CRS_LOCALITY', config.get('crs_locality', '')).strip() - csr_state_or_province_name = os.getenv('CSR_STATE_OR_PROVINCE_NAME', config.get( - 'csr_state_or_province_name', '')).strip() - csr_country_name = os.getenv( - 'CSR_COUNTRY_NAME', config.get('csr_country_name', '')).strip() - csr_email_address = os.getenv( - 'CSR_EMAIL_ADDRESS', config.get('csr_email_address', '')).strip() - - self.invoker_folder = os.path.join( - invoker_general_folder, capif_invoker_username) + self.logger.info("Initializing capif_invoker_connector") + + # Assign values from environment variables or JSON configuration + invoker_config = config.get('invoker', {}) + invoker_general_folder = os.path.abspath(os.getenv('invoker_folder', invoker_config.get('invoker_folder', '')).strip()) + + capif_host = os.getenv('CAPIF_HOST', config.get('capif_host', '')).strip() + register_host = os.getenv('REGISTER_HOST', config.get('register_host', '')).strip() + capif_https_port = str(os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) + capif_register_port = str(os.getenv('CAPIF_REGISTER_PORT', config.get('capif_register_port', '')).strip()) + capif_username = os.getenv('CAPIF_USERNAME', config.get('capif_username', '')).strip() + capif_invoker_password = os.getenv('CAPIF_PASSWORD', config.get('capif_password', '')).strip() + capif_callback_url = os.getenv('CAPIF_CALLBACK_URL', invoker_config.get('capif_callback_url', '')).strip() + + # Extract CSR configuration from the JSON + csr_config = invoker_config.get('cert_generation', {}) + csr_common_name = os.getenv('INVOKER_CSR_COMMON_NAME', csr_config.get('csr_common_name', '')).strip() + csr_organizational_unit = os.getenv('INVOKER_CSR_ORGANIZATIONAL_UNIT', csr_config.get('csr_organizational_unit', '')).strip() + csr_organization = os.getenv('INVOKER_CSR_ORGANIZATION', csr_config.get('csr_organization', '')).strip() + crs_locality = os.getenv('INVOKER_CRS_LOCALITY', csr_config.get('crs_locality', '')).strip() + csr_state_or_province_name = os.getenv('INVOKER_CSR_STATE_OR_PROVINCE_NAME', csr_config.get('csr_state_or_province_name', '')).strip() + csr_country_name = os.getenv('INVOKER_CSR_COUNTRY_NAME', csr_config.get('csr_country_name', '')).strip() + csr_email_address = os.getenv('INVOKER_CSR_EMAIL_ADDRESS', csr_config.get('csr_email_address', '')).strip() + + # Define the invoker folder path and create it if it doesn't exist + self.invoker_folder = os.path.join(invoker_general_folder, capif_username) os.makedirs(self.invoker_folder, exist_ok=True) - # Resto del código original para inicializar URLs y otros atributos + # Configure URLs for CAPIF HTTPS and register services if len(capif_https_port) == 0 or int(capif_https_port) == 443: self.capif_https_url = "https://" + capif_host.strip() + "/" else: - self.capif_https_url = ( - "https://" + capif_host.strip() + ":" + capif_https_port.strip() + "/" - ) + self.capif_https_url = "https://" + capif_host.strip() + ":" + capif_https_port.strip() + "/" if len(capif_register_port) == 0: self.capif_register_url = "https://" + register_host.strip() + ":8084/" else: - self.capif_register_url = ( - "https://" + register_host.strip() + ":" + capif_register_port.strip() + "/" - ) + self.capif_register_url = "https://" + register_host.strip() + ":" + capif_register_port.strip() + "/" - self.capif_callback_url = self.__add_trailing_slash_to_url_if_missing( - capif_callback_url.strip() - ) + # Ensure the callback URL ends with a slash + self.capif_callback_url = self.__add_trailing_slash_to_url_if_missing(capif_callback_url.strip()) - self.capif_invoker_username = capif_invoker_username + # Assign final attributes for CAPIF connection and CSR details + self.capif_username = capif_username self.capif_invoker_password = capif_invoker_password self.csr_common_name = "invoker_" + csr_common_name @@ -137,15 +121,12 @@ class CAPIFInvokerConnector: self.csr_state_or_province_name = csr_state_or_province_name self.csr_country_name = csr_country_name self.csr_email_address = csr_email_address - self.capif_api_details_filename = "capif_api_security_context_details-" + \ - self.capif_invoker_username+".json" - # self.capif_api_details = self.__load_invoker_api_details() + self.capif_api_details_filename = "capif_api_security_context_details-" + self.capif_username + ".json" - self.logger.info( - "CAPIFInvokerConnector initialized with the capif-sdk-config.json parameters") + self.logger.info("capif_invoker_connector initialized with the JSON parameters") def __load_config_file(self, config_file: str): - """Carga el archivo de configuración.""" + """Loads the configuration file.""" try: with open(config_file, 'r') as file: return json.load(file) @@ -277,7 +258,7 @@ class CAPIFInvokerConnector: folder_path = self.invoker_folder if os.path.exists(folder_path): - # Elimina todo el contenido dentro de la carpeta + # Removes all the content within the folder for root, dirs, files in os.walk(folder_path): for file in files: os.remove(os.path.join(root, file)) @@ -302,7 +283,7 @@ class CAPIFInvokerConnector: "GET", url, headers={"Content-Type": "application/json"}, - auth=HTTPBasicAuth(self.capif_invoker_username, + auth=HTTPBasicAuth(self.capif_username, self.capif_invoker_password), verify=False, ) @@ -353,7 +334,7 @@ class CAPIFInvokerConnector: ) response.raise_for_status() response_payload = json.loads(response.text) - name = self.capif_invoker_username+".crt" + name = self.capif_username+".crt" pathcsr = os.path.join(self.invoker_folder, name) certification_file = open( pathcsr, "wb" @@ -384,7 +365,7 @@ class CAPIFInvokerConnector: ) as outfile: json.dump( { - "user_name": self.capif_invoker_username, + "user_name": self.capif_username, "api_invoker_id": api_invoker_id, "discover_services_url": discover_services_url, }, @@ -449,7 +430,7 @@ class CAPIFInvokerConnector: } signed_key_crt_path = os.path.join( self.invoker_folder, - self.capif_invoker_username + ".crt" + self.capif_username + ".crt" ) private_key_path = os.path.join( diff --git a/sdk/CAPIFProviderConnector.py b/sdk/capif_provider_connector.py similarity index 84% rename from sdk/CAPIFProviderConnector.py rename to sdk/capif_provider_connector.py index 7d33a1d1392492319543afbc2e213543997d5095..f1d8640fd8e7525a1ab4210bf5a12fd7f72a6e0e 100644 --- a/sdk/CAPIFProviderConnector.py +++ b/sdk/capif_provider_connector.py @@ -22,7 +22,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) warnings.filterwarnings("ignore", category=RequestsDependencyWarning) -# Configuración básica del logger +# Basic logger configuration log_path = 'logs/sdk_logs.log' @@ -32,40 +32,43 @@ if not os.path.exists(log_dir): os.makedirs(log_dir) logging.basicConfig( - level=logging.NOTSET, # Nivel mÃnimo de severidad a registrar - # Formato del mensaje de log + level=logging.NOTSET, # Minimum severity level to log + # Log message format format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ - logging.FileHandler(log_path), # Registra en un archivo - logging.StreamHandler() # También muestra en la consola + logging.FileHandler(log_path), # Logs to a file + logging.StreamHandler() # Also outputs to the console ] ) -class CAPIFProviderConnector: +class capif_provider_connector: """ Τhis class is responsible for onboarding an exposer (eg. NEF emulator) to CAPIF """ def __init__(self, config_file: str): """ - Inicializa el conector CAPIFProvider con los parámetros especificados en el archivo de configuración. + Initializes the CAPIFProvider connector with the parameters specified in the configuration file. """ - # Cargar configuración desde archivo si es necesario + # Load configuration from file if necessary config_file = os.path.abspath(config_file) self.config_path = os.path.dirname(config_file)+"/" config = self.__load_config_file(config_file) - debug_mode = os.getenv('DEBUG_MODE', config.get( - 'debug_mode', 'False')).strip().lower() + debug_mode = os.getenv('DEBUG_MODE', config.get('debug_mode', 'False')).strip().lower() if debug_mode == "false": debug_mode = False - # Inicializar logger + else: + debug_mode = True + + # Initialize logger for this class self.logger = logging.getLogger(self.__class__.__name__) if debug_mode: self.logger.setLevel(logging.DEBUG) else: self.logger.setLevel(logging.WARNING) + # Set logging level for urllib based on debug_mode urllib_logger = logging.getLogger("urllib3") if not debug_mode: urllib_logger.setLevel(logging.WARNING) @@ -73,53 +76,45 @@ class CAPIFProviderConnector: urllib_logger.setLevel(logging.DEBUG) try: - + # Retrieve provider configuration from JSON or environment variables + provider_config = config.get('provider', {}) provider_general_folder = os.path.abspath( - os.getenv('PROVIDER_FOLDER', config.get('provider_folder', '')).strip()) - capif_host = os.getenv( - 'CAPIF_HOST', config.get('capif_host', '')).strip() - capif_register_host = os.getenv( - 'REGISTER_HOST', config.get('register_host', '')).strip() - capif_https_port = str( - os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) - capif_register_port = str(os.getenv( - 'CAPIF_REGISTER_PORT', config.get('capif_register_port', '')).strip()) - capif_provider_username = os.getenv( - 'CAPIF_USERNAME', config.get('capif_username', '')).strip() - capif_provider_password = os.getenv( - 'CAPIF_PASSWORD', config.get('capif_password', '')).strip() - - csr_common_name = os.getenv( - 'CSR_COMMON_NAME', config.get('csr_common_name', '')).strip() - csr_organizational_unit = os.getenv( - 'CSR_ORGANIZATIONAL_UNIT', config.get('csr_organizational_unit', '')).strip() - csr_organization = os.getenv( - 'CSR_ORGANIZATION', config.get('csr_organization', '')).strip() - crs_locality = os.getenv( - 'CRS_LOCALITY', config.get('crs_locality', '')).strip() - csr_state_or_province_name = os.getenv('CSR_STATE_OR_PROVINCE_NAME', config.get( - 'csr_state_or_province_name', '')).strip() - csr_country_name = os.getenv( - 'CSR_COUNTRY_NAME', config.get('csr_country_name', '')).strip() - csr_email_address = os.getenv( - 'CSR_EMAIL_ADDRESS', config.get('csr_email_address', '')).strip() - apfs = os.getenv('APFS', config.get('apfs', '')).strip() - aefs = os.getenv('AEFS', config.get('aefs', '')).strip() - api_description_path = os.path.abspath(os.getenv( - 'API_DESCRIPTION_PATH', config.get('api_description_path', '')).strip()) - + os.getenv('PROVIDER_FOLDER', provider_config.get('provider_folder', '')).strip()) + + capif_host = os.getenv('CAPIF_HOST', config.get('capif_host', '')).strip() + capif_register_host = os.getenv('REGISTER_HOST', config.get('register_host', '')).strip() + capif_https_port = str(os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) + capif_register_port = str(os.getenv('CAPIF_REGISTER_PORT', config.get('capif_register_port', '')).strip()) + capif_provider_username = os.getenv('CAPIF_USERNAME', config.get('capif_username', '')).strip() + capif_provider_password = os.getenv('CAPIF_PASSWORD', config.get('capif_password', '')).strip() + + # Get CSR (Certificate Signing Request) details from config or environment variables + cert_generation = provider_config.get('cert_generation', {}) + csr_common_name = os.getenv('PROVIDER_CSR_COMMON_NAME', cert_generation.get('csr_common_name', '')).strip() + csr_organizational_unit = os.getenv('PROVIDER_CSR_ORGANIZATIONAL_UNIT', cert_generation.get('csr_organizational_unit', '')).strip() + csr_organization = os.getenv('PROVIDER_CSR_ORGANIZATION', cert_generation.get('csr_organization', '')).strip() + crs_locality = os.getenv('PROVIDER_CRS_LOCALITY', cert_generation.get('crs_locality', '')).strip() + csr_state_or_province_name = os.getenv('PROVIDER_CSR_STATE_OR_PROVINCE_NAME', cert_generation.get('csr_state_or_province_name', '')).strip() + csr_country_name = os.getenv('PROVIDER_CSR_COUNTRY_NAME', cert_generation.get('csr_country_name', '')).strip() + csr_email_address = os.getenv('PROVIDER_CSR_EMAIL_ADDRESS', cert_generation.get('csr_email_address', '')).strip() + + # Retrieve provider specific values (APFs, AEFs) + apfs = os.getenv('APFS', provider_config.get('apfs', '')).strip() + aefs = os.getenv('AEFS', provider_config.get('aefs', '')).strip() + api_description_path = os.path.abspath(os.getenv('API_DESCRIPTION_PATH', provider_config.get('api_description_path', '')).strip()) + + # Check required fields and log warnings/errors if not capif_host: - self.logger.warning( - "CAPIF_HOST is not provided; defaulting to an empty string") + self.logger.warning("CAPIF_HOST is not provided; defaulting to an empty string") if not capif_provider_username: - self.logger.error( - "CAPIF_PROVIDER_USERNAME is required but not provided") + self.logger.error("CAPIF_PROVIDER_USERNAME is required but not provided") raise ValueError("CAPIF_PROVIDER_USERNAME is required") - self.provider_folder = os.path.join( - provider_general_folder, capif_provider_username) + # Setup the folder to store provider files (e.g., certificates) + self.provider_folder = os.path.join(provider_general_folder, capif_provider_username) os.makedirs(self.provider_folder, exist_ok=True) + # Set attributes for provider credentials and configuration self.capif_host = capif_host.strip() self.capif_provider_username = capif_provider_username self.capif_provider_password = capif_provider_password @@ -134,31 +129,40 @@ class CAPIFProviderConnector: self.csr_email_address = csr_email_address self.aefs = int(aefs) self.apfs = int(apfs) - config = config["publish_req"] + + # Get publish request details from config or environment variables + publish_req_config = provider_config.get('publish_req', {}) self.publish_req = { - "service_api_id": os.getenv('PUBLISH_REQ_SERVICE_API_ID', config.get('service_api_id', '')).strip(), - "publisher_apf_id": os.getenv('PUBLISH_REQ_PUBLISHER_APF_ID', config.get('publisher_apf_id', '')).strip(), - "publisher_aefs_ids": os.getenv('PUBLISH_REQ_PUBLISHER_AEFS_IDS', config.get('publisher_aefs_ids', '')) + "service_api_id": os.getenv('PUBLISH_REQ_SERVICE_API_ID', publish_req_config.get('service_api_id', '')).strip(), + "publisher_apf_id": os.getenv('PUBLISH_REQ_PUBLISHER_APF_ID', publish_req_config.get('publisher_apf_id', '')).strip(), + "publisher_aefs_ids": os.getenv('PUBLISH_REQ_PUBLISHER_AEFS_IDS', publish_req_config.get('publisher_aefs_ids', '')) } + # Set the path for the API description file self.api_description_path = api_description_path + # Set the CAPIF HTTPS port and construct CAPIF URLs self.capif_https_port = str(capif_https_port) + self.api_prov_funcs = {} + + # Construct the CAPIF HTTPS URL if len(self.capif_https_port) == 0 or int(self.capif_https_port) == 443: self.capif_https_url = f"https://{capif_host.strip()}/" else: self.capif_https_url = f"https://{capif_host.strip()}:{self.capif_https_port.strip()}/" + # Construct the CAPIF register URL if len(capif_register_port) == 0: self.capif_register_url = f"https://{capif_register_host.strip()}:8084/" else: self.capif_register_url = f"https://{capif_register_host.strip()}:{capif_register_port.strip()}/" - self.logger.info( - "CAPIFProviderConnector initialized with the capif-sdk-config.json parameters") + # Log initialization success message + self.logger.info("capif_provider_connector initialized with the capif_sdk_config.json parameters") except Exception as e: + # Catch and log any exceptions that occur during initialization self.logger.error(f"Error during initialization: {e}") raise @@ -170,7 +174,7 @@ class CAPIFProviderConnector: cmd = f"openssl s_client -connect {self.capif_host}:{self.capif_https_port} | openssl x509 -text > {self.provider_folder}/capif_cert_server.pem" try: - # Redirige la salida estándar y de errores a os.devnull para ocultar los logs + # Redirects standard output and error to os.devnull to hide logs with open(os.devnull, 'w') as devnull: subprocess.run(cmd, shell=True, check=True, stdout=devnull, stderr=devnull) @@ -241,14 +245,14 @@ class CAPIFProviderConnector: def __onboard_exposer_to_capif(self, access_token, capif_onboarding_url): self.logger.info( "Onboarding Provider to CAPIF and waiting signed certificate by giving our public keys to CAPIF") - + url = f"{self.capif_https_url}{capif_onboarding_url}" headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } - # Crear la lista de roles sin indexar + # Create the list of roles without indexing roles = ["AMF"] for n in range(1, self.aefs + 1): roles.append("AEF") @@ -256,9 +260,9 @@ class CAPIFProviderConnector: for n in range(1, self.apfs + 1): roles.append("APF") - # Construir el payload con los roles sin indexar + # Build the payload with the non-indexed roles payload = { - "apiProvFuncs": [ + "api_prov_funcs": [ {"regInfo": {"apiProvPubKey": ""}, "apiProvFuncRole": role, "apiProvFuncInfo": f"{role.lower()}"} for role in roles @@ -269,7 +273,7 @@ class CAPIFProviderConnector: "regSec": access_token, } - # Generar los roles indexados para la creación de certificados + # Generate the indexed roles for certificate creation indexedroles = ["AMF"] for n in range(1, self.aefs + 1): indexedroles.append(f"AEF-{n}") @@ -277,12 +281,12 @@ class CAPIFProviderConnector: for n in range(1, self.apfs + 1): indexedroles.append(f"APF-{n}") - # Guardar las claves públicas y generar los certificados con roles indexados - for i, api_func in enumerate(payload["apiProvFuncs"]): - # Generar las claves públicas con el rol indexado, pero no actualizar el payload con el rol indexado + # Save the public keys and generate certificates with indexed roles + for i, api_func in enumerate(payload["api_prov_funcs"]): + # Generate public keys with the indexed role, but do not update the payload with the indexed role public_key = self.__create_private_and_public_keys(indexedroles[i]) - # Asignar la clave pública al payload + # Assign the public key to the payload api_func["regInfo"]["apiProvPubKey"] = public_key.decode("utf-8") try: @@ -304,7 +308,7 @@ class CAPIFProviderConnector: def __write_to_file(self, onboarding_response, capif_registration_id, publish_url): self.logger.info("Saving the most relevant onboarding data") - # Generar los roles indexados para la correspondencia + # Generate the indexed roles for correspondence indexedroles = ["AMF"] for n in range(1, self.aefs + 1): indexedroles.append(f"AEF-{n}") @@ -312,24 +316,26 @@ class CAPIFProviderConnector: for n in range(1, self.apfs + 1): indexedroles.append(f"APF-{n}") - # Guardar los certificados con los nombres indexados - for i, func_profile in enumerate(onboarding_response["apiProvFuncs"]): + # Save the certificates with the indexed names + for i, func_profile in enumerate(onboarding_response["api_prov_funcs"]): role = indexedroles[i].lower() cert_path = os.path.join(self.provider_folder, f"{role}.crt") with open(cert_path, "wb") as cert_file: cert_file.write( func_profile["regInfo"]["apiProvCert"].encode("utf-8")) - # Guardar los detalles del proveedor + # Save the provider details provider_details_path = os.path.join( self.provider_folder, "capif_provider_details.json") with open(provider_details_path, "w") as outfile: data = { "capif_registration_id": capif_registration_id, "publish_url": publish_url, - **{f"{indexedroles[i]}_api_prov_func_id": api_prov_func["apiProvFuncId"] - for i, api_prov_func in enumerate(onboarding_response["apiProvFuncs"])} + **{f"{indexedroles[i]}": api_prov_func["apiProvFuncId"] + for i, api_prov_func in enumerate(onboarding_response["api_prov_funcs"])} } + for i, api_prov_func in enumerate(onboarding_response["api_prov_funcs"]): + self.api_prov_funcs[indexedroles[i]] = api_prov_func["apiProvFuncId"] json.dump(data, outfile, indent=4) self.logger.info("Data saved") @@ -421,7 +427,7 @@ class CAPIFProviderConnector: for key, value in provider_details.items(): if value == APF_api_prov_func_id and key.startswith("APF-"): apf_inter = key.split("-")[1] - # Obtener el número del APF + # Obtain the APF number apf_number = apf_inter.split("_")[0] break @@ -430,7 +436,7 @@ class CAPIFProviderConnector: f"No matching APF found for publisher_apf_id: {APF_api_prov_func_id}") raise ValueError("Invalid publisher_apf_id") service_api_description_json_full_path = self.api_description_path - # Leer y modificar la descripción de la API de servicio + # Read and modify the API description self.logger.info( f"Reading and modifying service API description from {service_api_description_json_full_path}") @@ -438,21 +444,21 @@ class CAPIFProviderConnector: with open(service_api_description_json_full_path, "r") as service_file: data = json.load(service_file) - # Verificamos que el número de AEFs coincide con el número de perfiles + # Verifying that the number of AEFs is equal to the aefProfiles if len(AEFs_list) != len(data.get("aefProfiles", [])): self.logger.error( "The number of AEFs in publisher_aefs_ids does not match the number of profiles in aefProfiles") raise ValueError( "Mismatch between number of AEFs and profiles") - # Asignamos los AEFs correspondientes + # Assigning each AEF for profile, aef_id in zip(data.get("aefProfiles", []), AEFs_list): profile["aefId"] = aef_id self.logger.info( "Service API description modified successfully") - # Guardamos los cambios en el archivo + # Saving changes into the file with open(service_api_description_json_full_path, "w") as service_file: json.dump(data, service_file, indent=4) @@ -498,7 +504,7 @@ class CAPIFProviderConnector: file_name = capif_response_json.get("apiName", "default_name") id = capif_response_json.get("apiId", "default_id") output_path = os.path.join( - self.provider_folder, f"CAPIF-{file_name}-{id}-api.json") + self.provider_folder, f"capif_{file_name}_{id}_api.json") with open(output_path, "w") as outfile: outfile.write(capif_response_text) @@ -506,16 +512,17 @@ class CAPIFProviderConnector: output_path = os.path.join( self.provider_folder, "published-apis.json") - # Leer el archivo existente de APIs publicados + # Read the existing file of published APIs published_apis = {} if os.path.exists(output_path): with open(output_path, "r") as outfile: published_apis = json.load(outfile) - # Agregar el nuevo API publicado + # Add the newly published API + published_apis[file_name] = id - # Escribir el archivo actualizado de APIs publicados + # Write the updated file of published APIs with open(output_path, "w") as outfile: json.dump(published_apis, outfile, indent=4) self.logger.info( @@ -557,7 +564,7 @@ class CAPIFProviderConnector: for key, value in provider_details.items(): if value == APF_api_prov_func_id and key.startswith("APF-"): apf_inter = key.split("-")[1] - # Obtener el número del APF + # Get the number of APFs apf_number = apf_inter.split("_")[0] break @@ -595,28 +602,28 @@ class CAPIFProviderConnector: for filename in os.listdir(directory): path = os.path.join(directory, filename) - # Verificar si el archivo empieza con 'CAPIF-' + # Check if the file starts with 'CAPIF-' if filename.startswith("CAPIF-") and publish["service_api_id"] in filename: - # Salir del bucle si el archivo es eliminado + # Exit the loop if the file is deleted os.remove(path) break output_path = os.path.join( self.provider_folder, "published-apis.json") - # Leer el archivo existente de APIs publicados + # Read the existing file of published APIs published_apis = {} if os.path.exists(output_path): with open(output_path, "r") as outfile: published_apis = json.load(outfile) - # ID del API que deseas eliminar - # Reemplaza con el ID especÃfico + # API ID you want to delete + # Replace with the specific ID api_id_to_delete = publish["service_api_id"] - # Buscar y eliminar el API por su ID + # Search and delete the API by its ID api_name_to_delete = None for name, id in published_apis.items(): if id == api_id_to_delete: @@ -631,7 +638,7 @@ class CAPIFProviderConnector: self.logger.warning( f"API with ID '{api_id_to_delete}' not found in Published Apis.") - # Escribir el archivo actualizado de APIs publicados + # Write the updated file of published APIs with open(output_path, "w") as outfile: json.dump(published_apis, outfile, indent=4) @@ -674,7 +681,7 @@ class CAPIFProviderConnector: for key, value in provider_details.items(): if value == APF_api_prov_func_id and key.startswith("APF-"): apf_inter = key.split("-")[1] - # Obtener el número del APF + # Get the number of apfs apf_number = apf_inter.split("_")[0] break @@ -746,7 +753,7 @@ class CAPIFProviderConnector: for key, value in provider_details.items(): if value == APF_api_prov_func_id and key.startswith("APF-"): apf_inter = key.split("-")[1] - # Obtener el número del APF + # Get the number of APFs apf_number = apf_inter.split("_")[0] break @@ -755,7 +762,7 @@ class CAPIFProviderConnector: f"No matching APF found for publisher_apf_id: {APF_api_prov_func_id}") raise ValueError("Invalid publisher_apf_id") - # Leer y modificar la descripción de la API de servicio + # Read and modify the description of the API services # Publish services url = f"{self.capif_https_url}{publish_url.replace('<apfId>', APF_api_prov_func_id)}" @@ -823,7 +830,7 @@ class CAPIFProviderConnector: for key, value in provider_details.items(): if value == APF_api_prov_func_id and key.startswith("APF-"): apf_inter = key.split("-")[1] - # Obtener el número del APF + # Get the number of APFs apf_number = apf_inter.split("_")[0] break @@ -833,7 +840,7 @@ class CAPIFProviderConnector: raise ValueError("Invalid publisher_apf_id") service_api_description_json_full_path = self.api_description_path - # Leer y modificar la descripción de la API de servicio + # Read and modify the description of the API services self.logger.info( f"Reading and modifying service API description from {service_api_description_json_full_path}") @@ -841,21 +848,21 @@ class CAPIFProviderConnector: with open(service_api_description_json_full_path, "r") as service_file: data = json.load(service_file) - # Verificamos que el número de AEFs coincide con el número de perfiles + # verify the aefs number corresponds to the aefProfiles if len(AEFs_list) != len(data.get("aefProfiles", [])): self.logger.error( "The number of AEFs in publisher_aefs_ids does not match the number of profiles in aefProfiles") raise ValueError( "Mismatch between number of AEFs and profiles") - # Asignamos los AEFs correspondientes + # Asing the chosen AEFs for profile, aef_id in zip(data.get("aefProfiles", []), AEFs_list): profile["aefId"] = aef_id self.logger.info( "Service API description modified successfully") - # Guardamos los cambios en el archivo + # Save changes with open(service_api_description_json_full_path, "w") as service_file: json.dump(data, service_file, indent=4) @@ -902,20 +909,20 @@ class CAPIFProviderConnector: id = capif_response_json.get("apiId", "default_id") directory = self.provider_folder - # Iterar sobre todos los archivos en el directorio + # Iterate over all files in the directory for filename in os.listdir(directory): path = os.path.join(directory, filename) - # Verificar si el archivo empieza con 'CAPIF-' + # Check if the file starts with 'CAPIF-' if filename.startswith("CAPIF-") and id in filename: - # Salir del bucle si el archivo es eliminado + # Exit the loop if the file is deleted os.remove(path) break output_path = os.path.join( - self.provider_folder, f"CAPIF-{file_name}-{id}-api.json") + self.provider_folder, f"capif_{file_name}_{id}_api.json") with open(output_path, "w") as outfile: outfile.write(capif_response_text) @@ -923,7 +930,7 @@ class CAPIFProviderConnector: output_path = os.path.join( self.provider_folder, "published-apis.json") - # Leer el archivo existente de APIs publicados + # Read the existing file of published APIs published_apis = {} if os.path.exists(output_path): with open(output_path, "r") as outfile: @@ -933,10 +940,10 @@ class CAPIFProviderConnector: value in published_apis.items() if value == id] for key in keys_to_remove: del published_apis[key] - # Agregar el nuevo API publicado + # Add the new id of the published API published_apis[file_name] = id - # Escribir el archivo actualizado de APIs publicados + # Update the file with the published APIs with open(output_path, "w") as outfile: json.dump(published_apis, outfile, indent=4) self.logger.info( @@ -1007,7 +1014,7 @@ class CAPIFProviderConnector: folder_path = self.provider_folder if os.path.exists(folder_path): - # Elimina todo el contenido dentro de la carpeta, incluyendo archivos y subcarpetas + # Deletes all content within the folder, including files and subfolders for root, dirs, files in os.walk(folder_path): for file in files: os.remove(os.path.join(root, file)) @@ -1070,20 +1077,21 @@ class CAPIFProviderConnector: apf_count = 0 aef_count = 0 - # Itera sobre las claves del diccionario (los campos del JSON) + # Iterate over the dictionary keys (the fields of the JSON) + for key in api_details.keys(): if key.startswith("APF"): apf_count += 1 elif key.startswith("AEF"): aef_count += 1 - # Loggea los resultados (o los retorna, depende de lo que necesites) + # Log the results (or return them, depending on what you need) self.logger.info(f"Total APFs: {apf_count}, Total AEFs: {aef_count}") APFscertstoremove = 0 APFscertstoadd = 0 AEFscertstoremove = 0 AEFscertstoadd = 0 - # Calcula la diferencia de APFs + # Calculate the difference of APFs if apf_count != self.apfs: diff = apf_count - self.apfs if diff < 0: @@ -1094,7 +1102,7 @@ class CAPIFProviderConnector: APFscertstoremove = 0 APFscertstoadd = 0 - # Calcula la diferencia de AEFs + # Calculate the difference of AEFs if aef_count != self.aefs: diff = aef_count - self.aefs if diff < 0: @@ -1105,7 +1113,7 @@ class CAPIFProviderConnector: AEFscertstoremove = 0 AEFscertstoadd = 0 - # Eliminar ficheros APF en orden descendente si hay más APFs de los que deberÃa haber + # Remove APF files in descending order if there are more APFs than there should be if APFscertstoremove: while apf_count > self.apfs: # List files starting with "APF-" or "apf-" in the directory @@ -1163,7 +1171,7 @@ class CAPIFProviderConnector: "Content-Type": "application/json", } - # Crear la lista de roles sin indexar + # Create the list of unindexed roles roles = ["AMF"] for n in range(1, self.aefs + 1): roles.append("AEF") @@ -1171,9 +1179,9 @@ class CAPIFProviderConnector: for n in range(1, self.apfs + 1): roles.append("APF") - # Construir el payload con los roles sin indexar + # Build the payload with unindexed roles payload = { - "apiProvFuncs": [ + "api_prov_funcs": [ {"regInfo": {"apiProvPubKey": ""}, "apiProvFuncRole": role, "apiProvFuncInfo": f"{role.lower()}"} for role in roles @@ -1184,29 +1192,29 @@ class CAPIFProviderConnector: "regSec": access_token, } - # Generar los roles indexados para la creación de certificados + # Generate the indexed roles for certificate creation indexed_roles = ["AMF"] + [f"AEF-{n}" for n in range(1, self.aefs + 1)] + [ f"APF-{n}" for n in range(1, self.apfs + 1)] - # Recorrer cada función de proveedor de API - for i, api_func in enumerate(payload["apiProvFuncs"]): - # Ruta de la carpeta de proveedores + # Iterate over each API provider function + for i, api_func in enumerate(payload["api_prov_funcs"]): + # Folder path for providers folder_path = self.provider_folder - # Verificar si la carpeta existe + # Check if the folder exists if os.path.exists(folder_path): - found_key = False # Variable para controlar si ya se encontró una clave pública + found_key = False # Variable to control if a public key has already been found - # Recorrer los archivos en la carpeta + # Iterate over the files in the folder for root, dirs, files in os.walk(folder_path): for file_name in files: if file_name.endswith(".csr"): - # Verificar si el archivo comienza con el rol esperado + # Check if the file starts with the expected role role_prefix = indexed_roles[i] if any(file_name.startswith(prefix) and role_prefix == prefix for prefix in [f"APF-{i+1}", f"AEF-{i+1}", "AMF"]): file_path = os.path.join(root, file_name) - # Leer la clave pública del archivo + # Read the public key from the file with open(file_path, "r") as csr_file: api_func["regInfo"]["apiProvPubKey"] = csr_file.read( ) @@ -1217,7 +1225,7 @@ class CAPIFProviderConnector: if found_key: break - # Si no se encuentra un archivo con la clave pública, generar una nueva clave + # If a file with the public key is not found, generate a new key if not found_key: public_key = self.__create_private_and_public_keys( diff --git a/sdk/sdk.py b/sdk/sdk.py index d10db5d959385eea4a67c503d79181ea7c3dc37c..69af626873e166cab2783beb31f877d19989b0bd 100644 --- a/sdk/sdk.py +++ b/sdk/sdk.py @@ -1,3 +1,3 @@ -from CAPIFInvokerConnector import CAPIFInvokerConnector -from CAPIFProviderConnector import CAPIFProviderConnector -from ServiceDiscoverer import ServiceDiscoverer \ No newline at end of file +from capif_invoker_connector import capif_invoker_connector +from capif_provider_connector import capif_provider_connector +from service_discoverer import service_discoverer \ No newline at end of file diff --git a/sdk/ServiceDiscoverer.py b/sdk/service_discoverer.py similarity index 82% rename from sdk/ServiceDiscoverer.py rename to sdk/service_discoverer.py index f138221a0ea91897ea765f15d867a177b0d4abcd..1df24ccb15dc2ffe59f0ff2604c04104ff7dbbb2 100644 --- a/sdk/ServiceDiscoverer.py +++ b/sdk/service_discoverer.py @@ -11,7 +11,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) warnings.filterwarnings("ignore", category=RequestsDependencyWarning) -# Configuración básica del logger +# Basic logger configuration log_path = 'logs/sdk_logs.log' @@ -21,17 +21,18 @@ if not os.path.exists(log_dir): os.makedirs(log_dir) logging.basicConfig( - level=logging.NOTSET, # Nivel mÃnimo de severidad a registrar - # Formato del mensaje de log + level=logging.NOTSET, # Minimum severity level to log + # Log message format format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ - logging.FileHandler(log_path), # Registra en un archivo - logging.StreamHandler() # También muestra en la consola + logging.FileHandler(log_path), # Logs to a file + logging.StreamHandler() # Also outputs to the console ] ) -class ServiceDiscoverer: + +class service_discoverer: class ServiceDiscovererException(Exception): pass @@ -39,70 +40,81 @@ class ServiceDiscoverer: self, config_file ): - # Cargar configuración desde archivo si es necesario + # Load configuration from file if necessary config_file = os.path.abspath(config_file) config = self.__load_config_file(config_file) - debug_mode = os.getenv('DEBUG_MODE', config.get( - 'debug_mode', 'False')).strip().lower() + debug_mode = os.getenv('DEBUG_MODE', config.get('debug_mode', 'False')).strip().lower() if debug_mode == "false": debug_mode = False + else: + debug_mode = True - # Inicializar logger + # Initialize logger for this class self.logger = logging.getLogger(self.__class__.__name__) if debug_mode: self.logger.setLevel(logging.DEBUG) else: self.logger.setLevel(logging.WARNING) + # Set logging level for urllib based on debug_mode urllib_logger = logging.getLogger("urllib3") if not debug_mode: urllib_logger.setLevel(logging.WARNING) else: urllib_logger.setLevel(logging.DEBUG) - self.config_path = os.path.dirname(config_file)+"/" - capif_host = os.getenv( - 'CAPIF_HOST', config.get('capif_host', '')).strip() - capif_https_port = str( - os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) + # Configuration path to store files + self.config_path = os.path.dirname(os.path.abspath(config_file)) + "/" + + # Retrieve host and port information from environment variables or config + capif_host = os.getenv('CAPIF_HOST', config.get('capif_host', '')).strip() + capif_https_port = str(os.getenv('CAPIF_HTTPS_PORT', config.get('capif_https_port', '')).strip()) + + # Get the folder for storing invoker certificates from environment or config + invoker_config = config.get('invoker', {}) invoker_general_folder = os.path.abspath( - os.getenv('invoker_folder', config.get('invoker_folder', '')).strip()) + os.getenv('invoker_folder', invoker_config.get('invoker_folder', '')).strip() + ) - capif_invoker_username = os.getenv( - 'CAPIF_USERNAME', config.get('capif_username', '')).strip() + # Retrieve CAPIF invoker username + capif_invoker_username = os.getenv('CAPIF_USERNAME', config.get('capif_username', '')).strip() - config = config["discover_filter"] + # Extract discover filter configuration from JSON or environment variables + discover_filter_config = invoker_config.get('discover_filter', {}) self.discover_filter = { - "api-name": os.getenv('DISCOVER_FILTER_API_NAME', config.get('api-name', '')).strip(), - "api-version": os.getenv('DISCOVER_FILTER_API_VERSION', config.get('api-version', '')).strip(), - "comm-type": os.getenv('DISCOVER_FILTER_COMM_TYPE', config.get('comm-type', '')).strip(), - "protocol": os.getenv('DISCOVER_FILTER_PROTOCOL', config.get('protocol', '')).strip(), - "aef-id": os.getenv('DISCOVER_FILTER_AEF_ID', config.get('aef-id', '')).strip(), - "data-format": os.getenv('DISCOVER_FILTER_DATA_FORMAT', config.get('data-format', '')).strip(), - "api-cat": os.getenv('DISCOVER_FILTER_API_CAT', config.get('api-cat', '')).strip(), - "preferred-aef-loc": os.getenv('DISCOVER_FILTER_PREFERRED_AEF_LOC', config.get('preferred-aef-loc', '')).strip(), - "req-api-prov-name": os.getenv('DISCOVER_FILTER_REQ_API_PROV_NAME', config.get('req-api-prov-name', '')).strip(), - "supported-features": os.getenv('DISCOVER_FILTER_SUPPORTED_FEATURES', config.get('supported-features', '')).strip(), - "api-supported-features": os.getenv('DISCOVER_FILTER_API_SUPPORTED_FEATURES', config.get('api-supported-features', '')).strip(), - "ue-ip-addr": os.getenv('DISCOVER_FILTER_UE_IP_ADDR', config.get('ue-ip-addr', '')).strip(), - "service-kpis": os.getenv('DISCOVER_FILTER_SERVICE_KPIS', config.get('service-kpis', '')).strip() + "api-name": os.getenv('DISCOVER_FILTER_API_NAME', discover_filter_config.get('api-name', '')).strip(), + "api-version": os.getenv('DISCOVER_FILTER_API_VERSION', discover_filter_config.get('api-version', '')).strip(), + "comm-type": os.getenv('DISCOVER_FILTER_COMM_TYPE', discover_filter_config.get('comm-type', '')).strip(), + "protocol": os.getenv('DISCOVER_FILTER_PROTOCOL', discover_filter_config.get('protocol', '')).strip(), + "aef-id": os.getenv('DISCOVER_FILTER_AEF_ID', discover_filter_config.get('aef-id', '')).strip(), + "data-format": os.getenv('DISCOVER_FILTER_DATA_FORMAT', discover_filter_config.get('data-format', '')).strip(), + "api-cat": os.getenv('DISCOVER_FILTER_API_CAT', discover_filter_config.get('api-cat', '')).strip(), + "preferred-aef-loc": os.getenv('DISCOVER_FILTER_PREFERRED_AEF_LOC', discover_filter_config.get('preferred-aef-loc', '')).strip(), + "req-api-prov-name": os.getenv('DISCOVER_FILTER_REQ_API_PROV_NAME', discover_filter_config.get('req-api-prov-name', '')).strip(), + "supported-features": os.getenv('DISCOVER_FILTER_SUPPORTED_FEATURES', discover_filter_config.get('supported-features', '')).strip(), + "api-supported-features": os.getenv('DISCOVER_FILTER_API_SUPPORTED_FEATURES', discover_filter_config.get('api-supported-features', '')).strip(), + "ue-ip-addr": os.getenv('DISCOVER_FILTER_UE_IP_ADDR', discover_filter_config.get('ue-ip-addr', '')).strip(), + "service-kpis": os.getenv('DISCOVER_FILTER_SERVICE_KPIS', discover_filter_config.get('service-kpis', '')).strip() } + + # Store important attributes for CAPIF invocation self.capif_invoker_username = capif_invoker_username self.capif_host = capif_host self.capif_https_port = capif_https_port - self.invoker_folder = os.path.join( - invoker_general_folder, capif_invoker_username - ) + + # Create invoker folder dynamically based on username and folder path + self.invoker_folder = os.path.join(invoker_general_folder, capif_invoker_username) os.makedirs(self.invoker_folder, exist_ok=True) + + # Load CAPIF API details self.capif_api_details = self.__load_provider_api_details() - self.signed_key_crt_path = os.path.join( - self.invoker_folder, self.capif_api_details["user_name"] + ".crt" - ) - self.private_key_path = os.path.join( - self.invoker_folder, "private.key") + # Define paths for certificates, private keys, and CA root + self.signed_key_crt_path = os.path.join(self.invoker_folder, self.capif_api_details["user_name"] + ".crt") + self.private_key_path = os.path.join(self.invoker_folder, "private.key") self.ca_root_path = os.path.join(self.invoker_folder, "ca.crt") + # Log initialization success message self.logger.info("ServiceDiscoverer initialized correctly") def get_api_provider_id(self): @@ -195,7 +207,7 @@ class ServiceDiscoverer: self.capif_api_details["registered_security_contexes"]) for i in range(0, number_of_apis): - # Obteniendo los valores de api_id y aef_id para cada API + # Obtaining the values of api_id and aef_id for each API api_id = self.capif_api_details["registered_security_contexes"][i]["api_id"] aef_id = self.capif_api_details["registered_security_contexes"][i]["aef_id"] @@ -255,7 +267,7 @@ class ServiceDiscoverer: self.capif_api_details["registered_security_contexes"]) for i in range(0, number_of_apis): - # Obteniendo los valores de api_id y aef_id para cada API + # Obtaining the values of api_id and aef_id for each API api_id = self.capif_api_details["registered_security_contexes"][i]["api_id"] aef_id = self.capif_api_details["registered_security_contexes"][i]["aef_id"] @@ -290,18 +302,19 @@ class ServiceDiscoverer: :return: El token de acceso (jwt) """ url = f"https://{self.capif_host}:{self.capif_https_port}/capif-security/v1/securities/{self.capif_api_details['api_invoker_id']}/token" - # Construir el scope concatenando aef_id y api_name separados por un ';' + # Build the scope by concatenating aef_id and api_name separated by a ';' scope_parts = [] - # Iterar sobre los contextos registrados y construir las partes del scope + # Iterate over the registered contexts and build the scope parts for context in self.capif_api_details["registered_security_contexes"]: aef_id = context["aef_id"] api_name = context["api_name"] scope_parts.append(f"{aef_id}:{api_name}") - # Unir todas las partes del scope con ';' y añadir el prefijo '3gpp#' + # Join all the scope parts with ';' and add the prefix '3gpp#' scope = "3gpp#" + ";".join(scope_parts) + payload = { "grant_type": "client_credentials", "client_id": self.capif_api_details["api_invoker_id"], @@ -334,14 +347,14 @@ class ServiceDiscoverer: Descubre los APIs de servicio desde CAPIF con filtros basados en un archivo JSON. :return: Payload JSON con los detalles de los APIs de servicio """ - # Cargar los parámetros desde el archivo JSON + # Load the parameters from the JSON file - # Filtrar parámetros que no sean vacÃos " + # Filter out parameters that are not empty filters = self.discover_filter query_params = {k: v for k, v in filters.items() if v.strip()} - # Formar la URL con los parámetros de query + # Form the URL with the query parameters query_string = "&".join([f"{k}={v}" for k, v in query_params.items()]) url = f"https://{self.capif_host}:{self.capif_https_port}/{self.capif_api_details['discover_services_url']}{self.capif_api_details['api_invoker_id']}" diff --git a/test/capif-sdk-config-sample-test.json b/test/capif-sdk-config-sample-test.json deleted file mode 100644 index 4638d8d031fb4dca65a0cc5be36462ec1c0aeb4f..0000000000000000000000000000000000000000 --- a/test/capif-sdk-config-sample-test.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", - "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", - "capif_host": "capif-prev.mobilesandbox.cloud", - "register_host": "registercapif-prev.mobilesandbox.cloud", - "capif_https_port": "36212", - "capif_register_port": "36211", - "capif_callback_url": "http://localhost:5000", - "csr_common_name": "test03", - "csr_organizational_unit": "test_app_ou", - "csr_organization": "test_app_o", - "crs_locality": "Madrid", - "csr_state_or_province_name": "Madrid", - "csr_country_name": "ES", - "csr_email_address": "test@example.com", - "capif_username": "echeva_0", - "capif_password": "echevapass", - "apfs": "1", - "aefs": "2", - "debug_mode": "False", - "discover_filter": { - "api-name": "Test", - "api-version": "", - "comm-type": "", - "protocol": "", - "aef-id": "", - "data-format": "", - "api-cat": "", - "preferred-aef-loc": "", - "req-api-prov-name": "", - "supported-features": "", - "api-supported-features": "", - "ue-ip-addr": "", - "service-kpis": "" - }, - "publish_req": { - "service_api_id": "1381cc9e326427e620cef5ecc217fa", - "publisher_apf_id": "APF32363ae4d627d61f883c4299d6582e", - "publisher_aefs_ids": [ - "AEFf0b8a0bba7dcf56f26dd2b853eb193", - "AEFc06eab46b0c494c5be4785ed76dfc6" - ] - }, - "api_description_path": "./netapp-provider-api-spec-3.json" -} \ No newline at end of file diff --git a/test/capif_sdk_config_sample_test.json b/test/capif_sdk_config_sample_test.json new file mode 100644 index 0000000000000000000000000000000000000000..e8d60d07dbc59362c88e0806099798bd3f28c1a9 --- /dev/null +++ b/test/capif_sdk_config_sample_test.json @@ -0,0 +1,60 @@ +{ + "capif_host": "capif-prev.mobilesandbox.cloud", + "register_host": "registercapif-prev.mobilesandbox.cloud", + "capif_https_port": "36212", + "capif_register_port": "36211", + "capif_username": "echeva_0", + "capif_password": "echevapass", + "debug_mode": "False", + "invoker":{ + "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", + "capif_callback_url": "http://localhost:5000", + "cert_generation":{ + "csr_common_name": "invoker", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + } + }, + "provider":{ + "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", + "apfs": "1", + "aefs": "3", + "publish_req": { + "service_api_id": "80dbdd52ee766d4ad4494264e4289c", + "publisher_apf_id": "APF73e3458fb483c3c65f2f7e126ec851", + "publisher_aefs_ids": [ + "AEF07a01ccd74a160c195e69b4f116d66", + "AEFb5c206b46fc68c192aed6870899ea1" + ] + }, + "cert_generation":{ + "csr_common_name": "provider", + "csr_organizational_unit": "test_app_ou", + "csr_organization": "test_app_o", + "crs_locality": "Madrid", + "csr_state_or_province_name": "Madrid", + "csr_country_name": "ES", + "csr_email_address": "test@example.com" + }, + "api_description_path": "./network_app_provider_api_spec.json" + } +} diff --git a/test/netapp-provider-api-spec.json b/test/network_app_provider_api_spec.json similarity index 98% rename from test/netapp-provider-api-spec.json rename to test/network_app_provider_api_spec.json index 9d88178cc2db402fe13d793cdac21445694dc28f..163612bdf4af0f55647557b3504287811c6c0152 100755 --- a/test/netapp-provider-api-spec.json +++ b/test/network_app_provider_api_spec.json @@ -2,7 +2,7 @@ "apiName": "Test", "aefProfiles": [ { - "aefId": "AEFd7162eb09d54693f8fdcda7dd857a4", + "aefId": "AEF1b4324ee22d449147a840e436c4748", "versions": [ { "apiVersion": "v1", @@ -61,7 +61,7 @@ ] }, { - "aefId": "AEFa41006150a3432cc018acd82d0505d", + "aefId": "AEFb30ab3c6eafea9974488d878be5cdc", "versions": [ { "apiVersion": "v1", diff --git a/test/netapp-provider-api-spec-2.json b/test/network_app_provider_api_spec_2.json similarity index 97% rename from test/netapp-provider-api-spec-2.json rename to test/network_app_provider_api_spec_2.json index 09981177d35a8527825ec419479253d6cf67713e..6ed52756374881064defa77d89b1a0dd1b3694f1 100755 --- a/test/netapp-provider-api-spec-2.json +++ b/test/network_app_provider_api_spec_2.json @@ -1,8 +1,8 @@ { - "apiName": "Test-2", + "apiName": "Test-two", "aefProfiles": [ { - "aefId": "AEFf9911166dadd72d9f2305e9121776d", + "aefId": "AEFcd7d8f2b15438f5262b75ccc91d4d3", "versions": [ { "apiVersion": "v1", @@ -61,7 +61,7 @@ ] }, { - "aefId": "AEF85bef05b30b1e9d32c5b7cb6ec7d00", + "aefId": "AEF3aca25d4e4bec6dbf49a44fd5186a7", "versions": [ { "apiVersion": "v1", @@ -136,7 +136,7 @@ ] }, { - "aefId": "AEFc68bb090546dfc11d3986adbccbb12", + "aefId": "AEF5c6d2c497f106b7a2112ddb6f6fa3a", "versions": [ { "apiVersion": "v1", diff --git a/test/netapp-provider-api-spec-3.json b/test/network_app_provider_api_spec_3.json similarity index 97% rename from test/netapp-provider-api-spec-3.json rename to test/network_app_provider_api_spec_3.json index 4593dc9b4120d6df189c7ddf9629fda21ebbfab3..083a06e272575e10d2be80f7e54656bb1dca075d 100755 --- a/test/netapp-provider-api-spec-3.json +++ b/test/network_app_provider_api_spec_3.json @@ -1,8 +1,8 @@ { - "apiName": "Test-3", + "apiName": "Test-three", "aefProfiles": [ { - "aefId": "AEFf0b8a0bba7dcf56f26dd2b853eb193", + "aefId": "AEFc240eb73d1c72d6a311a26062c8ce7", "versions": [ { "apiVersion": "v1", @@ -61,7 +61,7 @@ ] }, { - "aefId": "AEFc06eab46b0c494c5be4785ed76dfc6", + "aefId": "AEF08305d3333a26129610ae465a08907", "versions": [ { "apiVersion": "v1", diff --git a/test/test.py b/test/test.py index c06d7ed611e3f1b6415a74b8482e43845a7c5baf..a7693944c0709e37d9d8b422c3b4cc914224053a 100644 --- a/test/test.py +++ b/test/test.py @@ -6,40 +6,34 @@ import json script_dir = os.path.dirname(os.path.abspath(__file__)) # Current script directory sdk_path = os.path.join(script_dir, '..', 'sdk') # Go up two levels and point to 'sdk' sys.path.insert(0, sdk_path) -from sdk import CAPIFProviderConnector, CAPIFInvokerConnector, ServiceDiscoverer +from sdk import capif_invoker_connector, capif_provider_connector, service_discoverer -capif_sdk_config_path = "./capif-sdk-config-sample-test.json" +capif_sdk_config_path = "./capif_sdk_config_sample_test.json" -def preparation_for_update(APFs, AEFs, second_netapp_api): - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) - config['apfs'] = APFs - config['aefs'] = AEFs - if second_netapp_api: - config['api_description_path'] = "./netapp-provider-api-spec-2.json" - else: - config['api_description_path'] = "./netapp-provider-api-spec-3.json" - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) +def preparation_for_update(APFs, AEFs, second_network_app_api,capif_provider_connector): - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) + capif_provider_connector.apfs = APFs + capif_provider_connector.aefs = AEFs + if second_network_app_api: + capif_provider_connector.api_description_path = "./network_app_provider_api_spec_2.json" + else: + capif_provider_connector.api_description_path = "./network_app_provider_api_spec_3.json" return capif_provider_connector -def ensure_update(Chosen_apf, Chosen_aefs, second_netapp_api): +def ensure_update(Chosen_apf, Chosen_aefs, second_network_app_api,capif_provider_connector): - if second_netapp_api: + if second_network_app_api: # Get AEFs ids and APFs ids to publish an API - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) + - provider_folder = config.get('provider_folder') - username_folder = config.get('capif_username') + provider_folder = capif_provider_connector.provider_folder + if not provider_folder: raise ValueError("'provider_folder' value is not defined in the configuration file.") - detailspath = os.path.join(provider_folder, username_folder, "capif_provider_details.json") + detailspath = os.path.join(provider_folder, "capif_provider_details.json") if not os.path.exists(detailspath): raise FileNotFoundError(f"File {detailspath} not found") @@ -55,71 +49,52 @@ def ensure_update(Chosen_apf, Chosen_aefs, second_netapp_api): raise ValueError("Not all necessary values were found in 'capif_provider_details.json'") # Update configuration file - config['publish_req']['publisher_apf_id'] = APF - config['publish_req']['publisher_aefs_ids'] = [AEF1, AEF2, AEF3] + capif_provider_connector.publish_req['publisher_apf_id'] = APF + capif_provider_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2,AEF3] - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) # Save the formatted JSON - print("Configuration file updated successfully.") else: - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) - provider_folder = config.get('provider_folder') - username_folder = config.get('capif_username') + provider_folder = capif_provider_connector.provider_folder + if not provider_folder: raise ValueError("'provider_folder' value is not defined in the configuration file.") - detailspath = os.path.join(provider_folder, username_folder, "capif_provider_details.json") + detailspath = os.path.join(provider_folder, "capif_provider_details.json") if not os.path.exists(detailspath): raise FileNotFoundError(f"File {detailspath} not found") with open(detailspath, 'r') as file: details = json.load(file) - APF = details.get('APF-1_api_prov_func_id') - AEF1 = details.get('AEF-1_api_prov_func_id') - AEF2 = details.get('AEF-2_api_prov_func_id') + APF = details.get('APF-1') + AEF1 = details.get('AEF-1') + AEF2 = details.get('AEF-2') if not APF or not AEF1 or not AEF2: raise ValueError("Not all necessary values were found in 'capif_provider_details.json'") # Update configuration file - config['publish_req']['publisher_apf_id'] = APF - config['publish_req']['publisher_aefs_ids'] = [AEF1, AEF2] - - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) # Save the formatted JSON + capif_provider_connector.publish_req['publisher_apf_id'] = APF + capif_provider_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2] - print("Configuration file updated successfully.") - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) - capif_provider_connector.publish_services() print("PROVIDER PUBLISH COMPLETED") - PublishedApis = os.path.join(provider_folder, username_folder, "published-Apis.json") + PublishedApis = os.path.join(provider_folder,"published-Apis.json") if not os.path.exists(PublishedApis): raise FileNotFoundError(f"File {PublishedApis} not found") with open(PublishedApis, 'r') as file: PublishedApis = json.load(file) - if second_netapp_api: - service_api_id = PublishedApis.get('Test-2') + if second_network_app_api: + service_api_id = PublishedApis.get('Test-two') else: - service_api_id = PublishedApis.get('Test-3') - - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) - - config['publish_req']['service_api_id'] = service_api_id + service_api_id = PublishedApis.get('Test-three') - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) # Save the formatted JSON - - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) + capif_provider_connector.publish_req['service_api_id'] = service_api_id capif_provider_connector.update_service() @@ -134,70 +109,50 @@ def ensure_update(Chosen_apf, Chosen_aefs, second_netapp_api): print("PROVIDER GET SERVICE COMPLETED") capif_provider_connector.unpublish_service() + + return capif_provider_connector if __name__ == "__main__": try: # Initialization of the connector - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) + capif_provider_connector = capif_provider_connector(config_file=capif_sdk_config_path) capif_provider_connector.onboard_provider() print("PROVIDER ONBOARDING COMPLETED") # Get AEFs ids and APFs ids to publish an API - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) - provider_folder = config.get('provider_folder') - username_folder = config.get('capif_username') - if not provider_folder: - raise ValueError("'provider_folder' value is not defined in the configuration file.") - - detailspath = os.path.join(provider_folder, username_folder, "capif_provider_details.json") + provider_folder = capif_provider_connector.provider_folder + + detailspath = os.path.join(provider_folder, "capif_provider_details.json") if not os.path.exists(detailspath): raise FileNotFoundError(f"File {detailspath} not found") with open(detailspath, 'r') as file: details = json.load(file) - APF = details.get('APF-1_api_prov_func_id') - AEF1 = details.get('AEF-1_api_prov_func_id') - AEF2 = details.get('AEF-2_api_prov_func_id') + APF = details.get('APF-1') + AEF1 = details.get('AEF-1') + AEF2 = details.get('AEF-2') if not APF or not AEF1 or not AEF2: raise ValueError("Not all necessary values were found in 'capif_provider_details.json'") # Update configuration file - config['publish_req']['publisher_apf_id'] = APF - config['publish_req']['publisher_aefs_ids'] = [AEF1, AEF2] - - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) # Save the formatted JSON - - print("Configuration file updated successfully.") - # Update the constructor with new configuration parameters - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) + capif_provider_connector.publish_req['publisher_apf_id'] = APF + capif_provider_connector.publish_req['publisher_aefs_ids'] = [AEF1, AEF2] capif_provider_connector.publish_services() print("PROVIDER PUBLISH COMPLETED") - PublishedApis = os.path.join(provider_folder, username_folder, "published-Apis.json") - if not os.path.exists(PublishedApis): - raise FileNotFoundError(f"File {PublishedApis} not found") - + PublishedApis = os.path.join(provider_folder,"published-Apis.json") + with open(PublishedApis, 'r') as file: PublishedApis = json.load(file) service_api_id = PublishedApis.get('Test') - with open(capif_sdk_config_path, 'r') as file: - config = json.load(file) - - config['publish_req']['service_api_id'] = service_api_id - - with open(capif_sdk_config_path, 'w') as file: - json.dump(config, file, indent=4) # Save the formatted JSON - - capif_provider_connector = CAPIFProviderConnector(config_file=capif_sdk_config_path) + capif_provider_connector.publish_req['service_api_id'] = service_api_id capif_provider_connector.update_service() @@ -211,12 +166,12 @@ if __name__ == "__main__": print("PROVIDER GET SERVICE COMPLETED") - capif_invoker_connector = CAPIFInvokerConnector(config_file=capif_sdk_config_path) + capif_invoker_connector = capif_invoker_connector(config_file=capif_sdk_config_path) capif_invoker_connector.onboard_invoker() print("INVOKER ONBOARDING COMPLETED") - discoverer = ServiceDiscoverer(config_file=capif_sdk_config_path) + discoverer = service_discoverer(config_file=capif_sdk_config_path) discoverer.discover() @@ -238,27 +193,27 @@ if __name__ == "__main__": print("PROVIDER UNPUBLISH SERVICE COMPLETED") - capif_provider_connector = preparation_for_update("2", "4", True) + capif_provider_connector = preparation_for_update(2, 4, True,capif_provider_connector) capif_provider_connector.update_provider() - Chosen_apf = "APF-2_api_prov_func_id" + Chosen_apf = "APF-2" - Chosen_aefs = ["AEF-1_api_prov_func_id", "AEF-3_api_prov_func_id", "AEF-4_api_prov_func_id"] + Chosen_aefs = ["AEF-1", "AEF-3", "AEF-4"] - ensure_update(Chosen_apf, Chosen_aefs, True) + capif_provider_connector = ensure_update(Chosen_apf, Chosen_aefs, True,capif_provider_connector) print("PROVIDER UPDATE ONE COMPLETED") - capif_provider_connector = preparation_for_update("1", "2", False) + capif_provider_connector = preparation_for_update(1, 2, False,capif_provider_connector) capif_provider_connector.update_provider() - Chosen_apf = "APF-1_api_prov_func_id" + Chosen_apf = "APF-1" - Chosen_aefs = ["AEF-1_api_prov_func_id", "AEF-2_api_prov_func_id"] + Chosen_aefs = ["AEF-1", "AEF-2"] - ensure_update(Chosen_apf, Chosen_aefs, False) + capif_provider_connector = ensure_update(Chosen_apf, Chosen_aefs, False,capif_provider_connector) print("PROVIDER UPDATE TWO COMPLETED")