underpost 2.8.82 → 2.8.85

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 (115) hide show
  1. package/.env.development +1 -0
  2. package/.env.production +1 -0
  3. package/.env.test +1 -0
  4. package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +5 -5
  5. package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +5 -5
  6. package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
  7. package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +1 -1
  8. package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
  9. package/.vscode/extensions.json +1 -1
  10. package/.vscode/settings.json +0 -44
  11. package/README.md +62 -2
  12. package/bin/build.js +15 -5
  13. package/bin/deploy.js +42 -92
  14. package/bin/file.js +33 -9
  15. package/bin/vs.js +12 -4
  16. package/cli.md +90 -42
  17. package/conf.js +1 -1
  18. package/docker-compose.yml +1 -1
  19. package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
  20. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +65 -0
  21. package/manifests/maas/device-scan.sh +3 -3
  22. package/manifests/maas/gpu-diag.sh +19 -0
  23. package/manifests/maas/maas-setup.sh +10 -10
  24. package/manifests/maas/snap-clean.sh +26 -0
  25. package/package.json +4 -6
  26. package/src/api/user/user.router.js +24 -1
  27. package/src/api/user/user.service.js +1 -4
  28. package/src/cli/baremetal.js +105 -73
  29. package/src/cli/cloud-init.js +21 -12
  30. package/src/cli/cluster.js +227 -133
  31. package/src/cli/deploy.js +34 -0
  32. package/src/cli/index.js +28 -1
  33. package/src/cli/monitor.js +8 -12
  34. package/src/cli/repository.js +7 -4
  35. package/src/cli/run.js +367 -0
  36. package/src/cli/ssh.js +32 -0
  37. package/src/cli/test.js +1 -1
  38. package/src/client/Default.index.js +7 -3
  39. package/src/client/components/core/Account.js +1 -1
  40. package/src/client/components/core/Chat.js +1 -1
  41. package/src/client/components/core/CommonJs.js +24 -22
  42. package/src/client/components/core/Content.js +1 -5
  43. package/src/client/components/core/Css.js +258 -18
  44. package/src/client/components/core/CssCore.js +8 -8
  45. package/src/client/components/core/Docs.js +14 -61
  46. package/src/client/components/core/DropDown.js +137 -82
  47. package/src/client/components/core/EventsUI.js +92 -5
  48. package/src/client/components/core/LoadingAnimation.js +8 -15
  49. package/src/client/components/core/Modal.js +597 -136
  50. package/src/client/components/core/NotificationManager.js +2 -2
  51. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  52. package/src/client/components/core/Panel.js +158 -34
  53. package/src/client/components/core/PanelForm.js +12 -3
  54. package/src/client/components/core/Recover.js +1 -1
  55. package/src/client/components/core/Router.js +77 -17
  56. package/src/client/components/core/SocketIo.js +3 -3
  57. package/src/client/components/core/Translate.js +6 -2
  58. package/src/client/components/core/VanillaJs.js +0 -3
  59. package/src/client/components/core/Worker.js +3 -1
  60. package/src/client/components/default/CssDefault.js +17 -3
  61. package/src/client/components/default/MenuDefault.js +264 -45
  62. package/src/client/components/default/RoutesDefault.js +6 -12
  63. package/src/client/public/default/android-chrome-144x144.png +0 -0
  64. package/src/client/public/default/android-chrome-192x192.png +0 -0
  65. package/src/client/public/default/android-chrome-256x256.png +0 -0
  66. package/src/client/public/default/android-chrome-36x36.png +0 -0
  67. package/src/client/public/default/android-chrome-48x48.png +0 -0
  68. package/src/client/public/default/android-chrome-72x72.png +0 -0
  69. package/src/client/public/default/android-chrome-96x96.png +0 -0
  70. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  71. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  72. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  73. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  74. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  75. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  76. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  77. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  78. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  79. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  80. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  81. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  82. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  83. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  84. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  85. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  86. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  87. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  88. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  89. package/src/client/public/default/apple-touch-icon.png +0 -0
  90. package/src/client/public/default/assets/background/dark.jpg +0 -0
  91. package/src/client/public/default/assets/background/dark.svg +557 -0
  92. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  93. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  94. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  95. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  96. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  97. package/src/client/public/default/favicon-16x16.png +0 -0
  98. package/src/client/public/default/favicon-32x32.png +0 -0
  99. package/src/client/public/default/favicon.ico +0 -0
  100. package/src/client/public/default/mstile-144x144.png +0 -0
  101. package/src/client/public/default/mstile-150x150.png +0 -0
  102. package/src/client/public/default/mstile-310x150.png +0 -0
  103. package/src/client/public/default/mstile-310x310.png +0 -0
  104. package/src/client/public/default/mstile-70x70.png +0 -0
  105. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  106. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  107. package/src/index.js +34 -17
  108. package/src/monitor.js +24 -0
  109. package/src/runtime/lampp/Dockerfile +30 -39
  110. package/src/runtime/lampp/Lampp.js +11 -2
  111. package/src/server/client-build-docs.js +205 -0
  112. package/src/server/client-build.js +16 -166
  113. package/src/server/conf.js +18 -8
  114. package/src/server/process.js +16 -19
  115. package/src/server/valkey.js +102 -41
