hola-server 0.6.3 → 0.6.5
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/core/bash.js +283 -0
- package/core/chart.js +36 -0
- package/core/cron.js +10 -0
- package/core/file.js +8 -1
- package/core/lhs.js +27 -0
- package/core/msg.js +11 -0
- package/index.js +6 -1
- package/package.json +4 -2
package/core/bash.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
const { exec } = require("child_process");
|
|
2
|
+
const { is_log_debug, is_log_error, log_debug, log_error } = require('../db/db');
|
|
3
|
+
|
|
4
|
+
const LOG_BASH = "bash";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Run script and get stdout
|
|
8
|
+
* @param {host info,contains user,ip and password} host
|
|
9
|
+
* @param {commands to run} script
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
const run_script = async (host, script) => {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
exec(`sshpass -p '${host.pwd}' ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${host.port} ${host.user}@${host.ip} /bin/bash <<'EOT' \n ${script} \nEOT\n`, { maxBuffer: 1024 * 150000 }, (error, stdout) => {
|
|
15
|
+
if (error) {
|
|
16
|
+
if (is_log_error()) {
|
|
17
|
+
log_error(LOG_BASH, "error running on host:" + host.name + " the script:" + script + ",error:" + error);
|
|
18
|
+
}
|
|
19
|
+
resolve({ err: "error running the script:" + script + ",error:" + error });
|
|
20
|
+
} else {
|
|
21
|
+
if (is_log_debug()) {
|
|
22
|
+
log_debug(LOG_BASH, "executing on host:" + host.name + ", script:" + script + ",stdout:" + stdout);
|
|
23
|
+
}
|
|
24
|
+
resolve({ stdout: stdout });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const run_script_file = async (host, script_file) => {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
exec(`sshpass -p '${host.pwd}' ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${host.port} ${host.user}@${host.ip} /bin/bash < ${script_file}`, (error, stdout) => {
|
|
33
|
+
if (error) {
|
|
34
|
+
if (is_log_error()) {
|
|
35
|
+
log_error(LOG_BASH, "error running on host:" + host.name + " the script_file:" + script_file + ",error:" + error);
|
|
36
|
+
}
|
|
37
|
+
resolve({ err: "error running the script:" + script_file + ",error:" + error });
|
|
38
|
+
} else {
|
|
39
|
+
if (is_log_debug()) {
|
|
40
|
+
log_debug(LOG_BASH, "executing on host:" + host.name + ", script_file:" + script_file + ",stdout:" + stdout);
|
|
41
|
+
}
|
|
42
|
+
resolve({ stdout: stdout });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run command in local host
|
|
50
|
+
* @param {command to execute} cmd
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
const run_local_cmd = async (cmd, log_extra) => {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
exec(cmd, { maxBuffer: 1024 * 15000000 }, (error, stdout) => {
|
|
56
|
+
if (error) {
|
|
57
|
+
if (is_log_error()) {
|
|
58
|
+
log_error(LOG_BASH, "error running on local host with cmd:" + cmd + ",error:" + error, log_extra);
|
|
59
|
+
}
|
|
60
|
+
resolve({ err: "error running the cmd:" + cmd + ",error:" + error }, log_extra);
|
|
61
|
+
} else {
|
|
62
|
+
if (is_log_debug()) {
|
|
63
|
+
log_debug(LOG_BASH, "executing on local host with cmd:" + cmd + ",stdout:" + stdout, log_extra);
|
|
64
|
+
}
|
|
65
|
+
resolve({ stdout: stdout });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Scp remote file to local file
|
|
73
|
+
* @param {remote host} host
|
|
74
|
+
* @param {remote file path} remote_file
|
|
75
|
+
* @param {local file path} locale_file
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
const scp = async (host, remote_file, local_file) => {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
exec(`sshpass -p '${host.pwd}' scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P ${host.port} -q ${host.user}@${host.ip}:${remote_file} ${local_file}`, (error, stdout) => {
|
|
81
|
+
if (error) {
|
|
82
|
+
if (is_log_error()) {
|
|
83
|
+
log_error(LOG_BASH, "error scp on host:" + host.name + " remote:" + remote_file + ",local:" + local_file + ",error:" + error);
|
|
84
|
+
}
|
|
85
|
+
resolve({ err: "error scp:" + remote_file + " to locale:" + local_file + ",err:" + error });
|
|
86
|
+
} else {
|
|
87
|
+
if (is_log_debug()) {
|
|
88
|
+
log_debug(LOG_BASH, "executing scp on host:" + host.name + ", remote:" + remote_file + ",local:" + local_file + ",stdout:" + stdout);
|
|
89
|
+
}
|
|
90
|
+
resolve({ stdout: stdout });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
*Just run simple command and get result directly
|
|
98
|
+
* @param {host info,contains user,ip and password} host
|
|
99
|
+
* @param {command to run} cmd
|
|
100
|
+
* @returns
|
|
101
|
+
*/
|
|
102
|
+
const run_simple_cmd = async (host, cmd) => {
|
|
103
|
+
const { err, stdout } = await run_script(host, cmd);
|
|
104
|
+
if (err) {
|
|
105
|
+
return null;
|
|
106
|
+
} else {
|
|
107
|
+
return stdout.trim();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
*Just run simple command and get result directly
|
|
113
|
+
* @param {command to run} cmd
|
|
114
|
+
* @returns
|
|
115
|
+
*/
|
|
116
|
+
const run_simple_local_cmd = async (cmd) => {
|
|
117
|
+
const { err, stdout } = await run_local_cmd(cmd);
|
|
118
|
+
if (err) {
|
|
119
|
+
return null;
|
|
120
|
+
} else {
|
|
121
|
+
return stdout.trim();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Use regular expression to match the pattern to get some part of value
|
|
127
|
+
Speed: Unknown
|
|
128
|
+
Manufacturer: NO DIMM
|
|
129
|
+
Serial Number: NO DIMM
|
|
130
|
+
Asset Tag:
|
|
131
|
+
Part Number: NO DIMM
|
|
132
|
+
Rank: Unknown
|
|
133
|
+
Configured Memory Speed: Unknown
|
|
134
|
+
Minimum Voltage: 1.2 V
|
|
135
|
+
Maximum Voltage: 1.2 V
|
|
136
|
+
Configured Voltage: 1.2 V
|
|
137
|
+
|
|
138
|
+
* @param {stdout of the run_cmd} stdout
|
|
139
|
+
* @param {key to retrieve info} key
|
|
140
|
+
* @returns
|
|
141
|
+
*/
|
|
142
|
+
const get_info = (stdout, key) => {
|
|
143
|
+
const word_key = key.split(" ").join("\\s+");
|
|
144
|
+
const regex = new RegExp(`\n\\s?${word_key}\\s?:(.*)\\s`, 'g');
|
|
145
|
+
if (is_log_debug()) {
|
|
146
|
+
log_debug(LOG_BASH, "get_info and regex:" + JSON.stringify(regex));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const results = stdout.matchAll(regex);
|
|
150
|
+
|
|
151
|
+
const matched = [];
|
|
152
|
+
for (let result of results) {
|
|
153
|
+
matched.push(result[1].trim());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (is_log_debug()) {
|
|
157
|
+
log_debug(LOG_BASH, "get_info and matched:" + JSON.stringify(matched));
|
|
158
|
+
}
|
|
159
|
+
return matched;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get the lines of array
|
|
164
|
+
* @param {array of lines} array
|
|
165
|
+
* @param {array of line number to get,use range to define the lines} lines
|
|
166
|
+
* @returns
|
|
167
|
+
*/
|
|
168
|
+
const get_lines = (array, lines) => {
|
|
169
|
+
const result = [];
|
|
170
|
+
lines.forEach(line => {
|
|
171
|
+
result.push(array[line]);
|
|
172
|
+
});
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Read all the lines to object such as lscpu to retrieve all the cpu info
|
|
178
|
+
Core(s) per socket: 28
|
|
179
|
+
Socket(s): 2
|
|
180
|
+
NUMA node(s): 2
|
|
181
|
+
Vendor ID: GenuineIntel
|
|
182
|
+
CPU family: 6
|
|
183
|
+
Model: 85
|
|
184
|
+
Model name: Intel(R) Xeon(R) Gold 6258R CPU @ 2.70GHz
|
|
185
|
+
Stepping: 7
|
|
186
|
+
CPU MHz: 2965.749
|
|
187
|
+
CPU max MHz: 4000.0000
|
|
188
|
+
CPU min MHz: 1000.0000
|
|
189
|
+
BogoMIPS: 5400.00
|
|
190
|
+
Virtualization: VT-x
|
|
191
|
+
L1d cache: 32K
|
|
192
|
+
L1i cache: 32K
|
|
193
|
+
|
|
194
|
+
* @param {stdout of the run_cmd} stdout
|
|
195
|
+
* @param {delimiter to split} delimiter
|
|
196
|
+
* @param {lines to retrieve} lines
|
|
197
|
+
* @param {only retrieve the value that has the same property in config} config
|
|
198
|
+
* @returns
|
|
199
|
+
*/
|
|
200
|
+
const read_key_value_line = (stdout, delimiter = ":", lines, config, exclude_mode) => {
|
|
201
|
+
const obj = {};
|
|
202
|
+
if (!stdout) {
|
|
203
|
+
return obj;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const contents = stdout.toString().split(/(?:\r\n|\r|\n)/g);
|
|
207
|
+
const line_texts = lines ? get_lines(contents, lines) : contents;
|
|
208
|
+
line_texts.forEach(content => {
|
|
209
|
+
const key_value = content.split(delimiter);
|
|
210
|
+
if (key_value.length == 2) {
|
|
211
|
+
const key = key_value[0].trim();
|
|
212
|
+
if (key && !config || (exclude_mode != true && config && config[key]) || exclude_mode == true && config && !config[`!${key}`]) {
|
|
213
|
+
obj[key] = key_value[1].trim();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return obj;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Use delimeter to split the line and retrieve the info
|
|
223
|
+
*
|
|
224
|
+
MODEL SIZE
|
|
225
|
+
INTEL SSDSC2KB03 3.5T
|
|
226
|
+
INTEL SSDPE2KE032T7 2.9T
|
|
227
|
+
|
|
228
|
+
* @param {stdout of the run_cmd} stdout
|
|
229
|
+
* @param {the attribute keys} keys
|
|
230
|
+
* @param {the lines to ignore} ignore
|
|
231
|
+
* @param {delimiter to split the line} delimiter
|
|
232
|
+
* @returns
|
|
233
|
+
*/
|
|
234
|
+
const read_obj_line = (stdout, keys, ignore = 1, delimiter = " ") => {
|
|
235
|
+
const results = [];
|
|
236
|
+
if (!stdout) {
|
|
237
|
+
return results;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const contents = stdout.toString().split(/(?:\r\n|\r|\n)/g);
|
|
241
|
+
const line_texts = contents.slice(ignore);
|
|
242
|
+
line_texts.forEach(content => {
|
|
243
|
+
const values = content.split(delimiter);
|
|
244
|
+
if (values && values.length > 0 && values[0].trim().length > 0) {
|
|
245
|
+
const obj = {};
|
|
246
|
+
const attrs = values.filter(f => f.trim().length > 0);
|
|
247
|
+
for (let i = 0; i < keys.length; i++) {
|
|
248
|
+
const key = keys[i];
|
|
249
|
+
const value = attrs[i];
|
|
250
|
+
value && (obj[key] = value.trim());
|
|
251
|
+
}
|
|
252
|
+
results.push(obj);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
return results;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Retrive attributes value and put it to obj
|
|
260
|
+
* @param {host info,contains user,ip and password} host
|
|
261
|
+
* @param {*} attrs {name:"attr name",cmd:"command to get the attr"}
|
|
262
|
+
*/
|
|
263
|
+
const get_system_attributes = async (host, attrs) => {
|
|
264
|
+
const obj = {};
|
|
265
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
266
|
+
const attr = attrs[i];
|
|
267
|
+
const value = await run_simple_cmd(host, attr.cmd);
|
|
268
|
+
value && (obj[attr.name] = value);
|
|
269
|
+
}
|
|
270
|
+
return obj;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const stop_process = async (host, process_name, stop_cmd, using_full) => {
|
|
274
|
+
const grep_cmd = using_full == true ? `pgrep -f "${process_name}" | wc -l` : `pgrep ${process_name} | wc -l`;
|
|
275
|
+
const { stdout } = await run_script(host, grep_cmd);
|
|
276
|
+
const has_process = stdout && parseInt(stdout) > 0;
|
|
277
|
+
if (has_process) {
|
|
278
|
+
await run_script(host, stop_cmd);
|
|
279
|
+
}
|
|
280
|
+
return has_process;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { stop_process, scp, run_script, run_script_file, run_simple_cmd, run_local_cmd, run_simple_local_cmd, get_info, get_system_attributes, read_key_value_line, read_obj_line };
|
package/core/chart.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { has_value } = require("./validate");
|
|
2
|
+
|
|
3
|
+
const set_chart_header = (arr1, prefix) => {
|
|
4
|
+
if (!arr1 || arr1.length < 1) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const headers = arr1[0];
|
|
8
|
+
if (!headers || headers.length < 1) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (let i = 1; i < headers.length; i++) {
|
|
12
|
+
headers[i] = `${prefix}${headers[i]}`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const merge_chart_data = (arr1, arr2) => {
|
|
17
|
+
if (!arr1 || arr1.length < 2 || !arr2 || arr2.length < 2) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const max = Math.max(arr1.length, arr2.length);
|
|
22
|
+
const arr1_cols = arr1[0].length;
|
|
23
|
+
const arr2_cols = arr2[0].length;
|
|
24
|
+
for (let i = 0; i < max; i++) {
|
|
25
|
+
if (!has_value(arr1[i])) {
|
|
26
|
+
arr1[i] = [...new Array(arr1_cols)].map(o => "");
|
|
27
|
+
arr1[i][0] = arr2[i][0];
|
|
28
|
+
}
|
|
29
|
+
if (!has_value(arr2[i])) {
|
|
30
|
+
arr2[i] = [...new Array(arr2_cols)].map(o => "");
|
|
31
|
+
}
|
|
32
|
+
arr1[i] = [...arr1[i], ...arr2[i].splice(1)];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { set_chart_header, merge_chart_data }
|
package/core/cron.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const node_cron = require('node-cron');
|
|
2
|
+
|
|
3
|
+
const init_cron = async (crons, tasks) => {
|
|
4
|
+
crons.forEach((cron, index) => {
|
|
5
|
+
const schedule = node_cron.schedule(cron, () => { tasks[index](); }, { scheduled: true, timezone: "Asia/Shanghai" });
|
|
6
|
+
schedule.start();
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = { init_cron };
|
package/core/file.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const fs_promises = require('fs').promises;
|
|
3
|
+
|
|
2
4
|
const unzipper = require('unzipper');
|
|
3
5
|
|
|
4
6
|
const file_extension = file_name => file_name ? file_name.split('.').pop() : "";
|
|
@@ -28,4 +30,9 @@ const is_file_exist = (path) => {
|
|
|
28
30
|
return true;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
const get_file_size = async (path) => {
|
|
34
|
+
const stats = await fs_promises.stat(path);
|
|
35
|
+
return stats.size;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { file_extension, file_prefix, read_from_zip_by_extension, read_from_zip_by_prefix, is_file_exist, get_file_size }
|
package/core/lhs.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
const vm = require('node:vm');
|
|
3
|
+
const { range, scale, space } = require("./number");
|
|
4
|
+
|
|
5
|
+
const get_context = () => { return { range: range, scale: scale, space: space } }
|
|
6
|
+
|
|
7
|
+
const run_in_context = (code, ctx) => {
|
|
8
|
+
vm.createContext(ctx);
|
|
9
|
+
vm.runInContext(code, ctx);
|
|
10
|
+
return ctx;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const verify_template = (template, knob) => {
|
|
14
|
+
try {
|
|
15
|
+
run_in_context("__output__=`" + template + "`;", knob);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return err.message;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const execute_template = (template, knob) => {
|
|
23
|
+
const ctx = run_in_context("__output__=`" + template + "`;", knob);
|
|
24
|
+
return ctx["__output__"];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { get_context, run_in_context, verify_template, execute_template }
|
package/core/msg.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const wxm = require('wxmnode');
|
|
2
|
+
|
|
3
|
+
const init_wxm = (name, password) => {
|
|
4
|
+
wxm.init(name, password);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const send_msg = async (content, type, detail) => {
|
|
8
|
+
return await wxm.sendMsg(content, type, detail);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = { init_wxm, send_msg }
|
package/index.js
CHANGED
|
@@ -8,8 +8,13 @@ const { EntityMeta, get_entity_meta } = require('./core/meta');
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
const array = require('./core/array');
|
|
11
|
+
const bash = require('./core/bash');
|
|
12
|
+
const chart = require('./core/chart');
|
|
13
|
+
const cron = require('./core/cron');
|
|
11
14
|
const date = require('./core/date');
|
|
12
15
|
const file = require('./core/file');
|
|
16
|
+
const lhs = require('./core/lhs');
|
|
17
|
+
const msg = require('./core/msg');
|
|
13
18
|
const number = require('./core/number');
|
|
14
19
|
const obj = require('./core/obj');
|
|
15
20
|
const random = require('./core/random');
|
|
@@ -26,6 +31,6 @@ const { log_debug, log_info, log_warn, log_error, is_log_debug, is_log_info, is_
|
|
|
26
31
|
|
|
27
32
|
module.exports = {
|
|
28
33
|
init_settings, init_express_server, init_router, register_type, get_type, get_db,
|
|
29
|
-
Entity, EntityMeta, get_entity_meta, array, date, file, number, obj, random, thread, validate, code, err, params, context, gridfs, gen_i18n,
|
|
34
|
+
Entity, EntityMeta, get_entity_meta, array, bash, chart, cron, date, file, lhs, msg, number, obj, random, thread, validate, code, err, params, context, gridfs, gen_i18n,
|
|
30
35
|
log_debug, log_info, log_warn, log_error, is_log_debug, is_log_info, is_log_warn, is_log_error, get_session_userid, oid_queries, oid_query
|
|
31
36
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hola-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "a meta programming framework used to build nodejs restful api",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"mongodb": "^4.7.0",
|
|
19
19
|
"mongoist": "^2.5.4",
|
|
20
20
|
"multer": "^1.4.5-lts.1",
|
|
21
|
-
"unzipper": "^0.10.11"
|
|
21
|
+
"unzipper": "^0.10.11",
|
|
22
|
+
"node-cron": "^3.0.2",
|
|
23
|
+
"wxmnode": "^1.0.5"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
24
26
|
"chai": "^4.3.6",
|