🐍 Python Extensions — Module 11

Python Topology & Screens

Hands-on

Topology from Python Extensions

Python extensions create entities the same way SNMP extensions do — through the topology: section in extension.yaml. The difference is how dimensions reach the topology engine: via self.report_metric() instead of SNMP OIDs.

The Connection: Metrics → Topology

The topology engine watches for metrics matching the $prefix() condition. When a metric arrives with dimensions matching requiredDimensions, an entity is created:

# In your Python code:
self.report_metric(
    key="com.dynatrace.extension.myext.cpu",
    value=85.5,
    dimensions={
        "device.address": "10.0.0.1",
        "device.name": "switch-01",
        "device.type": "FortiSwitch",
    },
)

# In extension.yaml topology:
topology:
  types:
    - name: myext:device
      displayName: My Device
      enabled: true
      rules:
        - idPattern: myext_device_{device.address}
          instanceNamePattern: "{device.name} ({device.address})"
          sources:
            - sourceType: Metrics
              condition: $prefix(com.dynatrace.extension.myext)
          requiredDimensions:
            - key: device.address
            - key: device.type
              valuePattern: $eq(FortiSwitch)
          role: default

Parent + Child Entity Pattern

Report metrics with both parent and child dimensions to create the relationship:

def _collect_ports(self, device_url, device_name):
    ports = self.http_client.get(f"{device_url}/api/ports").json()

    for port in ports:
        # These dimensions create BOTH the parent device entity
        # AND the child port entity (via requiredDimensions matching)
        dims = {
            "device.address": device_url,
            "device.name": device_name,
            "port.name": port["name"],
            "port.speed": str(port["speed"]),
        }
        self.report_metric(key="myext.port.rx.bytes.count", value=port["rx_bytes"], dimensions=dims)
        self.report_metric(key="myext.port.tx.bytes.count", value=port["tx_bytes"], dimensions=dims)

The topology section then defines both entity types:

topology:
  types:
    - name: myext:device
      rules:
        - idPattern: myext_device_{device.address}
          requiredDimensions:
            - key: device.address
          role: default
    - name: myext:port
      rules:
        - idPattern: myext_port_{device.address}_{port.name}
          instanceNamePattern: "{port.name}"
          requiredDimensions:
            - key: device.address
            - key: port.name
  relationships:
    - fromType: myext:port
      toType: myext:device
      typeOfRelation: CHILD_OF
      sources:
        - sourceType: Metrics
          condition: $prefix(com.dynatrace.extension.myext)

Entity Attributes from Dimensions

Any dimension you report can become an entity attribute (property visible in the UI):

rules:
  - idPattern: myext_device_{device.address}
    attributes:
      - key: dt.ip_addresses          # Special: enables IP lookup
        pattern: "{device.address}"
      - key: firmwareVersion
        displayName: Firmware Version
        pattern: "{device.firmware}"
      - key: serialNumber
        displayName: Serial Number
        pattern: "{device.serial}"

Real Example: Waze Police Topology

The Waze extension creates two entity types from API data:

  • waze_police:region — one per configured city (5 entities: Reus-Tarragona, Linz, Maidenhead, Paris, London)
  • waze_police:alert — one per police sighting (1,229+ entities, growing continuously)

Each alert entity has properties: city, street, subtype, lat, lon — all passed as dimensions in report_metric().

Screens for Python Entities

Screens work identically to SNMP. The screens: section in extension.yaml doesn't care whether data came from SNMP or Python — it only cares about metric keys and entity types.

screens:
  - entityType: myext:device
    detailsSettings:
      staticContent:
        showProblems: true
        showProperties: true
      layout:
        autoGenerate: false
        cards:
          - key: overview
            type: CHART_GROUP
          - key: ports
            type: ENTITIES_LIST
    chartsCards:
      - key: overview
        displayName: Overview
        charts:
          - displayName: CPU Usage
            visualizationType: GRAPH_CHART
            graphChartConfig:
              metrics:
                - metricSelector: com.dynatrace.extension.myext.cpu:splitBy("dt.entity.myext:device")

Verifying Entity Creation

After deploying, verify entities exist:

# Entities API v2 (always works, including on sprint)
curl "$BASE/api/v2/entities?entitySelector=type(myext:device)&fields=+properties" \
  -H "Authorization: Api-Token $TOKEN"

# Check entity type registration
curl "$BASE/api/v2/settings/objects?schemaIds=builtin:monitoredentities.generic.type" \
  -H "Authorization: Api-Token $TOKEN"

Sprint gotcha: DQL fetch dt.entity.{custom_type} returns UNKNOWN_PARAMETER_DEFINED on sprint for custom entity types. Always use Entities API v2 instead.

🛠 Hands-On Exercise

Edit the YAML in the editor, then click "Check My Work" to validate.

Python Topology

Add topology to this Python extension that monitors a load balancer with pools.

  • Create a parent entity lb_topo:device with role: default
  • Create a child entity lb_topo:pool
  • Use device.address in the device idPattern
  • Use both device.address and pool.name in the pool idPattern
  • Add a CHILD_OF relationship
extension.yamlYAML
Loading...