Skip to content

Commit e47613c

Browse files
authored
Merge pull request ceph#62410 from rhcs-dashboard/custom-pool-dashboard
mgr/dashboard: use existing pools for cephfs vol creation
2 parents 1e333f3 + f898376 commit e47613c

File tree

12 files changed

+302
-20
lines changed

12 files changed

+302
-20
lines changed

src/pybind/mgr/dashboard/controllers/cephfs.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import json
55
import os
66
from collections import defaultdict
7-
from typing import Any, Dict, List
7+
from typing import Any, Dict, List, Optional
88

99
import cephfs
1010
import cherrypy
@@ -17,7 +17,8 @@
1717
from ..services.exception import handle_cephfs_error
1818
from ..tools import ViewCache, str_to_bool
1919
from . import APIDoc, APIRouter, DeletePermission, Endpoint, EndpointDoc, \
20-
RESTController, UIRouter, UpdatePermission, allow_empty_body
20+
ReadPermission, RESTController, UIRouter, UpdatePermission, \
21+
allow_empty_body
2122

2223
GET_QUOTAS_SCHEMA = {
2324
'max_bytes': (int, ''),
@@ -42,10 +43,15 @@ def __init__(self): # pragma: no cover
4243
self.cephfs_clients = {}
4344

4445
def list(self):
45-
fsmap = mgr.get("fs_map")
46-
return fsmap['filesystems']
47-
48-
def create(self, name: str, service_spec: Dict[str, Any]):
46+
return CephFS_.list_filesystems(all_info=True)
47+
48+
def create(
49+
self,
50+
name: str,
51+
service_spec: Dict[str, Any],
52+
data_pool: Optional[str] = None,
53+
metadata_pool: Optional[str] = None
54+
):
4955
service_spec_str = '1 '
5056
if 'labels' in service_spec['placement']:
5157
for label in service_spec['placement']['labels']:
@@ -56,8 +62,17 @@ def create(self, name: str, service_spec: Dict[str, Any]):
5662
service_spec_str += f'{host} '
5763
service_spec_str = service_spec_str[:-1]
5864

59-
error_code, _, err = mgr.remote('volumes', '_cmd_fs_volume_create', None,
60-
{'name': name, 'placement': service_spec_str})
65+
error_code, _, err = mgr.remote(
66+
'volumes',
67+
'_cmd_fs_volume_create',
68+
None,
69+
{
70+
'name': name,
71+
'placement': service_spec_str,
72+
'data_pool': data_pool,
73+
'meta_pool': metadata_pool
74+
}
75+
)
6176
if error_code != 0:
6277
raise RuntimeError(
6378
f'Error creating volume {name} with placement {str(service_spec)}: {err}')
@@ -720,6 +735,19 @@ def ls_dir(self, fs_id, path=None, depth=1):
720735
paths = []
721736
return paths
722737

738+
@Endpoint('GET', path='/used-pools')
739+
@ReadPermission
740+
def ls_used_pools(self):
741+
"""
742+
This API is created just to list all the used pools to the UI
743+
so that it can be used for different validation purposes within
744+
the UI
745+
"""
746+
pools = []
747+
for fs in CephFS_.list_filesystems(all_info=True):
748+
pools.extend(fs['mdsmap']['data_pools'] + [fs['mdsmap']['metadata_pool']])
749+
return pools
750+
723751

724752
@APIRouter('/cephfs/subvolume', Scope.CEPHFS)
725753
@APIDoc('CephFS Subvolume Management API', 'CephFSSubvolume')

src/pybind/mgr/dashboard/frontend/cypress/e2e/common/forms-helper.feature.po.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ And('enter {string} {string}', (field: string, value: string) => {
99
cy.get(`input[id=${field}]`).clear().type(value);
1010
});
1111

12+
/**
13+
* Ticks a checkbox in the form
14+
* @param field name of the field that needs to be filled out.
15+
*/
16+
And('checks {string}', (field: string) => {
17+
cy.get('cds-checkbox span').contains(field).click();
18+
});
19+
1220
/**
1321
* Fills in the given field using the value provided
1422
* @param field ID of the field that needs to be filled out.
@@ -95,6 +103,6 @@ Then('I should see an error in {string} field', (field: string) => {
95103
});
96104

97105
And('select {string} {string}', (selectionName: string, option: string) => {
98-
cy.get(`select[name=${selectionName}]`).select(option);
99-
cy.get(`select[name=${selectionName}] option:checked`).contains(option);
106+
cy.get(`select[id=${selectionName}]`).select(option);
107+
cy.get(`select[id=${selectionName}] option:checked`).contains(option);
100108
});

src/pybind/mgr/dashboard/frontend/cypress/e2e/common/urls.po.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export class UrlsCollection extends PageHelper {
4343

4444
// CephFS
4545
cephfs: { url: '#/cephfs/fs', id: 'cd-cephfs-list' },
46-
'create cephfs': { url: '#/cephfs/fs/create', id: 'cd-cephfs-form' }
46+
'create cephfs': { url: '#/cephfs/fs/create', id: 'cd-cephfs-form' },
47+
48+
// Pools
49+
pools: { url: '/#pool', id: 'cd-pool-list' },
50+
'create pool': { url: '#/pool/create', id: 'cd-pool-form' }
4751
};
4852
}

src/pybind/mgr/dashboard/frontend/cypress/e2e/filesystems/filesystems.e2e-spec.feature

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,47 @@ Feature: CephFS Management
2929
And I confirm the resource "test_cephfs"
3030
And I click on "Remove File System" button
3131
Then I should not see a row with "test_cephfs"
32+
33+
Scenario Outline: Create two cephfs pools for attaching to a volume
34+
Given I am on the "create pool" page
35+
And enter "name" "<pool_name>"
36+
And select "poolType" "<type>"
37+
And select options "<application>"
38+
And I click on "Create Pool" button
39+
Then I should see a row with "<pool_name>"
40+
41+
Examples:
42+
| pool_name | type | application |
43+
| e2e_cephfs_data | replicated | cephfs |
44+
| e2e_cephfs_meta | replicated | cephfs |
45+
46+
Scenario Outline: Create a CephFS Volume with pre-created pools
47+
Given I am on the "create cephfs" page
48+
And enter "name" "<fs_name>"
49+
And checks "Use existing pools"
50+
And select "dataPool" "<data_pool>"
51+
And select "metadataPool" "<metadata_pool>"
52+
And I click on "Create File System" button
53+
Then I should see a row with "<fs_name>"
54+
55+
Examples:
56+
| fs_name | data_pool | metadata_pool |
57+
| e2e_custom_pool_cephfs | e2e_cephfs_data | e2e_cephfs_meta |
58+
59+
Scenario Outline: Remove CephFS Volume that has pre-created pools
60+
Given I am on the "cephfs" page
61+
And I select a row "<fs_name>"
62+
And I click on "Remove" button from the table actions
63+
Then I should see the carbon modal
64+
And I confirm the resource "<fs_name>"
65+
And I click on "Remove File System" button
66+
Then I should not see a row with "<fs_name>"
67+
68+
# verify pools associated with the volume is removed
69+
Given I am on the "pools" page
70+
Then I should not see a row with "<data_pool>"
71+
And I should not see a row with "<metadata_pool>"
72+
73+
Examples:
74+
| fs_name | data_pool | metadata_pool |
75+
| e2e_custom_pool_cephfs | e2e_cephfs_data | e2e_cephfs_meta |

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.html

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,107 @@
5353
</ng-template>
5454
</div>
5555

56+
<div class="form-item"
57+
*ngIf="!editing">
58+
<cds-checkbox id="customPools"
59+
name="customPools"
60+
formControlName="customPools"
61+
i18n>Use existing pools
62+
<cd-help-text>Allows you to use replicated pools with 'cephfs' application tag that are already created.</cd-help-text>
63+
</cds-checkbox>
64+
65+
<cd-alert-panel *ngIf="pools.length < 2"
66+
type="info"
67+
spacingClass="mt-1"
68+
i18n>
69+
You need to have atleast 2 pools that are empty, applied with cephfs label and not erasure-coded.
70+
</cd-alert-panel>
71+
</div>
72+
73+
<!-- Data pool -->
74+
<div class="form-item"
75+
*ngIf="form.get('customPools')?.value || editing">
76+
<cds-text-label for="dataPool"
77+
i18n
78+
*ngIf="editing">Data pool
79+
<input cdsText
80+
type="text"
81+
placeholder="Pool name..."
82+
id="dataPool"
83+
name="dataPool"
84+
formControlName="dataPool">
85+
</cds-text-label>
86+
<cds-select label="Data pool"
87+
for="dataPool"
88+
name="dataPool"
89+
id="dataPool"
90+
formControlName="dataPool"
91+
(valueChange)="onPoolChange($event)"
92+
cdRequiredField="Data pool"
93+
[invalid]="!form.controls.dataPool.valid && form.controls.dataPool.dirty"
94+
[invalidText]="dataPoolError"
95+
*ngIf="!editing">
96+
<option *ngIf="dataPools === null"
97+
[ngValue]="null"
98+
i18n>Loading...</option>
99+
<option *ngIf="dataPools !== null && dataPools?.length === 0"
100+
[ngValue]="null"
101+
i18n>-- No cephfs pools available --</option>
102+
<option *ngIf="dataPools !== null && dataPools?.length > 0"
103+
[ngValue]="null"
104+
i18n>-- Select a pool --</option>
105+
<option *ngFor="let pool of dataPools"
106+
[value]="pool?.pool_name">{{ pool?.pool_name }}</option>
107+
</cds-select>
108+
<ng-template #dataPoolError>
109+
<span class="invalid-feedback"
110+
*ngIf="form.showError('dataPool', formDir, 'required')"
111+
i18n>This field is required!</span>
112+
</ng-template>
113+
</div>
114+
115+
<!-- Metadata pool -->
116+
<div class="form-item"
117+
*ngIf="form.get('customPools')?.value || editing">
118+
<cds-text-label for="metadataPool"
119+
i18n
120+
*ngIf="editing">Metadata pool
121+
<input cdsText
122+
type="text"
123+
placeholder="Pool name..."
124+
id="metadataPool"
125+
name="metadataPool"
126+
formControlName="metadataPool">
127+
</cds-text-label>
128+
<cds-select label="Metadata pool"
129+
for="metadataPool"
130+
name="metadataPool"
131+
id="metadataPool"
132+
formControlName="metadataPool"
133+
cdRequiredField="Metadata pool"
134+
[invalid]="!form.controls.metadataPool.valid && form.controls.metadataPool.dirty"
135+
[invalidText]="metadataPoolError"
136+
(valueChange)="onPoolChange($event, true)"
137+
*ngIf="!editing">
138+
<option *ngIf="metadatPools === null"
139+
[ngValue]="null"
140+
i18n>Loading...</option>
141+
<option *ngIf="metadatPools !== null && metadatPools?.length === 0"
142+
[ngValue]="null"
143+
i18n>-- No cephfs pools available --</option>
144+
<option *ngIf="metadatPools !== null && metadatPools?.length > 0"
145+
[ngValue]="null"
146+
i18n>-- Select a pool --</option>
147+
<option *ngFor="let pool of metadatPools"
148+
[value]="pool?.pool_name">{{ pool?.pool_name }}</option>
149+
</cds-select>
150+
<ng-template #metadataPoolError>
151+
<span class="invalid-feedback"
152+
*ngIf="form.showError('metadataPool', formDir, 'required')"
153+
i18n>This field is required!</span>
154+
</ng-template>
155+
</div>
156+
56157
<ng-container *ngIf="orchStatus.available">
57158
<!-- Placement -->
58159
<div class="form-item"

src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-form/cephfs-form.component.spec.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import { ReactiveFormsModule } from '@angular/forms';
1010
import { By } from '@angular/platform-browser';
1111
import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
1212
import { of } from 'rxjs';
13-
import { ComboBoxModule, GridModule, InputModule, SelectModule } from 'carbon-components-angular';
13+
import {
14+
CheckboxModule,
15+
ComboBoxModule,
16+
GridModule,
17+
InputModule,
18+
SelectModule
19+
} from 'carbon-components-angular';
1420

1521
describe('CephfsVolumeFormComponent', () => {
1622
let component: CephfsVolumeFormComponent;
@@ -29,7 +35,8 @@ describe('CephfsVolumeFormComponent', () => {
2935
GridModule,
3036
InputModule,
3137
SelectModule,
32-
ComboBoxModule
38+
ComboBoxModule,
39+
CheckboxModule
3340
],
3441
declarations: [CephfsVolumeFormComponent]
3542
});

0 commit comments

Comments
 (0)