Skip to content

Commit 94f9a2b

Browse files
Naman MunetNaman Munet
authored andcommitted
mgr/dashborad: RGW sync policy
Fixes: https://tracker.ceph.com/issues/66576 Signed-off-by: Naman Munet <[email protected]>
1 parent d2da796 commit 94f9a2b

18 files changed

+438
-121
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,21 @@ def get_sync_status(self):
129129
@Endpoint(path='/sync-policy')
130130
@EndpointDoc("Get the sync policy")
131131
@ReadPermission
132-
def get_sync_policy(self, bucket_name='', zonegroup_name=''):
132+
def get_sync_policy(self, bucket_name='', zonegroup_name='', all_policy=None):
133133
multisite_instance = RgwMultisite()
134+
all_policy = str_to_bool(all_policy)
135+
if all_policy:
136+
sync_policy_list = []
137+
buckets = json.loads(RgwBucket().list(stats=False))
138+
for bucket in buckets:
139+
sync_policy = multisite_instance.get_sync_policy(bucket, zonegroup_name)
140+
for policy in sync_policy['groups']:
141+
policy['bucketName'] = bucket
142+
sync_policy_list.append(policy)
143+
other_sync_policy = multisite_instance.get_sync_policy(bucket_name, zonegroup_name)
144+
for policy in other_sync_policy['groups']:
145+
sync_policy_list.append(policy)
146+
return sync_policy_list
134147
return multisite_instance.get_sync_policy(bucket_name, zonegroup_name)
135148

