DQL
Questions about Dynatrace Query Language
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to retrieve data from another tenant and display in chart through DQL

Hillman
Helper

We have created some charts which display the data from another tenant. In gen2 version, it can be configured like this:

Hillman_0-1767062399659.png

When I try to convert the chart to gen3 version, I am not sure how to use DQL to achieve this. Any suggestions?

12 REPLIES 12

olga_wall
Helper

Hi Hillman,

I haven't done this myself yet but I think you can achieve this by querying the data from another tenant via API in a code tile on a gen3 dashboard.

Mohamed_Hamdy
DynaMight Leader
DynaMight Leader

Hi @Hillman ,

In Dynatrace 3rd Gen, a flexible approach to query data from another environment is to use a Dashboard/Notebook Code tile (external/remote environment data). The idea is to create a Platform Token in the remote environment with the required storage read scopes, store it in the local environment Credential Vault, and then call the remote query endpoint from the Code tile. You can use the sample below and simply replace the remote URL, Credential Vault, and adjust the DQL as needed.

import { credentialVaultClient } from "@dynatrace-sdk/client-classic-environment-v2";

async function fetchRemoteDql(credentialId, url, query) {
  const { token } = await credentialVaultClient.getCredentialsDetails({ id: credentialId });

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({
      query,
      requestTimeoutMilliseconds: 60000,
      enablePreview: true,
    }),
  });

  if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
  return (await response.json()).result;
}

export default async function () {
  const credentialId = "CREDENTIALS_VAULT-XXXXXXX"; // your vault entry on the *local* env
  const url =
    "https://XXXXXXXX.apps.dynatrace.com/platform/storage/query/v1/query:execute";
  const query = `
timeseries usage = avg(dt.host.cpu.usage), by: { dt.entity.host }
| fieldsAdd entityName(dt.entity.host)
| sort arrayAvg(usage) desc
| limit 20
  `.trim();

  return await fetchRemoteDql(credentialId, url, query);
}

Note: make sure the remote environment url is added to the allowlist if it is configured to limit external requests from the environment.

https://developer.dynatrace.com/develop/app-functions/allow-outbound-connections/

Regards,

Hamdy

 

Certified Dynatrace Professional | Certified Dynatrace Services Delivery - Observability & CloudOps | Dynatrace Partner - yourcompass.ca

Hi @Mohamed_Hamdy,

I have tried with the DQL but I have found that it throws the following error.

{
  "error": {
    "code": 540,
    "message": "Execution crashed.",
    "details": {
      "logs": "403: The requested credential details cannot be accessed by the current user or entity.\n    at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n    at eventLoopTick (ext:core/01_core.js:177:7)\n    at async fetchRemoteDql (file:///script.ts:4:21)\n    at async default (file:///script.ts:34:10)\n",
      "type": "UNCAUGHT_EXCEPTION",
      "message": "Uncaught (in promise) 403: The requested credential details cannot be accessed by the current user or entity.",
      "details": {
        "lineNumber": 6983,
        "startColumn": 19,
        "stack": "403: The requested credential details cannot be accessed by the current user or entity.\n    at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n    at eventLoopTick (ext:core/01_core.js:177:7)\n    at async fetchRemoteDql (file:///script.ts:4:21)\n    at async default (file:///script.ts:34:10)"
      }
    }
  }
}

 

Hello @Hillman ,

It looks like the failure is related to Credential Vault access (the code tile can’t read the credential)

Could you please confirm the following:

  1. Did you create a Platform token in the remote environment (not an Environment API access token)? If yes, what scopes/permissions are enabled on that token?

  2. How was the Credential Vault entry created on the local environment?

For more details on Platform Tokens, please refer to Dynatrace documentation: https://docs.dynatrace.com/docs/shortlink/platform-tokens

Certified Dynatrace Professional | Certified Dynatrace Services Delivery - Observability & CloudOps | Dynatrace Partner - yourcompass.ca

Hi @Mohamed_Hamdy ,

Oh, I have mixed up the Platform token with the Environment API access token. Let me create one and try again. Regarding the scope, is it granting all storage:*:read?

@Mohamed_Hamdy I have created a Platform Token with all "storage:*:read" scopes in remote tenant, then create a credential vault entry in local tenant. However, it still returns the following error:

{
  "error": {
    "code": 540,
    "message": "Execution crashed.",
    "details": {
      "logs": "403: The requested credential details cannot be accessed by the current user or entity.\n    at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n    at eventLoopTick (ext:core/01_core.js:177:7)\n    at async fetchRemoteDql (file:///script.ts:4:21)\n    at async default (file:///script.ts:34:10)\n",
      "type": "UNCAUGHT_EXCEPTION",
      "message": "Uncaught (in promise) 403: The requested credential details cannot be accessed by the current user or entity.",
      "details": {
        "lineNumber": 6983,
        "startColumn": 19,
        "stack": "403: The requested credential details cannot be accessed by the current user or entity.\n    at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n    at eventLoopTick (ext:core/01_core.js:177:7)\n    at async fetchRemoteDql (file:///script.ts:4:21)\n    at async default (file:///script.ts:34:10)"
      }
    }
  }
}

 

