From 2c9e4a311a75978844f74ec3775c43f00d4b6075 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 5 Sep 2025 10:58:59 +0200 Subject: [PATCH 1/5] refs #2755. Fixed bug with client params --- .../run-script-assistant.component.ts | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts index 61297c8..90a2c8e 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts @@ -50,15 +50,12 @@ export class RunScriptAssistantComponent implements OnInit{ this.clientData = JSON.parse(params['clientData']); } if (params['runScriptContext']) { - this.runScriptContext = params['runScriptContext']; + this.runScriptContext = JSON.parse(params['runScriptContext']); } }); this.clientId = this.clientData?.length ? this.clientData[0]['@id'] : null; - this.clientData.forEach((client: { selected: boolean; status: string}) => { client.selected = true; }); - - this.selectedClients = this.clientData.filter( - (client: { selected: boolean; status: string }) => client.selected - ); + + this.initializeClientSelection(); this.allSelected = this.clientData.length > 0 && this.clientData.every((client: { selected: boolean }) => client.selected); @@ -66,9 +63,6 @@ export class RunScriptAssistantComponent implements OnInit{ } ngOnInit(): void { - this.route.queryParams.subscribe(params => { - this.runScriptContext = params['runScriptContext'] ? JSON.parse(params['runScriptContext']) : null; - }); } get runScriptTitle(): string { @@ -205,13 +199,21 @@ export class RunScriptAssistantComponent implements OnInit{ } openScheduleModal(): void { - let scope = this.runScriptContext.type; + let scope = this.runScriptContext?.type || 'clients'; let selectedClients = null; - - if ((!this.runScriptContext || this.runScriptContext.type === 'client' || this.selectedClients.length === 1) && this.selectedClients && this.selectedClients.length > 0) { + if (this.selectedClients.length === 0 && this.clientData.length > 0) { + this.updateSelectedClients(); + } + + const isOrganizationalContext = this.runScriptContext?.type && + ['organizational-unit', 'classroom', 'classrooms-group', 'clients-group'].includes(this.runScriptContext.type); + + if (!isOrganizationalContext && this.selectedClients && this.selectedClients.length > 0) { scope = 'clients'; selectedClients = this.selectedClients; + } else if (isOrganizationalContext && this.selectedClients && this.selectedClients.length > 0) { + selectedClients = null; } const dialogRef = this.dialog.open(CreateTaskComponent, { @@ -226,7 +228,6 @@ export class RunScriptAssistantComponent implements OnInit{ }); dialogRef.afterClosed().subscribe(result => { - console.log(result); if (result) { this.http.post(`${this.baseUrl}/command-task-scripts`, { commandTask: result.taskId['@id'], @@ -244,4 +245,35 @@ export class RunScriptAssistantComponent implements OnInit{ } }); } + + private initializeClientSelection(): void { + const context = this.runScriptContext; + + this.clientData.forEach((client: { selected: boolean; status: string}) => { + client.selected = true; + }); + + if (context && typeof context === 'object' && context.type && + ['classroom', 'classrooms-group', 'clients-group'].includes(context.type)) { + this.clientData.forEach((client: { selected: boolean; status: string}) => { + client.selected = false; + }); + } + + else if (context && typeof context === 'object' && context.type === 'client') { + this.clientData.forEach((client: { selected: boolean; status: string; name: string; uuid: string}) => { + client.selected = client.name === context.name || client.uuid === context.uuid || client.uuid === context['@id']; + }); + } + + else if (context && Array.isArray(context)) { + this.clientData.forEach((client: { selected: boolean; status: string; name: string; uuid: string}) => { + client.selected = context.some(ctx => + ctx.name === client.name || ctx.uuid === client.uuid || ctx['@id'] === client.uuid + ); + }); + } + + this.updateSelectedClients(); + } } -- 2.40.1 From 4e22286e0417c0c9c0a417406e6f42627e219e30 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 5 Sep 2025 12:07:10 +0200 Subject: [PATCH 2/5] refs #2764. Updated EFI partition 512 to ddbb --- .../partition-assistant.component.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts index 729da20..cf4a8dd 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/partition-assistant/partition-assistant.component.ts @@ -246,14 +246,18 @@ export class PartitionAssistantComponent implements OnInit, AfterViewInit, OnDes if (partition.partitionNumber === 0) { disk!.totalDiskSize = this.convertBytesToGB(partition.size); } else { + const isFirstPartitionGPT = partition.partitionNumber === 1 && this.partitionCode === 'GPT'; + const hasValidExistingData = partition.size > 0 && partition.partitionCode && partition.filesystem; + const shouldUseEFIDefaults = isFirstPartitionGPT && !hasValidExistingData; + disk!.partitions.push({ uuid: partition.uuid, partitionNumber: partition.partitionNumber, - size: this.convertBytesToGB(partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 512 : partition.size), + size: this.convertBytesToGB(shouldUseEFIDefaults ? 512 : partition.size), memoryUsage: partition.memoryUsage, - partitionCode: partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 'EFI' : this.validatePartitionCode(partition.partitionCode), - filesystem: partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 'FAT32' : partition.filesystem, - sizeBytes: partition.partitionNumber === 1 && this.partitionCode === 'GPT' ? 512 : partition.size, + partitionCode: shouldUseEFIDefaults ? 'EFI' : this.validatePartitionCode(partition.partitionCode), + filesystem: shouldUseEFIDefaults ? 'FAT32' : partition.filesystem, + sizeBytes: shouldUseEFIDefaults ? 512 : partition.size, format: false, color: this.getColorForPartition(partition.partitionNumber), percentage: 0, -- 2.40.1 From 2d9c66919a5a5be90ee04a562c6fbdcdd0da4d50 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 5 Sep 2025 12:12:11 +0200 Subject: [PATCH 3/5] refs #2756. Fixed bug with stored comands --- .../run-script-assistant.component.css | 270 ++++++++++++++++++ .../run-script-assistant.component.html | 176 ++++++++++-- .../run-script-assistant.component.ts | 24 +- 3 files changed, 439 insertions(+), 31 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.css index 615b9b9..9227a68 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.css @@ -533,4 +533,274 @@ mat-spinner { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} + +.script-input-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding: 16px; + background: #f8f9fa; + border-radius: 8px; + border: 1px solid #e9ecef; +} + +.script-input-header h4 { + margin: 0; + display: flex; + align-items: center; + gap: 8px; + color: #2c3e50; + font-weight: 600; +} + +.script-stats { + display: flex; + gap: 16px; + align-items: center; +} + +.stat-item { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: #6c757d; + background: white; + padding: 4px 8px; + border-radius: 4px; + border: 1px solid #e9ecef; +} + +.stat-item.valid { + color: #28a745; + border-color: #28a745; + background: #f8fff9; +} + +.stat-item.invalid { + color: #dc3545; + border-color: #dc3545; + background: #fff8f8; +} + +.stat-item mat-icon { + font-size: 14px; + width: 14px; + height: 14px; +} + +.script-textarea { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 14px; + line-height: 1.5; + background: #f8f9fa; +} + +.detected-params { + background: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 8px; + padding: 16px; + margin: 16px 0; +} + +.detected-params h4 { + margin: 0 0 12px 0; + display: flex; + align-items: center; + gap: 8px; + color: #856404; + font-weight: 600; +} + +.params-grid .mat-mdc-chip-listbox { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.param-chip { + background: #fff !important; + border: 1px solid #ffeaa7 !important; + color: #856404 !important; +} + +.action-buttons { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 16px; +} + +.action-buttons button { + display: flex; + align-items: center; + gap: 8px; +} + +.script-selector-container { + margin-bottom: 20px; +} + +.script-option { + display: flex; + flex-direction: column; + gap: 2px; +} + +.script-name { + font-weight: 500; + color: #2c3e50; +} + +.script-description { + font-size: 12px; + color: #6c757d; + opacity: 0.8; +} + +/* Tarjetas de vista previa */ +.script-preview-container { + margin-top: 20px; +} + +.script-card, .params-card { + margin-bottom: 16px; + border: 1px solid #e3f2fd !important; + border-radius: 12px !important; +} + +.script-card .mat-mdc-card-header, +.params-card .mat-mdc-card-header { + background: #f8f9fa; + border-radius: 12px 12px 0 0; +} + +.script-card .mat-mdc-card-title, +.params-card .mat-mdc-card-title { + display: flex; + align-items: center; + gap: 8px; + color: #2c3e50; + font-weight: 600; +} + +.script-card .mat-mdc-card-subtitle, +.params-card .mat-mdc-card-subtitle { + color: #6c757d; + margin-top: 4px; +} + +.script-content-wrapper { + background: #f8f9fa; + border-radius: 8px; + border: 1px solid #e9ecef; + max-height: 300px; + overflow-y: auto; +} + +.script-preview { + background-color: #ffffff; + border: 1px solid #e9ecef; + padding: 16px; + border-radius: 8px; + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 13px; + line-height: 1.6; + white-space: pre-wrap; + min-height: 60px; + max-height: 280px; + overflow-y: auto; +} + +.script-params-section { + margin-top: 16px; +} + +.params-count { + background: #667eea !important; + color: white !important; + font-size: 11px; + height: 20px; + min-height: 20px; + padding: 0 8px; +} + +.params-form { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 16px; +} + +.param-field { + position: relative; +} + +/* Estados vacíos */ +.empty-state { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; + background: #f8f9fa; + border: 2px dashed #e9ecef; + border-radius: 12px; + margin: 20px 0; +} + +.empty-state-content { + text-align: center; + color: #6c757d; +} + +.empty-state-content mat-icon { + font-size: 48px; + width: 48px; + height: 48px; + color: #dee2e6; + margin-bottom: 16px; +} + +.empty-state-content h3 { + margin: 0 0 8px 0; + color: #495057; + font-weight: 500; +} + +.empty-state-content p { + margin: 0; + font-size: 14px; + max-width: 300px; +} + +/* Mejoras en chips de acción */ +::ng-deep .action-chip mat-icon { + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + margin-right: 4px !important; +} + +/* Responsive design */ +@media (max-width: 768px) { + .script-input-header { + flex-direction: column; + align-items: flex-start; + gap: 12px; + } + + .script-stats { + flex-wrap: wrap; + gap: 8px; + } + + .params-form { + grid-template-columns: 1fr; + } + + .action-buttons { + justify-content: center; + } } \ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html index c9bef47..7e772c9 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.html @@ -86,50 +86,168 @@
+ add_circle Nuevo Script + folder Script Guardado
- +
- - Ingrese el script - - - -
- -
- - Seleccione script a ejecutar - - {{ script.name }} - - -
- -
-
-