136149
@Endpoint(path='/sync-policy-group')
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { MultisitePageHelper } from './multisite.po';
2+
3+
describe('Multisite page', () => {
4+
const multisite = new MultisitePageHelper();
5+
6+
beforeEach(() => {
7+
cy.login();
8+
multisite.navigateTo();
9+
});
10+
11+
describe('tabs and table tests', () => {
12+
it('should show two tabs', () => {
13+
multisite.getTabsCount().should('eq', 2);
14+
});
15+
16+
it('should show Configuration tab as a first tab', () => {
17+
multisite.getTabText(0).should('eq', 'Configuration');
18+
});
19+
20+
it('should show sync policy tab as a second tab', () => {
21+
multisite.getTabText(1).should('eq', 'Sync Policy');
22+
});
23+
24+
it('should show empty table in Sync Policy page', () => {
25+
multisite.getTab('Sync Policy').click();
26+
multisite.getDataTables().should('exist');
27+
multisite.getTableCount('total').should('eq', 0);
28+
});
29+
});
30+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { PageHelper } from '../page-helper.po';
2+
3+
const pages = {
4+
index: { url: '#/rgw/multisite', id: 'cd-rgw-multisite-details' }
5+
};
6+
export class MultisitePageHelper extends PageHelper {
7+
pages = pages;
8+
}

src/pybind/mgr/dashboard/frontend/jest.config.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const jestConfig = {
3333
},
3434
setupFiles: ['jest-canvas-mock'],
3535
coverageReporters: ['cobertura', 'html'],
36-
modulePathIgnorePatterns: ['<rootDir>/coverage/', '<rootDir>/node_modules/simplebar-angular'],
36+
modulePathIgnorePatterns: ['<rootDir>/coverage/', '<rootDir>/node_modules/simplebar-angular', '<rootDir>/cypress'],
3737
testMatch: ['**/*.spec.ts'],
3838
testRunner: 'jest-jasmine2'
3939
};
Lines changed: 145 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,155 @@
1-
<div class="row">
2-
<div class="col-sm-12 col-lg-12">
3-
<div>
4-
<cd-alert-panel *ngIf="!rgwModuleStatus"
5-
type="info"
6-
spacingClass="mb-3"
7-
class="d-flex align-items-center"
8-
i18n>In order to access the import/export feature, the rgw module must be enabled
1+
<nav ngbNav
2+
#nav="ngbNav"
3+
class="nav-tabs"
4+
(navChange)="onNavChange($event)">
5+
<ng-container ngbNavItem="configuration">
6+
<a ngbNavLink
7+
i18n>Configuration</a>
8+
<ng-template ngbNavContent>
9+
<div>
10+
<cd-alert-panel
11+
*ngIf="!rgwModuleStatus"
12+
type="info"
13+
spacingClass="mb-3"
14+
class="d-flex align-items-center"
15+
i18n
16+
>In order to access the import/export feature, the rgw module must be enabled
917

10-
<button class="btn btn-light mx-2"
11-
type="button"
12-
(click)="enableRgwModule()">Enable</button>
13-
</cd-alert-panel>
14-
<cd-alert-panel *ngIf="restartGatewayMessage"
15-
type="warning"
16-
spacingClass="mb-3"
17-
i18n>Please restart all Ceph Object Gateway instances in all zones to ensure consistent multisite configuration updates.
18-
<a class="text-decoration-underline"
19-
routerLink="/services">
20-
Cluster->Services</a>
21-
</cd-alert-panel>
22-
<cd-table-actions class="btn-group mb-4 me-2"
23-
[permission]="permission"
24-
[selection]="selection"
25-
[tableActions]="createTableActions">
26-
</cd-table-actions>
27-
<span *ngIf="showMigrateAction">
28-
<cd-table-actions class="btn-group mb-4 me-2 secondary"
29-
[permission]="permission"
30-
[btnColor]="'light'"
31-
[selection]="selection"
32-
[tableActions]="migrateTableAction">
18+
<button class="btn btn-light mx-2"
19+
type="button"
20+
(click)="enableRgwModule()">
21+
Enable
22+
</button>
23+
</cd-alert-panel>
24+
<cd-alert-panel
25+
*ngIf="restartGatewayMessage"
26+
type="warning"
27+
spacingClass="mb-3"
28+
i18n>Please restart all Ceph Object Gateway instances in all zones to ensure consistent
29+
multisite configuration updates.
30+
<a class="text-decoration-underline"
31+
routerLink="/services"> Cluster->Services</a>
32+
</cd-alert-panel>
33+
<cd-table-actions
34+
class="btn-group mb-4 me-2"
35+
[permission]="permission"
36+
[selection]="selection"
37+
[tableActions]="createTableActions"
38+
>
3339
</cd-table-actions>
34-
</span>
35-
<cd-table-actions class="btn-group mb-4 me-2"
36-
[permission]="permission"
37-
[btnColor]="'light'"
38-
[selection]="selection"
39-
[tableActions]="importAction">
40-
</cd-table-actions>
41-
<cd-table-actions class="btn-group mb-4 me-2"
42-
[permission]="permission"
43-
[btnColor]="'light'"
44-
[selection]="selection"
45-
[tableActions]="exportAction">
46-
</cd-table-actions>
47-
</div>
48-
<div class="card">
49-
<div class="card-header"
50-
i18n>Topology Viewer</div>
51-
<div class="card-body">
52-
<div class="row">
53-
<div class="col-sm-6 col-lg-6 tree-container">
54-
<i *ngIf="loadingIndicator"
55-
[ngClass]="[icons.large, icons.spinner, icons.spin]"></i>
56-
<tree-root #tree
57-
[nodes]="nodes"
58-
[options]="treeOptions"
59-
(updateData)="onUpdateData()">
60-
<ng-template #treeNodeTemplate
61-
let-node>
62-
<span *ngIf="node.data.name"
63-
class="me-3">
64-
<span *ngIf="(node.data.show_warning)">
65-
<i class="text-danger"
40+
<span *ngIf="showMigrateAction">
41+
<cd-table-actions
42+
class="btn-group mb-4 me-2 secondary"
43+
[permission]="permission"
44+
[btnColor]="'light'"
45+
[selection]="selection"
46+
[tableActions]="migrateTableAction"
47+
>
48+
</cd-table-actions>
49+
</span>
50+
<cd-table-actions
51+
class="btn-group mb-4 me-2"
52+
[permission]="permission"
53+
[btnColor]="'light'"
54+
[selection]="selection"
55+
[tableActions]="importAction"
56+
>
57+
</cd-table-actions>
58+
<cd-table-actions
59+
class="btn-group mb-4 me-2"
60+
[permission]="permission"
61+
[btnColor]="'light'"
62+
[selection]="selection"
63+
[tableActions]="exportAction">
64+
</cd-table-actions>
65+
</div>
66+
<div class="card">
67+
<div class="card-header"
68+
i18n>Topology Viewer</div>
69+
<div class="card-body">
70+
<div class="row">
71+
<div class="col-sm-6 col-lg-6 tree-container">
72+
<i *ngIf="loadingIndicator"
73+
[ngClass]="[icons.large, icons.spinner, icons.spin]"></i>
74+
<tree-root
75+
#tree
76+
[nodes]="nodes"
77+
[options]="treeOptions"
78+
(updateData)="onUpdateData()">
79+
<ng-template
80+
#treeNodeTemplate
81+
let-node>
82+
<span *ngIf="node.data.name"
83+
class="me-3">
84+
<span *ngIf="node.data.show_warning">
85+
<i
86+
class="text-danger"
6687
i18n-title
6788
[title]="node.data.warning_message"
68-
[ngClass]="icons.danger"></i>
69-
</span>
70-
<i [ngClass]="node.data.icon"></i>
89+
[ngClass]="icons.danger"
90+
></i>
91+
</span>
92+
<i [ngClass]="node.data.icon"></i>
7193
{{ node.data.name }}
72-
</span>
73-
<span class="badge badge-success me-2"
74-
*ngIf="node.data.is_default">
75-
default
76-
</span>
77-
<span class="badge badge-warning me-2"
78-
*ngIf="node.data.is_master">
79-
master
80-
</span>
81-
<span class="badge badge-warning me-2"
82-
*ngIf="node.data.secondary_zone">
83-
secondary-zone
84-
</span>
85-
<div class="btn-group align-inline-btns"
86-
*ngIf="node.isFocused"
87-
role="group">
88-
<div [title]="editTitle"
89-
i18n-title>
90-
<button type="button"
91-
class="btn btn-light dropdown-toggle-split ms-1"
92-
(click)="openModal(node, true)"
93-
[disabled]="getDisable() || node.data.secondary_zone">
94-
<i [ngClass]="[icons.edit]"></i>
95-
</button>
96-
</div>
97-
<div [title]="deleteTitle"
98-
i18n-title>
99-
<button type="button"
100-
class="btn btn-light ms-1"
101-
[disabled]="isDeleteDisabled(node) || node.data.secondary_zone"
102-
(click)="delete(node)">
103-
<i [ngClass]="[icons.destroy]"></i>
104-
</button>
94+
</span>
95+
<span class="badge badge-success me-2"
96+
*ngIf="node.data.is_default">
97+
default
98+
</span>
99+
<span class="badge badge-warning me-2"
100+
*ngIf="node.data.is_master"> master </span>
101+
<span class="badge badge-warning me-2"
102+
*ngIf="node.data.secondary_zone">
103+
secondary-zone
104+
</span>
105+
<div class="btn-group align-inline-btns"
106+
*ngIf="node.isFocused"
107+
role="group">
108+
<div [title]="editTitle"
109+
i18n-title>
110+
<button
111+
type="button"
112+
class="btn btn-light dropdown-toggle-split ms-1"
113+
(click)="openModal(node, true)"
114+
[disabled]="getDisable() || node.data.secondary_zone">
115+
<i [ngClass]="[icons.edit]"></i>
116+
</button>
117+
</div>
118+
<div [title]="deleteTitle"
119+
i18n-title>
120+
<button
121+
type="button"
122+
class="btn btn-light ms-1"
123+
[disabled]="isDeleteDisabled(node) || node.data.secondary_zone"
124+
(click)="delete(node)">
125+
<i [ngClass]="[icons.destroy]"></i>
126+
</button>
127+
</div>
105128
</div>
106-
</div>
107-
</ng-template>
108-
</tree-root>
109-
</div>
110-
<div class="col-sm-6 col-lg-6 metadata"
111-
*ngIf="metadata">
112-
<legend>{{ metadataTitle }}</legend>
113-
<div>
114-
<cd-table-key-value cdTableDetail
115-
[data]="metadata">
116-
</cd-table-key-value>
129+
</ng-template>
130+
</tree-root>
131+
</div>
132+
<div class="col-sm-6 col-lg-6 metadata"
133+
*ngIf="metadata">
134+
<legend>{{ metadataTitle }}</legend>
135+
<div>
136+
<cd-table-key-value
137+
cdTableDetail
138+
[data]="metadata"></cd-table-key-value>
139+
</div>
117140
</div>
118141
</div>
119142
</div>
120143
</div>
121-
</div>
122-
</div>
123-
</div>
144+
</ng-template>
145+
</ng-container>
146+
<ng-container ngbNavItem="syncPolicy">
147+
<a ngbNavLink
148+
i18n>Sync Policy</a>
149+
<ng-template ngbNavContent>
150+
<cd-rgw-multisite-sync-policy></cd-rgw-multisite-sync-policy>
151+
</ng-template>
152+
</ng-container>
153+
</nav>
154+
155+
<div [ngbNavOutlet]="nav"></div>

src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { SharedModule } from '~/app/shared/shared.module';
88
import { RgwMultisiteDetailsComponent } from './rgw-multisite-details.component';
99
import { RouterTestingModule } from '@angular/router/testing';
1010
import { configureTestBed } from '~/testing/unit-test-helper';
11+
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
1112

1213
describe('RgwMultisiteDetailsComponent', () => {
1314
let component: RgwMultisiteDetailsComponent;
@@ -21,7 +22,8 @@ describe('RgwMultisiteDetailsComponent', () => {
2122
TreeModule,
2223
SharedModule,
2324
ToastrModule.forRoot(),
24-
RouterTestingModule
25+
RouterTestingModule,
26+
NgbNavModule
2527
]
2628
});
2729

src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
267267
}
268268
});
269269
}
270-
271270
/* setConfigValues() {
272271
this.rgwDaemonService
273272
.setMultisiteConfig(
@@ -589,4 +588,19 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
589588
}
590589
);
591590
}
591+
592+
onNavChange(event: any) {
593+
if (event.nextId == 'configuration') {
594+
this.metadata = null;
595+
/*
596+
It is a known issue with angular2-tree package when tree is hidden (for example inside tab or modal),
597+
it is not rendered when it becomes visible. Solution is to call this.tree.sizeChanged() which recalculates
598+
the rendered nodes according to the actual viewport size. (https://angular2-tree.readme.io/docs/common-issues)
599+
*/
600+
setTimeout(() => {
601+
this.tree.sizeChanged();
602+
this.onUpdateData();
603+
}, 200);
604+
}
605+
}
592606
}

0 commit comments

Comments
 (0)