Python Topology & Screens
Hands-onTopology 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:devicewithrole: default - Create a child entity
lb_topo:pool - Use
device.addressin the device idPattern - Use both
device.addressandpool.namein the pool idPattern - Add a
CHILD_OFrelationship