@gibme/mikrotik 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +48 -4
- package/dist/index.js +304 -99
- package/dist/types.d.ts +160 -19
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import SSH from '@gibme/ssh';
|
|
2
2
|
import { BandwidthTest, Response } from './types';
|
|
3
3
|
import Cache from '@gibme/cache/memory';
|
|
4
|
-
export { ConnectConfig } from '@gibme/ssh';
|
|
4
|
+
export type { ConnectConfig } from '@gibme/ssh';
|
|
5
5
|
export type Direction = BandwidthTest.Direction;
|
|
6
6
|
export type Protocol = BandwidthTest.Protocol;
|
|
7
7
|
export type Status = BandwidthTest.Status;
|
|
@@ -9,10 +9,14 @@ export type Update = BandwidthTest.Update;
|
|
|
9
9
|
export type Options = BandwidthTest.Options;
|
|
10
10
|
export type Address = Response.Address;
|
|
11
11
|
export type Interface = Response.Interface;
|
|
12
|
+
export type Tunnel = Response.Tunnel;
|
|
12
13
|
export type Route = Response.Route;
|
|
13
14
|
export type RouteCount = Response.RouteCount;
|
|
14
15
|
export type Ping = Response.Ping;
|
|
15
16
|
export type Traceroute = Response.Traceroute;
|
|
17
|
+
export type RouterBoard = Response.Routerboard;
|
|
18
|
+
export type Resource = Response.Resource;
|
|
19
|
+
export type Health = Response.Health;
|
|
16
20
|
export default class Mikrotik extends SSH {
|
|
17
21
|
protected static cache: Cache;
|
|
18
22
|
/**
|
|
@@ -20,16 +24,21 @@ export default class Mikrotik extends SSH {
|
|
|
20
24
|
*
|
|
21
25
|
* @param min_distance
|
|
22
26
|
* @param vrf
|
|
27
|
+
* @param active_only
|
|
23
28
|
*/
|
|
24
|
-
get_ip_routes(min_distance?: number, vrf?: string): Promise<Route[]>;
|
|
29
|
+
get_ip_routes(min_distance?: number, vrf?: string, active_only?: boolean): Promise<Route[]>;
|
|
25
30
|
/**
|
|
26
31
|
* Retrieves the IP addresses active on the system
|
|
32
|
+
*
|
|
33
|
+
* @param active_only
|
|
27
34
|
*/
|
|
28
|
-
get_ip_addresses(): Promise<Address[]>;
|
|
35
|
+
get_ip_addresses(active_only?: boolean): Promise<Address[]>;
|
|
29
36
|
/**
|
|
30
37
|
* Retrieves the interfaces active on the system
|
|
38
|
+
*
|
|
39
|
+
* @param active_only
|
|
31
40
|
*/
|
|
32
|
-
get_interfaces(): Promise<Interface[]>;
|
|
41
|
+
get_interfaces(active_only?: boolean): Promise<Interface[]>;
|
|
33
42
|
/**
|
|
34
43
|
* For all device IP addresses, counts the routes that exit (either directly or tunneled)
|
|
35
44
|
* the device using each IP address
|
|
@@ -65,6 +74,34 @@ export default class Mikrotik extends SSH {
|
|
|
65
74
|
* @param source
|
|
66
75
|
*/
|
|
67
76
|
traceroute(target: string, source?: string): Promise<Traceroute[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Fetches routerboard information from the device
|
|
79
|
+
*/
|
|
80
|
+
routerboard(): Promise<Response.Routerboard>;
|
|
81
|
+
/**
|
|
82
|
+
* Fetches the identity of the device
|
|
83
|
+
*/
|
|
84
|
+
identity(): Promise<string>;
|
|
85
|
+
/**
|
|
86
|
+
* Fetches the resource information of the device
|
|
87
|
+
*/
|
|
88
|
+
resource(): Promise<Response.Resource>;
|
|
89
|
+
/**
|
|
90
|
+
* Fetches the current RouterOS version
|
|
91
|
+
*/
|
|
92
|
+
version(): Promise<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Fetches the semantic RouterOS version
|
|
95
|
+
*/
|
|
96
|
+
semantic_version(): Promise<{
|
|
97
|
+
major: number;
|
|
98
|
+
minor: number;
|
|
99
|
+
patch: number;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Fetches the current health information
|
|
103
|
+
*/
|
|
104
|
+
health(): Promise<Response.Health>;
|
|
68
105
|
/**
|
|
69
106
|
* Executes a command with expected terse response and parses it accordingly
|
|
70
107
|
*
|
|
@@ -72,4 +109,11 @@ export default class Mikrotik extends SSH {
|
|
|
72
109
|
* @protected
|
|
73
110
|
*/
|
|
74
111
|
protected terse<Type extends object = any>(command: string): Promise<Type[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Executes a command that expects the result as a 'table' of key-value pairs separated by a colon (:)
|
|
114
|
+
*
|
|
115
|
+
* @param command
|
|
116
|
+
* @protected
|
|
117
|
+
*/
|
|
118
|
+
protected kvs<Type extends object = any>(command: string): Promise<Type>;
|
|
75
119
|
}
|
package/dist/index.js
CHANGED
|
@@ -33,73 +33,99 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
33
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
34
|
const ssh_1 = __importDefault(require("@gibme/ssh"));
|
|
35
35
|
const types_1 = require("./types");
|
|
36
|
-
const
|
|
36
|
+
const ip_address_1 = require("ip-address");
|
|
37
37
|
const memory_1 = __importDefault(require("@gibme/cache/memory"));
|
|
38
38
|
const dns_1 = require("dns");
|
|
39
|
+
const semver_1 = require("semver");
|
|
40
|
+
/** @ignore */
|
|
41
|
+
const toVersion = (version) => (0, semver_1.valid)((0, semver_1.coerce)(version)) || version;
|
|
42
|
+
/** @ignore */
|
|
43
|
+
const toIP4 = (address) => {
|
|
44
|
+
try {
|
|
45
|
+
return new ip_address_1.Address4(address);
|
|
46
|
+
}
|
|
47
|
+
catch (_a) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
39
51
|
class Mikrotik extends ssh_1.default {
|
|
40
52
|
/**
|
|
41
53
|
* Retrieves the routes in the routing table
|
|
42
54
|
*
|
|
43
55
|
* @param min_distance
|
|
44
56
|
* @param vrf
|
|
57
|
+
* @param active_only
|
|
45
58
|
*/
|
|
46
59
|
get_ip_routes() {
|
|
47
|
-
return __awaiter(this, arguments, void 0, function* (min_distance = 0, vrf) {
|
|
48
|
-
const ifaces = yield
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
return __awaiter(this, arguments, void 0, function* (min_distance = 0, vrf, active_only = true) {
|
|
61
|
+
const [ifaces, ips] = yield Promise.all([
|
|
62
|
+
this.get_interfaces(),
|
|
63
|
+
this.get_ip_addresses()
|
|
64
|
+
]);
|
|
65
|
+
const command = `/ip route print terse without-paging${active_only ? ' where active' : ''}`;
|
|
66
|
+
const routes = yield this.terse(command);
|
|
51
67
|
return routes.map(route => {
|
|
52
68
|
var _a;
|
|
53
69
|
const _network = route['dst-address'].split('/');
|
|
54
70
|
const address = _network[0];
|
|
55
71
|
const cidr = parseInt(_network[1]);
|
|
56
72
|
const [gateway, iface] = (() => {
|
|
57
|
-
if (route.gateway)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return [ip === null || ip === void 0 ? void 0 : ip.ipaddress, route.gateway];
|
|
68
|
-
}
|
|
73
|
+
if (!route.gateway)
|
|
74
|
+
return [undefined, undefined];
|
|
75
|
+
const gw = toIP4(route.gateway);
|
|
76
|
+
if (route.gateway.includes('%')) {
|
|
77
|
+
const [gateway, ...iface] = route.gateway.split('%');
|
|
78
|
+
return [gateway, iface.join('%')];
|
|
79
|
+
}
|
|
80
|
+
else if (gw) {
|
|
81
|
+
const ip = ips.filter(ip => ip.includes(route.gateway)).shift();
|
|
82
|
+
return [route.gateway, ip === null || ip === void 0 ? void 0 : ip.iface];
|
|
69
83
|
}
|
|
70
84
|
else {
|
|
71
|
-
|
|
85
|
+
const ip = ips.filter(ip => ip.iface === route.gateway).shift();
|
|
86
|
+
return [ip === null || ip === void 0 ? void 0 : ip.ipaddress, route.gateway];
|
|
72
87
|
}
|
|
73
88
|
})();
|
|
74
89
|
const distance = parseInt(route.distance);
|
|
75
90
|
const scope = parseInt(route.scope);
|
|
76
91
|
const target_scope = parseInt(route.target_scope) || undefined;
|
|
77
92
|
// this does not account for if a device has multiple addresses in the same network
|
|
78
|
-
const preferred_source = (_a = ips.filter(ip => gateway ? ip.
|
|
79
|
-
const _vrf = route['routing-mark'] || 'main';
|
|
93
|
+
const preferred_source = (_a = ips.filter(ip => gateway ? ip.includes(gateway) : false).shift()) === null || _a === void 0 ? void 0 : _a.ipaddress;
|
|
94
|
+
const _vrf = route['routing-table'] || route['routing-mark'] || 'main';
|
|
80
95
|
const tunnel = (() => {
|
|
81
96
|
const tunnel = ifaces.filter(_iface => _iface.name === iface).shift();
|
|
82
|
-
|
|
97
|
+
const parent = ips.filter(ip => ip.ipaddress === (tunnel === null || tunnel === void 0 ? void 0 : tunnel.local_address)).shift();
|
|
98
|
+
if (!tunnel || !parent)
|
|
83
99
|
return undefined;
|
|
84
|
-
|
|
85
|
-
if (!root)
|
|
86
|
-
return undefined;
|
|
87
|
-
return Object.assign(Object.assign({}, tunnel), { root });
|
|
100
|
+
return Object.assign(Object.assign({}, tunnel), { parent });
|
|
88
101
|
})();
|
|
89
|
-
|
|
90
|
-
network:
|
|
91
|
-
|
|
92
|
-
cidr
|
|
93
|
-
},
|
|
94
|
-
preferred_source,
|
|
95
|
-
gateway,
|
|
96
|
-
iface,
|
|
97
|
-
tunnel,
|
|
102
|
+
const result = {
|
|
103
|
+
network: address,
|
|
104
|
+
cidr,
|
|
98
105
|
distance,
|
|
99
106
|
scope,
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
vrf: _vrf,
|
|
108
|
+
includes: (ipaddress) => {
|
|
109
|
+
const temp_address = toIP4(ipaddress);
|
|
110
|
+
const temp_network = toIP4(`${address}/${cidr}`);
|
|
111
|
+
if (!temp_address || !temp_network)
|
|
112
|
+
return false;
|
|
113
|
+
return temp_address.isInSubnet(temp_network);
|
|
114
|
+
}
|
|
102
115
|
};
|
|
116
|
+
if (preferred_source)
|
|
117
|
+
result.preferred_source = preferred_source;
|
|
118
|
+
if (gateway)
|
|
119
|
+
result.gateway = gateway;
|
|
120
|
+
if (iface)
|
|
121
|
+
result.iface = iface;
|
|
122
|
+
if (tunnel)
|
|
123
|
+
result.tunnel = tunnel;
|
|
124
|
+
if (target_scope)
|
|
125
|
+
result.target_scope = target_scope;
|
|
126
|
+
if (route.comment)
|
|
127
|
+
result.comment = route.comment;
|
|
128
|
+
return result;
|
|
103
129
|
})
|
|
104
130
|
.filter(route => route.distance >= min_distance)
|
|
105
131
|
.filter(route => vrf ? route.vrf === vrf : true);
|
|
@@ -107,76 +133,84 @@ class Mikrotik extends ssh_1.default {
|
|
|
107
133
|
}
|
|
108
134
|
/**
|
|
109
135
|
* Retrieves the IP addresses active on the system
|
|
136
|
+
*
|
|
137
|
+
* @param active_only
|
|
110
138
|
*/
|
|
111
139
|
get_ip_addresses() {
|
|
112
|
-
return __awaiter(this,
|
|
113
|
-
const
|
|
140
|
+
return __awaiter(this, arguments, void 0, function* (active_only = true) {
|
|
141
|
+
const command = `/ip address print terse without-paging${active_only ? ' where !disabled' : ''}`;
|
|
142
|
+
const addresses = yield this.terse(command);
|
|
114
143
|
return addresses.map(line => {
|
|
115
144
|
const _address = line.address.split('/');
|
|
116
145
|
const ipaddress = _address[0];
|
|
117
146
|
const cidr = parseInt(_address[1]);
|
|
118
|
-
|
|
147
|
+
const result = {
|
|
119
148
|
ipaddress,
|
|
120
149
|
network: line.network,
|
|
121
150
|
cidr,
|
|
122
151
|
iface: line.interface,
|
|
123
|
-
|
|
152
|
+
includes: (ipaddress) => {
|
|
153
|
+
const temp_address = toIP4(ipaddress);
|
|
154
|
+
const temp_network = toIP4(`${line.network}/${cidr}`);
|
|
155
|
+
if (!temp_address || !temp_network)
|
|
156
|
+
return false;
|
|
157
|
+
return temp_address.isInSubnet(temp_network);
|
|
158
|
+
}
|
|
124
159
|
};
|
|
160
|
+
if (line.comment)
|
|
161
|
+
result.comment = line.comment;
|
|
162
|
+
return result;
|
|
125
163
|
});
|
|
126
164
|
});
|
|
127
165
|
}
|
|
128
166
|
/**
|
|
129
167
|
* Retrieves the interfaces active on the system
|
|
168
|
+
*
|
|
169
|
+
* @param active_only
|
|
130
170
|
*/
|
|
131
171
|
get_interfaces() {
|
|
132
|
-
return __awaiter(this,
|
|
133
|
-
const
|
|
134
|
-
const gre = yield
|
|
135
|
-
|
|
136
|
-
|
|
172
|
+
return __awaiter(this, arguments, void 0, function* (active_only = true) {
|
|
173
|
+
const filter = active_only ? ' where !disabled' : '';
|
|
174
|
+
const [ifaces, gre, ipip, eoip] = yield Promise.all([
|
|
175
|
+
this.terse(`/interface print terse without-paging${filter}`),
|
|
176
|
+
this.terse(`/interface gre print terse without-paging${filter}`),
|
|
177
|
+
this.terse(`/interface ipip print terse without-paging${filter}`),
|
|
178
|
+
this.terse(`/interface eoip print terse without-paging${filter}`)
|
|
179
|
+
]);
|
|
180
|
+
const tunnels = [...gre, ...ipip, ...eoip];
|
|
137
181
|
return ifaces.map(line => {
|
|
138
182
|
const _type = (() => {
|
|
139
183
|
switch (line.type) {
|
|
184
|
+
case 'ether':
|
|
185
|
+
return 'ethernet';
|
|
186
|
+
case 'lo':
|
|
187
|
+
return 'loopback';
|
|
140
188
|
case 'gre-tunnel':
|
|
141
189
|
return 'gre';
|
|
142
190
|
case 'ipip-tunnel':
|
|
143
191
|
return 'ipip';
|
|
144
192
|
case 'eoip-tunnel':
|
|
145
193
|
return 'eoip';
|
|
194
|
+
case 'wg':
|
|
195
|
+
return 'wireguard';
|
|
146
196
|
default:
|
|
147
197
|
return line.type;
|
|
148
198
|
}
|
|
149
199
|
})();
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
case 'ipip':
|
|
155
|
-
return ipip.filter(tunnel => tunnel.name === line.name)[0]['local-address'];
|
|
156
|
-
case 'eoip':
|
|
157
|
-
return eoip.filter(tunnel => tunnel.name === line.name)[0]['local-address'];
|
|
158
|
-
default:
|
|
159
|
-
return undefined;
|
|
160
|
-
}
|
|
161
|
-
})();
|
|
162
|
-
const remote_address = (() => {
|
|
163
|
-
switch (line.type) {
|
|
164
|
-
case 'gre-tunnel':
|
|
165
|
-
return gre.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
166
|
-
case 'ipip':
|
|
167
|
-
return ipip.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
168
|
-
case 'eoip':
|
|
169
|
-
return eoip.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
170
|
-
default:
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
})();
|
|
174
|
-
return {
|
|
200
|
+
const tunnel = tunnels.filter(tunnel => tunnel.name === line.name).shift();
|
|
201
|
+
const local_address = tunnel === null || tunnel === void 0 ? void 0 : tunnel['local-address'];
|
|
202
|
+
const remote_address = tunnel === null || tunnel === void 0 ? void 0 : tunnel['remote-address'];
|
|
203
|
+
const result = {
|
|
175
204
|
name: line.name,
|
|
176
|
-
type: _type
|
|
177
|
-
local_address,
|
|
178
|
-
remote_address
|
|
205
|
+
type: _type
|
|
179
206
|
};
|
|
207
|
+
if (local_address)
|
|
208
|
+
result.local_address = local_address;
|
|
209
|
+
if (remote_address)
|
|
210
|
+
result.remote_address = remote_address;
|
|
211
|
+
if (line.comment)
|
|
212
|
+
result.comment = line.comment;
|
|
213
|
+
return result;
|
|
180
214
|
});
|
|
181
215
|
});
|
|
182
216
|
}
|
|
@@ -189,8 +223,10 @@ class Mikrotik extends ssh_1.default {
|
|
|
189
223
|
*/
|
|
190
224
|
get_route_counts() {
|
|
191
225
|
return __awaiter(this, arguments, void 0, function* (min_distance = 0, vrf) {
|
|
192
|
-
const routes = yield
|
|
193
|
-
|
|
226
|
+
const [routes, ips] = yield Promise.all([
|
|
227
|
+
this.get_ip_routes(min_distance, vrf),
|
|
228
|
+
this.get_ip_addresses()
|
|
229
|
+
]);
|
|
194
230
|
const results = {};
|
|
195
231
|
ips.forEach(ip => {
|
|
196
232
|
results[ip.ipaddress] = {
|
|
@@ -203,12 +239,12 @@ class Mikrotik extends ssh_1.default {
|
|
|
203
239
|
ip: '',
|
|
204
240
|
count: 0
|
|
205
241
|
};
|
|
206
|
-
|
|
242
|
+
Object.keys(results).forEach(key => {
|
|
207
243
|
if (results[key].count > best.count) {
|
|
208
244
|
best.count = results[key].count;
|
|
209
245
|
best.ip = key;
|
|
210
246
|
}
|
|
211
|
-
}
|
|
247
|
+
});
|
|
212
248
|
if (results[best.ip]) {
|
|
213
249
|
results[best.ip].active = true;
|
|
214
250
|
}
|
|
@@ -228,13 +264,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
228
264
|
* @param options
|
|
229
265
|
*/
|
|
230
266
|
bandwidth_test(target_1, username_1, password_1) {
|
|
231
|
-
return __awaiter(this, arguments, void 0, function* (target, username, password, options = {
|
|
232
|
-
duration: 15,
|
|
233
|
-
direction: 'both',
|
|
234
|
-
protocol: 'udp',
|
|
235
|
-
random_data: false,
|
|
236
|
-
callback: () => { }
|
|
237
|
-
}) {
|
|
267
|
+
return __awaiter(this, arguments, void 0, function* (target, username, password, options = {}) {
|
|
238
268
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
239
269
|
const $ = this;
|
|
240
270
|
const sleep = (timeout) => __awaiter(this, void 0, void 0, function* () { return new Promise(resolve => setTimeout(resolve, timeout)); });
|
|
@@ -370,10 +400,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
370
400
|
};
|
|
371
401
|
if (source)
|
|
372
402
|
result.source = source;
|
|
373
|
-
|
|
374
|
-
if (source) {
|
|
375
|
-
command += ` src-address=${source}`;
|
|
376
|
-
}
|
|
403
|
+
const command = `/ping address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
377
404
|
try {
|
|
378
405
|
const response = (yield this.exec(command))
|
|
379
406
|
.toString()
|
|
@@ -402,10 +429,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
402
429
|
*/
|
|
403
430
|
traceroute(target, source) {
|
|
404
431
|
return __awaiter(this, void 0, void 0, function* () {
|
|
405
|
-
|
|
406
|
-
if (source) {
|
|
407
|
-
command += ` src-address=${source}`;
|
|
408
|
-
}
|
|
432
|
+
const command = `/tool traceroute address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
409
433
|
const response = (yield this.exec(command))
|
|
410
434
|
.toString()
|
|
411
435
|
.split('\r\n\r\n')
|
|
@@ -420,7 +444,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
420
444
|
if (!response)
|
|
421
445
|
throw new Error(`Could not perform traceroute to ${target}`);
|
|
422
446
|
const results = [];
|
|
423
|
-
const
|
|
447
|
+
const resolveDNS = (ip) => __awaiter(this, void 0, void 0, function* () {
|
|
424
448
|
return new Promise(resolve => {
|
|
425
449
|
(0, dns_1.reverse)(ip, (error, addresses) => {
|
|
426
450
|
if (error)
|
|
@@ -445,7 +469,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
445
469
|
results.push(result);
|
|
446
470
|
}
|
|
447
471
|
(yield Promise.all(results.filter(result => result.address)
|
|
448
|
-
.map(result =>
|
|
472
|
+
.map(result => resolveDNS(result.address || ''))))
|
|
449
473
|
.forEach(result => {
|
|
450
474
|
for (let i = 0; i < results.length; i++) {
|
|
451
475
|
if (result[0] === results[i].address && result[1]) {
|
|
@@ -456,6 +480,161 @@ class Mikrotik extends ssh_1.default {
|
|
|
456
480
|
return results;
|
|
457
481
|
});
|
|
458
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Fetches routerboard information from the device
|
|
485
|
+
*/
|
|
486
|
+
routerboard() {
|
|
487
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
488
|
+
const result = yield this.kvs('/system routerboard print');
|
|
489
|
+
return {
|
|
490
|
+
routerboard: result.routerboard === 'yes',
|
|
491
|
+
board_name: result['board-name'],
|
|
492
|
+
model: result.model,
|
|
493
|
+
serial_number: result['serial-number'],
|
|
494
|
+
firmware_type: result['firmware-type'],
|
|
495
|
+
factory_firmware: toVersion(result['factory-firmware']),
|
|
496
|
+
current_firmware: toVersion(result['current-firmware']),
|
|
497
|
+
upgrade_firmware: toVersion(result['upgrade-firmware'])
|
|
498
|
+
};
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Fetches the identity of the device
|
|
503
|
+
*/
|
|
504
|
+
identity() {
|
|
505
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
506
|
+
const response = yield this.kvs('/system identity print');
|
|
507
|
+
return response.name;
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Fetches the resource information of the device
|
|
512
|
+
*/
|
|
513
|
+
resource() {
|
|
514
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
515
|
+
const response = yield this.kvs('/system resource print');
|
|
516
|
+
const result = {
|
|
517
|
+
uptime: response.uptime,
|
|
518
|
+
version: toVersion(response.version),
|
|
519
|
+
build_time: new Date(`${response['build-time']}Z`),
|
|
520
|
+
factory_sofware: response['factory-software'],
|
|
521
|
+
free_memory: response['free-memory'],
|
|
522
|
+
total_memory: response['total-memory'],
|
|
523
|
+
cpu: response.cpu,
|
|
524
|
+
cpu_count: parseInt(response['cpu-count']) || 0,
|
|
525
|
+
cpu_frequency: parseInt(response['cpu-frequency']) || 0,
|
|
526
|
+
cpu_load: (parseInt(response['cpu-load']) || 0) / 100,
|
|
527
|
+
hdd_space: {
|
|
528
|
+
free: response['free-hdd-space'],
|
|
529
|
+
total: response['total-hdd-space']
|
|
530
|
+
},
|
|
531
|
+
architecture_name: response['architecture-name'],
|
|
532
|
+
board_name: response['board-name'],
|
|
533
|
+
platform: response.platform
|
|
534
|
+
};
|
|
535
|
+
if (response.version.includes('long-term')) {
|
|
536
|
+
result.lts = true;
|
|
537
|
+
}
|
|
538
|
+
else if (response.version.includes('stable')) {
|
|
539
|
+
result.stable = true;
|
|
540
|
+
}
|
|
541
|
+
else if (response.version.includes('testing')) {
|
|
542
|
+
result.testing = true;
|
|
543
|
+
}
|
|
544
|
+
else if (response.version.includes('development')) {
|
|
545
|
+
result.development = true;
|
|
546
|
+
}
|
|
547
|
+
if (response['write-sect-since-reboot'] && response['write-sect-total'] && response['bad-blocks']) {
|
|
548
|
+
result.nvram = {
|
|
549
|
+
write_since_reboot: parseInt(response['write-sect-since-reboot']) || 0,
|
|
550
|
+
write_total: parseInt(response['write-sect-total']) || 0,
|
|
551
|
+
bad_blocks: parseInt(response['bad-blocks']) || 0
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
return result;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Fetches the current RouterOS version
|
|
559
|
+
*/
|
|
560
|
+
version() {
|
|
561
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
562
|
+
return (yield this.resource()).version;
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Fetches the semantic RouterOS version
|
|
567
|
+
*/
|
|
568
|
+
semantic_version() {
|
|
569
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
570
|
+
const version = yield this.version();
|
|
571
|
+
const [major, minor, patch] = version.split('.')
|
|
572
|
+
.map(elem => parseInt(elem) || 0);
|
|
573
|
+
return {
|
|
574
|
+
major,
|
|
575
|
+
minor,
|
|
576
|
+
patch
|
|
577
|
+
};
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Fetches the current health information
|
|
582
|
+
*/
|
|
583
|
+
health() {
|
|
584
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
585
|
+
const { major } = yield this.semantic_version();
|
|
586
|
+
const result = {};
|
|
587
|
+
if (major === 6) {
|
|
588
|
+
const response = yield this.kvs('/system health print');
|
|
589
|
+
result.voltage = parseFloat(response.voltage) || 0;
|
|
590
|
+
result.current = parseFloat(response.current) || 0;
|
|
591
|
+
result.temperature = parseFloat(response.temperature) || 0;
|
|
592
|
+
result.power_consumption = parseFloat(response['power-consumption']) || 0;
|
|
593
|
+
if (response['psu-voltage']) {
|
|
594
|
+
result.psu_voltage = parseFloat(response['psu-voltage']);
|
|
595
|
+
}
|
|
596
|
+
if (response['psu1-voltage']) {
|
|
597
|
+
result.psu1_voltage = parseFloat(response['psu1-voltage']);
|
|
598
|
+
}
|
|
599
|
+
if (response['psu2-voltage']) {
|
|
600
|
+
result.psu2_voltage = parseFloat(response['psu2-voltage']);
|
|
601
|
+
}
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
else if (major === 7) {
|
|
605
|
+
const response = yield this.terse('/system health print terse');
|
|
606
|
+
for (const { name, value, type } of response) {
|
|
607
|
+
const num = parseFloat(value) || 0;
|
|
608
|
+
switch (name) {
|
|
609
|
+
case 'voltage':
|
|
610
|
+
result.voltage = num;
|
|
611
|
+
break;
|
|
612
|
+
case 'temperature':
|
|
613
|
+
result.temperature = num;
|
|
614
|
+
break;
|
|
615
|
+
case 'power-consumption':
|
|
616
|
+
result.power_consumption = num;
|
|
617
|
+
break;
|
|
618
|
+
case 'current':
|
|
619
|
+
result.current = type === 'A' ? num * 1000 : num;
|
|
620
|
+
break;
|
|
621
|
+
case 'psu-voltage':
|
|
622
|
+
result.psu_voltage = num;
|
|
623
|
+
break;
|
|
624
|
+
case 'psu1-voltage':
|
|
625
|
+
result.psu1_voltage = num;
|
|
626
|
+
break;
|
|
627
|
+
case 'psu2-voltage':
|
|
628
|
+
result.psu2_voltage = num;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
throw new Error('RouterOS version is not supported for this command');
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
}
|
|
459
638
|
/**
|
|
460
639
|
* Executes a command with expected terse response and parses it accordingly
|
|
461
640
|
*
|
|
@@ -469,15 +648,19 @@ class Mikrotik extends ssh_1.default {
|
|
|
469
648
|
.toString()
|
|
470
649
|
.split('\r\n')
|
|
471
650
|
.map(line => line.trim())
|
|
472
|
-
.filter(line => line.split(/\s+/)
|
|
473
|
-
.length !== 0 && line.length !== 0);
|
|
651
|
+
.filter(line => line.split(/\s+/).length !== 0 && line.length !== 0);
|
|
474
652
|
lines.forEach(line => {
|
|
475
653
|
const result = {};
|
|
476
654
|
line.split(/\s+/)
|
|
477
|
-
.
|
|
655
|
+
.map(col => col.trim())
|
|
478
656
|
.forEach(col => {
|
|
479
|
-
|
|
480
|
-
|
|
657
|
+
if (col.includes('=')) {
|
|
658
|
+
const [key, ...value] = col.split('=');
|
|
659
|
+
result[key] = value.join('=');
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
result[col] = col;
|
|
663
|
+
}
|
|
481
664
|
});
|
|
482
665
|
if (Object.keys(result).length !== 0)
|
|
483
666
|
results.push(result);
|
|
@@ -485,6 +668,28 @@ class Mikrotik extends ssh_1.default {
|
|
|
485
668
|
return results;
|
|
486
669
|
});
|
|
487
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* Executes a command that expects the result as a 'table' of key-value pairs separated by a colon (:)
|
|
673
|
+
*
|
|
674
|
+
* @param command
|
|
675
|
+
* @protected
|
|
676
|
+
*/
|
|
677
|
+
kvs(command) {
|
|
678
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
679
|
+
const result = {};
|
|
680
|
+
const lines = (yield this.exec(command))
|
|
681
|
+
.toString()
|
|
682
|
+
.split('\r\n')
|
|
683
|
+
.map(line => line.trim())
|
|
684
|
+
.filter(line => line.length !== 0)
|
|
685
|
+
.map(line => line.split(':')
|
|
686
|
+
.map(col => col.trim()));
|
|
687
|
+
for (const [key, ...value] of lines) {
|
|
688
|
+
result[key] = value.join(':');
|
|
689
|
+
}
|
|
690
|
+
return result;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
488
693
|
}
|
|
489
694
|
Mikrotik.cache = new memory_1.default({
|
|
490
695
|
stdTTL: 15,
|
package/dist/types.d.ts
CHANGED
|
@@ -38,51 +38,142 @@ export declare namespace BandwidthTest {
|
|
|
38
38
|
}
|
|
39
39
|
export {};
|
|
40
40
|
}
|
|
41
|
+
export declare namespace CommandResponses {
|
|
42
|
+
type YesNo = 'yes' | 'no';
|
|
43
|
+
interface Comment {
|
|
44
|
+
comment?: string;
|
|
45
|
+
}
|
|
46
|
+
interface Route extends Comment {
|
|
47
|
+
'dst-address': string;
|
|
48
|
+
gateway: string;
|
|
49
|
+
distance: string;
|
|
50
|
+
scope: string;
|
|
51
|
+
target_scope: string;
|
|
52
|
+
'routing-table'?: string;
|
|
53
|
+
'routing-mark'?: string;
|
|
54
|
+
'bgp-as-path'?: string;
|
|
55
|
+
'bgp-origin'?: string;
|
|
56
|
+
'bgp-local-pref'?: string;
|
|
57
|
+
'received-from'?: string;
|
|
58
|
+
'check-gateway'?: string;
|
|
59
|
+
'suppress-hw-offload'?: string;
|
|
60
|
+
'blackhole'?: string;
|
|
61
|
+
'immediate-gw'?: string;
|
|
62
|
+
reachable?: string;
|
|
63
|
+
'gateway-status'?: string;
|
|
64
|
+
}
|
|
65
|
+
interface Address extends Comment {
|
|
66
|
+
address: string;
|
|
67
|
+
network: string;
|
|
68
|
+
interface: string;
|
|
69
|
+
'actual-interface': string;
|
|
70
|
+
}
|
|
71
|
+
interface Interface extends Comment {
|
|
72
|
+
name: string;
|
|
73
|
+
type: string;
|
|
74
|
+
mtu: string;
|
|
75
|
+
'default-name'?: string;
|
|
76
|
+
'actual-mtu': string;
|
|
77
|
+
'mac-address'?: string;
|
|
78
|
+
'link-downs'?: string;
|
|
79
|
+
l2mtu?: string;
|
|
80
|
+
}
|
|
81
|
+
interface TunnelInterface extends Omit<Interface, 'type'>, Comment {
|
|
82
|
+
'local-address': string;
|
|
83
|
+
'remote-address': string;
|
|
84
|
+
'clamp-tcp-mss': YesNo;
|
|
85
|
+
'dont-fragment': YesNo;
|
|
86
|
+
'allow-fast-path': YesNo;
|
|
87
|
+
}
|
|
88
|
+
interface Routeboard {
|
|
89
|
+
routerboard: YesNo;
|
|
90
|
+
'board-name': string;
|
|
91
|
+
model: string;
|
|
92
|
+
'serial-number': string;
|
|
93
|
+
'firmware-type': string;
|
|
94
|
+
'factory-firmware': string;
|
|
95
|
+
'current-firmware': string;
|
|
96
|
+
'upgrade-firmware': string;
|
|
97
|
+
}
|
|
98
|
+
interface Identity {
|
|
99
|
+
name: string;
|
|
100
|
+
}
|
|
101
|
+
interface Resource {
|
|
102
|
+
uptime: string;
|
|
103
|
+
version: string;
|
|
104
|
+
'build-time': string;
|
|
105
|
+
'factory-software': string;
|
|
106
|
+
'free-memory': string;
|
|
107
|
+
'total-memory': string;
|
|
108
|
+
cpu: string;
|
|
109
|
+
'cpu-count': string;
|
|
110
|
+
'cpu-frequency': string;
|
|
111
|
+
'cpu-load': string;
|
|
112
|
+
'free-hdd-space': string;
|
|
113
|
+
'total-hdd-space': string;
|
|
114
|
+
'architecture-name': string;
|
|
115
|
+
'board-name': string;
|
|
116
|
+
platform: string;
|
|
117
|
+
'write-sect-since-reboot'?: string;
|
|
118
|
+
'write-sect-total'?: string;
|
|
119
|
+
'bad-blocks'?: string;
|
|
120
|
+
}
|
|
121
|
+
interface Health {
|
|
122
|
+
voltage: string;
|
|
123
|
+
current: string;
|
|
124
|
+
temperature: string;
|
|
125
|
+
'power-consumption': string;
|
|
126
|
+
'psu-voltage'?: string;
|
|
127
|
+
'psu1-voltage'?: string;
|
|
128
|
+
'psu2-voltage'?: string;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
41
131
|
export declare namespace Response {
|
|
42
|
-
interface
|
|
132
|
+
interface includesAddress {
|
|
133
|
+
includes(ipaddress: string): boolean;
|
|
134
|
+
}
|
|
135
|
+
interface Comment {
|
|
136
|
+
comment?: string;
|
|
137
|
+
}
|
|
138
|
+
export interface Address extends includesAddress, Comment {
|
|
43
139
|
ipaddress: string;
|
|
44
140
|
network: string;
|
|
45
141
|
cidr: number;
|
|
46
142
|
iface: string;
|
|
47
|
-
isLocal(ipaddress: string): boolean;
|
|
48
143
|
}
|
|
49
|
-
interface Interface {
|
|
144
|
+
export interface Interface extends Comment {
|
|
50
145
|
name: string;
|
|
51
146
|
type: string;
|
|
52
147
|
local_address?: string;
|
|
53
148
|
remote_address?: string;
|
|
54
149
|
}
|
|
55
|
-
interface Tunnel extends Interface {
|
|
56
|
-
|
|
150
|
+
export interface Tunnel extends Interface {
|
|
151
|
+
parent: Address;
|
|
57
152
|
}
|
|
58
|
-
interface Route {
|
|
59
|
-
network:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
153
|
+
export interface Route extends includesAddress, Comment {
|
|
154
|
+
network: string;
|
|
155
|
+
cidr: number;
|
|
156
|
+
distance: number;
|
|
157
|
+
scope: number;
|
|
158
|
+
vrf: string;
|
|
63
159
|
preferred_source?: string;
|
|
64
160
|
gateway?: string;
|
|
65
161
|
iface?: string;
|
|
66
162
|
tunnel?: Tunnel;
|
|
67
|
-
distance: number;
|
|
68
|
-
scope: number;
|
|
69
163
|
target_scope?: number;
|
|
70
|
-
vrf: string;
|
|
71
164
|
}
|
|
72
|
-
interface RouteCount {
|
|
165
|
+
export interface RouteCount {
|
|
73
166
|
interface: string;
|
|
74
167
|
count: number;
|
|
75
168
|
active: boolean;
|
|
76
169
|
}
|
|
77
|
-
interface Ping {
|
|
78
|
-
source?: string;
|
|
170
|
+
export interface Ping {
|
|
79
171
|
target: string;
|
|
80
172
|
latency: number;
|
|
173
|
+
source?: string;
|
|
81
174
|
}
|
|
82
|
-
interface Traceroute {
|
|
175
|
+
export interface Traceroute {
|
|
83
176
|
hop: number;
|
|
84
|
-
address?: string;
|
|
85
|
-
hostname?: string;
|
|
86
177
|
loss: number;
|
|
87
178
|
sent: number;
|
|
88
179
|
last: number;
|
|
@@ -90,5 +181,55 @@ export declare namespace Response {
|
|
|
90
181
|
best: number;
|
|
91
182
|
worst: number;
|
|
92
183
|
timeout: boolean;
|
|
184
|
+
address?: string;
|
|
185
|
+
hostname?: string;
|
|
186
|
+
}
|
|
187
|
+
export interface Routerboard {
|
|
188
|
+
routerboard: boolean;
|
|
189
|
+
board_name: string;
|
|
190
|
+
model: string;
|
|
191
|
+
serial_number: string;
|
|
192
|
+
firmware_type: string;
|
|
193
|
+
factory_firmware: string;
|
|
194
|
+
current_firmware: string;
|
|
195
|
+
upgrade_firmware: string;
|
|
93
196
|
}
|
|
197
|
+
export interface Resource {
|
|
198
|
+
uptime: string;
|
|
199
|
+
version: string;
|
|
200
|
+
build_time: Date;
|
|
201
|
+
factory_sofware: string;
|
|
202
|
+
free_memory: string;
|
|
203
|
+
total_memory: string;
|
|
204
|
+
cpu: string;
|
|
205
|
+
cpu_count: number;
|
|
206
|
+
cpu_frequency: number;
|
|
207
|
+
cpu_load: number;
|
|
208
|
+
hdd_space: {
|
|
209
|
+
free: string;
|
|
210
|
+
total: string;
|
|
211
|
+
};
|
|
212
|
+
architecture_name: string;
|
|
213
|
+
board_name: string;
|
|
214
|
+
platform: string;
|
|
215
|
+
lts?: boolean;
|
|
216
|
+
stable?: boolean;
|
|
217
|
+
testing?: boolean;
|
|
218
|
+
development?: boolean;
|
|
219
|
+
nvram?: {
|
|
220
|
+
write_since_reboot: number;
|
|
221
|
+
write_total: number;
|
|
222
|
+
bad_blocks: number;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
export interface Health {
|
|
226
|
+
voltage: number;
|
|
227
|
+
current: number;
|
|
228
|
+
temperature: number;
|
|
229
|
+
power_consumption: number;
|
|
230
|
+
psu_voltage?: number;
|
|
231
|
+
psu1_voltage?: number;
|
|
232
|
+
psu2_voltage?: number;
|
|
233
|
+
}
|
|
234
|
+
export {};
|
|
94
235
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gibme/mikrotik",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A simple mikrotik helper/wrapper",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@gibme/cache": "^1.1.5",
|
|
59
59
|
"@gibme/ssh": "^1.0.2",
|
|
60
|
-
"@types/
|
|
61
|
-
"ip": "^
|
|
60
|
+
"@types/jsbn": "^1.2.33",
|
|
61
|
+
"ip-address": "^9.0.5",
|
|
62
|
+
"semver": "^7.6.2"
|
|
62
63
|
}
|
|
63
64
|
}
|