Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mgr/dashboard: Create OSDs section in cluster creation wizard #42583

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/pybind/mgr/dashboard/ci/cephadm/bootstrap-cluster.sh
Expand Up @@ -8,7 +8,7 @@ chmod +x /root/bin/cephadm
mkdir -p /etc/ceph
mon_ip=$(ifconfig eth0 | grep 'inet ' | awk '{ print $2}')

cephadm bootstrap --mon-ip $mon_ip --initial-dashboard-password {{ admin_password }} --allow-fqdn-hostname --dashboard-password-noupdate --shared_ceph_folder /mnt/{{ ceph_dev_folder }}
cephadm bootstrap --mon-ip $mon_ip --initial-dashboard-password {{ admin_password }} --allow-fqdn-hostname --skip-monitoring-stack --dashboard-password-noupdate --shared_ceph_folder /mnt/{{ ceph_dev_folder }}

fsid=$(cat /etc/ceph/ceph.conf | grep fsid | awk '{ print $3}')

Expand Down
Expand Up @@ -93,7 +93,10 @@ export class CreateClusterWizardHelper extends PageHelper {
}
}
cy.get('cd-modal cd-submit-button').click();
this.checkLabelExists(hostname, labels, add);
}

checkLabelExists(hostname: string, labels: string[], add: boolean) {
// Verify labels are added or removed from Labels column
// First find row with hostname, then find labels in the row
this.getTableCell(this.columnIndex.hostname, hostname)
Expand All @@ -110,4 +113,18 @@ export class CreateClusterWizardHelper extends PageHelper {
}
});
}

