First commit
This commit is contained in:
commit
65068b7c4d
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
root=true
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 3
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.yaml]
|
||||||
|
indent_size = 2
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
lib/
|
||||||
|
connectivity-exporter
|
14
example.config.yaml
Normal file
14
example.config.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
targets:
|
||||||
|
- host: 127.0.0.1
|
||||||
|
icmp: true
|
||||||
|
tcp:
|
||||||
|
- 1234
|
||||||
|
- host: 1.1.1.1
|
||||||
|
traceroute: true
|
||||||
|
icmp: true
|
||||||
|
tcp:
|
||||||
|
- 53
|
||||||
|
- host: 8.8.8.8
|
||||||
|
icmp: true
|
||||||
|
tcp:
|
||||||
|
- 53
|
1983
package-lock.json
generated
Normal file
1983
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@hibas123/connectivity-exporter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"bin": "lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && pkg -t node12-linux .",
|
||||||
|
"dev": "nodemon -e ts --exec ts-node src/index.ts -- --config example.config.yaml"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^13.13.4",
|
||||||
|
"@types/ping": "^0.2.0",
|
||||||
|
"nodemon": "^2.0.3",
|
||||||
|
"pkg": "^4.4.8",
|
||||||
|
"ts-node": "^8.9.1",
|
||||||
|
"typescript": "^3.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/argparse": "^1.0.38",
|
||||||
|
"argparse": "^1.0.10",
|
||||||
|
"nodejs-traceroute": "^1.2.0",
|
||||||
|
"ping": "^0.2.3",
|
||||||
|
"prom-client": "^12.0.0",
|
||||||
|
"yaml": "^1.9.2"
|
||||||
|
}
|
||||||
|
}
|
264
src/index.ts
Normal file
264
src/index.ts
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import * as client from "prom-client";
|
||||||
|
import * as yaml from "yaml";
|
||||||
|
import { readFileSync, watchFile } from "fs";
|
||||||
|
import * as net from "net";
|
||||||
|
import * as ping from "ping";
|
||||||
|
import * as Traceroute from "nodejs-traceroute";
|
||||||
|
import * as argparse from "argparse";
|
||||||
|
|
||||||
|
import { strict as assert } from "assert";
|
||||||
|
|
||||||
|
const argp = new argparse.ArgumentParser();
|
||||||
|
|
||||||
|
argp.addArgument(["-c", "--config"], {
|
||||||
|
help: "Path of the config file",
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
dest: "config",
|
||||||
|
});
|
||||||
|
|
||||||
|
argp.addArgument(["-p", "--port"], {
|
||||||
|
help: "The port to listen at",
|
||||||
|
type: Number,
|
||||||
|
defaultValue: 9285,
|
||||||
|
dest: "port",
|
||||||
|
});
|
||||||
|
|
||||||
|
const args: {
|
||||||
|
config: string;
|
||||||
|
port: number;
|
||||||
|
} = argp.parseArgs();
|
||||||
|
|
||||||
|
async function testPing(host: string) {
|
||||||
|
let probe = await ping.promise.probe(host, {
|
||||||
|
timeout: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!probe.alive) return -1;
|
||||||
|
|
||||||
|
return Number(probe.avg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testTraceroute(host: string) {
|
||||||
|
const tracer = new Traceroute();
|
||||||
|
return new Promise((yes, no) => {
|
||||||
|
console.log("Tracepath to:", host);
|
||||||
|
tracer
|
||||||
|
.on("pid", (pid) => {
|
||||||
|
console.log(`pid: ${pid}`);
|
||||||
|
})
|
||||||
|
.on("destination", (destination) => {
|
||||||
|
console.log(`destination: ${destination}`);
|
||||||
|
})
|
||||||
|
.on("hop", (hop) => {
|
||||||
|
console.log(`hop: ${JSON.stringify(hop)}`);
|
||||||
|
})
|
||||||
|
.on("close", (code) => {
|
||||||
|
console.log(`close: code ${code}`);
|
||||||
|
})
|
||||||
|
.on("error", no);
|
||||||
|
|
||||||
|
tracer.trace("github.com");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testTCP(host: string, port: number) {
|
||||||
|
return new Promise((yes, no) => {
|
||||||
|
console.log("Checking TCP", host, port);
|
||||||
|
let socket = net.connect(port, host);
|
||||||
|
|
||||||
|
socket.on("error", () => {
|
||||||
|
yes(false);
|
||||||
|
socket.destroy();
|
||||||
|
});
|
||||||
|
socket.on("connect", () => {
|
||||||
|
yes(true);
|
||||||
|
socket.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let getMetrics: () => Promise<string>;
|
||||||
|
|
||||||
|
type IConfig = {
|
||||||
|
targets: {
|
||||||
|
host: string;
|
||||||
|
icmp: boolean;
|
||||||
|
traceroute: boolean;
|
||||||
|
tcp: (number | { port: number })[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
let config: IConfig;
|
||||||
|
try {
|
||||||
|
console.log("Loading: ", args.config);
|
||||||
|
config = yaml.parse(readFileSync(args.config, "utf8"));
|
||||||
|
|
||||||
|
// Verify config
|
||||||
|
|
||||||
|
assert(typeof config === "object", "Config is invalid!");
|
||||||
|
assert(typeof config.targets !== "undefined", "Field targets required!");
|
||||||
|
assert(Array.isArray(config.targets), "Field targets must be an array");
|
||||||
|
config.targets.forEach((target, idx) => {
|
||||||
|
assert(typeof target === "object", "Each target must be an object!");
|
||||||
|
assert(
|
||||||
|
typeof target.host === "string",
|
||||||
|
`The host must be a string in the ${idx} target`
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
typeof target.icmp === "undefined" ||
|
||||||
|
typeof target.icmp === "boolean",
|
||||||
|
`Field icmp must be undefined or boolean in target ${target.host}`
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
typeof target.traceroute === "undefined" ||
|
||||||
|
typeof target.traceroute === "boolean",
|
||||||
|
`Field traceroute must be undefined or boolean in target ${target.host}`
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
typeof target.tcp === "undefined" || Array.isArray(target.tcp),
|
||||||
|
`Field tcp must be undefined or array in target ${target.host}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (target.tcp) {
|
||||||
|
target.tcp.forEach((tcp) => {
|
||||||
|
assert(
|
||||||
|
typeof tcp === "number" || typeof tcp === "object",
|
||||||
|
"Each entry in TCP has to be a number or an object"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof tcp === "object") {
|
||||||
|
assert(
|
||||||
|
typeof tcp.port === "number",
|
||||||
|
"The port in TCP is required!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Config verified. Putting in production");
|
||||||
|
// Enable config
|
||||||
|
|
||||||
|
const reg = new client.Registry();
|
||||||
|
|
||||||
|
const reachable = new client.Gauge({
|
||||||
|
name: "connectivity_ping_reachable",
|
||||||
|
help: "Checks if the target is pingable by this host",
|
||||||
|
labelNames: ["target"],
|
||||||
|
registers: [reg],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pingTimes = new client.Gauge({
|
||||||
|
name: "connectivity_ping_time",
|
||||||
|
help: "Contains the current ping time from this host to target",
|
||||||
|
labelNames: ["target"],
|
||||||
|
registers: [reg],
|
||||||
|
});
|
||||||
|
|
||||||
|
const hopCount = new client.Gauge({
|
||||||
|
name: "connectivity_hops_count",
|
||||||
|
help: "The number of hops between this host and the target",
|
||||||
|
labelNames: ["target"],
|
||||||
|
registers: [reg],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tcpCheck = new client.Gauge({
|
||||||
|
name: "connectivity_tcp_available",
|
||||||
|
help: "Checks if connectivity to specified tcp port is available",
|
||||||
|
labelNames: ["target", "port"],
|
||||||
|
registers: [reg],
|
||||||
|
});
|
||||||
|
|
||||||
|
getMetrics = async () => {
|
||||||
|
console.log("Running");
|
||||||
|
await Promise.all(
|
||||||
|
config.targets.map(async (target) => {
|
||||||
|
let jobs = [];
|
||||||
|
if (target.icmp) {
|
||||||
|
jobs.push(
|
||||||
|
testPing(target.host).then((ping) => {
|
||||||
|
reachable.set(
|
||||||
|
{
|
||||||
|
target: target.host,
|
||||||
|
},
|
||||||
|
ping >= 0 ? 1 : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
pingTimes.set(
|
||||||
|
{
|
||||||
|
target: target.host,
|
||||||
|
},
|
||||||
|
ping as any
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.traceroute && false) {
|
||||||
|
//Currently not working
|
||||||
|
jobs.push(testTraceroute(target.host).then(() => {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.tcp) {
|
||||||
|
jobs.push(
|
||||||
|
...target.tcp.map((check) =>
|
||||||
|
testTCP(
|
||||||
|
target.host,
|
||||||
|
typeof check === "number" ? check : check.port
|
||||||
|
).then((avail) => {
|
||||||
|
tcpCheck.set(
|
||||||
|
{
|
||||||
|
target: target.host,
|
||||||
|
port:
|
||||||
|
typeof check === "number" ? check : check.port,
|
||||||
|
},
|
||||||
|
avail ? 1 : 0
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(jobs);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return reg.metrics();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on("SIGHUP", () => load());
|
||||||
|
watchFile(args.config, load);
|
||||||
|
load();
|
||||||
|
|
||||||
|
import * as http from "http";
|
||||||
|
import { URL } from "url";
|
||||||
|
|
||||||
|
http
|
||||||
|
.createServer(async (req, res) => {
|
||||||
|
const url = new URL(req.url, "http://127.0.0.1");
|
||||||
|
if (url.pathname == "/metrics") {
|
||||||
|
if (getMetrics)
|
||||||
|
getMetrics()
|
||||||
|
.then((data) => res.end(data))
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Error", err);
|
||||||
|
res.statusCode = 400;
|
||||||
|
res.end("Something went wrong!");
|
||||||
|
});
|
||||||
|
else {
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.end("Not ready!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.end("<a href='/metrics'>Metrics</a>");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.listen(args.port, () => {
|
||||||
|
console.log("Listening at:", args.port);
|
||||||
|
});
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"target": "ES2020",
|
||||||
|
"outDir": "lib/"
|
||||||
|
},
|
||||||
|
"include": ["src/"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user