@gibme/mikrotik 1.1.1 → 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 +299 -89
- package/dist/types.d.ts +160 -19
- package/package.json +5 -4
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,32 +33,52 @@ 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] = (() => {
|
|
73
|
+
if (!route.gateway)
|
|
74
|
+
return [undefined, undefined];
|
|
75
|
+
const gw = toIP4(route.gateway);
|
|
57
76
|
if (route.gateway.includes('%')) {
|
|
58
|
-
|
|
77
|
+
const [gateway, ...iface] = route.gateway.split('%');
|
|
78
|
+
return [gateway, iface.join('%')];
|
|
59
79
|
}
|
|
60
|
-
else if (
|
|
61
|
-
const ip = ips.filter(ip => ip.
|
|
80
|
+
else if (gw) {
|
|
81
|
+
const ip = ips.filter(ip => ip.includes(route.gateway)).shift();
|
|
62
82
|
return [route.gateway, ip === null || ip === void 0 ? void 0 : ip.iface];
|
|
63
83
|
}
|
|
64
84
|
else {
|
|
@@ -70,31 +90,42 @@ class Mikrotik extends ssh_1.default {
|
|
|
70
90
|
const scope = parseInt(route.scope);
|
|
71
91
|
const target_scope = parseInt(route.target_scope) || undefined;
|
|
72
92
|
// this does not account for if a device has multiple addresses in the same network
|
|
73
|
-
const preferred_source = (_a = ips.filter(ip => gateway ? ip.
|
|
74
|
-
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';
|
|
75
95
|
const tunnel = (() => {
|
|
76
96
|
const tunnel = ifaces.filter(_iface => _iface.name === iface).shift();
|
|
77
|
-
|
|
97
|
+
const parent = ips.filter(ip => ip.ipaddress === (tunnel === null || tunnel === void 0 ? void 0 : tunnel.local_address)).shift();
|
|
98
|
+
if (!tunnel || !parent)
|
|
78
99
|
return undefined;
|
|
79
|
-
|
|
80
|
-
if (!root)
|
|
81
|
-
return undefined;
|
|
82
|
-
return Object.assign(Object.assign({}, tunnel), { root });
|
|
100
|
+
return Object.assign(Object.assign({}, tunnel), { parent });
|
|
83
101
|
})();
|
|
84
|
-
|
|
85
|
-
network:
|
|
86
|
-
|
|
87
|
-
cidr
|
|
88
|
-
},
|
|
89
|
-
preferred_source,
|
|
90
|
-
gateway,
|
|
91
|
-
iface,
|
|
92
|
-
tunnel,
|
|
102
|
+
const result = {
|
|
103
|
+
network: address,
|
|
104
|
+
cidr,
|
|
93
105
|
distance,
|
|
94
106
|
scope,
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
|
97
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;
|
|
98
129
|
})
|
|
99
130
|
.filter(route => route.distance >= min_distance)
|
|
100
131
|
.filter(route => vrf ? route.vrf === vrf : true);
|
|
@@ -102,76 +133,84 @@ class Mikrotik extends ssh_1.default {
|
|
|
102
133
|
}
|
|
103
134
|
/**
|
|
104
135
|
* Retrieves the IP addresses active on the system
|
|
136
|
+
*
|
|
137
|
+
* @param active_only
|
|
105
138
|
*/
|
|
106
139
|
get_ip_addresses() {
|
|
107
|
-
return __awaiter(this,
|
|
108
|
-
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);
|
|
109
143
|
return addresses.map(line => {
|
|
110
144
|
const _address = line.address.split('/');
|
|
111
145
|
const ipaddress = _address[0];
|
|
112
146
|
const cidr = parseInt(_address[1]);
|
|
113
|
-
|
|
147
|
+
const result = {
|
|
114
148
|
ipaddress,
|
|
115
149
|
network: line.network,
|
|
116
150
|
cidr,
|
|
117
151
|
iface: line.interface,
|
|
118
|
-
|
|
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
|
+
}
|
|
119
159
|
};
|
|
160
|
+
if (line.comment)
|
|
161
|
+
result.comment = line.comment;
|
|
162
|
+
return result;
|
|
120
163
|
});
|
|
121
164
|
});
|
|
122
165
|
}
|
|
123
166
|
/**
|
|
124
167
|
* Retrieves the interfaces active on the system
|
|
168
|
+
*
|
|
169
|
+
* @param active_only
|
|
125
170
|
*/
|
|
126
171
|
get_interfaces() {
|
|
127
|
-
return __awaiter(this,
|
|
128
|
-
const
|
|
129
|
-
const gre = yield
|
|
130
|
-
|
|
131
|
-
|
|
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];
|
|
132
181
|
return ifaces.map(line => {
|
|
133
182
|
const _type = (() => {
|
|
134
183
|
switch (line.type) {
|
|
184
|
+
case 'ether':
|
|
185
|
+
return 'ethernet';
|
|
186
|
+
case 'lo':
|
|
187
|
+
return 'loopback';
|
|
135
188
|
case 'gre-tunnel':
|
|
136
189
|
return 'gre';
|
|
137
190
|
case 'ipip-tunnel':
|
|
138
191
|
return 'ipip';
|
|
139
192
|
case 'eoip-tunnel':
|
|
140
193
|
return 'eoip';
|
|
194
|
+
case 'wg':
|
|
195
|
+
return 'wireguard';
|
|
141
196
|
default:
|
|
142
197
|
return line.type;
|
|
143
198
|
}
|
|
144
199
|
})();
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
case 'ipip':
|
|
150
|
-
return ipip.filter(tunnel => tunnel.name === line.name)[0]['local-address'];
|
|
151
|
-
case 'eoip':
|
|
152
|
-
return eoip.filter(tunnel => tunnel.name === line.name)[0]['local-address'];
|
|
153
|
-
default:
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
})();
|
|
157
|
-
const remote_address = (() => {
|
|
158
|
-
switch (line.type) {
|
|
159
|
-
case 'gre-tunnel':
|
|
160
|
-
return gre.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
161
|
-
case 'ipip':
|
|
162
|
-
return ipip.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
163
|
-
case 'eoip':
|
|
164
|
-
return eoip.filter(tunnel => tunnel.name === line.name)[0]['remote-address'];
|
|
165
|
-
default:
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
})();
|
|
169
|
-
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 = {
|
|
170
204
|
name: line.name,
|
|
171
|
-
type: _type
|
|
172
|
-
local_address,
|
|
173
|
-
remote_address
|
|
205
|
+
type: _type
|
|
174
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;
|
|
175
214
|
});
|
|
176
215
|
});
|
|
177
216
|
}
|
|
@@ -184,8 +223,10 @@ class Mikrotik extends ssh_1.default {
|
|
|
184
223
|
*/
|
|
185
224
|
get_route_counts() {
|
|
186
225
|
return __awaiter(this, arguments, void 0, function* (min_distance = 0, vrf) {
|
|
187
|
-
const routes = yield
|
|
188
|
-
|
|
226
|
+
const [routes, ips] = yield Promise.all([
|
|
227
|
+
this.get_ip_routes(min_distance, vrf),
|
|
228
|
+
this.get_ip_addresses()
|
|
229
|
+
]);
|
|
189
230
|
const results = {};
|
|
190
231
|
ips.forEach(ip => {
|
|
191
232
|
results[ip.ipaddress] = {
|
|
@@ -198,12 +239,12 @@ class Mikrotik extends ssh_1.default {
|
|
|
198
239
|
ip: '',
|
|
199
240
|
count: 0
|
|
200
241
|
};
|
|
201
|
-
|
|
242
|
+
Object.keys(results).forEach(key => {
|
|
202
243
|
if (results[key].count > best.count) {
|
|
203
244
|
best.count = results[key].count;
|
|
204
245
|
best.ip = key;
|
|
205
246
|
}
|
|
206
|
-
}
|
|
247
|
+
});
|
|
207
248
|
if (results[best.ip]) {
|
|
208
249
|
results[best.ip].active = true;
|
|
209
250
|
}
|
|
@@ -223,13 +264,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
223
264
|
* @param options
|
|
224
265
|
*/
|
|
225
266
|
bandwidth_test(target_1, username_1, password_1) {
|
|
226
|
-
return __awaiter(this, arguments, void 0, function* (target, username, password, options = {
|
|
227
|
-
duration: 15,
|
|
228
|
-
direction: 'both',
|
|
229
|
-
protocol: 'udp',
|
|
230
|
-
random_data: false,
|
|
231
|
-
callback: () => { }
|
|
232
|
-
}) {
|
|
267
|
+
return __awaiter(this, arguments, void 0, function* (target, username, password, options = {}) {
|
|
233
268
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
234
269
|
const $ = this;
|
|
235
270
|
const sleep = (timeout) => __awaiter(this, void 0, void 0, function* () { return new Promise(resolve => setTimeout(resolve, timeout)); });
|
|
@@ -365,10 +400,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
365
400
|
};
|
|
366
401
|
if (source)
|
|
367
402
|
result.source = source;
|
|
368
|
-
|
|
369
|
-
if (source) {
|
|
370
|
-
command += ` src-address=${source}`;
|
|
371
|
-
}
|
|
403
|
+
const command = `/ping address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
372
404
|
try {
|
|
373
405
|
const response = (yield this.exec(command))
|
|
374
406
|
.toString()
|
|
@@ -397,10 +429,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
397
429
|
*/
|
|
398
430
|
traceroute(target, source) {
|
|
399
431
|
return __awaiter(this, void 0, void 0, function* () {
|
|
400
|
-
|
|
401
|
-
if (source) {
|
|
402
|
-
command += ` src-address=${source}`;
|
|
403
|
-
}
|
|
432
|
+
const command = `/tool traceroute address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
404
433
|
const response = (yield this.exec(command))
|
|
405
434
|
.toString()
|
|
406
435
|
.split('\r\n\r\n')
|
|
@@ -415,7 +444,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
415
444
|
if (!response)
|
|
416
445
|
throw new Error(`Could not perform traceroute to ${target}`);
|
|
417
446
|
const results = [];
|
|
418
|
-
const
|
|
447
|
+
const resolveDNS = (ip) => __awaiter(this, void 0, void 0, function* () {
|
|
419
448
|
return new Promise(resolve => {
|
|
420
449
|
(0, dns_1.reverse)(ip, (error, addresses) => {
|
|
421
450
|
if (error)
|
|
@@ -440,7 +469,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
440
469
|
results.push(result);
|
|
441
470
|
}
|
|
442
471
|
(yield Promise.all(results.filter(result => result.address)
|
|
443
|
-
.map(result =>
|
|
472
|
+
.map(result => resolveDNS(result.address || ''))))
|
|
444
473
|
.forEach(result => {
|
|
445
474
|
for (let i = 0; i < results.length; i++) {
|
|
446
475
|
if (result[0] === results[i].address && result[1]) {
|
|
@@ -451,6 +480,161 @@ class Mikrotik extends ssh_1.default {
|
|
|
451
480
|
return results;
|
|
452
481
|
});
|
|
453
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
|
+
}
|
|
454
638
|
/**
|
|
455
639
|
* Executes a command with expected terse response and parses it accordingly
|
|
456
640
|
*
|
|
@@ -464,15 +648,19 @@ class Mikrotik extends ssh_1.default {
|
|
|
464
648
|
.toString()
|
|
465
649
|
.split('\r\n')
|
|
466
650
|
.map(line => line.trim())
|
|
467
|
-
.filter(line => line.split(/\s+/)
|
|
468
|
-
.length !== 0 && line.length !== 0);
|
|
651
|
+
.filter(line => line.split(/\s+/).length !== 0 && line.length !== 0);
|
|
469
652
|
lines.forEach(line => {
|
|
470
653
|
const result = {};
|
|
471
654
|
line.split(/\s+/)
|
|
472
|
-
.
|
|
655
|
+
.map(col => col.trim())
|
|
473
656
|
.forEach(col => {
|
|
474
|
-
|
|
475
|
-
|
|
657
|
+
if (col.includes('=')) {
|
|
658
|
+
const [key, ...value] = col.split('=');
|
|
659
|
+
result[key] = value.join('=');
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
result[col] = col;
|
|
663
|
+
}
|
|
476
664
|
});
|
|
477
665
|
if (Object.keys(result).length !== 0)
|
|
478
666
|
results.push(result);
|
|
@@ -480,6 +668,28 @@ class Mikrotik extends ssh_1.default {
|
|
|
480
668
|
return results;
|
|
481
669
|
});
|
|
482
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
|
+
}
|
|
483
693
|
}
|
|
484
694
|
Mikrotik.cache = new memory_1.default({
|
|
485
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",
|
|
@@ -56,8 +56,9 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@gibme/cache": "^1.1.5",
|
|
59
|
-
"@gibme/ssh": "^1.0.
|
|
60
|
-
"@types/
|
|
61
|
-
"ip": "^
|
|
59
|
+
"@gibme/ssh": "^1.0.2",
|
|
60
|
+
"@types/jsbn": "^1.2.33",
|
|
61
|
+
"ip-address": "^9.0.5",
|
|
62
|
+
"semver": "^7.6.2"
|
|
62
63
|
}
|
|
63
64
|
}
|