fix: specific node firmware update
This commit is contained in:
@@ -1067,6 +1067,20 @@ class NodeDetailsComponent extends Component {
|
||||
class FirmwareComponent extends Component {
|
||||
constructor(container, viewModel, eventBus) {
|
||||
super(container, viewModel, eventBus);
|
||||
|
||||
console.log('FirmwareComponent: Constructor called');
|
||||
console.log('FirmwareComponent: Container:', container);
|
||||
console.log('FirmwareComponent: Container ID:', container?.id);
|
||||
|
||||
// Check if the dropdown exists in the container
|
||||
if (container) {
|
||||
const dropdown = container.querySelector('#specific-node-select');
|
||||
console.log('FirmwareComponent: Dropdown found in constructor:', !!dropdown);
|
||||
if (dropdown) {
|
||||
console.log('FirmwareComponent: Dropdown tagName:', dropdown.tagName);
|
||||
console.log('FirmwareComponent: Dropdown id:', dropdown.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
@@ -1084,8 +1098,18 @@ class FirmwareComponent extends Component {
|
||||
|
||||
// Setup specific node select change handler
|
||||
const specificNodeSelect = this.findElement('#specific-node-select');
|
||||
console.log('FirmwareComponent: setupEventListeners - specificNodeSelect found:', !!specificNodeSelect);
|
||||
if (specificNodeSelect) {
|
||||
this.addEventListener(specificNodeSelect, 'change', this.handleNodeSelect.bind(this));
|
||||
console.log('FirmwareComponent: specificNodeSelect element:', specificNodeSelect);
|
||||
console.log('FirmwareComponent: specificNodeSelect tagName:', specificNodeSelect.tagName);
|
||||
console.log('FirmwareComponent: specificNodeSelect id:', specificNodeSelect.id);
|
||||
|
||||
// Store the bound handler as an instance property
|
||||
this._boundNodeSelectHandler = this.handleNodeSelect.bind(this);
|
||||
this.addEventListener(specificNodeSelect, 'change', this._boundNodeSelectHandler);
|
||||
console.log('FirmwareComponent: Event listener added to specificNodeSelect');
|
||||
} else {
|
||||
console.warn('FirmwareComponent: specificNodeSelect element not found during setupEventListeners');
|
||||
}
|
||||
|
||||
// Setup deploy button
|
||||
@@ -1114,6 +1138,23 @@ class FirmwareComponent extends Component {
|
||||
this.subscribeToProperty('isUploading', this.updateUploadState.bind(this));
|
||||
}
|
||||
|
||||
mount() {
|
||||
super.mount();
|
||||
|
||||
console.log('FirmwareComponent: Mounting...');
|
||||
|
||||
// Check if the dropdown exists when mounted
|
||||
const dropdown = this.findElement('#specific-node-select');
|
||||
console.log('FirmwareComponent: Mount - dropdown found:', !!dropdown);
|
||||
if (dropdown) {
|
||||
console.log('FirmwareComponent: Mount - dropdown tagName:', dropdown.tagName);
|
||||
console.log('FirmwareComponent: Mount - dropdown id:', dropdown.id);
|
||||
console.log('FirmwareComponent: Mount - dropdown innerHTML:', dropdown.innerHTML);
|
||||
}
|
||||
|
||||
console.log('FirmwareComponent: Mounted successfully');
|
||||
}
|
||||
|
||||
render() {
|
||||
// Initial render is handled by the HTML template
|
||||
this.updateDeployButton();
|
||||
@@ -1131,7 +1172,15 @@ class FirmwareComponent extends Component {
|
||||
|
||||
handleNodeSelect(event) {
|
||||
const nodeIp = event.target.value;
|
||||
console.log('FirmwareComponent: handleNodeSelect called with nodeIp:', nodeIp);
|
||||
console.log('Event:', event);
|
||||
console.log('Event target:', event.target);
|
||||
console.log('Event target value:', event.target.value);
|
||||
|
||||
this.viewModel.setSpecificNode(nodeIp);
|
||||
|
||||
// Also update the deploy button state
|
||||
this.updateDeployButton();
|
||||
}
|
||||
|
||||
async handleDeploy() {
|
||||
@@ -1206,14 +1255,36 @@ class FirmwareComponent extends Component {
|
||||
// Show upload progress area
|
||||
this.showUploadProgress(file, [{ ip: nodeIp, hostname: nodeIp }]);
|
||||
|
||||
// Update progress to show starting
|
||||
this.updateNodeProgress(1, 1, nodeIp, 'Uploading...');
|
||||
|
||||
// Perform single node upload
|
||||
const result = await this.performSingleUpload(file, nodeIp);
|
||||
|
||||
// Update progress to show completion
|
||||
this.updateNodeProgress(1, 1, nodeIp, 'Completed');
|
||||
this.updateOverallProgress(1, 1);
|
||||
|
||||
// Display results
|
||||
this.displayUploadResults([result]);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Failed to upload firmware to node ${nodeIp}:`, error);
|
||||
|
||||
// Update progress to show failure
|
||||
this.updateNodeProgress(1, 1, nodeIp, 'Failed');
|
||||
this.updateOverallProgress(0, 1);
|
||||
|
||||
// Display error results
|
||||
const errorResult = {
|
||||
nodeIp: nodeIp,
|
||||
hostname: nodeIp,
|
||||
success: false,
|
||||
error: error.message,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
this.displayUploadResults([errorResult]);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1298,7 +1369,7 @@ class FirmwareComponent extends Component {
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="overall-progress-bar" style="width: 0%; background-color: #fbbf24;"></div>
|
||||
</div>
|
||||
<span class="progress-text">0/0 Successful (0%)</span>
|
||||
<span class="progress-text">0/${nodes.length} Successful (0%)</span>
|
||||
</div>
|
||||
<div class="progress-summary" id="progress-summary">
|
||||
<span>Status: Preparing upload...</span>
|
||||
@@ -1320,6 +1391,12 @@ class FirmwareComponent extends Component {
|
||||
`;
|
||||
|
||||
container.innerHTML = progressHTML;
|
||||
|
||||
// Initialize progress for single-node uploads
|
||||
if (nodes.length === 1) {
|
||||
const node = nodes[0];
|
||||
this.updateNodeProgress(1, 1, node.ip, 'Pending...');
|
||||
}
|
||||
}
|
||||
|
||||
updateNodeProgress(current, total, nodeIp, status) {
|
||||
@@ -1370,6 +1447,16 @@ class FirmwareComponent extends Component {
|
||||
} else {
|
||||
progressBar.style.backgroundColor = '#fbbf24';
|
||||
}
|
||||
|
||||
// Update progress summary for single-node uploads
|
||||
const progressSummary = this.findElement('#progress-summary');
|
||||
if (progressSummary && totalNodes === 1) {
|
||||
if (successfulUploads === 1) {
|
||||
progressSummary.innerHTML = '<span>Status: Upload completed successfully</span>';
|
||||
} else if (successfulUploads === 0) {
|
||||
progressSummary.innerHTML = '<span>Status: Upload failed</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1382,10 +1469,21 @@ class FirmwareComponent extends Component {
|
||||
const totalCount = results.length;
|
||||
const successRate = Math.round((successCount / totalCount) * 100);
|
||||
|
||||
if (successCount === totalCount) {
|
||||
if (totalCount === 1) {
|
||||
// Single node upload
|
||||
if (successCount === 1) {
|
||||
progressHeader.textContent = `📤 Firmware Upload Complete`;
|
||||
progressSummary.innerHTML = `<span>✅ Upload to ${results[0].hostname || results[0].nodeIp} completed successfully at ${new Date().toLocaleTimeString()}</span>`;
|
||||
} else {
|
||||
progressHeader.textContent = `📤 Firmware Upload Failed`;
|
||||
progressSummary.innerHTML = `<span>❌ Upload to ${results[0].hostname || results[0].nodeIp} failed at ${new Date().toLocaleTimeString()}</span>`;
|
||||
}
|
||||
} else if (successCount === totalCount) {
|
||||
// Multi-node upload - all successful
|
||||
progressHeader.textContent = `📤 Firmware Upload Complete (${successCount}/${totalCount} Successful)`;
|
||||
progressSummary.innerHTML = `<span>✅ All uploads completed successfully at ${new Date().toLocaleTimeString()}</span>`;
|
||||
} else {
|
||||
// Multi-node upload - some failed
|
||||
progressHeader.textContent = `📤 Firmware Upload Results (${successCount}/${totalCount} Successful)`;
|
||||
progressSummary.innerHTML = `<span>⚠️ Upload completed with ${totalCount - successCount} failure(s) at ${new Date().toLocaleTimeString()}</span>`;
|
||||
}
|
||||
@@ -1412,21 +1510,35 @@ class FirmwareComponent extends Component {
|
||||
const targetType = this.viewModel.get('targetType');
|
||||
const specificNodeSelect = this.findElement('#specific-node-select');
|
||||
|
||||
console.log('FirmwareComponent: updateTargetVisibility called with targetType:', targetType);
|
||||
|
||||
if (targetType === 'specific') {
|
||||
specificNodeSelect.style.visibility = 'visible';
|
||||
specificNodeSelect.style.opacity = '1';
|
||||
this.populateNodeSelect();
|
||||
console.log('FirmwareComponent: Showing specific node select');
|
||||
|
||||
// Check if the dropdown exists and is ready
|
||||
if (specificNodeSelect && specificNodeSelect.tagName === 'SELECT') {
|
||||
console.log('FirmwareComponent: Dropdown is ready, populating immediately');
|
||||
this.populateNodeSelect();
|
||||
} else {
|
||||
console.log('FirmwareComponent: Dropdown not ready, delaying population');
|
||||
// Small delay to ensure the dropdown is visible before populating
|
||||
setTimeout(() => {
|
||||
this.populateNodeSelect();
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
specificNodeSelect.style.visibility = 'hidden';
|
||||
specificNodeSelect.style.opacity = '0';
|
||||
console.log('FirmwareComponent: Hiding specific node select');
|
||||
}
|
||||
|
||||
this.updateDeployButton();
|
||||
}
|
||||
|
||||
handleNodeSelect() {
|
||||
this.updateDeployButton();
|
||||
}
|
||||
// Note: handleNodeSelect is already defined above and handles the actual node selection
|
||||
// This duplicate method was causing the issue - removing it
|
||||
|
||||
updateDeployButton() {
|
||||
const deployBtn = this.findElement('#deploy-btn');
|
||||
@@ -1437,7 +1549,19 @@ class FirmwareComponent extends Component {
|
||||
|
||||
populateNodeSelect() {
|
||||
const select = this.findElement('#specific-node-select');
|
||||
if (!select) return;
|
||||
if (!select) {
|
||||
console.warn('FirmwareComponent: populateNodeSelect - select element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (select.tagName !== 'SELECT') {
|
||||
console.warn('FirmwareComponent: populateNodeSelect - element is not a SELECT:', select.tagName);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('FirmwareComponent: populateNodeSelect called');
|
||||
console.log('FirmwareComponent: Select element:', select);
|
||||
console.log('FirmwareComponent: Available nodes:', this.viewModel.get('availableNodes'));
|
||||
|
||||
// Clear existing options
|
||||
select.innerHTML = '<option value="">Select a node...</option>';
|
||||
@@ -1461,6 +1585,27 @@ class FirmwareComponent extends Component {
|
||||
option.textContent = `${node.hostname} (${node.ip})`;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
// Ensure event listener is still bound after repopulating
|
||||
this.ensureNodeSelectListener(select);
|
||||
|
||||
console.log('FirmwareComponent: Node select populated with', availableNodes.length, 'nodes');
|
||||
}
|
||||
|
||||
// Ensure the node select change listener is properly bound
|
||||
ensureNodeSelectListener(select) {
|
||||
if (!select) return;
|
||||
|
||||
// Store the bound handler as an instance property to avoid binding issues
|
||||
if (!this._boundNodeSelectHandler) {
|
||||
this._boundNodeSelectHandler = this.handleNodeSelect.bind(this);
|
||||
}
|
||||
|
||||
// Remove any existing listeners and add the bound one
|
||||
select.removeEventListener('change', this._boundNodeSelectHandler);
|
||||
select.addEventListener('change', this._boundNodeSelectHandler);
|
||||
|
||||
console.log('FirmwareComponent: Node select event listener ensured');
|
||||
}
|
||||
|
||||
updateUploadProgress() {
|
||||
@@ -1704,21 +1849,33 @@ class FirmwareViewComponent extends Component {
|
||||
constructor(container, viewModel, eventBus) {
|
||||
super(container, viewModel, eventBus);
|
||||
|
||||
console.log('FirmwareViewComponent: Constructor called');
|
||||
console.log('FirmwareViewComponent: Container:', container);
|
||||
|
||||
const firmwareContainer = this.findElement('#firmware-container');
|
||||
console.log('FirmwareViewComponent: Firmware container found:', !!firmwareContainer);
|
||||
|
||||
this.firmwareComponent = new FirmwareComponent(
|
||||
this.findElement('#firmware-container'),
|
||||
firmwareContainer,
|
||||
viewModel,
|
||||
eventBus
|
||||
);
|
||||
|
||||
console.log('FirmwareViewComponent: FirmwareComponent created');
|
||||
}
|
||||
|
||||
mount() {
|
||||
super.mount();
|
||||
|
||||
console.log('FirmwareViewComponent: Mounting...');
|
||||
|
||||
// Mount sub-component
|
||||
this.firmwareComponent.mount();
|
||||
|
||||
// Update available nodes
|
||||
this.updateAvailableNodes();
|
||||
|
||||
console.log('FirmwareViewComponent: Mounted successfully');
|
||||
}
|
||||
|
||||
unmount() {
|
||||
@@ -1758,9 +1915,12 @@ class FirmwareViewComponent extends Component {
|
||||
|
||||
async updateAvailableNodes() {
|
||||
try {
|
||||
console.log('FirmwareViewComponent: updateAvailableNodes called');
|
||||
const response = await window.apiClient.getClusterMembers();
|
||||
const nodes = response.members || [];
|
||||
console.log('FirmwareViewComponent: Got nodes:', nodes);
|
||||
this.viewModel.updateAvailableNodes(nodes);
|
||||
console.log('FirmwareViewComponent: Available nodes updated in view model');
|
||||
} catch (error) {
|
||||
console.error('Failed to update available nodes:', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user