diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ce488ba..931e6fd 100755 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,26 +7,6 @@ on: permissions: contents: read -# jobs: -# build: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout repository -# uses: actions/checkout@v3 -# - name: Install Node.js -# uses: actions/setup-node@v2 -# with: -# node-version: '16' -# - name: Install Antora -# run: npm i antora -# - name: Generate Site -# run: npx antora antora-playbook-local.yml -# - name: Publish to GitHub Pages -# uses: peaceiris/actions-gh-pages@v3 -# with: -# github_token: ${{ secrets.GITHUB_TOKEN }} -# publish_dir: ./public - jobs: # Build the documentation and upload the static HTML files as an artifact. build: diff --git a/CHANGELOG.md b/CHANGELOG.md index 40fc891..c3a0270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog + +## [0.0.13] 2023-12-1 + +### What's Changed + +* Fixed `foreign_source` model for processing `parameter`s. +* Updated `dao` and `model` module bases to allow importing within from the `pyonms` module +* Remove deprecated cloud portal module +* Remove deprecated Antora documentation + +**Full Changelog**: https://github.com/mmahacek/PyONMS/compare/v0.0.12...v0.0.13 + ## [0.0.12] 2023-11-30 ### What's Changed @@ -8,7 +20,6 @@ **Full Changelog**: https://github.com/mmahacek/PyONMS/compare/v0.0.11...v0.0.12 - ## [0.0.11] 2023-11-29 ### What's Changed diff --git a/antora-playbook-local.yml b/antora-playbook-local.yml deleted file mode 100755 index 209fdb1..0000000 --- a/antora-playbook-local.yml +++ /dev/null @@ -1,29 +0,0 @@ -site: - title: opennms.py -content: - sources: - - url: . - branches: HEAD - start_path: docs -ui: - bundle: - url: https://github.com/opennms-forge/antora-ui-opennms/releases/download/v3.1.0/ui-bundle.zip - supplemental_files: - - path: ui.yml - contents: | - static_files: - - .nojekyll - - index.html - - path: .nojekyll - - path: index.html - contents: | - -asciidoc: - attributes: - experimental: true -output: - clean: true - dir: ./public - destinations: - - provider: fs - - provider: archive diff --git a/docs/antora.yml b/docs/antora.yml deleted file mode 100755 index d3a6073..0000000 --- a/docs/antora.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: opennms.py -version: "0.0.12" -prerelease: true -title: OpenNMS.py -asciidoc: - attributes: - full-display-version: "0.0.12" - experimental: true - source-language: asciidoc@ - xrefstyle: short@ - python-version: 3.9 - repo-url: "https://github.com/mmahacek/PyONMS" -nav: - - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc deleted file mode 100755 index 0e26f64..0000000 --- a/docs/modules/ROOT/nav.adoc +++ /dev/null @@ -1,18 +0,0 @@ -.About -* xref:introduction.adoc[] - -.Using -* xref:using/introduction.adoc[] -* xref:using/connecting.adoc[] -* xref:using/support.adoc[] - -.Endpoints -* xref:endpoints/alarms.adoc[] -* xref:endpoints/business_services.adoc[] -* xref:endpoints/events.adoc[] -* xref:endpoints/foreign_sources.adoc[] -* xref:endpoints/nodes.adoc[] -* xref:endpoints/requisitions.adoc[] - -.Portal -* xref:portal/introduction.adoc[] diff --git a/docs/modules/ROOT/pages/endpoints/alarms.adoc b/docs/modules/ROOT/pages/endpoints/alarms.adoc deleted file mode 100644 index aa93404..0000000 --- a/docs/modules/ROOT/pages/endpoints/alarms.adoc +++ /dev/null @@ -1,69 +0,0 @@ - -# Alarms - -The Alarms endpoint provides read-only access to system alarm records. - -== Supported methods - -=== get_alarm() - -Get alarm by ID. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|ID of the alarm to retrieve. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|Alarm -|Alarm object -|=== - -=== get_alarms() - -Get all alarms in system. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|limit -|int -|100 -|Maximum number of alarms to return. -Set to `0` to get all alarms. - -|batch_size -|int -|100 -|Number of alarms to get per API call. -If batch size is less than the limit, multiple API calls will automatically be made until limit is reached. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[Alarm] -|List of Alarm objects. -|=== diff --git a/docs/modules/ROOT/pages/endpoints/business_services.adoc b/docs/modules/ROOT/pages/endpoints/business_services.adoc deleted file mode 100644 index 325f430..0000000 --- a/docs/modules/ROOT/pages/endpoints/business_services.adoc +++ /dev/null @@ -1,214 +0,0 @@ - -# Business Services - -The Business Services endpoint provides read and write access to Business Service Monitoring (BSM) records. - -== Supported methods - -=== get_bsm() - -Get business service by ID. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|ID of the business service to retrieve. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|BusinessService -|BusinessService object -|=== - -=== get_bsms() - -Get all business services in system. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|threads -|int -|10 -|Number of simultaneous threads to make API calls. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[BusinessService] -|List of BusinessService objects. -|=== - -=== find_bsm_name() - -Search for a business service by name. -The REST API does not provide a way to search for business service objects by name. -This method will pull each service record from the server until a matching name is found. -If the `cache_only` parameter is `True`, only business service objects previously retrieved this session will be searched. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|name -|str -|Required -|Name of the business service to retrieve. -Must be an exact, case-sensitive match. - -|cache_only -|bool -|False -|If True, only search BSM objects that have previously been retrieved this session. -If False, pull list of BSMs from the server and search for the specified `name`. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|BusinessService -|BusinessService object -|=== - -=== reload_bsm_daemon() - -Reloads the business service daemon to apply any create, update, or delete actions. - -.Parameters -[options="header, autowidth", cols="1"] -|=== -|Description - -|This method does not take any parameters -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|None -|No response is returned from this method. -|=== - - -=== create_bsm() - -Create new business service object. -Will return a `DuplicateEntityError` if there already is an existing business service with the same name. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|bsm -|BusinessServiceRequest -|Required -|BusinessServiceRequest object. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|None -|No response is returned from this method. -|=== - -=== update_bsm() - -Update an existing business service object. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|ID of the business service to update. - -|bsm -|BusinessServiceRequest -|Required -|BusinessServiceRequest object to update the specified the service with the given `id`. -The provided object will overwrite the existing business service. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|None -|No response is returned from this method. -|=== - -=== delete_bsm() - -Delete a business service object by ID. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|ID of the business service to delete. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|None -|No response is returned from this method. -|=== diff --git a/docs/modules/ROOT/pages/endpoints/events.adoc b/docs/modules/ROOT/pages/endpoints/events.adoc deleted file mode 100644 index 2351f3a..0000000 --- a/docs/modules/ROOT/pages/endpoints/events.adoc +++ /dev/null @@ -1,71 +0,0 @@ - -# Events - -The Events endpoint provides read-only access to system event records. - -== Supported methods - -=== get_event() - -Get event by ID. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|ID of the event to retrieve. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|Event -|Event object -|=== - -=== get_events() - -Get all events in system. - -WARNING: Do not use this with a limit of `0` as you probably have a lot of events in your server. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|limit -|int -|100 -|Maximum number of events to return. -Set to `0` to get all events. - -|batch_size -|int -|100 -|Number of events to get per API call. -If batch size is less than the limit, multiple API calls will automatically be made until limit is reached. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[Event] -|List of Event objects. -|=== diff --git a/docs/modules/ROOT/pages/endpoints/foreign_sources.adoc b/docs/modules/ROOT/pages/endpoints/foreign_sources.adoc deleted file mode 100644 index c230cbd..0000000 --- a/docs/modules/ROOT/pages/endpoints/foreign_sources.adoc +++ /dev/null @@ -1,71 +0,0 @@ - -# Foreign Sources - -The Foreign Sources endpoint currently only provides read-only access to the detectors and policies of a requisition. -Information regarding requisition nodes is included as part of the xref:ROOT:endpoints/requisitions.adoc[Requisition] endpoint. - - -== Supported methods - -=== get_foreign_source() - -Get specific foreign source definition by name. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|name -|str -|Required -|Name of foreign source definition to retrieve. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|ForeignSource -|ForeignSource object -|=== - -=== get_foreign_sources() - -Get all foreign source definitions in system. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|limit -|int -|100 -|Maximum number of foreign source definitions to return. -Set to `0` to get all foreign source definitions. - -|batch_size -|int -|100 -|Number of foreign source definitions to get per API call. -If batch size is less than the limit, multiple API calls will automatically be made until limit is reached. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[ForeignSource] -|List of ForeignSource objects. -|=== diff --git a/docs/modules/ROOT/pages/endpoints/nodes.adoc b/docs/modules/ROOT/pages/endpoints/nodes.adoc deleted file mode 100644 index 0392d76..0000000 --- a/docs/modules/ROOT/pages/endpoints/nodes.adoc +++ /dev/null @@ -1,112 +0,0 @@ - -= Nodes - -The Nodes endpoint provides read-only access to nodes in inventory. -Any changes to nodes should be made through a xref:ROOT:endpoints/requisitions.adoc[requisition]. - -== Supported methods - -=== get_node() - -Get one node by database ID number. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|id -|int -|Required -|Database ID of node to retrieve. - -|components -|List[NodeComponents] -|[NodeComponents.ALL] -|List of <> objects. -Defaults to include all component options. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|Node -|Node object -|=== - -=== get_nodes() - -Get all nodes in inventory. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|components -|List[NodeComponents] -|[None] -|List of <> objects. -Defaults to include no component options. - -|limit -|int -|100 -|Maximum number of nodes to return. -Set to `0` to get all nodes. - -|batch_size -|int -|100 -|Number of nodes to get per API call. -If batch size is less than the limit, multiple API calls will automatically be made until limit is reached. - -|threads -|int -|10 -|Number of simultaneous threads to make API calls. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[Node] -|List of Node objects. -|=== - -== NodeComponents - -By default, the node endpoints gather only basic information about the node. -The `components` parameter takes a list of NodeComponents enum values to indicate what additional data to include for the node. - -.Example call for just IP and SNMP interfaces -[source, python] ----- -nodes = server.nodes.get_nodes(limit=0, components=[NodeComponents.IP, NodeComponents.SNMP]) ----- - -NOTE: Each components requires an additional API call per node to retrieve data. - -Available components: - -NodeComponents.SNMP:: SnmpInterfaces. -NodeComponents.IP:: IPInterfaces. -NodeComponents.SERVICES:: Monitored Services. -As services are associated with IP Interfaces, if `SERVICES` is included, all IPInterfaces will also be collected. -NodeComponents.METADATA:: Metadata records. -NodeComponents.HARDWARE:: Hardware Inventory. -Currently only the default set of hardware inventory fields are supported. -If you have customized hardware inventory collection, including this component will generate an error. -NodeComponents.ALL:: Includes all of the above components. diff --git a/docs/modules/ROOT/pages/endpoints/requisitions.adoc b/docs/modules/ROOT/pages/endpoints/requisitions.adoc deleted file mode 100644 index fabc266..0000000 --- a/docs/modules/ROOT/pages/endpoints/requisitions.adoc +++ /dev/null @@ -1,150 +0,0 @@ - -# Requisitions - -The Requisitions endpoint currently only provides read-only access to list of included nodes. -Information regarding detectors and policies is included as part of the xref:ROOT:endpoints/foreign_sources.adoc[Foreign Sources] endpoint. - - -== Supported methods - -=== get_requisition() - -Get specific requisition by name. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|name -|str -|Required -|Name of requisition to retrieve. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|Requisition -|Requisition object -|=== - -=== get_requisitions() - -Get all requisitions in system. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|limit -|int -|100 -|Maximum number of requisitions to return. -Set to `0` to get all requisitions. - -|batch_size -|int -|100 -|Number of requisitions to get per API call. -If batch size is less than the limit, multiple API calls will automatically be made until limit is reached. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|List[Requisition] -|List of Requisition objects. -|=== - -=== import_requisition() - -Trigger a reimport of a specified requisition. -Import and rescan of requisitions happens in the background and the API will not indicate when the import starts or stops, just if the request was received. - -.Parameters -[options="header, autowidth", cols="1,1,1,2"] -|=== -|Name -|Type -|Default -|Description - -|name -|str -|Required -|Name of requisition to reimport. - -|rescan -|bool -|False -|If True, perform rescan of existing nodes. -If False, skip rescan of existing nodes. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|Boolean -|Returns True or False if request was received. -|=== - -=== get_requisition_active_count() - -Returns the number of active requisitions in the server. - -.Parameters -[options="header, autowidth", cols="1"] -|=== -|Description - -|This method does not take any parameters. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|int -|Count of active requisitions. -|=== - -=== get_requisition_deployed_count() - -Returns the number of deployed requisitions in the server. - -.Parameters -[options="header, autowidth", cols="1"] -|=== -|Description - -|This method does not take any parameters. -|=== - -.Returns -[options="header, autowidth", cols="1,2"] -|=== -|Type -|Description - -|int -|Count of deployed requisitions. -|=== diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc deleted file mode 100755 index 0e4360c..0000000 --- a/docs/modules/ROOT/pages/index.adoc +++ /dev/null @@ -1,8 +0,0 @@ - -[[welcome-index]] -= {page-component-title} {page-version} Documentation - - -Welcome to the {page-component-title} {page-version} documentation: information and guides to help you learn about {page-component-title} and start exploring its features. - -TIP: Use the left navigation bar to browse the documentation. diff --git a/docs/modules/ROOT/pages/introduction.adoc b/docs/modules/ROOT/pages/introduction.adoc deleted file mode 100755 index a863a4b..0000000 --- a/docs/modules/ROOT/pages/introduction.adoc +++ /dev/null @@ -1,6 +0,0 @@ - -[[introduction]] -= What is {page-component-title}? - -{page-component-title} is a Python library for accessing the https://docs.opennms.com/horizon/30/development/rest/rest-api.html[OpenNMS REST API]. -The goal of this project is to provide a way to interact with entities an OpenNMS server as native Python objects. diff --git a/docs/modules/ROOT/pages/portal/introduction.adoc b/docs/modules/ROOT/pages/portal/introduction.adoc deleted file mode 100644 index 54b9c49..0000000 --- a/docs/modules/ROOT/pages/portal/introduction.adoc +++ /dev/null @@ -1,15 +0,0 @@ - -= Cloud Portal - -The OpenNMS Cloud Portal provides management capabilities for hosted services. -A library for accessing the Cloud Portal API is included along site the {page-component-title} library. - -== Getting started - -A `test_portal.py` file is provided as an example of interacting with the library. - -Define the following environment variable to store your API key for connecting to the cloud API. - -* `portal_secret` - -NOTE: The portal endpoints are still in development, therefore documentation is limited. diff --git a/docs/modules/ROOT/pages/using/connecting.adoc b/docs/modules/ROOT/pages/using/connecting.adoc deleted file mode 100644 index 2dd507e..0000000 --- a/docs/modules/ROOT/pages/using/connecting.adoc +++ /dev/null @@ -1,31 +0,0 @@ - -= Connecting To A Server - -The {page-component-title} library works by defining an instance and then making calls against the various endpoints. - -.Define a server instance -[source, python] ----- -from pyonms import PyONMS - -my_server = PyONMS( - hostname='http://localhost:8980/opennms',<1> - username='admin',<2> - password='admin', -) ----- -<1> Replace with the hostname to your instance. -<2> Ensure the credentials provided have at least `ROLE_REST`. - -== Performing endpoint actions - -From your server instance object, endpoints can be utilized as class attributes of the server instance. - -.Example call to retrieve nodes for the instance -[source, python] ----- -nodes = my_server.nodes.get_nodes() ----- - -NOTE: Due to the early stage of this library, full documentation is not available for the various endpoints. -This library does use type hinting, so your IDE should provide information regarding the inputs and results for the various methods. diff --git a/docs/modules/ROOT/pages/using/introduction.adoc b/docs/modules/ROOT/pages/using/introduction.adoc deleted file mode 100755 index cebfac9..0000000 --- a/docs/modules/ROOT/pages/using/introduction.adoc +++ /dev/null @@ -1,15 +0,0 @@ - -= Basics - -{page-component-title} is still in an early alpha phase. -It is not currently packaged for deployment via `pip`. -You can fork the {repo-url}[repository] to contribute to the project or try it in your environment. - -== Installing {page-component-title} - -{page-component-title} is not published to PyPi at this time, but it can be installed from GitHub. -The project repo at `git+{repo-url}.git@main#egg=PyONMS` can be used either to install via `pip` or as part of your project's `requirements.txt`. - -== Getting started - -A `test_example.py` file is provided as an example of interacting with the library. diff --git a/docs/modules/ROOT/pages/using/support.adoc b/docs/modules/ROOT/pages/using/support.adoc deleted file mode 100644 index 18120a2..0000000 --- a/docs/modules/ROOT/pages/using/support.adoc +++ /dev/null @@ -1,6 +0,0 @@ - -= Support - -The {page-component-title} library is not an officially supported product of The OpenNMS Group. -The source for the library can be found at {repo-url}. -Bugs with the library can be reported via the {repo-url}/issues[GitHub Issues] feature and usage questions can be directed to {repo-url}/discussions[GitHub Discussions]. diff --git a/external-functionapp-openapi.yaml b/external-functionapp-openapi.yaml deleted file mode 100644 index 67d3293..0000000 --- a/external-functionapp-openapi.yaml +++ /dev/null @@ -1,1013 +0,0 @@ -openapi: 3.0.1 -info: - title: Appliance Service APIs - description: | - Use the ReST APIs in cases requiring automation or when performing operations on a large number of entities (where repeated use of the portal becomes cumbersome) - version: '1.0' -servers: - - url: 'https://portal.opennms.com/api/v1/external/' -paths: - '/appliance': - get: - tags: - - 'Appliance' - summary: Retrieve all appliances (paginated). - description: getAppliances - operationId: get-getappliances - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/appliance' - '/appliance/{id}': - get: - tags: - - 'Appliance' - summary: Retrieve appliance via ID. - description: getAppliance - operationId: get-getappliance - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/appliance' - put: - tags: - - 'Appliance' - summary: Update appliance via ID. - description: updateAppliance - operationId: put-updateappliance - parameters: - - name: id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/appliance' - responses: - '204': - description: 'Successfully updated the appliance.' - '/appliance/{id}/status': - get: - tags: - - 'Appliance Status' - summary: Retrieve appliance statuses via ID. - description: | - getApplianceStatus - - *connectivityState* - Indicates connection to cloud. - - *minionStatus* - Indicates state of the Minion. - - *onmsStatus* - Indicates Minions view of the openNMS instance. - operationId: get-getappliancestatus - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/status' - '/appliance-profile': - get: - tags: - - 'Appliance Profile' - summary: Retrieve all appliance profiles (paginated). - description: getApplianceProfiles - operationId: get-getapplianceprofiles - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/applianceProfile' - '/appliance-profile/{id}': - get: - tags: - - 'Appliance Profile' - summary: Retrieve appliance profile via ID. - description: getApplianceProfile - operationId: get-getapplianceprofile - parameters: - - name: id - in: path - required: true - schema: - type: 'string' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/applianceProfile' - '/appliance/{id}/configuration': - get: - tags: - - 'Appliance Configuration' - summary: Retrieve and download appliance's configuration via ID. - description: getApplianceConfiguration - operationId: get-getapplianceconfiguration - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/octet-stream: - schema: - type: string - format: binary - '/appliance/{id}/info': - get: - tags: - - 'Appliance Info' - summary: Retrieve appliance platform information (IP addresses, hostname) via ID. - description: getAppliancePlatformInfoExternal - operationId: get-getapplianceplatforminfoexternal - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/appliancePlatformInfo' - '/instance': - post: - tags: - - 'Instance' - summary: Create instance. - description: createInstance - operationId: post-createinstance - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/instanceCreate' - responses: - '201': - description: 'Successfully created an instance' - content: - text/plain: - schema: - type: string - get: - tags: - - 'Instance' - summary: Retrieve all instances (paginated). - description: getInstances - operationId: get-getinstances - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/instance' - '/instance/{id}': - get: - tags: - - 'Instance' - summary: Retrieve instance via ID. - description: getInstance - operationId: get-getinstance - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/instance' - delete: - tags: - - 'Instance' - summary: Delete instance via ID. - description: deleteInstance - operationId: delete-deleteinstance - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '204': - description: '' - '/subscription': - get: - tags: - - 'Subscription' - summary: Retrieve all subscriptions (paginated). - description: getSubscriptions - operationId: get-getSubscriptions - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: [ 'ASC', 'DESC' ] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/subscription' - '/subscription/{id}': - get: - tags: - - 'Subscription' - summary: Retrieve subscription via ID. - description: getSubscription - operationId: get-getsubscription - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/subscription' - '/minion-profile': - get: - tags: - - 'Minion Profile' - summary: Retrieve all profiles (paginated). - description: getMinionProfiles - operationId: get-getminionprofiles - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/minionProfile' - '/minion-profile/{id}': - get: - tags: - - 'Minion Profile' - summary: Retrieve Minion profile via ID. - description: getMinionProfile - operationId: get-getminionprofile - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/minionProfile' - '/connectivity-profile': - get: - tags: - - 'Connectivity Profile' - summary: Retrieve all connectivity profiles (paginated). - description: getConnectivityProfiles - operationId: get-getconnectivityprofiles - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/connectivityProfile' - post: - tags: - - 'Connectivity Profile' - summary: Create a connectivity profile. - description: createConnectivityProfile - operationId: post-createconnectivityprofile - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/connectivityProfileCreate' - responses: - '201': - description: 'Successfully created a connectivity profile.' - content: - text/plain: - schema: - type: string - '/connectivity-profile/{id}': - get: - tags: - - 'Connectivity Profile' - summary: Retrieve connectivity profile via ID. - description: getConnectivityProfile - operationId: get-getconnectivityprofile - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/connectivityProfile' - delete: - tags: - - 'Connectivity Profile' - summary: Remove connectivity profile via ID. - description: deleteConnectivityProfile - operationId: delete-deleteconnectivityprofile - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '204': - description: 'Successfully deleted the connectivity profile.' - '/feature-profile': - get: - tags: - - 'Feature Profile' - summary: Retrieve all feature profiles (paginated). - description: getFeatureProfiles - operationId: get-getfeatureprofiles - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/featureProfile' - '/feature-profile/{id}': - get: - tags: - - 'Feature Profile' - summary: Retrieve feature profile via ID. - description: getFeatureProfile - operationId: get-getfeatureprofile - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/featureProfile' - '/location': - get: - tags: - - 'Location' - summary: Retrieve all locations (paginated). - description: getLocations - operationId: get-getlocations - parameters: - - name: orderBy - in: query - required: false - schema: - type: string - - name: limit - in: query - required: false - schema: - type: number - - name: offset - in: query - required: false - schema: - type: number - - name: sort - in: query - required: false - schema: - type: string - enum: ['ASC', 'DESC'] - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - properties: - totalRecords: - type: number - pagedRecords: - type: array - items: - $ref: '#/components/schemas/location' - post: - tags: - - 'Location' - summary: Create a location. - description: createLocation - operationId: post-createlocation - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/locationCreate' - responses: - '201': - description: 'Successfully created a location.' - content: - text/plain: - schema: - type: string - '/location/{id}': - get: - tags: - - 'Location' - summary: Retrieve location via ID. - description: getLocation - operationId: get-getlocation - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/location' - put: - tags: - - 'Location' - summary: Update location via ID. - description: updateLocation - operationId: put-updatelocation - parameters: - - name: id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/location' - responses: - '204': - description: 'Successfully updated the location.' - delete: - tags: - - 'Location' - summary: Remove location via ID. - description: deleteLocation - operationId: delete-deletelocation - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '204': - description: 'Successfully deleted the location.' -components: - schemas: - appliance: - required: - - label - type: object - properties: - id: - type: string - format: UUID - readOnly: true - label: - type: string - type: - type: string - enum: ['HARDWARE' , 'VIRTUAL'] - readOnly: true - geoLocationLabel: - type: string - latitude: - type: number - longitude: - type: number - applianceProfileId: - type: string - format: UUID - minion: - $ref: '#/components/schemas/minion' - subscriptionId: - type: string - format: UUID - applianceProfile: - required: - - name - type: object - properties: - id: - type: string - format: UUID - readOnly: true - description: Automatically generated backend value. - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - name: - type: string - subscription: - required: - - count - - expiry - - state - type: object - properties: - id: - type: string - format: UUID - readOnly: true - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - count: - type: number - expiry: - type: string - state: - type: string - location: - required: - - name - - onmsInstanceId - - connectivityProfileId - type: object - properties: - id: - type: string - format: UUID - readOnly: true - description: Automatically generated backend value. - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - name: - type: string - onmsInstanceId: - type: string - format: UUID - example: 9d6d1226-35f7-43a1-b2b7-852b78dbc220 - minionFeatureProfileId: - type: string - format: UUID - example: 7f01217c-d7eb-4e55-b7b6-7e151abe9764 - connectivityProfileId: - type: string - format: UUID - example: edb60e4c-eead-477a-ad9a-6f04e113c80c - locationCreate: - required: - - name - - onmsInstanceId - - connectivityProfileId - type: object - properties: - name: - type: string - onmsInstanceId: - type: string - format: UUID - example: 9d6d1226-35f7-43a1-b2b7-852b78dbc220 - minionFeatureProfileId: - type: string - format: UUID - example: 7f01217c-d7eb-4e55-b7b6-7e151abe9764 - connectivityProfileId: - type: string - format: UUID - example: edb60e4c-eead-477a-ad9a-6f04e113c80c - minion: - type: object - properties: - locationId: - type: string - format: UUID - example: 6a75a1ef-b5ff-4733-a9d1-81c82b951865 - minionProfileId: - type: string - format: UUID - example: b426d422-6a51-4e79-8d9c-43810f83c1cb - status: - type: object - properties: - status: - type: object - properties: - connectivityState: - type: string - description: Indicates connection to cloud. - minionStatus: - type: string - description: Indicates state of the Minion. - enum: ['UP' , 'DOWN' , 'GRACE_PERIOD' , 'UNKNOWN'] - onmsStatus: - type: string - description: Indicates Minion's view of the openNMS instance. - enum: ['UP' , 'DOWN' , 'GRACE_PERIOD' , 'UNKNOWN'] - connectivityProfile: - type: object - properties: - id: - type: string - format: UUID - readOnly: true - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - onmsInstance: - readOnly: true - allOf: - - $ref: '#/components/schemas/instance' - name: - type: string - connectivityProfileCreate: - required: - - name - - onmsInstanceId - - httpConfig - - brokerConfig - type: object - properties: - name: - type: string - onmsInstanceId: - type: string - format: UUID - example: 9d6d1226-35f7-43a1-b2b7-852b78dbc220 - httpConfig: - allOf: - - $ref: '#/components/schemas/httpConfig' - brokerConfig: - oneOf: - - $ref: '#/components/schemas/brokerJms' - - $ref: '#/components/schemas/brokerKafka' - httpConfig: - required: - - url - - user - - password - type: object - properties: - url: - type: string - user: - type: string - password: - type: string - brokerJms: - allOf: - - $ref: '#/components/schemas/brokerConfig' - - type: object - required: - - url - - user - - password - properties: - url: - type: string - user: - type: string - password: - type: string - brokerKafka: - allOf: - - $ref: '#/components/schemas/brokerConfig' - - type: object - required: - - bootstrapServers - properties: - bootstrapServers: - type: string - brokerConfig: - required: - - type - type: object - properties: - type: - type: string - description: Indicates type of the broker. - enum: [ 'JMS' , 'KAFKA' ] - featureProfile: - type: object - properties: - id: - type: string - format: UUID - readOnly: true - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - name: - type: string - onmsInstance: - readOnly: true - allOf: - - $ref: '#/components/schemas/instance' - instance: - type: object - properties: - id: - type: string - format: UUID - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - name: - type: string - readOnly: true - instanceCreate: - required: - - name - type: object - properties: - name: - type: string - readOnly: true - minionProfile: - required: - - processEnvConfigId - type: object - properties: - id: - type: string - format: UUID - readOnly: true - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - name: - type: string - processEnvConfigId: - type: string - format: UUID - example: 08a4abca-ac1b-4424-8881-9ffde851e308 - appliancePlatformInfo: - type: object - properties: - hostname: - type: string - readOnly: true - ipInfo: - type: object - additionalProperties: - type: object - properties: - ipAddresses: - type: array - items: - type: string - interfaceName: - type: string - securitySchemes: - apiKeyHeader: - type: apiKey - name: X-API-Key - in: header -security: - - apiKeyHeader: [] diff --git a/pyonms/__init__.py b/pyonms/__init__.py index 291ce32..7719125 100755 --- a/pyonms/__init__.py +++ b/pyonms/__init__.py @@ -7,23 +7,12 @@ .. include:: ../README.md """ -__version__ = "0.0.12" +__version__ = "0.0.13" from multiprocessing import current_process from urllib.parse import urlsplit -import pyonms.dao.alarms -import pyonms.dao.business_services -import pyonms.dao.events -import pyonms.dao.foreign_sources -import pyonms.dao.health -import pyonms.dao.info -import pyonms.dao.ips -import pyonms.dao.nodes -import pyonms.dao.requisitions -import pyonms.dao.udl -from pyonms.models.event import Event -from pyonms.models.exceptions import InvalidValueError +from pyonms import dao, models class PyONMS: @@ -63,9 +52,9 @@ def __init__( self.name = urlsplit(hostname).netloc.split(":")[0] args["name"] = self.name - self.health = pyonms.dao.health.HealthAPI(args) + self.health = dao.health.HealthAPI(args) """`pyonms.dao.health.HealthAPI` endpoint""" - self.info = pyonms.dao.info.InfoAPI(args) + self.info = dao.info.InfoAPI(args) """`pyonms.dao.info.InfoAPI` endpoint""" if current_process().name == "MainProcess": @@ -74,21 +63,21 @@ def __init__( self.server_status = self.info.get_info() args["version"] = self.server_status.version - self.alarms = pyonms.dao.alarms.AlarmAPI(args) + self.alarms = dao.alarms.AlarmAPI(args) """`pyonms.dao.alarms.AlarmAPI` endpoint""" - self.bsm = pyonms.dao.business_services.BSMAPI(args) + self.bsm = dao.business_services.BSMAPI(args) """`pyonms.dao.business_services.BSMAPI` endpoint""" - self.events = pyonms.dao.events.EventAPI(args) + self.events = dao.events.EventAPI(args) """`pyonms.dao.events.EventAPI` endpoint""" - self.fs = pyonms.dao.foreign_sources.ForeignSourceAPI(args) + self.fs = dao.foreign_sources.ForeignSourceAPI(args) """`pyonms.dao.foreign_sources.ForeignSourceAPI` endpoint""" - self.nodes = pyonms.dao.nodes.NodeAPI(args) + self.nodes = dao.nodes.NodeAPI(args) """`pyonms.dao.nodes.NodeAPI` endpoint""" - self.ips = pyonms.dao.ips.IPAPI(args) + self.ips = dao.ips.IPAPI(args) """`pyonms.dao.ips.IPAPI` endpoint""" - self.requisitions = pyonms.dao.requisitions.RequisitionsAPI(args) + self.requisitions = dao.requisitions.RequisitionsAPI(args) """`pyonms.dao.requisitions.RequisitionsAPI` endpoint""" - self.udl = pyonms.dao.udl.UDLAPI(args) + self.udl = dao.udl.UDLAPI(args) """`pyonms.dao.udl.UDLAPI` endpoint""" def __repr__(self): @@ -103,10 +92,10 @@ def reload_daemon(self, name: str): self.server_status.enabled_services and name.lower() not in self.server_status.enabled_services ): - raise InvalidValueError( + raise models.exceptions.InvalidValueError( name="name", value=name, valid=self.server_status.enabled_services ) - reload_event = Event( + reload_event = models.event.Event( uei="uei.opennms.org/internal/reloadDaemonConfig", source="pyonms" ) reload_event.set_parameter(name="daemonName", value=name, type="string") diff --git a/pyonms/dao/__init__.py b/pyonms/dao/__init__.py index 8e840a2..d5a8a48 100755 --- a/pyonms/dao/__init__.py +++ b/pyonms/dao/__init__.py @@ -1,256 +1,14 @@ # dao.__init__.py -"""Base classes for DAO objects""" - -from typing import List - -import requests -from requests.auth import HTTPBasicAuth -from requests.packages import urllib3 -from tqdm import tqdm -from urllib3.exceptions import InsecureRequestWarning - -import pyonms.utils -from pyonms.models.exceptions import AuthenticationError, InvalidValueError - -urllib3.disable_warnings(category=InsecureRequestWarning) - - -class Endpoint: - """Base class for endpoint data access""" - - def __init__( - self, hostname: str, username: str, password: str, name: str, **kwargs - ): - if hostname[-1:] == "/": - hostname = hostname[:-1] - self.base_v1 = f"{hostname}/rest/" - self.base_v2 = f"{hostname}/api/v2/" - self.hostname = hostname - self.username = username - self.password = password - self.verify_ssl = True - self.timeout = 30 - self.name = name - self.headers = {"Accept": "application/json"} - self.auth = HTTPBasicAuth(self.username, self.password) - for key, value in kwargs.items(): - setattr(self, key, value) - - def _get_batch( - self, - url: str, - endpoint: str, - limit: int = 0, - batch_size: int = 100, - params: dict = None, - hide_progress: bool = False, - ) -> List[dict]: - if not params: - params = {} - with tqdm( - total=limit, - unit="record", - desc=f"Pulling {self.name} {endpoint} data", - disable=hide_progress, - ) as pbar: - result = [] - params["offset"] = 0 - if limit > batch_size: - params["limit"] = batch_size - else: - params["limit"] = limit - records = self._get( - uri=url, - params=params, - endpoint=endpoint, - headers=self.headers, - ) - if records.get(endpoint, [None]) in [[None], []]: - return [None] - if limit == 0 or records["totalCount"] < limit: - target_count = records["totalCount"] - pbar.total = target_count - else: - target_count = limit - while params["offset"] < target_count: - for record in records[endpoint]: - result.append(record) - params["offset"] += 1 - pbar.update(1) - if params["offset"] >= target_count: - return result - records = self._get(uri=url, params=params, endpoint=endpoint) - return result - - def _get( - self, uri: str, headers: dict = None, params: dict = None, endpoint: str = None - ) -> dict: - # if self.base_v1 in uri: - # return self._get_v1( - # uri=uri, headers=headers, params=params, endpoint=endpoint - # ) - if not headers: - headers = {} - if not params: - params = {} - if endpoint != "raw": - for key, value in self.headers.items(): - headers[key] = value - response = requests.get( - uri, - auth=self.auth, - headers=headers, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - if response.status_code == 200: - if response.encoding in ("ISO-8859-1") or uri[-5:] in ["probe"]: - return response.text - elif "was not found" not in response.text: - return response.json() - elif response.status_code == 401: - raise AuthenticationError - elif response.status_code in [500]: - print(response.text) - raise InvalidValueError(name="get", value=uri) - return {} - - def _get_v1( - self, uri: str, endpoint: str, headers: dict = None, params: dict = None - ) -> dict: - if not headers: - headers = {} - if not params: - params = {} - response = requests.get( - uri, - auth=self.auth, - headers=headers, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - if response.status_code == 200: - if "Sign in to your account" in response.text: - raise AuthenticationError - elif "was not found" not in response.text: - if endpoint == "raw": - return response.text - else: - xml_data = pyonms.utils.convert_xml(response.text) - return self._convert_v1_to_v2(endpoint, xml_data) - elif response.status_code == 599: - return response.text - return {} - - def _post( - self, - uri: str, - headers: dict = None, - data: dict = None, - json: dict = None, - params: dict = None, - ) -> requests.Response: - if not headers: - headers = {} - if not params: - params = {} - if json: - response = requests.post( - uri, - auth=self.auth, - headers=headers, - json=json, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - elif data: - response = requests.post( - uri, - auth=self.auth, - headers=headers, - data=data, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - else: - response = requests.post( - uri, - auth=self.auth, - headers=headers, - verify=self.verify_ssl, - timeout=self.timeout, - ) - return response - - def _put( - self, - uri: str, - data: dict = None, - json: dict = None, - headers: dict = None, - params: dict = None, - ) -> dict: - if not headers: - headers = {} - if not params: - params = {} - if json: - response = requests.put( - uri, - auth=self.auth, - headers=headers, - json=json, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - elif data: - response = requests.put( - uri, - auth=self.auth, - headers=headers, - data=data, - params=params, - verify=self.verify_ssl, - timeout=self.timeout, - ) - else: - return None - return response.text - - def _convert_v1_to_v2(self, endpoint: str, data: dict) -> dict: - v2_data = {} - if "model_import" in data.keys(): - if data["model_import"].get("xmlns"): - del data["model_import"]["xmlns"] - v2_data = data["model_import"] - elif endpoint in data.keys(): - for key, value in data.items(): - v2_data["count"] = int(value["count"]) - v2_data["offset"] = int(value["offset"]) - v2_data["totalCount"] = int(value["totalCount"]) - if isinstance(value["model_import"], list): - v2_data[key] = value["model_import"] - elif isinstance(value["model_import"], dict): - v2_data[key] = [value["model_import"]] - return v2_data - - def _delete(self, uri: str, headers: dict = None, params: dict = None) -> dict: - if not headers: - headers = {} - if not params: - params = {} - headers["Accept"] = "application/json" - requests.delete( - uri, - auth=self.auth, - headers=headers, - verify=self.verify_ssl, - timeout=self.timeout, - ) - return {} +from pyonms.dao import ( + alarms, + business_services, + events, + foreign_sources, + health, + info, + ips, + nodes, + requisitions, + udl, +) diff --git a/pyonms/dao/alarms.py b/pyonms/dao/alarms.py index 8430cdd..189c5bd 100755 --- a/pyonms/dao/alarms.py +++ b/pyonms/dao/alarms.py @@ -3,7 +3,7 @@ from typing import List, Optional import pyonms.models.alarm -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint from pyonms.models import exceptions diff --git a/pyonms/dao/base.py b/pyonms/dao/base.py new file mode 100644 index 0000000..a9cf4e5 --- /dev/null +++ b/pyonms/dao/base.py @@ -0,0 +1,256 @@ +# dao.base.py + +"""Base classes for DAO objects""" + +from typing import List + +import requests +from requests.auth import HTTPBasicAuth +from requests.packages import urllib3 +from tqdm import tqdm +from urllib3.exceptions import InsecureRequestWarning + +import pyonms.utils +from pyonms.models.exceptions import AuthenticationError, InvalidValueError + +urllib3.disable_warnings(category=InsecureRequestWarning) + + +class Endpoint: + """Base class for endpoint data access""" + + def __init__( + self, hostname: str, username: str, password: str, name: str, **kwargs + ): + if hostname[-1:] == "/": + hostname = hostname[:-1] + self.base_v1 = f"{hostname}/rest/" + self.base_v2 = f"{hostname}/api/v2/" + self.hostname = hostname + self.username = username + self.password = password + self.verify_ssl = True + self.timeout = 30 + self.name = name + self.headers = {"Accept": "application/json"} + self.auth = HTTPBasicAuth(self.username, self.password) + for key, value in kwargs.items(): + setattr(self, key, value) + + def _get_batch( + self, + url: str, + endpoint: str, + limit: int = 0, + batch_size: int = 100, + params: dict = None, + hide_progress: bool = False, + ) -> List[dict]: + if not params: + params = {} + with tqdm( + total=limit, + unit="record", + desc=f"Pulling {self.name} {endpoint} data", + disable=hide_progress, + ) as pbar: + result = [] + params["offset"] = 0 + if limit > batch_size: + params["limit"] = batch_size + else: + params["limit"] = limit + records = self._get( + uri=url, + params=params, + endpoint=endpoint, + headers=self.headers, + ) + if records.get(endpoint, [None]) in [[None], []]: + return [None] + if limit == 0 or records["totalCount"] < limit: + target_count = records["totalCount"] + pbar.total = target_count + else: + target_count = limit + while params["offset"] < target_count: + for record in records[endpoint]: + result.append(record) + params["offset"] += 1 + pbar.update(1) + if params["offset"] >= target_count: + return result + records = self._get(uri=url, params=params, endpoint=endpoint) + return result + + def _get( + self, uri: str, headers: dict = None, params: dict = None, endpoint: str = None + ) -> dict: + # if self.base_v1 in uri: + # return self._get_v1( + # uri=uri, headers=headers, params=params, endpoint=endpoint + # ) + if not headers: + headers = {} + if not params: + params = {} + if endpoint != "raw": + for key, value in self.headers.items(): + headers[key] = value + response = requests.get( + uri, + auth=self.auth, + headers=headers, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + if response.status_code == 200: + if response.encoding in ("ISO-8859-1") or uri[-5:] in ["probe"]: + return response.text + elif "was not found" not in response.text: + return response.json() + elif response.status_code == 401: + raise AuthenticationError + elif response.status_code in [500]: + print(response.text) + raise InvalidValueError(name="get", value=uri) + return {} + + def _get_v1( + self, uri: str, endpoint: str, headers: dict = None, params: dict = None + ) -> dict: + if not headers: + headers = {} + if not params: + params = {} + response = requests.get( + uri, + auth=self.auth, + headers=headers, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + if response.status_code == 200: + if "Sign in to your account" in response.text: + raise AuthenticationError + elif "was not found" not in response.text: + if endpoint == "raw": + return response.text + else: + xml_data = pyonms.utils.convert_xml(response.text) + return self._convert_v1_to_v2(endpoint, xml_data) + elif response.status_code == 599: + return response.text + return {} + + def _post( + self, + uri: str, + headers: dict = None, + data: dict = None, + json: dict = None, + params: dict = None, + ) -> requests.Response: + if not headers: + headers = {} + if not params: + params = {} + if json: + response = requests.post( + uri, + auth=self.auth, + headers=headers, + json=json, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + elif data: + response = requests.post( + uri, + auth=self.auth, + headers=headers, + data=data, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + else: + response = requests.post( + uri, + auth=self.auth, + headers=headers, + verify=self.verify_ssl, + timeout=self.timeout, + ) + return response + + def _put( + self, + uri: str, + data: dict = None, + json: dict = None, + headers: dict = None, + params: dict = None, + ) -> dict: + if not headers: + headers = {} + if not params: + params = {} + if json: + response = requests.put( + uri, + auth=self.auth, + headers=headers, + json=json, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + elif data: + response = requests.put( + uri, + auth=self.auth, + headers=headers, + data=data, + params=params, + verify=self.verify_ssl, + timeout=self.timeout, + ) + else: + return None + return response.text + + def _convert_v1_to_v2(self, endpoint: str, data: dict) -> dict: + v2_data = {} + if "model_import" in data.keys(): + if data["model_import"].get("xmlns"): + del data["model_import"]["xmlns"] + v2_data = data["model_import"] + elif endpoint in data.keys(): + for key, value in data.items(): + v2_data["count"] = int(value["count"]) + v2_data["offset"] = int(value["offset"]) + v2_data["totalCount"] = int(value["totalCount"]) + if isinstance(value["model_import"], list): + v2_data[key] = value["model_import"] + elif isinstance(value["model_import"], dict): + v2_data[key] = [value["model_import"]] + return v2_data + + def _delete(self, uri: str, headers: dict = None, params: dict = None) -> dict: + if not headers: + headers = {} + if not params: + params = {} + headers["Accept"] = "application/json" + requests.delete( + uri, + auth=self.auth, + headers=headers, + verify=self.verify_ssl, + timeout=self.timeout, + ) + return {} diff --git a/pyonms/dao/business_services.py b/pyonms/dao/business_services.py index 20e7e63..d1df9da 100644 --- a/pyonms/dao/business_services.py +++ b/pyonms/dao/business_services.py @@ -8,7 +8,7 @@ from tqdm import tqdm import pyonms.models.business_service -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class BSMAPI(Endpoint): diff --git a/pyonms/dao/events.py b/pyonms/dao/events.py index 8a559f7..f3d4594 100755 --- a/pyonms/dao/events.py +++ b/pyonms/dao/events.py @@ -4,7 +4,7 @@ import pyonms.models.event import pyonms.models.node -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class EventAPI(Endpoint): diff --git a/pyonms/dao/foreign_sources.py b/pyonms/dao/foreign_sources.py index a924cdb..bee824b 100644 --- a/pyonms/dao/foreign_sources.py +++ b/pyonms/dao/foreign_sources.py @@ -5,7 +5,7 @@ import requests import pyonms.models.foreign_source -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class ForeignSourceAPI(Endpoint): diff --git a/pyonms/dao/health.py b/pyonms/dao/health.py index 3a834a5..1a1f62b 100644 --- a/pyonms/dao/health.py +++ b/pyonms/dao/health.py @@ -1,7 +1,7 @@ # dao.health.py import pyonms.models.health -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class HealthAPI(Endpoint): diff --git a/pyonms/dao/info.py b/pyonms/dao/info.py index 7a0b92e..c94accc 100644 --- a/pyonms/dao/info.py +++ b/pyonms/dao/info.py @@ -1,7 +1,7 @@ # dao.info.py import pyonms.models.info -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class InfoAPI(Endpoint): diff --git a/pyonms/dao/ips.py b/pyonms/dao/ips.py index a309bfc..70bc81a 100644 --- a/pyonms/dao/ips.py +++ b/pyonms/dao/ips.py @@ -5,7 +5,7 @@ from typing import List, Union import pyonms.models.node -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint from pyonms.models import exceptions diff --git a/pyonms/dao/nodes.py b/pyonms/dao/nodes.py index 1ee73c0..e97963c 100755 --- a/pyonms/dao/nodes.py +++ b/pyonms/dao/nodes.py @@ -9,7 +9,7 @@ from tqdm import tqdm import pyonms.models.node -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class NodeComponents(Enum): diff --git a/pyonms/dao/requisitions.py b/pyonms/dao/requisitions.py index 940b5dc..2179dda 100755 --- a/pyonms/dao/requisitions.py +++ b/pyonms/dao/requisitions.py @@ -5,7 +5,7 @@ import requests import pyonms.models.requisition -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint from pyonms.utils import normalize_dict diff --git a/pyonms/dao/udl.py b/pyonms/dao/udl.py index 731862d..32bbae7 100644 --- a/pyonms/dao/udl.py +++ b/pyonms/dao/udl.py @@ -5,7 +5,7 @@ from typing import List, Optional import pyonms.models.udl -from pyonms.dao import Endpoint +from pyonms.dao.base import Endpoint class UDLAPI(Endpoint): diff --git a/pyonms/models/__init__.py b/pyonms/models/__init__.py index 939910e..b5b713b 100755 --- a/pyonms/models/__init__.py +++ b/pyonms/models/__init__.py @@ -1 +1,14 @@ # models.__init__.py + +from pyonms.models import ( + alarm, + business_service, + event, + exceptions, + foreign_source, + health, + info, + node, + requisition, + udl, +) diff --git a/pyonms/models/foreign_source.py b/pyonms/models/foreign_source.py index cfc54eb..5b3ff9f 100644 --- a/pyonms/models/foreign_source.py +++ b/pyonms/models/foreign_source.py @@ -25,16 +25,16 @@ def _to_dict(self) -> dict: class Detector: name: str class_type: str - parameters: List[Optional[Parameter]] = field(default_factory=list) + parameter: List[Optional[Parameter]] = field(default_factory=list) def __post_init__(self): - parameters = [] - for param in self.parameters: + parameter = [] + for param in self.parameter: if isinstance(param, dict): - parameters.append(Parameter(**param)) + parameter.append(Parameter(**param)) elif isinstance(param, Parameter): - parameters.append(param) - self.parameters = parameters + parameter.append(param) + self.parameter = parameter def __repr__(self): return ( @@ -43,7 +43,7 @@ def __repr__(self): def _to_dict(self) -> dict: payload = {"name": self.name, "class": self.class_type} - payload["parameter"] = [parameter._to_dict() for parameter in self.parameters] + payload["parameter"] = [parameter._to_dict() for parameter in self.parameter] return payload @@ -51,23 +51,23 @@ def _to_dict(self) -> dict: class Policy: name: str class_type: str - parameters: List[Optional[Parameter]] = field(default_factory=list) + parameter: List[Optional[Parameter]] = field(default_factory=list) def __post_init__(self): - parameters = [] - for param in self.parameters: + parameter = [] + for param in self.parameter: if isinstance(param, dict): - parameters.append(Parameter(**param)) + parameter.append(Parameter(**param)) elif isinstance(param, Parameter): - parameters.append(param) - self.parameters = parameters + parameter.append(param) + self.parameter = parameter def __repr__(self): return f"Policy(name={self.name}, class_type={self.class_type.split('.')[-1]})" def _to_dict(self) -> dict: payload = {"name": self.name, "class": self.class_type} - payload["parameter"] = [parameter._to_dict() for parameter in self.parameters] + payload["parameter"] = [parameter._to_dict() for parameter in self.parameter] return payload diff --git a/pyonms/models/info.py b/pyonms/models/info.py index 5cfcea7..3c22a39 100644 --- a/pyonms/models/info.py +++ b/pyonms/models/info.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field -from typing import Dict, List, Optional +from typing import List, Optional @dataclass @@ -66,10 +66,11 @@ def __post_init__(self): self.ticketerConfig = TicketerConfig(**self.ticketerConfig) if isinstance(self.datetimeformatConfig, dict): self.datetimeformatConfig = DateFormat(**self.datetimeformatConfig) - services = [] - for service, status in self.services.items(): - services.append(Service(name=service, status=status)) - self.services = services + if isinstance(self.services, dict): + services = [] + for service, status in self.services.items(): + services.append(Service(name=service, status=status)) + self.services = services self.enabled_services = [service.name.lower() for service in self.services] def __repr__(self): diff --git a/pyonms/portal/__init__.py b/pyonms/portal/__init__.py deleted file mode 100644 index 588d137..0000000 --- a/pyonms/portal/__init__.py +++ /dev/null @@ -1,201 +0,0 @@ -# portal.__init__.py - -from typing import List, Optional - -import requests -from tqdm import tqdm - -from pyonms.models.exceptions import DuplicateEntityError -from pyonms.portal.models import ( - PortalAppliance, - PortalApplianceProfile, - PortalApplianceStatus, - PortalConnectivityProfile, - PortalConnectivityProfileCreate, - PortalFeatureProfile, - PortalInstance, - PortalInstanceCreate, - PortalLocation, - PortalLocationCreate, - PortalMinion, - PortalSubscription, -) - - -class Portal: - def __init__(self, secret: str): - """OpenNMS Cloud Portal - Attributes: - secret (str): API Key - Returns: - `Portal` object - """ - self.hostname = "https://portal.opennms.com/api/v1/external/" - self.base_v1 = self.hostname - self.secret = secret - self.headers = {"Accept": "application/json", "X-API-Key": secret} - - def __get_batch( - self, - url: str, - endpoint: str, - limit: int = 0, - batch_size: int = 100, - params: dict = {}, - ) -> List[dict]: - with tqdm(total=limit, unit="record") as pbar: - result = [] - params["offset"] = 0 - if limit > batch_size: - params["limit"] = batch_size - else: - params["limit"] = limit - records = self._get(uri=url, params=params, endpoint=endpoint) - if records.get(endpoint, [None]) == [None]: - return [None] - if limit == 0 or records["totalCount"] < limit: - target_count = records["totalCount"] - pbar.total = target_count - else: - target_count = limit - while params["offset"] < target_count: - for record in records[endpoint]: - result.append(record) - params["offset"] += 1 - pbar.update(1) - if params["offset"] >= target_count: - return result - records = self._get(uri=url, params=params, endpoint=endpoint) - return result - - def _get(self, uri: str, headers: dict = {}, params: dict = {}) -> dict: - if not headers: - headers = self.headers - response = requests.get(uri, headers=headers, params=params) - if response.status_code == 200: - if "was not found" not in response.text: - return response.json() - return {} - - def _post( - self, - uri: str, - headers: dict = {}, - json: dict = None, - params: dict = {}, - ) -> dict: - if not headers: - headers = self.headers - if json: - response = requests.post(uri, headers=headers, json=json, params=params) - else: - response = requests.post(uri, headers=headers) - if response.status_code in [409]: - raise DuplicateEntityError(name=json["name"], model="PortalInstance") - return response.text - - def _put( - self, uri: str, json: dict, headers: dict = {}, params: dict = {} - ) -> requests.Response: - if not headers: - headers = self.headers - return requests.put(uri, headers=headers, json=json, params=params) - - def get_appliance(self, id: str) -> PortalAppliance: - data = self._get(uri=f"{self.base_v1}appliance/{id}") - if data: - return PortalAppliance(**data) - else: - return None - - def update_appliance(self, appliance: PortalAppliance) -> PortalAppliance: - response = self._put( - uri=f"{self.base_v1}appliance/{appliance.id}", json=appliance.to_dict() - ) - if response.status_code in [204]: - new_instance = self.get_appliance(id=appliance.id) - return new_instance - - def get_all_appliances(self) -> List[Optional[PortalAppliance]]: - appliances = [] - data = self._get(uri=f"{self.base_v1}appliance") - for appliance in data["pagedRecords"]: - appliances.append(PortalAppliance(**appliance)) - return appliances - - def get_appliance_status(self, appliance: PortalAppliance) -> PortalApplianceStatus: - data = self._get(uri=f"{self.base_v1}appliance/{appliance.id}/status") - return PortalApplianceStatus(**data) - - def get_appliance_profile(self, id: str) -> PortalApplianceProfile: - data = self._get(uri=f"{self.base_v1}appliance-profile/{id}") - if data: - return PortalApplianceProfile(**data) - else: - return None - - def get_instance(self, id: str) -> PortalInstance: - data = self._get(uri=f"{self.base_v1}instance/{id}") - if data: - return PortalInstance(**data) - else: - return None - - def create_instance(self, instance: PortalInstanceCreate) -> PortalInstance: - instance_id = self._post(uri=f"{self.base_v1}instance", json=instance.to_dict()) - new_instance = self.get_instance(id=instance_id) - return new_instance - - def get_subscription(self, id: str) -> PortalSubscription: - data = self._get(uri=f"{self.base_v1}subscription/{id}") - if data: - return PortalSubscription(**data) - else: - return None - - def get_connectivity_profile(self, id: str) -> PortalConnectivityProfile: - data = self._get(uri=f"{self.base_v1}connectivity-profile/{id}") - if data: - return PortalConnectivityProfile(**data) - else: - return None - - def create_connectivity_profile( - self, connectivity_profile: PortalConnectivityProfileCreate - ) -> PortalConnectivityProfile: - instance_id = self._post( - uri=f"{self.base_v1}connectivity-profile", - json=connectivity_profile.to_dict(), - ) - new_instance = self.get_connectivity_profile(id=instance_id) - return new_instance - - def get_feature_profile(self, id: str) -> PortalFeatureProfile: - data = self._get(uri=f"{self.base_v1}feature-profile/{id}") - if data: - return PortalFeatureProfile(**data) - else: - return None - - def get_location(self, id: str) -> PortalLocation: - data = self._get(uri=f"{self.base_v1}location/{id}") - if data: - location = PortalLocation(**data) - if isinstance(location.onmsInstanceId, str): - location.onmsInstanceId = self.get_instance(location.onmsInstanceId) - if isinstance(location.minionFeatureProfileId, str): - location.minionFeatureProfileId = self.get_feature_profile( - location.minionFeatureProfileId - ) - if isinstance(location.connectivityProfileId, str): - location.connectivityProfileId = self.get_connectivity_profile( - location.connectivityProfileId - ) - return location - else: - return None - - def create_location(self, location: PortalLocationCreate) -> PortalLocation: - instance_id = self._post(uri=f"{self.base_v1}location", json=location.to_dict()) - new_instance = self.get_location(id=instance_id) - return new_instance diff --git a/pyonms/portal/models.py b/pyonms/portal/models.py deleted file mode 100644 index fb93d5b..0000000 --- a/pyonms/portal/models.py +++ /dev/null @@ -1,270 +0,0 @@ -# portal.models.py - -from dataclasses import dataclass, field -from datetime import datetime -from typing import List, Optional, Union - -from pyonms.models.exceptions import InvalidValueError - -from pyonms.utils import convert_time - -APPLIANCE_TYPE = ["HARDWARE", "VIRTUAL"] -BROKER_TYPE = ["JMS", "KAFKA"] -MINION_STATUS = ["UP", "DOWN", "GRACE_PERIOD", "UNKNOWN"] -ONMS_STATUS = ["UP", "DOWN", "GRACE_PERIOD", "UNKNOWN"] - - -@dataclass -class PortalBrokerConfig: - type: str - - def __post_init__(self): - if self.type not in BROKER_TYPE: - raise InvalidValueError( - name="brokerType", value=self.type, valid=BROKER_TYPE - ) - - -@dataclass -class PortalBrokerKafka(PortalBrokerConfig): - bootstrapServers: List[str] = field(default_factory=list) - - def to_dict(self) -> dict: - payload = {"type": self.type, "bootstrapServers": self.bootstrapServers} - return payload - - -@dataclass -class PortalBrokerJms(PortalBrokerConfig): - url: str - user: str - password: str - - def to_dict(self) -> dict: - payload = { - "type": self.type, - "url": self.url, - "user": self.user, - "password": self.password, - } - return payload - - -@dataclass -class PortalHttpConfig: - url: str - user: str - password: str - - def to_dict(self) -> dict: - payload = {"url": self.url, "user": self.user, "password": self.password} - return payload - - -@dataclass -class PortalInstance: - id: str - name: str - - -@dataclass -class PortalInstanceCreate: - name: str - - def to_dict(self) -> dict: - payload = {"name": self.name} - return payload - - -@dataclass(repr=False) -class PortalFeatureProfile: - id: str - name: str - onmsInstance: PortalInstance - - def __post_init__(self): - if isinstance(self.onmsInstance, dict): - self.onmsInstance = PortalInstance(**self.onmsInstance) - - def __repr__(self): - return f"PortalFeatureProfile(id='{self.id}', name='{self.name}', onmsInstance={self.onmsInstance.name})" - - -@dataclass(repr=False) -class PortalConnectivityProfile: - id: str - name: str - onmsInstance: PortalInstance - - def __post_init__(self): - if isinstance(self.onmsInstance, dict): - self.onmsInstance = PortalInstance(**self.onmsInstance) - - def __repr__(self): - return f"PortalConnectivityProfile(id='{self.id}', name='{self.name}', onmsInstance={self.onmsInstance.name})" - - -@dataclass -class PortalConnectivityProfileCreate: - name: str - onmsInstance: PortalInstance - httpConfig: PortalHttpConfig - brokerConfig: Union[PortalBrokerJms, PortalBrokerKafka] - - def to_dict(self) -> dict: - payload = { - "name": self.name, - "httpConfig": self.httpConfig.to_dict(), - "brokerConfig": self.brokerConfig.to_dict(), - } - if isinstance(self.onmsInstance, str): - payload["onmsInstanceId"] = self.onmsInstance - elif isinstance(self.onmsInstance, PortalInstance): - payload["onmsInstanceId"] = self.onmsInstance.id - return payload - - -@dataclass -class PortalLocation: - id: str - name: str - onmsInstanceId: PortalInstance - connectivityProfileId: PortalConnectivityProfile - minionFeatureProfileId: PortalFeatureProfile = None - - -@dataclass -class PortalLocationCreate: - name: str - onmsInstance: PortalInstance - connectivityProfile: PortalConnectivityProfile - minionFeatureProfile: PortalFeatureProfile = None - - def to_dict(self) -> dict: - payload = {"name": self.name} - if isinstance(self.onmsInstance, str): - payload["onmsInstanceId"] = self.onmsInstance - elif isinstance(self.onmsInstance, PortalInstance): - payload["onmsInstanceId"] = self.onmsInstance.id - if isinstance(self.connectivityProfile, str): - payload["connectivityProfileId"] = self.connectivityProfile - elif isinstance(self.connectivityProfile, PortalConnectivityProfile): - payload["connectivityProfileId"] = self.connectivityProfile.id - if self.minionFeatureProfile: - if isinstance(self.minionFeatureProfile, str): - payload["minionFeatureProfileId"] = self.minionFeatureProfile - elif isinstance(self.minionFeatureProfile, PortalFeatureProfile): - payload["minionFeatureProfileID"] = self.minionFeatureProfile.id - return payload - - -@dataclass -class PortalMinionProfile: - id: str - name: str - processEnvConfigId: str - - -@dataclass -class PortalMinion: - locationId: Union[str, PortalLocation] - minionProfileId: Optional[Union[str, PortalMinionProfile]] = None - - def to_dict(self) -> dict: - payload = {} - if isinstance(self.locationId, str): - payload["locationId"] = self.locationId - elif isinstance(self.locationId, PortalLocation): - payload["locationId"] = self.locationId.id - if isinstance(self.minionProfileId, str): - payload["minionProfileId"] = self.minionProfileId - elif isinstance(self.minionProfileId, PortalMinionProfile): - payload["minionProfileId"] = self.minionProfileId.id - return payload - - -@dataclass -class PortalSubscription: - id: str - count: int - expiry: datetime - state: str - - def __post_init__(self): - if isinstance(self.expiry, int): - self.expiry = convert_time(self.expiry) - - -@dataclass(repr=False) -class PortalAppliance: - id: str - label: str - type: str - applianceProfileId: str - minion: PortalMinion - subscriptionId: PortalSubscription - geoLocationLabel: str = None - latitude: float = None - longitude: float = None - - def __post_init__(self): - if self.type not in APPLIANCE_TYPE: - raise InvalidValueError(name="type", value=self.type, valid=APPLIANCE_TYPE) - if isinstance(self.minion, dict): - self.minion = PortalMinion(**self.minion) - - def __repr__(self): - return f"PortalAppliance(label={self.label}, id={self.id}" - - def to_dict(self) -> dict: - payload = { - "id": self.id, - "label": self.label, - "type": self.type, - "geoLocationLabel": self.geoLocationLabel, - "latitude": self.latitude, - "longitude": self.longitude, - "applianceProfileId": self.applianceProfileId, - "minion": self.minion.to_dict(), - "subscriptionId": self.subscriptionId, - } - return payload - - -@dataclass -class PortalApplianceProfile: - id: str - name: str - - -@dataclass -class PortalApplianceStatus: - connected: bool - minionStatus: str - onmsStatus: str - - def __post_init__(self): - if self.minionStatus not in MINION_STATUS: - raise InvalidValueError( - name="minionStatus", value=self.minionStatus, valid=MINION_STATUS - ) - if self.onmsStatus not in ONMS_STATUS: - raise InvalidValueError( - name="onmsStatus", value=self.onmsStatus, valid=ONMS_STATUS - ) - - -@dataclass -class PortalIpInfo: - interfaceName: str - ipAddresses: list = field(default_factory=list) - - -@dataclass -class PortalAppliancePlatformInfo: - hostname: str - ipInfo: PortalIpInfo - - def __post_init__(self): - if isinstance(self.ipInfo, dict): - self.ipInfo = PortalIpInfo(**self.ipInfo)