Script:

-
+
+

+ edit_note + Editor de Script +

+
+ + format_list_numbered + {{ getLineCount(newScript) }} líneas + + + text_fields + {{ newScript.length }} caracteres + + + {{ isScriptValid(newScript) ? 'check_circle' : 'error' }} + {{ isScriptValid(newScript) ? 'Válido' : 'Requiere contenido' }} + +
-
-

Ingrese los parámetros:

-
- - {{ paramName }} - - + + Ingrese el script + + Escriba comandos de shell/bash. Use @parametro para variables dinámicas. + + + +
+

+ tune + Parámetros detectados +

+
+ + + label + @{{ param }} + + +
+
+ +
+ +
+
+ + +
+
+ + Seleccione script a ejecutar + + +
+ {{ script.name }} + {{ script.description }} +
+
+
+ No hay scripts guardados disponibles + {{ scripts.length }} script(s) disponible(s) +
+
+ + +
+ + + + preview + Vista previa del script + + {{ selectedScript.name }} + + + +
+
+
+
+
+ + +
+ + + + settings + Parámetros del script + {{ parameterNames.length }} + + Configure los valores para los parámetros requeridos + + + +
+
+ + {{ paramName }} + + label + Parámetro: @{{ paramName }} + Este parámetro es requerido + +
+
+
+
-
+ +
+
+ folder_open +

