underpost 2.8.79 → 2.8.82

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.
Files changed (45) hide show
  1. package/.github/workflows/ghpkg.yml +23 -21
  2. package/.github/workflows/npmpkg.yml +16 -11
  3. package/.github/workflows/pwa-microservices-template.page.yml +12 -3
  4. package/.github/workflows/pwa-microservices-template.test.yml +20 -17
  5. package/.vscode/extensions.json +1 -2
  6. package/.vscode/settings.json +3 -0
  7. package/Dockerfile +14 -33
  8. package/README.md +25 -24
  9. package/bin/db.js +1 -0
  10. package/bin/deploy.js +88 -796
  11. package/bin/vs.js +10 -3
  12. package/cli.md +340 -207
  13. package/conf.js +4 -0
  14. package/docker-compose.yml +1 -1
  15. package/manifests/deployment/dd-template-development/deployment.yaml +167 -0
  16. package/manifests/deployment/dd-template-development/proxy.yaml +46 -0
  17. package/manifests/lxd/lxd-admin-profile.yaml +1 -0
  18. package/manifests/lxd/lxd-preseed.yaml +9 -37
  19. package/manifests/lxd/underpost-setup.sh +98 -81
  20. package/manifests/maas/device-scan.sh +43 -0
  21. package/manifests/maas/lxd-preseed.yaml +32 -0
  22. package/manifests/maas/maas-setup.sh +120 -0
  23. package/manifests/maas/nat-iptables.sh +26 -0
  24. package/manifests/mariadb/statefulset.yaml +2 -1
  25. package/manifests/mariadb/storage-class.yaml +10 -0
  26. package/manifests/mongodb-4.4/service-deployment.yaml +2 -2
  27. package/manifests/valkey/service.yaml +3 -9
  28. package/manifests/valkey/statefulset.yaml +10 -12
  29. package/package.json +1 -1
  30. package/src/cli/baremetal.js +1248 -0
  31. package/src/cli/cloud-init.js +528 -0
  32. package/src/cli/cluster.js +424 -240
  33. package/src/cli/deploy.js +27 -3
  34. package/src/cli/env.js +2 -2
  35. package/src/cli/image.js +57 -9
  36. package/src/cli/index.js +252 -231
  37. package/src/cli/lxd.js +314 -81
  38. package/src/index.js +33 -15
  39. package/src/runtime/lampp/Dockerfile +41 -47
  40. package/src/server/conf.js +58 -0
  41. package/src/server/logger.js +3 -3
  42. package/src/server/runtime.js +1 -6
  43. package/src/server/ssl.js +1 -12
  44. package/src/server/valkey.js +3 -3
  45. package/supervisord-openssh-server.conf +0 -5
package/src/cli/lxd.js CHANGED
@@ -3,8 +3,36 @@ import { getLocalIPv4Address } from '../server/dns.js';
3
3
  import { pbcopy, shellExec } from '../server/process.js';
4
4
  import fs from 'fs-extra';
5
5
 
