Skip to content

Commit f6a8cef

Browse files
committed
feat: Add DNS monitoring capabilities
- Introduced new DNS monitor types and criteria checks in CriteriaFilter.ts. - Implemented DNS monitor instance creation in MonitorCriteriaInstance.ts. - Enhanced MonitorStep to support DNS configurations. - Added DNS response handling in ProbeMonitorResponse.ts. - Created DNS monitor forms and summary views in the Dashboard. - Developed DNS query utilities and response handling in MonitorTypes/DnsMonitor.ts. - Added DNS record types and response structures for better data handling. - Implemented criteria evaluation for DNS monitoring in DnsMonitorCriteria.ts.
1 parent 770ef00 commit f6a8cef

File tree

17 files changed

+1140
-5
lines changed

17 files changed

+1140
-5
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import DataToProcess from "../DataToProcess";
2+
import CompareCriteria from "./CompareCriteria";
3+
import {
4+
CheckOn,
5+
CriteriaFilter,
6+
FilterType,
7+
} from "../../../../Types/Monitor/CriteriaFilter";
8+
import DnsMonitorResponse, {
9+
DnsRecordResponse,
10+
} from "../../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
11+
import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
12+
import EvaluateOverTime from "./EvaluateOverTime";
13+
import CaptureSpan from "../../Telemetry/CaptureSpan";
14+
import logger from "../../Logger";
15+
16+
export default class DnsMonitorCriteria {
17+
@CaptureSpan()
18+
public static async isMonitorInstanceCriteriaFilterMet(input: {
19+
dataToProcess: DataToProcess;
20+
criteriaFilter: CriteriaFilter;
21+
}): Promise<string | null> {
22+
let threshold: number | string | undefined | null =
23+
input.criteriaFilter.value;
24+
25+
const dataToProcess: ProbeMonitorResponse =
26+
input.dataToProcess as ProbeMonitorResponse;
27+
28+
const dnsResponse: DnsMonitorResponse | undefined =
29+
dataToProcess.dnsResponse;
30+
31+
let overTimeValue: Array<number | boolean> | number | boolean | undefined =
32+
undefined;
33+
34+
if (
35+
input.criteriaFilter.evaluateOverTime &&
36+
input.criteriaFilter.evaluateOverTimeOptions
37+
) {
38+
try {
39+
overTimeValue = await EvaluateOverTime.getValueOverTime({
40+
projectId: (input.dataToProcess as ProbeMonitorResponse).projectId,
41+
monitorId: input.dataToProcess.monitorId!,
42+
evaluateOverTimeOptions: input.criteriaFilter.evaluateOverTimeOptions,
43+
metricType: input.criteriaFilter.checkOn,
44+
});
45+
46+
if (Array.isArray(overTimeValue) && overTimeValue.length === 0) {
47+
overTimeValue = undefined;
48+
}
49+
} catch (err) {
50+
logger.error(
51+
`Error in getting over time value for ${input.criteriaFilter.checkOn}`,
52+
);
53+
logger.error(err);
54+
overTimeValue = undefined;
55+
}
56+
}
57+
58+
// Check if DNS is online
59+
if (input.criteriaFilter.checkOn === CheckOn.DnsIsOnline) {
60+
const currentIsOnline: boolean | Array<boolean> =
61+
(overTimeValue as Array<boolean>) ||
62+
(input.dataToProcess as ProbeMonitorResponse).isOnline;
63+
64+
return CompareCriteria.compareCriteriaBoolean({
65+
value: currentIsOnline,
66+
criteriaFilter: input.criteriaFilter,
67+
});
68+
}
69+
70+
// Check DNS response time
71+
if (input.criteriaFilter.checkOn === CheckOn.DnsResponseTime) {
72+
threshold = CompareCriteria.convertToNumber(threshold);
73+
74+
if (threshold === null || threshold === undefined) {
75+
return null;
76+
}
77+
78+
const currentResponseTime: number | Array<number> =
79+
(overTimeValue as Array<number>) ||
80+
dnsResponse?.responseTimeInMs ||
81+
(input.dataToProcess as ProbeMonitorResponse).responseTimeInMs;
82+
83+
if (currentResponseTime === null || currentResponseTime === undefined) {
84+
return null;
85+
}
86+
87+
return CompareCriteria.compareCriteriaNumbers({
88+
value: currentResponseTime,
89+
threshold: threshold as number,
90+
criteriaFilter: input.criteriaFilter,
91+
});
92+
}
93+
94+
// Check if DNS record exists
95+
if (input.criteriaFilter.checkOn === CheckOn.DnsRecordExists) {
96+
const exists: boolean = Boolean(
97+
dnsResponse?.records && dnsResponse.records.length > 0,
98+
);
99+
100+
const isTrue: boolean =
101+
input.criteriaFilter.filterType === FilterType.True;
102+
const isFalse: boolean =
103+
input.criteriaFilter.filterType === FilterType.False;
104+
105+
if (exists && isTrue) {
106+
return `DNS records exist for the query.`;
107+
}
108+
109+
if (!exists && isFalse) {
110+
return `No DNS records found for the query.`;
111+
}
112+
113+
return null;
114+
}
115+
116+
// Check DNSSEC validity
117+
if (input.criteriaFilter.checkOn === CheckOn.DnssecIsValid) {
118+
const isTrue: boolean =
119+
input.criteriaFilter.filterType === FilterType.True;
120+
const isFalse: boolean =
121+
input.criteriaFilter.filterType === FilterType.False;
122+
123+
if (dnsResponse?.isDnssecValid === undefined) {
124+
return null;
125+
}
126+
127+
if (dnsResponse.isDnssecValid && isTrue) {
128+
return `DNSSEC is valid.`;
129+
}
130+
131+
if (!dnsResponse.isDnssecValid && isFalse) {
132+
return `DNSSEC is not valid.`;
133+
}
134+
135+
return null;
136+
}
137+
138+
// Check DNS record value
139+
if (input.criteriaFilter.checkOn === CheckOn.DnsRecordValue) {
140+
if (!dnsResponse?.records || dnsResponse.records.length === 0) {
141+
return null;
142+
}
143+
144+
// Check if any record value matches the criteria
145+
for (const record of dnsResponse.records) {
146+
const recordValue: string = record.value;
147+
148+
// Try numeric comparison first
149+
if (
150+
typeof threshold === "number" ||
151+
(typeof threshold === "string" && !isNaN(Number(threshold)))
152+
) {
153+
const numericThreshold: number | null =
154+
CompareCriteria.convertToNumber(threshold);
155+
156+
if (numericThreshold !== null && !isNaN(Number(recordValue))) {
157+
const result: string | null = CompareCriteria.compareCriteriaNumbers(
158+
{
159+
value: Number(recordValue),
160+
threshold: numericThreshold,
161+
criteriaFilter: input.criteriaFilter,
162+
},
163+
);
164+
165+
if (result) {
166+
return `DNS record (${record.type}): ${result}`;
167+
}
168+
}
169+
}
170+
171+
// String comparison
172+
const result: string | null = CompareCriteria.compareCriteriaStrings({
173+
value: recordValue,
174+
threshold: String(threshold),
175+
criteriaFilter: input.criteriaFilter,
176+
});
177+
178+
if (result) {
179+
return `DNS record (${record.type}): ${result}`;
180+
}
181+
}
182+
}
183+
184+
return null;
185+
}
186+
}

