1
0
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:
Massimo Candela
2020-03-07 03:51:51 +01:00
parent d7df9b5482
commit fa9d8501f4
9 changed files with 218 additions and 75 deletions

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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.");
}
});
};
}

View File

@@ -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)
});
}

View File

@@ -56,6 +56,10 @@ export default class Monitor {
};
setInterval(this._publishFadeOffGroups, this.internalConfig.checkFadeOffGroups);
this.input.onChange(() => {
this.updateMonitoredResources();
});
};
updateMonitoredResources = () => {

View File

@@ -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)

View File

@@ -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';
};

View File

@@ -33,7 +33,6 @@
import Monitor from "./monitor";
import ipUtils from "ip-sub";
export default class MonitorVisibility extends Monitor {
constructor(name, channel, params, env){

View File

@@ -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);