createOSD(deviceType: 'hdd' | 'ssd') {
// Click Primary devices Add button
cy.get('cd-osd-devices-selection-groups[name="Primary"]').as('primaryGroups');
cy.get('@primaryGroups').find('button').click();

// Select all devices with `deviceType`
cy.get('cd-osd-devices-selection-modal').within(() => {
cy.get('.modal-footer .tc_submitButton').as('addButton').should('be.disabled');
this.filterTable('Type', deviceType);
this.getTableCount('total').should('be.gte', 1);
cy.get('@addButton').click();
});
}
}
Expand Up @@ -8,7 +8,7 @@ describe('Create cluster add host page', () => {
'ceph-node-02.cephlab.com'
];
const addHost = (hostname: string, exist?: boolean) => {
createCluster.add(hostname, exist, true);
createCluster.add(hostname, exist, false);
createCluster.checkExist(hostname, true);
};

Expand All @@ -25,13 +25,18 @@ describe('Create cluster add host page', () => {
cy.get('.title').should('contain.text', 'Add Hosts');
});

it('should check existing host and add new hosts into maintenance mode', () => {
it('should check existing host and add new hosts', () => {
createCluster.checkExist(hostnames[0], true);

addHost(hostnames[1], false);
addHost(hostnames[2], false);
});

it('should verify "_no_schedule" label is added', () => {
createCluster.checkLabelExists(hostnames[1], ['_no_schedule'], true);
createCluster.checkLabelExists(hostnames[2], ['_no_schedule'], true);
});

it('should not add an existing host', () => {
createCluster.add(hostnames[0], true);
});
Expand Down
@@ -0,0 +1,38 @@
import { CreateClusterWizardHelper } from 'cypress/integration/cluster/create-cluster.po';
import { OSDsPageHelper } from 'cypress/integration/cluster/osds.po';

const osds = new OSDsPageHelper();

describe('Create cluster create osds page', () => {
const createCluster = new CreateClusterWizardHelper();

beforeEach(() => {
cy.login();
Cypress.Cookies.preserveOnce('token');
createCluster.navigateTo();
createCluster.createCluster();
cy.get('button[aria-label="Next"]').click();
});

it('should check if nav-link and title contains Create OSDs', () => {
cy.get('.nav-link').should('contain.text', 'Create OSDs');

cy.get('.title').should('contain.text', 'Create OSDs');
});

describe('when Orchestrator is available', () => {
it('should create OSDs', () => {
osds.navigateTo();
osds.getTableCount('total').as('initOSDCount');

createCluster.navigateTo();
createCluster.createCluster();
cy.get('button[aria-label="Next"]').click();

createCluster.createOSD('hdd');

cy.get('button[aria-label="Next"]').click();
cy.get('button[aria-label="Next"]').click();
});
});
});
Expand Up @@ -10,13 +10,12 @@ describe('Create Cluster Review page', () => {
createCluster.createCluster();

cy.get('button[aria-label="Next"]').click();
cy.get('button[aria-label="Next"]').click();
aaSharma14 marked this conversation as resolved.
Show resolved Hide resolved
});

describe('navigation link and title test', () => {
it('should check if nav-link and title contains Review', () => {
cy.get('.nav-link').should('contain.text', 'Review');

cy.get('.title').should('contain.text', 'Review');
aaSharma14 marked this conversation as resolved.
Show resolved Hide resolved
});
});

Expand All @@ -27,6 +26,7 @@ describe('Create Cluster Review page', () => {

// check for fields in table
createCluster.getStatusTables().should('contain.text', 'Hosts');
createCluster.getStatusTables().should('contain.text', 'Storage Capacity');
});

it('should check Hosts by Label and Host Details tables are present', () => {
Expand Down
@@ -1,5 +1,6 @@
import { CreateClusterWizardHelper } from 'cypress/integration/cluster/create-cluster.po';
import { HostsPageHelper } from 'cypress/integration/cluster/hosts.po';
import { OSDsPageHelper } from 'cypress/integration/cluster/osds.po';

describe('when cluster creation is completed', () => {
const createCluster = new CreateClusterWizardHelper();
Expand All @@ -13,6 +14,7 @@ describe('when cluster creation is completed', () => {
createCluster.navigateTo();
createCluster.createCluster();

cy.get('button[aria-label="Next"]').click();
cy.get('button[aria-label="Next"]').click();
cy.get('button[aria-label="Next"]').click();

Expand All @@ -26,9 +28,9 @@ describe('when cluster creation is completed', () => {
beforeEach(() => {
hosts.navigateTo();
});
it('should have already exited from maintenance', () => {
it('should have removed "_no_schedule" label', () => {
for (let host = 0; host < hostnames.length; host++) {
cy.get('datatable-row-wrapper').should('not.have.text', 'maintenance');
cy.get('datatable-row-wrapper').should('not.have.text', '_no_schedule');
}
});

Expand All @@ -46,4 +48,16 @@ describe('when cluster creation is completed', () => {
});
});
});

describe('OSDs page', () => {
const osds = new OSDsPageHelper();

beforeEach(() => {
osds.navigateTo();
});

it('should check if osds are created', { retries: 1 }, () => {
osds.expectTableCount('total', 2);
});
});
});
Expand Up @@ -9,6 +9,13 @@
class="bold">Hosts</td>
<td>{{ hostsCount }}</td>
</tr>
<tr>
<td i18n
class="bold">Storage Capacity</td>
<td><span i18n
*ngIf="filteredDevices && capacity">Number of devices: {{ filteredDevices.length }}. Raw capacity:
{{ capacity | dimlessBinary }}.</span></td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right to me. Maybe just Storage capaticy: x GiB or acc. to @pcuzner suggestion
"just use a table showing columns for hdd, flash showing sum for the total capacity and drive count per type - so you can do a quick visual check "

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Something like this may work too: 2xHDD xGiB, 3xFlash yGiB
what u think? @nizamial09 @epuertat

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that could work. Also I believe you are taking care of this in a separate PR right @avanthakkar ? So its best to do it separately on that PR where you are following up on the review page (#42488) because I believe this PR has went through a lot to get here. Does that works for you @avanthakkar ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I like separating them into different columns better. Its more visually catchy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that could work. Also I believe you are taking care of this in a separate PR right @avanthakkar ? So its best to do it separately on that PR where you are following up on the review page (#42488) because I believe this PR has went through a lot to get here. Does that works for you @avanthakkar ?

I've already introduced the review section changes in my current PR for gather facts itself. But yeah I agree some minor things can be introduced in later PR, maybe having a general expand cluster workflow remaining cleanups.

</tr>
</table>
</fieldset>
</div>
Expand Down
Expand Up @@ -4,6 +4,8 @@ import _ from 'lodash';

import { HostService } from '~/app/shared/api/host.service';
import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
import { InventoryDevice } from '../inventory/inventory-devices/inventory-device.model';

@Component({
selector: 'cd-create-cluster-review',
Expand All @@ -18,8 +20,10 @@ export class CreateClusterReviewComponent implements OnInit {
labelOccurrences = {};
hostsCountPerLabel: object[] = [];
uniqueLabels: Set<string> = new Set();
filteredDevices: InventoryDevice[] = [];
capacity = 0;

constructor(private hostService: HostService) {}
constructor(private hostService: HostService, public wizardStepService: WizardStepsService) {}

ngOnInit() {
this.hostsDetails = {
Expand Down Expand Up @@ -82,5 +86,8 @@ export class CreateClusterReviewComponent implements OnInit {
this.hostsByLabel['data'] = [...this.hostsCountPerLabel];
this.hostsDetails['data'] = [...this.hosts];
});

this.filteredDevices = this.wizardStepService.osdDevices;
this.capacity = this.wizardStepService.osdCapacity;
}
}
Expand Up @@ -44,6 +44,15 @@
</div>
<div *ngSwitchCase="'2'"
class="ml-5">
<h4 class="title"
i18n>Create OSDs</h4>
<br>
<div class="alignForm">
<cd-osd-form [clusterCreation]="true"></cd-osd-form>
</div>
</div>
<div *ngSwitchCase="'3'"
class="ml-5">
<cd-create-cluster-review></cd-create-cluster-review>
</div>
</ng-container>
Expand Down
Expand Up @@ -24,3 +24,7 @@ cd-hosts {
display: none;
}
}

.alignForm {
margin-left: -1%;
}
Expand Up @@ -9,6 +9,7 @@ import { CephModule } from '~/app/ceph/ceph.module';
import { CoreModule } from '~/app/core/core.module';
import { HostService } from '~/app/shared/api/host.service';
import { ConfirmationModalComponent } from '~/app/shared/components/confirmation-modal/confirmation-modal.component';
import { LoadingPanelComponent } from '~/app/shared/components/loading-panel/loading-panel.component';
import { AppConstants } from '~/app/shared/constants/app.constants';
import { ModalService } from '~/app/shared/services/modal.service';
import { WizardStepsService } from '~/app/shared/services/wizard-steps.service';
Expand All @@ -24,16 +25,19 @@ describe('CreateClusterComponent', () => {
let modalServiceShowSpy: jasmine.Spy;
const projectConstants: typeof AppConstants = AppConstants;

configureTestBed({
imports: [
HttpClientTestingModule,
RouterTestingModule,
ToastrModule.forRoot(),
SharedModule,
CoreModule,
CephModule
]
});
configureTestBed(
{
imports: [
HttpClientTestingModule,
RouterTestingModule,
ToastrModule.forRoot(),
SharedModule,
CoreModule,
CephModule
]
},
[LoadingPanelComponent]
);

beforeEach(() => {
fixture = TestBed.createComponent(CreateClusterComponent);
Expand Down Expand Up @@ -91,7 +95,7 @@ describe('CreateClusterComponent', () => {
component.onNextStep();
fixture.detectChanges();
expect(wizardStepServiceSpy).toHaveBeenCalledTimes(1);
expect(hostServiceSpy).toBeCalledTimes(2);
expect(hostServiceSpy).toBeCalledTimes(1);
});

it('should show the button labels correctly', () => {
Expand All @@ -102,6 +106,13 @@ describe('CreateClusterComponent', () => {
let cancelBtnLabel = component.showCancelButtonLabel();
expect(cancelBtnLabel).toEqual('Cancel');

component.onNextStep();
fixture.detectChanges();
submitBtnLabel = component.showSubmitButtonLabel();
expect(submitBtnLabel).toEqual('Next');
cancelBtnLabel = component.showCancelButtonLabel();
expect(cancelBtnLabel).toEqual('Back');

// Last page of the wizard
component.onNextStep();
fixture.detectChanges();
Expand Down