Common/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import MetricMonitorCriteria from "./Criteria/MetricMonitorCriteria";
1212
import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
1313
import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
1414
import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
15+
import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
1516
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
1617
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
1718
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
@@ -493,6 +494,18 @@ ${contextBlock}
493494
}
494495
}
495496

497+
if (input.monitor.monitorType === MonitorType.DNS) {
498+
const dnsMonitorResult: string | null =
499+
await DnsMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
500+
dataToProcess: input.dataToProcess,
501+
criteriaFilter: input.criteriaFilter,
502+
});
503+
504+
if (dnsMonitorResult) {
505+
return dnsMonitorResult;
506+
}
507+
}
508+
496509
return null;
497510
}
498511

Common/Types/Monitor/CriteriaFilter.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ export enum CheckOn {
6060
SnmpOidExists = "SNMP OID Exists",
6161
SnmpResponseTime = "SNMP Response Time (in ms)",
6262
SnmpIsOnline = "SNMP Device Is Online",
63+
64+
// DNS monitors.
65+
DnsResponseTime = "DNS Response Time (in ms)",
66+
DnsIsOnline = "DNS Is Online",
67+
DnsRecordValue = "DNS Record Value",
68+
DnssecIsValid = "DNSSEC Is Valid",
69+
DnsRecordExists = "DNS Record Exists",
6370
}
6471

