redirectTo: 'dashboard',
backend: 'cephadm'
},
- breadcrumbs: 'Expand Cluster'
+ breadcrumbs: 'Cluster/Expand Cluster'
}
},
{
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
-import { ComboBoxModule, DropdownModule, CheckboxModule } from 'carbon-components-angular';
+import {
+ ComboBoxModule,
+ DropdownModule,
+ CheckboxModule,
+ ButtonModule,
+ GridModule,
+ ProgressIndicatorModule
+} from 'carbon-components-angular';
import { TreeModule } from '@circlon/angular-tree-component';
import {
DashboardV3Module,
ComboBoxModule,
DropdownModule,
- CheckboxModule
+ CheckboxModule,
+ GridModule,
+ ProgressIndicatorModule,
+ ButtonModule
],
declarations: [
HostsComponent,
-<div class="row">
- <div class="col-lg-3">
+<div cdsRow>
+ <div cdsCol>
<fieldset>
<legend class="cd-header"
i18n>Cluster Resources</legend>
<table class="cds--data-table--sort cds--data-table--no-border cds--data-table cds--data-table--md">
- <tr>
- <td i18n
- class="bold">Hosts</td>
- <td>{{ hostsCount }}</td>
- </tr>
- <tr *ngIf="!isSimpleDeployment; else simpleDeploymentTextTpl">
- <td>
- <dl>
- <dt>
- <p i18n>Storage Capacity</p>
- </dt>
- <dd>
- <p i18n>Number of devices</p>
- </dd>
- <dd>
- <p i18n>Raw capacity</p>
- </dd>
- </dl>
- </td>
- <td class="pt-5"><p>{{ totalDevices }}</p><p>
- {{ totalCapacity | dimlessBinary }}</p></td>
- </tr>
- <tr>
- <td i18n
- class="bold">CPUs</td>
- <td>{{ totalCPUs | empty }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Memory</td>
- <td>{{ totalMemory | empty }}</td>
- </tr>
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold">Hosts</td>
+ <td>{{ hostsCount }}</td>
+ </tr>
+ <tr *ngIf="!isSimpleDeployment; else simpleDeploymentTextTpl">
+ <td>
+ <dl>
+ <dt>
+ <p i18n>Storage Capacity</p>
+ </dt>
+ <dd>
+ <p i18n>Number of devices</p>
+ </dd>
+ <dd>
+ <p i18n>Raw capacity</p>
+ </dd>
+ </dl>
+ </td>
+ <td class="pt-5"><p>{{ totalDevices }}</p><p>
+ {{ totalCapacity | dimlessBinary }}</p></td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">CPUs</td>
+ <td>{{ totalCPUs | empty }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Memory</td>
+ <td>{{ totalMemory | empty }}</td>
+ </tr>
+ </tbody>
</table>
</fieldset>
</div>
-
- <div class="col-lg-9">
- <legend i18n
- class="cd-header">Host Details</legend>
- <cd-hosts [hiddenColumns]="['services', 'status']"
- [hideToolHeader]="true"
- [hasTableDetails]="false"
- [showGeneralActionsOnly]="true"
- [showExpandClusterBtn]="false">
+</div>
+<div cdsRow>
+ <div cdsCol>
+ <legend i18n
+ class="cd-header">Host Details</legend>
+ <cd-hosts [hiddenColumns]="['service_instances', 'status']"
+ [hideToolHeader]="true"
+ [hasTableDetails]="false"
+ [showGeneralActionsOnly]="true"
+ [showExpandClusterBtn]="false"
+ [showInlineActions]="false">
</cd-hosts>
</div>
</div>
<div class="container h-75"
*ngIf="startClusterCreation">
- <div class="row h-100 justify-content-center align-items-center">
+ <div class="h-100 justify-content-center align-items-center"
+ cdsRow>
<div class="blank-page">
<!-- htmllint img-req-src="false" -->
<img [src]="projectConstants.cephLogo"
<h4 class="text-center"
i18n>Please expand your cluster first</h4>
<div class="text-center">
- <button class="btn btn-accent m-2"
+ <button cdsButton="primary"
name="expand-cluster"
(click)="createCluster()"
aria-label="Expand Cluster"
i18n>Expand Cluster</button>
- <button class="btn btn-light"
+ <button cdsButton="secondary"
name="skip-cluster-creation"
aria-label="Skip"
(click)="skipClusterCreation()"
</div>
</div>
-<div class="card"
+<div cdsRow
+ class="form"
*ngIf="!startClusterCreation">
- <div class="card-header"
- i18n>Expand Cluster</div>
- <div class="container-fluid">
+ <div cdsCol
+ [columnNumbers]="{'lg': 2, 'md': 2, 'sm': 2}"
+ class="indicator-wrapper">
+
+ <div class="form-header"
+ i18n>Expand Cluster</div>
<cd-wizard [stepsTitle]="stepTitles"></cd-wizard>
- <div class="card-body vertical-line">
- <ng-container [ngSwitch]="currentStep?.stepIndex">
- <div *ngSwitchCase="'1'"
- class="ms-5">
- <h4 class="title"
- i18n>Add Hosts</h4>
- <br>
- <cd-hosts [hiddenColumns]="['services']"
- [hideMaintenance]="true"
- [hasTableDetails]="false"
- [showGeneralActionsOnly]="true"
- [showExpandClusterBtn]="false"></cd-hosts>
- </div>
- <div *ngSwitchCase="'2'"
- class="ms-5">
- <h4 class="title"
- i18n>Create OSDs</h4>
- <div class="alignForm">
- <cd-osd-form [hideTitle]="true"
- [hideSubmitBtn]="true"
- (emitDriveGroup)="setDriveGroup($event)"
- (emitDeploymentOption)="setDeploymentOptions($event)"
- (emitMode)="setDeploymentMode($event)"></cd-osd-form>
- </div>
- </div>
- <div *ngSwitchCase="'3'"
- class="ms-5">
- <h4 class="title"
- i18n>Create Services</h4>
- <br>
- <cd-services [hasDetails]="false"
- [hiddenServices]="['mon', 'mgr', 'crash', 'agent']"
- [hiddenColumns]="['status.running', 'status.size', 'status.last_refresh']"
- [routedModal]="false"></cd-services>
- </div>
- <div *ngSwitchCase="'4'"
- class="ms-5">
- <cd-create-cluster-review></cd-create-cluster-review>
+ </div>
+
+ <div cdsCol
+ [columnNumbers]="{'lg': 14, 'md': 14, 'sm': 14}">
+ <ng-container [ngSwitch]="currentStep?.stepIndex">
+ <div *ngSwitchCase="'0'"
+ class="ms-5">
+ <h4 class="title"
+ i18n>Add Hosts</h4>
+
+ <cd-hosts [hiddenColumns]="['service_instances']"
+ [hideMaintenance]="true"
+ [hasTableDetails]="false"
+ [showGeneralActionsOnly]="true"
+ [showExpandClusterBtn]="false"></cd-hosts>
+ </div>
+ <div *ngSwitchCase="'1'"
+ class="ms-5">
+ <h4 class="title"
+ i18n>Create OSDs</h4>
+ <div class="alignForm">
+ <cd-osd-form [hideTitle]="true"
+ [hideSubmitBtn]="true"
+ (emitDriveGroup)="setDriveGroup($event)"
+ (emitDeploymentOption)="setDeploymentOptions($event)"
+ (emitMode)="setDeploymentMode($event)"></cd-osd-form>
</div>
- </ng-container>
+ </div>
+ <div *ngSwitchCase="'2'"
+ class="ms-5">
+ <h4 class="title"
+ i18n>Create Services</h4>
+ <cd-services [hasDetails]="false"
+ [hiddenServices]="['mon', 'mgr', 'crash', 'agent']"
+ [hiddenColumns]="['status.running', 'status.size', 'status.last_refresh']"
+ [routedModal]="false"></cd-services>
+ </div>
+ <div *ngSwitchCase="'3'"
+ class="ms-5">
+ <cd-create-cluster-review></cd-create-cluster-review>
+ </div>
+ </ng-container>
+ <div cdsRow
+ class="m-5">
+ <button cdsButton="secondary"
+ class="me-3"
+ id="skipStepBtn"
+ (click)="onSkip()"
+ aria-label="Skip this step"
+ *ngIf="stepTitles[currentStep?.stepIndex]?.label === 'Create OSDs'"
+ i18n>Skip</button>
+ <cd-back-button buttonType="secondary"
+ aria-label="Close"
+ (backAction)="onPreviousStep()"
+ [name]="showCancelButtonLabel()"></cd-back-button>
+ <button cdsButton="primary"
+ (click)="onNextStep()"
+ aria-label="Next"
+ i18n>{{ showSubmitButtonLabel() }}</button>
</div>
</div>
- <div class="card-footer">
- <button class="btn btn-accent m-2 float-end"
- (click)="onNextStep()"
- aria-label="Next"
- i18n>{{ showSubmitButtonLabel() }}</button>
- <cd-back-button class="m-2 float-end"
- aria-label="Close"
- (backAction)="onPreviousStep()"
- [name]="showCancelButtonLabel()"></cd-back-button>
- <button class="btn btn-light m-2 me-4 float-end"
- id="skipStepBtn"
- (click)="onSkip()"
- aria-label="Skip this step"
- *ngIf="stepTitles[currentStep.stepIndex - 1] === 'Create OSDs'"
- i18n>Skip</button>
- </div>
</div>
<ng-template #skipConfirmTpl>
import { DriveGroup } from '../osd/osd-form/drive-group.model';
import { Location } from '@angular/common';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { Step } from 'carbon-components-angular';
@Component({
selector: 'cd-create-cluster',
currentStepSub: Subscription;
permissions: Permissions;
projectConstants: typeof AppConstants = AppConstants;
- stepTitles = ['Add Hosts', 'Create OSDs', 'Create Services', 'Review'];
+ stepTitles: Step[] = [
+ {
+ label: 'Add Hosts'
+ },
+ {
+ label: 'Create OSDs',
+ complete: false
+ },
+ {
+ label: 'Create Services',
+ complete: false
+ },
+ {
+ label: 'Review',
+ complete: false
+ }
+ ];
startClusterCreation = false;
observables: any = [];
modalRef: NgbModalRef;
.subscribe((step: WizardStepModel) => {
this.currentStep = step;
});
- this.currentStep.stepIndex = 1;
+ this.currentStep.stepIndex = 0;
}
ngOnInit(): void {
+ this.stepTitles.forEach((steps, index) => {
+ steps.onClick = () => (this.currentStep.stepIndex = index);
+ });
this.route.queryParams.subscribe((params) => {
// reading 'welcome' value true/false to toggle expand-cluster wizand view and welcome view
const showWelcomeScreen = params['welcome'];
});
this.stepTitles.forEach((stepTitle) => {
- this.stepsToSkip[stepTitle] = false;
+ this.stepsToSkip[stepTitle.label] = false;
});
}
+ onStepClick(step: WizardStepModel) {
+ this.wizardStepsService.setCurrentStep(step);
+ }
+
createCluster() {
this.startClusterCreation = false;
}
}
onSkip() {
- const stepTitle = this.stepTitles[this.currentStep.stepIndex - 1];
- this.stepsToSkip[stepTitle] = true;
+ const stepTitle = this.stepTitles[this.currentStep.stepIndex];
+ this.stepsToSkip[stepTitle.label] = true;
this.onNextStep();
}
[maxLimit]="25"
(setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
- [toolHeader]="!hideToolHeader">
+ [toolHeader]="!hideToolHeader"
+ [showMenu]="showMenu">
<div class="table-actions">
<cd-table-actions [permission]="permissions.hosts"
[selection]="selection"
@Input()
showExpandClusterBtn = true;
+ @Input()
+ showInlineActions = true;
+
permissions: Permissions;
columns: Array<CdTableColumn> = [];
hosts: Array<object> = [];
import { Router } from '@angular/router';
import { RgwMultisiteWizardComponent } from '../rgw-multisite-wizard/rgw-multisite-wizard.component';
import { RgwMultisiteSyncPolicyComponent } from '../rgw-multisite-sync-policy/rgw-multisite-sync-policy.component';
+import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
const BASE_URL = 'rgw/multisite/configuration';
public rgwZoneService: RgwZoneService,
public rgwDaemonService: RgwDaemonService,
public mgrModuleService: MgrModuleService,
- private notificationService: NotificationService
+ private notificationService: NotificationService,
+ private cdsModalService: ModalCdsService
) {
this.permission = this.authStorageService.getPermissions().rgw;
}
}
openMultisiteSetupWizard() {
- this.bsModalRef = this.modalService.show(RgwMultisiteWizardComponent, {
- size: 'lg'
- });
+ this.bsModalRef = this.cdsModalService.show(RgwMultisiteWizardComponent);
}
openMigrateModal() {
-<div class="custom-modal-content">
- <cd-modal [modalRef]="activeModal"
- [pageURL]="pageURL">
- <ng-container i18n="form title"
- class="modal-title">Set up Multi-site Replication</ng-container>
- <ng-container class="modal-content">
- <div class="card">
- <div class="container-fluid">
- <cd-wizard [stepsTitle]="stepTitles"></cd-wizard>
- <div class="card-body vertical-line">
- <form [formGroup]="multisiteSetupForm"
- #formDir="ngForm"
- novalidate>
- <ng-container [ngSwitch]="currentStep?.stepIndex">
- <cd-alert-panel *ngIf="loading"
- spacingClass="mb-3"
- type="info">
- <span i18n>Please note that this process can take some time. During this period, do not click the back button or close the wizard. Thank you for your patience.</span>
- </cd-alert-panel>
- <div *ngSwitchCase="'1'"
- class="ms-5">
- <h4 class="title"
- i18n>Create Realm & Zonegroup</h4>
- <br>
- <div class="modal-body">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="realmName"
- i18n>Realm Name</label>
- <div class="cd-col-form-input">
- <input class="form-control"
- type="text"
- placeholder="Realm name..."
- id="realmName"
- name="realmName"
- formControlName="realmName">
- <cd-help-text>
- <span i18n>Enter a unique name for the Realm. The Realm is a logical grouping of all your Zonegroups.</span>
- </cd-help-text>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('realmName', formDir, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('realmName', formDir, 'uniqueName')"
- i18n>The chosen realm name is already in use.</span>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="zonegroupName"
- i18n>Zone Group Name</label>
- <div class="cd-col-form-input">
- <input class="form-control"
- type="text"
- placeholder="Zone group name..."
- id="zonegroupName"
- name="zonegroupName"
- formControlName="zonegroupName">
- <cd-help-text>
- <span i18n>Enter a name for the Zonegroup. Zonegroup will help you identify and manage the group of zones.</span>
- </cd-help-text>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'uniqueName')"
- i18n>The chosen zone group name is already in use.</span>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="zonegroup_endpoints"
- i18n>Zonegroup Endpoints</label>
- <div class="cd-col-form-input">
- <cd-select-badges id="zonegroup_endpoints"
- [data]="rgwEndpoints.value"
- [options]="rgwEndpoints.options"
- [customBadges]="true">
- </cd-select-badges>
- <cd-help-text>
- <span i18n>Select the endpoints for the Zonegroup. Endpoints are the URLs or IP addresses from which the rgw gateways in that zonegroup can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zonegroup</span>
- </cd-help-text>
- </div>
- </div>
+
+<cds-modal size="lg"
+ [open]="open"
+ [hasScrollingContent]="true"
+ (overlaySelected)="closeModal()">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>Set up Multi-site Replication</h3>
+ </cds-modal-header>
+
+ <div cdsModalContent>
+ <div cdsRow>
+ <div cdsCol
+ [columnNumbers]="{'lg': 2, 'md': 2, 'sm': 2}"
+ class="indicator-wrapper">
+ <cd-wizard [stepsTitle]="stepTitles"></cd-wizard>
+ </div>
+
+ <div cdsCol
+ [columnNumbers]="{'lg': 14, 'md': 14, 'sm': 14}">
+ <form [formGroup]="multisiteSetupForm"
+ #formDir="ngForm"
+ novalidate>
+ <ng-container [ngSwitch]="currentStep?.stepIndex">
+ <cd-alert-panel *ngIf="loading"
+ spacingClass="mb-3"
+ type="info">
+ <span i18n>Please note that this process can take some time. During this period, do not click the back button or close the wizard. Thank you for your patience.</span>
+ </cd-alert-panel>
+ <div *ngSwitchCase="'0'"
+ class="ms-5">
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="realmName"
+ i18n>Realm Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Realm name..."
+ id="realmName"
+ name="realmName"
+ formControlName="realmName"
+ modal-primary-focus>
+ <cd-help-text>
+ <span i18n>Enter a unique name for the Realm. The Realm is a logical grouping of all your Zonegroups.</span>
+ </cd-help-text>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('realmName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('realmName', formDir, 'uniqueName')"
+ i18n>The chosen realm name is already in use.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroupName"
+ i18n>Zone Group Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Zone group name..."
+ id="zonegroupName"
+ name="zonegroupName"
+ formControlName="zonegroupName">
+ <cd-help-text>
+ <span i18n>Enter a name for the Zonegroup. Zonegroup will help you identify and manage the group of zones.</span>
+ </cd-help-text>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('zonegroupName', formDir, 'uniqueName')"
+ i18n>The chosen zone group name is already in use.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroup_endpoints"
+ i18n>Zonegroup Endpoints</label>
+ <div class="cd-col-form-input">
+ <cd-select-badges id="zonegroup_endpoints"
+ [data]="rgwEndpoints.value"
+ [options]="rgwEndpoints.options"
+ [customBadges]="true">
+ </cd-select-badges>
+ <cd-help-text>
+ <span i18n>Select the endpoints for the Zonegroup. Endpoints are the URLs or IP addresses from which the rgw gateways in that zonegroup can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zonegroup</span>
+ </cd-help-text>
+ </div>
+ </div>
+ </div>
+ <div *ngSwitchCase="'1'"
+ class="ms-5">
+ <h4 class="title"
+ i18n>Create Zone</h4>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zonegroupName"
+ i18n>Zone Name</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Zone name..."
+ id="zoneName"
+ name="zoneName"
+ formControlName="zoneName">
+ <cd-help-text>
+ <span i18n>Enter a unique name for the Zone. A Zone represents a distinct data center or geographical location within a Zonegroup.</span>
+ </cd-help-text>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'uniqueName')"
+ i18n>The chosen zone name is already in use.</span>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="zone_endpoints"
+ i18n>Zone Endpoints</label>
+ <div class="cd-col-form-input">
+ <cd-select-badges id="zone_endpoints"
+ [data]="rgwEndpoints.value"
+ [options]="rgwEndpoints.options"
+ [customBadges]="true">
+ </cd-select-badges>
+ <cd-help-text>
+ <span i18n>Select the endpoints for the Zone. Endpoints are the URLs or IP addresses from which the rgw gateways in that zone can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zone</span>
+ </cd-help-text>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="username"
+ i18n>Username</label>
+ <div class="cd-col-form-input">
+ <input class="form-control"
+ type="text"
+ placeholder="Username..."
+ id="username"
+ name="username"
+ formControlName="username"
+ ngbTooltip="White spaces at the beginning and end will be trimmed"
+ i18n-ngbTooltip
+ cdTrim>
+ <cd-help-text>
+ <span i18n>Specify the username for the system user.</span>
+ </cd-help-text>
+ <cd-alert-panel type="info"
+ [showTitle]="false">
+ <span i18n>This user will be created automatically as part of the process, and it will have the necessary permissions to manage and synchronize resources across zones.</span>
+ </cd-alert-panel>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('username', formDir, 'required')"
+ i18n>This field is required.</span>
+ <span class="invalid-feedback"
+ *ngIf="multisiteSetupForm.showError('username', formDir, 'notUnique')"
+ i18n>The username already exists.</span>
+ </div>
+ </div>
+ </div>
+ <div class="ms-5"
+ *ngSwitchCase="'2'">
+ <div *ngIf="isMultiClusterConfigured; else exportTokenTemplate">
+ <h4 class="title"
+ i18n>Select Cluster</h4>
+ <div class="form-group row">
+ <label class="cd-col-form-label required"
+ for="cluster"
+ i18n>Cluster</label>
+ <div class="cd-col-form-input">
+ <select class="form-select"
+ id="cluster"
+ [(ngModel)]="selectedCluster"
+ formControlName="cluster"
+ name="cluster">
+ <option *ngFor="let cluster_detail of clusterDetailsArray"
+ [value]="cluster_detail.name">
+ {{ cluster_detail.cluster_alias }} - {{ cluster_detail.name }}
+ </option>
+ </select>
+ <cd-help-text>
+ <span i18n>Choose the cluster where you want to apply this multisite configuration. The selected cluster will integrate the defined Realm, Zonegroup, and Zones, enabling data synchronization and management across the multisite setup.</span>
+ </cd-help-text>
+ <cd-alert-panel type="info"
+ [showTitle]="false">
+ <span i18n>Before submitting this form, please verify that the selected cluster has an active RGW (Rados Gateway) service running.</span>
+ </cd-alert-panel>
</div>
</div>
- <div *ngSwitchCase="'2'"
- class="ms-5">
- <h4 class="title"
- i18n>Create Zone</h4>
+ </div>
+ <ng-template #exportTokenTemplate>
+ <h4 class="title"
+ i18n>Export Token</h4>
+ <div *ngFor="let realminfo of realms">
<div class="form-group row">
- <label class="cd-col-form-label required"
- for="zonegroupName"
- i18n>Zone Name</label>
+ <label class="cd-col-form-label"
+ for="realmName"
+ i18n>Realm Name</label>
<div class="cd-col-form-input">
- <input class="form-control"
+ <input id="realmName"
+ name="realmName"
type="text"
- placeholder="Zone name..."
- id="zoneName"
- name="zoneName"
- formControlName="zoneName">
+ [value]="realminfo.realm"
+ readonly>
<cd-help-text>
- <span i18n>Enter a unique name for the Zone. A Zone represents a distinct data center or geographical location within a Zonegroup.</span>
+ <span i18n>Name of the realm that will be involved in replication.</span>
</cd-help-text>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('zoneName', formDir, 'uniqueName')"
- i18n>The chosen zone name is already in use.</span>
</div>
</div>
<div class="form-group row">
- <label class="cd-col-form-label required"
- for="zone_endpoints"
- i18n>Zone Endpoints</label>
+ <label class="cd-col-form-label"
+ for="token"
+ i18n>Token</label>
<div class="cd-col-form-input">
- <cd-select-badges id="zone_endpoints"
- [data]="rgwEndpoints.value"
- [options]="rgwEndpoints.options"
- [customBadges]="true">
- </cd-select-badges>
- <cd-help-text>
- <span i18n>Select the endpoints for the Zone. Endpoints are the URLs or IP addresses from which the rgw gateways in that zone can be accessed. You can select multiple endpoints in case you have multiple rgw gateways in a zone</span>
- </cd-help-text>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="username"
- i18n>Username</label>
- <div class="cd-col-form-input">
- <input class="form-control"
+ <input id="realmToken"
+ name="realmToken"
type="text"
- placeholder="Username..."
- id="username"
- name="username"
- formControlName="username"
- ngbTooltip="White spaces at the beginning and end will be trimmed"
- i18n-ngbTooltip
- cdTrim>
+ [value]="realminfo.token"
+ class="me-2 mb-4"
+ readonly>
+ <cd-copy-2-clipboard-button [source]="realminfo.token"
+ [byId]="false">
+ </cd-copy-2-clipboard-button>
<cd-help-text>
- <span i18n>Specify the username for the system user.</span>
+ <span i18n>This field displays the token needed to import the multisite configuration into a secondary cluster. Copy this token securely and use it on the secondary cluster to replicate the current multisite setup. Ensure that the token is handled securely to prevent unauthorized access.</span>
</cd-help-text>
- <cd-alert-panel type="info"
- [showTitle]="false">
- <span i18n>This user will be created automatically as part of the process, and it will have the necessary permissions to manage and synchronize resources across zones.</span>
- </cd-alert-panel>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('username', formDir, 'required')"
- i18n>This field is required.</span>
- <span class="invalid-feedback"
- *ngIf="multisiteSetupForm.showError('username', formDir, 'notUnique')"
- i18n>The username already exists.</span>
</div>
</div>
+ <hr *ngIf="realms.length > 1">
</div>
- <div class="ms-5"
- *ngSwitchCase="'3'">
- <div *ngIf="isMultiClusterConfigured; else exportTokenTemplate">
- <h4 class="title"
- i18n>Select Cluster</h4>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="cluster"
- i18n>Cluster</label>
- <div class="cd-col-form-input">
- <select class="form-select"
- id="cluster"
- [(ngModel)]="selectedCluster"
- formControlName="cluster"
- name="cluster">
- <option *ngFor="let cluster_detail of clusterDetailsArray"
- [value]="cluster_detail.name">
- {{ cluster_detail.cluster_alias }} - {{ cluster_detail.name }}
- </option>
- </select>
- <cd-help-text>
- <span i18n>Choose the cluster where you want to apply this multisite configuration. The selected cluster will integrate the defined Realm, Zonegroup, and Zones, enabling data synchronization and management across the multisite setup.</span>
- </cd-help-text>
- <cd-alert-panel type="info"
- [showTitle]="false">
- <span i18n>Before submitting this form, please verify that the selected cluster has an active RGW (Rados Gateway) service running.</span>
- </cd-alert-panel>
- </div>
- </div>
- </div>
- <ng-template #exportTokenTemplate>
- <h4 class="title"
- i18n>Export Token</h4>
- <div *ngFor="let realminfo of realms">
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="realmName"
- i18n>Realm Name</label>
- <div class="cd-col-form-input">
- <input id="realmName"
- name="realmName"
- type="text"
- [value]="realminfo.realm"
- readonly>
- <cd-help-text>
- <span i18n>Name of the realm that will be involved in replication.</span>
- </cd-help-text>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="token"
- i18n>Token</label>
- <div class="cd-col-form-input">
- <input id="realmToken"
- name="realmToken"
- type="text"
- [value]="realminfo.token"
- class="me-2 mb-4"
- readonly>
- <cd-copy-2-clipboard-button [source]="realminfo.token"
- [byId]="false">
- </cd-copy-2-clipboard-button>
- <cd-help-text>
- <span i18n>This field displays the token needed to import the multisite configuration into a secondary cluster. Copy this token securely and use it on the secondary cluster to replicate the current multisite setup. Ensure that the token is handled securely to prevent unauthorized access.</span>
- </cd-help-text>
- </div>
- </div>
- <hr *ngIf="realms.length > 1">
- </div>
- </ng-template>
- </div>
- </ng-container>
- </form>
- </div>
- </div>
- <div class="card-footer">
- <button class="btn btn-accent m-2 float-end"
- (click)="onNextStep()"
- aria-label="Next"
- i18n>{{ showSubmitButtonLabel() }}
- <span *ngIf="loading">
- <i [ngClass]="[icons.spinner, icons.spin]"></i>
- </span>
- </button>
- <cd-back-button class="m-2 float-end"
- aria-label="Close"
- (backAction)="onPreviousStep()"
- [name]="showCancelButtonLabel()"
- [disabled]="loading">
- </cd-back-button>
- </div>
+ </ng-template>
+ </div>
+ </ng-container>
+ </form>
</div>
- </ng-container>
- </cd-modal>
-</div>
+ </div>
+ </div>
+ <cds-modal-footer>
+ <button cdsButton="secondary"
+ (click)="onPreviousStep()"
+ [attr.aria-label]="showCancelButtonLabel()"
+ [disabled]="loading"
+ i18n>{{ showCancelButtonLabel() }}</button>
+ <button cdsButton="primary"
+ (click)="onNextStep()"
+ aria-label="Next"
+ i18n>{{ showSubmitButtonLabel() }}
+ <cds-loading [isActive]="loading"
+ [overlay]="false"
+ size="sm"
+ *ngIf="loading"></cds-loading>
+ </button>
+ </cds-modal-footer>
+</cds-modal>
-.container-fluid {
- align-items: flex-start;
- display: flex;
- padding-left: 0;
- width: 100%;
-}
-
-::ng-deep .custom-modal-content .modal-content {
- right: 40vh;
- width: 140vh;
+cds-loading {
+ margin-left: 0.5rem;
}
import { SelectMessages } from '~/app/shared/components/select/select-messages.model';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
import { NotificationService } from '~/app/shared/services/notification.service';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
import { map, switchMap } from 'rxjs/operators';
+import { BaseModal, Step } from 'carbon-components-angular';
+import { Location } from '@angular/common';
@Component({
selector: 'cd-rgw-multisite-wizard',
templateUrl: './rgw-multisite-wizard.component.html',
styleUrls: ['./rgw-multisite-wizard.component.scss']
})
-export class RgwMultisiteWizardComponent implements OnInit {
+export class RgwMultisiteWizardComponent extends BaseModal implements OnInit {
multisiteSetupForm: CdFormGroup;
currentStep: WizardStepModel;
currentStepSub: Subscription;
permissions: Permissions;
- stepTitles = ['Create Realm & Zonegroup', 'Create Zone', 'Select Cluster'];
+ stepTitles: Step[] = [
+ {
+ label: 'Create Realm & Zonegroup'
+ },
+ {
+ label: 'Create Zone'
+ },
+ {
+ label: 'Select Cluster'
+ }
+ ];
stepsToSkip: { [steps: string]: boolean } = {};
daemons: RgwDaemon[] = [];
selectedCluster = '';
private multiClusterService: MultiClusterService,
private rgwMultisiteService: RgwMultisiteService,
public notificationService: NotificationService,
- private router: Router
+ private router: Router,
+ private route: ActivatedRoute,
+ private location: Location
) {
+ super();
this.pageURL = 'rgw/multisite/configuration';
this.currentStepSub = this.wizardStepsService
.getCurrentStep()
.subscribe((step: WizardStepModel) => {
this.currentStep = step;
});
- this.currentStep.stepIndex = 1;
+ this.currentStep.stepIndex = 0;
this.createForm();
this.rgwEndpoints = {
value: [],
}
ngOnInit(): void {
+ this.open = this.route.outlet === 'modal';
this.rgwDaemonService
.list()
.pipe(
.filter((cluster) => cluster['cluster_alias'] !== 'local-cluster');
this.isMultiClusterConfigured = this.clusterDetailsArray.length > 0;
if (!this.isMultiClusterConfigured) {
- this.stepTitles = ['Create Realm & Zonegroup', 'Create Zone', 'Export Multi-site token'];
+ this.stepTitles = [
+ {
+ label: 'Create Realm & Zonegroup'
+ },
+ {
+ label: 'Create Zone'
+ },
+ {
+ label: 'Export Multi-site token'
+ }
+ ];
+ this.stepTitles.forEach((steps, index) => {
+ steps.onClick = () => (this.currentStep.stepIndex = index);
+ });
} else {
this.selectedCluster = this.clusterDetailsArray[0]['name'];
}
if (!this.wizardStepsService.isFirstStep()) {
this.wizardStepsService.moveToPreviousStep();
} else {
- this.activeModal.close();
+ this.location.back();
}
}
onSkip() {
- const stepTitle = this.stepTitles[this.currentStep.stepIndex - 1];
- this.stepsToSkip[stepTitle] = true;
+ const stepTitle = this.stepTitles[this.currentStep.stepIndex];
+ this.stepsToSkip[stepTitle.label] = true;
this.onNextStep();
}
}
import { RgwMultisiteSyncFlowModalComponent } from './rgw-multisite-sync-flow-modal/rgw-multisite-sync-flow-modal.component';
import { RgwMultisiteSyncPipeModalComponent } from './rgw-multisite-sync-pipe-modal/rgw-multisite-sync-pipe-modal.component';
import { RgwMultisiteTabsComponent } from './rgw-multisite-tabs/rgw-multisite-tabs.component';
+import {
+ ButtonModule,
+ GridModule,
+ IconModule,
+ LoadingModule,
+ ModalModule,
+ ProgressIndicatorModule
+} from 'carbon-components-angular';
@NgModule({
imports: [
TreeModule,
DataTableModule,
DashboardV3Module,
- NgbTypeaheadModule
+ NgbTypeaheadModule,
+ ModalModule,
+ GridModule,
+ ProgressIndicatorModule,
+ ButtonModule,
+ LoadingModule,
+ IconModule
],
exports: [
RgwDaemonListComponent,
NumberModule,
DropdownModule,
SelectModule,
- ComboBoxModule
+ ComboBoxModule,
+ ProgressIndicatorModule
} from 'carbon-components-angular';
import { MotdComponent } from '~/app/shared/components/motd/motd.component';
TimePickerSelectModule,
DropdownModule,
SelectModule,
- ComboBoxModule
+ ComboBoxModule,
+ ProgressIndicatorModule
],
declarations: [
SparklineComponent,
-<div class="card-body">
- <div class="row m-7">
- <nav class="col">
- <ul class="nav nav-pills flex-column"
- *ngFor="let step of steps | async; let i = index;">
- <li class="nav-item text-nowrap">
- <a class="nav-link"
- (click)="onStepClick(step)"
- [ngClass]="{active: currentStep.stepIndex === step.stepIndex}">
- <span class="circle-step"
- [ngClass]="{active: currentStep.stepIndex === step.stepIndex}"
- i18n>{{ step.stepIndex }}</span>
- <span i18n>{{ stepsTitle[i] }}</span>
- </a>
- </li>
- </ul>
- </nav>
- </div>
+<div class="indicator">
+ <cds-progress-indicator
+ orientation="vertical"
+ [steps]="stepsTitle"
+ [current]="currentStep?.stepIndex">
+ </cds-progress-indicator>
</div>
beforeEach(() => {
fixture = TestBed.createComponent(WizardComponent);
component = fixture.componentInstance;
- component.stepsTitle = ['Add Hosts', 'Review'];
+ component.stepsTitle = [
+ {
+ label: 'Add Hosts'
+ },
+ {
+ label: 'Create OSDs',
+ complete: false
+ }
+ ];
fixture.detectChanges();
});
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Step } from 'carbon-components-angular';
import * as _ from 'lodash';
import { Observable, Subscription } from 'rxjs';
})
export class WizardComponent implements OnInit, OnDestroy {
@Input()
- stepsTitle: string[];
+ stepsTitle: Step[];
steps: Observable<WizardStepModel[]>;
currentStep: WizardStepModel;
currentStepSub: Subscription;
- constructor(private stepsService: WizardStepsService) {}
+ constructor(private stepsService: WizardStepsService) {
+ this.stepsTitle?.forEach((steps, index) => {
+ steps.onClick = () => (this.currentStep.stepIndex = index);
+ });
+ }
ngOnInit(): void {
this.stepsService.setTotalSteps(this.stepsTitle.length);
@Input()
hasDetails = false;
+ @Input()
+ showInlineActions = true;
+
/**
* Auto reload time in ms - per default every 5s
* You can set it to 0, undefined or false to disable the auto reload feature in order to
}
ngAfterViewInit(): void {
- if (this.tableActions?.dropDownActions?.length) {
+ if (this.showInlineActions && this.tableActions?.dropDownActions?.length) {
this.tableColumns = [
...this.tableColumns,
{
}
tableItem.template = column.cellTemplate || this.defaultValueTpl;
-
return tableItem;
});
});
if (!_.find(this.visibleColumns, (c: CdTableColumn) => c.prop === sortProp)) {
this.userConfig.sorts = this.createSortingDefinition(this.visibleColumns[0].prop);
}
- if (this.tableActions?.dropDownActions?.length) {
+ if (this.showInlineActions && this.tableActions?.dropDownActions?.length) {
this.tableColumns = [
...this.tableColumns,
{
import { WizardStepModel } from '~/app/shared/models/wizard-steps';
-const initialStep = [{ stepIndex: 1, isComplete: false }];
+const initialStep = [{ stepIndex: 0, isComplete: false }];
@Injectable({
providedIn: 'root'
setTotalSteps(step: number) {
const steps: WizardStepModel[] = [];
- for (let i = 1; i <= step; i++) {
+ for (let i = 0; i < step; i++) {
steps.push({ stepIndex: i, isComplete: false });
}
this.steps$ = new BehaviorSubject<WizardStepModel[]>(steps);
moveToNextStep(): void {
const index = this.currentStep$.value.stepIndex;
- this.currentStep$.next(this.steps$.value[index]);
+ this.currentStep$.next(this.steps$.value[index + 1]);
}
moveToPreviousStep(): void {
- const index = this.currentStep$.value.stepIndex - 1;
+ const index = this.currentStep$.value.stepIndex;
this.currentStep$.next(this.steps$.value[index - 1]);
}
isLastStep(): boolean {
- return this.currentStep$.value.stepIndex === this.steps$.value.length;
+ return this.currentStep$.value?.stepIndex === this.steps$.value.length - 1;
}
isFirstStep(): boolean {
- return this.currentStep$.value?.stepIndex - 1 === 0;
+ return this.currentStep$.value?.stepIndex === 0;
}
}
text-secondary: vv.$dark,
text-disabled: vv.$gray-500,
icon-secondary: vv.$body-bg-alt,
- field-01: colors.$gray-10
+ field-01: colors.$gray-10,
+ interactive: vv.$primary
)
);