@gibme/mikrotik 1.1.2 → 2.0.1
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 +319 -101
- package/dist/types.d.ts +161 -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,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
|
-
|
|
83
|
-
|
|
84
|
-
const root = ips.filter(ip => ip.ipaddress === tunnel.local_address).shift();
|
|
85
|
-
if (!root)
|
|
97
|
+
const parent = ips.filter(ip => ip.ipaddress === (tunnel === null || tunnel === void 0 ? void 0 : tunnel.local_address)).shift();
|
|
98
|
+
if (!tunnel || !parent)
|
|
86
99
|
return undefined;
|
|
87
|
-
return Object.assign(Object.assign({}, tunnel), {
|
|
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)); });
|
|
@@ -345,14 +375,27 @@ class Mikrotik extends ssh_1.default {
|
|
|
345
375
|
});
|
|
346
376
|
}
|
|
347
377
|
this.on('stream', handleStream);
|
|
378
|
+
const cleanup = () => __awaiter(this, void 0, void 0, function* () {
|
|
379
|
+
this.off('stream', handleStream);
|
|
380
|
+
yield Mikrotik.cache.del(target);
|
|
381
|
+
});
|
|
348
382
|
yield Mikrotik.cache.set(target, target, options.duration); // set our mutex
|
|
349
|
-
yield this.stream(`/tool bandwidth-test protocol=${options.protocol} ` +
|
|
383
|
+
const cancel = yield this.stream(`/tool bandwidth-test protocol=${options.protocol} ` +
|
|
350
384
|
`user=${username} password=${password} ` +
|
|
351
385
|
`duration=${options.duration}s direction=${options.direction} ` +
|
|
352
386
|
`address=${target} random-data=${options.random_data ? 'yes' : 'no'} interval=1s`, {
|
|
353
387
|
separator: '\r\n\r\n'
|
|
354
388
|
});
|
|
355
|
-
|
|
389
|
+
this.once('stream_complete', () => __awaiter(this, void 0, void 0, function* () {
|
|
390
|
+
yield cleanup();
|
|
391
|
+
}));
|
|
392
|
+
this.once('stream_cancelled', () => __awaiter(this, void 0, void 0, function* () {
|
|
393
|
+
yield cleanup();
|
|
394
|
+
return reject(new Error('Bandwidth Test Cancelled'));
|
|
395
|
+
}));
|
|
396
|
+
if (options.timeout) {
|
|
397
|
+
setTimeout(cancel, options.timeout);
|
|
398
|
+
}
|
|
356
399
|
}));
|
|
357
400
|
});
|
|
358
401
|
}
|
|
@@ -370,10 +413,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
370
413
|
};
|
|
371
414
|
if (source)
|
|
372
415
|
result.source = source;
|
|
373
|
-
|
|
374
|
-
if (source) {
|
|
375
|
-
command += ` src-address=${source}`;
|
|
376
|
-
}
|
|
416
|
+
const command = `/ping address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
377
417
|
try {
|
|
378
418
|
const response = (yield this.exec(command))
|
|
379
419
|
.toString()
|
|
@@ -402,10 +442,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
402
442
|
*/
|
|
403
443
|
traceroute(target, source) {
|
|
404
444
|
return __awaiter(this, void 0, void 0, function* () {
|
|
405
|
-
|
|
406
|
-
if (source) {
|
|
407
|
-
command += ` src-address=${source}`;
|
|
408
|
-
}
|
|
445
|
+
const command = `/tool traceroute address=${target} count=1${source ? ` src-address=${source}` : ''}`;
|
|
409
446
|
const response = (yield this.exec(command))
|
|
410
447
|
.toString()
|
|
411
448
|
.split('\r\n\r\n')
|
|
@@ -420,7 +457,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
420
457
|
if (!response)
|
|
421
458
|
throw new Error(`Could not perform traceroute to ${target}`);
|
|
422
459
|
const results = [];
|
|
423
|
-
const
|
|
460
|
+
const resolveDNS = (ip) => __awaiter(this, void 0, void 0, function* () {
|
|
424
461
|
return new Promise(resolve => {
|
|
425
462
|
(0, dns_1.reverse)(ip, (error, addresses) => {
|
|
426
463
|
if (error)
|
|
@@ -445,7 +482,7 @@ class Mikrotik extends ssh_1.default {
|
|
|
445
482
|
results.push(result);
|
|
446
483
|
}
|
|
447
484
|
(yield Promise.all(results.filter(result => result.address)
|
|
448
|
-
.map(result =>
|
|
485
|
+
.map(result => resolveDNS(result.address || ''))))
|
|
449
486
|
.forEach(result => {
|
|
450
487
|
for (let i = 0; i < results.length; i++) {
|
|
451
488
|
if (result[0] === results[i].address && result[1]) {
|
|
@@ -456,6 +493,161 @@ class Mikrotik extends ssh_1.default {
|
|
|
456
493
|
return results;
|
|
457
494
|
});
|
|
458
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Fetches routerboard information from the device
|
|
498
|
+
*/
|
|
499
|
+
routerboard() {
|
|
500
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
501
|
+
const result = yield this.kvs('/system routerboard print');
|
|
502
|
+
return {
|
|
503
|
+
routerboard: result.routerboard === 'yes',
|
|
504
|
+
board_name: result['board-name'],
|
|
505
|
+
model: result.model,
|
|
506
|
+
serial_number: result['serial-number'],
|
|
507
|
+
firmware_type: result['firmware-type'],
|
|
508
|
+
factory_firmware: toVersion(result['factory-firmware']),
|
|
509
|
+
current_firmware: toVersion(result['current-firmware']),
|
|
510
|
+
upgrade_firmware: toVersion(result['upgrade-firmware'])
|
|
511
|
+
};
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Fetches the identity of the device
|
|
516
|
+
*/
|
|
517
|
+
identity() {
|
|
518
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
519
|
+
const response = yield this.kvs('/system identity print');
|
|
520
|
+
return response.name;
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Fetches the resource information of the device
|
|
525
|
+
*/
|
|
526
|
+
resource() {
|
|
527
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
528
|
+
const response = yield this.kvs('/system resource print');
|
|
529
|
+
const result = {
|
|
530
|
+
uptime: response.uptime,
|
|
531
|
+
version: toVersion(response.version),
|
|
532
|
+
build_time: new Date(`${response['build-time']}Z`),
|
|
533
|
+
factory_sofware: response['factory-software'],
|
|
534
|
+
free_memory: response['free-memory'],
|
|
535
|
+
total_memory: response['total-memory'],
|
|
536
|
+
cpu: response.cpu,
|
|
537
|
+
cpu_count: parseInt(response['cpu-count']) || 0,
|
|
538
|
+
cpu_frequency: parseInt(response['cpu-frequency']) || 0,
|
|
539
|
+
cpu_load: (parseInt(response['cpu-load']) || 0) / 100,
|
|
540
|
+
hdd_space: {
|
|
541
|
+
free: response['free-hdd-space'],
|
|
542
|
+
total: response['total-hdd-space']
|
|
543
|
+
},
|
|
544
|
+
architecture_name: response['architecture-name'],
|
|
545
|
+
board_name: response['board-name'],
|
|
546
|
+
platform: response.platform
|
|
547
|
+
};
|
|
548
|
+
if (response.version.includes('long-term')) {
|
|
549
|
+
result.lts = true;
|
|
550
|
+
}
|
|
551
|
+
else if (response.version.includes('stable')) {
|
|
552
|
+
result.stable = true;
|
|
553
|
+
}
|
|
554
|
+
else if (response.version.includes('testing')) {
|
|
555
|
+
result.testing = true;
|
|
556
|
+
}
|
|
557
|
+
else if (response.version.includes('development')) {
|
|
558
|
+
result.development = true;
|
|
559
|
+
}
|
|
560
|
+
if (response['write-sect-since-reboot'] && response['write-sect-total'] && response['bad-blocks']) {
|
|
561
|
+
result.nvram = {
|
|
562
|
+
write_since_reboot: parseInt(response['write-sect-since-reboot']) || 0,
|
|
563
|
+
write_total: parseInt(response['write-sect-total']) || 0,
|
|
564
|
+
bad_blocks: parseInt(response['bad-blocks']) || 0
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Fetches the current RouterOS version
|
|
572
|
+
*/
|
|
573
|
+
version() {
|
|
574
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
575
|
+
return (yield this.resource()).version;
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Fetches the semantic RouterOS version
|
|
580
|
+
*/
|
|
581
|
+
semantic_version() {
|
|
582
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
583
|
+
const version = yield this.version();
|
|
584
|
+
const [major, minor, patch] = version.split('.')
|
|
585
|
+
.map(elem => parseInt(elem) || 0);
|
|
586
|
+
return {
|
|
587
|
+
major,
|
|
588
|
+
minor,
|
|
589
|
+
patch
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Fetches the current health information
|
|
595
|
+
*/
|
|
596
|
+
health() {
|
|
597
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
598
|
+
const { major } = yield this.semantic_version();
|
|
599
|
+
const result = {};
|
|
600
|
+
if (major === 6) {
|
|
601
|
+
const response = yield this.kvs('/system health print');
|
|
602
|
+
result.voltage = parseFloat(response.voltage) || 0;
|
|
603
|
+
result.current = parseFloat(response.current) || 0;
|
|
604
|
+
result.temperature = parseFloat(response.temperature) || 0;
|
|
605
|
+
result.power_consumption = parseFloat(response['power-consumption']) || 0;
|
|
606
|
+
if (response['psu-voltage']) {
|
|
607
|
+
result.psu_voltage = parseFloat(response['psu-voltage']);
|
|
608
|
+
}
|
|
609
|
+
if (response['psu1-voltage']) {
|
|
610
|
+
result.psu1_voltage = parseFloat(response['psu1-voltage']);
|
|
611
|
+
}
|
|
612
|
+
if (response['psu2-voltage']) {
|
|
613
|
+
result.psu2_voltage = parseFloat(response['psu2-voltage']);
|
|
614
|
+
}
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
else if (major === 7) {
|
|
618
|
+
const response = yield this.terse('/system health print terse');
|
|
619
|
+
for (const { name, value, type } of response) {
|
|
620
|
+
const num = parseFloat(value) || 0;
|
|
621
|
+
switch (name) {
|
|
622
|
+
case 'voltage':
|
|
623
|
+
result.voltage = num;
|
|
624
|
+
break;
|
|
625
|
+
case 'temperature':
|
|
626
|
+
result.temperature = num;
|
|
627
|
+
break;
|
|
628
|
+
case 'power-consumption':
|
|
629
|
+
result.power_consumption = num;
|
|
630
|
+
break;
|
|
631
|
+
case 'current':
|
|
632
|
+
result.current = type === 'A' ? num * 1000 : num;
|
|
633
|
+
break;
|
|
634
|
+
case 'psu-voltage':
|
|
635
|
+
result.psu_voltage = num;
|
|
636
|
+
break;
|
|
637
|
+
case 'psu1-voltage':
|
|
638
|
+
result.psu1_voltage = num;
|
|
639
|
+
break;
|
|
640
|
+
case 'psu2-voltage':
|
|
641
|
+
result.psu2_voltage = num;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return result;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
throw new Error('RouterOS version is not supported for this command');
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
459
651
|
/**
|
|
460
652
|
* Executes a command with expected terse response and parses it accordingly
|
|
461
653
|
*
|
|
@@ -469,15 +661,19 @@ class Mikrotik extends ssh_1.default {
|
|
|
469
661
|
.toString()
|
|
470
662
|
.split('\r\n')
|
|
471
663
|
.map(line => line.trim())
|
|
472
|
-
.filter(line => line.split(/\s+/)
|
|
473
|
-
.length !== 0 && line.length !== 0);
|
|
664
|
+
.filter(line => line.split(/\s+/).length !== 0 && line.length !== 0);
|
|
474
665
|
lines.forEach(line => {
|
|
475
666
|
const result = {};
|
|
476
667
|
line.split(/\s+/)
|
|
477
|
-
.
|
|
668
|
+
.map(col => col.trim())
|
|
478
669
|
.forEach(col => {
|
|
479
|
-
|
|
480
|
-
|
|
670
|
+
if (col.includes('=')) {
|
|
671
|
+
const [key, ...value] = col.split('=');
|
|
672
|
+
result[key] = value.join('=');
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
result[col] = col;
|
|
676
|
+
}
|
|
481
677
|
});
|
|
482
678
|
if (Object.keys(result).length !== 0)
|
|
483
679
|
results.push(result);
|
|
@@ -485,6 +681,28 @@ class Mikrotik extends ssh_1.default {
|
|
|
485
681
|
return results;
|
|
486
682
|
});
|
|
487
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* Executes a command that expects the result as a 'table' of key-value pairs separated by a colon (:)
|
|
686
|
+
*
|
|
687
|
+
* @param command
|
|
688
|
+
* @protected
|
|
689
|
+
*/
|
|
690
|
+
kvs(command) {
|
|
691
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
692
|
+
const result = {};
|
|
693
|
+
const lines = (yield this.exec(command))
|
|
694
|
+
.toString()
|
|
695
|
+
.split('\r\n')
|
|
696
|
+
.map(line => line.trim())
|
|
697
|
+
.filter(line => line.length !== 0)
|
|
698
|
+
.map(line => line.split(':')
|
|
699
|
+
.map(col => col.trim()));
|
|
700
|
+
for (const [key, ...value] of lines) {
|
|
701
|
+
result[key] = value.join(':');
|
|
702
|
+
}
|
|
703
|
+
return result;
|
|
704
|
+
});
|
|
705
|
+
}
|
|
488
706
|
}
|
|
489
707
|
Mikrotik.cache = new memory_1.default({
|
|
490
708
|
stdTTL: 15,
|
package/dist/types.d.ts
CHANGED
|
@@ -35,54 +35,146 @@ export declare namespace BandwidthTest {
|
|
|
35
35
|
protocol: Protocol;
|
|
36
36
|
random_data: boolean;
|
|
37
37
|
callback: (frame: Update) => void;
|
|
38
|
+
timeout: number;
|
|
38
39
|
}
|
|
39
40
|
export {};
|
|
40
41
|
}
|
|
42
|
+
export declare namespace CommandResponses {
|
|
43
|
+
type YesNo = 'yes' | 'no';
|
|
44
|
+
interface Comment {
|
|
45
|
+
comment?: string;
|
|
46
|
+
}
|
|
47
|
+
interface Route extends Comment {
|
|
48
|
+
'dst-address': string;
|
|
49
|
+
gateway: string;
|
|
50
|
+
distance: string;
|
|
51
|
+
scope: string;
|
|
52
|
+
target_scope: string;
|
|
53
|
+
'routing-table'?: string;
|
|
54
|
+
'routing-mark'?: string;
|
|
55
|
+
'bgp-as-path'?: string;
|
|
56
|
+
'bgp-origin'?: string;
|
|
57
|
+
'bgp-local-pref'?: string;
|
|
58
|
+
'received-from'?: string;
|
|
59
|
+
'check-gateway'?: string;
|
|
60
|
+
'suppress-hw-offload'?: string;
|
|
61
|
+
'blackhole'?: string;
|
|
62
|
+
'immediate-gw'?: string;
|
|
63
|
+
reachable?: string;
|
|
64
|
+
'gateway-status'?: string;
|
|
65
|
+
}
|
|
66
|
+
interface Address extends Comment {
|
|
67
|
+
address: string;
|
|
68
|
+
network: string;
|
|
69
|
+
interface: string;
|
|
70
|
+
'actual-interface': string;
|
|
71
|
+
}
|
|
72
|
+
interface Interface extends Comment {
|
|
73
|
+
name: string;
|
|
74
|
+
type: string;
|
|
75
|
+
mtu: string;
|
|
76
|
+
'default-name'?: string;
|
|
77
|
+
'actual-mtu': string;
|
|
78
|
+
'mac-address'?: string;
|
|
79
|
+
'link-downs'?: string;
|
|
80
|
+
l2mtu?: string;
|
|
81
|
+
}
|
|
82
|
+
interface TunnelInterface extends Omit<Interface, 'type'>, Comment {
|
|
83
|
+
'local-address': string;
|
|
84
|
+
'remote-address': string;
|
|
85
|
+
'clamp-tcp-mss': YesNo;
|
|
86
|
+
'dont-fragment': YesNo;
|
|
87
|
+
'allow-fast-path': YesNo;
|
|
88
|
+
}
|
|
89
|
+
interface Routeboard {
|
|
90
|
+
routerboard: YesNo;
|
|
91
|
+
'board-name': string;
|
|
92
|
+
model: string;
|
|
93
|
+
'serial-number': string;
|
|
94
|
+
'firmware-type': string;
|
|
95
|
+
'factory-firmware': string;
|
|
96
|
+
'current-firmware': string;
|
|
97
|
+
'upgrade-firmware': string;
|
|
98
|
+
}
|
|
99
|
+
interface Identity {
|
|
100
|
+
name: string;
|
|
101
|
+
}
|
|
102
|
+
interface Resource {
|
|
103
|
+
uptime: string;
|
|
104
|
+
version: string;
|
|
105
|
+
'build-time': string;
|
|
106
|
+
'factory-software': string;
|
|
107
|
+
'free-memory': string;
|
|
108
|
+
'total-memory': string;
|
|
109
|
+
cpu: string;
|
|
110
|
+
'cpu-count': string;
|
|
111
|
+
'cpu-frequency': string;
|
|
112
|
+
'cpu-load': string;
|
|
113
|
+
'free-hdd-space': string;
|
|
114
|
+
'total-hdd-space': string;
|
|
115
|
+
'architecture-name': string;
|
|
116
|
+
'board-name': string;
|
|
117
|
+
platform: string;
|
|
118
|
+
'write-sect-since-reboot'?: string;
|
|
119
|
+
'write-sect-total'?: string;
|
|
120
|
+
'bad-blocks'?: string;
|
|
121
|
+
}
|
|
122
|
+
interface Health {
|
|
123
|
+
voltage: string;
|
|
124
|
+
current: string;
|
|
125
|
+
temperature: string;
|
|
126
|
+
'power-consumption': string;
|
|
127
|
+
'psu-voltage'?: string;
|
|
128
|
+
'psu1-voltage'?: string;
|
|
129
|
+
'psu2-voltage'?: string;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
41
132
|
export declare namespace Response {
|
|
42
|
-
interface
|
|
133
|
+
interface includesAddress {
|
|
134
|
+
includes(ipaddress: string): boolean;
|
|
135
|
+
}
|
|
136
|
+
interface Comment {
|
|
137
|
+
comment?: string;
|
|
138
|
+
}
|
|
139
|
+
export interface Address extends includesAddress, Comment {
|
|
43
140
|
ipaddress: string;
|
|
44
141
|
network: string;
|
|
45
142
|
cidr: number;
|
|
46
143
|
iface: string;
|
|
47
|
-
isLocal(ipaddress: string): boolean;
|
|
48
144
|
}
|
|
49
|
-
interface Interface {
|
|
145
|
+
export interface Interface extends Comment {
|
|
50
146
|
name: string;
|
|
51
147
|
type: string;
|
|
52
148
|
local_address?: string;
|
|
53
149
|
remote_address?: string;
|
|
54
150
|
}
|
|
55
|
-
interface Tunnel extends Interface {
|
|
56
|
-
|
|
151
|
+
export interface Tunnel extends Interface {
|
|
152
|
+
parent: Address;
|
|
57
153
|
}
|
|
58
|
-
interface Route {
|
|
59
|
-
network:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
154
|
+
export interface Route extends includesAddress, Comment {
|
|
155
|
+
network: string;
|
|
156
|
+
cidr: number;
|
|
157
|
+
distance: number;
|
|
158
|
+
scope: number;
|
|
159
|
+
vrf: string;
|
|
63
160
|
preferred_source?: string;
|
|
64
161
|
gateway?: string;
|
|
65
162
|
iface?: string;
|
|
66
163
|
tunnel?: Tunnel;
|
|
67
|
-
distance: number;
|
|
68
|
-
scope: number;
|
|
69
164
|
target_scope?: number;
|
|
70
|
-
vrf: string;
|
|
71
165
|
}
|
|
72
|
-
interface RouteCount {
|
|
166
|
+
export interface RouteCount {
|
|
73
167
|
interface: string;
|
|
74
168
|
count: number;
|
|
75
169
|
active: boolean;
|
|
76
170
|
}
|
|
77
|
-
interface Ping {
|
|
78
|
-
source?: string;
|
|
171
|
+
export interface Ping {
|
|
79
172
|
target: string;
|
|
80
173
|
latency: number;
|
|
174
|
+
source?: string;
|
|
81
175
|
}
|
|
82
|
-
interface Traceroute {
|
|
176
|
+
export interface Traceroute {
|
|
83
177
|
hop: number;
|
|
84
|
-
address?: string;
|
|
85
|
-
hostname?: string;
|
|
86
178
|
loss: number;
|
|
87
179
|
sent: number;
|
|
88
180
|
last: number;
|
|
@@ -90,5 +182,55 @@ export declare namespace Response {
|
|
|
90
182
|
best: number;
|
|
91
183
|
worst: number;
|
|
92
184
|
timeout: boolean;
|
|
185
|
+
address?: string;
|
|
186
|
+
hostname?: string;
|
|
187
|
+
}
|
|
188
|
+
export interface Routerboard {
|
|
189
|
+
routerboard: boolean;
|
|
190
|
+
board_name: string;
|
|
191
|
+
model: string;
|
|
192
|
+
serial_number: string;
|
|
193
|
+
firmware_type: string;
|
|
194
|
+
factory_firmware: string;
|
|
195
|
+
current_firmware: string;
|
|
196
|
+
upgrade_firmware: string;
|
|
93
197
|
}
|
|
198
|
+
export interface Resource {
|
|
199
|
+
uptime: string;
|
|
200
|
+
version: string;
|
|
201
|
+
build_time: Date;
|
|
202
|
+
factory_sofware: string;
|
|
203
|
+
free_memory: string;
|
|
204
|
+
total_memory: string;
|
|
205
|
+
cpu: string;
|
|
206
|
+
cpu_count: number;
|
|
207
|
+
cpu_frequency: number;
|
|
208
|
+
cpu_load: number;
|
|
209
|
+
hdd_space: {
|
|
210
|
+
free: string;
|
|
211
|
+
total: string;
|
|
212
|
+
};
|
|
213
|
+
architecture_name: string;
|
|
214
|
+
board_name: string;
|
|
215
|
+
platform: string;
|
|
216
|
+
lts?: boolean;
|
|
217
|
+
stable?: boolean;
|
|
218
|
+
testing?: boolean;
|
|
219
|
+
development?: boolean;
|
|
220
|
+
nvram?: {
|
|
221
|
+
write_since_reboot: number;
|
|
222
|
+
write_total: number;
|
|
223
|
+
bad_blocks: number;
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
export interface Health {
|
|
227
|
+
voltage: number;
|
|
228
|
+
current: number;
|
|
229
|
+
temperature: number;
|
|
230
|
+
power_consumption: number;
|
|
231
|
+
psu_voltage?: number;
|
|
232
|
+
psu1_voltage?: number;
|
|
233
|
+
psu2_voltage?: number;
|
|
234
|
+
}
|
|
235
|
+
export {};
|
|
94
236
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gibme/mikrotik",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
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.3",
|
|
60
|
+
"@types/jsbn": "^1.2.33",
|
|
61
|
+
"ip-address": "^9.0.5",
|
|
62
|
+
"semver": "^7.6.2"
|
|
62
63
|
}
|
|
63
64
|
}
|