underpost 2.8.817 → 2.8.821

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/bin/deploy.js CHANGED
@@ -51,522 +51,6 @@ logger.info('argv', process.argv);
51
51
 
52
52
  const [exe, dir, operator] = process.argv;
53
53
 
54
- const chronyConfPath = `/etc/chrony/chrony.conf`;
55
-
56
- const timezone = 'America/New_York';
57
-
58
- const timeZoneSteps = [
59
- `export DEBIAN_FRONTEND=noninteractive`,
60
-
61
- `ln -fs /usr/share/zoneinfo/${timezone} /etc/localtime`,
62
-
63
- `sudo dpkg-reconfigure --frontend noninteractive tzdata`,
64
- ];
65
- const keyboardSteps = [
66
- `sudo locale-gen en_US.UTF-8`,
67
- `sudo update-locale LANG=en_US.UTF-8`,
68
- `sudo sed -i 's/XKBLAYOUT="us"/XKBLAYOUT="es"/' /etc/default/keyboard`,
69
- `sudo dpkg-reconfigure --frontend noninteractive keyboard-configuration`,
70
- `sudo systemctl restart keyboard-setup.service`,
71
- ];
72
-
73
- const kernelLibVersion = `6.8.0-41-generic`;
74
-
75
- const installSteps = [
76
- `cat <<EOF | tee /etc/apt/sources.list
77
- deb http://ports.ubuntu.com/ubuntu-ports noble main restricted universe multiverse
78
- deb http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted universe multiverse
79
- deb http://ports.ubuntu.com/ubuntu-ports noble-security main restricted universe multiverse
80
- EOF`,
81
-
82
- `apt update -qq`,
83
- `apt -y full-upgrade`,
84
- `apt install -y build-essential xinput x11-xkb-utils usbutils`,
85
- 'apt install -y linux-image-generic',
86
- `apt install -y linux-modules-${kernelLibVersion} linux-modules-extra-${kernelLibVersion}`,
87
-
88
- `depmod -a ${kernelLibVersion}`,
89
- // `apt install -y cloud-init=25.1.2-0ubuntu0~24.04.1`,
90
- `apt install -y cloud-init systemd-sysv openssh-server sudo locales udev util-linux systemd-sysv iproute2 netplan.io ca-certificates curl wget chrony`,
91
- `ln -sf /lib/systemd/systemd /sbin/init`,
92
-
93
- `apt-get update`,
94
- `DEBIAN_FRONTEND=noninteractive apt-get install -y apt-utils`,
95
- `DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata kmod keyboard-configuration console-setup iputils-ping`,
96
- ];
97
-
98
- const bootCmdSteps = [
99
- `/underpost/dns.sh`,
100
- `/underpost/host.sh`,
101
- // `/underpost/date.sh`,
102
- `cp -a /underpost/90_maas.cfg /etc/cloud/cloud.cfg.d/90_maas.cfg`,
103
- ];
104
-
105
- const cloudInitReset = `sudo cloud-init clean --logs --seed --configs all --machine-id
106
- sudo rm -rf /var/lib/cloud/*`;
107
-
108
- const cloudConfigCmdRunFactory = (steps = []) =>
109
- steps
110
- .map(
111
- (step, i, a) =>
112
- ' - echo "\\$(date) | ' + (i + 1) + '/' + a.length + ' - ' + step.split('\n')[0] + '"' + `\n` + ` - ${step}`,
113
- )
114
- .join('\n');
115
-
116
- const cloudConfigFactory = (
117
- { IP_ADDRESS, architecture, host, nfsHostPath, ipaddr, update, gatewayip, reset },
118
- { consumer_key, consumer_secret, token_key, token_secret },
119
- path = '/etc/cloud/cloud.cfg.d/90_maas.cfg',
120
- ) => [
121
- // Configure cloud-init for MAAS
122
- `cat <<EOF_MAAS_CFG > ${path}
123
- #cloud-config
124
-
125
- hostname: ${host}
126
- # fqdn: server01.midominio.cl
127
- # prefer_fqdn_over_hostname: true
128
- # metadata_url: http://${IP_ADDRESS}:5240/MAAS/metadata
129
- # metadata_url: http://${IP_ADDRESS}:5248/MAAS/metadata
130
-
131
- # Check:
132
- # /MAAS/metadata/latest/enlist-preseed/?op=get_enlist_preseed
133
-
134
- # Debug:
135
- # https://maas.io/docs/how-to-use-logging
136
-
137
- datasource_list: [ MAAS ]
138
- datasource:
139
- MAAS:
140
- metadata_url: http://${IP_ADDRESS}:5240/MAAS/metadata/
141
- ${
142
- reset
143
- ? ''
144
- : `consumer_key: ${consumer_key}
145
- consumer_secret: ${consumer_secret}
146
- token_key: ${token_key}
147
- token_secret: ${token_secret}`
148
- }
149
-
150
-
151
- users:
152
- - name: ${process.env.MAAS_ADMIN_USERNAME}
153
- sudo: ['ALL=(ALL) NOPASSWD:ALL']
154
- shell: /bin/bash
155
- lock_passwd: false
156
- groups: sudo,users,admin,wheel,lxd
157
- plain_text_passwd: '${process.env.MAAS_ADMIN_USERNAME}'
158
- ssh_authorized_keys:
159
- - ${fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')}
160
-
161
- # manage_resolv_conf: true
162
- # resolv_conf:
163
- # nameservers: [8.8.8.8]
164
-
165
- # keyboard:
166
- # layout: es
167
-
168
- # check timedatectl on host
169
- # timezone: America/Santiago
170
- timezone: ${timezone}
171
-
172
- ntp:
173
- enabled: true
174
- servers:
175
- - ${process.env.MAAS_NTP_SERVER}
176
- ntp_client: chrony
177
- config:
178
- confpath: ${chronyConfPath}
179
-
180
- # ssh:
181
- # allow-pw: false
182
- # install-server: true
183
-
184
- # ssh_pwauth: false
185
-
186
- package_update: true
187
- package_upgrade: true
188
- packages:
189
- - git
190
- - htop
191
- - snapd
192
- - chrony
193
- resize_rootfs: false
194
- growpart:
195
- mode: false
196
- network:
197
- version: 2
198
- ethernets:
199
- ${process.env.RPI4_INTERFACE_NAME}:
200
- dhcp4: true
201
- addresses:
202
- - ${ipaddr}/24
203
- # routes:
204
- # - to: default
205
- # via: ${gatewayip}
206
-
207
- # chpasswd:
208
- # expire: false
209
- # users:
210
- # - {name: root, password: changeme, type: text}
211
-
212
- final_message: "====== Cloud init finished ======"
213
-
214
- # power_state:
215
- # mode: reboot
216
- # message: Rebooting after initial setup
217
- # timeout: 30
218
- # condition: True
219
- bootcmd:
220
- - echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
221
- - echo "Init bootcmd"
222
- - echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
223
- ${cloudConfigCmdRunFactory(bootCmdSteps)}
224
- runcmd:
225
- - echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
226
- - echo "Init runcmd"
227
- - echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
228
-
229
- # If this is set, 'root' will not be able to ssh in and they
230
- # will get a message to login instead as the default $user
231
- disable_root: true
232
-
233
- # This will cause the set+update hostname module to not operate (if true)
234
- preserve_hostname: false
235
-
236
- # The modules that run in the 'init' stage
237
- cloud_init_modules:
238
- - migrator
239
- - bootcmd
240
- - write-files
241
- - growpart
242
- - resizefs
243
- - set_hostname
244
- - update_etc_hosts
245
- - rsyslog
246
- - users-groups
247
- - ssh
248
-
249
- cloud_config_modules:
250
- - mounts
251
- - locale
252
- - set-passwords
253
- - package-update-upgrade-install
254
- - timezone
255
- - runcmd
256
- - ssh-import-id
257
- - ntp
258
-
259
- cloud_final_modules:
260
- - rightscale_userdata
261
- - scripts-per-once
262
- - scripts-per-boot
263
- - scripts-per-instance
264
- - scripts-user
265
- - ssh-authkey-fingerprints
266
- - keys-to-console
267
- - phone-home
268
- - final-message
269
-
270
- EOF_MAAS_CFG`,
271
- ];
272
-
273
- const runSteps = (nfsHostPath, steps = []) => {
274
- const script = steps
275
- .map(
276
- (s, i) => `echo "step ${i + 1}/${steps.length}: ${s.split('\n')[0]}"
277
- ${s}`,
278
- )
279
- .join('\n');
280
-
281
- const cmd = `sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF_OUTER'
282
- ${script}
283
- EOF_OUTER`;
284
-
285
- shellExec(cmd);
286
- };
287
-
288
- const chronySetUp = (path, alias = 'chrony') => {
289
- // use alias = 'chronyd' for RHEL
290
- // use alias = 'chrony' for Ubuntu
291
- return [
292
- `echo '
293
- # Use public servers from the pool.ntp.org project.
294
- # Please consider joining the pool (http://www.pool.ntp.org/join.html).
295
- # pool 2.pool.ntp.org iburst
296
- server ${process.env.MAAS_NTP_SERVER} iburst
297
-
298
- # Record the rate at which the system clock gains/losses time.
299
- driftfile /var/lib/chrony/drift
300
-
301
- # Allow the system clock to be stepped in the first three updates
302
- # if its offset is larger than 1 second.
303
- makestep 1.0 3
304
-
305
- # Enable kernel synchronization of the real-time clock (RTC).
306
- rtcsync
307
-
308
- # Enable hardware timestamping on all interfaces that support it.
309
- #hwtimestamp *
310
-
311
- # Increase the minimum number of selectable sources required to adjust
312
- # the system clock.
313
- #minsources 2
314
-
315
- # Allow NTP client access from local network.
316
- #allow 192.168.0.0/16
317
-
318
- # Serve time even if not synchronized to a time source.
319
- #local stratum 10
320
-
321
- # Specify file containing keys for NTP authentication.
322
- keyfile /etc/chrony.keys
323
-
324
- # Get TAI-UTC offset and leap seconds from the system tz database.
325
- leapsectz right/UTC
326
-
327
- # Specify directory for log files.
328
- logdir /var/log/chrony
329
-
330
- # Select which information is logged.
331
- #log measurements statistics tracking
332
- ' > ${path} `,
333
- `systemctl stop ${alias}`,
334
-
335
- `${alias}d -q 'server ntp.ubuntu.com iburst'`,
336
-
337
- // `chronyd -q 'server 0.europe.pool.ntp.org iburst'`,
338
-
339
- `sudo systemctl enable --now ${alias}`,
340
- `sudo systemctl restart ${alias}`,
341
- `sudo systemctl status ${alias}`,
342
-
343
- `chronyc sources`,
344
- `chronyc tracking`,
345
-
346
- `chronyc sourcestats -v`,
347
- `timedatectl status`,
348
- ];
349
- };
350
-
351
- const installUbuntuUnderpostTools = ({ nfsHostPath, host }) => {
352
- fs.mkdirSync(`${nfsHostPath}/underpost`, { recursive: true });
353
-
354
- logger.info('Build', `${nfsHostPath}/underpost/date.sh`);
355
- fs.writeFileSync(
356
- `${nfsHostPath}/underpost/date.sh`,
357
- `${timeZoneSteps.join('\n')}
358
- ${chronySetUp(chronyConfPath).join('\n')}
359
- `,
360
- 'utf8',
361
- );
362
-
363
- logger.info('Build', `${nfsHostPath}/underpost/host.sh`);
364
- fs.writeFileSync(
365
- `${nfsHostPath}/underpost/host.sh`,
366
- `echo -e "127.0.0.1 localhost\n127.0.1.1 ${host}" | tee -a /etc/hosts`,
367
- 'utf8',
368
- );
369
-
370
- logger.info('Build', `${nfsHostPath}/underpost/keys.sh`);
371
- fs.writeFileSync(
372
- `${nfsHostPath}/underpost/keys.sh`,
373
- `cat /etc/cloud/cloud.cfg.d/90_maas.cfg | grep -C 5 'metadata'`,
374
- 'utf8',
375
- );
376
-
377
- logger.info('Build', `${nfsHostPath}/underpost/keyboard.sh`);
378
- fs.writeFileSync(
379
- `${nfsHostPath}/underpost/keyboard.sh`,
380
- `${keyboardSteps.join('\n')}
381
- `,
382
- 'utf8',
383
- );
384
-
385
- logger.info('Build', `${nfsHostPath}/underpost/dns.sh`);
386
- // echo "nameserver ${process.env.MAAS_DNS}" | tee /etc/resolv.conf > /dev/null
387
- fs.writeFileSync(
388
- `${nfsHostPath}/underpost/dns.sh`,
389
- `rm /etc/resolv.conf
390
- echo 'nameserver 8.8.8.8' > /run/systemd/resolve/stub-resolv.conf
391
- ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf`,
392
- 'utf8',
393
- );
394
-
395
- logger.info('Build', `${nfsHostPath}/underpost/start.sh`);
396
- fs.writeFileSync(
397
- `${nfsHostPath}/underpost/start.sh`,
398
- `#!/bin/bash
399
- set -x
400
- sudo cloud-init --all-stages
401
- `,
402
- 'utf8',
403
- );
404
-
405
- logger.info('Build', `${nfsHostPath}/underpost/reset.sh`);
406
- fs.writeFileSync(
407
- `${nfsHostPath}/underpost/reset.sh`,
408
- `${cloudInitReset}
409
- ${bootCmdSteps.join('\n')}`,
410
- 'utf8',
411
- );
412
-
413
- logger.info('Build', `${nfsHostPath}/underpost/help.sh`);
414
- fs.writeFileSync(
415
- `${nfsHostPath}/underpost/help.sh`,
416
- `echo "=== Cloud init utils ==="
417
- echo "sudo cloud-init --all-stages"
418
- echo "sudo cloud-init clean --logs --seed --configs all --machine-id --reboot"
419
- echo "sudo cloud-init init --local"
420
- echo "sudo cloud-init init"
421
- echo "sudo cloud-init modules --mode=config"
422
- echo "sudo cloud-init modules --mode=final"`,
423
- 'utf8',
424
- );
425
-
426
- logger.info('Build', `${nfsHostPath}/underpost/test.sh`);
427
- fs.writeFileSync(
428
- `${nfsHostPath}/underpost/test.sh`,
429
- `echo -e "\n=== Current date/time ==="
430
- date '+%Y-%m-%d %H:%M:%S'
431
- echo -e "\n=== Keyboard layout ==="
432
- cat /etc/default/keyboard
433
- echo -e "\n=== Registered users ==="
434
- cut -d: -f1 /etc/passwd
435
- `,
436
- 'utf8',
437
- );
438
-
439
- logger.info('Build', `${nfsHostPath}/underpost/config-path.sh`);
440
- fs.writeFileSync(`${nfsHostPath}/underpost/config-path.sh`, `echo "/etc/cloud/cloud.cfg.d/90_maas.cfg"`, 'utf8');
441
-
442
- shellExec(`sudo rm -rf ${nfsHostPath}/root/.ssh`);
443
- shellExec(`sudo rm -rf ${nfsHostPath}/home/root/.ssh`);
444
-
445
- fs.copySync(`/root/.ssh`, `${nfsHostPath}/root/.ssh`);
446
-
447
- logger.info('Run', `${nfsHostPath}/underpost/test.sh`);
448
- runSteps(nfsHostPath, [
449
- `chmod +x /underpost/date.sh`,
450
- `chmod +x /underpost/keyboard.sh`,
451
- `chmod +x /underpost/dns.sh`,
452
- `chmod +x /underpost/help.sh`,
453
- `chmod +x /underpost/config-path.sh`,
454
- `chmod +x /underpost/host.sh`,
455
- `chmod +x /underpost/keys.sh`,
456
- `chmod +x /underpost/test.sh`,
457
- `chmod +x /underpost/start.sh`,
458
- `chmod +x /underpost/reset.sh`,
459
- chronySetUp(chronyConfPath)[0],
460
- `sudo chmod 700 ~/.ssh/`,
461
- `sudo chmod 600 ~/.ssh/authorized_keys`,
462
- `sudo chmod 644 ~/.ssh/known_hosts`,
463
- `sudo chmod 600 ~/.ssh/id_rsa`,
464
- `sudo chmod 600 /etc/ssh/ssh_host_ed25519_key`,
465
- `chown -R root:root ~/.ssh`,
466
- `/underpost/test.sh`,
467
- ]);
468
- };
469
-
470
- const updateVirtualRoot = async ({ IP_ADDRESS, architecture, host, nfsHostPath, ipaddr, update, gatewayip }) => {
471
- // <consumer_key>:<consumer_token>:<secret>
472
- // <consumer_key>:<consumer_secret>:<token_key>:<token_secret>
473
- // maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}
474
- // maas ${process.env.MAAS_ADMIN_USERNAME} account create-authorisation-token
475
- // maas apikey --generate --username ${process.env.MAAS_ADMIN_USERNAME}
476
- // https://github.com/CanonicalLtd/maas-docs/issues/647
477
-
478
- const parts = shellExec(`maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}`, {
479
- stdout: true,
480
- })
481
- .trim()
482
- .split(`\n`)[0]
483
- .split(':');
484
-
485
- let consumer_key, consumer_secret, token_key, token_secret;
486
-
487
- if (parts.length === 4) {
488
- [consumer_key, consumer_secret, token_key, token_secret] = parts;
489
- } else if (parts.length === 3) {
490
- [consumer_key, token_key, token_secret] = parts;
491
- consumer_secret = '""';
492
- token_secret = token_secret.split(' MAAS consumer')[0].trim();
493
- } else {
494
- throw new Error('Invalid token format');
495
- }
496
-
497
- logger.info('Maas api token generated', { consumer_key, consumer_secret, token_key, token_secret });
498
-
499
- if (update) {
500
- // --reboot
501
- if (process.argv.includes('reset'))
502
- shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
503
- ${cloudInitReset}
504
- EOF`);
505
-
506
- if (fs.existsSync(`${nfsHostPath}/var/log/`)) {
507
- fs.writeFileSync(`${nfsHostPath}/var/log/cloud-init.log`, '', 'utf8');
508
- fs.writeFileSync(`${nfsHostPath}/var/log/cloud-init-output.log`, '', 'utf8');
509
- }
510
- } else {
511
- runSteps(nfsHostPath, installSteps);
512
- runSteps(nfsHostPath, [
513
- `useradd -m -s /bin/bash -G sudo root`,
514
- `echo 'root:root' | chpasswd`,
515
- `mkdir -p /home/root/.ssh`,
516
- `echo '${fs.readFileSync(
517
- `/home/dd/engine/engine-private/deploy/id_rsa.pub`,
518
- 'utf8',
519
- )}' > /home/root/.ssh/authorized_keys`,
520
- `chown -R root /home/root/.ssh`,
521
- `chmod 700 /home/root/.ssh`,
522
- `chmod 600 /home/root/.ssh/authorized_keys`,
523
- ]);
524
- runSteps(nfsHostPath, [
525
- // `date -s "${shellExec(`date '+%Y-%m-%d %H:%M:%S'`, { stdout: true }).trim()}"`,
526
- // `date`,
527
- ...timeZoneSteps,
528
- ...chronySetUp(chronyConfPath),
529
- ...keyboardSteps,
530
- ]);
531
- }
532
-
533
- runSteps(
534
- nfsHostPath,
535
- cloudConfigFactory(
536
- {
537
- reset: process.argv.includes('reset') ? true : false,
538
- IP_ADDRESS,
539
- architecture,
540
- host,
541
- nfsHostPath,
542
- ipaddr,
543
- update,
544
- gatewayip,
545
- },
546
- { consumer_key, consumer_secret, token_key, token_secret },
547
- ),
548
- );
549
-
550
- runSteps(
551
- nfsHostPath,
552
- cloudConfigFactory(
553
- {
554
- IP_ADDRESS,
555
- architecture,
556
- host,
557
- nfsHostPath,
558
- ipaddr,
559
- update,
560
- gatewayip,
561
- },
562
- { consumer_key, consumer_secret, token_key, token_secret },
563
- '/underpost/90_maas.cfg',
564
- ),
565
- );
566
-
567
- installUbuntuUnderpostTools({ nfsHostPath, host });
568
- };
569
-
570
54
  try {
571
55
  switch (operator) {
572
56
  case 'save':
@@ -1743,609 +1227,6 @@ EOF`);
1743
1227
  break;
1744
1228
  }
1745
1229
 
1746
- case 'maas': {
1747
- shellExec(
1748
- `underpost secret underpost --create-from-file /home/dd/engine/engine-private/conf/dd-cron/.env.production`,
1749
- );
1750
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
1751
- const IP_ADDRESS = getLocalIPv4Address();
1752
- const serverip = IP_ADDRESS;
1753
- const tftpRoot = process.argv.includes('v3.0')
1754
- ? `/var/snap/maas/common/maas/boot-resources/snapshot-20250720-162718`
1755
- : process.env.TFTP_ROOT;
1756
- const ipaddr = process.env.RPI4_IP;
1757
- const netmask = process.env.NETMASK;
1758
- const gatewayip = process.env.GATEWAY_IP;
1759
-
1760
- const machineFactory = (m) => ({
1761
- system_id: m.interface_set[0].system_id,
1762
- mac_address: m.interface_set[0].mac_address,
1763
- hostname: m.hostname,
1764
- status_name: m.status_name,
1765
- });
1766
-
1767
- let resources;
1768
- try {
1769
- resources = JSON.parse(
1770
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resources read`, {
1771
- silent: true,
1772
- stdout: true,
1773
- }),
1774
- ).map((o) => ({
1775
- id: o.id,
1776
- name: o.name,
1777
- architecture: o.architecture,
1778
- }));
1779
- } catch (error) {
1780
- logger.error(error);
1781
- }
1782
-
1783
- let machines;
1784
- try {
1785
- machines = JSON.parse(
1786
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machines read`, {
1787
- stdout: true,
1788
- silent: true,
1789
- }),
1790
- ).map((m) => machineFactory(m));
1791
- } catch (error) {
1792
- logger.error(error);
1793
- }
1794
-
1795
- if (process.argv.includes('ls')) {
1796
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-sources read`);
1797
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} commissioning-scripts read`);
1798
- // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-source-selections read 60`);
1799
- logger.info('Resources');
1800
- console.table(resources);
1801
- logger.info('Machines');
1802
- console.table(machines);
1803
- process.exit(0);
1804
- }
1805
-
1806
- if (process.argv.includes('config')) {
1807
- shellExec(`sudo sed -i 's/^#Storage=auto/Storage=volatile/' /etc/systemd/journald.conf`);
1808
- shellExec(`sudo systemctl daemon-reload`);
1809
- shellExec(`sudo systemctl restart systemd-journald`);
1810
- shellExec(`journalctl --disk-usage`);
1811
- process.exit(0);
1812
- }
1813
-
1814
- if (process.argv.includes('db')) {
1815
- // DROP, ALTER, CREATE, WITH ENCRYPTED
1816
- // sudo -u <user> -h <host> psql <db-name>
1817
- shellExec(`DB_PG_MAAS_NAME=${process.env.DB_PG_MAAS_NAME}`);
1818
- shellExec(`DB_PG_MAAS_PASS=${process.env.DB_PG_MAAS_PASS}`);
1819
- shellExec(`DB_PG_MAAS_USER=${process.env.DB_PG_MAAS_USER}`);
1820
- shellExec(`DB_PG_MAAS_HOST=${process.env.DB_PG_MAAS_HOST}`);
1821
- shellExec(
1822
- `sudo -i -u postgres psql -c "CREATE USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
1823
- );
1824
- shellExec(
1825
- `sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ENCRYPTED PASSWORD '$DB_PG_MAAS_PASS'"`,
1826
- );
1827
- const actions = ['LOGIN', 'SUPERUSER', 'INHERIT', 'CREATEDB', 'CREATEROLE', 'REPLICATION'];
1828
- shellExec(`sudo -i -u postgres psql -c "ALTER USER \"$DB_PG_MAAS_USER\" WITH ${actions.join(' ')}"`);
1829
- shellExec(`sudo -i -u postgres psql -c "\\du"`);
1830
-
1831
- shellExec(`sudo -i -u postgres createdb -O "$DB_PG_MAAS_USER" "$DB_PG_MAAS_NAME"`);
1832
-
1833
- shellExec(`sudo -i -u postgres psql -c "\\l"`);
1834
- process.exit(0);
1835
- }
1836
-
1837
- // TODO: - Disable maas proxy (egress forwarding to public dns)
1838
- // - Configure maas dhcp control server
1839
- // - Configure maas dns forwarding ${process.env.MAAS_DNS}
1840
- // - Disable DNSSEC validation to No (Disable DNSSEC; useful when upstream DNS is misconfigured)
1841
-
1842
- if (process.argv.includes('clear')) {
1843
- for (const machine of machines) {
1844
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
1845
- }
1846
- // machines = [];
1847
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries clear all=true`);
1848
- if (process.argv.includes('force')) {
1849
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries scan force=true`);
1850
- }
1851
- process.exit(0);
1852
- }
1853
- if (process.argv.includes('grub-arm64')) {
1854
- shellExec(`sudo dnf install grub2-efi-aa64-modules`);
1855
- shellExec(`sudo dnf install grub2-efi-x64-modules`);
1856
- // sudo grub2-mknetdir --net-directory=${tftpRoot} --subdir=/boot/grub --module-path=/usr/lib/grub/arm64-efi arm64-efi
1857
- process.exit(0);
1858
- }
1859
-
1860
- if (process.argv.includes('psql')) {
1861
- const cmd = `psql -U ${process.env.DB_PG_MAAS_USER} -h ${process.env.DB_PG_MAAS_HOST} -W ${process.env.DB_PG_MAAS_NAME}`;
1862
- pbcopy(cmd);
1863
- process.exit(0);
1864
- }
1865
- if (process.argv.includes('logs')) {
1866
- shellExec(`maas status`);
1867
- const cmd = `journalctl -f -t dhcpd -u snap.maas.pebble.service`;
1868
- pbcopy(cmd);
1869
- process.exit(0);
1870
- }
1871
- if (process.argv.includes('reset')) {
1872
- // shellExec(
1873
- // `maas init region+rack --database-uri "postgres://$DB_PG_MAAS_USER:$DB_PG_MAAS_PASS@$DB_PG_MAAS_HOST/$DB_PG_MAAS_NAME"` +
1874
- // ` --maas-url http://${IP_ADDRESS}:5240/MAAS`,
1875
- // );
1876
- const cmd =
1877
- `maas init region+rack --database-uri "postgres://${process.env.DB_PG_MAAS_USER}:${process.env.DB_PG_MAAS_PASS}@${process.env.DB_PG_MAAS_HOST}/${process.env.DB_PG_MAAS_NAME}"` +
1878
- ` --maas-url http://${IP_ADDRESS}:5240/MAAS`;
1879
- pbcopy(cmd);
1880
- process.exit(0);
1881
- }
1882
-
1883
- if (process.argv.includes('restart')) {
1884
- shellExec(`sudo snap restart maas.pebble`);
1885
- let secs = 0;
1886
- while (
1887
- !(
1888
- shellExec(`maas status`, { silent: true, disableLog: true, stdout: true })
1889
- .split(' ')
1890
- .filter((l) => l.match('inactive')).length === 1
1891
- )
1892
- ) {
1893
- await timer(1000);
1894
- console.log(`Waiting... (${++secs}s)`);
1895
- }
1896
- process.exit(0);
1897
- }
1898
-
1899
- // shellExec(`MAAS_ADMIN_USERNAME=${process.env.MAAS_ADMIN_USERNAME}`);
1900
- // shellExec(`MAAS_ADMIN_EMAIL=${process.env.MAAS_ADMIN_EMAIL}`);
1901
- // shellExec(`maas createadmin --username $MAAS_ADMIN_USERNAME --email $MAAS_ADMIN_EMAIL`);
1902
-
1903
- // MaaS admin CLI:
1904
- // maas login <maas-admin-username> http://localhost:5240/MAAS
1905
- // paste GUI API KEY (profile section)
1906
-
1907
- // Import custom image
1908
- // maas <maas-admin-username> boot-resources create name='custom/RockyLinuxRpi4' \
1909
- // title='RockyLinuxRpi4' \
1910
- // architecture='arm64/generic' \
1911
- // filetype='tgz' \
1912
- // content@=/home/RockyLinuxRpi_9-latest.tar.gz
1913
-
1914
- // Image boot resource:
1915
- // /var/snap/maas/current/root/snap/maas
1916
- // /var/snap/maas/common/maas/tftp_root
1917
- // sudo chmod 755 /var/snap/maas/common/maas/tftp_root
1918
-
1919
- // /var/snap/maas/common/maas/dhcpd.conf
1920
- // sudo snap restart maas.pebble
1921
-
1922
- // PXE Linux files:
1923
- // /var/snap/maas/common/maas/image-storage/bootloaders/pxe/i386
1924
- // sudo nmcli con modify <interface-device-name-connection-id> ethtool.feature-rx on ethtool.feature-tx off
1925
- // sudo nmcli connection up <interface-device-name-connection-id>
1926
-
1927
- // man nm-settings |grep feature-tx-checksum
1928
-
1929
- // nmcli c modify <interface-device-name-connection-id> \
1930
- // ethtool.feature-tx-checksum-fcoe-crc off \
1931
- // ethtool.feature-tx-checksum-ip-generic off \
1932
- // ethtool.feature-tx-checksum-ipv4 off \
1933
- // ethtool.feature-tx-checksum-ipv6 off \
1934
- // ethtool.feature-tx-checksum-sctp off
1935
-
1936
- // Ensure Rocky NFS server and /etc/exports configured
1937
- // sudo systemctl restart nfs-server
1938
- // Check mounts: showmount -e <server-ip>
1939
- // Check nfs ports: rpcinfo -p
1940
- // sudo chown -R root:root ${process.env.NFS_EXPORT_PATH}/rpi4mb
1941
- // sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/rpi4mb
1942
-
1943
- // tftp server
1944
- // sudo chown -R root:root /var/snap/maas/common/maas/tftp_root/rpi4mb
1945
-
1946
- // tftp client
1947
- // sudo dnf install tftp
1948
- // tftp <server-ip> -c get <path>
1949
-
1950
- // Check firewall-cmd
1951
- // firewall-cmd --permanent --add-service=rpc-bind
1952
- // firewall-cmd --reload
1953
- // systemctl disable firewalld
1954
- // sudo firewall-cmd --permanent --add-port=10259/tcp --zone=public
1955
-
1956
- // Image extension transform (.img.xz to .tar.gz):
1957
- // tar -cvzf image-name.tar.gz image-name.img.xz
1958
-
1959
- // Rocky network configuration:
1960
- // /etc/NetworkManager/system-connections
1961
-
1962
- // Rocky kernel params update
1963
- // sudo grubby --args="<key>=<value> <key>=<value>" --update-kernel=ALL
1964
- // sudo reboot now
1965
-
1966
- // Temporal:
1967
- // sudo snap install temporal
1968
- // journalctl -u snap.maas.pebble -t maas-regiond
1969
- // journalctl -u snap.maas.pebble -t maas-temporal -n 100 --no-pager -f
1970
-
1971
- // Remove:
1972
- // sudo dnf remove <package> -y; sudo dnf autoremove -y; sudo dnf clean packages
1973
- // check: ~
1974
- // check: ~./cache
1975
- // check: ~./config
1976
-
1977
- // Check file logs
1978
- // grep -i -E -C 1 '<key-a>|<key-b>' /example.log | tail -n 600
1979
-
1980
- // Back into your firmware setup (UEFI or BIOS config screen).
1981
- // grub> fwsetup
1982
-
1983
- // Poweroff:
1984
- // grub > halt
1985
- // initramfs > poweroff
1986
-
1987
- // Check interface
1988
- // ip link show
1989
- // nmcli con show
1990
-
1991
- let firmwarePath,
1992
- tftpSubDir,
1993
- kernelFilesPaths,
1994
- name,
1995
- architecture,
1996
- resource,
1997
- nfsConnectStr,
1998
- etcExports,
1999
- nfsServerRootPath,
2000
- bootConf,
2001
- zipFirmwareFileName,
2002
- zipFirmwareName,
2003
- zipFirmwareUrl,
2004
- interfaceName,
2005
- nfsHost,
2006
- bootResourcesPath,
2007
- bootKernelPath;
2008
-
2009
- switch (process.argv[3]) {
2010
- case 'rpi4mb':
2011
- tftpSubDir = '/rpi4mb';
2012
- zipFirmwareFileName = `RPi4_UEFI_Firmware_v1.41.zip`;
2013
- zipFirmwareName = zipFirmwareFileName.split('.zip')[0];
2014
- zipFirmwareUrl = `https://github.com/pftf/RPi4/releases/download/v1.41/RPi4_UEFI_Firmware_v1.41.zip`;
2015
- firmwarePath = `../${zipFirmwareName}`;
2016
- interfaceName = process.env.RPI4_INTERFACE_NAME;
2017
- nfsHost = 'rpi4mb';
2018
- if (!fs.existsSync(firmwarePath)) {
2019
- await Downloader(zipFirmwareUrl, `../${zipFirmwareFileName}`);
2020
- shellExec(`cd .. && mkdir ${zipFirmwareName} && cd ${zipFirmwareName} && unzip ../${zipFirmwareFileName}`);
2021
- }
2022
- resource = resources.find((o) => o.architecture === 'arm64/ga-24.04' && o.name === 'ubuntu/noble');
2023
- name = resource.name;
2024
- architecture = resource.architecture;
2025
- // resource = resources.find((o) => o.name === name && o.architecture === architecture);
2026
- nfsServerRootPath = `${process.env.NFS_EXPORT_PATH}/rpi4mb`;
2027
- // ,anonuid=1001,anongid=100
2028
- // etcExports = `${nfsServerRootPath} *(rw,all_squash,sync,no_root_squash,insecure)`;
2029
- etcExports = `${nfsServerRootPath} 192.168.1.0/24(${[
2030
- 'rw',
2031
- // 'all_squash',
2032
- 'sync',
2033
- 'no_root_squash',
2034
- 'no_subtree_check',
2035
- 'insecure',
2036
- ]})`;
2037
- if (process.argv.includes('v3.0')) {
2038
- bootResourcesPath = `/var/snap/maas/common/maas/boot-resources/snapshot-20250720-162718`;
2039
- bootKernelPath = `/var/snap/maas/common/maas/boot-resources/snapshot-20250720-162718/ubuntu/arm64/hwe-24.04/noble/stable`;
2040
- kernelFilesPaths = {
2041
- 'vmlinuz-efi': `${bootKernelPath}/boot-kernel`,
2042
- 'initrd.img': `${bootKernelPath}/boot-initrd`,
2043
- squashfs: `${bootKernelPath}/squashfs`,
2044
- };
2045
- } else {
2046
- const resourceData = JSON.parse(
2047
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} boot-resource read ${resource.id}`, {
2048
- stdout: true,
2049
- silent: true,
2050
- disableLog: true,
2051
- }),
2052
- );
2053
- const bootFiles = resourceData.sets[Object.keys(resourceData.sets)[0]].files;
2054
- const suffix = architecture.match('xgene') ? '.xgene' : '';
2055
- bootResourcesPath = `/var/snap/maas/common/maas/image-storage/bootloaders/uefi/arm64`;
2056
- bootKernelPath = `/var/snap/maas/common/maas/image-storage`;
2057
- kernelFilesPaths = {
2058
- 'vmlinuz-efi': `${bootKernelPath}/${bootFiles['boot-kernel' + suffix].filename_on_disk}`,
2059
- 'initrd.img': `${bootKernelPath}/${bootFiles['boot-initrd' + suffix].filename_on_disk}`,
2060
- squashfs: `${bootKernelPath}/${bootFiles['squashfs'].filename_on_disk}`,
2061
- };
2062
- }
2063
-
2064
- const protocol = 'tcp'; // v3 -> tcp, v4 -> udp
2065
-
2066
- const mountOptions = [
2067
- protocol,
2068
- 'vers=3',
2069
- 'nfsvers=3',
2070
- 'nolock',
2071
- // 'protocol=tcp',
2072
- // 'hard=true',
2073
- 'port=2049',
2074
- // 'sec=none',
2075
- 'rw',
2076
- 'hard',
2077
- 'intr',
2078
- 'rsize=32768',
2079
- 'wsize=32768',
2080
- 'acregmin=0',
2081
- 'acregmax=0',
2082
- 'acdirmin=0',
2083
- 'acdirmax=0',
2084
- 'noac',
2085
- // 'nodev',
2086
- // 'nosuid',
2087
- ];
2088
- const cmd = [
2089
- `console=serial0,115200`,
2090
- // `console=ttyAMA0,115200`,
2091
- `console=tty1`,
2092
- // `initrd=-1`,
2093
- // `net.ifnames=0`,
2094
- // `dwc_otg.lpm_enable=0`,
2095
- // `elevator=deadline`,
2096
- `root=/dev/nfs`,
2097
- `nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb,${mountOptions}`,
2098
- // `nfsroot=${serverip}:${process.env.NFS_EXPORT_PATH}/rpi4mb`,
2099
- `ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${nfsHost}:${interfaceName}:static`,
2100
- `rootfstype=nfs`,
2101
- `rw`,
2102
- `rootwait`,
2103
- `fixrtc`,
2104
- 'initrd=initrd.img',
2105
- // 'boot=casper',
2106
- // 'ro',
2107
- 'netboot=nfs',
2108
- `init=/sbin/init`,
2109
- // `cloud-config-url=/dev/null`,
2110
- // 'ip=dhcp',
2111
- // 'ip=dfcp',
2112
- // 'autoinstall',
2113
- // 'rd.break',
2114
-
2115
- // Disable services that not apply over nfs
2116
- `systemd.mask=systemd-network-generator.service`,
2117
- `systemd.mask=systemd-networkd.service`,
2118
- `systemd.mask=systemd-fsck-root.service`,
2119
- `systemd.mask=systemd-udev-trigger.service`,
2120
- ];
2121
-
2122
- // TODO: use autoinstall cloud-config-url=http://<MAAS_IP>:5240/MAAS/metadata/latest
2123
- // #cloud-config
2124
- // autoinstall:
2125
- // version: 1
2126
-
2127
- // keyboard:
2128
- // layout: es
2129
- // variant: latinamerican
2130
-
2131
- // identity:
2132
- // hostname: rpi4
2133
- // username: root
2134
- // password: "{{PASSWORD}}"
2135
-
2136
- // ssh:
2137
- // install-server: true
2138
- // allow-pw: true
2139
- // authorized-keys:
2140
- // - "{{SSH_KEY}}"
2141
-
2142
- // locale: es_ES.UTF-8
2143
- // timezone: America/Santiago
2144
-
2145
- // packages:
2146
- // - cloud-init
2147
- // - systemd-sysv
2148
- // - openssh-server
2149
- // - sudo
2150
- // - udev
2151
- // - netplan.io
2152
-
2153
- // late-commands:
2154
- // - curtin in-target --target=/target ln -sf /lib/systemd/systemd /sbin/init
2155
-
2156
- nfsConnectStr = cmd.join(' ');
2157
- bootConf = `[all]
2158
- MAC_ADDRESS=00:00:00:00:00:00
2159
- MAC_ADDRESS_OTP=0,1
2160
- BOOT_UART=0
2161
- WAKE_ON_GPIO=1
2162
- POWER_OFF_ON_HALT=0
2163
- ENABLE_SELF_UPDATE=1
2164
- DISABLE_HDMI=0
2165
- TFTP_IP=${serverip}
2166
- TFTP_PREFIX=1
2167
- TFTP_PREFIX_STR=${tftpSubDir.slice(1)}/
2168
- NET_INSTALL_ENABLED=1
2169
- DHCP_TIMEOUT=45000
2170
- DHCP_REQ_TIMEOUT=4000
2171
- TFTP_FILE_TIMEOUT=30000
2172
- BOOT_ORDER=0x21`;
2173
- // CLIENT_IP=${ipaddr}
2174
- // SUBNET=255.255.255.0
2175
- break;
2176
-
2177
- default:
2178
- break;
2179
- }
2180
- shellExec(`sudo chmod 755 ${process.env.NFS_EXPORT_PATH}/${nfsHost}`);
2181
-
2182
- shellExec(`sudo rm -rf ${tftpRoot}${tftpSubDir}`);
2183
- shellExec(`sudo cp -a ${firmwarePath} ${tftpRoot}${tftpSubDir}`);
2184
- shellExec(`mkdir -p ${tftpRoot}${tftpSubDir}/pxe`);
2185
-
2186
- fs.writeFileSync(`/etc/exports`, etcExports, 'utf8');
2187
- if (bootConf) fs.writeFileSync(`${tftpRoot}${tftpSubDir}/boot.conf`, bootConf, 'utf8');
2188
-
2189
- shellExec(`node bin/deploy nfs`);
2190
-
2191
- switch (process.argv[3]) {
2192
- case 'rpi4mb':
2193
- {
2194
- for (const file of ['bootaa64.efi', 'grubaa64.efi']) {
2195
- shellExec(`sudo cp -a ${bootResourcesPath}/${file} ${tftpRoot}${tftpSubDir}/pxe/${file}`);
2196
- }
2197
-
2198
- {
2199
- for (const file of Object.keys(kernelFilesPaths)) {
2200
- shellExec(`sudo cp -a ${kernelFilesPaths[file]} ${tftpRoot}${tftpSubDir}/pxe/${file}`);
2201
- }
2202
-
2203
- fs.mkdirSync(`${tftpRoot}/grub`, { recursive: true });
2204
-
2205
- const menuentryStr = 'UNDERPOST.NET UEFI/GRUB/MAAS RPi4 commissioning (ARM64)';
2206
- const grubCfgPath = `${tftpRoot}/grub/grub.cfg`;
2207
- fs.writeFileSync(
2208
- grubCfgPath,
2209
- `
2210
- insmod gzio
2211
- insmod http
2212
- insmod nfs
2213
- set timeout=5
2214
- set default=0
2215
-
2216
- menuentry '${menuentryStr}' {
2217
- set root=(tftp,${serverip})
2218
- linux ${tftpSubDir}/pxe/vmlinuz-efi ${nfsConnectStr}
2219
- initrd ${tftpSubDir}/pxe/initrd.img
2220
- boot
2221
- }
2222
-
2223
- `,
2224
- 'utf8',
2225
- );
2226
- }
2227
- const arm64EfiPath = `${tftpRoot}/grub/arm64-efi`;
2228
- if (fs.existsSync(arm64EfiPath)) shellExec(`sudo rm -rf ${arm64EfiPath}`);
2229
- shellExec(`sudo cp -a /usr/lib/grub/arm64-efi ${arm64EfiPath}`);
2230
- }
2231
-
2232
- break;
2233
-
2234
- default:
2235
- break;
2236
- }
2237
-
2238
- logger.info('succes maas deploy', {
2239
- resource,
2240
- kernelFilesPaths,
2241
- tftpRoot,
2242
- tftpSubDir,
2243
- firmwarePath,
2244
- etcExports,
2245
- nfsServerRootPath,
2246
- nfsConnectStr,
2247
- });
2248
- if (process.argv.includes('restart')) {
2249
- if (fs.existsSync(`node engine-private/r.js`)) shellExec(`node engine-private/r`);
2250
- shellExec(`node bin/deploy maas dhcp`);
2251
- shellExec(`sudo chown -R root:root ${tftpRoot}`);
2252
- shellExec(`sudo sudo chmod 755 ${tftpRoot}`);
2253
- }
2254
- // for (const machine of machines) {
2255
- // // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine delete ${machine.system_id}`);
2256
- // shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${machine.system_id}`, {
2257
- // silent: true,
2258
- // });
2259
- // }
2260
- // machines = [];
2261
-
2262
- const monitor = async () => {
2263
- // discoveries Query observed discoveries.
2264
- // discovery Read or delete an observed discovery.
2265
-
2266
- const discoveries = JSON.parse(
2267
- shellExec(`maas ${process.env.MAAS_ADMIN_USERNAME} discoveries read`, {
2268
- silent: true,
2269
- stdout: true,
2270
- }),
2271
- ).filter(
2272
- (o) => o.ip !== IP_ADDRESS && o.ip !== gatewayip && !machines.find((_o) => _o.mac_address === o.mac_address),
2273
- );
2274
-
2275
- // {
2276
- // "discovery_id": "",
2277
- // "ip": "192.168.1.189",
2278
- // "mac_address": "00:00:00:00:00:00",
2279
- // "last_seen": "2025-05-05T14:17:37.354",
2280
- // "hostname": null,
2281
- // "fabric_name": "",
2282
- // "vid": null,
2283
- // "mac_organization": "",
2284
- // "observer": {
2285
- // "system_id": "",
2286
- // "hostname": "",
2287
- // "interface_id": 1,
2288
- // "interface_name": ""
2289
- // },
2290
- // "resource_uri": "/MAAS/api/2.0/discovery/MTkyLjE2OC4xLjE4OSwwMDowMDowMDowMDowMDowMA==/"
2291
- // },
2292
-
2293
- for (const discovery of discoveries) {
2294
- const machine = {
2295
- architecture: architecture.match('amd') ? 'amd64/generic' : 'arm64/generic',
2296
- mac_address: discovery.mac_address,
2297
- hostname: discovery.hostname ?? discovery.mac_organization ?? discovery.domain ?? `generic-host-${s4()}`,
2298
- // discovery.ip.match(ipaddr)
2299
- // ? nfsHost
2300
- // : `unknown-${s4()}`,
2301
- // description: '',
2302
- // https://maas.io/docs/reference-power-drivers
2303
- power_type: 'manual', // manual
2304
- // power_parameters_power_address: discovery.ip,
2305
- mac_addresses: discovery.mac_address,
2306
- };
2307
- machine.hostname = machine.hostname.replaceAll(' ', '').replaceAll('.', '');
2308
-
2309
- if (machine.hostname.match('generic-host'))
2310
- try {
2311
- let newMachine = shellExec(
2312
- `maas ${process.env.MAAS_ADMIN_USERNAME} machines create ${Object.keys(machine)
2313
- .map((k) => `${k}="${machine[k]}"`)
2314
- .join(' ')}`,
2315
- {
2316
- silent: true,
2317
- stdout: true,
2318
- },
2319
- );
2320
- newMachine = machineFactory(JSON.parse(newMachine));
2321
- machines.push(newMachine);
2322
- console.log(newMachine);
2323
- // commissioning_scripts=90-verify-user.sh
2324
- shellExec(
2325
- `maas ${process.env.MAAS_ADMIN_USERNAME} machine commission ${newMachine.system_id} enable_ssh=1 skip_bmc_config=1 skip_networking=1 skip_storage=1`,
2326
- {
2327
- silent: true,
2328
- },
2329
- );
2330
- } catch (error) {
2331
- logger.error(error, error.stack);
2332
- }
2333
- }
2334
- // if (discoveries.length > 0) {
2335
- // shellExec(
2336
- // `maas ${process.env.MAAS_ADMIN_USERNAME} machines read | jq '.[] | {system_id: .interface_set[0].system_id, hostname, status_name, mac_address: .interface_set[0].mac_address}'`,
2337
- // );
2338
- // }
2339
- await timer(1000);
2340
- monitor();
2341
- };
2342
- // shellExec(`node bin/deploy open-virtual-root ${architecture.match('amd') ? 'amd64' : 'arm64'} ${nfsHost}`);
2343
- machines = [];
2344
- shellExec(`node bin/deploy maas clear`);
2345
- monitor();
2346
- break;
2347
- }
2348
-
2349
1230
  case 'nfs': {
2350
1231
  // Daemon RPC NFSv3. ports:
2351
1232
 
@@ -2421,137 +1302,6 @@ udp-port = 32766
2421
1302
  shellExec(`sudo systemctl restart nfs-server`);
2422
1303
  break;
2423
1304
  }
