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/README.md +2 -2
- package/bin/deploy.js +2 -1252
- package/cli.md +24 -16
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
- package/manifests/maas/device-scan.sh +43 -0
- package/manifests/maas/maas-setup.sh +81 -26
- package/manifests/maas/nat-iptables.sh +26 -0
- package/package.json +1 -1
- package/src/cli/baremetal.js +1233 -46
- package/src/cli/cloud-init.js +537 -0
- package/src/cli/index.js +15 -10
- package/src/index.js +26 -16
- package/src/server/runtime.js +0 -5
- package/src/server/ssl.js +1 -12
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { shellExec } from '../server/process.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import UnderpostBaremetal from './baremetal.js';
|
|
5
|
+
import { loggerFactory } from '../server/logger.js';
|
|
6
|
+
import { getNpmRootPath } from '../server/conf.js';
|
|
7
|
+
|
|
8
|
+
dotenv.config();
|
|
9
|
+
|
|
10
|
+
const logger = loggerFactory(import.meta);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @class UnderpostCloudInit
|
|
14
|
+
* @description Manages the generation and deployment of cloud-init configuration files
|
|
15
|
+
* and associated scripts for baremetal provisioning. This class provides methods
|
|
16
|
+
* to build various shell scripts and a cloud-init configuration file tailored
|
|
17
|
+
* for MAAS (Metal as a Service) integration.
|
|
18
|
+
*/
|
|
19
|
+
class UnderpostCloudInit {
|
|
20
|
+
static API = {
|
|
21
|
+
/**
|
|
22
|
+
* @method buildTools
|
|
23
|
+
* @description Builds and writes various shell scripts and configuration files
|
|
24
|
+
* to the NFS host path, which are then used by the target baremetal machine
|
|
25
|
+
* during the cloud-init process.
|
|
26
|
+
* @param {object} params - The parameters for building the tools.
|
|
27
|
+
* @param {string} params.workflowId - The identifier for the specific workflow configuration.
|
|
28
|
+
* @param {string} params.nfsHostPath - The base path on the NFS host where tools will be written.
|
|
29
|
+
* @param {string} params.hostname - The hostname of the target baremetal machine.
|
|
30
|
+
* @param {object} params.callbackMetaData - Metadata about the callback, used for dynamic configuration.
|
|
31
|
+
* @param {boolean} params.dev - Development mode flag.
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
buildTools({ workflowId, nfsHostPath, hostname, callbackMetaData, dev }) {
|
|
35
|
+
// Destructure workflow configuration for easier access.
|
|
36
|
+
const { systemProvisioning, chronyc, networkInterfaceName, debootstrap } =
|
|
37
|
+
UnderpostBaremetal.API.workflowsConfig[workflowId];
|
|
38
|
+
const { timezone, chronyConfPath } = chronyc;
|
|
39
|
+
// Define the specific directory for underpost tools within the NFS host path.
|
|
40
|
+
const nfsHostToolsPath = `${nfsHostPath}/underpost`;
|
|
41
|
+
|
|
42
|
+
// Determine the root path for npm and underpost based on development mode.
|
|
43
|
+
const npmRoot = getNpmRootPath();
|
|
44
|
+
const underpostRoot = dev === true ? '.' : `${npmRoot}/underpost`;
|
|
45
|
+
|
|
46
|
+
// Use a switch statement to handle different system provisioning types.
|
|
47
|
+
switch (systemProvisioning) {
|
|
48
|
+
case 'ubuntu': {
|
|
49
|
+
// Ensure the target directory for tools is clean and exists.
|
|
50
|
+
if (fs.existsSync(`${nfsHostToolsPath}`)) fs.removeSync(`${nfsHostToolsPath}`);
|
|
51
|
+
fs.mkdirSync(`${nfsHostToolsPath}`, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// Build and write the date configuration script.
|
|
54
|
+
logger.info('Build', `${nfsHostToolsPath}/date.sh`);
|
|
55
|
+
fs.writeFileSync(
|
|
56
|
+
`${nfsHostToolsPath}/date.sh`,
|
|
57
|
+
UnderpostBaremetal.API.stepsRender(
|
|
58
|
+
UnderpostBaremetal.API.systemProvisioningFactory[systemProvisioning].timezone({
|
|
59
|
+
timezone,
|
|
60
|
+
chronyConfPath,
|
|
61
|
+
}),
|
|
62
|
+
false,
|
|
63
|
+
),
|
|
64
|
+
'utf8',
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Build and write the keyboard configuration script.
|
|
68
|
+
logger.info('Build', `${nfsHostToolsPath}/keyboard.sh`);
|
|
69
|
+
fs.writeFileSync(
|
|
70
|
+
`${nfsHostToolsPath}/keyboard.sh`,
|
|
71
|
+
UnderpostBaremetal.API.stepsRender(
|
|
72
|
+
UnderpostBaremetal.API.systemProvisioningFactory[systemProvisioning].keyboard(),
|
|
73
|
+
false,
|
|
74
|
+
),
|
|
75
|
+
'utf8',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Build and write the hosts file configuration script.
|
|
79
|
+
logger.info('Build', `${nfsHostToolsPath}/host.sh`);
|
|
80
|
+
fs.writeFileSync(
|
|
81
|
+
`${nfsHostToolsPath}/host.sh`,
|
|
82
|
+
`echo -e "127.0.0.1 localhost\n127.0.1.1 ${hostname}" | tee -a /etc/hosts`,
|
|
83
|
+
'utf8',
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Build and write the DNS configuration script.
|
|
87
|
+
logger.info('Build', `${nfsHostToolsPath}/dns.sh`);
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
`${nfsHostToolsPath}/dns.sh`,
|
|
90
|
+
`rm /etc/resolv.conf
|
|
91
|
+
echo 'nameserver 8.8.8.8' > /run/systemd/resolve/stub-resolv.conf
|
|
92
|
+
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf`,
|
|
93
|
+
'utf8',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Build and write the main startup script for cloud-init.
|
|
97
|
+
logger.info('Build', `${nfsHostToolsPath}/start.sh`);
|
|
98
|
+
fs.writeFileSync(
|
|
99
|
+
`${nfsHostToolsPath}/start.sh`,
|
|
100
|
+
`#!/bin/bash
|
|
101
|
+
set -x
|
|
102
|
+
# sudo cloud-init --all-stages
|
|
103
|
+
${UnderpostBaremetal.API.stepsRender(
|
|
104
|
+
[
|
|
105
|
+
`/underpost/date.sh`,
|
|
106
|
+
`sleep 3`,
|
|
107
|
+
`/underpost/reset.sh`,
|
|
108
|
+
`sleep 3`,
|
|
109
|
+
`cloud-init init --local`,
|
|
110
|
+
`sleep 3`,
|
|
111
|
+
`cloud-init init`,
|
|
112
|
+
`sleep 3`,
|
|
113
|
+
`cloud-init modules --mode=config`,
|
|
114
|
+
`sleep 3`,
|
|
115
|
+
`cloud-init modules --mode=final`,
|
|
116
|
+
`sleep 3`,
|
|
117
|
+
`/underpost/enlistment.sh`,
|
|
118
|
+
],
|
|
119
|
+
false,
|
|
120
|
+
)}`,
|
|
121
|
+
'utf8',
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Build and write the cloud-init reset script.
|
|
125
|
+
logger.info('Build', `${nfsHostToolsPath}/reset.sh`);
|
|
126
|
+
fs.writeFileSync(
|
|
127
|
+
`${nfsHostToolsPath}/reset.sh`,
|
|
128
|
+
`sudo cloud-init clean --seed --configs all --machine-id # --logs
|
|
129
|
+
sudo rm -rf /var/lib/cloud/*
|
|
130
|
+
echo '' > /var/log/cloud-init.log
|
|
131
|
+
echo '' > /var/log/cloud-init-output.log`,
|
|
132
|
+
'utf8',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Build and write the cloud-init help script.
|
|
136
|
+
logger.info('Build', `${nfsHostToolsPath}/help.sh`);
|
|
137
|
+
fs.writeFileSync(
|
|
138
|
+
`${nfsHostToolsPath}/help.sh`,
|
|
139
|
+
`echo "=== Cloud init utils ==="
|
|
140
|
+
echo "sudo cloud-init --all-stages"
|
|
141
|
+
echo "sudo cloud-init clean --logs --seed --configs all --machine-id --reboot"
|
|
142
|
+
echo "sudo cloud-init init --local"
|
|
143
|
+
echo "sudo cloud-init init"
|
|
144
|
+
echo "sudo cloud-init modules --mode=config"
|
|
145
|
+
echo "sudo cloud-init modules --mode=final"`,
|
|
146
|
+
'utf8',
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Build and write the test script for verifying configuration.
|
|
150
|
+
logger.info('Build', `${nfsHostToolsPath}/test.sh`);
|
|
151
|
+
fs.writeFileSync(
|
|
152
|
+
`${nfsHostToolsPath}/test.sh`,
|
|
153
|
+
`echo -e "\n=== Current date/time ==="
|
|
154
|
+
date '+%Y-%m-%d %H:%M:%S'
|
|
155
|
+
echo -e "\n=== Keyboard layout ==="
|
|
156
|
+
cat /etc/default/keyboard
|
|
157
|
+
echo -e "\n=== Registered users ==="
|
|
158
|
+
cut -d: -f1 /etc/passwd`,
|
|
159
|
+
'utf8',
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Build and write the shutdown script.
|
|
163
|
+
logger.info('Build', `${nfsHostToolsPath}/shutdown.sh`);
|
|
164
|
+
fs.writeFileSync(`${nfsHostToolsPath}/shutdown.sh`, `sudo shutdown -h now`, 'utf8');
|
|
165
|
+
|
|
166
|
+
// Build and write the MAC address retrieval script.
|
|
167
|
+
logger.info('Build', `${nfsHostToolsPath}/mac.sh`);
|
|
168
|
+
fs.writeFileSync(
|
|
169
|
+
`${nfsHostToolsPath}/mac.sh`,
|
|
170
|
+
`echo "$(cat /sys/class/net/${networkInterfaceName}/address)" > /underpost/mac`,
|
|
171
|
+
'utf8',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Copy the device scan script from manifests.
|
|
175
|
+
logger.info('Build', `${nfsHostToolsPath}/device_scan.sh`);
|
|
176
|
+
fs.copySync(`${underpostRoot}/manifests/maas/device-scan.sh`, `${nfsHostToolsPath}/device_scan.sh`);
|
|
177
|
+
|
|
178
|
+
// Build and write the config path script.
|
|
179
|
+
logger.info('Build', `${nfsHostToolsPath}/config-path.sh`);
|
|
180
|
+
fs.writeFileSync(`${nfsHostToolsPath}/config-path.sh`, `echo "/etc/cloud/cloud.cfg.d/90_maas.cfg"`, 'utf8');
|
|
181
|
+
|
|
182
|
+
// Build and write the MAAS enlistment script.
|
|
183
|
+
logger.info('Build', `${nfsHostToolsPath}/enlistment.sh`);
|
|
184
|
+
fs.writeFileSync(
|
|
185
|
+
`${nfsHostToolsPath}/enlistment.sh`,
|
|
186
|
+
`#!/bin/bash
|
|
187
|
+
set -x
|
|
188
|
+
|
|
189
|
+
# ------------------------------------------------------------
|
|
190
|
+
# Step: Commission a machine in MAAS using OAuth1 authentication
|
|
191
|
+
# ------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
MACHINE_ID=$(cat /underpost/system-id)
|
|
194
|
+
CONSUMER_KEY=$(cat /underpost/consumer-key)
|
|
195
|
+
TOKEN_KEY=$(cat /underpost/token-key)
|
|
196
|
+
TOKEN_SECRET=$(cat /underpost/token-secret)
|
|
197
|
+
|
|
198
|
+
echo ">>> Starting MAAS machine commissioning for system_id: $MACHINE_ID …"
|
|
199
|
+
|
|
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
|
+
-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 $?"`,
|
|
218
|
+
'utf8',
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Import SSH keys for root user.
|
|
222
|
+
logger.info('Import ssh keys');
|
|
223
|
+
shellExec(`sudo rm -rf ${nfsHostPath}/root/.ssh`);
|
|
224
|
+
shellExec(`sudo rm -rf ${nfsHostPath}/home/root/.ssh`); // Ensure home root .ssh is also clean.
|
|
225
|
+
logger.info('Copy', `/root/.ssh -> ${nfsHostPath}/root/.ssh`);
|
|
226
|
+
fs.copySync(`/root/.ssh`, `${nfsHostPath}/root/.ssh`);
|
|
227
|
+
|
|
228
|
+
// Enable execution permissions for all generated scripts and run a test.
|
|
229
|
+
logger.info('Enable tools execution and test');
|
|
230
|
+
UnderpostBaremetal.API.crossArchRunner({
|
|
231
|
+
nfsHostPath,
|
|
232
|
+
debootstrapArch: debootstrap.image.architecture,
|
|
233
|
+
callbackMetaData,
|
|
234
|
+
steps: [
|
|
235
|
+
`chmod +x /underpost/date.sh`,
|
|
236
|
+
`chmod +x /underpost/keyboard.sh`,
|
|
237
|
+
`chmod +x /underpost/dns.sh`,
|
|
238
|
+
`chmod +x /underpost/help.sh`,
|
|
239
|
+
`chmod +x /underpost/config-path.sh`,
|
|
240
|
+
`chmod +x /underpost/host.sh`,
|
|
241
|
+
`chmod +x /underpost/test.sh`,
|
|
242
|
+
`chmod +x /underpost/start.sh`,
|
|
243
|
+
`chmod +x /underpost/reset.sh`,
|
|
244
|
+
`chmod +x /underpost/shutdown.sh`,
|
|
245
|
+
`chmod +x /underpost/device_scan.sh`,
|
|
246
|
+
`chmod +x /underpost/mac.sh`,
|
|
247
|
+
`chmod +x /underpost/enlistment.sh`,
|
|
248
|
+
`sudo chmod 700 ~/.ssh/`, // Set secure permissions for .ssh directory.
|
|
249
|
+
`sudo chmod 600 ~/.ssh/authorized_keys`, // Set secure permissions for authorized_keys.
|
|
250
|
+
`sudo chmod 644 ~/.ssh/known_hosts`, // Set permissions for known_hosts.
|
|
251
|
+
`sudo chmod 600 ~/.ssh/id_rsa`, // Set secure permissions for private key.
|
|
252
|
+
`sudo chmod 600 /etc/ssh/ssh_host_ed25519_key`, // Set secure permissions for host key.
|
|
253
|
+
`chown -R root:root ~/.ssh`, // Ensure root owns the .ssh directory.
|
|
254
|
+
`/underpost/test.sh`, // Run the test script to verify setup.
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
// Throw an error if an unsupported system provisioning type is provided.
|
|
262
|
+
throw new Error('Invalid system provisioning: ' + systemProvisioning);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @method configFactory
|
|
268
|
+
* @description Generates the cloud-init configuration file (`90_maas.cfg`)
|
|
269
|
+
* for MAAS integration. This configuration includes hostname, network settings,
|
|
270
|
+
* user accounts, SSH keys, timezone, NTP, and various cloud-init modules.
|
|
271
|
+
* @param {object} params - The parameters for generating the configuration.
|
|
272
|
+
* @param {string} params.controlServerIp - The IP address of the MAAS control server.
|
|
273
|
+
* @param {string} params.hostname - The hostname of the target baremetal machine.
|
|
274
|
+
* @param {string} params.commissioningDeviceIp - The IP address to assign to the commissioning device.
|
|
275
|
+
* @param {string} params.gatewayip - The gateway IP address for the network.
|
|
276
|
+
* @param {boolean} params.auth - Flag indicating whether to include MAAS authentication credentials.
|
|
277
|
+
* @param {string} params.mac - The MAC address of the network interface.
|
|
278
|
+
* @param {string} params.timezone - The timezone to set for the machine.
|
|
279
|
+
* @param {string} params.chronyConfPath - The path to the Chrony configuration file.
|
|
280
|
+
* @param {string} params.networkInterfaceName - The name of the primary network interface.
|
|
281
|
+
* @param {object} [authCredentials={}] - Optional MAAS authentication credentials.
|
|
282
|
+
* @param {string} [path='/etc/cloud/cloud.cfg.d/90_maas.cfg'] - The target path for the cloud-init configuration file.
|
|
283
|
+
* @returns {string} The generated cloud-init configuration content.
|
|
284
|
+
*/
|
|
285
|
+
configFactory(
|
|
286
|
+
{
|
|
287
|
+
controlServerIp,
|
|
288
|
+
hostname,
|
|
289
|
+
commissioningDeviceIp,
|
|
290
|
+
gatewayip,
|
|
291
|
+
auth,
|
|
292
|
+
mac,
|
|
293
|
+
timezone,
|
|
294
|
+
chronyConfPath,
|
|
295
|
+
networkInterfaceName,
|
|
296
|
+
},
|
|
297
|
+
authCredentials = { consumer_key: '', consumer_secret: '', token_key: '', token_secret: '' },
|
|
298
|
+
path = '/etc/cloud/cloud.cfg.d/90_maas.cfg',
|
|
299
|
+
) {
|
|
300
|
+
const { consumer_key, consumer_secret, token_key, token_secret } = authCredentials;
|
|
301
|
+
// Configure cloud-init for MAAS using a heredoc string.
|
|
302
|
+
return `cat <<EOF_MAAS_CFG > ${path}
|
|
303
|
+
#cloud-config
|
|
304
|
+
|
|
305
|
+
hostname: ${hostname}
|
|
306
|
+
fqdn: ${hostname}.maas
|
|
307
|
+
# prefer_fqdn_over_hostname: true
|
|
308
|
+
# metadata_url: http://${controlServerIp}:5240/MAAS/metadata
|
|
309
|
+
# metadata_url: http://${controlServerIp}:5248/MAAS/metadata
|
|
310
|
+
|
|
311
|
+
# Check:
|
|
312
|
+
# /MAAS/metadata/latest/enlist-preseed/?op=get_enlist_preseed
|
|
313
|
+
|
|
314
|
+
# Debug:
|
|
315
|
+
# https://maas.io/docs/how-to-use-logging
|
|
316
|
+
|
|
317
|
+
datasource_list: [ MAAS ]
|
|
318
|
+
datasource:
|
|
319
|
+
MAAS:
|
|
320
|
+
metadata_url: http://${controlServerIp}:5240/MAAS/metadata/
|
|
321
|
+
${
|
|
322
|
+
// Conditionally include authentication details if 'auth' flag is true.
|
|
323
|
+
!auth
|
|
324
|
+
? ''
|
|
325
|
+
: `consumer_key: ${consumer_key}
|
|
326
|
+
consumer_secret: ${consumer_secret}
|
|
327
|
+
token_key: ${token_key}
|
|
328
|
+
token_secret: ${token_secret}`
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
users:
|
|
333
|
+
- name: ${process.env.MAAS_ADMIN_USERNAME}
|
|
334
|
+
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
|
335
|
+
shell: /bin/bash
|
|
336
|
+
lock_passwd: false
|
|
337
|
+
groups: sudo,users,admin,wheel,lxd
|
|
338
|
+
plain_text_passwd: '${process.env.MAAS_ADMIN_USERNAME}'
|
|
339
|
+
ssh_authorized_keys:
|
|
340
|
+
- ${fs.readFileSync(`/home/dd/engine/engine-private/deploy/id_rsa.pub`, 'utf8')}
|
|
341
|
+
|
|
342
|
+
# manage_resolv_conf: true
|
|
343
|
+
# resolv_conf:
|
|
344
|
+
# nameservers: [8.8.8.8]
|
|
345
|
+
|
|
346
|
+
# keyboard:
|
|
347
|
+
# layout: es
|
|
348
|
+
|
|
349
|
+
# check timedatectl on hostname
|
|
350
|
+
# timezone: America/Santiago
|
|
351
|
+
timezone: ${timezone}
|
|
352
|
+
|
|
353
|
+
ntp:
|
|
354
|
+
enabled: true
|
|
355
|
+
servers:
|
|
356
|
+
- ${process.env.MAAS_NTP_SERVER}
|
|
357
|
+
ntp_client: chrony
|
|
358
|
+
config:
|
|
359
|
+
confpath: ${chronyConfPath}
|
|
360
|
+
|
|
361
|
+
# ssh:
|
|
362
|
+
# allow-pw: false
|
|
363
|
+
# install-server: true
|
|
364
|
+
|
|
365
|
+
# ssh_pwauth: false
|
|
366
|
+
|
|
367
|
+
package_update: true
|
|
368
|
+
package_upgrade: true
|
|
369
|
+
packages:
|
|
370
|
+
- git
|
|
371
|
+
- htop
|
|
372
|
+
- snapd
|
|
373
|
+
- chrony
|
|
374
|
+
- lldpd
|
|
375
|
+
- lshw
|
|
376
|
+
|
|
377
|
+
resize_rootfs: false
|
|
378
|
+
growpart:
|
|
379
|
+
mode: "off"
|
|
380
|
+
network:
|
|
381
|
+
version: 2
|
|
382
|
+
ethernets:
|
|
383
|
+
${networkInterfaceName}:
|
|
384
|
+
match:
|
|
385
|
+
macaddress: "${mac}"
|
|
386
|
+
mtu: 1500
|
|
387
|
+
set-name: ${networkInterfaceName}
|
|
388
|
+
dhcp4: false
|
|
389
|
+
addresses:
|
|
390
|
+
- ${commissioningDeviceIp}/24
|
|
391
|
+
routes:
|
|
392
|
+
- to: default
|
|
393
|
+
via: ${gatewayip}
|
|
394
|
+
# gateway4: ${gatewayip}
|
|
395
|
+
nameservers:
|
|
396
|
+
addresses:
|
|
397
|
+
- ${process.env.MAAS_DNS}
|
|
398
|
+
|
|
399
|
+
final_message: "====== Cloud init finished ======"
|
|
400
|
+
|
|
401
|
+
# power_state:
|
|
402
|
+
# mode: reboot
|
|
403
|
+
# message: Rebooting after initial setup
|
|
404
|
+
# timeout: 30
|
|
405
|
+
# condition: True
|
|
406
|
+
|
|
407
|
+
bootcmd:
|
|
408
|
+
- echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
|
|
409
|
+
- echo "Init bootcmd"
|
|
410
|
+
- echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
|
|
411
|
+
${UnderpostBaremetal.API.stepsRender(
|
|
412
|
+
[`/underpost/dns.sh`, `/underpost/host.sh`, `/underpost/mac.sh`, `cat /underpost/mac`],
|
|
413
|
+
true,
|
|
414
|
+
)}
|
|
415
|
+
runcmd:
|
|
416
|
+
- echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
|
|
417
|
+
- echo "Init runcmd"
|
|
418
|
+
- echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
|
|
419
|
+
|
|
420
|
+
# If this is set, 'root' will not be able to ssh in and they
|
|
421
|
+
# will get a message to login instead as the default $user
|
|
422
|
+
disable_root: true
|
|
423
|
+
|
|
424
|
+
# This will cause the set+update hostname module to not operate (if true)
|
|
425
|
+
preserve_hostname: false
|
|
426
|
+
|
|
427
|
+
# The modules that run in the 'init' stage
|
|
428
|
+
cloud_init_modules:
|
|
429
|
+
- migrator
|
|
430
|
+
- seed_random
|
|
431
|
+
- bootcmd
|
|
432
|
+
- write-files
|
|
433
|
+
- growpart
|
|
434
|
+
- resizefs
|
|
435
|
+
- set_hostname
|
|
436
|
+
- update_hostname
|
|
437
|
+
- update_etc_hosts
|
|
438
|
+
- ca-certs
|
|
439
|
+
- rsyslog
|
|
440
|
+
- users-groups
|
|
441
|
+
- ssh
|
|
442
|
+
|
|
443
|
+
# The modules that run in the 'config' stage
|
|
444
|
+
cloud_config_modules:
|
|
445
|
+
# Emit the cloud config ready event
|
|
446
|
+
# this can be used by upstart jobs for 'start on cloud-config'.
|
|
447
|
+
- emit_upstart
|
|
448
|
+
- disk_setup
|
|
449
|
+
- mounts
|
|
450
|
+
- ssh-import-id
|
|
451
|
+
- locale
|
|
452
|
+
- set-passwords
|
|
453
|
+
- grub-dpkg
|
|
454
|
+
- apt-pipelining
|
|
455
|
+
- apt-configure
|
|
456
|
+
- package-update-upgrade-install
|
|
457
|
+
- landscape
|
|
458
|
+
- timezone
|
|
459
|
+
- puppet
|
|
460
|
+
- chef
|
|
461
|
+
- salt-minion
|
|
462
|
+
- mcollective
|
|
463
|
+
- disable-ec2-metadata
|
|
464
|
+
- runcmd
|
|
465
|
+
- byobu
|
|
466
|
+
- ssh-import-id
|
|
467
|
+
- ntp
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
# phone_home:
|
|
471
|
+
# url: "http://${controlServerIp}:5240/MAAS/metadata/v1/?op=phone_home"
|
|
472
|
+
# post: all
|
|
473
|
+
# tries: 3
|
|
474
|
+
|
|
475
|
+
# The modules that run in the 'final' stage
|
|
476
|
+
cloud_final_modules:
|
|
477
|
+
- rightscale_userdata
|
|
478
|
+
- scripts-vendor
|
|
479
|
+
- scripts-per-once
|
|
480
|
+
- scripts-per-boot
|
|
481
|
+
# - scripts-per-instance
|
|
482
|
+
- scripts-user
|
|
483
|
+
- ssh-authkey-fingerprints
|
|
484
|
+
- keys-to-console
|
|
485
|
+
# - phone-home
|
|
486
|
+
- final-message
|
|
487
|
+
- power-state-change
|
|
488
|
+
EOF_MAAS_CFG`;
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @method authCredentialsFactory
|
|
493
|
+
* @description Retrieves MAAS API key credentials from the MAAS CLI.
|
|
494
|
+
* This method parses the output of `maas apikey` to extract the consumer key,
|
|
495
|
+
* consumer secret, token key, and token secret.
|
|
496
|
+
* @returns {object} An object containing the MAAS authentication credentials.
|
|
497
|
+
* @throws {Error} If the MAAS API key format is invalid.
|
|
498
|
+
*/
|
|
499
|
+
authCredentialsFactory() {
|
|
500
|
+
// Expected formats:
|
|
501
|
+
// <consumer_key>:<consumer_token>:<secret> (older format)
|
|
502
|
+
// <consumer_key>:<consumer_secret>:<token_key>:<token_secret> (newer format)
|
|
503
|
+
// Commands used to generate API keys:
|
|
504
|
+
// maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}
|
|
505
|
+
// maas ${process.env.MAAS_ADMIN_USERNAME} account create-authorisation-token
|
|
506
|
+
// maas apikey --generate --username ${process.env.MAAS_ADMIN_USERNAME}
|
|
507
|
+
// Reference: https://github.com/CanonicalLtd/maas-docs/issues/647
|
|
508
|
+
|
|
509
|
+
const parts = shellExec(`maas apikey --with-names --username ${process.env.MAAS_ADMIN_USERNAME}`, {
|
|
510
|
+
stdout: true,
|
|
511
|
+
})
|
|
512
|
+
.trim()
|
|
513
|
+
.split(`\n`)[0] // Take only the first line of output.
|
|
514
|
+
.split(':'); // Split by colon to get individual parts.
|
|
515
|
+
|
|
516
|
+
let consumer_key, consumer_secret, token_key, token_secret;
|
|
517
|
+
|
|
518
|
+
// Determine the format of the API key and assign parts accordingly.
|
|
519
|
+
if (parts.length === 4) {
|
|
520
|
+
[consumer_key, consumer_secret, token_key, token_secret] = parts;
|
|
521
|
+
} else if (parts.length === 3) {
|
|
522
|
+
// Handle older 3-part format, setting consumer_secret as empty.
|
|
523
|
+
[consumer_key, token_key, token_secret] = parts;
|
|
524
|
+
consumer_secret = '""';
|
|
525
|
+
token_secret = token_secret.split(' MAAS consumer')[0].trim(); // Clean up token secret.
|
|
526
|
+
} else {
|
|
527
|
+
// Throw an error if the format is not recognized.
|
|
528
|
+
throw new Error('Invalid token format');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
logger.info('Maas api token generated', { consumer_key, consumer_secret, token_key, token_secret });
|
|
532
|
+
return { consumer_key, consumer_secret, token_key, token_secret };
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export default UnderpostCloudInit;
|
package/src/cli/index.js
CHANGED
|
@@ -349,19 +349,24 @@ program
|
|
|
349
349
|
|
|
350
350
|
// 'baremetal' command: Baremetal server management
|
|
351
351
|
program
|
|
352
|
-
.command('baremetal')
|
|
352
|
+
.command('baremetal [workflow-id] [hostname] [ip-address]')
|
|
353
353
|
.option('--control-server-install', 'Installs the baremetal control server.')
|
|
354
|
-
.option('--control-server-db-init', 'Sets up the database for the baremetal control server.')
|
|
355
|
-
.option('--control-server-db-uninstall', 'Uninstalls the database for the baremetal control server.')
|
|
356
|
-
.option('--control-server-init', 'Initializes the baremetal control server.')
|
|
357
|
-
.option('--control-server-login', 'Logs in as an administrator to the control server.')
|
|
358
354
|
.option('--control-server-uninstall', 'Uninstalls the baremetal control server.')
|
|
359
|
-
.option('--control-server-
|
|
360
|
-
.option('--control-server-
|
|
361
|
-
.option('--
|
|
362
|
-
.option('--
|
|
355
|
+
.option('--control-server-db-install', 'Installs up the database for the baremetal control server.')
|
|
356
|
+
.option('--control-server-db-uninstall', 'Uninstalls the database for the baremetal control server.')
|
|
357
|
+
.option('--commission', 'Init workflow for commissioning a physical machine.')
|
|
358
|
+
.option('--nfs-build', 'Builds an NFS root filesystem for a workflow id config architecture using QEMU emulation.')
|
|
359
|
+
.option('--nfs-mount', 'Mounts the NFS root filesystem for a workflow id config architecture.')
|
|
360
|
+
.option('--nfs-unmount', 'Unmounts the NFS root filesystem for a workflow id config architecture.')
|
|
361
|
+
.option('--nfs-sh', 'Copies QEMU emulation root entrypoint shell command to the clipboard.')
|
|
362
|
+
.option('--cloud-init-update', 'Updates cloud init for a workflow id config architecture.')
|
|
363
|
+
.option('--cloud-init-reset', 'Resets cloud init for a workflow id config architecture.')
|
|
364
|
+
.option('--logs <log-id>', 'Displays logs for log id: dhcp, cloud, machine, cloud-config.')
|
|
363
365
|
.option('--dev', 'Sets the development context environment for baremetal operations.')
|
|
364
|
-
.
|
|
366
|
+
.option('--ls', 'Lists available boot resources and machines.')
|
|
367
|
+
.description(
|
|
368
|
+
'Manages baremetal server operations, including installation, database setup, commissioning, and user management.',
|
|
369
|
+
)
|
|
365
370
|
.action(UnderpostBaremetal.API.callback);
|
|
366
371
|
|
|
367
372
|
export { program };
|