refs #1090. PartitionAssistant changes
testing/ogGui-multibranch/pipeline/head This commit is unstable Details

pull/10/head
Manuel Aranda Rosales 2024-12-11 08:46:17 +01:00
parent 4f905778f8
commit 016e5a821a
8 changed files with 96 additions and 76 deletions

View File

@ -198,7 +198,7 @@ export class ClientMainViewComponent implements OnInit {
} }
loadPartitions(): void { loadPartitions(): void {
this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData?.id}&order[partitionNumber]=ASC`).subscribe({ this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData?.id}&order[diskNumber, partitionNumber]=ASC`).subscribe({
next: data => { next: data => {
this.dataSource = data['hydra:member']; this.dataSource = data['hydra:member'];
this.partitions = data['hydra:member']; this.partitions = data['hydra:member'];

View File

@ -54,9 +54,9 @@
</table> </table>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<h3 *ngIf="isMethod('multicast')" class="input-group">Opciones multicast</h3> <h3 *ngIf="isMethod('udpcast') || isMethod('uftp')" class="input-group">Opciones multicast</h3>
<h3 *ngIf="isMethod('torrent')" class="input-group">Opciones torrent</h3> <h3 *ngIf="isMethod('p2p')" class="input-group">Opciones torrent</h3>
<div *ngIf="isMethod('multicast')" class="input-group"> <div *ngIf="isMethod('udpcast') || isMethod('uftp')" class="input-group">
<mat-form-field appearance="fill" class="input-field"> <mat-form-field appearance="fill" class="input-field">
<mat-label>Puerto</mat-label> <mat-label>Puerto</mat-label>
<input matInput [(ngModel)]="mcastPort" name="mcastPort"> <input matInput [(ngModel)]="mcastPort" name="mcastPort">

View File

@ -39,22 +39,25 @@ export class DeployImageComponent {
{ name: 'Seeder', value: 'p2p-mode-seeder' }, { name: 'Seeder', value: 'p2p-mode-seeder' },
]; ];
protected multicastModeOptions = [ protected multicastModeOptions = [
{"name": 'Half duplex', "value": "half-duplex"}, { name: 'Half duplex', value: "half"},
{"name": 'Full duplex', "value": "full-duplex"}, { name: 'Full duplex', value: "full"},
]; ];
allMethods = [ allMethods = [
'multicast', 'uftp',
'udpcast',
'multicast-direct', 'multicast-direct',
'unicast', 'unicast',
'unicast-direct', 'unicast-direct',
'torrent' 'p2p'
]; ];
updateCacheMethods = [ updateCacheMethods = [
'uftp',
'udpcast',
'multicast', 'multicast',
'unicast', 'unicast',
'torrent' 'p2p'
]; ];
dataSource = new MatTableDataSource<any>(); dataSource = new MatTableDataSource<any>();
@ -177,8 +180,8 @@ export class DeployImageComponent {
this.http.post(`${this.baseUrl}${this.selectedImage}/deploy-image`, payload) this.http.post(`${this.baseUrl}${this.selectedImage}/deploy-image`, payload)
.subscribe({ .subscribe({
next: (response) => { next: (response) => {
this.toastService.success('Imagen creada exitosamente'); this.toastService.success('Petición de despliegue enviada correctamente');
this.router.navigate(['/commmands-logs']) this.router.navigate(['/commands-logs'])
}, },
error: (error) => { error: (error) => {
console.error('Error:', error); console.error('Error:', error);

View File

@ -162,3 +162,8 @@ button.remove-btn:hover {
align-content: center; align-content: center;
justify-self: center; justify-self: center;
} }
.disk-select {
padding: 20px;
margin: 10px auto;
}

View File

@ -1,21 +1,27 @@
<div class="header-container"> <div class="header-container">
<h2 class="title" i18n="@@subnetsTitle">Asistente de particionado</h2> <h2 class="title" i18n="@@subnetsTitle">Asistente de particionado</h2>
<div class="subnets-button-row"> <div class="subnets-button-row">
<button mat-flat-button color="primary" (click)="save()">Ejecutar</button> <button mat-flat-button color="primary" [disabled]="data.status === 'busy'" (click)="save()">Ejecutar</button>
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<mat-dialog-content> <mat-dialog-content>
<div class="partition-assistant" *ngFor="let disk of disks; let i = index"> <div class="disk-select">
<div class="header"> <mat-form-field appearance="fill">
<label for="disk-number-{{ i }}">Disco {{ disk.diskNumber }}:</label> <mat-label>Seleccionar disco</mat-label>
<span class="disk-size">Tamaño: {{ (disk.totalDiskSize / 1024).toFixed(2) }} GB</span> <mat-select [(ngModel)]="selectedDiskNumber">
</div> <mat-option *ngFor="let disk of disks" [value]="disk.diskNumber">
Disco {{ disk.diskNumber }} ({{ (disk.totalDiskSize / 1024).toFixed(2) }} GB)
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="partition-assistant" *ngIf="selectedDisk">
<div class="partition-bar"> <div class="partition-bar">
<div <div
*ngFor="let partition of activePartitions(disk.diskNumber)" *ngFor="let partition of activePartitions(selectedDisk.diskNumber)"
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}" [ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
class="partition-segment" class="partition-segment"
> >
@ -23,7 +29,7 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<button mat-flat-button color="primary" (click)="addPartition(disk.diskNumber)">Añadir partición</button> <button mat-flat-button color="primary" (click)="addPartition(selectedDisk.diskNumber)">Añadir partición</button>
</div> </div>
<div class="row"> <div class="row">
@ -41,7 +47,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ng-container *ngFor="let partition of disk.partitions; let j = index"> <ng-container *ngFor="let partition of selectedDisk.partitions; let j = index">
<tr *ngIf="!partition.removed"> <tr *ngIf="!partition.removed">
<td>{{ partition.partitionNumber }}</td> <td>{{ partition.partitionNumber }}</td>
<td> <td>
@ -62,21 +68,21 @@
<input <input
type="number" type="number"
[(ngModel)]="partition.size" required [(ngModel)]="partition.size" required
(input)="updatePartitionSize(disk.diskNumber, j, partition.size)" (input)="updatePartitionSize(selectedDisk.diskNumber, j, partition.size)"
/> />
</td> </td>
<td> <td>
<input <input
type="number" type="number"
[(ngModel)]="partition.percentage" [(ngModel)]="partition.percentage"
(input)="updatePartitionSizeFromPercentage(disk.diskNumber, j, partition.percentage)" (input)="updatePartitionSizeFromPercentage(selectedDisk.diskNumber, j, partition.percentage)"
/> />
</td> </td>
<td> <td>
<input type="checkbox" [(ngModel)]="partition.format" /> <input type="checkbox" [(ngModel)]="partition.format" />
</td> </td>
<td> <td>
<button (click)="removePartition(disk.diskNumber, partition)" class="remove-btn">X</button> <button (click)="removePartition(selectedDisk.diskNumber, partition)" class="remove-btn">X</button>
</td> </td>
</tr> </tr>
</ng-container> </ng-container>
@ -87,7 +93,7 @@
<div class="chart-container"> <div class="chart-container">
<ngx-charts-pie-chart <ngx-charts-pie-chart
[view]="view" [view]="view"
[results]="disk.chartData" [results]="selectedDisk.chartData"
[doughnut]="true" [doughnut]="true"
> >
</ngx-charts-pie-chart> </ngx-charts-pie-chart>
@ -96,5 +102,4 @@
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div> <div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>

View File

@ -35,6 +35,7 @@ export class PartitionAssistantComponent implements OnInit {
originalPartitions: any[] = []; originalPartitions: any[] = [];
clientId: string | null = null; clientId: string | null = null;
newPartitions: any[] = []; newPartitions: any[] = [];
selectedDiskNumber: number | null = null;
updateRequests: any[] = []; updateRequests: any[] = [];
data: any = {}; data: any = {};
disks: { diskNumber: number; totalDiskSize: number; partitions: Partition[]; chartData: any[]; used: number; percentage: number }[] = []; disks: { diskNumber: number; totalDiskSize: number; partitions: Partition[]; chartData: any[]; used: number; percentage: number }[] = [];
@ -57,6 +58,10 @@ export class PartitionAssistantComponent implements OnInit {
this.loadPartitions(); this.loadPartitions();
} }
get selectedDisk():any {
return this.disks.find(disk => disk.diskNumber === this.selectedDiskNumber) || null;
}
loadPartitions() { loadPartitions() {
const url = `${this.baseUrl}/clients/${this.clientId}`; const url = `${this.baseUrl}/clients/${this.clientId}`;
this.http.get(url).subscribe( this.http.get(url).subscribe(
@ -246,59 +251,57 @@ export class PartitionAssistantComponent implements OnInit {
} }
save() { save() {
const invalidDisks = this.disks.filter(disk => { if (!this.selectedDisk) {
const totalPartitionSize = disk.partitions.reduce((sum, partition) => sum + partition.size, 0); this.errorMessage = 'Por favor selecciona un disco antes de guardar.';
return totalPartitionSize > disk.totalDiskSize; return;
}); }
console.log(invalidDisks); const totalPartitionSize = this.selectedDisk.partitions.reduce((sum: any, partition: { size: any; }) => sum + partition.size, 0);
if (invalidDisks.length > 0) {
this.errorMessage = 'El tamaño total de las particiones en uno o más discos excede el tamaño total del disco.'; if (totalPartitionSize > this.selectedDisk.totalDiskSize) {
this.errorMessage = 'El tamaño total de las particiones en el disco seleccionado excede el tamaño total del disco.';
return; return;
} else { } else {
this.errorMessage = ''; this.errorMessage = '';
} }
const modifiedPartitions = this.getModifiedOrNewPartitions(); const modifiedPartitions = this.selectedDisk.partitions.filter((partition: { removed: any; format: any; }) => !partition.removed || partition.format);
if (modifiedPartitions.length === 0) { if (modifiedPartitions.length === 0) {
this.errorMessage = 'No hay cambios para guardar.'; this.errorMessage = 'No hay cambios para guardar en el disco seleccionado.';
return; return;
} }
modifiedPartitions.forEach(({ partition, diskNumber, partitionNumber }) => { const newPartitions = modifiedPartitions.map((partition: { partitionNumber: any; memoryUsage: any; size: any; partitionCode: any; filesystem: any; uuid: any; removed: any; format: any; }) => ({
const payload = { diskNumber: this.selectedDisk.diskNumber,
diskNumber: diskNumber, partitionNumber: partition.partitionNumber,
partitionNumber: partitionNumber, memoryUsage: partition.memoryUsage,
memoryUsage: partition.memoryUsage, size: partition.size,
size: partition.size, partitionCode: partition.partitionCode,
partitionCode: partition.partitionCode, filesystem: partition.filesystem,
filesystem: partition.filesystem, client: `/clients/${this.clientId}`,
client: `/clients/${this.clientId}`, uuid: partition.uuid,
uuid: partition.uuid, removed: partition.removed || false,
removed: partition.removed || false, format: partition.format || false,
format: partition.format || false, }));
};
this.newPartitions.push(payload); if (newPartitions.length > 0) {
}); const bulkPayload = { partitions: newPartitions };
if (this.newPartitions.length > 0) {
const bulkPayload = { partitions: this.newPartitions };
this.http.post(this.apiUrl, bulkPayload).subscribe( this.http.post(this.apiUrl, bulkPayload).subscribe(
(response) => { (response) => {
this.toastService.success('Particiones creadas exitosamente'); this.toastService.success('Particiones creadas exitosamente para el disco seleccionado.');
this.router.navigate(['/commands-logs']); this.router.navigate(['/commands-logs']);
}, },
(error) => { (error) => {
console.error('Error al crear las particiones:', error); console.error('Error al crear las particiones:', error);
this.toastService.error('Error al crear las particiones'); this.toastService.error('Error al crear las particiones.');
} }
); );
} }
} }
removePartition(diskNumber: number, partition: Partition) { removePartition(diskNumber: number, partition: Partition) {
const disk = this.disks.find((d) => d.diskNumber === diskNumber); const disk = this.disks.find((d) => d.diskNumber === diskNumber);
if (disk) { if (disk) {

View File

@ -220,7 +220,7 @@
<button <button
*ngIf="(!syncStatus || syncingClientId !== client.uuid)" *ngIf="(!syncStatus || syncingClientId !== client.uuid)"
mat-icon-button color="primary" mat-icon-button color="primary"
(click)="getStatus(client)"> (click)="getStatus(client, selectedNode)">
<mat-icon>sync</mat-icon> <mat-icon>sync</mat-icon>
</button> </button>
@ -251,6 +251,23 @@
</div> </div>
<div class="clients-table" *ngIf="currentView === 'list'"> <div class="clients-table" *ngIf="currentView === 'list'">
<table mat-table matSort [dataSource]="selectedClients" class="mat-elevation-z8"> <table mat-table matSort [dataSource]="selectedClients" class="mat-elevation-z8">
<ng-container matColumnDef="sync">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'sync' | translate }} </th>
<td mat-cell *matCellDef="let client">
<button
*ngIf="(!syncStatus || syncingClientId !== client.uuid)"
mat-icon-button color="primary"
(click)="getStatus(client, selectedNode)">
<mat-icon>sync</mat-icon>
</button>
<button
*ngIf="syncStatus && syncingClientId === client.uuid"
mat-icon-button color="primary">
<mat-spinner diameter="24"></mat-spinner>
</button>
</td>
</ng-container>
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'name' | translate }} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'name' | translate }} </th>
<td mat-cell *matCellDef="let client"> <td mat-cell *matCellDef="let client">
@ -263,7 +280,7 @@
</ng-container> </ng-container>
<ng-container matColumnDef="oglive"> <ng-container matColumnDef="oglive">
<th mat-header-cell *matHeaderCellDef mat-sort-header> OG Live </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> OG Live </th>
<td mat-cell *matCellDef="let client"> {{ client.ogLive?.name }} </td> <td mat-cell *matCellDef="let client"> {{ (client.ogLive?.name || '').slice(0, 15) }}{{ (client.ogLive?.name?.length > 15) ? '...' : '' }} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="status"> <ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'status' | translate }} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'status' | translate }} </th>
@ -279,23 +296,9 @@
{{ client.status || 'off' }} {{ client.status || 'off' }}
</mat-chip> </mat-chip>
<button
*ngIf="(!syncStatus || syncingClientId !== client.uuid)"
mat-icon-button color="primary"
(click)="getStatus(client)">
<mat-icon>sync</mat-icon>
</button>
<button
*ngIf="syncStatus && syncingClientId === client.uuid"
mat-icon-button color="primary">
<mat-spinner diameter="24"></mat-spinner>
</button>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="maintenace"> <ng-container matColumnDef="maintenace">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'maintenance' | translate }} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ 'maintenance' | translate }} </th>
<td mat-cell *matCellDef="let client"> {{ client.maintenance }} </td> <td mat-cell *matCellDef="let client"> {{ client.maintenance }} </td>
@ -320,8 +323,8 @@
<button mat-icon-button [matMenuTriggerFor]="clientMenu"> <button mat-icon-button [matMenuTriggerFor]="clientMenu">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
<mat-menu #clientMenu="matMenu"> <mat-menu #clientMenu="matMenu">
@ -330,7 +333,7 @@
<span>{{ command.name }}</span> <span>{{ command.name }}</span>
</button> </button>
</mat-menu> </mat-menu>
<button mat-menu-item [matMenuTriggerFor]="commandMenu" (click)="fetchCommands()"> <button mat-menu-item [matMenuTriggerFor]="commandMenu" (click)="fetchCommands()">
<mat-icon>play_arrow</mat-icon> <mat-icon>play_arrow</mat-icon>
<span>{{ 'executeCommand' | translate }}</span> <span>{{ 'executeCommand' | translate }}</span>

View File

@ -63,7 +63,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
syncingClientId: string | null = null; syncingClientId: string | null = null;
private originalTreeData: TreeNode[] = []; private originalTreeData: TreeNode[] = [];
displayedColumns: string[] = ['name', 'oglive', 'status', 'maintenace', 'subnet', 'pxeTemplate', 'parentName', 'actions']; displayedColumns: string[] = ['sync', 'name', 'oglive', 'status', 'subnet', 'pxeTemplate', 'parentName', 'actions'];
private _sort!: MatSort; private _sort!: MatSort;
private _paginator!: MatPaginator; private _paginator!: MatPaginator;
@ -563,7 +563,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
this.selectedNode = node; this.selectedNode = node;
} }
getStatus(client: Client): void { getStatus(client: Client, node: any): void {
if (!client.uuid || !client['@id']) return; if (!client.uuid || !client['@id']) return;
this.syncingClientId = client.uuid; this.syncingClientId = client.uuid;
@ -573,14 +573,15 @@ export class GroupsComponent implements OnInit, OnDestroy {
this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe( this.http.post(`${this.baseUrl}${client['@id']}/agent/status`, {}).subscribe(
() => { () => {
this.toastr.success('Cliente actualizado correctamente'); this.toastr.success('Cliente actualizado correctamente');
this.search();
this.syncStatus = false; this.syncStatus = false;
this.syncingClientId = null; this.syncingClientId = null;
this.onNodeClick(node);
}, },
() => { () => {
this.toastr.error('Error de conexión con el cliente'); this.toastr.error('Error de conexión con el cliente');
this.syncStatus = false; this.syncStatus = false;
this.syncingClientId = null; this.syncingClientId = null;
this.onNodeClick(node);
} }
) )
); );