]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard : Enable cephadm-signed TLS for NVMeOf
authorAbhishek Desai <abhishek.desai1@ibm.com>
Mon, 15 Jun 2026 11:12:15 +0000 (16:42 +0530)
committerpujashahu <pshahu@redhat.com>
Mon, 6 Jul 2026 14:50:39 +0000 (20:20 +0530)
fixes: https://tracker.ceph.com/issues/77406
Signed-off-by: Abhishek Desai <abhishek.desai1@ibm.com>
(cherry picked from commit 14162473bb6a6993ec729056b35dc514cad2228e)
Signed-off-by: pujashahu <pshahu@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-group/nvmeof-gateway-group.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/nvmeof-gateway-node/nvmeof-gateway-node.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/details-card/details-card.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/details-card/details-card.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/details-card/details-card.component.ts

index 078553f626a1e1215aa0fb11747c0667f20a866f..287361a41dc23618903307ca37b5ed7b99656ae6 100644 (file)
@@ -1,6 +1,19 @@
 <div class="nvmeof-content-layout">
   <div class="nvmeof-content-main">
     <ng-container *ngIf="gatewayGroup$ | async as gateways">
+      <!-- Details Card Section -->
+      <ng-container *ngIf="selection.hasSelection">
+        <cd-details-card
+          cardTitle="Details"
+          i18n-cardTitle
+          [details]="selectedGatewayDetails"
+          [showEditButton]="true"
+          [editButtonLabel]="actionLabels.EDIT"
+          [columns]="4"
+          (editClicked)="editSelectedGatewayGroup()">
+        </cd-details-card>
+      </ng-container>
+
       <cd-table
         #table
         [data]="gateways"
         i18n-emptyStateTitle
         emptyStateMessage="Set up your first gateway group to start using NVMe over Fabrics. This will allow you to create high-performance block storage with NVMe/TCP protocol."
         i18n-emptyStateMessage>
-      <cd-table-actions class="table-actions"
-                        [permission]="permission"
-                        [selection]="selection"
-                        [tableActions]="tableActions">
-      </cd-table-actions>
+        <div class="table-actions">
+          <cd-table-actions
+            [permission]="permission"
+            [selection]="selection"
+            [tableActions]="tableActions">
+          </cd-table-actions>
+        </div>
       </cd-table>
     </ng-container>
   </div>
index 17766f2b7175272e9d7cb323dce4c333c12e9b7c..04c3bf4fbe7dcb5ee8f42972291b0daeb04b07e4 100644 (file)
@@ -342,6 +342,45 @@ export class NvmeofGatewayGroupComponent implements OnInit, OnDestroy {
     this.router.navigate([this.viewUrl, groupName]);
   }
 
+  editSelectedGatewayGroup(): void {
+    const selectedGroup = this.selection.first();
+    if (!selectedGroup) {
+      return;
+    }
+    this.router.navigate([this.urlBuilder.getEdit(selectedGroup.name)]);
+  }
+
+  private buildGatewayDetails(selectedGroup: any): DetailItem[] {
+    if (!selectedGroup) {
+      return [];
+    }
+
+    const runningGateways = selectedGroup.statusCount?.running ?? 0;
+    const errorGateways = selectedGroup.statusCount?.error ?? 0;
+    const totalGateways = runningGateways + errorGateways;
+
+    return [
+      {
+        label: $localize`Gateway name`,
+        value: selectedGroup.name
+      },
+      {
+        label: $localize`Gateway nodes`,
+        value: totalGateways
+      },
+      {
+        label: $localize`Encryption`,
+        value: selectedGroup.spec?.enable_auth ? $localize`Enabled` : $localize`Disabled`,
+        type: 'status'
+      },
+      {
+        label: $localize`mTLS`,
+        value: selectedGroup.spec?.enable_mtls ? $localize`Enabled` : $localize`Disabled`,
+        type: 'status'
+      }
+    ];
+  }
+
   ngOnDestroy(): void {
     this.destroy$.next();
     this.destroy$.complete();
index 0a97671fa3b1d685b415f32dfbb99a90c317b487..b8025d08f0689d9cb7d2ff19d5d7673a631f7acd 100644 (file)
@@ -1,7 +1,7 @@
 <div class="content-theme">
   @if (mode === 'details' && groupName) {
-  <cd-details-card cardTitle="Details" i18n-cardTitle [details]="gatewayDetails" [showEditButton]="false"
-    class="cds-mb-5">
+  <cd-details-card cardTitle="Details" i18n-cardTitle [details]="gatewayDetails" [showEditButton]="true"
+    editButtonLabel="Edit" i18n-editButtonLabel (editClicked)="onEditGateway()" class="cds-mb-5">
   </cd-details-card>
   }
 
index 89fe4865356f6b92c97d84f23dbfe1ced6655fe4..fecbbe14a476cf2f533a37c18838600678cd7db3 100644 (file)
@@ -439,6 +439,8 @@ export class NvmeofGatewayNodeComponent implements OnInit, OnDestroy, OnChanges
     this.hostsLoaded.emit(this.count);
   }
 
