From 64313093e0cbd8a066f933e876f435a90a4edae8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:51:05 +0000 Subject: [PATCH 1/6] Initial plan From fe72cdea30747a34fd3fcb9cdf3bbf94d73706d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 12:00:00 +0000 Subject: [PATCH 2/6] Add Device42 to Infrahub sync adapter and example Co-authored-by: BeArchiTek <1334310+BeArchiTek@users.noreply.github.com> --- examples/device42_to_infrahub/README.md | 147 ++++++++++++++++++ examples/device42_to_infrahub/config.yml | 140 +++++++++++++++++ .../device42_to_infrahub/device42/__init__.py | 1 + .../device42/sync_adapter.py | 28 ++++ .../device42/sync_models.py | 111 +++++++++++++ .../device42_to_infrahub/infrahub/__init__.py | 1 + .../infrahub/sync_adapter.py | 28 ++++ .../infrahub/sync_models.py | 111 +++++++++++++ infrahub_sync/adapters/device42.py | 71 +++++++++ infrahub_sync/adapters/genericrestapi.py | 5 + 10 files changed, 643 insertions(+) create mode 100644 examples/device42_to_infrahub/README.md create mode 100644 examples/device42_to_infrahub/config.yml create mode 100644 examples/device42_to_infrahub/device42/__init__.py create mode 100644 examples/device42_to_infrahub/device42/sync_adapter.py create mode 100644 examples/device42_to_infrahub/device42/sync_models.py create mode 100644 examples/device42_to_infrahub/infrahub/__init__.py create mode 100644 examples/device42_to_infrahub/infrahub/sync_adapter.py create mode 100644 examples/device42_to_infrahub/infrahub/sync_models.py create mode 100644 infrahub_sync/adapters/device42.py diff --git a/examples/device42_to_infrahub/README.md b/examples/device42_to_infrahub/README.md new file mode 100644 index 0000000..7a9caae --- /dev/null +++ b/examples/device42_to_infrahub/README.md @@ -0,0 +1,147 @@ +# Device42 to Infrahub Sync + +This example demonstrates how to synchronize data from Device42 to Infrahub using the infrahub-sync framework. + +## Overview + +Device42 is a comprehensive IT asset management and documentation solution. This sync integration allows you to: + +- Import organizations (customers) from Device42 as organizations in Infrahub +- Sync locations (buildings and rooms) as location hierarchies +- Transfer device inventory including racks, hardware models, and devices +- Map network interfaces from Device42 IP records + +## Configuration + +### Device42 Settings + +Update the `config.yml` file with your Device42 instance details: + +```yaml +source: + name: device42 + settings: + url: "https://your-device42-instance.com" + username: "your-username" + password: "your-password" + verify_ssl: true +``` + +### Environment Variables + +You can also configure the connection using environment variables: + +```bash +export DEVICE42_URL="https://your-device42-instance.com" +export DEVICE42_USERNAME="your-username" +export DEVICE42_PASSWORD="your-password" +``` + +### Infrahub Settings + +Configure your Infrahub destination: + +```yaml +destination: + name: infrahub + settings: + url: "http://localhost:8000" +``` + +## Data Mapping + +This example maps the following Device42 objects to Infrahub: + +| Device42 Object | Infrahub Object | Notes | +|----------------|----------------|-------| +| Customers | OrganizationGeneric | Device42 customer records | +| Buildings | LocationGeneric | Building-type locations | +| Rooms | LocationGeneric | Room-type locations | +| Racks | InfraRack | Physical rack infrastructure | +| Hardware Models | ChoiceDeviceType | Device type definitions | +| Devices | InfraDevice | Network devices and servers | +| IP Records (with labels) | InfraInterfaceL2L3 | Network interfaces | + +## API Endpoints Used + +The sync utilizes these Device42 API v2.0 endpoints: + +- `/api/2.0/customers/` - Customer/organization data +- `/api/2.0/buildings/` - Building location data +- `/api/2.0/rooms/` - Room location data +- `/api/2.0/racks/` - Rack infrastructure +- `/api/2.0/hardwares/` - Hardware model definitions +- `/api/2.0/devices/` - Device inventory +- `/api/2.0/ips/` - IP address and interface data + +## Running the Sync + +1. Configure your settings in `config.yml` or environment variables +2. Ensure Infrahub is running and accessible +3. Run the synchronization: + +```bash +infrahub-sync sync examples/device42_to_infrahub/config.yml +``` + +## Customization + +### Filters + +You can add filters to limit which objects are synchronized. For example, to only sync devices from a specific customer: + +```yaml +- name: InfraDevice + mapping: devices + filters: + - field: customer + operation: "equals" + value: "your-customer-name" +``` + +### Transforms + +Transform data during synchronization. For example, to normalize device names: + +```yaml +transforms: + - field: name + expression: "{{ name.lower().replace(' ', '-') if name else '' }}" +``` + +## Authentication + +Device42 uses HTTP Basic Authentication. Ensure your user account has the necessary permissions to read the required objects: + +- Customer management +- Location management +- Device management +- Network management + +## Troubleshooting + +### Connection Issues + +1. Verify your Device42 URL is correct and accessible +2. Check that your credentials are valid +3. Ensure SSL certificate verification settings are appropriate + +### Data Issues + +1. Check the Device42 API documentation for field availability +2. Verify that referenced objects exist (e.g., racks must exist before devices) +3. Review the sync order in `config.yml` - dependencies should be synced first + +### API Limits + +Device42 may have API rate limits. If you encounter issues: + +1. Add delays between requests in the sync configuration +2. Consider using smaller batch sizes +3. Monitor Device42 system resources during large syncs + +## Support + +For Device42-specific questions, consult the Device42 API documentation at: https://api.device42.com/ + +For infrahub-sync framework questions, see the main documentation. \ No newline at end of file diff --git a/examples/device42_to_infrahub/config.yml b/examples/device42_to_infrahub/config.yml new file mode 100644 index 0000000..d106c42 --- /dev/null +++ b/examples/device42_to_infrahub/config.yml @@ -0,0 +1,140 @@ +--- +name: from-device42 + +source: + name: device42 + settings: + url: "https://device42.example.com" + username: "your-username" + password: "your-password" + verify_ssl: true + +destination: + name: infrahub + settings: + url: "http://localhost:8000" + +order: [ + "BuiltinTag", + "RoleGeneric", + "OrganizationGeneric", + "LocationGeneric", + "InfraRack", + "ChoiceDeviceType", + "InfraDevice", + "InfraInterfaceL2L3", +] + +schema_mapping: + # Organizations (Customers in Device42) + - name: OrganizationGeneric + mapping: customers + identifiers: ["name"] + fields: + - name: name + mapping: name + - name: description + mapping: notes + + # Locations (Buildings in Device42) + - name: LocationGeneric + mapping: buildings + identifiers: ["name"] + fields: + - name: name + mapping: name + - name: description + mapping: notes + - name: type + static: "Building" + + # Locations (Rooms in Device42) + - name: LocationGeneric + mapping: rooms + identifiers: ["name"] + fields: + - name: name + mapping: name + - name: description + mapping: notes + - name: type + static: "Room" + + # Racks + - name: InfraRack + mapping: racks + identifiers: ["name", "location"] + fields: + - name: name + mapping: name + - name: location + mapping: room + reference: LocationGeneric + - name: height + mapping: size + - name: serial_number + mapping: serial_no + - name: asset_tag + mapping: asset_no + + # Hardware Models (Device Types) + - name: ChoiceDeviceType + mapping: hardwares + identifiers: ["name", "manufacturer"] + fields: + - name: name + mapping: name + - name: part_number + mapping: part_no + - name: height + mapping: size + - name: manufacturer + mapping: manufacturer + reference: OrganizationGeneric + + # Devices + - name: InfraDevice + identifiers: ["name"] + mapping: devices + fields: + - name: name + mapping: name + - name: serial_number + mapping: serial_no + - name: asset_tag + mapping: asset_no + - name: description + mapping: notes + - name: model + mapping: hardware + reference: ChoiceDeviceType + - name: organization + mapping: customer + reference: OrganizationGeneric + - name: location + mapping: room + reference: LocationGeneric + - name: rack + mapping: rack + reference: InfraRack + transforms: + - field: name + expression: "{{ name.lower() if name else '' }}" + + # Network Interfaces (Device42 IP addresses often represent interfaces) + - name: InfraInterfaceL2L3 + identifiers: ["device", "name"] + mapping: ips + fields: + - name: name + mapping: label + - name: description + mapping: notes + - name: device + mapping: device + reference: InfraDevice + # Only include IPs that have interface-like labels + filters: + - field: label + operation: "exists" + value: true \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/__init__.py b/examples/device42_to_infrahub/device42/__init__.py new file mode 100644 index 0000000..748554d --- /dev/null +++ b/examples/device42_to_infrahub/device42/__init__.py @@ -0,0 +1 @@ +# Device42 Sync Package \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/sync_adapter.py b/examples/device42_to_infrahub/device42/sync_adapter.py new file mode 100644 index 0000000..a2ed627 --- /dev/null +++ b/examples/device42_to_infrahub/device42/sync_adapter.py @@ -0,0 +1,28 @@ +from infrahub_sync.adapters.device42 import Device42Adapter + +from .sync_models import ( + BuiltinTag, + ChoiceDeviceType, + InfraDevice, + InfraInterfaceL2L3, + InfraRack, + LocationGeneric, + OrganizationGeneric, + RoleGeneric, +) + + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class Device42Sync(Device42Adapter): + BuiltinTag = BuiltinTag + ChoiceDeviceType = ChoiceDeviceType + InfraDevice = InfraDevice + InfraInterfaceL2L3 = InfraInterfaceL2L3 + InfraRack = InfraRack + LocationGeneric = LocationGeneric + OrganizationGeneric = OrganizationGeneric + RoleGeneric = RoleGeneric \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/sync_models.py b/examples/device42_to_infrahub/device42/sync_models.py new file mode 100644 index 0000000..551e49f --- /dev/null +++ b/examples/device42_to_infrahub/device42/sync_models.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from typing import Any + +from infrahub_sync.adapters.device42 import Device42Model + + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class BuiltinTag(Device42Model): + _modelname = "BuiltinTag" + _identifiers = ("name",) + _attributes = ("description",) + description: str | None = None + name: str + + local_id: str | None = None + local_data: Any | None = None + + +class ChoiceDeviceType(Device42Model): + _modelname = "ChoiceDeviceType" + _identifiers = ("name", "manufacturer") + _attributes = ("part_number", "height") + name: str + manufacturer: str + part_number: str | None = None + height: int | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraDevice(Device42Model): + _modelname = "InfraDevice" + _identifiers = ("name",) + _attributes = ("serial_number", "asset_tag", "description", "model", "organization", "location", "rack") + name: str + serial_number: str | None = None + asset_tag: str | None = None + description: str | None = None + model: str | None = None + organization: str | None = None + location: str | None = None + rack: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraInterfaceL2L3(Device42Model): + _modelname = "InfraInterfaceL2L3" + _identifiers = ("device", "name") + _attributes = ("description",) + device: str + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraRack(Device42Model): + _modelname = "InfraRack" + _identifiers = ("name", "location") + _attributes = ("height", "serial_number", "asset_tag") + name: str + location: str + height: int | None = None + serial_number: str | None = None + asset_tag: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class LocationGeneric(Device42Model): + _modelname = "LocationGeneric" + _identifiers = ("name",) + _attributes = ("description", "type") + name: str + description: str | None = None + type: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class OrganizationGeneric(Device42Model): + _modelname = "OrganizationGeneric" + _identifiers = ("name",) + _attributes = ("description",) + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class RoleGeneric(Device42Model): + _modelname = "RoleGeneric" + _identifiers = ("name",) + _attributes = ("description",) + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None \ No newline at end of file diff --git a/examples/device42_to_infrahub/infrahub/__init__.py b/examples/device42_to_infrahub/infrahub/__init__.py new file mode 100644 index 0000000..e9cfaa4 --- /dev/null +++ b/examples/device42_to_infrahub/infrahub/__init__.py @@ -0,0 +1 @@ +# Infrahub Sync Package \ No newline at end of file diff --git a/examples/device42_to_infrahub/infrahub/sync_adapter.py b/examples/device42_to_infrahub/infrahub/sync_adapter.py new file mode 100644 index 0000000..ea7102f --- /dev/null +++ b/examples/device42_to_infrahub/infrahub/sync_adapter.py @@ -0,0 +1,28 @@ +from infrahub_sync.adapters.infrahub import InfrahubAdapter + +from .sync_models import ( + BuiltinTag, + ChoiceDeviceType, + InfraDevice, + InfraInterfaceL2L3, + InfraRack, + LocationGeneric, + OrganizationGeneric, + RoleGeneric, +) + + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class InfrahubSync(InfrahubAdapter): + BuiltinTag = BuiltinTag + ChoiceDeviceType = ChoiceDeviceType + InfraDevice = InfraDevice + InfraInterfaceL2L3 = InfraInterfaceL2L3 + InfraRack = InfraRack + LocationGeneric = LocationGeneric + OrganizationGeneric = OrganizationGeneric + RoleGeneric = RoleGeneric \ No newline at end of file diff --git a/examples/device42_to_infrahub/infrahub/sync_models.py b/examples/device42_to_infrahub/infrahub/sync_models.py new file mode 100644 index 0000000..fc336d7 --- /dev/null +++ b/examples/device42_to_infrahub/infrahub/sync_models.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from typing import Any + +from infrahub_sync.adapters.infrahub import InfrahubModel + + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class BuiltinTag(InfrahubModel): + _modelname = "BuiltinTag" + _identifiers = ("name",) + _attributes = ("description",) + description: str | None = None + name: str + + local_id: str | None = None + local_data: Any | None = None + + +class ChoiceDeviceType(InfrahubModel): + _modelname = "ChoiceDeviceType" + _identifiers = ("name", "manufacturer") + _attributes = ("part_number", "height") + name: str + manufacturer: str + part_number: str | None = None + height: int | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraDevice(InfrahubModel): + _modelname = "InfraDevice" + _identifiers = ("name",) + _attributes = ("serial_number", "asset_tag", "description", "model", "organization", "location", "rack") + name: str + serial_number: str | None = None + asset_tag: str | None = None + description: str | None = None + model: str | None = None + organization: str | None = None + location: str | None = None + rack: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraInterfaceL2L3(InfrahubModel): + _modelname = "InfraInterfaceL2L3" + _identifiers = ("device", "name") + _attributes = ("description",) + device: str + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class InfraRack(InfrahubModel): + _modelname = "InfraRack" + _identifiers = ("name", "location") + _attributes = ("height", "serial_number", "asset_tag") + name: str + location: str + height: int | None = None + serial_number: str | None = None + asset_tag: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class LocationGeneric(InfrahubModel): + _modelname = "LocationGeneric" + _identifiers = ("name",) + _attributes = ("description", "type") + name: str + description: str | None = None + type: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class OrganizationGeneric(InfrahubModel): + _modelname = "OrganizationGeneric" + _identifiers = ("name",) + _attributes = ("description",) + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None + + +class RoleGeneric(InfrahubModel): + _modelname = "RoleGeneric" + _identifiers = ("name",) + _attributes = ("description",) + name: str + description: str | None = None + + local_id: str | None = None + local_data: Any | None = None \ No newline at end of file diff --git a/infrahub_sync/adapters/device42.py b/infrahub_sync/adapters/device42.py new file mode 100644 index 0000000..c2144e3 --- /dev/null +++ b/infrahub_sync/adapters/device42.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +from infrahub_sync import ( + SyncAdapter, + SyncConfig, +) + +from .genericrestapi import GenericrestapiAdapter, GenericrestapiModel + +if TYPE_CHECKING: + from collections.abc import Mapping + + from diffsync import Adapter + + +class Device42Adapter(GenericrestapiAdapter): + """Device42 adapter that extends the generic REST API adapter.""" + + def __init__(self, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs) -> None: + # Set Device42-specific defaults + settings = adapter.settings or {} + + # Apply Device42-specific defaults if not specified + if "auth_method" not in settings: + settings["auth_method"] = "basic" + if "api_endpoint" not in settings: + settings["api_endpoint"] = "/api/2.0" + if "url_env_vars" not in settings: + settings["url_env_vars"] = ["DEVICE42_ADDRESS", "DEVICE42_URL"] + if "username_env_vars" not in settings: + settings["username_env_vars"] = ["DEVICE42_USERNAME"] + if "password_env_vars" not in settings: + settings["password_env_vars"] = ["DEVICE42_PASSWORD"] + + # Device42 typically returns results with pagination info + if "response_key_pattern" not in settings: + settings["response_key_pattern"] = "{resource}" + + # Create a new adapter with updated settings + updated_adapter = SyncAdapter(name=adapter.name, settings=settings) + + super().__init__(target=target, adapter=updated_adapter, config=config, adapter_type="Device42", **kwargs) + + +class Device42Model(GenericrestapiModel): + """Device42 model that extends the generic REST API model.""" + + @classmethod + def create( + cls, + adapter: Adapter, + ids: Mapping[Any, Any], + attrs: Mapping[Any, Any], + ) -> Self | None: + # TODO: To implement + return super().create(adapter=adapter, ids=ids, attrs=attrs) + + def update(self, attrs: dict) -> Self | None: + # TODO: To implement + return super().update(attrs=attrs) + + +# Aliases for consistency +Device42Sync = Device42Adapter \ No newline at end of file diff --git a/infrahub_sync/adapters/genericrestapi.py b/infrahub_sync/adapters/genericrestapi.py index 8be742e..08abea7 100644 --- a/infrahub_sync/adapters/genericrestapi.py +++ b/infrahub_sync/adapters/genericrestapi.py @@ -303,3 +303,8 @@ def create( def update(self, attrs: dict) -> Self | None: # TODO: To implement return super().update(attrs=attrs) + + +# Aliases for backward compatibility and consistency +GenericRestApiAdapter = GenericrestapiAdapter +GenericRestApiModel = GenericrestapiModel From e2bdbfb7934c0e48c520b3559d6ab6401ecae9cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 12:13:37 +0000 Subject: [PATCH 3/6] Update Device42 adapter with demo credentials and enhanced API handling Co-authored-by: BeArchiTek <1334310+BeArchiTek@users.noreply.github.com> --- examples/device42_to_infrahub/README.md | 57 ++++++++++++++++- examples/device42_to_infrahub/config.yml | 9 +-- infrahub_sync/adapters/device42.py | 81 +++++++++++++++++++++++- infrahub_sync/adapters/genericrestapi.py | 16 +++++ 4 files changed, 155 insertions(+), 8 deletions(-) diff --git a/examples/device42_to_infrahub/README.md b/examples/device42_to_infrahub/README.md index 7a9caae..b85d3a3 100644 --- a/examples/device42_to_infrahub/README.md +++ b/examples/device42_to_infrahub/README.md @@ -21,22 +21,43 @@ Update the `config.yml` file with your Device42 instance details: source: name: device42 settings: - url: "https://your-device42-instance.com" + url: "http://your-device42-instance.com" username: "your-username" password: "your-password" verify_ssl: true ``` +For testing and demonstration, you can use the Device42 demo server: + +```yaml +source: + name: device42 + settings: + url: "http://swaggerdemo.device42.com" + username: "guest" + password: "device42_rocks!" + verify_ssl: false + debug: true # Enable debug logging to see API response structure +``` + ### Environment Variables You can also configure the connection using environment variables: ```bash -export DEVICE42_URL="https://your-device42-instance.com" +export DEVICE42_URL="http://your-device42-instance.com" export DEVICE42_USERNAME="your-username" export DEVICE42_PASSWORD="your-password" ``` +For testing with the demo server: + +```bash +export DEVICE42_URL="http://swaggerdemo.device42.com" +export DEVICE42_USERNAME="guest" +export DEVICE42_PASSWORD="device42_rocks!" +``` + ### Infrahub Settings Configure your Infrahub destination: @@ -118,6 +139,38 @@ Device42 uses HTTP Basic Authentication. Ensure your user account has the necess - Device management - Network management +## Device42-Specific Features + +### Response Format Handling + +Device42 API returns data in a structured format: + +```json +{ + "objects": [...], + "total_count": 123, + "limit": 100, + "offset": 0 +} +``` + +The adapter automatically handles this format and extracts the data from the `objects` key. + +### Pagination Support + +Device42 APIs return paginated results. The adapter includes methods to handle pagination automatically, though the basic implementation loads the first page. For large datasets, the adapter will log information about remaining records. + +### Debug Mode + +Enable debug mode to see detailed information about API responses: + +```yaml +settings: + debug: true +``` + +This will show the available keys in each API response and help troubleshoot data mapping issues. + ## Troubleshooting ### Connection Issues diff --git a/examples/device42_to_infrahub/config.yml b/examples/device42_to_infrahub/config.yml index d106c42..589653c 100644 --- a/examples/device42_to_infrahub/config.yml +++ b/examples/device42_to_infrahub/config.yml @@ -4,10 +4,11 @@ name: from-device42 source: name: device42 settings: - url: "https://device42.example.com" - username: "your-username" - password: "your-password" - verify_ssl: true + url: "http://swaggerdemo.device42.com" + username: "guest" + password: "device42_rocks!" + verify_ssl: false + # debug: true # Uncomment to enable debug logging destination: name: infrahub diff --git a/infrahub_sync/adapters/device42.py b/infrahub_sync/adapters/device42.py index c2144e3..14eeccd 100644 --- a/infrahub_sync/adapters/device42.py +++ b/infrahub_sync/adapters/device42.py @@ -10,6 +10,7 @@ from infrahub_sync import ( SyncAdapter, SyncConfig, + SchemaMappingModel, ) from .genericrestapi import GenericrestapiAdapter, GenericrestapiModel @@ -39,15 +40,91 @@ def __init__(self, target: str, adapter: SyncAdapter, config: SyncConfig, **kwar if "password_env_vars" not in settings: settings["password_env_vars"] = ["DEVICE42_PASSWORD"] - # Device42 typically returns results with pagination info + # Device42 API returns results in an "objects" key for most endpoints if "response_key_pattern" not in settings: - settings["response_key_pattern"] = "{resource}" + settings["response_key_pattern"] = "objects" # Create a new adapter with updated settings updated_adapter = SyncAdapter(name=adapter.name, settings=settings) super().__init__(target=target, adapter=updated_adapter, config=config, adapter_type="Device42", **kwargs) + def _extract_objects_from_response( + self, + response_data: dict[str, Any], + resource_name: str, + element: SchemaMappingModel, + ) -> list[dict[str, Any]]: + """ + Extract objects from Device42 API response data. + + Device42 API typically returns data in this format: + { + "objects": [...], + "total_count": 123, + "limit": 100, + "offset": 0 + } + """ + # Device42 typically uses "objects" as the key for the data array + objs = response_data.get("objects", []) + + # Fallback to generic extraction if "objects" key is not present + if not objs: + objs = super()._extract_objects_from_response(response_data, resource_name, element) + + # Handle pagination if needed (Device42 returns total_count for pagination info) + total_count = response_data.get("total_count", len(objs)) + limit = response_data.get("limit", len(objs)) + offset = response_data.get("offset", 0) + + # For now, we'll handle the first page. In the future, this could be extended + # to handle pagination automatically by making additional requests + if total_count > len(objs) and offset + limit < total_count: + # Log that there are more results available + remaining = total_count - (offset + len(objs)) + print(f"Device42: Retrieved {len(objs)} of {total_count} {resource_name} records. {remaining} remaining.") + + return objs + + def _fetch_paginated_data(self, endpoint: str, params: dict[str, Any] | None = None) -> list[dict[str, Any]]: + """ + Fetch all data from a paginated Device42 endpoint. + + This method handles Device42's pagination by making multiple requests + if necessary to retrieve all available data. + """ + all_objects = [] + offset = 0 + limit = 100 # Device42 default limit + + if params is None: + params = {} + + while True: + # Add pagination parameters + paginated_params = params.copy() + paginated_params.update({"offset": offset, "limit": limit}) + + try: + response_data = self.client.get(endpoint=endpoint, params=paginated_params) + objects = response_data.get("objects", []) + total_count = response_data.get("total_count", len(objects)) + + all_objects.extend(objects) + + # Check if we've retrieved all objects + if len(objects) < limit or offset + len(objects) >= total_count: + break + + offset += limit + + except Exception as exc: + msg = f"Error fetching paginated data from Device42 API: {exc!s}" + raise ValueError(msg) from exc + + return all_objects + class Device42Model(GenericrestapiModel): """Device42 model that extends the generic REST API model.""" diff --git a/infrahub_sync/adapters/genericrestapi.py b/infrahub_sync/adapters/genericrestapi.py index 08abea7..6e5f080 100644 --- a/infrahub_sync/adapters/genericrestapi.py +++ b/infrahub_sync/adapters/genericrestapi.py @@ -201,8 +201,24 @@ def _extract_objects_from_response( else default_key ) + # Debug information for API response structure + debug_mode = self.settings.get("debug", False) + if debug_mode: + print(f"API Response keys for {resource_name}: {list(response_data.keys())}") + print(f"Looking for data using key: {response_key}") + # Try to get data using the response key objs = response_data.get(response_key, response_data.get(resource_name, {})) + + # If no data found with expected key, try common alternative keys + if not objs and response_key != "objects": + # Try some common API response keys + for alt_key in ["objects", "data", "results", "items"]: + if alt_key in response_data: + objs = response_data[alt_key] + if debug_mode: + print(f"Found data using alternative key: {alt_key}") + break # Handle different response formats if isinstance(objs, dict): From 571d14ebe785acb44220e0a34a870e68058b74a4 Mon Sep 17 00:00:00 2001 From: Bearchitek Date: Tue, 16 Sep 2025 15:23:55 +0200 Subject: [PATCH 4/6] cleanup Device42 idea --- examples/device42_to_infrahub/README.md | 200 ------------------ examples/device42_to_infrahub/config.yml | 123 ++--------- .../device42_to_infrahub/device42/__init__.py | 1 - .../device42/sync_adapter.py | 28 --- .../device42/sync_models.py | 111 ---------- .../genericrestapi/__init__.py | 0 .../genericrestapi/sync_adapter.py | 18 ++ .../genericrestapi/sync_models.py | 39 ++++ .../device42_to_infrahub/infrahub/__init__.py | 1 - .../infrahub/sync_adapter.py | 18 +- .../infrahub/sync_models.py | 90 +------- infrahub_sync/adapters/device42.py | 148 ------------- infrahub_sync/adapters/genericrestapi.py | 16 -- infrahub_sync/adapters/rest_api_client.py | 4 +- 14 files changed, 90 insertions(+), 707 deletions(-) delete mode 100644 examples/device42_to_infrahub/README.md delete mode 100644 examples/device42_to_infrahub/device42/__init__.py delete mode 100644 examples/device42_to_infrahub/device42/sync_adapter.py delete mode 100644 examples/device42_to_infrahub/device42/sync_models.py create mode 100644 examples/device42_to_infrahub/genericrestapi/__init__.py create mode 100644 examples/device42_to_infrahub/genericrestapi/sync_adapter.py create mode 100644 examples/device42_to_infrahub/genericrestapi/sync_models.py delete mode 100644 infrahub_sync/adapters/device42.py diff --git a/examples/device42_to_infrahub/README.md b/examples/device42_to_infrahub/README.md deleted file mode 100644 index b85d3a3..0000000 --- a/examples/device42_to_infrahub/README.md +++ /dev/null @@ -1,200 +0,0 @@ -# Device42 to Infrahub Sync - -This example demonstrates how to synchronize data from Device42 to Infrahub using the infrahub-sync framework. - -## Overview - -Device42 is a comprehensive IT asset management and documentation solution. This sync integration allows you to: - -- Import organizations (customers) from Device42 as organizations in Infrahub -- Sync locations (buildings and rooms) as location hierarchies -- Transfer device inventory including racks, hardware models, and devices -- Map network interfaces from Device42 IP records - -## Configuration - -### Device42 Settings - -Update the `config.yml` file with your Device42 instance details: - -```yaml -source: - name: device42 - settings: - url: "http://your-device42-instance.com" - username: "your-username" - password: "your-password" - verify_ssl: true -``` - -For testing and demonstration, you can use the Device42 demo server: - -```yaml -source: - name: device42 - settings: - url: "http://swaggerdemo.device42.com" - username: "guest" - password: "device42_rocks!" - verify_ssl: false - debug: true # Enable debug logging to see API response structure -``` - -### Environment Variables - -You can also configure the connection using environment variables: - -```bash -export DEVICE42_URL="http://your-device42-instance.com" -export DEVICE42_USERNAME="your-username" -export DEVICE42_PASSWORD="your-password" -``` - -For testing with the demo server: - -```bash -export DEVICE42_URL="http://swaggerdemo.device42.com" -export DEVICE42_USERNAME="guest" -export DEVICE42_PASSWORD="device42_rocks!" -``` - -### Infrahub Settings - -Configure your Infrahub destination: - -```yaml -destination: - name: infrahub - settings: - url: "http://localhost:8000" -``` - -## Data Mapping - -This example maps the following Device42 objects to Infrahub: - -| Device42 Object | Infrahub Object | Notes | -|----------------|----------------|-------| -| Customers | OrganizationGeneric | Device42 customer records | -| Buildings | LocationGeneric | Building-type locations | -| Rooms | LocationGeneric | Room-type locations | -| Racks | InfraRack | Physical rack infrastructure | -| Hardware Models | ChoiceDeviceType | Device type definitions | -| Devices | InfraDevice | Network devices and servers | -| IP Records (with labels) | InfraInterfaceL2L3 | Network interfaces | - -## API Endpoints Used - -The sync utilizes these Device42 API v2.0 endpoints: - -- `/api/2.0/customers/` - Customer/organization data -- `/api/2.0/buildings/` - Building location data -- `/api/2.0/rooms/` - Room location data -- `/api/2.0/racks/` - Rack infrastructure -- `/api/2.0/hardwares/` - Hardware model definitions -- `/api/2.0/devices/` - Device inventory -- `/api/2.0/ips/` - IP address and interface data - -## Running the Sync - -1. Configure your settings in `config.yml` or environment variables -2. Ensure Infrahub is running and accessible -3. Run the synchronization: - -```bash -infrahub-sync sync examples/device42_to_infrahub/config.yml -``` - -## Customization - -### Filters - -You can add filters to limit which objects are synchronized. For example, to only sync devices from a specific customer: - -```yaml -- name: InfraDevice - mapping: devices - filters: - - field: customer - operation: "equals" - value: "your-customer-name" -``` - -### Transforms - -Transform data during synchronization. For example, to normalize device names: - -```yaml -transforms: - - field: name - expression: "{{ name.lower().replace(' ', '-') if name else '' }}" -``` - -## Authentication - -Device42 uses HTTP Basic Authentication. Ensure your user account has the necessary permissions to read the required objects: - -- Customer management -- Location management -- Device management -- Network management - -## Device42-Specific Features - -### Response Format Handling - -Device42 API returns data in a structured format: - -```json -{ - "objects": [...], - "total_count": 123, - "limit": 100, - "offset": 0 -} -``` - -The adapter automatically handles this format and extracts the data from the `objects` key. - -### Pagination Support - -Device42 APIs return paginated results. The adapter includes methods to handle pagination automatically, though the basic implementation loads the first page. For large datasets, the adapter will log information about remaining records. - -### Debug Mode - -Enable debug mode to see detailed information about API responses: - -```yaml -settings: - debug: true -``` - -This will show the available keys in each API response and help troubleshoot data mapping issues. - -## Troubleshooting - -### Connection Issues - -1. Verify your Device42 URL is correct and accessible -2. Check that your credentials are valid -3. Ensure SSL certificate verification settings are appropriate - -### Data Issues - -1. Check the Device42 API documentation for field availability -2. Verify that referenced objects exist (e.g., racks must exist before devices) -3. Review the sync order in `config.yml` - dependencies should be synced first - -### API Limits - -Device42 may have API rate limits. If you encounter issues: - -1. Add delays between requests in the sync configuration -2. Consider using smaller batch sizes -3. Monitor Device42 system resources during large syncs - -## Support - -For Device42-specific questions, consult the Device42 API documentation at: https://api.device42.com/ - -For infrahub-sync framework questions, see the main documentation. \ No newline at end of file diff --git a/examples/device42_to_infrahub/config.yml b/examples/device42_to_infrahub/config.yml index 589653c..bc43bca 100644 --- a/examples/device42_to_infrahub/config.yml +++ b/examples/device42_to_infrahub/config.yml @@ -2,13 +2,14 @@ name: from-device42 source: - name: device42 + name: genericrestapi settings: url: "http://swaggerdemo.device42.com" + auth_method: "basic" username: "guest" password: "device42_rocks!" - verify_ssl: false - # debug: true # Uncomment to enable debug logging + api_endpoint: "/api/1.0" + response_key_pattern: "objects" destination: name: infrahub @@ -17,125 +18,37 @@ destination: order: [ "BuiltinTag", - "RoleGeneric", - "OrganizationGeneric", - "LocationGeneric", - "InfraRack", - "ChoiceDeviceType", - "InfraDevice", - "InfraInterfaceL2L3", + "OrganizationTenant", + "LocationSite", ] schema_mapping: - # Organizations (Customers in Device42) - - name: OrganizationGeneric - mapping: customers + # Builtin Tags (Device42 tags) + - name: BuiltinTag + mapping: tags identifiers: ["name"] fields: - name: name mapping: name - - name: description - mapping: notes - # Locations (Buildings in Device42) - - name: LocationGeneric - mapping: buildings - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: notes - - name: type - static: "Building" - - # Locations (Rooms in Device42) - - name: LocationGeneric - mapping: rooms + # Organizations (Customers in Device42) + - name: OrganizationTenant + mapping: customers identifiers: ["name"] fields: - name: name mapping: name - name: description mapping: notes - - name: type - static: "Room" - # Racks - - name: InfraRack - mapping: racks - identifiers: ["name", "location"] - fields: - - name: name - mapping: name - - name: location - mapping: room - reference: LocationGeneric - - name: height - mapping: size - - name: serial_number - mapping: serial_no - - name: asset_tag - mapping: asset_no - - # Hardware Models (Device Types) - - name: ChoiceDeviceType - mapping: hardwares - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: name - - name: part_number - mapping: part_no - - name: height - mapping: size - - name: manufacturer - mapping: manufacturer - reference: OrganizationGeneric - - # Devices - - name: InfraDevice + # Locations (Buildings in Device42) + - name: LocationSite + mapping: buildings identifiers: ["name"] - mapping: devices fields: - name: name mapping: name - - name: serial_number - mapping: serial_no - - name: asset_tag - mapping: asset_no - - name: description - mapping: notes - - name: model - mapping: hardware - reference: ChoiceDeviceType - - name: organization - mapping: customer - reference: OrganizationGeneric - - name: location - mapping: room - reference: LocationGeneric - - name: rack - mapping: rack - reference: InfraRack - transforms: - - field: name - expression: "{{ name.lower() if name else '' }}" + - name: tags + mapping: tags + reference: BuiltinTag - # Network Interfaces (Device42 IP addresses often represent interfaces) - - name: InfraInterfaceL2L3 - identifiers: ["device", "name"] - mapping: ips - fields: - - name: name - mapping: label - - name: description - mapping: notes - - name: device - mapping: device - reference: InfraDevice - # Only include IPs that have interface-like labels - filters: - - field: label - operation: "exists" - value: true \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/__init__.py b/examples/device42_to_infrahub/device42/__init__.py deleted file mode 100644 index 748554d..0000000 --- a/examples/device42_to_infrahub/device42/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Device42 Sync Package \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/sync_adapter.py b/examples/device42_to_infrahub/device42/sync_adapter.py deleted file mode 100644 index a2ed627..0000000 --- a/examples/device42_to_infrahub/device42/sync_adapter.py +++ /dev/null @@ -1,28 +0,0 @@ -from infrahub_sync.adapters.device42 import Device42Adapter - -from .sync_models import ( - BuiltinTag, - ChoiceDeviceType, - InfraDevice, - InfraInterfaceL2L3, - InfraRack, - LocationGeneric, - OrganizationGeneric, - RoleGeneric, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class Device42Sync(Device42Adapter): - BuiltinTag = BuiltinTag - ChoiceDeviceType = ChoiceDeviceType - InfraDevice = InfraDevice - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraRack = InfraRack - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric \ No newline at end of file diff --git a/examples/device42_to_infrahub/device42/sync_models.py b/examples/device42_to_infrahub/device42/sync_models.py deleted file mode 100644 index 551e49f..0000000 --- a/examples/device42_to_infrahub/device42/sync_models.py +++ /dev/null @@ -1,111 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from infrahub_sync.adapters.device42 import Device42Model - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class BuiltinTag(Device42Model): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - description: str | None = None - name: str - - local_id: str | None = None - local_data: Any | None = None - - -class ChoiceDeviceType(Device42Model): - _modelname = "ChoiceDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("part_number", "height") - name: str - manufacturer: str - part_number: str | None = None - height: int | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraDevice(Device42Model): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("serial_number", "asset_tag", "description", "model", "organization", "location", "rack") - name: str - serial_number: str | None = None - asset_tag: str | None = None - description: str | None = None - model: str | None = None - organization: str | None = None - location: str | None = None - rack: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraInterfaceL2L3(Device42Model): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("device", "name") - _attributes = ("description",) - device: str - name: str - description: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraRack(Device42Model): - _modelname = "InfraRack" - _identifiers = ("name", "location") - _attributes = ("height", "serial_number", "asset_tag") - name: str - location: str - height: int | None = None - serial_number: str | None = None - asset_tag: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class LocationGeneric(Device42Model): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("description", "type") - name: str - description: str | None = None - type: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class OrganizationGeneric(Device42Model): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class RoleGeneric(Device42Model): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: str | None = None - - local_id: str | None = None - local_data: Any | None = None \ No newline at end of file diff --git a/examples/device42_to_infrahub/genericrestapi/__init__.py b/examples/device42_to_infrahub/genericrestapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/device42_to_infrahub/genericrestapi/sync_adapter.py b/examples/device42_to_infrahub/genericrestapi/sync_adapter.py new file mode 100644 index 0000000..37df9d5 --- /dev/null +++ b/examples/device42_to_infrahub/genericrestapi/sync_adapter.py @@ -0,0 +1,18 @@ +from infrahub_sync.adapters.genericrestapi import GenericrestapiAdapter + +from .sync_models import ( + BuiltinTag, + LocationSite, + OrganizationTenant, +) + + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class GenericrestapiSync(GenericrestapiAdapter): + BuiltinTag = BuiltinTag + LocationSite = LocationSite + OrganizationTenant = OrganizationTenant diff --git a/examples/device42_to_infrahub/genericrestapi/sync_models.py b/examples/device42_to_infrahub/genericrestapi/sync_models.py new file mode 100644 index 0000000..7ef3d5a --- /dev/null +++ b/examples/device42_to_infrahub/genericrestapi/sync_models.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import Any, List + +from infrahub_sync.adapters.genericrestapi import GenericrestapiModel + +# ------------------------------------------------------- +# AUTO-GENERATED FILE, DO NOT MODIFY +# This file has been generated with the command `infrahub-sync generate` +# All modifications will be lost the next time you reexecute this command +# ------------------------------------------------------- +class BuiltinTag(GenericrestapiModel): + _modelname = "BuiltinTag" + _identifiers = ("name",) + _attributes = () + name: str + + local_id: str | None = None + local_data: Any | None = None + +class LocationSite(GenericrestapiModel): + _modelname = "LocationSite" + _identifiers = ("name",) + _attributes = ("tags",) + name: str + tags: list[str] | None = [] + + local_id: str | None = None + local_data: Any | None = None + +class OrganizationTenant(GenericrestapiModel): + _modelname = "OrganizationTenant" + _identifiers = ("name",) + _attributes = ("description",) + description: str | None = None + name: str + + local_id: str | None = None + local_data: Any | None = None diff --git a/examples/device42_to_infrahub/infrahub/__init__.py b/examples/device42_to_infrahub/infrahub/__init__.py index e9cfaa4..e69de29 100644 --- a/examples/device42_to_infrahub/infrahub/__init__.py +++ b/examples/device42_to_infrahub/infrahub/__init__.py @@ -1 +0,0 @@ -# Infrahub Sync Package \ No newline at end of file diff --git a/examples/device42_to_infrahub/infrahub/sync_adapter.py b/examples/device42_to_infrahub/infrahub/sync_adapter.py index ea7102f..a1210b3 100644 --- a/examples/device42_to_infrahub/infrahub/sync_adapter.py +++ b/examples/device42_to_infrahub/infrahub/sync_adapter.py @@ -2,13 +2,8 @@ from .sync_models import ( BuiltinTag, - ChoiceDeviceType, - InfraDevice, - InfraInterfaceL2L3, - InfraRack, - LocationGeneric, - OrganizationGeneric, - RoleGeneric, + LocationSite, + OrganizationTenant, ) @@ -19,10 +14,5 @@ # ------------------------------------------------------- class InfrahubSync(InfrahubAdapter): BuiltinTag = BuiltinTag - ChoiceDeviceType = ChoiceDeviceType - InfraDevice = InfraDevice - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraRack = InfraRack - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric \ No newline at end of file + LocationSite = LocationSite + OrganizationTenant = OrganizationTenant diff --git a/examples/device42_to_infrahub/infrahub/sync_models.py b/examples/device42_to_infrahub/infrahub/sync_models.py index fc336d7..e264bb1 100644 --- a/examples/device42_to_infrahub/infrahub/sync_models.py +++ b/examples/device42_to_infrahub/infrahub/sync_models.py @@ -1,10 +1,9 @@ from __future__ import annotations -from typing import Any +from typing import Any, List from infrahub_sync.adapters.infrahub import InfrahubModel - # ------------------------------------------------------- # AUTO-GENERATED FILE, DO NOT MODIFY # This file has been generated with the command `infrahub-sync generate` @@ -13,99 +12,28 @@ class BuiltinTag(InfrahubModel): _modelname = "BuiltinTag" _identifiers = ("name",) - _attributes = ("description",) - description: str | None = None - name: str - - local_id: str | None = None - local_data: Any | None = None - - -class ChoiceDeviceType(InfrahubModel): - _modelname = "ChoiceDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("part_number", "height") - name: str - manufacturer: str - part_number: str | None = None - height: int | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("serial_number", "asset_tag", "description", "model", "organization", "location", "rack") - name: str - serial_number: str | None = None - asset_tag: str | None = None - description: str | None = None - model: str | None = None - organization: str | None = None - location: str | None = None - rack: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraInterfaceL2L3(InfrahubModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("device", "name") - _attributes = ("description",) - device: str + _attributes = () name: str - description: str | None = None local_id: str | None = None local_data: Any | None = None - -class InfraRack(InfrahubModel): - _modelname = "InfraRack" - _identifiers = ("name", "location") - _attributes = ("height", "serial_number", "asset_tag") - name: str - location: str - height: int | None = None - serial_number: str | None = None - asset_tag: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class LocationGeneric(InfrahubModel): - _modelname = "LocationGeneric" +class LocationSite(InfrahubModel): + _modelname = "LocationSite" _identifiers = ("name",) - _attributes = ("description", "type") + _attributes = ("tags",) name: str - description: str | None = None - type: str | None = None + tags: list[str] | None = [] local_id: str | None = None local_data: Any | None = None - -class OrganizationGeneric(InfrahubModel): - _modelname = "OrganizationGeneric" +class OrganizationTenant(InfrahubModel): + _modelname = "OrganizationTenant" _identifiers = ("name",) _attributes = ("description",) - name: str description: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class RoleGeneric(InfrahubModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) name: str - description: str | None = None local_id: str | None = None - local_data: Any | None = None \ No newline at end of file + local_data: Any | None = None diff --git a/infrahub_sync/adapters/device42.py b/infrahub_sync/adapters/device42.py deleted file mode 100644 index 14eeccd..0000000 --- a/infrahub_sync/adapters/device42.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -from infrahub_sync import ( - SyncAdapter, - SyncConfig, - SchemaMappingModel, -) - -from .genericrestapi import GenericrestapiAdapter, GenericrestapiModel - -if TYPE_CHECKING: - from collections.abc import Mapping - - from diffsync import Adapter - - -class Device42Adapter(GenericrestapiAdapter): - """Device42 adapter that extends the generic REST API adapter.""" - - def __init__(self, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs) -> None: - # Set Device42-specific defaults - settings = adapter.settings or {} - - # Apply Device42-specific defaults if not specified - if "auth_method" not in settings: - settings["auth_method"] = "basic" - if "api_endpoint" not in settings: - settings["api_endpoint"] = "/api/2.0" - if "url_env_vars" not in settings: - settings["url_env_vars"] = ["DEVICE42_ADDRESS", "DEVICE42_URL"] - if "username_env_vars" not in settings: - settings["username_env_vars"] = ["DEVICE42_USERNAME"] - if "password_env_vars" not in settings: - settings["password_env_vars"] = ["DEVICE42_PASSWORD"] - - # Device42 API returns results in an "objects" key for most endpoints - if "response_key_pattern" not in settings: - settings["response_key_pattern"] = "objects" - - # Create a new adapter with updated settings - updated_adapter = SyncAdapter(name=adapter.name, settings=settings) - - super().__init__(target=target, adapter=updated_adapter, config=config, adapter_type="Device42", **kwargs) - - def _extract_objects_from_response( - self, - response_data: dict[str, Any], - resource_name: str, - element: SchemaMappingModel, - ) -> list[dict[str, Any]]: - """ - Extract objects from Device42 API response data. - - Device42 API typically returns data in this format: - { - "objects": [...], - "total_count": 123, - "limit": 100, - "offset": 0 - } - """ - # Device42 typically uses "objects" as the key for the data array - objs = response_data.get("objects", []) - - # Fallback to generic extraction if "objects" key is not present - if not objs: - objs = super()._extract_objects_from_response(response_data, resource_name, element) - - # Handle pagination if needed (Device42 returns total_count for pagination info) - total_count = response_data.get("total_count", len(objs)) - limit = response_data.get("limit", len(objs)) - offset = response_data.get("offset", 0) - - # For now, we'll handle the first page. In the future, this could be extended - # to handle pagination automatically by making additional requests - if total_count > len(objs) and offset + limit < total_count: - # Log that there are more results available - remaining = total_count - (offset + len(objs)) - print(f"Device42: Retrieved {len(objs)} of {total_count} {resource_name} records. {remaining} remaining.") - - return objs - - def _fetch_paginated_data(self, endpoint: str, params: dict[str, Any] | None = None) -> list[dict[str, Any]]: - """ - Fetch all data from a paginated Device42 endpoint. - - This method handles Device42's pagination by making multiple requests - if necessary to retrieve all available data. - """ - all_objects = [] - offset = 0 - limit = 100 # Device42 default limit - - if params is None: - params = {} - - while True: - # Add pagination parameters - paginated_params = params.copy() - paginated_params.update({"offset": offset, "limit": limit}) - - try: - response_data = self.client.get(endpoint=endpoint, params=paginated_params) - objects = response_data.get("objects", []) - total_count = response_data.get("total_count", len(objects)) - - all_objects.extend(objects) - - # Check if we've retrieved all objects - if len(objects) < limit or offset + len(objects) >= total_count: - break - - offset += limit - - except Exception as exc: - msg = f"Error fetching paginated data from Device42 API: {exc!s}" - raise ValueError(msg) from exc - - return all_objects - - -class Device42Model(GenericrestapiModel): - """Device42 model that extends the generic REST API model.""" - - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ) -> Self | None: - # TODO: To implement - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs: dict) -> Self | None: - # TODO: To implement - return super().update(attrs=attrs) - - -# Aliases for consistency -Device42Sync = Device42Adapter \ No newline at end of file diff --git a/infrahub_sync/adapters/genericrestapi.py b/infrahub_sync/adapters/genericrestapi.py index 6e5f080..08abea7 100644 --- a/infrahub_sync/adapters/genericrestapi.py +++ b/infrahub_sync/adapters/genericrestapi.py @@ -201,24 +201,8 @@ def _extract_objects_from_response( else default_key ) - # Debug information for API response structure - debug_mode = self.settings.get("debug", False) - if debug_mode: - print(f"API Response keys for {resource_name}: {list(response_data.keys())}") - print(f"Looking for data using key: {response_key}") - # Try to get data using the response key objs = response_data.get(response_key, response_data.get(resource_name, {})) - - # If no data found with expected key, try common alternative keys - if not objs and response_key != "objects": - # Try some common API response keys - for alt_key in ["objects", "data", "results", "items"]: - if alt_key in response_data: - objs = response_data[alt_key] - if debug_mode: - print(f"Found data using alternative key: {alt_key}") - break # Handle different response formats if isinstance(objs, dict): diff --git a/infrahub_sync/adapters/rest_api_client.py b/infrahub_sync/adapters/rest_api_client.py index 573ab40..65dcdcb 100644 --- a/infrahub_sync/adapters/rest_api_client.py +++ b/infrahub_sync/adapters/rest_api_client.py @@ -25,7 +25,7 @@ def __init__( # Determine authentication method, some are use by more than one API. # Example: - # -> Peering Manager + # -> Peering Manager & Device42 if auth_method == "token" and api_token: self.headers["Authorization"] = f"Token {api_token}" # -> LibreNMS @@ -37,7 +37,7 @@ def __init__( # -> RIPE API elif auth_method == "key" and api_token: self.headers["Authorization"] = f"Key {api_token}" - # -> Observium + # -> Observium & Device42 elif auth_method == "basic" and username and password: self.auth = (username, password) elif auth_method == "none": From 9dd4c3f361d613bba1a19b33d6c008335cc96e1a Mon Sep 17 00:00:00 2001 From: Bearchitek Date: Tue, 16 Sep 2025 15:26:43 +0200 Subject: [PATCH 5/6] some more cleanup --- .../genericrestapi/sync_models.py | 5 +- .../infrahub/sync_models.py | 5 +- .../genenericrestapi/__init__.py | 0 .../genenericrestapi/sync_adapter.py | 28 ---- .../genenericrestapi/sync_models.py | 141 ------------------ .../generic_rest_api/__init__.py | 0 .../generic_rest_api/sync_adapter.py | 28 ---- .../generic_rest_api/sync_models.py | 141 ------------------ .../genericrestapi/sync_models.py | 5 +- .../infrahub/sync_models.py | 5 +- .../infrahub/sync_adapter.py | 2 +- .../infrahub/sync_models.py | 16 +- .../prometheus/sync_adapter.py | 2 +- .../prometheus/sync_models.py | 16 +- infrahub_sync/adapters/genericrestapi.py | 5 - 15 files changed, 40 insertions(+), 359 deletions(-) delete mode 100644 examples/peeringdb_to_infrahub/genenericrestapi/__init__.py delete mode 100644 examples/peeringdb_to_infrahub/genenericrestapi/sync_adapter.py delete mode 100644 examples/peeringdb_to_infrahub/genenericrestapi/sync_models.py delete mode 100644 examples/peeringdb_to_infrahub/generic_rest_api/__init__.py delete mode 100644 examples/peeringdb_to_infrahub/generic_rest_api/sync_adapter.py delete mode 100644 examples/peeringdb_to_infrahub/generic_rest_api/sync_models.py diff --git a/examples/device42_to_infrahub/genericrestapi/sync_models.py b/examples/device42_to_infrahub/genericrestapi/sync_models.py index 7ef3d5a..41e7cda 100644 --- a/examples/device42_to_infrahub/genericrestapi/sync_models.py +++ b/examples/device42_to_infrahub/genericrestapi/sync_models.py @@ -1,9 +1,10 @@ from __future__ import annotations -from typing import Any, List +from typing import Any from infrahub_sync.adapters.genericrestapi import GenericrestapiModel + # ------------------------------------------------------- # AUTO-GENERATED FILE, DO NOT MODIFY # This file has been generated with the command `infrahub-sync generate` @@ -18,6 +19,7 @@ class BuiltinTag(GenericrestapiModel): local_id: str | None = None local_data: Any | None = None + class LocationSite(GenericrestapiModel): _modelname = "LocationSite" _identifiers = ("name",) @@ -28,6 +30,7 @@ class LocationSite(GenericrestapiModel): local_id: str | None = None local_data: Any | None = None + class OrganizationTenant(GenericrestapiModel): _modelname = "OrganizationTenant" _identifiers = ("name",) diff --git a/examples/device42_to_infrahub/infrahub/sync_models.py b/examples/device42_to_infrahub/infrahub/sync_models.py index e264bb1..d8e14ef 100644 --- a/examples/device42_to_infrahub/infrahub/sync_models.py +++ b/examples/device42_to_infrahub/infrahub/sync_models.py @@ -1,9 +1,10 @@ from __future__ import annotations -from typing import Any, List +from typing import Any from infrahub_sync.adapters.infrahub import InfrahubModel + # ------------------------------------------------------- # AUTO-GENERATED FILE, DO NOT MODIFY # This file has been generated with the command `infrahub-sync generate` @@ -18,6 +19,7 @@ class BuiltinTag(InfrahubModel): local_id: str | None = None local_data: Any | None = None + class LocationSite(InfrahubModel): _modelname = "LocationSite" _identifiers = ("name",) @@ -28,6 +30,7 @@ class LocationSite(InfrahubModel): local_id: str | None = None local_data: Any | None = None + class OrganizationTenant(InfrahubModel): _modelname = "OrganizationTenant" _identifiers = ("name",) diff --git a/examples/peeringdb_to_infrahub/genenericrestapi/__init__.py b/examples/peeringdb_to_infrahub/genenericrestapi/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/peeringdb_to_infrahub/genenericrestapi/sync_adapter.py b/examples/peeringdb_to_infrahub/genenericrestapi/sync_adapter.py deleted file mode 100644 index 616c643..0000000 --- a/examples/peeringdb_to_infrahub/genenericrestapi/sync_adapter.py +++ /dev/null @@ -1,28 +0,0 @@ -from infrahub_sync.adapters.genenericrestapi import GenenericrestapiAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, - IpamIPAddress, - OrganizationProvider, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class GenenericrestapiSync(GenenericrestapiAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - IpamIPAddress = IpamIPAddress - OrganizationProvider = OrganizationProvider - InfraBGPCommunity = InfraBGPCommunity - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/examples/peeringdb_to_infrahub/genenericrestapi/sync_models.py b/examples/peeringdb_to_infrahub/genenericrestapi/sync_models.py deleted file mode 100644 index 3e095d1..0000000 --- a/examples/peeringdb_to_infrahub/genenericrestapi/sync_models.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from infrahub_sync.adapters.genenericrestapi import GenenericrestapiModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(GenenericrestapiModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ( - "organization", - "affiliated", - "irr_as_set", - "name", - "ipv4_max_prefixes", - "description", - "ipv6_max_prefixes", - ) - asn: int - affiliated: bool | None = None - irr_as_set: str | None = None - name: str - ipv4_max_prefixes: int | None = None - description: str | None = None - ipv6_max_prefixes: int | None = None - organization: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPPeerGroup(GenenericrestapiModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("bgp_communities", "import_policies", "export_policies", "description", "status") - name: str - description: str | None = None - status: str | None = None - bgp_communities: list[str] | None = [] - import_policies: list[str] | None = [] - export_policies: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class IpamIPAddress(GenenericrestapiModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - description: str | None = None - address: str - - local_id: str | None = None - local_data: Any | None = None - - -class OrganizationProvider(GenenericrestapiModel): - _modelname = "OrganizationProvider" - _identifiers = ("name",) - _attributes = () - name: str - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPCommunity(GenenericrestapiModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "label", "community_type", "value") - name: str - description: str | None = None - label: str | None = None - community_type: str | None = None - value: str - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPRoutingPolicy(GenenericrestapiModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "address_family", "policy_type", "label", "weight", "description") - address_family: int - policy_type: str - label: str | None = None - weight: int | None = 1000 - name: str - description: str | None = None - bgp_communities: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class InfraIXP(GenenericrestapiModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "status", "description") - status: str | None = "enabled" - name: str - description: str | None = None - export_policies: list[str] | None = [] - bgp_communities: list[str] | None = [] - import_policies: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class InfraIXPConnection(GenenericrestapiModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ( - "internet_exchange_point", - "ipv4_address", - "ipv6_address", - "status", - "peeringdb_netixlan", - "vlan", - "description", - ) - name: str - status: str | None = "enabled" - peeringdb_netixlan: int | None = None - vlan: int | None = None - description: str | None = None - internet_exchange_point: str - ipv4_address: str | None = None - ipv6_address: str | None = None - - local_id: str | None = None - local_data: Any | None = None diff --git a/examples/peeringdb_to_infrahub/generic_rest_api/__init__.py b/examples/peeringdb_to_infrahub/generic_rest_api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/peeringdb_to_infrahub/generic_rest_api/sync_adapter.py b/examples/peeringdb_to_infrahub/generic_rest_api/sync_adapter.py deleted file mode 100644 index 376377d..0000000 --- a/examples/peeringdb_to_infrahub/generic_rest_api/sync_adapter.py +++ /dev/null @@ -1,28 +0,0 @@ -from infrahub_sync.adapters.generic_rest_api import Generic_Rest_ApiAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, - IpamIPAddress, - OrganizationProvider, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class Generic_Rest_ApiSync(Generic_Rest_ApiAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - IpamIPAddress = IpamIPAddress - OrganizationProvider = OrganizationProvider - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraBGPCommunity = InfraBGPCommunity - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/examples/peeringdb_to_infrahub/generic_rest_api/sync_models.py b/examples/peeringdb_to_infrahub/generic_rest_api/sync_models.py deleted file mode 100644 index abe1283..0000000 --- a/examples/peeringdb_to_infrahub/generic_rest_api/sync_models.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from infrahub_sync.adapters.generic_rest_api import Generic_Rest_ApiModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(Generic_Rest_ApiModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ( - "organization", - "affiliated", - "irr_as_set", - "name", - "ipv4_max_prefixes", - "description", - "ipv6_max_prefixes", - ) - asn: int - affiliated: bool | None = None - irr_as_set: str | None = None - name: str - ipv4_max_prefixes: int | None = None - description: str | None = None - ipv6_max_prefixes: int | None = None - organization: str | None = None - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPPeerGroup(Generic_Rest_ApiModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("import_policies", "export_policies", "bgp_communities", "description", "status") - name: str - description: str | None = None - status: str | None = None - import_policies: list[str] | None = [] - export_policies: list[str] | None = [] - bgp_communities: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class IpamIPAddress(Generic_Rest_ApiModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - description: str | None = None - address: str - - local_id: str | None = None - local_data: Any | None = None - - -class OrganizationProvider(Generic_Rest_ApiModel): - _modelname = "OrganizationProvider" - _identifiers = ("name",) - _attributes = () - name: str - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPRoutingPolicy(Generic_Rest_ApiModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "label", "description", "policy_type", "weight", "address_family") - name: str - label: str | None = None - description: str | None = None - policy_type: str - weight: int | None = 1000 - address_family: int - bgp_communities: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class InfraBGPCommunity(Generic_Rest_ApiModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "label", "community_type", "value") - name: str - description: str | None = None - label: str | None = None - community_type: str | None = None - value: str - - local_id: str | None = None - local_data: Any | None = None - - -class InfraIXP(Generic_Rest_ApiModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "description", "status") - name: str - description: str | None = None - status: str | None = "enabled" - export_policies: list[str] | None = [] - bgp_communities: list[str] | None = [] - import_policies: list[str] | None = [] - - local_id: str | None = None - local_data: Any | None = None - - -class InfraIXPConnection(Generic_Rest_ApiModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ( - "internet_exchange_point", - "ipv4_address", - "ipv6_address", - "status", - "peeringdb_netixlan", - "vlan", - "description", - ) - name: str - status: str | None = "enabled" - peeringdb_netixlan: int | None = None - vlan: int | None = None - description: str | None = None - internet_exchange_point: str - ipv4_address: str | None = None - ipv6_address: str | None = None - - local_id: str | None = None - local_data: Any | None = None diff --git a/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py b/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py index f07111e..2f82c68 100644 --- a/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py +++ b/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py @@ -13,12 +13,9 @@ class InfraAutonomousSystem(GenericrestapiModel): _modelname = "InfraAutonomousSystem" _identifiers = ("asn",) - _attributes = ("irr_as_set", "name", "ipv4_max_prefixes", "ipv6_max_prefixes") + _attributes = ("name",) asn: int - irr_as_set: str | None = None name: str - ipv4_max_prefixes: int | None = None - ipv6_max_prefixes: int | None = None local_id: str | None = None local_data: Any | None = None diff --git a/examples/peeringdb_to_infrahub/infrahub/sync_models.py b/examples/peeringdb_to_infrahub/infrahub/sync_models.py index dd1b07c..5cad66d 100644 --- a/examples/peeringdb_to_infrahub/infrahub/sync_models.py +++ b/examples/peeringdb_to_infrahub/infrahub/sync_models.py @@ -13,12 +13,9 @@ class InfraAutonomousSystem(InfrahubModel): _modelname = "InfraAutonomousSystem" _identifiers = ("asn",) - _attributes = ("irr_as_set", "name", "ipv4_max_prefixes", "ipv6_max_prefixes") + _attributes = ("name",) asn: int - irr_as_set: str | None = None name: str - ipv4_max_prefixes: int | None = None - ipv6_max_prefixes: int | None = None local_id: str | None = None local_data: Any | None = None diff --git a/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_adapter.py b/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_adapter.py index d91c544..5cdfe8d 100644 --- a/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_adapter.py +++ b/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_adapter.py @@ -1,10 +1,10 @@ from infrahub_sync.adapters.infrahub import InfrahubAdapter from .sync_models import ( + VirtualizationVirtualMachine, VirtualizationVMDisk, VirtualizationVMFilesystem, VirtualizationVMNetworkInterface, - VirtualizationVirtualMachine, ) diff --git a/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_models.py b/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_models.py index 79fbe14..17dd6c3 100644 --- a/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_models.py +++ b/examples/prometheus_to_infrahub (node_exporter)/infrahub/sync_models.py @@ -1,9 +1,10 @@ from __future__ import annotations -from typing import Any, List +from typing import Any from infrahub_sync.adapters.infrahub import InfrahubModel + # ------------------------------------------------------- # AUTO-GENERATED FILE, DO NOT MODIFY # This file has been generated with the command `infrahub-sync generate` @@ -20,6 +21,7 @@ class VirtualizationVMDisk(InfrahubModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVMFilesystem(InfrahubModel): _modelname = "VirtualizationVMFilesystem" _identifiers = ("virtual_machine", "mountpoint") @@ -35,6 +37,7 @@ class VirtualizationVMFilesystem(InfrahubModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVMNetworkInterface(InfrahubModel): _modelname = "VirtualizationVMNetworkInterface" _identifiers = ("virtual_machine", "name") @@ -51,10 +54,19 @@ class VirtualizationVMNetworkInterface(InfrahubModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVirtualMachine(InfrahubModel): _modelname = "VirtualizationVirtualMachine" _identifiers = ("name",) - _attributes = ("os_name", "ip_forwarding_enabled", "os_kernel", "status", "architecture", "conntrack_limit", "mem_total_bytes") + _attributes = ( + "os_name", + "ip_forwarding_enabled", + "os_kernel", + "status", + "architecture", + "conntrack_limit", + "mem_total_bytes", + ) os_name: str | None = None ip_forwarding_enabled: bool | None = None os_kernel: str | None = None diff --git a/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_adapter.py b/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_adapter.py index 9967a03..c1bd69f 100644 --- a/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_adapter.py +++ b/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_adapter.py @@ -1,10 +1,10 @@ from infrahub_sync.adapters.prometheus import PrometheusAdapter from .sync_models import ( + VirtualizationVirtualMachine, VirtualizationVMDisk, VirtualizationVMFilesystem, VirtualizationVMNetworkInterface, - VirtualizationVirtualMachine, ) diff --git a/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_models.py b/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_models.py index 11523da..573ba10 100644 --- a/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_models.py +++ b/examples/prometheus_to_infrahub (node_exporter)/prometheus/sync_models.py @@ -1,9 +1,10 @@ from __future__ import annotations -from typing import Any, List +from typing import Any from infrahub_sync.adapters.prometheus import PrometheusModel + # ------------------------------------------------------- # AUTO-GENERATED FILE, DO NOT MODIFY # This file has been generated with the command `infrahub-sync generate` @@ -20,6 +21,7 @@ class VirtualizationVMDisk(PrometheusModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVMFilesystem(PrometheusModel): _modelname = "VirtualizationVMFilesystem" _identifiers = ("virtual_machine", "mountpoint") @@ -35,6 +37,7 @@ class VirtualizationVMFilesystem(PrometheusModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVMNetworkInterface(PrometheusModel): _modelname = "VirtualizationVMNetworkInterface" _identifiers = ("virtual_machine", "name") @@ -51,10 +54,19 @@ class VirtualizationVMNetworkInterface(PrometheusModel): local_id: str | None = None local_data: Any | None = None + class VirtualizationVirtualMachine(PrometheusModel): _modelname = "VirtualizationVirtualMachine" _identifiers = ("name",) - _attributes = ("os_name", "ip_forwarding_enabled", "os_kernel", "status", "architecture", "conntrack_limit", "mem_total_bytes") + _attributes = ( + "os_name", + "ip_forwarding_enabled", + "os_kernel", + "status", + "architecture", + "conntrack_limit", + "mem_total_bytes", + ) os_name: str | None = None ip_forwarding_enabled: bool | None = None os_kernel: str | None = None diff --git a/infrahub_sync/adapters/genericrestapi.py b/infrahub_sync/adapters/genericrestapi.py index 08abea7..8be742e 100644 --- a/infrahub_sync/adapters/genericrestapi.py +++ b/infrahub_sync/adapters/genericrestapi.py @@ -303,8 +303,3 @@ def create( def update(self, attrs: dict) -> Self | None: # TODO: To implement return super().update(attrs=attrs) - - -# Aliases for backward compatibility and consistency -GenericRestApiAdapter = GenericrestapiAdapter -GenericRestApiModel = GenericrestapiModel From f7b1061933a4151476013553a7f58648ac63686d Mon Sep 17 00:00:00 2001 From: Bearchitek Date: Tue, 16 Sep 2025 15:32:52 +0200 Subject: [PATCH 6/6] forgot to refresh schema --- .../peeringdb_to_infrahub/genericrestapi/sync_models.py | 7 +++++-- examples/peeringdb_to_infrahub/infrahub/sync_models.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py b/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py index 2f82c68..a7eec18 100644 --- a/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py +++ b/examples/peeringdb_to_infrahub/genericrestapi/sync_models.py @@ -13,9 +13,12 @@ class InfraAutonomousSystem(GenericrestapiModel): _modelname = "InfraAutonomousSystem" _identifiers = ("asn",) - _attributes = ("name",) - asn: int + _attributes = ("ipv6_max_prefixes", "name", "ipv4_max_prefixes", "irr_as_set") + ipv6_max_prefixes: int | None = None name: str + ipv4_max_prefixes: int | None = None + asn: int + irr_as_set: str | None = None local_id: str | None = None local_data: Any | None = None diff --git a/examples/peeringdb_to_infrahub/infrahub/sync_models.py b/examples/peeringdb_to_infrahub/infrahub/sync_models.py index 5cad66d..7d43792 100644 --- a/examples/peeringdb_to_infrahub/infrahub/sync_models.py +++ b/examples/peeringdb_to_infrahub/infrahub/sync_models.py @@ -13,9 +13,12 @@ class InfraAutonomousSystem(InfrahubModel): _modelname = "InfraAutonomousSystem" _identifiers = ("asn",) - _attributes = ("name",) - asn: int + _attributes = ("ipv6_max_prefixes", "name", "ipv4_max_prefixes", "irr_as_set") + ipv6_max_prefixes: int | None = None name: str + ipv4_max_prefixes: int | None = None + asn: int + irr_as_set: str | None = None local_id: str | None = None local_data: Any | None = None