Dynatrace
Port's Dynatrace integration allows you to import problem
, slo
and entity
resources from your Dynatrace instance into Port, according to your mapping and definition.
Common use cases
- Map your monitored entities, problems and SLOs in Dynatrace.
- Watch for object changes (create/update) in real-time, and automatically apply the changes to your entities in Port.
Installation
Choose one of the following installation methods:
- Hosted by Port
- Real Time & Always On
- Scheduled
Using this installation option means that the integration will be hosted by Port, with a customizable resync interval to ingest data into Port.
The Hosted by Port
option is currently in beta, and is still undergoing final testing before its official release.
Should you encounter any bugs or functionality issues, please let us know so we can rectify them as soon as possible.
Your help is greatly appreciated! ⭐
Live event support
Currently, live events are not supported for integrations hosted by Port.
Resyncs will be performed periodically every 1 hour by default (can be configured differently after installation), or manually triggered by you via Port's UI.
Therefore, real-time events (including GitOps) will not be ingested into Port immediately.
Support for live events is WIP and will be supported in the near future.
Installation
To install, follow the following steps:
-
Go to the Data sources page of your portal.
-
Click on the
+ Data source
button in the top-right corner. -
Click on the relevant integration in the list.
-
Under
Select your installation method
, chooseHosted by Port
. -
Configure the
integration settings
andapplication settings
as you wish (see below for details).
Application settings
Every integration hosted by Port has the following customizable application settings, which are configurable after installation:
-
Resync interval
: The frequency at which Port will ingest data from the integration. There are various options available, ranging from every 1 hour to once a day. -
Send raw data examples
: A boolean toggle (enabled
by default). If enabled, raw data examples will be sent from the integration to Port. These examples are used when testing your mapping configuration, they allow you to run yourjq
expressions against real data and see the results.
Integration settings
Every integration has its own tool-specific settings, under the Integration settings
section.
Each of these settings has an ⓘ icon next to it, which you can hover over to see a description of the setting.
Port secrets
Some integration settings require sensitive pieces of data, such as tokens.
For these settings, Port secrets will be used, ensuring that your sensitive data is encrypted and secure.
When changing such a setting, you will be prompted to choose an existing secret or create a new one:
Port source IP addresses
When using this installation method, Port will make outbound calls to your 3rd-party applications from static IP addresses.
You may need to add these addresses to your allowlist, in order to allow Port to interact with the integrated service:
- Europe (EU)
- United States (US)
54.73.167.226
63.33.143.237
54.76.185.219
3.234.37.33
54.225.172.136
3.225.234.99
Using this installation option means that the integration will be able to update Port in real time using webhooks.
This table summarizes the available parameters for the installation. Set them as you wish in the script below, then copy it and run it in your terminal:
Parameter | Description | Required |
---|---|---|
port.clientId | Your port client id | ✅ |
port.clientSecret | Your port client secret | ✅ |
port.baseUrl | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US | ✅ |
integration.identifier | Change the identifier to describe your integration | ✅ |
integration.type | The integration type | ✅ |
integration.eventListener.type | The event listener type | ✅ |
integration.secrets.dynatraceApiKey | API Key for Dynatrace instance | ✅ |
integration.config.dynatraceHostUrl | The API URL of the Dynatrace instance | ✅ |
scheduledResyncInterval | The number of minutes between each resync | ❌ |
initializePortResources | Default true, When set to true the integration will create default blueprints and the port App config Mapping | ❌ |
- Helm
- ArgoCD
To install the integration using Helm, run the following command:
helm repo add --force-update port-labs https://port-labs.github.io/helm-charts
helm upgrade --install my-dynatrace-integration port-labs/port-ocean \
--set port.clientId="CLIENT_ID" \
--set port.clientSecret="CLIENT_SECRET" \
--set port.baseUrl="https://api.getport.io" \
--set initializePortResources=true \
--set scheduledResyncInterval=60 \
--set integration.identifier="my-dynatrace-integration" \
--set integration.type="dynatrace" \
--set integration.eventListener.type="POLLING" \
--set integration.secrets.dynatraceApiKey="<your-api-key>" \
--set integration.config.dynatraceHostUrl="<your-instance-url>"
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
To install the integration using ArgoCD, follow these steps:
- Create a
values.yaml
file inargocd/my-ocean-dynatrace-integration
in your git repository with the content:
Remember to replace the placeholders for DYNATRACE_HOST_URL
and DYNATRACE_API_KEY
.
initializePortResources: true
scheduledResyncInterval: 120
integration:
identifier: my-ocean-dynatrace-integration
type: dynatrace
eventListener:
type: POLLING
config:
dynatraceHostUrl: DYNATRACE_HOST_URL
secrets:
dynatraceApiKey: DYNATRACE_API_KEY
- Install the
my-ocean-dynatrace-integration
ArgoCD Application by creating the followingmy-ocean-dynatrace-integration.yaml
manifest:
Remember to replace the placeholders for YOUR_PORT_CLIENT_ID
YOUR_PORT_CLIENT_SECRET
and YOUR_GIT_REPO_URL
.
Multiple sources ArgoCD documentation can be found here.
ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-ocean-dynatrace-integration
namespace: argocd
spec:
destination:
namespace: my-ocean-dynatrace-integration
server: https://kubernetes.default.svc
project: default
sources:
- repoURL: 'https://port-labs.github.io/helm-charts/'
chart: port-ocean
targetRevision: 0.1.14
helm:
valueFiles:
- $values/argocd/my-ocean-dynatrace-integration/values.yaml
parameters:
- name: port.clientId
value: YOUR_PORT_CLIENT_ID
- name: port.clientSecret
value: YOUR_PORT_CLIENT_SECRET
- name: port.baseUrl
value: https://api.getport.io
- repoURL: YOUR_GIT_REPO_URL
targetRevision: main
ref: values
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
- Apply your application manifest with
kubectl
:
kubectl apply -f my-ocean-dynatrace-integration.yaml
- GitHub
- Jenkins
- Azure Devops
- GitLab
This workflow will run the Dynatrace integration once and then exit, this is useful for scheduled ingestion of data.
If you want the integration to update Port in real time using webhooks, use the Real Time & Always On installation option.
Make sure to configure the following Github Secrets:
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY | The Dynatrace API key | ✅ |
OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL | The Dynatrace API host URL | ✅ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | ❌ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | ❌ |
OCEAN__PORT__CLIENT_ID | Your port client id | ✅ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret | ✅ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US | ✅ |
Here is an example for dynatrace-integration.yml
workflow file:
name: Dynatrace Exporter Workflow
on:
workflow_dispatch:
schedule:
- cron: '0 */1 * * *' # Determines the scheduled interval for this workflow. This example runs every hour.
jobs:
run-integration:
runs-on: ubuntu-latest
timeout-minutes: 30 # Set a time limit for the job
steps:
- uses: port-labs/ocean-sail@v1
with:
type: 'dynatrace'
port_client_id: ${{ secrets.OCEAN__PORT__CLIENT_ID }}
port_client_secret: ${{ secrets.OCEAN__PORT__CLIENT_SECRET }}
port_base_url: https://api.getport.io
config: |
dynatrace_api_key: ${{ secrets.OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY }}
dynatrace_host_url: ${{ secrets.OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL }}
This pipeline will run the Dynatrace integration once and then exit, this is useful for scheduled ingestion of data.
Your Jenkins agent should be able to run docker commands.
If you want the integration to update Port in real time using webhooks you should use the Real Time & Always On installation option.
Make sure to configure the following Jenkins Credentials
of Secret Text
type:
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY | The Dynatrace API key | ✅ |
OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL | The Dynatrace host URL | ✅ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | ❌ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | ❌ |
OCEAN__PORT__CLIENT_ID | Your port client id | ✅ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret | ✅ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US | ✅ |
Here is an example for Jenkinsfile
groovy pipeline file:
pipeline {
agent any
stages {
stage('Run Dynatrace Integration') {
steps {
script {
withCredentials([
string(credentialsId: 'OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY', variable: 'OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY'),
string(credentialsId: 'OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL', variable: 'OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL'),
string(credentialsId: 'OCEAN__PORT__CLIENT_ID', variable: 'OCEAN__PORT__CLIENT_ID'),
string(credentialsId: 'OCEAN__PORT__CLIENT_SECRET', variable: 'OCEAN__PORT__CLIENT_SECRET'),
]) {
sh('''
#Set Docker image and run the container
integration_type="dynatrace"
version="latest"
image_name="ghcr.io/port-labs/port-ocean-${integration_type}:${version}"
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY=$OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL=$OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL \
-e OCEAN__PORT__CLIENT_ID=$OCEAN__PORT__CLIENT_ID \
-e OCEAN__PORT__CLIENT_SECRET=$OCEAN__PORT__CLIENT_SECRET \
-e OCEAN__PORT__BASE_URL='https://api.getport.io' \
$image_name
exit $?
''')
}
}
}
}
}
}
This pipeline will run the Dynatrace integration once and then exit, this is useful for scheduled ingestion of data.
Your Azure Devops agent should be able to run docker commands. Learn more about agents here.
If you want the integration to update Port in real time using webhooks you should use the Real Time & Always On installation option.
Variable groups store values and secrets you'll use in your pipelines across your project. Learn more
Setting Up Your Credentials
- Create a Variable Group: Name it port-ocean-credentials. Store the required variables from the table.
- Authorize Your Pipeline:
- Go to "Library" -> "Variable groups."
- Find port-ocean-credentials and click on it.
- Select "Pipeline Permissions" and add your pipeline to the authorized list.
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY | The Dynatrace API key | ✅ |
OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL | The Dynatrace API host URL | ✅ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | ❌ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | ❌ |
OCEAN__PORT__CLIENT_ID | Your port client id | ✅ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret | ✅ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US | ✅ |
Here is an example for dyntrace-integration.yml
pipeline file:
trigger:
- main
pool:
vmImage: "ubuntu-latest"
variables:
- group: port-ocean-credentials
steps:
- script: |
# Set Docker image and run the container
integration_type="dynatrace"
version="latest"
image_name="ghcr.io/port-labs/port-ocean-$integration_type:$version"
docker run -i --rm \
-e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY=$(OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY) \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL=$(OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL) \
-e OCEAN__PORT__CLIENT_ID=$(OCEAN__PORT__CLIENT_ID) \
-e OCEAN__PORT__CLIENT_SECRET=$(OCEAN__PORT__CLIENT_SECRET) \
-e OCEAN__PORT__BASE_URL='https://api.getport.io' \
$image_name
exit $?
displayName: 'Ingest Data into Port'
This workflow will run the Dynatrace integration once and then exit, this is useful for scheduled ingestion of data.
If you want the integration to update Port in real time using webhooks you should use the Real Time & Always On installation option.
Make sure to configure the following GitLab variables:
Parameter | Description | Required |
---|---|---|
OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY | The Dynatrace API key | ✅ |
OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL | The Dynatrace API host URL | ✅ |
OCEAN__INITIALIZE_PORT_RESOURCES | Default true, When set to false the integration will not create default blueprints and the port App config Mapping | ❌ |
OCEAN__INTEGRATION__IDENTIFIER | Change the identifier to describe your integration, if not set will use the default one | ❌ |
OCEAN__PORT__CLIENT_ID | Your port client id | ✅ |
OCEAN__PORT__CLIENT_SECRET | Your port client secret | ✅ |
OCEAN__PORT__BASE_URL | Your Port API URL - https://api.getport.io for EU, https://api.us.getport.io for US | ✅ |
Here is an example for .gitlab-ci.yml
pipeline file:
default:
image: docker:24.0.5
services:
- docker:24.0.5-dind
before_script:
- docker info
variables:
INTEGRATION_TYPE: dynatrace
VERSION: latest
stages:
- ingest
ingest_data:
stage: ingest
variables:
IMAGE_NAME: ghcr.io/port-labs/port-ocean-$INTEGRATION_TYPE:$VERSION
script:
- |
docker run -i --rm --platform=linux/amd64 \
-e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \
-e OCEAN__INITIALIZE_PORT_RESOURCES=true \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY=$OCEAN__INTEGRATION__CONFIG__DYNATRACE_API_KEY \
-e OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL=$OCEAN__INTEGRATION__CONFIG__DYNATRACE_HOST_URL \
-e OCEAN__PORT__CLIENT_ID=$OCEAN__PORT__CLIENT_ID \
-e OCEAN__PORT__CLIENT_SECRET=$OCEAN__PORT__CLIENT_SECRET \
-e OCEAN__PORT__BASE_URL='https://api.getport.io' \
$IMAGE_NAME
rules: # Run only when changes are made to the main branch
- if: '$CI_COMMIT_BRANCH == "main"'
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
Generating Dynatrace API key
- Navigate to
<instanceURL>/ui/apps/dynatrace.classic.tokens/ui/access-tokens
. For example, if you access your Dynatrace instance athttps://npm82883.apps.dynatrace.com
, you should navigate tohttps://npm82883.apps.dynatrace.com/ui/apps/dynatrace.classic.tokens/ui/access-tokens
. - Click Generate new token to create a new token. Ensure the permissions:
DataExport
,Read entities
,Read problems
andRead SLO
are assigned to the token. TheDataExport
permission allows Dynatrace to perform healthchecks before ingestion starts.
Constructing Dynatrace Host URL
Your Dynatrace host URL should be https://<environment-id>.live.dynatrace.com
. Note that there is a difference between the instance URL and the API host URL. The former contains apps
while the latter (as shown prior) uses live
. This means if your environment ID is npm82883
, your API host URL should be https://npm82883.live.dynatrace.com
.
Ingesting Dynatrace objects
The Dynatrace integration uses a YAML configuration to describe the process of loading data into the developer portal.
Here is an example snippet from the config which demonstrates the process for getting entity
data from Dynatrace:
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: entity
selector:
query: "true"
port:
entity:
mappings:
identifier: .entityId
title: .displayName
blueprint: '"dynatraceEntity"'
properties:
firstSeen: ".firstSeenTms / 1000 | todate"
lastSeen: ".lastSeenTms / 1000 | todate"
type: .type
tags: .tags[].stringRepresentation
The integration makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from Dynatrace's API events.
Configuration structure
The integration configuration determines which resources will be queried from Dynatrace, and which entities and properties will be created in Port.
The following resources can be used to map data from Dynatrace, it is possible to reference any field that appears in the API responses linked below for the mapping configuration.
problem
entity
slo
entity types
for selectors in theentity
resource.
-
The root key of the integration configuration is the
resources
key:resources:
- kind: entity
selector:
... -
The
kind
key is a specifier for an Dynatrace object:resources:
- kind: entity
selector:
... -
The
selector
and thequery
keys allow you to filter which objects of the specifiedkind
will be ingested into your software catalog:resources:
- kind: entity
selector:
query: "true" # JQ boolean expression. If evaluated to false - this object will be skipped.
entityTypes: ["APPLICATION", "SERVICE"] # An optional list of entity types to filter by. If not specified, defaults to ["APPLICATION", "SERVICE"].
port: -
The
port
,entity
and themappings
keys are used to map the Dynatrace object fields to Port entities. To create multiple mappings of the same kind, you can add another item in theresources
array:resources:
- kind: entity
selector:
query: "true"
entityTypes: ["APPLICATION", "SERVICE"]
port:
entity:
mappings: # Mappings between one Dynatrace object to a Port entity. Each value is a JQ query.
identifier: .entityId
title: .displayName
blueprint: '"dynatraceEntity"'
properties:
firstSeen: ".firstSeenTms / 1000 | todate"
lastSeen: ".lastSeenTms / 1000 | todate"
type: .type
tags: .tags[].stringRepresentation
managementZones: .managementZones[].name
properties: .properties
fromRelationships: .fromRelationships
toRelationships: .toRelationships
- kind: entity # In this instance entity is mapped again with a different filter
selector:
query: '.displayName == "MyEntityName"'
entityTypes: ["APPLICATION", "SERVICE"]
port:
entity:
mappings: ...Blueprint keyNote the value of the
blueprint
key - if you want to use a hardcoded string, you need to encapsulate it in 2 sets of quotes, for example use a pair of single-quotes ('
) and then another pair of double-quotes ("
)
Ingest additional resource types
By default, the entity
kind ingests only entities of type APPLICATION
and SERVICE
due to the large number of available resources. However, you can configure the entity
kind mapping to ingest entities of other types.
To do this, use the entityTypes
selector in the entity mapping like so:
Mapping configuration (Click to expand)
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: entity
selector:
query: "true"
entityTypes: ["APPLICATION", "SERVICE"]
port:
entity:
mappings:
identifier: .entityId
title: .displayName
blueprint: '"dynatraceEntity"'
properties:
firstSeen: ".firstSeenTms / 1000 | todate"
lastSeen: ".lastSeenTms / 1000 | todate"
type: .type
tags: .tags[].stringRepresentation
managementZones: .managementZones[].name
properties: .properties
fromRelationships: .fromRelationships
toRelationships: .toRelationships
Available resource types
You can retrieve a list of available resource types by using the Dynatrace Entity Types API. Below is a list of resource types retrieved from the API:
Dynatrace entity types (Click to expand)
APM_SECURITY_GATEWAY
APPLICATION
APPLICATION_METHOD
APPLICATION_METHOD_GROUP
APPMON_SERVER
APPMON_SYSTEM_PROFILE
AUTO_SCALING_GROUP
AWS_APPLICATION_LOAD_BALANCER
AWS_AVAILABILITY_ZONE
AWS_CREDENTIALS
AWS_LAMBDA_FUNCTION
AWS_NETWORK_LOAD_BALANCER
AZURE_API_MANAGEMENT_SERVICE
AZURE_APPLICATION_GATEWAY
AZURE_APP_SERVICE_PLAN
AZURE_COSMOS_DB
AZURE_CREDENTIALS
AZURE_EVENT_HUB
AZURE_EVENT_HUB_NAMESPACE
AZURE_FUNCTION_APP
AZURE_IOT_HUB
AZURE_LOAD_BALANCER
AZURE_MGMT_GROUP
AZURE_REDIS_CACHE
AZURE_REGION
AZURE_SERVICE_BUS_NAMESPACE
AZURE_SERVICE_BUS_QUEUE
AZURE_SERVICE_BUS_TOPIC
AZURE_SQL_DATABASE
AZURE_SQL_ELASTIC_POOL
AZURE_SQL_SERVER
AZURE_STORAGE_ACCOUNT
AZURE_SUBSCRIPTION
AZURE_TENANT
AZURE_VM
AZURE_VM_SCALE_SET
AZURE_WEB_APP
BOSH_DEPLOYMENT
BROWSER
CF_FOUNDATION
CINDER_VOLUME
CLOUD_APPLICATION
CLOUD_APPLICATION_INSTANCE
CLOUD_APPLICATION_NAMESPACE
CONTAINER_GROUP
CONTAINER_GROUP_INSTANCE
CUSTOM_APPLICATION
CUSTOM_DEVICE
CUSTOM_DEVICE_GROUP
DATASTORE
DCRUM_APPLICATION
DCRUM_SERVICE
DCRUM_SERVICE_INSTANCE
DEVICE_APPLICATION_METHOD
DEVICE_APPLICATION_METHOD_GROUP
DISK
DOCKER_CONTAINER_GROUP
DOCKER_CONTAINER_GROUP_INSTANCE
DYNAMO_DB_TABLE
EBS_VOLUME
EC2_INSTANCE
ELASTIC_LOAD_BALANCER
ENVIRONMENT
EXTERNAL_SYNTHETIC_TEST
EXTERNAL_SYNTHETIC_TEST_STEP
GCP_ZONE
GEOLOCATION
GEOLOC_SITE
GOOGLE_COMPUTE_ENGINE
HOST
HOST_GROUP
HTTP_CHECK
HTTP_CHECK_STEP
HYPERVISOR
HYPERVISOR_CLUSTER
HYPERVISOR_DISK
KUBERNETES_CLUSTER
KUBERNETES_NODE
KUBERNETES_SERVICE
MOBILE_APPLICATION
MULTIPROTOCOL_MONITOR
NETWORK_INTERFACE
NEUTRON_SUBNET
OPENSTACK_AVAILABILITY_ZONE
OPENSTACK_COMPUTE_NODE
OPENSTACK_PROJECT
OPENSTACK_REGION
OPENSTACK_VM
OS
PROCESS_GROUP
PROCESS_GROUP_INSTANCE
QUEUE
QUEUE_INSTANCE
RELATIONAL_DATABASE_SERVICE
RUNTIME_COMPONENT
S3BUCKET
SERVICE
SERVICE_INSTANCE
SERVICE_METHOD
SERVICE_METHOD_GROUP
SOFTWARE_COMPONENT
SWIFT_CONTAINER
SYNTHETIC_LOCATION
SYNTHETIC_TEST
SYNTHETIC_TEST_STEP
VCENTER
VIRTUALMACHINE
VMWARE_DATACENTER
cloud:aws:acmprivateca
cloud:aws:api_gateway
cloud:aws:app_runner
cloud:aws:appstream
cloud:aws:appsync
cloud:aws:athena
cloud:aws:aurora
cloud:aws:autoscaling
cloud:aws:billing
cloud:aws:cassandra
cloud:aws:chatbot
cloud:aws:cloud_front
cloud:aws:cloudhsm
cloud:aws:cloudsearch
cloud:aws:codebuild
cloud:aws:cognito
cloud:aws:connect
cloud:aws:datasync
cloud:aws:dax
cloud:aws:dms
cloud:aws:documentdb
cloud:aws:dxcon
cloud:aws:dynamodb
cloud:aws:ebs
cloud:aws:ec2_spot
cloud:aws:ec2api
cloud:aws:ecs
cloud:aws:ecs:cluster
cloud:aws:efs
cloud:aws:eks:cluster
cloud:aws:elasticache
cloud:aws:elasticbeanstalk
cloud:aws:elasticinference
cloud:aws:elastictranscoder
cloud:aws:emr
cloud:aws:es
cloud:aws:events
cloud:aws:fsx
cloud:aws:gamelift
cloud:aws:glue
cloud:aws:inspector
cloud:aws:iot
cloud:aws:iot_things_graph
cloud:aws:iotanalytics
cloud:aws:kafka
cloud:aws:kinesis:data_analytics
cloud:aws:kinesis:data_firehose
cloud:aws:kinesis:data_stream
cloud:aws:kinesis:video_stream
cloud:aws:lambda
cloud:aws:lex
cloud:aws:logs
cloud:aws:media_tailor
cloud:aws:mediaconnect
cloud:aws:mediaconvert
cloud:aws:mediapackagelive
cloud:aws:mediapackagevod
cloud:aws:mq
cloud:aws:nat_gateway
cloud:aws:neptune
cloud:aws:opsworks
cloud:aws:polly
cloud:aws:qldb
cloud:aws:rds
cloud:aws:redshift
cloud:aws:rekognition
cloud:aws:robomaker
cloud:aws:route53
cloud:aws:route53resolver
cloud:aws:s3
cloud:aws:sage_maker:batch_transform_job
cloud:aws:sage_maker:endpoint
cloud:aws:sage_maker:endpoint_instance
cloud:aws:sage_maker:ground_truth
cloud:aws:sage_maker:processing_job
cloud:aws:sage_maker:training_job
cloud:aws:servicecatalog
cloud:aws:ses
cloud:aws:sns
cloud:aws:sqs
cloud:aws:ssm-runcommand
cloud:aws:states
cloud:aws:storagegateway
cloud:aws:swf
cloud:aws:textract
cloud:aws:transfer
cloud:aws:transitgateway
cloud:aws:translate
cloud:aws:trustedadvisor
cloud:aws:usage
cloud:aws:vpn
cloud:aws:waf
cloud:aws:wafv2
cloud:aws:workmail
cloud:aws:workspaces
cloud:azure:apimanagement:service
cloud:azure:app:containerapps
cloud:azure:app:managedenvironments
cloud:azure:appconfiguration:configurationstores
cloud:azure:appplatform:spring
cloud:azure:automation:automationaccounts
cloud:azure:batch:account
cloud:azure:blockchain:blockchainmembers
cloud:azure:cache:redis
cloud:azure:cdn:cdnwebapplicationfirewallpolicies
cloud:azure:cdn:profiles
cloud:azure:classic_storage_account
cloud:azure:classic_storage_account:blob
cloud:azure:classic_storage_account:file
cloud:azure:classic_storage_account:queue
cloud:azure:classic_storage_account:table
cloud:azure:classic_virtual_machine
cloud:azure:cognitiveservices:allinone
cloud:azure:cognitiveservices:anomalydetector
cloud:azure:cognitiveservices:bingautosuggest
cloud:azure:cognitiveservices:bingcustomsearch
cloud:azure:cognitiveservices:bingentitysearch
cloud:azure:cognitiveservices:bingsearch
cloud:azure:cognitiveservices:bingspellcheck
cloud:azure:cognitiveservices:computervision
cloud:azure:cognitiveservices:contentmoderator
cloud:azure:cognitiveservices:customvisionprediction
cloud:azure:cognitiveservices:customvisiontraining
cloud:azure:cognitiveservices:face
cloud:azure:cognitiveservices:immersivereader
cloud:azure:cognitiveservices:inkrecognizer
cloud:azure:cognitiveservices:luis
cloud:azure:cognitiveservices:luisauthoring
cloud:azure:cognitiveservices:openai
cloud:azure:cognitiveservices:personalizer
cloud:azure:cognitiveservices:qnamaker
cloud:azure:cognitiveservices:speech
cloud:azure:cognitiveservices:textanalytics
cloud:azure:cognitiveservices:translator
cloud:azure:containerinstance:containergroup
cloud:azure:containerregistry:registries
cloud:azure:containerservice:managedcluster
cloud:azure:datafactory:v1
cloud:azure:datafactory:v2
cloud:azure:datalakeanalytics:accounts
cloud:azure:datalakestore:accounts
cloud:azure:datashare:accounts
cloud:azure:devices:iothubs
cloud:azure:devices:provisioningservices
cloud:azure:documentdb:databaseaccounts:global
cloud:azure:documentdb:databaseaccounts:mongo
cloud:azure:eventgrid:domains
cloud:azure:eventgrid:systemtopics
cloud:azure:eventgrid:topics
cloud:azure:eventhub:clusters
cloud:azure:frontdoor
cloud:azure:hdinsight:cluster
cloud:azure:hybridcompute:machines
cloud:azure:insights:components
cloud:azure:iotcentral:iotapps
cloud:azure:keyvault:vaults
cloud:azure:kusto:clusters
cloud:azure:logic:integrationserviceenvironments
cloud:azure:logic:workflows
cloud:azure:machinelearningservices:workspaces
cloud:azure:maps:accounts
cloud:azure:mariadb:server
cloud:azure:media:mediaservices
cloud:azure:media:mediaservices:streamingendpoints
cloud:azure:mysql:flexibleservers
cloud:azure:mysql:server
cloud:azure:netapp:netappaccounts:capacitypools
cloud:azure:netapp:netappaccounts:capacitypools:volumes
cloud:azure:network:applicationgateways
cloud:azure:network:azurefirewalls
cloud:azure:network:dnszones
cloud:azure:network:expressroutecircuits
cloud:azure:network:loadbalancers:basic
cloud:azure:network:loadbalancers:gateway
cloud:azure:network:loadbalancers:standard
cloud:azure:network:networkinterfaces
cloud:azure:network:networkwatchers:connectionmonitors
cloud:azure:network:networkwatchers:connectionmonitors:previe
cloud:azure:network:privatednszones
cloud:azure:network:publicipaddresses
cloud:azure:notificationhubs:namespaces:notificationhubs
cloud:azure:postgresql:flexibleservers
cloud:azure:postgresql:server
cloud:azure:postgresql:serverv2
cloud:azure:powerbidedicated:capacities
cloud:azure:recoveryservices:vaults
cloud:azure:relay:namespaces
cloud:azure:search:searchservices
cloud:azure:servicefabricmesh:applications
cloud:azure:signalrservice:signalr
cloud:azure:sql:managed
cloud:azure:sql:servers
cloud:azure:sql:servers:databases:datawarehouse
cloud:azure:sql:servers:databases:dtu
cloud:azure:sql:servers:databases:hyperscale
cloud:azure:sql:servers:databases:vcore
cloud:azure:sql:servers:elasticpools:dtu
cloud:azure:sql:servers:elasticpools:vcore
cloud:azure:storage:storageaccounts
cloud:azure:storage:storageaccounts:blob
cloud:azure:storage:storageaccounts:file
cloud:azure:storage:storageaccounts:queue
cloud:azure:storage:storageaccounts:table
cloud:azure:storagesync:storagesyncservices
cloud:azure:streamanalytics:streamingjobs
cloud:azure:synapse:workspaces
cloud:azure:synapse:workspaces:bigdatapools
cloud:azure:synapse:workspaces:sqlpools
cloud:azure:timeseriesinsights:environments
cloud:azure:timeseriesinsights:eventsources
cloud:azure:traffic_manager_profile
cloud:azure:virtual_network_gateway
cloud:azure:web:appslots
cloud:azure:web:functionslots
cloud:azure:web:hostingenvironments:v2
cloud:azure:web:serverfarms
cloud:gcp:autoscaler
cloud:gcp:bigquery_biengine_model
cloud:gcp:cloud_function
cloud:gcp:cloud_run_revision
cloud:gcp:cloudsql_database
cloud:gcp:filestore_instance
cloud:gcp:gae_app
cloud:gcp:gce_instance
cloud:gcp:gcs_bucket
cloud:gcp:https_lb
cloud:gcp:instance_group
cloud:gcp:internal_http_lb_rule
cloud:gcp:internal_network_lb_rule
cloud:gcp:k8s_cluster
cloud:gcp:k8s_container
cloud:gcp:k8s_node
cloud:gcp:k8s_pod
cloud:gcp:network_lb_rule
cloud:gcp:project
cloud:gcp:pubsub_snapshot
cloud:gcp:pubsub_subscription
cloud:gcp:pubsub_topic
cloud:gcp:pubsublite_subscription_partition
cloud:gcp:pubsublite_topic_partition
cloud:gcp:tcp_ssl_proxy_rule
cloud:gcp:tpu_worker
os:service
Configuring real-time updates
Currently, the Dynatrace API lacks support for programmatic webhook creation. To set up a webhook configuration in Dynatrace for sending alert notifications to the Ocean integration, follow these steps:
Prerequisite
Prepare a webhook URL
using this format: {app_host}/integration/webhook/problem
. The app_host
parameter should match the ingress or external load balancer where the integration will be deployed. For example, if your ingress or load balancer exposes the Dynatrace Ocean integration at https://myservice.domain.com
, your webhook URL
should be https://myservice.domain.com/integration/webhook/problem
.
Create a webhook in Dynatrace
- Go to Dynatrace.
- Go to Settings > Integration > Problem notifications.
- Select Add notification.
- Select Custom integration from the available notification types.
- Configure the notification using the following details.
Enabled
- ensure the notification is enabled.Display name
- use a meaningful name such as Port Ocean Webhook.Webhook URL
- enter the value of theURL
you created above.- Enable Call webhook is new events merge into existing problems.
Custom payload
- paste the following configuration:
You can customize to your taste, the only important thing is the{
"State":"{State}",
"ProblemID":"{ProblemID}",
"ProblemTitle":"{ProblemTitle}"
}ProblemID
key. The webhook integration will not work without it. 6.Alerting profile
- select the corresponding alerting profile. 7. Leave the rest of the fields as is. - Click Save changes.
Ingest data into Port
To ingest Dynatrace objects using the integration configuration, you can follow the steps below:
- Go to the DevPortal Builder page.
- Select a blueprint you want to ingest using Dynatrace.
- Choose the Ingest Data option from the menu.
- Select Dynatrace under the Incident Management category.
- Modify the configuration according to your needs.
- Click
Resync
.
Examples
Examples of blueprints and the relevant integration configurations:
Entity
Entity blueprint
{
"identifier": "dynatraceEntity",
"description": "This blueprint represents a Dynatrace Entity",
"title": "Dynatrace Entity",
"icon": "Dynatrace",
"schema": {
"properties": {
"firstSeen": {
"type": "string",
"title": "First Seen",
"description": "The timestamp at which the entity was first seen, in UTC milliseconds.",
"format": "date-time"
},
"lastSeen": {
"type": "string",
"title": "Last Seen",
"description": "The timestamp at which the entity was last seen, in UTC milliseconds.",
"format": "date-time"
},
"type": {
"type": "string",
"title": "Type",
"description": "The type of the entity."
},
"tags": {
"type": "array",
"title": "Tags",
"description": "A list of tags of the entity.",
"items": {
"type": "string"
}
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: entity
selector:
query: "true"
entityTypes: ["APPLICATION", "SERVICE"] # An optional list of entity types to filter by. If not specified, defaults to ["APPLICATION", "SERVICE"].
port:
entity:
mappings:
identifier: .entityId
title: .displayName
blueprint: '"dynatraceEntity"'
properties:
firstSeen: ".firstSeenTms / 1000 | todate"
lastSeen: ".lastSeenTms / 1000 | todate"
type: .type
tags: .tags[].stringRepresentation
Problem
Problem blueprint
{
"identifier": "dynatraceProblem",
"description": "This blueprint represents a Dynatrace Problem",
"title": "Dynatrace Problem",
"icon": "Dynatrace",
"schema": {
"properties": {
"entityTags": {
"type": "array",
"title": "Entity Tags",
"description": "A list of all entity tags of the problem.",
"items": {
"type": "string"
}
},
"evidenceDetails": {
"type": "array",
"title": "Evidence Details",
"description": "A list of all evidence details of the problem.",
"items": {
"type": "string"
}
},
"managementZones": {
"type": "array",
"title": "Management Zones",
"description": "A list of all management zones that the problem belongs to.",
"items": {
"type": "string"
}
},
"problemFilters": {
"type": "array",
"title": "Problem Filters",
"description": "A list of alerting profiles that match the problem.",
"items": {
"type": "string"
}
},
"severityLevel": {
"type": "string",
"title": "Severity Level",
"description": "The severity level of the problem.",
"enum": [
"AVAILABILITY",
"CUSTOM_ALERT",
"ERROR",
"INFO",
"MONITORING_UNAVAILABLE",
"PERFORMANCE",
"RESOURCE_CONTENTION"
],
"enumColors": {
"AVAILABILITY": "blue",
"CUSTOM_ALERT": "turquoise",
"ERROR": "red",
"INFO": "green",
"MONITORING_UNAVAILABLE": "darkGray",
"PERFORMANCE": "orange",
"RESOURCE_CONTENTION": "yellow"
}
},
"status": {
"type": "string",
"title": "Status",
"description": "The status of the problem.",
"enum": ["CLOSED", "OPEN"],
"enumColors": {
"CLOSED": "green",
"OPEN": "red"
}
},
"startTime": {
"type": "string",
"title": "Start Time",
"description": "The start time of the problem, in UTC milliseconds.",
"format": "date-time"
},
"endTime": {
"type": "string",
"title": "End Time",
"description": "The end time of the problem, in UTC milliseconds.",
"format": "date-time"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"impactedEntities": {
"title": "Impacted Entities",
"target": "dynatraceEntity",
"required": false,
"many": true
},
"linkedProblemInfo": {
"title": "Linked Problem Info",
"target": "dynatraceProblem",
"required": false,
"many": false
},
"rootCauseEntity": {
"title": "Root Cause Entity",
"target": "dynatraceEntity",
"required": false,
"many": false
}
}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: problem
selector:
query: "true"
port:
entity:
mappings:
identifier: .problemId
title: .title
blueprint: '"dynatraceProblem"'
properties:
entityTags: .entityTags[].stringRepresentation
evidenceDetails: .evidenceDetails.details[].displayName
managementZones: .managementZones[].name
problemFilters: .problemFilters[].name
severityLevel: .severityLevel
status: .status
startTime: ".startTime / 1000 | todate"
endTime: ".endTime | if . == -1 then null else (./1000 | todate) end"
relations:
impactedEntities: .impactedEntities[].entityId.id
linkedProblemInfo: .linkedProblemInfo.problemId
rootCauseEntity: .rootCauseEntity.entityId.id
SLO
SLO blueprint
{
"identifier": "dynatraceSlo",
"description": "This blueprint represents a Dynatrace SLO",
"title": "Dynatrace SLO",
"icon": "Dynatrace",
"schema": {
"properties": {
"status": {
"type": "string",
"title": "Status",
"description": "The status of the SLO.",
"enum": ["FAILURE", "WARNING", "SUCCESS"],
"enumColors": {
"FAILURE": "red",
"WARNING": "yellow",
"SUCCESS": "green"
}
},
"target": {
"type": "number",
"title": "Target",
"description": "The target value of the SLO."
},
"enabled": {
"type": "boolean",
"title": "Enabled",
"description": "Whether the SLO is enabled."
},
"warning": {
"type": "number",
"title": "Warning",
"description": "The warning value of the SLO. At warning state the SLO is still fulfilled but is getting close to failure."
},
"error": {
"type": "string",
"title": "Error",
"description": "The error of the SLO calculation. If the value differs from NONE, there is something wrong with the SLO calculation."
},
"errorBudget": {
"type": "number",
"title": "Error Budget",
"description": "The error budget of the calculated SLO."
},
"evaluatedPercentage": {
"type": "number",
"title": "Evaluated Percentage",
"description": "The calculated status value of the SLO."
},
"evaluationType": {
"type": "string",
"title": "Evaluation Type",
"description": "The type of the SLO evaluation."
},
"filter": {
"type": "string",
"title": "Filter",
"description": "The filter for the SLO evaluation."
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: slo
selector:
query: "true"
port:
entity:
mappings:
identifier: .id
title: .name
blueprint: '"dynatraceSlo"'
properties:
status: .status
target: .target
enabled: .enabled
warning: .warning
error: .error
errorBudget: .errorBudget
evaluatedPercentage: .evaluatedPercentage
evaluationType: .evaluationType
filter: .filter
Let's Test It
This section includes a sample response data from Dynatrace. In addition, it includes the entity created from the resync event based on the Ocean configuration provided in the previous section.
Payload
Here is an example of the payload structure from Dynatrace:
Entity response data
{
"displayName": "my host",
"entityId": "HOST-06F288EE2A930951",
"firstSeenTms": 1574697667547,
"fromRelationships": {
"isInstanceOf": [
{
"id": "HOST_GROUP-0E489369D663A4BF",
"type": "HOST_GROUP"
}
]
},
"icon": {
"customIconPath": "host",
"primaryIconType": "linux",
"secondaryIconType": "microsoft-azure-signet"
},
"lastSeenTms": 1588242361417,
"managementZones": [
{
"id": "6239538939987181652",
"name": "main app"
}
],
"properties": {
"bitness": 64,
"cpuCores": 8,
"monitoringMode": "FULL_STACK",
"networkZoneId": "aws.us.east01",
"osArchitecture": "X86",
"osType": "LINUX"
},
"tags": [
{
"context": "CONTEXTLESS",
"key": "architecture",
"stringRepresentation": "architecture:x86",
"value": "x86"
},
{
"context": "ENVIRONMENT",
"key": "Infrastructure",
"stringRepresentation": "[ENVIRONMENT]Infrastructure:Linux",
"value": "Linux"
}
],
"toRelationships": {
"isDiskOf": [
{
"id": "DISK-0393340DCA3853B0",
"type": "DISK"
}
]
},
"type": "HOST"
}
Problem response data
{
"affectedEntities": [
{
"entityId": {
"id": "string",
"type": "string"
},
"name": "string"
}
],
"displayId": "string",
"endTime": 1574697669865,
"entityTags": [
{
"context": "CONTEXTLESS",
"key": "architecture",
"stringRepresentation": "architecture:x86",
"value": "x86"
},
{
"context": "ENVIRONMENT",
"key": "Infrastructure",
"stringRepresentation": "[ENVIRONMENT]Infrastructure:Linux",
"value": "Linux"
}
],
"evidenceDetails": {
"details": [
{
"displayName": "Availability evidence",
"entity": {},
"evidenceType": "AVAILABILITY_EVIDENCE",
"groupingEntity": {},
"rootCauseRelevant": true,
"startTime": 1
},
{
"displayName": "User action evidence",
"entity": {},
"evidenceType": "USER_ACTION_EVIDENCE",
"groupingEntity": {},
"rootCauseRelevant": true,
"startTime": 1
}
],
"totalCount": 1
},
"impactAnalysis": {
"impacts": [
{
"estimatedAffectedUsers": 1,
"impactType": "APPLICATION",
"impactedEntity": {}
}
]
},
"impactLevel": "APPLICATION",
"impactedEntities": [{}],
"linkedProblemInfo": {
"displayId": "string",
"problemId": "string"
},
"managementZones": [
{
"id": "string",
"name": "HOST"
}
],
"problemFilters": [
{
"id": "E2A930951",
"name": "BASELINE"
}
],
"problemId": "06F288EE2A930951",
"recentComments": {
"comments": [
{
"authorName": "string",
"content": "string",
"context": "string",
"createdAtTimestamp": 1,
"id": "string"
}
],
"nextPageKey": "AQAAABQBAAAABQ==",
"pageSize": 1,
"totalCount": 1
},
"rootCauseEntity": {},
"severityLevel": "AVAILABILITY",
"startTime": 1574697667547,
"status": "CLOSED",
"title": "title"
}
SLO response data
{
"burnRateMetricKey": "func:slo.errorBudgetBurnRate.payment_service_availability",
"denominatorValue": 90,
"description": "Rate of successful payments per week",
"enabled": true,
"error": "NONE",
"errorBudget": 1.25,
"errorBudgetBurnRate": {
"burnRateType": "SLOW",
"burnRateValue": 1.25,
"burnRateVisualizationEnabled": true,
"estimatedTimeToConsumeErrorBudget": 24,
"fastBurnThreshold": 1.5,
"sloValue": 95
},
"errorBudgetMetricKey": "func:slo.errorBudget.payment_service_availability",
"evaluatedPercentage": 96.25,
"evaluationType": "AGGREGATE",
"filter": "type(\"SERVICE\")",
"id": "123e4567-e89b-42d3-a456-556642440000",
"metricDenominator": "builtin:service.requestCount.server",
"metricExpression": "(100)*(builtin:service.errors.server.successCount:splitBy())/(builtin:service.requestCount.server:splitBy())",
"metricKey": "func:slo.payment_service_availability",
"metricNumerator": "builtin:service.errors.server.successCount",
"metricRate": "builtin:service.successes.server.rate",
"name": "Payment service availability",
"normalizedErrorBudgetMetricKey": "func:slo.normalizedErrorBudget.payment_service_availability",
"numeratorValue": 80,
"problemFilters": "[type(\"SERVICE\")]",
"relatedOpenProblems": 1,
"relatedTotalProblems": 1,
"status": "WARNING",
"target": 95,
"timeframe": "-1d",
"useRateMetric": true,
"warning": 97.5
}
Mapping Result
The combination of the sample payload and the Ocean configuration generates the following Port entity:
Entity entity in Port
{
"identifier": "HOST-06F288EE2A930951",
"title": "my host",
"blueprint": "dynatraceEntity",
"team": [],
"icon": "Dynatrace",
"properties": {
"firstSeen": "2019-11-25T14:14:27Z",
"lastSeen": "2020-04-30T14:52:41Z",
"type": "HOST",
"tags": ["architecture:x86", "[ENVIRONMENT]Infrastructure:Linux"]
},
"relations": {},
"createdAt": "2024-2-6T09:30:57.924Z",
"createdBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW",
"updatedAt": "2024-2-6T11:49:20.881Z",
"updatedBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW"
}
Problem entity in Port
{
"identifier": "06F288EE2A930951",
"title": "title",
"blueprint": "dynatraceProblem",
"team": [],
"icon": "Dynatrace",
"properties": {
"entityTags": ["architecture:x86", "[ENVIRONMENT]Infrastructure:Linux"],
"evidenceDetails": ["Availability evidence", "User action evidence"],
"managementZones": ["HOST"],
"problemFilters": ["BASELINE"],
"severityLevel": "AVAILABILITY",
"status": "CLOSED",
"startTime": "2019-11-25T14:14:27Z",
"endTime": "2020-04-30T14:52:41Z"
},
"relations": {
"impactedEntities": ["HOST-06F288EE2A930951"],
"linkedProblemInfo": "06F288EE2A930951",
"rootCauseEntity": "HOST-06F288EE2A930951"
},
"createdAt": "2024-2-6T09:30:57.924Z",
"createdBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW",
"updatedAt": "2024-2-6T11:49:20.881Z",
"updatedBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW"
}
SLO entity in Port
{
"identifier": "123e4567-e89b-42d3-a456-556642440000",
"title": "Payment service availability",
"blueprint": "dynatraceSlo",
"team": [],
"icon": "Dynatrace",
"properties": {
"status": "WARNING",
"target": 95,
"enabled": true,
"warning": 97.5,
"error": "NONE",
"errorBudget": 1.25,
"evaluatedPercentage": 96.25,
"evaluationType": "AGGREGATE",
"filter": "type(\"SERVICE\")"
},
}
Alternative installation via webhook
While the Ocean integration described above is the recommended installation method, you may prefer to use a webhook to ingest problem from Dynatrace. If so, use the following instructions:
Note that when using this method, data will be ingested into Port only when the webhook is triggered.
Webhook installation (click to expand)
Port configuration
Create the following blueprint definitions:
Dynatrace microservice blueprint
{
"identifier": "microservice",
"title": "Microservice",
"icon": "Service",
"schema": {
"properties": {
"description": {
"title": "Description",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Dynatrace problem blueprint
{
"identifier": "dynatraceProblem",
"description": "This blueprint represents a Dynatrace problem in our software catalog",
"title": "Dynatrace Problem",
"icon": "Deployment",
"schema": {
"properties": {
"state": {
"type": "string",
"title": "Problem State"
},
"url": {
"type": "string",
"format": "url",
"title": "Problem URL"
},
"details": {
"type": "string",
"title": "Details"
},
"impact": {
"type": "string",
"title": "Problem Impact"
},
"severity": {
"type": "string",
"title": "Problem Severity"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"title": "Tags"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"microservice": {
"title": "Impacted Services",
"target": "microservice",
"required": false,
"many": true
}
}
}
Create the following webhook configuration using Port's UI:
Dynatrace problem webhook configuration
-
Basic details tab - fill the following details:
- Title :
Dynatrace Problem Mapper
; - Identifier :
dynatrace_problem_mapper
; - Description :
A webhook configuration for problem events from Dynatrace
; - Icon :
Dynatrace
;
- Title :
-
Integration configuration tab - fill the following JQ mapping:
[
{
"blueprint": "dynatraceProblem",
"entity": {
"identifier": ".body.PID | tostring",
"title": ".body.ProblemTitle",
"properties": {
"state": ".body.State",
"url": ".body.ProblemURL",
"details": ".body.ProblemDetailsText",
"impact": ".body.ProblemImpact",
"severity": ".body.ProblemSeverity",
"tags": ".body.ProblemTags | split(\", \")"
},
"relations": {
"microservice": "[.body.ImpactedEntities[].entity]"
}
}
}
] -
Click Save at the bottom of the page.
The webhook configuration's relation mapping will function properly only when the identifiers of the Port microservice entities match the names of the entities in your Dynatrace.
If there is a mismatch, you can utilize Dynatrace Tags to align the actual identifier in Port.
To do this, create a tag with the key proj
and value microservice_identifier
.
Then, update the relation JQ syntax to establish a connection between the Dynatrace problem and the Port microservice. Here is the updated JQ Mappings:
{
"blueprint": "dynatraceProblem",
"entity": {
...Properties mappings,
"relations": {
"microservice": ".body.ProblemTags | split(\", \") | map(select(test(\"proj:\")) | sub(\"proj:\";\"\"))"
}
}
}
Details
JQ expression explained
The above JQ expression will split the tags by comma and space, then filter the tags that start withproj:
and remove the proj:
prefix from the tag value.Create a webhook in Dynatrace
- Log in to Dynatrace with your credentials.
- Click on Settings at the left sidebar of the page.
- Choose Integration and click on Problem notifications.
- Select Add notification.
- Select Custom integration from the available integration types.
- Input the following details:
-
Display name
- use a meaningful name such as Port Webhook. -
Webhook URL
- enter the value of theurl
key you received after creating the webhook configuration. -
Overview
- you can add an optional HTTP header to your webhook request. -
Custom payload
- When a problem is detected or resolved on your entity, this payload will be sent to the webhook URL. You can enter this JSON placeholder in the textbox:{
"State":"{State}",
"PID":"{PID}",
"ProblemTitle":"{ProblemTitle}",
"ImpactedEntity": "{ImpactedEntity}",
"ProblemDetailsText": "{ProblemDetailsText}",
"ProblemImpact": "{ProblemImpact}",
"ProblemSeverity": "{ProblemSeverity}",
"ProblemURL": "{ProblemURL}",
"ProblemTags": "{ProblemTags}",
"ImpactedEntities": {ImpactedEntities}
} -
Alerting profile
- configure your preferred alerting rule or use the default one.
-
- Click Save changes at the bottom of the page.
To view the different payloads and events available in Dynatrace webhooks, look here.
Done! Any problem detected on your Dynatrace entity will trigger a webhook event. Port will parse the events according to the mapping and update the catalog entities accordingly.
Let's Test It
This section includes a sample response data from Dynatrace. In addition, it includes the entity created from the resync event based on the Ocean configuration provided in the previous section.
Payload
Here is an example of the payload structure from Dynatrace:
Problem response data
{
"affectedEntities": [
{
"entityId": {
"id": "string",
"type": "string"
},
"name": "string"
}
],
"displayId": "string",
"endTime": 1574697669865,
"entityTags": [
{
"context": "CONTEXTLESS",
"key": "architecture",
"stringRepresentation": "architecture:x86",
"value": "x86"
},
{
"context": "ENVIRONMENT",
"key": "Infrastructure",
"stringRepresentation": "[ENVIRONMENT]Infrastructure:Linux",
"value": "Linux"
}
],
"evidenceDetails": {
"details": [
{
"displayName": "Availability evidence",
"entity": {},
"evidenceType": "AVAILABILITY_EVIDENCE",
"groupingEntity": {},
"rootCauseRelevant": true,
"startTime": 1
},
{
"displayName": "User action evidence",
"entity": {},
"evidenceType": "USER_ACTION_EVIDENCE",
"groupingEntity": {},
"rootCauseRelevant": true,
"startTime": 1
}
],
"totalCount": 1
},
"impactAnalysis": {
"impacts": [
{
"estimatedAffectedUsers": 1,
"impactType": "APPLICATION",
"impactedEntity": {}
}
]
},
"impactLevel": "APPLICATION",
"impactedEntities": [{}],
"linkedProblemInfo": {
"displayId": "string",
"problemId": "string"
},
"managementZones": [
{
"id": "string",
"name": "HOST"
}
],
"problemFilters": [
{
"id": "E2A930951",
"name": "BASELINE"
}
],
"problemId": "06F288EE2A930951",
"recentComments": {
"comments": [
{
"authorName": "string",
"content": "string",
"context": "string",
"createdAtTimestamp": 1,
"id": "string"
}
],
"nextPageKey": "AQAAABQBAAAABQ==",
"pageSize": 1,
"totalCount": 1
},
"rootCauseEntity": {},
"severityLevel": "AVAILABILITY",
"startTime": 1574697667547,
"status": "CLOSED",
"title": "title"
}
Mapping Result
The combination of the sample payload and the Ocean configuration generates the following Port entity:
Problem entity in Port
{
"identifier": "06F288EE2A930951",
"title": "title",
"blueprint": "dynatraceProblem",
"team": [],
"icon": "Dynatrace",
"properties": {
"entityTags": ["architecture:x86", "[ENVIRONMENT]Infrastructure:Linux"],
"evidenceDetails": ["Availability evidence", "User action evidence"],
"managementZones": ["HOST"],
"problemFilters": ["BASELINE"],
"severityLevel": "AVAILABILITY",
"status": "CLOSED",
"startTime": "2019-11-25T14:14:27Z",
"endTime": "2020-04-30T14:52:41Z"
},
"relations": {
"impactedEntities": ["HOST-06F288EE2A930951"],
"linkedProblemInfo": "06F288EE2A930951",
"rootCauseEntity": "HOST-06F288EE2A930951"
},
"createdAt": "2024-2-6T09:30:57.924Z",
"createdBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW",
"updatedAt": "2024-2-6T11:49:20.881Z",
"updatedBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW"
}
Ingest Dynatrace Entities
In this example,
you will create a dynatrace_entity
blueprint that ingests monitored entities from your Dynatrace account.
You will then add a Python script to make API calls to Dynatrace REST API and fetch data for your account.