+  onEditGateway(): void {}
+
   private buildGatewayDetails(
     serviceSpec: CephServiceSpec,
     gatewayNodeCount: number
index d88531a22c2f67d538d1cf5c9387a1a385bf0e8b..b6e9374e006fe4a1671e4bc6fba2826ab2e44030 100644 (file)
            i18n>Choose Certificate Authority</label>
     <cds-radio-group formControlName="certificateType"
                      orientation="horizontal"
-                     helperText="Select how certificates will be signed for this service. Choose internal to use the clusters CA, or external to upload certificates signed by your organization."
+                     helperText="Select how certificates will be signed for this service. Choose internal to use the cluster's CA, or external to upload certificates signed by your organization."
                      i18n-helperText>
       <cds-radio [value]="CertificateType.internal"
                  (change)="onCertificateTypeChange(CertificateType.internal)"
index 33f6d602c4cea26398c748df2c0c0e788c0fce56..26b6a0b27219c9bf7e846a5fa799c6dd3429a8d7 100644 (file)
@@ -978,6 +978,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
         });
     }
     this.detectChanges();
+    this.updateRgwPlacementControlsState();
+    this.updateGrafanaPasswordControlState();
   }
 
   detectChanges(): void {
@@ -1151,7 +1153,7 @@ export class ServiceFormComponent extends CdForm implements OnInit {
         } else {
           this.showRealmCreationForm = false;
         }
-        this.updateRgwControlStates();
+         this.updateRgwControlStates();
       },
       (_error) => {
         const defaultZone = new RgwZone();
@@ -1160,7 +1162,7 @@ export class ServiceFormComponent extends CdForm implements OnInit {
         defaultZonegroup.name = 'default';
         this.zoneList.push(defaultZone);
         this.zonegroupList.push(defaultZonegroup);
-        this.updateRgwControlStates();
+         this.updateRgwControlStates();
       }
     );
   }