6
+ /**
7
+ * @class UnderpostLxd
8
+ * @description Provides a set of static methods to interact with LXD,
9
+ * encapsulating common LXD operations for VM management and network testing.
10
+ */
6
11
  class UnderpostLxd {
7
12
  static API = {
13
+ /**
14
+ * @method callback
15
+ * @description Main entry point for LXD operations based on provided options.
16
+ * @param {object} options - Configuration options for LXD operations.
17
+ * @param {boolean} [options.init=false] - Initialize LXD.
18
+ * @param {boolean} [options.reset=false] - Reset LXD installation.
19
+ * @param {boolean} [options.dev=false] - Run in development mode (adjusts paths).
20
+ * @param {boolean} [options.install=false] - Install LXD snap.
21
+ * @param {boolean} [options.createVirtualNetwork=false] - Create default LXD bridge network (lxdbr0).
22
+ * @param {boolean} [options.createAdminProfile=false] - Create admin-profile for VMs.
23
+ * @param {boolean} [options.control=false] - Flag for control plane VM initialization.
24
+ * @param {boolean} [options.worker=false] - Flag for worker node VM initialization.
25
+ * @param {boolean} [options.k3s=false] - Flag to indicate K3s cluster type for VM initialization.
26
+ * @param {string} [options.initVm=''] - Initialize a specific VM.
27
+ * @param {string} [options.createVm=''] - Create a new VM with the given name.
28
+ * @param {string} [options.infoVm=''] - Display information about a specific VM.
29
+ * @param {string} [options.rootSize=''] - Root disk size for new VMs (e.g., '32GiB').
30
+ * @param {string} [options.joinNode=''] - Join a worker node to a control plane (format: 'workerName,controlName').
31
+ * @param {string} [options.expose=''] - Expose ports from a VM to the host (format: 'vmName:port1,port2').
32
+ * @param {string} [options.deleteExpose=''] - Delete exposed ports from a VM (format: 'vmName:port1,port2').
33
+ * @param {string} [options.test=''] - Test health, status and network connectivity for a VM.
34
+ * @param {string} [options.autoExposeK8sPorts=''] - Automatically expose common Kubernetes ports for the VM.
35
+ */
8
36
  async callback(
9
37
  options = {
10
38
  init: false,
@@ -12,22 +40,26 @@ class UnderpostLxd {
12
40
  dev: false,
13
41
  install: false,
14
42
  createVirtualNetwork: false,
43
+ createAdminProfile: false,
15
44
  control: false,
16
45
  worker: false,
17
- startVm: '',
46
+ k3s: false,
18
47
  initVm: '',
19
48
  createVm: '',
20
49
  infoVm: '',
21
50
  rootSize: '',
22
51
  joinNode: '',
23
52
  expose: '',
53
+ deleteExpose: '',
54
+ test: '',
55
+ autoExposeK8sPorts: '',
24
56
  },
25
57
  ) {
26
58
  const npmRoot = getNpmRootPath();
27
59
  const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
28
60
  if (options.reset === true) {
29
- shellExec(`sudo systemctl stop snap.lxd.daemon`);
30
- shellExec(`sudo snap remove lxd --purge`);
61
+ shellExec(`sudo systemctl stop snap.lxd.daemon || true`);
62
+ shellExec(`sudo snap remove lxd --purge || true`);
31
63
  }
32
64
  if (options.install === true) shellExec(`sudo snap install lxd`);
33
65
  if (options.init === true) {
@@ -36,56 +68,157 @@ class UnderpostLxd {
36
68
  const lxdPressedContent = fs
37
69
  .readFileSync(`${underpostRoot}/manifests/lxd/lxd-preseed.yaml`, 'utf8')
38
70
  .replaceAll(`127.0.0.1`, getLocalIPv4Address());
39
- // shellExec(`lxc profile show admin-profile`);
40
- // shellExec(`lxc network show lxdbr0`);
41
- // shellExec(`lxd init --preseed < ${underpostRoot}/manifests/lxd/lxd-preseed.yaml`);
42
71
  shellExec(`echo "${lxdPressedContent}" | lxd init --preseed`);
43
72
  shellExec(`lxc cluster list`);
44
73
  }
45
- if (options.createVm && typeof options.createVm === 'string') {
46
- // lxc launch
47
- const createVmCommand = `lxc init images:rockylinux/9/cloud ${
48
- options.createVm
49
- } --vm --target lxd-node1 -c limits.cpu=2 -c limits.memory=4GB --profile admin-profile -d root,size=${
50
- options.rootSize && typeof options.rootSize === 'string' ? options.rootSize + 'GiB' : '32GiB'
51
- }`;
52
- pbcopy(createVmCommand); // Copy the command to clipboard for user
74
+ if (options.createVirtualNetwork === true) {
75
+ shellExec(`lxc network create lxdbr0 \
76
+ ipv4.address=10.250.250.1/24 \
77
+ ipv4.nat=true \
78
+ ipv4.dhcp=true \
79
+ ipv6.address=none`);
53
80
  }
54
- if (options.startVm && typeof options.startVm === 'string') {
55
- const vmIp = UnderpostLxd.API.getNextAvailableIp();
56
- shellExec(`lxc stop ${options.startVm}`);
57
- shellExec(
58
- `lxc config set ${options.startVm} user.network-config="${UnderpostLxd.API.generateCloudInitNetworkConfig(
59
- vmIp,
60
- )}"`,
61
- );
62
- shellExec(`lxc config device override ${options.startVm} eth0`);
63
- shellExec(`lxc config device set ${options.startVm} eth0 ipv4.address ${vmIp}`);
64
- shellExec(
65
- `lxc config set ${options.startVm} user.user-data="#cloud-config
66
- runcmd:
67
- - [touch, /var/log/userdata-ok]"`,
81
+ if (options.createAdminProfile === true) {
82
+ pbcopy(`lxc profile create admin-profile`);
83
+ shellExec(`cat ${underpostRoot}/manifests/lxd/lxd-admin-profile.yaml | lxc profile edit admin-profile`);
84
+ shellExec(`lxc profile show admin-profile`);
85
+ }
86
+ if (options.createVm && typeof options.createVm === 'string') {
87
+ pbcopy(
88
+ `lxc launch images:rockylinux/9 ${
89
+ options.createVm
90
+ } --vm --target lxd-node1 -c limits.cpu=2 -c limits.memory=4GB --profile admin-profile -d root,size=${
91
+ options.rootSize && typeof options.rootSize === 'string' ? options.rootSize + 'GiB' : '32GiB'
92
+ }`,
68
93
  );
69
- shellExec(`lxc start ${options.startVm}`);
70
94
  }
71
95
  if (options.initVm && typeof options.initVm === 'string') {
72
96
  let flag = '';
73
97
  if (options.control === true) {
74
- flag = ' -s -- --kubeadm';
98
+ if (options.k3s === true) {
99
+ flag = ' -s -- --k3s';
100
+ } else {
101
+ // Default to kubeadm if not K3s
102
+ flag = ' -s -- --kubeadm';
103
+ }
75
104
  shellExec(`lxc exec ${options.initVm} -- bash -c 'mkdir -p /home/dd/engine'`);
76
105
  shellExec(`lxc file push /home/dd/engine/engine-private ${options.initVm}/home/dd/engine --recursive`);
106
+ shellExec(`lxc file push /home/dd/engine/manifests ${options.initVm}/home/dd/engine --recursive`);
77
107
  } else if (options.worker == true) {
78
- flag = ' -s -- --worker';
108
+ if (options.k3s === true) {
109
+ flag = ' -s -- --worker --k3s';
110
+ } else {
111
+ // Default to kubeadm worker
112
+ flag = ' -s -- --worker';
113
+ }
114
+ }
115
+ console.log(`Executing underpost-setup.sh on VM: ${options.initVm}`);
116
+ shellExec(`cat ${underpostRoot}/manifests/lxd/underpost-setup.sh | lxc exec ${options.initVm} -- bash${flag}`);
117
+ console.log(`underpost-setup.sh execution completed on VM: ${options.initVm}`);
118
+ }
119
+ // --- Automatic Kubernetes Port Exposure ---
120
+ if (options.autoExposeK8sPorts && typeof options.autoExposeK8sPorts === 'string') {
121
+ console.log(`Automatically exposing Kubernetes ports for VM: ${options.autoExposeK8sPorts}`);
122
+ const vmName = options.autoExposeK8sPorts;
123
+ const hostIp = getLocalIPv4Address();
124
+ let vmIp = '';
125
+ let retries = 0;
126
+ const maxRetries = 10;
127
+ const delayMs = 5000; // 5 seconds
128
+
129
+ // Wait for VM to get an IP address
130
+ while (!vmIp && retries < maxRetries) {
131
+ try {
132
+ console.log(`Attempting to get IPv4 address for ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
133
+ vmIp = shellExec(
134
+ `lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
135
+ { stdout: true },
136
+ ).trim();
137
+ if (vmIp) {
138
+ console.log(`IPv4 address found for ${vmName}: ${vmIp}`);
139
+ } else {
140
+ console.log(`IPv4 address not yet available for ${vmName}. Retrying in ${delayMs / 1000} seconds...`);
141
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
142
+ }
143
+ } catch (error) {
144
+ console.error(`Error getting IPv4 address for exposure: ${error.message}`);
145
+ console.log(`Retrying in ${delayMs / 1000} seconds...`);
146
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
147
+ }
148
+ retries++;
149
+ }
150
+
151
+ if (!vmIp) {
152
+ console.error(`Failed to get VM IP for ${vmName} after ${maxRetries} attempts. Cannot expose ports.`);
153
+ return;
154
+ }
155
+
156
+ let portsToExpose = [];
157
+ if (options.control === true) {
158
+ // Kubernetes API Server (Kubeadm and K3s both use 6443 by default)
159
+ portsToExpose.push('6443');
160
+ // Standard HTTP/HTTPS for Ingress if deployed
161
+ portsToExpose.push('80');
162
+ portsToExpose.push('443');
163
+ }
164
+ // Add common NodePorts if needed, or rely on explicit 'expose'
165
+ portsToExpose.push('30000'); // Example NodePort
166
+ portsToExpose.push('30001'); // Example NodePort
167
+ portsToExpose.push('30002'); // Example NodePort
168
+
169
+ const protocols = ['tcp']; // Most K8s services are TCP, UDP for some like DNS
170
+
171
+ for (const port of portsToExpose) {
172
+ for (const protocol of protocols) {
173
+ const deviceName = `${vmName}-${protocol}-port-${port}`;
174
+ try {
175
+ // Remove existing device first to avoid conflicts if re-running
176
+ shellExec(`lxc config device remove ${vmName} ${deviceName} || true`);
177
+ shellExec(
178
+ `lxc config device add ${vmName} ${deviceName} proxy listen=${protocol}:${hostIp}:${port} connect=${protocol}:${vmIp}:${port} nat=true`,
179
+ );
180
+ console.log(`Exposed ${protocol}:${hostIp}:${port} -> ${vmIp}:${port} for ${vmName}`);
181
+ } catch (error) {
182
+ console.error(`Failed to expose port ${port} for ${vmName}: ${error.message}`);
183
+ }
184
+ }
79
185
  }
80
- pbcopy(`cat ${underpostRoot}/manifests/lxd/underpost-setup.sh | lxc exec ${options.initVm} -- bash${flag}`);
81
186
  }
82
187
  if (options.joinNode && typeof options.joinNode === 'string') {
83
188
  const [workerNode, controlNode] = options.joinNode.split(',');
84
- const token = shellExec(
85
- `echo "$(lxc exec ${controlNode} -- bash -c 'sudo kubeadm token create --print-join-command')"`,
86
- { stdout: true },
87
- );
88
- shellExec(`lxc exec ${workerNode} -- bash -c '${token}'`);
189
+ // Determine if it's a Kubeadm or K3s join
190
+ const isK3sJoin = options.k3s === true;
191
+
192
+ if (isK3sJoin) {
193
+ console.log(`Attempting to join K3s worker node ${workerNode} to control plane ${controlNode}`);
194
+ // Get K3s token from control plane
195
+ const k3sToken = shellExec(
196
+ `lxc exec ${controlNode} -- bash -c 'sudo cat /var/lib/rancher/k3s/server/node-token'`,
197
+ { stdout: true },
198
+ ).trim();
199
+ // Get control plane IP
200
+ const controlPlaneIp = shellExec(
201
+ `lxc list ${controlNode} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
202
+ { stdout: true },
203
+ ).trim();
204
+
205
+ if (!k3sToken || !controlPlaneIp) {
206
+ console.error(`Failed to get K3s token or control plane IP. Cannot join worker.`);
207
+ return;
208
+ }
209
+ const k3sJoinCommand = `K3S_URL=https://${controlPlaneIp}:6443 K3S_TOKEN=${k3sToken} curl -sfL https://get.k3s.io | sh -`;
210
+ shellExec(`lxc exec ${workerNode} -- bash -c '${k3sJoinCommand}'`);
211
+ console.log(`K3s worker node ${workerNode} join command executed.`);
212
+ } else {
213
+ // Kubeadm join
214
+ console.log(`Attempting to join Kubeadm worker node ${workerNode} to control plane ${controlNode}`);
215
+ const token = shellExec(
216
+ `echo "$(lxc exec ${controlNode} -- bash -c 'sudo kubeadm token create --print-join-command')"`,
217
+ { stdout: true },
218
+ );
219
+ shellExec(`lxc exec ${workerNode} -- bash -c '${token}'`);
220
+ console.log(`Kubeadm worker node ${workerNode} join command executed.`);
221
+ }
89
222
  }
90
223
  if (options.infoVm && typeof options.infoVm === 'string') {
91
224
  shellExec(`lxc config show ${options.infoVm}`);
@@ -94,67 +227,167 @@ runcmd:
94
227
  shellExec(`lxc list ${options.infoVm}`);
95
228
  }
96
229
  if (options.expose && typeof options.expose === 'string') {
97
- const [controlNode, ports] = options.expose.split(':');
98
- console.log({ controlNode, ports });
99
- const protocols = ['tcp', 'udp'];
230
+ const [vmName, ports] = options.expose.split(':');
231
+ console.log({ vmName, ports });
232
+ const protocols = ['tcp']; // udp
100
233
  const hostIp = getLocalIPv4Address();
101
234
  const vmIp = shellExec(
102
- `lxc list ${controlNode} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
235
+ `lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
103
236
  { stdout: true },
104
237
  ).trim();
238
+ if (!vmIp) {
239
+ console.error(`Could not get VM IP for ${vmName}. Cannot expose ports.`);
240
+ return;
241
+ }
105
242
  for (const port of ports.split(',')) {
106
243
  for (const protocol of protocols) {
107
- shellExec(`lxc config device remove ${controlNode} ${controlNode}-port-${port}`);
244
+ const deviceName = `${vmName}-${protocol}-port-${port}`;
245
+ shellExec(`lxc config device remove ${vmName} ${deviceName} || true`); // Use || true to prevent error if device doesn't exist
108
246
  shellExec(
109
- `lxc config device add ${controlNode} ${controlNode}-port-${port} proxy listen=${protocol}:${hostIp}:${port} connect=${protocol}:${vmIp}:${port} nat=true`,
247
+ `lxc config device add ${vmName} ${deviceName} proxy listen=${protocol}:${hostIp}:${port} connect=${protocol}:${vmIp}:${port} nat=true`,
110
248
  );
111
- shellExec(`lxc config show ${controlNode} --expanded | grep proxy`);
249
+ console.log(`Manually exposed ${protocol}:${hostIp}:${port} -> ${vmIp}:${port} for ${vmName}`);
112
250
  }
113
251
  }
114
252
  }
115
- },
116
- generateCloudInitNetworkConfig(ip) {
117
- return `version: 2
118
- ethernets:
119
- enp5s0:
120
- dhcp4: false
121
- addresses:
122
- - ${ip}/24
123
- gateway4: 10.250.250.1
124
- nameservers:
125
- addresses: [1.1.1.1, 8.8.8.8]`;
126
- },
127
- getUsedIpsFromLxd() {
128
- const json = shellExec('lxc list --format json', { stdout: true, silent: true });
129
- const vms = JSON.parse(json);
130
-
131
- const usedIps = [];
132
-
133
- for (const vm of vms) {
134
- if (vm.state && vm.state.network) {
135
- for (const iface of Object.values(vm.state.network)) {
136
- if (iface.addresses) {
137
- for (const addr of iface.addresses) {
138
- if (addr.family === 'inet' && addr.address.startsWith('10.250.250.')) {
139
- usedIps.push(addr.address);
140
- }
141
- }
142
- }
253
+ if (options.deleteExpose && typeof options.deleteExpose === 'string') {
254
+ const [controlNode, ports] = options.deleteExpose.split(':');
255
+ console.log({ controlNode, ports });
256
+ const protocols = ['tcp']; // udp
257
+ for (const port of ports.split(',')) {
258
+ for (const protocol of protocols) {
259
+ shellExec(`lxc config device remove ${controlNode} ${controlNode}-${protocol}-port-${port}`);
143
260
  }
144
261
  }
145
262
  }
146
263
 
147
- return usedIps;
148
- },
149
- getNextAvailableIp(base = '10.250.250.', start = 100, end = 254) {
150
- const usedIps = UnderpostLxd.API.getUsedIpsFromLxd();
151
- for (let i = start; i <= end; i++) {
152
- const candidate = `${base}${i}`;
153
- if (!usedIps.includes(candidate)) {
154
- return candidate;
264
+ if (options.test && typeof options.test === 'string') {
265
+ const vmName = options.test;
266
+ console.log(`Starting comprehensive test for VM: ${vmName}`);
267
+
268
+ // 1. Monitor for IPv4 address
269
+ let vmIp = '';
270
+ let retries = 0;
271
+ const maxRetries = 10;
272
+ const delayMs = 5000; // 5 seconds
273
+
274
+ while (!vmIp && retries < maxRetries) {
275
+ try {
276
+ console.log(`Attempting to get IPv4 address for ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
277
+ vmIp = shellExec(
278
+ `lxc list ${vmName} --format json | jq -r '.[0].state.network.enp5s0.addresses[] | select(.family=="inet") | .address'`,
279
+ { stdout: true },
280
+ ).trim();
281
+ if (vmIp) {
282
+ console.log(`IPv4 address found for ${vmName}: ${vmIp}`);
283
+ } else {
284
+ console.log(`IPv4 address not yet available for ${vmName}. Retrying in ${delayMs / 1000} seconds...`);
285
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
286
+ }
287
+ } catch (error) {
288
+ console.error(`Error getting IPv4 address: ${error.message}`);
289
+ console.log(`Retrying in ${delayMs / 1000} seconds...`);
290
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
291
+ }
292
+ retries++;
293
+ }
294
+
295
+ if (!vmIp) {
296
+ console.error(`Failed to get IPv4 address for ${vmName} after ${maxRetries} attempts. Aborting tests.`);
297
+ return;
298
+ }
299
+
300
+ // 2. Iteratively check connection to google.com
301
+ let connectedToGoogle = false;
302
+ retries = 0;
303
+ while (!connectedToGoogle && retries < maxRetries) {
304
+ try {
305
+ console.log(`Checking connectivity to google.com from ${vmName} (Attempt ${retries + 1}/${maxRetries})...`);
306
+ const curlOutput = shellExec(
307
+ `lxc exec ${vmName} -- bash -c 'curl -s -o /dev/null -w "%{http_code}" http://google.com'`,
308
+ { stdout: true },
309
+ );
310
+ if (curlOutput.startsWith('2') || curlOutput.startsWith('3')) {
311
+ console.log(`Successfully connected to google.com from ${vmName}.`);
312
+ connectedToGoogle = true;
313
+ } else {
314
+ console.log(`Connectivity to google.com not yet verified. Retrying in ${delayMs / 1000} seconds...`);
315
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
316
+ }
317
+ } catch (error) {
318
+ console.error(`Error checking connectivity to google.com: ${error.message}`);
319
+ console.log(`Retrying in ${delayMs / 1000} seconds...`);
320
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
321
+ }
322
+ retries++;
155
323
  }
324
+
325
+ if (!connectedToGoogle) {
326
+ console.error(
327
+ `Failed to connect to google.com from ${vmName} after ${maxRetries} attempts. Aborting further tests.`,
328
+ );
329
+ return;
330
+ }
331
+
332
+ // 3. Check other connectivity, network, and VM health parameters
333
+ console.log(`\n--- Comprehensive Health Report for ${vmName} ---`);
334
+
335
+ // VM Status
336
+ console.log('\n--- VM Status ---');
337
+ try {
338
+ const vmStatus = shellExec(`lxc list ${vmName} --format json`, { stdout: true, silent: true });
339
+ console.log(JSON.stringify(JSON.parse(vmStatus), null, 2));
340
+ } catch (error) {
341
+ console.error(`Error getting VM status: ${error.message}`);
342
+ }
343
+
344
+ // CPU Usage
345
+ console.log('\n--- CPU Usage ---');
346
+ try {
347
+ const cpuUsage = shellExec(`lxc exec ${vmName} -- bash -c 'top -bn1 | grep "Cpu(s)"'`, { stdout: true });
348
+ console.log(cpuUsage.trim());
349
+ } catch (error) {
350
+ console.error(`Error getting CPU usage: ${error.message}`);
351
+ }
352
+
353
+ // Memory Usage
354
+ console.log('\n--- Memory Usage ---');
355
+ try {
356
+ const memoryUsage = shellExec(`lxc exec ${vmName} -- bash -c 'free -m'`, { stdout: true });
357
+ console.log(memoryUsage.trim());
358
+ } catch (error) {
359
+ console.error(`Error getting memory usage: ${error.message}`);
360
+ }
361
+
362
+ // Disk Usage
363
+ console.log('\n--- Disk Usage (Root Partition) ---');
364
+ try {
365
+ const diskUsage = shellExec(`lxc exec ${vmName} -- bash -c 'df -h /'`, { stdout: true });
366
+ console.log(diskUsage.trim());
367
+ } catch (error) {
368
+ console.error(`Error getting disk usage: ${error.message}`);
369
+ }
370
+
371
+ // Network Interface Status
372
+ console.log('\n--- Network Interface Status (ip a) ---');
373
+ try {
374
+ const ipA = shellExec(`lxc exec ${vmName} -- bash -c 'ip a'`, { stdout: true });
375
+ console.log(ipA.trim());
376
+ } catch (error) {
377
+ console.error(`Error getting network interface status: ${error.message}`);
378
+ }
379
+
380
+ // DNS Resolution (resolv.conf)
381
+ console.log('\n--- DNS Configuration (/etc/resolv.conf) ---');
382
+ try {
383
+ const resolvConf = shellExec(`lxc exec ${vmName} -- bash -c 'cat /etc/resolv.conf'`, { stdout: true });
384
+ console.log(resolvConf.trim());
385
+ } catch (error) {
386
+ console.error(`Error getting DNS configuration: ${error.message}`);
387
+ }
388
+
389
+ console.log(`\nComprehensive test for VM: ${vmName} completed.`);
156
390
  }
157
- throw new Error('No IPs available in the static range');
158
391
  },
159
392
  };
160
393
  }
package/src/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
  * @namespace Underpost
5
5
  */
6
6
 
7
+ import UnderpostBaremetal from './cli/baremetal.js';
8
+ import UnderpostCloudInit from './cli/cloud-init.js';
7
9
  import UnderpostCluster from './cli/cluster.js';
8
10
  import UnderpostCron from './cli/cron.js';
9
11
  import UnderpostDB from './cli/db.js';
@@ -31,105 +33,121 @@ class Underpost {
31
33
  * @type {String}
32
34
  * @memberof Underpost
33
35
  */
34
- static version = 'v2.8.79';
36
+ static version = 'v2.8.82';
35
37
  /**
36
38
  * Repository cli API
37
39
  * @static
38
- * @type {UnderpostRepository.API}
40
+ * @type {UnderpostRepository}
39
41
  * @memberof Underpost
40
42
  */
41
43
  static repo = UnderpostRepository.API;
42
44
  /**
43
45
  * Root Env cli API
44
46
  * @static
45
- * @type {UnderpostRootEnv.API}
47
+ * @type {UnderpostRootEnv}
46
48
  * @memberof Underpost
47
49
  */
48
50
  static env = UnderpostRootEnv.API;
49
51
  /**
50
52
  * Test cli API
51
53
  * @static
52
- * @type {UnderpostTest.API}
54
+ * @type {UnderpostTest}
53
55
  * @memberof Underpost
54
56
  */
55
57
  static test = UnderpostTest.API;
56
58
  /**
57
59
  * Underpost Start Up cli API
58
60
  * @static
59
- * @type {UnderpostStartUp.API}
61
+ * @type {UnderpostStartUp}
60
62
  * @memberof Underpost
61
63
  */
62
64
  static start = UnderpostStartUp.API;
63
65
  /**
64
66
  * Cluster cli API
65
67
  * @static
66
- * @type {UnderpostCluster.API}
68
+ * @type {UnderpostCluster}
67
69
  * @memberof Underpost
68
70
  */
69
71
  static cluster = UnderpostCluster.API;
70
72
  /**
71
73
  * Image cli API
72
74
  * @static
73
- * @type {UnderpostImage.API}
75
+ * @type {UnderpostImage}
74
76
  * @memberof Underpost
75
77
  */
76
78
  static image = UnderpostImage.API;
77
79
  /**
78
80
  * Secrets cli API
79
81
  * @static
80
- * @type {UnderpostSecret.API}
82
+ * @type {UnderpostSecret}
81
83
  * @memberof Underpost
82
84
  */
83
85
  static secret = UnderpostSecret.API;
84
86
  /**
85
87
  * Scripts cli API
86
88
  * @static
87
- * @type {UnderpostScript.API}
89
+ * @type {UnderpostScript}
88
90
  * @memberof Underpost
89
91
  */
90
92
  static script = UnderpostScript.API;
91
93
  /**
92
94
  * Database cli API
93
95
  * @static
94
- * @type {UnderpostDB.API}
96
+ * @type {UnderpostDB}
95
97
  * @memberof Underpost
96
98
  */
97
99
  static db = UnderpostDB.API;
98
100
  /**
99
101
  * Deployment cli API
100
102
  * @static
101
- * @type {UnderpostDeploy.API}
103
+ * @type {UnderpostDeploy}
102
104
  * @memberof Underpost
103
105
  */
104
106
  static deploy = UnderpostDeploy.API;
105
107
  /**
106
108
  * Cron cli API
107
109
  * @static
108
- * @type {UnderpostCron.API}
110
+ * @type {UnderpostCron}
109
111
  * @memberof Underpost
110
112
  */
111
113
  static cron = UnderpostCron.API;
112
114
  /**
113
115
  * File Storage cli API
114
116
  * @static
115
- * @type {UnderpostFileStorage.API}
117
+ * @type {UnderpostFileStorage}
116
118
  * @memberof Underpost
117
119
  */
118
120
  static fs = UnderpostFileStorage.API;
119
121
  /**
120
122
  * Monitor cli API
121
123
  * @static
122
- * @type {UnderpostMonitor.API}
124
+ * @type {UnderpostMonitor}
123
125
  * @memberof Underpost
124
126
  */
125
127
  static monitor = UnderpostMonitor.API;
126
128
  /**
127
129
  * LXD cli API
128
130
  * @static
129
- * @type {UnderpostLxd.API}
131
+ * @type {UnderpostLxd}
130
132
  * @memberof Underpost
131
133
  */
132
134
  static lxd = UnderpostLxd.API;
135
+
136
+ /**
137
+ * Cloud Init cli API
138
+ * @static
139
+ * @type {UnderpostCloudInit}
140
+ * @memberof Underpost
141
+ */
142
+ static cloudInit = UnderpostCloudInit.API;
143
+
144
+ /**
145
+ * Baremetal cli API
146
+ * @static
147
+ * @type {UnderpostBaremetal}
148
+ * @memberof Underpost
149
+ */
150
+ static baremetal = UnderpostBaremetal.API;
133
151
  }
134
152
 
135
153
  const up = Underpost;