6572
export interface ServerMonitorOptions {
@@ -141,15 +148,23 @@ export class CriteriaFilterUtil {
141148
}): boolean {
142149
const { checkOn } = data;
143150

144-
if (checkOn === CheckOn.IsOnline || checkOn === CheckOn.SnmpIsOnline) {
151+
if (
152+
checkOn === CheckOn.IsOnline ||
153+
checkOn === CheckOn.SnmpIsOnline ||
154+
checkOn === CheckOn.DnsIsOnline
155+
) {
145156
return false;
146157
}
147158

148159
if (checkOn === CheckOn.IsRequestTimeout) {
149160
return false;
150161
}
151162

152-
if (checkOn === CheckOn.SnmpOidExists) {
163+
if (
164+
checkOn === CheckOn.SnmpOidExists ||
165+
checkOn === CheckOn.DnssecIsValid ||
166+
checkOn === CheckOn.DnsRecordExists
167+
) {
153168
return false;
154169
}
155170

@@ -204,7 +219,9 @@ export class CriteriaFilterUtil {
204219
checkOn === CheckOn.MemoryUsagePercent ||
205220
checkOn === CheckOn.IsOnline ||
206221
checkOn === CheckOn.SnmpResponseTime ||
207-
checkOn === CheckOn.SnmpIsOnline
222+
checkOn === CheckOn.SnmpIsOnline ||
223+
checkOn === CheckOn.DnsResponseTime ||
224+
checkOn === CheckOn.DnsIsOnline
208225
);
209226
}
210227
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import DnsRecordType from "./DnsRecordType";
2+
3+
export interface DnsRecordResponse {
4+
type: DnsRecordType;
5+
value: string;
6+
ttl?: number | undefined;
7+
}
8+
9+
export default interface DnsMonitorResponse {
10+
isOnline: boolean;
11+
responseTimeInMs: number;
12+
failureCause: string;
13+
records: Array<DnsRecordResponse>;
14+
isDnssecValid?: boolean | undefined;
15+
isTimeout?: boolean | undefined;
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum DnsRecordType {
2+
A = "A",
3+
AAAA = "AAAA",
4+
CNAME = "CNAME",
5+
MX = "MX",
6+
NS = "NS",
7+
TXT = "TXT",
8+
SOA = "SOA",
9+
PTR = "PTR",
10+
SRV = "SRV",
11+
CAA = "CAA",
12+
}
13+
14+
export default DnsRecordType;

Common/Types/Monitor/MonitorCriteriaInstance.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,33 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
394394
return monitorCriteriaInstance;
395395
}
396396

397+
if (arg.monitorType === MonitorType.DNS) {
398+
const monitorCriteriaInstance: MonitorCriteriaInstance =
399+
new MonitorCriteriaInstance();
400+
401+
monitorCriteriaInstance.data = {
402+
id: ObjectID.generate().toString(),
403+
monitorStatusId: arg.monitorStatusId,
404+
filterCondition: FilterCondition.All,
405+
filters: [
406+
{
407+
checkOn: CheckOn.DnsIsOnline,
408+
filterType: FilterType.True,
409+
value: undefined,
410+
},
411+
],
412+
incidents: [],
413+
alerts: [],
414+
createAlerts: false,
415+
changeMonitorStatus: true,
416+
createIncidents: false,
417+
name: `Check if ${arg.monitorName} is online`,
418+
description: `This criteria checks if the ${arg.monitorName} DNS resolution is online`,
419+
};
420+
421+
return monitorCriteriaInstance;
422+
}
423+
397424
return null;
398425
}
399426

@@ -495,6 +522,46 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
495522
};
496523
}
497524

525+
if (arg.monitorType === MonitorType.DNS) {
526+
monitorCriteriaInstance.data = {
527+
id: ObjectID.generate().toString(),
528+
monitorStatusId: arg.monitorStatusId,
529+
filterCondition: FilterCondition.Any,
530+
filters: [
531+
{
532+
checkOn: CheckOn.DnsIsOnline,
533+
filterType: FilterType.False,
534+
value: undefined,
535+
},
536+
],
537+
incidents: [
538+
{
539+
title: `${arg.monitorName} is offline`,
540+
description: `${arg.monitorName} DNS resolution is currently failing.`,
541+
incidentSeverityId: arg.incidentSeverityId,
542+
autoResolveIncident: true,
543+
id: ObjectID.generate().toString(),
544+
onCallPolicyIds: [],
545+
},
546+
],
547+
changeMonitorStatus: true,
548+
createIncidents: true,
549+
createAlerts: false,
550+
alerts: [
551+
{
552+
title: `${arg.monitorName} is offline`,
553+
description: `${arg.monitorName} DNS resolution is currently failing.`,
554+
alertSeverityId: arg.alertSeverityId,
555+
autoResolveAlert: true,
556+
id: ObjectID.generate().toString(),
557+
onCallPolicyIds: [],
558+
},
559+
],
560+
name: `Check if ${arg.monitorName} is offline`,
561+
description: `This criteria checks if the ${arg.monitorName} DNS resolution is failing`,
562+
};
563+
}
564+
498565
if (
499566
arg.monitorType === MonitorType.API ||
500567
arg.monitorType === MonitorType.Website

0 commit comments

Comments
 (0)