@@ -1191,6 +1193,7 @@ export class ServiceFormComponent extends CdForm implements OnInit {
 
   onServiceTypeChange(selectedServiceType: string) {
     this.setServiceId(selectedServiceType);
+    this.updateGrafanaPasswordControlState(selectedServiceType);
 
     this.serviceIds = this.serviceList
       ?.filter((service) => service['service_type'] === selectedServiceType)
@@ -1217,6 +1220,33 @@ export class ServiceFormComponent extends CdForm implements OnInit {
     }
   }
 
+  private updateRgwPlacementControlsState(): void {
+    this.toggleFormControlState('realm_name', this.editing || this.realmList.length === 0);
+    this.toggleFormControlState('zonegroup_name', this.editing || this.zonegroupList.length === 0);
+    this.toggleFormControlState('zone_name', this.editing || this.zoneList.length === 0);
+  }
+
+  private updateGrafanaPasswordControlState(serviceType = this.serviceForm?.get('service_type')?.value): void {
+    this.toggleFormControlState(
+      'grafana_admin_password',
+      this.editing && serviceType === 'grafana'
+    );
+  }
+
+  private toggleFormControlState(controlName: string, disabled: boolean): void {
+    const control = this.serviceForm.get(controlName);
+    if (!control) {
+      return;
+    }
+    if (disabled && control.enabled) {
+      control.disable({ emitEvent: false });
+      return;
+    }
+    if (!disabled && control.disabled) {
+      control.enable({ emitEvent: false });
+    }
+  }
+
   onPlacementChange(selected: string) {
     if (selected === 'label') {
       this.serviceForm.get('count').setValue(null);
index 91a5cbced6af39472fa65225a32a4474bde4eef8..fd81525f009e420028e10f59a2b06d6cba532843 100644 (file)
@@ -142,7 +142,6 @@ import { NumberWithUnitComponent } from './number-with-unit/number-with-unit.com
 @NgModule({
   imports: [
     CommonModule,
-    ProductiveCardComponent,
     FormsModule,
     ReactiveFormsModule,
     NgbAlertModule,
@@ -186,7 +185,8 @@ import { NumberWithUnitComponent } from './number-with-unit/number-with-unit.com
     TagModule,
     LinkModule,
     LayerModule,
-    ThemeModule
+    ThemeModule,
+    ProductiveCardComponent
   ],
   declarations: [
     SparklineComponent,
index b1595fbcdf2d9da26e69fed05ae54b8b28115efc..3d558fb8c56ebe1035af1498eb1d29ea275cead7 100644 (file)
     }
   </ng-template>
 
-  <div [cdsStack]="'vertical'" 
-       [gap]="0">
+  <div>
     @if (details && details.length > 0) {
-    <div [cdsStack]="'horizontal'"
-         [gap]="5">
-      @for (detail of getVisibleDetails(); track detail.label) {
-      <div [cdsStack]="'vertical'" 
-           [gap]="2">
-        <label class="cds--type-label-01">{{ detail.label }}</label>
-        <div class="cds--type-body-01">
-          @switch (detail.type) {
-            @case ('status') {
-              <div [cdsStack]="'horizontal'" [gap]="3">
-                <cd-icon [type]="getStatusIcon(detail)"></cd-icon>
-                <span>{{ getDisplayValue(detail.value) }}</span>
-              </div>
-            }
-            
-            @default {
-              <span>{{ getDisplayValue(detail.value) }}</span>
-            }
-          }
+    <div class="cds--grid cds--no-gutter">
+      <div class="cds--row">
+        @for (detail of getVisibleDetails(); track detail.label) {
+        <div class="cds--col">
+          <div [cdsStack]="'vertical'" [gap]="2">
+            <label class="cds--type-label-01">{{ detail.label }}</label>
+            <div class="cds--type-body-01">
+              @switch (detail.type) {
+                @case ('status') {
+                  <div [cdsStack]="'horizontal'" [gap]="3">
+                    <cd-icon [type]="getStatusIcon(detail)"></cd-icon>
+                    <span>{{ getDisplayValue(detail.value) }}</span>
+                  </div>
+                }
+                
+                @default {
+                  <span>{{ getDisplayValue(detail.value) }}</span>
+                }
+              }
+            </div>
+          </div>
         </div>
+        }
       </div>
-      }
     </div>
     }
     <ng-content></ng-content>
index a8eb9996aa84c97dd97f84d73bf29ab615630e66..0896ba2a603624c5e06e29a2c03e5558fc35f4e4 100644 (file)
@@ -1,6 +1,7 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { DetailsCardComponent } from './details-card.component';
+import { ProductiveCardComponent } from '../productive-card/productive-card.component';
 
 describe('DetailsCardComponent', () => {
   let component: DetailsCardComponent;
@@ -8,7 +9,8 @@ describe('DetailsCardComponent', () => {
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
-      declarations: [DetailsCardComponent]
+      declarations: [DetailsCardComponent],
+      imports: [ProductiveCardComponent]
     }).compileComponents();
 
     fixture = TestBed.createComponent(DetailsCardComponent);
index 9b2fad7afb85d2366ae532ebfb97e105a5527e80..cbd62d6f8efe23745fbf9001510729ea28f4bb57 100644 (file)
@@ -43,7 +43,6 @@ export class DetailsCardComponent {
   onEditClick(): void {
     this.editClicked.emit();
   }
-
   getVisibleDetails(): DetailItem[] {
     return (this.details || []).filter((detail) => !detail.hidden);
   }