2424
- case 'update-virtual-root': {
2425
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
2426
- const IP_ADDRESS = getLocalIPv4Address();
2427
- const architecture = process.argv[3];
2428
- const host = process.argv[4];
2429
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
2430
- const ipaddr = process.env.RPI4_IP;
2431
- const gatewayip = process.env.GATEWAY_IP;
2432
- await updateVirtualRoot({
2433
- IP_ADDRESS,
2434
- architecture,
2435
- host,
2436
- nfsHostPath,
2437
- ipaddr,
2438
- update: true,
2439
- gatewayip,
2440
- });
2441
- break;
2442
- }
2443
- case 'open-virtual-root': {
2444
- dotenv.config({ path: `${getUnderpostRootPath()}/.env`, override: true });
2445
- const IP_ADDRESS = getLocalIPv4Address();
2446
- const architecture = process.argv[3];
2447
- const host = process.argv[4];
2448
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
2449
- const gatewayip = process.env.GATEWAY_IP;
2450
- shellExec(`sudo dnf install -y iptables-legacy`);
2451
- shellExec(`sudo dnf install -y debootstrap`);
2452
- shellExec(`sudo dnf install kernel-modules-extra-$(uname -r)`);
2453
- switch (architecture) {
2454
- case 'arm64':
2455
- shellExec(`sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes`);
2456
-
2457
- break;
2458
-
2459
- default:
2460
- break;
2461
- }
2462
-
2463
- shellExec(`sudo modprobe binfmt_misc`);
2464
- shellExec(`sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc`);
2465
-
2466
- if (process.argv.includes('build')) {
2467
- shellExec(`mkdir -p ${nfsHostPath}`);
2468
- let cmd;
2469
- switch (host) {
2470
- case 'rpi4mb':
2471
- shellExec(`sudo rm -rf ${nfsHostPath}/*`);
2472
- shellExec(`sudo chown -R root:root ${nfsHostPath}`);
2473
- cmd = [
2474
- `sudo debootstrap`,
2475
- `--arch=arm64`,
2476
- `--variant=minbase`,
2477
- `--foreign`, // arm64 on amd64
2478
- [`noble`, `jammy`][0],
2479
- nfsHostPath,
2480
- `http://ports.ubuntu.com/ubuntu-ports/`,
2481
- ];
2482
- break;
2483
-
2484
- default:
2485
- break;
2486
- }
2487
- shellExec(cmd.join(' '));
2488
-
2489
- shellExec(`sudo podman create --name extract multiarch/qemu-user-static`);
2490
- shellExec(`podman ps -a`);
2491
- shellExec(`sudo podman cp extract:/usr/bin/qemu-aarch64-static ${nfsHostPath}/usr/bin/`);
2492
- shellExec(`sudo podman rm extract`);
2493
- shellExec(`podman ps -a`);
2494
-
2495
- switch (host) {
2496
- case 'rpi4mb':
2497
- shellExec(`file ${nfsHostPath}/bin/bash`); // expected: ELF 64-bit LSB pie executable, ARM aarch64 …
2498
- break;
2499
-
2500
- default:
2501
- break;
2502
- }
2503
-
2504
- shellExec(`sudo chroot ${nfsHostPath} /usr/bin/qemu-aarch64-static /bin/bash <<'EOF'
2505
- /debootstrap/debootstrap --second-stage
2506
- EOF`);
2507
- }
2508
- if (process.argv.includes('mount')) {
2509
- shellExec(`sudo mount --bind /proc ${nfsHostPath}/proc`);
2510
- shellExec(`sudo mount --bind /sys ${nfsHostPath}/sys`);
2511
- shellExec(`sudo mount --rbind /dev ${nfsHostPath}/dev`);
2512
- shellExec(`sudo mount --rbind /dev/pts ${nfsHostPath}/dev/pts`);
2513
- shellExec(`sudo mount --bind /run ${nfsHostPath}/run`);
2514
- }
2515
-
2516
- if (process.argv.includes('build')) {
2517
- switch (host) {
2518
- case 'rpi4mb':
2519
- const ipaddr = process.env.RPI4_IP;
2520
-
2521
- await updateVirtualRoot({
2522
- IP_ADDRESS,
2523
- architecture,
2524
- host,
2525
- nfsHostPath,
2526
- ipaddr,
2527
- gatewayip,
2528
- });
2529
-
2530
- break;
2531
-
2532
- default:
2533
- break;
2534
- }
2535
- }
2536
- // if (process.argv.includes('mount')) {
2537
- // shellExec(`sudo mount --bind /lib/modules ${nfsHostPath}/lib/modules`);
2538
- // }
2539
-
2540
- break;
2541
- }
2542
-
2543
- case 'close-virtual-root': {
2544
- const architecture = process.argv[3];
2545
- const host = process.argv[4];
2546
- const nfsHostPath = `${process.env.NFS_EXPORT_PATH}/${host}`;
2547
- shellExec(`sudo umount ${nfsHostPath}/proc`);
2548
- shellExec(`sudo umount ${nfsHostPath}/sys`);
2549
- shellExec(`sudo umount ${nfsHostPath}/dev/pts`);
2550
- shellExec(`sudo umount ${nfsHostPath}/dev`);
2551
- shellExec(`sudo umount ${nfsHostPath}/run`);
2552
- // shellExec(`sudo umount ${nfsHostPath}/lib/modules`);
2553
- break;
2554
- }
2555
1305
 
2556
1306
  case 'mount': {
2557
1307
  const mounts = shellExec(`mount`).split(`\n`);
@@ -2574,10 +1324,10 @@ EOF`);
2574
1324
 
2575
1325
  case 'create-ports': {
2576
1326
  const cmd = [];
2577
- const ipaddr = getLocalIPv4Address();
1327
+ const commissioningDeviceIp = getLocalIPv4Address();
2578
1328
  for (const port of ['5240']) {
2579
1329
  const name = 'maas';
2580
- cmd.push(`${name}:${port}-${port}:${ipaddr}`);
1330
+ cmd.push(`${name}:${port}-${port}:${commissioningDeviceIp}`);
2581
1331
  }
2582
1332
  pbcopy(`node engine-private/r create-port ${cmd}`);
2583
1333
  break;