genbox 1.0.30 → 1.0.31
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/dist/commands/status.js +84 -29
- package/package.json +1 -1
package/dist/commands/status.js
CHANGED
|
@@ -79,6 +79,72 @@ function sshExec(ip, keyPath, command, timeoutSecs = 10) {
|
|
|
79
79
|
return '';
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
function formatDuration(secs) {
|
|
83
|
+
const mins = Math.floor(secs / 60);
|
|
84
|
+
const remainingSecs = secs % 60;
|
|
85
|
+
return `${mins}m ${remainingSecs}s`;
|
|
86
|
+
}
|
|
87
|
+
function getTimingBreakdown(ip, keyPath) {
|
|
88
|
+
const timing = {
|
|
89
|
+
sshReady: null,
|
|
90
|
+
cloudInit: null,
|
|
91
|
+
total: null,
|
|
92
|
+
};
|
|
93
|
+
// Get total system uptime
|
|
94
|
+
const uptime = sshExec(ip, keyPath, "cat /proc/uptime 2>/dev/null | cut -d' ' -f1 | cut -d'.' -f1");
|
|
95
|
+
if (uptime) {
|
|
96
|
+
timing.total = parseInt(uptime, 10);
|
|
97
|
+
}
|
|
98
|
+
// Get SSH ready time (sshd service start time relative to boot)
|
|
99
|
+
// ActiveEnterTimestampMonotonic gives microseconds since boot when service became active
|
|
100
|
+
const sshdTimestamp = sshExec(ip, keyPath, "systemctl show sshd --property=ActiveEnterTimestampMonotonic 2>/dev/null | cut -d'=' -f2");
|
|
101
|
+
if (sshdTimestamp && sshdTimestamp.trim()) {
|
|
102
|
+
const microseconds = parseInt(sshdTimestamp.trim(), 10);
|
|
103
|
+
if (!isNaN(microseconds) && microseconds > 0) {
|
|
104
|
+
timing.sshReady = Math.round(microseconds / 1000000); // Convert to seconds
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Get cloud-init execution time from logs
|
|
108
|
+
const cloudInitTime = sshExec(ip, keyPath, "sudo grep -oP 'Setup Complete in \\K[0-9]+m [0-9]+s' /var/log/cloud-init-output.log 2>/dev/null | tail -1");
|
|
109
|
+
if (cloudInitTime && cloudInitTime.trim()) {
|
|
110
|
+
// Parse "Xm Ys" format
|
|
111
|
+
const match = cloudInitTime.trim().match(/(\d+)m\s*(\d+)s/);
|
|
112
|
+
if (match) {
|
|
113
|
+
timing.cloudInit = parseInt(match[1], 10) * 60 + parseInt(match[2], 10);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Fallback: calculate cloud-init time from result.json if not in logs
|
|
117
|
+
if (timing.cloudInit === null && timing.sshReady !== null && timing.total !== null) {
|
|
118
|
+
// Get cloud-init finish timestamp
|
|
119
|
+
const resultTimestamp = sshExec(ip, keyPath, "stat -c %Y /run/cloud-init/result.json 2>/dev/null");
|
|
120
|
+
const bootTimestamp = sshExec(ip, keyPath, "date -d \"$(uptime -s)\" +%s 2>/dev/null");
|
|
121
|
+
if (resultTimestamp && bootTimestamp) {
|
|
122
|
+
const resultTime = parseInt(resultTimestamp.trim(), 10);
|
|
123
|
+
const bootTime = parseInt(bootTimestamp.trim(), 10);
|
|
124
|
+
if (!isNaN(resultTime) && !isNaN(bootTime) && resultTime > bootTime) {
|
|
125
|
+
// Cloud-init time = (finish time - boot time) - ssh ready time
|
|
126
|
+
const totalFromBoot = resultTime - bootTime;
|
|
127
|
+
timing.cloudInit = Math.max(0, totalFromBoot - timing.sshReady);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return timing;
|
|
132
|
+
}
|
|
133
|
+
function displayTimingBreakdown(timing) {
|
|
134
|
+
if (timing.sshReady !== null || timing.cloudInit !== null || timing.total !== null) {
|
|
135
|
+
console.log(chalk_1.default.blue('[INFO] === Timing Breakdown ==='));
|
|
136
|
+
if (timing.sshReady !== null) {
|
|
137
|
+
console.log(` SSH Ready: ${formatDuration(timing.sshReady)}`);
|
|
138
|
+
}
|
|
139
|
+
if (timing.cloudInit !== null) {
|
|
140
|
+
console.log(` Cloud-init: ${formatDuration(timing.cloudInit)}`);
|
|
141
|
+
}
|
|
142
|
+
if (timing.total !== null) {
|
|
143
|
+
console.log(` ${chalk_1.default.bold('Total: ' + formatDuration(timing.total))}`);
|
|
144
|
+
}
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
82
148
|
exports.statusCommand = new commander_1.Command('status')
|
|
83
149
|
.description('Check cloud-init progress and service status of a Genbox')
|
|
84
150
|
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
@@ -145,18 +211,24 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
145
211
|
return;
|
|
146
212
|
}
|
|
147
213
|
if (status.includes('running')) {
|
|
148
|
-
// Get
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
const mins = Math.floor(uptimeSecs / 60);
|
|
153
|
-
const secs = uptimeSecs % 60;
|
|
154
|
-
console.log(chalk_1.default.yellow(`[WARN] Cloud-init is still running... (elapsed: ${mins}m ${secs}s)`));
|
|
214
|
+
// Get timing breakdown
|
|
215
|
+
const timing = getTimingBreakdown(target.ipAddress, keyPath);
|
|
216
|
+
if (timing.total !== null) {
|
|
217
|
+
console.log(chalk_1.default.yellow(`[WARN] Cloud-init is still running... (elapsed: ${formatDuration(timing.total)})`));
|
|
155
218
|
}
|
|
156
219
|
else {
|
|
157
220
|
console.log(chalk_1.default.yellow('[WARN] Cloud-init is still running...'));
|
|
158
221
|
}
|
|
159
222
|
console.log('');
|
|
223
|
+
// Show timing breakdown so far
|
|
224
|
+
if (timing.sshReady !== null) {
|
|
225
|
+
console.log(chalk_1.default.blue('[INFO] === Timing So Far ==='));
|
|
226
|
+
console.log(` SSH Ready: ${formatDuration(timing.sshReady)}`);
|
|
227
|
+
if (timing.total !== null) {
|
|
228
|
+
console.log(` Elapsed: ${formatDuration(timing.total)}`);
|
|
229
|
+
}
|
|
230
|
+
console.log('');
|
|
231
|
+
}
|
|
160
232
|
console.log(chalk_1.default.blue('[INFO] Latest setup progress:'));
|
|
161
233
|
// Tail cloud-init output log for progress
|
|
162
234
|
const logOutput = sshExec(target.ipAddress, keyPath, "sudo tail -30 /var/log/cloud-init-output.log 2>/dev/null | grep -E '^===|^#|Progress:|DONE|error|Error|failed|Failed' || sudo tail -20 /var/log/cloud-init-output.log", 15);
|
|
@@ -165,29 +237,12 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
165
237
|
}
|
|
166
238
|
}
|
|
167
239
|
else if (status.includes('done')) {
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const setupTime = sshExec(target.ipAddress, keyPath, "sudo grep -oP 'Setup Complete in \\K[0-9]+m [0-9]+s' /var/log/cloud-init-output.log 2>/dev/null | tail -1");
|
|
172
|
-
if (setupTime && setupTime.trim()) {
|
|
173
|
-
completionTimeFormatted = setupTime.trim();
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
// Fallback: Calculate from cloud-init result.json timestamp vs boot time
|
|
177
|
-
// Get boot timestamp and cloud-init finish timestamp
|
|
178
|
-
const timestamps = sshExec(target.ipAddress, keyPath, "echo $(date -d \"$(uptime -s)\" +%s) $(stat -c %Y /run/cloud-init/result.json 2>/dev/null || echo 0)");
|
|
179
|
-
if (timestamps && timestamps.trim()) {
|
|
180
|
-
const [bootTime, finishTime] = timestamps.trim().split(' ').map(Number);
|
|
181
|
-
if (bootTime && finishTime && finishTime > bootTime) {
|
|
182
|
-
const elapsedSecs = finishTime - bootTime;
|
|
183
|
-
const mins = Math.floor(elapsedSecs / 60);
|
|
184
|
-
const secs = elapsedSecs % 60;
|
|
185
|
-
completionTimeFormatted = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
console.log(chalk_1.default.green(`[SUCCESS] Cloud-init completed!${completionTimeFormatted ? ` (${completionTimeFormatted})` : ''}`));
|
|
240
|
+
// Get timing breakdown
|
|
241
|
+
const timing = getTimingBreakdown(target.ipAddress, keyPath);
|
|
242
|
+
console.log(chalk_1.default.green(`[SUCCESS] Cloud-init completed!${timing.total !== null ? ` (${formatDuration(timing.total)})` : ''}`));
|
|
190
243
|
console.log('');
|
|
244
|
+
// Show detailed timing breakdown
|
|
245
|
+
displayTimingBreakdown(timing);
|
|
191
246
|
// Show Database Stats - only if configured
|
|
192
247
|
let dbContainer = '';
|
|
193
248
|
let dbName = '';
|