@Mohamed_Hamdy I have found out the root cause now. I have to enable the following options in credential vault entry to make it work:

Hillman_0-1767864752383.png

 

@Mohamed_Hamdy Sorry that I have got one more question. If the original DQL contains "`". How can I embed it into the code?

Hello @Hillman , 

If your original DQL contains a backtick ` and you’re embedding it in the code using a JavaScript template literal (also delimited by backticks), you just need to escape the backtick inside the DQL.

Use  (backslash + backtick).

Example:

const query = `
fetch logs
| filter contains(content, \`some-value\`)
| limit 20
`.trim();

 

Certified Dynatrace Professional | Certified Dynatrace Services Delivery - Observability & CloudOps | Dynatrace Partner - yourcompass.ca

follow the question, can you help me with the query? there is lookup to join two table in this. I have result when i run two query separate but when I combine them with lookup, there is no result. but they support have results as 

export default async function() {
const credentialId = "CREDENTIALS_VAULT-A80F208D9C83954A"; // Replace with your credential vault ID.
const url = "https://{environmentid}.apps.dynatrace.com/platform/storage/query/v1/query:execute"; // Replace with API URL.
const query =
load "/lookups/2026Wave1-4"
| fieldsAdd Servers=lower(Name)
| fieldsAdd tenant= if(IN(Environment,{"Production", "DisasterRecovery"}), "Production"
,else: if(In(Environment,{"Test","QualityAssurance","QAEnvironment"}),"Non-Prod"))
| filterout In(Environment,{"Production", "DisasterRecovery"})
|lookup [fetch dt.entity.host
|fieldsAdd hostname = entity.name, hostGroupName, installerVersion
| parse hostname, """LD:Host '.'"""
| fields Host = lower(Host), entity.name, hostGroupName], sourceField:Servers,lookupField:Host, prefix:"DT-"
| fieldsAdd Status = if(`Servers` == `DT-Host`, "Dynatrace", else:"need onboard")
//|filter contains(`AppCode`,"FDP")
| filter isNotNull(tenant)
// |filter contains(`Wave`,"")
| summarize by:{AppCode, Environment, Tier,Wave, tenant, SRE}, {
Plan=countif(Status=="need onboard"),
Installed=countif(Status == "Dynatrace"),
total=count()

}
//| fieldsAdd Need_Onboard = (toDouble(Plan) / toDouble(total)) * 100
| fieldsAdd Onboard_status = (toDouble(Installed) / toDouble(total)) * 100
| fieldsAdd Status = if(Onboard_status == 100, "Done",
else: if(Onboard_status > 0, "In-Progress",
else: "Not_started"))
";

Try this, I’ve only corrected the backticks/field reference to avoid parsing issues. Please let me know if it works on your side.

const query = `
load "/lookups/2026Wave1-4"
| fieldsAdd Servers=lower(Name)
| fieldsAdd tenant= if(IN(Environment,{"Production", "DisasterRecovery"}), "Production"
,else: if(In(Environment,{"Test","QualityAssurance","QAEnvironment"}),"Non-Prod"))
| filterout In(Environment,{"Production", "DisasterRecovery"})
|lookup [fetch dt.entity.host
  |fieldsAdd hostname = entity.name, hostGroupName, installerVersion
  | parse hostname, """LD:Host '.'"""
  | fields Host = lower(Host), entity.name, hostGroupName
], sourceField:Servers, lookupField:Host, prefix:"DT-"
| fieldsAdd Status = if(Servers == \`DT-Host\`, "Dynatrace", else:"need onboard")
| filter isNotNull(tenant)
| summarize by:{AppCode, Environment, Tier, Wave, tenant, SRE}, {
    Plan=countif(Status=="need onboard"),
    Installed=countif(Status == "Dynatrace"),
    total=count()
}
| fieldsAdd Onboard_status = (toDouble(Installed) / toDouble(total)) * 100
| fieldsAdd Status = if(Onboard_status == 100, "Done",
  else: if(Onboard_status > 0, "In-Progress",
  else: "Not_started"))
`.trim();

 

Certified Dynatrace Professional | Certified Dynatrace Services Delivery - Observability & CloudOps | Dynatrace Partner - yourcompass.ca

@Mohamed_Hamdy I have tried to use backslash to escape but the query still fails. ☹️

Featured Posts