mirror of
https://github.com/nttgin/BGPalerter.git
synced 2024-05-19 06:50:08 +00:00
interactive auto configuration
This commit is contained in:
6
index.js
6
index.js
@@ -38,8 +38,6 @@ const params = yargs
|
||||
.command('$0', 'Run BGPalerter (default)', function () {
|
||||
})
|
||||
.example('$0 run -c config.yml', 'Run BGPalerter')
|
||||
|
||||
|
||||
.command('generate', 'Generate prefixes to monitor', function () {
|
||||
yargs.alias('o', 'output')
|
||||
.nargs('o', 1)
|
||||
@@ -76,8 +74,6 @@ const params = yargs
|
||||
.demandOption(['o']);
|
||||
})
|
||||
.example('$0 generate -a 2914 -o prefixes.yml', 'Generate prefixes for AS2914')
|
||||
|
||||
|
||||
.help('h')
|
||||
.alias('h', 'help')
|
||||
.epilog('Copyright (c) 2019, NTT Ltd')
|
||||
@@ -100,7 +96,7 @@ switch(params._[0]) {
|
||||
if (fs.existsSync(params.l)) {
|
||||
prefixes = fs.readFileSync(params.l, 'utf8').split(/\r?\n/).filter(i => i && true);
|
||||
} else {
|
||||
throw new Error("The prefix list fil\e (-l) is not readable");
|
||||
throw new Error("The prefix list file (-l) is not readable");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,10 @@ export default class ConnectorRIS extends Connector{
|
||||
new Promise((resolve, reject) => {
|
||||
this.subscription = input;
|
||||
try {
|
||||
input.onChange(() => {
|
||||
this._close();
|
||||
});
|
||||
|
||||
if (this.params.carefulSubscription) {
|
||||
this._subscribeToPrefixes(input);
|
||||
this._subscribeToASns(input);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
import ipUtils from "ip-sub";
|
||||
import inquirer from "inquirer";
|
||||
|
||||
export default class Input {
|
||||
|
||||
@@ -40,6 +41,19 @@ export default class Input {
|
||||
this.asns = [];
|
||||
this.cache = {};
|
||||
this.config = config;
|
||||
this.callbacks = [];
|
||||
|
||||
setTimeout(() => {
|
||||
this.loadPrefixes()
|
||||
.then(() => {
|
||||
this._change();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
process.exit();
|
||||
});
|
||||
}, 200);
|
||||
|
||||
};
|
||||
|
||||
_isAlreadyContained = (prefix, lessSpecifics) => {
|
||||
@@ -56,6 +70,16 @@ export default class Input {
|
||||
return false;
|
||||
};
|
||||
|
||||
onChange = (callback) => {
|
||||
this.callbacks.push(callback);
|
||||
};
|
||||
|
||||
_change = () => {
|
||||
for (let call of this.callbacks) {
|
||||
call();
|
||||
}
|
||||
};
|
||||
|
||||
getMonitoredLessSpecifics = () => {
|
||||
|
||||
if (!this.prefixes.length) {
|
||||
@@ -111,4 +135,71 @@ export default class Input {
|
||||
throw new Error('The method getMonitoredASns MUST be implemented');
|
||||
};
|
||||
|
||||
loadPrefixes = () => {
|
||||
throw new Error('The method loadPrefixes MUST be implemented');
|
||||
};
|
||||
|
||||
save = () => {
|
||||
throw new Error('The method save MUST be implemented');
|
||||
};
|
||||
|
||||
generate = () => {
|
||||
return inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'continue',
|
||||
message: "The file prefixes.yml cannot be loaded. Do you want to auto-configure BGPalerter?",
|
||||
default: true
|
||||
}
|
||||
])
|
||||
.then((answer) => {
|
||||
if (answer.continue) {
|
||||
return inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'asns',
|
||||
message: "Which Autonomous System(s) you want to monitor? (comma-separated, e.g. 2914,3333)",
|
||||
default: true,
|
||||
validate: function(value) {
|
||||
const asns = value.split(",").filter(i => i !== "" && !isNaN(i));
|
||||
return asns.length > 0;
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'i',
|
||||
message: "Are there sub-prefixes delegated to other ASes? (e.g. sub-prefixes announced by customers)",
|
||||
default: true
|
||||
},
|
||||
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'm',
|
||||
message: "Do you want to be notified when your AS is announcing a new prefix?",
|
||||
default: true
|
||||
}
|
||||
])
|
||||
.then((answer) => {
|
||||
const generatePrefixes = require("../generatePrefixesList");
|
||||
const asns = answer.asns.split(",");
|
||||
return generatePrefixes(
|
||||
asns,
|
||||
"prefixes.yml",
|
||||
[],
|
||||
answer.i,
|
||||
null,
|
||||
answer.m ? asns : []
|
||||
);
|
||||
});
|
||||
} else {
|
||||
throw new Error("Nothing to monitor.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -47,77 +47,88 @@ export default class InputYml extends Input {
|
||||
if (!config.monitoredPrefixesFiles || config.monitoredPrefixesFiles.length === 0) {
|
||||
throw new Error("The monitoredPrefixesFiles key is missing in the config file");
|
||||
}
|
||||
};
|
||||
|
||||
const uniquePrefixes = {};
|
||||
const uniqueAsns = {};
|
||||
for (let prefixesFile of config.monitoredPrefixesFiles) {
|
||||
|
||||
let monitoredPrefixesFile = {};
|
||||
let fileContent;
|
||||
|
||||
if (fs.existsSync('./' + prefixesFile)) {
|
||||
fileContent = fs.readFileSync('./' + prefixesFile, 'utf8');
|
||||
try {
|
||||
monitoredPrefixesFile = yaml.safeLoad(fileContent) || {};
|
||||
} catch (error) {
|
||||
throw new Error("The file " + prefixesFile + " is not valid yml: " + error.message.split(":")[0]);
|
||||
}
|
||||
|
||||
if (Object.keys(monitoredPrefixesFile).length === 0) {
|
||||
throw new Error("No prefixes to monitor in " + prefixesFile + ". Please read https://github.com/nttgin/BGPalerter/blob/master/docs/prefixes.md");
|
||||
}
|
||||
|
||||
if (this.validate(monitoredPrefixesFile)) {
|
||||
|
||||
if (monitoredPrefixesFile.options && monitoredPrefixesFile.options.monitorASns) {
|
||||
this.asns = Object
|
||||
.keys(monitoredPrefixesFile.options.monitorASns)
|
||||
.map(asn => {
|
||||
if (uniqueAsns[asn]) {
|
||||
throw new Error("Duplicate entry for monitored AS " + asn);
|
||||
}
|
||||
uniqueAsns[asn] = true;
|
||||
return Object.assign({
|
||||
asn: new AS(asn),
|
||||
group: 'default'
|
||||
}, monitoredPrefixesFile.options.monitorASns[asn]);
|
||||
});
|
||||
}
|
||||
|
||||
const monitoredPrefixes = Object
|
||||
.keys(monitoredPrefixesFile)
|
||||
.filter(i => i !== "options")
|
||||
.map(i => {
|
||||
if (uniquePrefixes[i]) {
|
||||
throw new Error("Duplicate entry for " + i);
|
||||
}
|
||||
uniquePrefixes[i] = true;
|
||||
monitoredPrefixesFile[i].asn = new AS(monitoredPrefixesFile[i].asn);
|
||||
|
||||
return Object.assign({
|
||||
prefix: i,
|
||||
group: 'default',
|
||||
ignore: false,
|
||||
excludeMonitors: [],
|
||||
includeMonitors: [],
|
||||
}, monitoredPrefixesFile[i])
|
||||
})
|
||||
.filter(i => i !== null);
|
||||
|
||||
this.prefixes = this.prefixes.concat(monitoredPrefixes);
|
||||
}
|
||||
|
||||
} else {
|
||||
fs.writeFileSync(prefixesFile, "");
|
||||
throw new Error("The file " + prefixesFile + " cannot be loaded. An empty one has been created.");
|
||||
}
|
||||
loadPrefixes = () => {
|
||||
if (!fs.existsSync('./' + this.config.monitoredPrefixesFiles[0])) {
|
||||
return this.generate()
|
||||
.then(() => this._loadPrefixes());
|
||||
}
|
||||
|
||||
this.prefixes.sort((a, b) => {
|
||||
return ipUtils.sortByPrefixLength(b.prefix, a.prefix);
|
||||
});
|
||||
return this._loadPrefixes();
|
||||
};
|
||||
|
||||
_loadPrefixes = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const uniquePrefixes = {};
|
||||
const uniqueAsns = {};
|
||||
for (let prefixesFile of this.config.monitoredPrefixesFiles) {
|
||||
|
||||
let monitoredPrefixesFile = {};
|
||||
let fileContent;
|
||||
|
||||
if (fs.existsSync('./' + prefixesFile)) {
|
||||
fileContent = fs.readFileSync('./' + prefixesFile, 'utf8');
|
||||
try {
|
||||
monitoredPrefixesFile = yaml.safeLoad(fileContent) || {};
|
||||
} catch (error) {
|
||||
throw new Error("The file " + prefixesFile + " is not valid yml: " + error.message.split(":")[0]);
|
||||
}
|
||||
|
||||
if (Object.keys(monitoredPrefixesFile).length === 0) {
|
||||
throw new Error("No prefixes to monitor in " + prefixesFile + ". Please read https://github.com/nttgin/BGPalerter/blob/master/docs/prefixes.md");
|
||||
}
|
||||
|
||||
if (this.validate(monitoredPrefixesFile)) {
|
||||
|
||||
if (monitoredPrefixesFile.options && monitoredPrefixesFile.options.monitorASns) {
|
||||
this.asns = Object
|
||||
.keys(monitoredPrefixesFile.options.monitorASns)
|
||||
.map(asn => {
|
||||
if (uniqueAsns[asn]) {
|
||||
throw new Error("Duplicate entry for monitored AS " + asn);
|
||||
}
|
||||
uniqueAsns[asn] = true;
|
||||
return Object.assign({
|
||||
asn: new AS(asn),
|
||||
group: 'default'
|
||||
}, monitoredPrefixesFile.options.monitorASns[asn]);
|
||||
});
|
||||
}
|
||||
|
||||
const monitoredPrefixes = Object
|
||||
.keys(monitoredPrefixesFile)
|
||||
.filter(i => i !== "options")
|
||||
.map(i => {
|
||||
if (uniquePrefixes[i]) {
|
||||
throw new Error("Duplicate entry for " + i);
|
||||
}
|
||||
uniquePrefixes[i] = true;
|
||||
monitoredPrefixesFile[i].asn = new AS(monitoredPrefixesFile[i].asn);
|
||||
|
||||
return Object.assign({
|
||||
prefix: i,
|
||||
group: 'default',
|
||||
ignore: false,
|
||||
excludeMonitors: [],
|
||||
includeMonitors: [],
|
||||
}, monitoredPrefixesFile[i])
|
||||
})
|
||||
.filter(i => i !== null);
|
||||
|
||||
this.prefixes = this.prefixes.concat(monitoredPrefixes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.prefixes.sort((a, b) => {
|
||||
return ipUtils.sortByPrefixLength(b.prefix, a.prefix);
|
||||
});
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
validate = (fileContent) => {
|
||||
let prefixesError = [];
|
||||
let optionsError = [];
|
||||
@@ -215,7 +226,6 @@ export default class InputYml extends Input {
|
||||
return errors.length === 0;
|
||||
};
|
||||
|
||||
|
||||
_validateRegex = (regex) => {
|
||||
if (regex) {
|
||||
try {
|
||||
@@ -238,4 +248,38 @@ export default class InputYml extends Input {
|
||||
getMonitoredASns = () => {
|
||||
return this.asns;
|
||||
};
|
||||
|
||||
save = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const prefixes = {};
|
||||
for (let rule of this.prefixes) {
|
||||
const prefix = rule.prefix;
|
||||
prefixes[prefix] = {
|
||||
asn: rule.asn.getValue(),
|
||||
description: rule.description,
|
||||
group: rule.group,
|
||||
ignore: rule.ignore,
|
||||
ignoreMorespecifics: rule.ignoreMorespecifics,
|
||||
};
|
||||
|
||||
if (rule.excludeMonitors.length) prefixes[prefix].excludeMonitors = rule.excludeMonitors;
|
||||
if (rule.includeMonitors.length) prefixes[prefix].includeMonitors = rule.includeMonitors;
|
||||
}
|
||||
|
||||
const options = {
|
||||
options: {
|
||||
monitorASns: {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (let asnRule of this.asns) {
|
||||
options.options.monitorASns[asnRule.asn.getValue()] = {
|
||||
group: asnRule.group
|
||||
};
|
||||
}
|
||||
|
||||
fs.writeFileSync("prefixes.yml", yaml.dump({ ...prefixes, ...options }));
|
||||
resolve(true)
|
||||
});
|
||||
}
|
||||
@@ -56,6 +56,10 @@ export default class Monitor {
|
||||
};
|
||||
|
||||
setInterval(this._publishFadeOffGroups, this.internalConfig.checkFadeOffGroups);
|
||||
|
||||
this.input.onChange(() => {
|
||||
this.updateMonitoredResources();
|
||||
});
|
||||
};
|
||||
|
||||
updateMonitoredResources = () => {
|
||||
|
||||
@@ -5,7 +5,9 @@ export default class MonitorRPKI extends Monitor {
|
||||
|
||||
constructor(name, channel, params, env){
|
||||
super(name, channel, params, env);
|
||||
this.updateMonitoredPrefixes();
|
||||
this.input.onChange(() => {
|
||||
this.updateMonitoredResources();
|
||||
});
|
||||
this.validationQueue = [];
|
||||
|
||||
rpki.preCache(60)
|
||||
|
||||
@@ -38,6 +38,10 @@ export default class MonitorSwUpdates extends Monitor {
|
||||
super(name, channel, params, env);
|
||||
};
|
||||
|
||||
updateMonitoredResources = () => {
|
||||
// throw new Error('The method updateMonitoredResources must be implemented in ' + this.name);
|
||||
};
|
||||
|
||||
filter = (message) => {
|
||||
return message.type === 'software-update';
|
||||
};
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
import Monitor from "./monitor";
|
||||
import ipUtils from "ip-sub";
|
||||
|
||||
|
||||
export default class MonitorVisibility extends Monitor {
|
||||
|
||||
constructor(name, channel, params, env){
|
||||
|
||||
@@ -505,7 +505,6 @@ describe("Alerting", function () {
|
||||
message = JSON.parse(JSON.stringify(message));
|
||||
const id = message.id;
|
||||
|
||||
// console.log(expectedData, message);
|
||||
expect(Object.keys(expectedData).includes(id)).to.equal(true);
|
||||
expect(expectedData[id] != null).to.equal(true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user