Selecciona un script

+

Elige un script guardado de la lista para ver su contenido y configurar parámetros.

+
+
+ + +
+
+ edit_note +

Escribe tu script

+

Crea un nuevo script escribiendo comandos de shell/bash en el editor de texto.

+
+
+
diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts index 90a2c8e..32f2c91 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component.ts @@ -35,6 +35,7 @@ export class RunScriptAssistantComponent implements OnInit{ selection = new SelectionModel(true, []); parameterNames: string[] = Object.keys(this.parameters); runScriptContext: any = null; + newScriptParameters: string[] = []; constructor( private http: HttpClient, @@ -168,6 +169,24 @@ export class RunScriptAssistantComponent implements OnInit{ this.scriptContent = updatedScript; } + getLineCount(text: string): number { + if (!text) return 0; + return text.split('\n').length; + } + + isScriptValid(script: string): boolean { + return Boolean(script && script.trim().length > 0); + } + + onNewScriptChange(): void { + const matches = this.newScript.match(/@(\w+)/g) || []; + this.newScriptParameters = Array.from(new Set(matches.map(m => m.slice(1)))); + } + + trackByParam(index: number, paramName: string): string { + return paramName; + } + save(): void { const dialogRef = this.dialog.open(QueueConfirmationModalComponent, { width: '400px', @@ -228,11 +247,12 @@ export class RunScriptAssistantComponent implements OnInit{ }); dialogRef.afterClosed().subscribe(result => { + console.log(result); if (result) { this.http.post(`${this.baseUrl}/command-task-scripts`, { - commandTask: result.taskId['@id'], + commandTask: result['@id'], content: this.commandType === 'existing' ? this.scriptContent : this.newScript, - order: result.executionOrder, + order: result['executionOrder'] || 1, type: 'run-script', }).subscribe({ next: () => { -- 2.40.1 From e31eda7d2fe3189270ab3d23a75b6c2c6c78255a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 5 Sep 2025 12:12:46 +0200 Subject: [PATCH 4/5] refs #2754. Deploy bug fixed --- .../deploy-image/deploy-image.component.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts index a349f28..2f5db22 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.ts @@ -445,23 +445,50 @@ export class DeployImageComponent implements OnInit{ dialogRef.afterClosed().subscribe((result: { [x: string]: any; }) => { if (result !== undefined) { - const payload = { + const basePayload: any = { method: this.selectedMethod, diskNumber: this.selectedPartition.diskNumber, partitionNumber: this.selectedPartition.partitionNumber, - p2pMode: this.selectedMethod === 'p2p' ? this.p2pMode : null, - p2pTime: this.selectedMethod === 'p2p' && this.p2pMode === 'seeder' ? this.p2pTime : null, - mcastIp: this.selectedMethod === 'udpcast' ? this.mcastIp : null, - mcastPort: this.selectedMethod === 'udpcast' ? this.mcastPort : null, - mcastMode: this.selectedMethod === 'udpcast' ? this.mcastMode : null, - mcastSpeed: this.selectedMethod === 'udpcast' ? this.mcastSpeed : null, - maxTime: this.selectedMethod === 'udpcast' ? this.mcastMaxTime : null, - maxClients: this.selectedMethod === 'udpcast' ? this.mcastMaxClients : null, + imageName: this.selectedImage.name, + imageUuid: this.selectedImage.uuid, + type: this.imageType }; + if (this.selectedMethod === 'p2p' && this.p2pMode) { + basePayload['p2pMode'] = this.p2pMode; + } + + if (this.selectedMethod === 'p2p' && this.p2pMode === 'seeder' && this.p2pTime) { + basePayload['p2pTime'] = this.p2pTime; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastIp) { + basePayload['mcastIp'] = this.mcastIp; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastPort) { + basePayload['mcastPort'] = this.mcastPort; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastMode) { + basePayload['mcastMode'] = this.mcastMode; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastSpeed) { + basePayload['mcastSpeed'] = this.mcastSpeed; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastMaxTime) { + basePayload['maxTime'] = this.mcastMaxTime; + } + + if (this.selectedMethod === 'udpcast' || this.selectedMethod === 'udpcast-direct' && this.mcastMaxClients) { + basePayload['maxClients'] = this.mcastMaxClients; + } + this.http.post(`${this.baseUrl}/command-task-scripts`, { commandTask: result['taskId'] ? result['taskId']['@id'] : result['@id'], - parameters: payload, + parameters: basePayload, order: result['executionOrder'] || 1, type: 'deploy-image', }).subscribe({ -- 2.40.1 From 2ca681cdf9888e1635f00f65399c29f4b65721e4 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 5 Sep 2025 12:17:01 +0200 Subject: [PATCH 5/5] updated changelog --- CHANGELOG.md | 9 +++++++++ Jenkins/Jenkinsfile-deb-pkg | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71f6c1f..b18f13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # Changelog +## [0.22.2] - 2025-09-05 +### Improved +- Se ha mejorado la UX en el asistente de ejecurcion de scripts. + +### Fixed +- Se ha corregido en el particionador, el que el tamaño de las particiones EFI no esten fijas a 512 cuando ya haya datos almacenados. +- Se ha corregido un bug que hacia que no pasara los clientes seleccionados en el asistente de script, y en las tareas programadas. + +--- ## [0.22.1] - 2025-09-05 ### Improved - Se ha mejorado la experiencia de usuario con el despleable de "tipos de particion" en el asistente de particonado. diff --git a/Jenkins/Jenkinsfile-deb-pkg b/Jenkins/Jenkinsfile-deb-pkg index 394bc73..ad33e30 100644 --- a/Jenkins/Jenkinsfile-deb-pkg +++ b/Jenkins/Jenkinsfile-deb-pkg @@ -99,8 +99,21 @@ pipeline { } } post { + success { + script { + // Solo lanzar cuando el build sea exitoso y en la rama main + if (env.BRANCH_NAME == 'main') { + build job: 'Aptly publish nightly repository', + wait: false, + parameters: [ + string(name: 'TRIGGERED_BY', value: "${env.JOB_NAME}-${env.BUILD_NUMBER}") + ] + } + } + + } always { - notifyBuildStatus('narenas@qindel.com') + notifyBuildStatus('opengnsys@qindel.com') } } } -- 2.40.1