@@ -1,5 +1,5 @@
1
1
  import { getNpmRootPath, getUnderpostRootPath } from '../server/conf.js';
2
- import { pbcopy, shellExec } from '../server/process.js';
2
+ import { openTerminal, pbcopy, shellExec } from '../server/process.js';
3
3
  import dotenv from 'dotenv';
4
4
  import { loggerFactory } from '../server/logger.js';
5
5
  import { getLocalIPv4Address } from '../server/dns.js';
@@ -75,6 +75,9 @@ class UnderpostBaremetal {
75
75
  // Set default MAC address
76
76
  let macAddress = '00:00:00:00:00:00';
77
77
 
78
+ // Define the debootstrap architecture.
79
+ let debootstrapArch;
80
+
78
81
  // Define the database provider ID.
79
82
  const dbProviderId = 'postgresql-17';
80
83
 
@@ -88,7 +91,7 @@ class UnderpostBaremetal {
88
91
  const callbackMetaData = {
89
92
  args: { hostname, ipAddress, workflowId },
90
93
  options,
91
- runnerHost: { architecture: UnderpostBaremetal.API.getHostArch(), ip: getLocalIPv4Address() },
94
+ runnerHost: { architecture: UnderpostBaremetal.API.getHostArch().alias, ip: getLocalIPv4Address() },
92
95
  nfsHostPath,
93
96
  tftpRootPath,
94
97
  };
@@ -119,8 +122,23 @@ class UnderpostBaremetal {
119
122
 
120
123
  // Handle NFS shell access option.
121
124
  if (options.nfsSh === true) {
125
+ const { debootstrap } = UnderpostBaremetal.API.workflowsConfig[workflowId];
122
126
  // Copy the chroot command to the clipboard for easy execution.
123
- pbcopy(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash`);
127
+ if (debootstrap.image.architecture !== callbackMetaData.runnerHost.architecture)
128
+ switch (debootstrap.image.architecture) {
129
+ case 'arm64':
130
+ pbcopy(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash`);
131
+ break;
132
+
133
+ case 'amd64':
134
+ pbcopy(`sudo chroot ${nfsHostPath} /usr/bin/qemu-x86_64-static /bin/bash`);
135
+ break;
136
+
137
+ default:
138
+ break;
139
+ }
140
+ else pbcopy(`sudo chroot ${nfsHostPath} /bin/bash`);
141
+
124
142
  return; // Exit early as this is a specific interactive operation.
125
143
  }
126
144
 
@@ -131,6 +149,7 @@ class UnderpostBaremetal {
131
149
  shellExec(`chmod +x ${underpostRoot}/manifests/maas/nat-iptables.sh`);
132
150
  shellExec(`${underpostRoot}/manifests/maas/maas-setup.sh`);
133
151
  shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`);
152
+ return;
134
153
  }
135
154
 
136
155
  // Handle control server uninstallation.
@@ -148,6 +167,7 @@ class UnderpostBaremetal {
148
167
  shellExec(`sudo rm -rf /etc/maas`);
149
168
  shellExec(`sudo rm -rf /var/lib/maas`);
150
169
  shellExec(`sudo rm -rf /var/log/maas`);
170
+ return;
151
171
  }
152
172
 
153
173
  // Handle control server database installation.
@@ -157,16 +177,26 @@ class UnderpostBaremetal {
157
177
  shellExec(
158
178
  `node ${underpostRoot}/bin/deploy pg-drop-db ${process.env.DB_PG_MAAS_NAME} ${process.env.DB_PG_MAAS_USER}`,
159
179
  );
160
- shellExec(`node ${underpostRoot}/bin/deploy maas db`);
180
+ shellExec(`node ${underpostRoot}/bin/deploy maas-db`);
181
+ return;
161
182
  }
162
183
 
163
184
  // Handle control server database uninstallation.
164
185
  if (options.controlServerDbUninstall === true) {
165
186
  shellExec(`node ${underpostRoot}/bin/deploy ${dbProviderId} uninstall`);
187
+ return;
188
+ }
189
+
190
+ // Set debootstrap architecture.
191
+ {
192
+ const { architecture } = UnderpostBaremetal.API.workflowsConfig[workflowId].debootstrap.image;
193
+ debootstrapArch = architecture;
166
194
  }
167
195
 
168
196
  // Handle NFS mount operation.
169
197
  if (options.nfsMount === true) {
198
+ // Mount binfmt_misc filesystem.
199
+ UnderpostBaremetal.API.mountBinfmtMisc({ nfsHostPath });
170
200
  UnderpostBaremetal.API.nfsMountCallback({ hostname, workflowId, mount: true });
171
201
  }
172
202
 
@@ -185,27 +215,16 @@ class UnderpostBaremetal {
185
215
  }
186
216
  logger.info('NFS root filesystem is not mounted, building...');
187
217
 
188
- // Install necessary packages for debootstrap and QEMU.
189
- shellExec(`sudo dnf install -y iptables-legacy`);
190
- shellExec(`sudo dnf install -y debootstrap`);
191
- shellExec(`sudo dnf install kernel-modules-extra-$(uname -r)`);
192
- // Reset QEMU user-static binfmt for proper cross-architecture execution.
193
- shellExec(`sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes`);
194
- shellExec(`sudo modprobe binfmt_misc`);
195
- shellExec(`sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc`);
196
-
197
218
  // Clean and create the NFS host path.
198
219
  shellExec(`sudo rm -rf ${nfsHostPath}/*`);
199
220
  shellExec(`mkdir -p ${nfsHostPath}`);
200
- shellExec(`sudo chown -R root:root ${nfsHostPath}`);
201
- shellExec(`sudo chmod 755 ${nfsHostPath}`);
202
221
 
203
- let debootstrapArch;
222
+ // Mount binfmt_misc filesystem.
223
+ UnderpostBaremetal.API.mountBinfmtMisc({ nfsHostPath });
204
224
 
205
225
  // Perform the first stage of debootstrap.
206
226
  {
207
227
  const { architecture, name } = UnderpostBaremetal.API.workflowsConfig[workflowId].debootstrap.image;
208
- debootstrapArch = architecture;
209
228
  shellExec(
210
229
  [
211
230
  `sudo debootstrap`,
@@ -272,44 +291,43 @@ class UnderpostBaremetal {
272
291
  }
273
292
  }
274
293
 
275
- let resources, resource, machines;
276
-
277
294
  // Fetch boot resources and machines if commissioning or listing.
278
- if (options.commission === true || options.ls === true) {
279
- resources = JSON.parse(
280
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resources read`, {
281
- silent: true,
282
- stdout: true,
283
- }),
284
- ).map((o) => ({
285
- id: o.id,
286
- name: o.name,
287
- architecture: o.architecture,
288
- }));
289
- if (options.ls === true) {
290
- console.table(resources);
291
- }
292
- machines = JSON.parse(
293
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machines read`, {
294
- stdout: true,
295
- silent: true,
296
- }),
297
- ).map((m) => ({
298
- system_id: m.interface_set[0].system_id,
299
- mac_address: m.interface_set[0].mac_address,
300
- hostname: m.hostname,
301
- status_name: m.status_name,
302
- }));
303
- if (options.ls === true) {
304
- console.table(machines);
305
- }
295
+
296
+ let resources = JSON.parse(
297
+ shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resources read`, {
298
+ silent: true,
299
+ stdout: true,
300
+ }),
301
+ ).map((o) => ({
302
+ id: o.id,
303
+ name: o.name,
304
+ architecture: o.architecture,
305
+ }));
306
+ if (options.ls === true) {
307
+ console.table(resources);
308
+ }
309
+ let machines = JSON.parse(
310
+ shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machines read`, {
311
+ stdout: true,
312
+ silent: true,
313
+ }),
314
+ ).map((m) => ({
315
+ system_id: m.interface_set[0].system_id,
316
+ mac_address: m.interface_set[0].mac_address,
317
+ hostname: m.hostname,
318
+ status_name: m.status_name,
319
+ }));
320
+ if (options.ls === true) {
321
+ console.table(machines);
306
322
  }
307
323
 
308
324
  // Handle commissioning tasks (placeholder for future implementation).
309
325
  if (options.commission === true) {
310
326
  const { firmwares, networkInterfaceName, maas, netmask, menuentryStr } =
311
327
  UnderpostBaremetal.API.workflowsConfig[workflowId];
312
- resource = resources.find((o) => o.architecture === maas.image.architecture && o.name === maas.image.name);
328
+ const resource = resources.find(
329
+ (o) => o.architecture === maas.image.architecture && o.name === maas.image.name,
330
+ );
313
331
  logger.info('Commissioning resource', resource);
314
332
 
315
333
  // Clean and create TFTP root path.
@@ -466,8 +484,12 @@ menuentry '${menuentryStr}' {
466
484
  shellExec(`sudo sudo chmod 755 ${process.env.TFTP_ROOT}`);
467
485
  }
468
486
 
469
- // Build cloud-init tools if commissioning or updating cloud-init.
487
+ // Final commissioning steps.
470
488
  if (options.commission === true || options.cloudInitUpdate === true) {
489
+ const { debootstrap, networkInterfaceName, chronyc, maas } = UnderpostBaremetal.API.workflowsConfig[workflowId];
490
+ const { timezone, chronyConfPath } = chronyc;
491
+
492
+ // Build cloud-init tools.
471
493
  UnderpostCloudInit.API.buildTools({
472
494
  workflowId,
473
495
  nfsHostPath,
@@ -475,15 +497,6 @@ menuentry '${menuentryStr}' {
475
497
  callbackMetaData,
476
498
  dev: options.dev,
477
499
  });
478
- }
479
-
480
- // Final commissioning steps.
481
- if (options.commission === true) {
482
- const { debootstrap, networkInterfaceName, chronyc, maas } = UnderpostBaremetal.API.workflowsConfig[workflowId];
483
- const { timezone, chronyConfPath } = chronyc;
484
-
485
- // Remove existing machines from MAAS.
486
- machines = UnderpostBaremetal.API.removeMachines({ machines });
487
500
 
488
501
  // Run cloud-init reset and configure cloud-init.
489
502
  UnderpostBaremetal.API.crossArchRunner({
@@ -491,7 +504,8 @@ menuentry '${menuentryStr}' {
491
504
  debootstrapArch: debootstrap.image.architecture,
492
505
  callbackMetaData,
493
506
  steps: [
494
- `/underpost/reset.sh`,
507
+ options.cloudInitUpdate === true ? '' : `/underpost/reset.sh`,
508
+ `chown root:root /usr/bin/sudo && chmod 4755 /usr/bin/sudo`,
495
509
  UnderpostCloudInit.API.configFactory({
496
510
  controlServerIp: callbackMetaData.runnerHost.ip,
497
511
  hostname,
@@ -505,6 +519,8 @@ menuentry '${menuentryStr}' {
505
519
  ],
506
520
  });
507
521
 
522
+ if (options.cloudInitUpdate === true) return;
523
+
508
524
  // Apply NAT iptables rules.
509
525
  shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
510
526
 
@@ -533,6 +549,9 @@ menuentry '${menuentryStr}' {
533
549
  ],
534
550
  });
535
551
 
552
+ // Remove existing machines from MAAS.
553
+ machines = UnderpostBaremetal.API.removeMachines({ machines });
554
+
536
555
  // Monitor commissioning process.
537
556
  UnderpostBaremetal.API.commissionMonitor({
538
557
  macAddress,
@@ -680,18 +699,8 @@ menuentry '${menuentryStr}' {
680
699
  fs.writeFileSync(`${nfsHostPath}/underpost/token-secret`, token_secret, 'utf8');
681
700
 
682
701
  // Open new terminals for live cloud-init logs.
683
- shellExec(
684
- `gnome-terminal -- bash -c "node ${underpostRoot}/bin baremetal --logs cloud; exec bash" & disown`,
685
- {
686
- async: true,
687
- },
688
- );
689
- shellExec(
690
- `gnome-terminal -- bash -c "node ${underpostRoot}/bin baremetal --logs machine; exec bash" & disown`,
691
- {
692
- async: true,
693
- },
694
- );
702
+ openTerminal(`node ${underpostRoot}/bin baremetal --logs cloud`);
703
+ openTerminal(`node ${underpostRoot}/bin baremetal --logs machine`);
695
704
  } catch (error) {
696
705
  logger.error(error, error.stack);
697
706
  } finally {
@@ -710,6 +719,29 @@ menuentry '${menuentryStr}' {
710
719
  }
711
720
  },
712
721
 
722
+ /**
723
+ * @method mountBinfmtMisc
724
+ * @description Mounts the binfmt_misc filesystem to enable QEMU user-static binfmt support.
725
+ * This is necessary for cross-architecture execution within a chroot environment.
726
+ * @param {object} params - The parameters for the function.
727
+ * @param {string} params.nfsHostPath - The path to the NFS root filesystem on the host.
728
+ * @returns {void}
729
+ */
730
+ mountBinfmtMisc({ nfsHostPath }) {
731
+ // Install necessary packages for debootstrap and QEMU.
732
+ shellExec(`sudo dnf install -y iptables-legacy`);
733
+ shellExec(`sudo dnf install -y debootstrap`);
734
+ shellExec(`sudo dnf install kernel-modules-extra-$(uname -r)`);
735
+ // Reset QEMU user-static binfmt for proper cross-architecture execution.
736
+ shellExec(`sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes`);
737
+ // Mount binfmt_misc filesystem.
738
+ shellExec(`sudo modprobe binfmt_misc`);
739
+ shellExec(`sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc`);
740
+ // Set ownership and permissions for the NFS host path.
741
+ shellExec(`sudo chown -R root:root ${nfsHostPath}`);
742
+ shellExec(`sudo chmod 755 ${nfsHostPath}`);
743
+ },
744
+
713
745
  /**
714
746
  * @method removeMachines
715
747
  * @description Deletes all specified machines from MAAS.
@@ -903,8 +935,8 @@ EOF`);
903
935
  getHostArch() {
904
936
  // `uname -m` returns e.g. 'x86_64' or 'aarch64'
905
937
  const machine = shellExec('uname -m', { stdout: true }).trim();
906
- if (machine === 'x86_64') return 'amd64';
907
- if (machine === 'aarch64') return 'arm64';
938
+ if (machine === 'x86_64') return { alias: 'amd64', name: 'x86_64' };
939
+ if (machine === 'aarch64') return { alias: 'arm64', name: 'aarch64' };
908
940
  throw new Error(`Unsupported host architecture: ${machine}`);
909
941
  },
910
942
 
@@ -198,17 +198,23 @@ TOKEN_SECRET=$(cat /underpost/token-secret)
198
198
  echo ">>> Starting MAAS machine commissioning for system_id: $MACHINE_ID …"
199
199
 
200
200
  curl -X POST \\
201
- --fail --location --verbose --include --raw --trace-ascii /dev/stdout\\
202
- --header "Authorization:\\
203
- OAuth oauth_version=1.0,\\
204
- oauth_signature_method=PLAINTEXT,\\
205
- oauth_consumer_key=$CONSUMER_KEY,\\
206
- oauth_token=$TOKEN_KEY,\\
207
- oauth_signature=&$TOKEN_SECRET,\\
208
- oauth_nonce=$(uuidgen),\\
209
- oauth_timestamp=$(date +%s)"\\
210
- http://${callbackMetaData.runnerHost.ip}:5240/MAAS/api/2.0/machines/$MACHINE_ID/op-commission \\
211
- 2>&1 | tee /underpost/enlistment.log || echo "ERROR: MAAS commissioning returned code $?"`,
201
+ --fail --location --verbose --include --raw --trace-ascii /dev/stdout\\
202
+ --header "Authorization:\\
203
+ OAuth oauth_version=1.0,\\
204
+ oauth_signature_method=PLAINTEXT,\\
205
+ oauth_consumer_key=$CONSUMER_KEY,\\
206
+ oauth_token=$TOKEN_KEY,\\
207
+ oauth_signature=&$TOKEN_SECRET,\\
208
+ oauth_nonce=$(uuidgen),\\
209
+ oauth_timestamp=$(date +%s)"\\
210
+ -F "commissioning_scripts=20-maas-01-install-lldpd"\\
211
+ -F "enable_ssh=1"\\
212
+ -F "skip_bmc_config=1"\\
213
+ -F "skip_networking=1"\\
214
+ -F "skip_storage=1"\\
215
+ -F "testing_scripts=none"\\
216
+ http://${callbackMetaData.runnerHost.ip}:5240/MAAS/api/2.0/machines/$MACHINE_ID/op-commission \\
217
+ 2>&1 | tee /underpost/enlistment.log || echo "ERROR: MAAS commissioning returned code $?"`,
212
218
  'utf8',
213
219
  );
214
220
 
@@ -365,6 +371,9 @@ packages:
365
371
  - htop
366
372
  - snapd
367
373
  - chrony
374
+ - lldpd
375
+ - lshw
376
+
368
377
  resize_rootfs: false
369
378
  growpart:
370
379
  mode: "off"
@@ -470,7 +479,7 @@ cloud_final_modules:
470
479
  - scripts-per-once
471
480
  - scripts-per-boot
472
481
  # - scripts-per-instance
473
- # - scripts-user
482
+ - scripts-user
474
483
  - ssh-authkey-fingerprints
475
484
  - keys-to-console
476
485
  # - phone-home