@Endpoint(path='/sync-policy')
@EndpointDoc("Get the sync policy")
@ReadPermission
- def get_sync_policy(self, bucket_name='', zonegroup_name='', all_policy=None):
+ def get_sync_policy(self, bucket_name='', zonegroup_name='', all_policy=None, daemon_name=''):
multisite_instance = RgwMultisite()
all_policy = str_to_bool(all_policy)
if all_policy:
sync_policy_list = []
- buckets = json.loads(RgwBucket().list(stats=False))
- zonegroups_info = RgwMultisite().get_all_zonegroups_info()
- default_zonegroup = ''
- if 'zonegroups' in zonegroups_info and 'default_zonegroup' in zonegroups_info:
- default_zonegroup = next(
- (zonegroup['name'] for zonegroup in zonegroups_info['zonegroups']
- if 'id' in zonegroup and 'name' in zonegroup
- and zonegroup['id'] == zonegroups_info['default_zonegroup']),
- ''
- )
+ # Filter buckets by daemon_name to only get buckets from that daemon's zone/zonegroup
+ buckets = json.loads(RgwBucket().list(
+ stats=False, daemon_name=daemon_name if daemon_name else None))
+ # Get zonegroup from daemon if daemon_name is provided, otherwise use default
+ target_zonegroup = ''
+ if daemon_name:
+ target_zonegroup = (
+ multisite_instance.get_zonegroup_from_daemon(daemon_name) or '')
+ if not target_zonegroup:
+ zonegroups_info = RgwMultisite().get_all_zonegroups_info()
+ if 'zonegroups' in zonegroups_info and 'default_zonegroup' in zonegroups_info:
+ target_zonegroup = next(
+ (zonegroup['name'] for zonegroup in zonegroups_info['zonegroups']
+ if 'id' in zonegroup and 'name' in zonegroup
+ and zonegroup['id'] == zonegroups_info['default_zonegroup']),
+ ''
+ )
for bucket in buckets:
- sync_policy = multisite_instance.get_sync_policy(bucket, zonegroup_name)
- for policy in sync_policy['groups']:
- policy['bucketName'] = bucket
- sync_policy_list.append(policy)
- other_sync_policy = multisite_instance.get_sync_policy(bucket_name, zonegroup_name)
+ try:
+ sync_policy = multisite_instance.get_sync_policy(
+ bucket, zonegroup_name, daemon_name)
+ for policy in sync_policy['groups']:
+ policy['bucketName'] = bucket
+ sync_policy_list.append(policy)
+ except DashboardException:
+ # Skip buckets that don't have sync policies or aren't accessible
+ logger.debug("Skipping bucket %s - no sync policy or not accessible", bucket)
+ continue
+ other_sync_policy = multisite_instance.get_sync_policy(
+ bucket_name, zonegroup_name, daemon_name)
for policy in other_sync_policy['groups']:
- policy['zonegroup'] = default_zonegroup
+ policy['zonegroup'] = target_zonegroup
sync_policy_list.append(policy)
return sync_policy_list
- return multisite_instance.get_sync_policy(bucket_name, zonegroup_name)
+ return multisite_instance.get_sync_policy(bucket_name, zonegroup_name, daemon_name)
@Endpoint(path='/sync-policy-group')
@EndpointDoc("Get the sync policy group")
@ReadPermission
- def get_sync_policy_group(self, group_id: str, bucket_name=''):
+ def get_sync_policy_group(self, group_id: str, bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.get_sync_policy_group(group_id, bucket_name)
+ return multisite_instance.get_sync_policy_group(group_id, bucket_name, '', daemon_name)
@Endpoint(method='POST', path='/sync-policy-group')
@EndpointDoc("Create the sync policy group")
@CreatePermission
- def create_sync_policy_group(self, group_id: str, status: str, bucket_name=''):
+ def create_sync_policy_group(self, group_id: str, status: str, bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.create_sync_policy_group(group_id, status, bucket_name, True)
+ return multisite_instance.create_sync_policy_group(
+ group_id, status, bucket_name, True, daemon_name)
@Endpoint(method='PUT', path='/sync-policy-group')
@EndpointDoc("Update the sync policy group")
@UpdatePermission
- def update_sync_policy_group(self, group_id: str, status: str, bucket_name=''):
+ def update_sync_policy_group(self, group_id: str, status: str, bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.update_sync_policy_group(group_id, status, bucket_name, True)
+ return multisite_instance.update_sync_policy_group(
+ group_id, status, bucket_name, True, daemon_name)
@Endpoint(method='DELETE', path='/sync-policy-group')
@EndpointDoc("Remove the sync policy group")
@DeletePermission
- def remove_sync_policy_group(self, group_id: str, bucket_name=''):
+ def remove_sync_policy_group(self, group_id: str, bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.remove_sync_policy_group(group_id, bucket_name, True)
+ return multisite_instance.remove_sync_policy_group(group_id, bucket_name, True, daemon_name)
@Endpoint(method='PUT', path='/sync-flow')
@EndpointDoc("Create or update the sync flow")
source_zone: Optional[str] = None,
destination_zone: Optional[str] = None,
zones: Optional[Dict[str, List]] = None,
- bucket_name=''):
+ bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.create_sync_flow(group_id, flow_id, flow_type, zones,
- bucket_name, source_zone, destination_zone, True)
+ return multisite_instance.create_sync_flow(
+ group_id, flow_id, flow_type, zones, bucket_name, source_zone,
+ destination_zone, True, daemon_name)
@Endpoint(method='DELETE', path='/sync-flow')
@EndpointDoc("Remove the sync flow")
@DeletePermission
def remove_sync_flow(self, flow_id: str, flow_type: str, group_id: str,
source_zone='', destination_zone='', zones: Optional[List[str]] = None,
- bucket_name=''):
+ bucket_name='', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.remove_sync_flow(group_id, flow_id, flow_type, source_zone,
- destination_zone, zones, bucket_name, True)
+ return multisite_instance.remove_sync_flow(
+ group_id, flow_id, flow_type, source_zone, destination_zone,
+ zones, bucket_name, True, daemon_name)
@Endpoint(method='PUT', path='/sync-pipe')
@EndpointDoc("Create or update the sync pipe")
destination_zones: Dict[str, Any],
source_bucket: str = '',
destination_bucket: str = '', bucket_name: str = '',
- user: str = '', mode: str = ''):
+ user: str = '', mode: str = '', daemon_name=''):
multisite_instance = RgwMultisite()
return multisite_instance.create_sync_pipe(group_id, pipe_id, source_zones,
destination_zones, source_bucket,
destination_bucket, bucket_name, True,
- user, mode)
+ user, mode, daemon_name)
@Endpoint(method='DELETE', path='/sync-pipe')
@EndpointDoc("Remove the sync pipe")
def remove_sync_pipe(self, group_id: str, pipe_id: str,
source_zones: Optional[List[str]] = None,
destination_zones: Optional[List[str]] = None,
- bucket_name: str = ''):
+ bucket_name: str = '', daemon_name=''):
multisite_instance = RgwMultisite()
- return multisite_instance.remove_sync_pipe(group_id, pipe_id, source_zones,
- destination_zones, bucket_name, True)
+ return multisite_instance.remove_sync_pipe(
+ group_id, pipe_id, source_zones, destination_zones,
+ bucket_name, True, daemon_name)
@APIRouter('/rgw/daemon', Scope.RGW)
private rgwRoleUrlPrefix = '/rgw/roles';
private rgwBuckerUrlPrefix = '/rgw/bucket';
private rgwAccountsUrlPrefix = '/rgw/accounts';
+ private rgwMultisiteSyncPolicyPrefix = '/rgw/multisite/sync-policy';
permissions: Permissions;
featureToggleMap$: FeatureTogglesMap$;
isRgwRoute =
document.location.href.includes(this.rgwUserUrlPrefix) ||
document.location.href.includes(this.rgwBuckerUrlPrefix) ||
document.location.href.includes(this.rgwRoleUrlPrefix) ||
- document.location.href.includes(this.rgwAccountsUrlPrefix);
+ document.location.href.includes(this.rgwAccountsUrlPrefix) ||
+ document.location.href.includes(this.rgwMultisiteSyncPolicyPrefix);
constructor(
private authStorageService: AuthStorageService,
this.rgwBuckerUrlPrefix,
this.rgwUserUrlPrefix,
this.rgwRoleUrlPrefix,
- this.rgwAccountsUrlPrefix
+ this.rgwAccountsUrlPrefix,
+ this.rgwMultisiteSyncPolicyPrefix
].some((urlPrefix) => this.router.url.startsWith(urlPrefix)))
)
);
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
-import { configureTestBed } from '~/testing/unit-test-helper';
+import { configureTestBed, RgwHelper } from '~/testing/unit-test-helper';
import { RgwMultisiteService } from './rgw-multisite.service';
import { BlockUIModule } from 'ng-block-ui';
import { ToastrModule } from 'ngx-toastr';
beforeEach(() => {
service = TestBed.inject(RgwMultisiteService);
httpTesting = TestBed.inject(HttpTestingController);
+ RgwHelper.selectDaemon();
});
afterEach(() => {
it('should fetch all the sync policy related or un-related to a bucket', () => {
service.getSyncPolicy('', '', true).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy?all_policy=true');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy?${RgwHelper.DAEMON_QUERY_PARAM}&all_policy=true`
+ );
expect(req.request.method).toBe('GET');
req.flush(mockSyncPolicyData);
});
it('should create Sync Policy Group w/o bucket_name', () => {
const postData = { group_id: 'test', status: 'enabled' };
service.createSyncPolicyGroup(postData).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy-group');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy-group?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(postData);
req.flush(null);
it('should create Sync Policy Group with bucket_name', () => {
const postData = { group_id: 'test', status: 'enabled', bucket_name: 'test' };
service.createSyncPolicyGroup(postData).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy-group');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy-group?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual(postData);
req.flush(null);
it('should modify Sync Policy Group', () => {
const postData = { group_id: 'test', status: 'enabled', bucket_name: 'test' };
service.modifySyncPolicyGroup(postData).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy-group');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy-group?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(postData);
req.flush(null);
it('should remove Sync Policy Group', () => {
const group_id = 'test';
service.removeSyncPolicyGroup(group_id).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy-group/' + group_id);
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy-group/${group_id}?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('DELETE');
req.flush(null);
});
it('should fetch the sync policy group with given group_id and bucket_name', () => {
service.getSyncPolicyGroup('test', 'test').subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-policy-group/test?bucket_name=test');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-policy-group/test?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_name=test`
+ );
expect(req.request.method).toBe('GET');
req.flush(mockSyncPolicyData[1]);
});
zones: ['zone1-zg1-realm1']
};
service.createEditSyncFlow(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-flow');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-flow?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
destination_zone: ['zone1-zg2-realm2']
};
service.createEditSyncFlow(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-flow');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-flow?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
zones: ['zone1-zg1-realm1', 'zone2-zg1-realm1']
};
service.createEditSyncFlow(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-flow');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-flow?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
destination_zone: ['zone1-zg2-realm2', 'zone2-zg2-realm2']
};
service.createEditSyncFlow(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-flow');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-flow?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
it('should remove Symmetrical Sync flow', () => {
service.removeSyncFlow('test', 'symmetrical', 'test', 'new-bucket').subscribe();
const req = httpTesting.expectOne(
- `api/rgw/multisite/sync-flow/test/symmetrical/test?bucket_name=new-bucket`
+ `api/rgw/multisite/sync-flow/test/symmetrical/test?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_name=new-bucket`
);
expect(req.request.method).toBe('DELETE');
req.flush(null);
it('should remove Directional Sync flow', () => {
service.removeSyncFlow('test', 'directional', 'test', 'new-bucket').subscribe();
const req = httpTesting.expectOne(
- `api/rgw/multisite/sync-flow/test/directional/test?bucket_name=new-bucket`
+ `api/rgw/multisite/sync-flow/test/directional/test?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_name=new-bucket`
);
expect(req.request.method).toBe('DELETE');
req.flush(null);
group_id: 'sync-grp'
};
service.createEditSyncPipe(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-pipe');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-pipe?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
group_id: 'sync-grp'
};
service.createEditSyncFlow(payload).subscribe();
- const req = httpTesting.expectOne('api/rgw/multisite/sync-flow');
+ const req = httpTesting.expectOne(
+ `api/rgw/multisite/sync-flow?${RgwHelper.DAEMON_QUERY_PARAM}`
+ );
expect(req.request.method).toBe('PUT');
expect(req.request.body).toEqual(payload);
req.flush(null);
it('should remove Sync Pipe', () => {
service.removeSyncPipe('test', 'sync-grp', 'new-bucket').subscribe();
const req = httpTesting.expectOne(
- `api/rgw/multisite/sync-pipe/sync-grp/test?bucket_name=new-bucket`
+ `api/rgw/multisite/sync-pipe/sync-grp/test?${RgwHelper.DAEMON_QUERY_PARAM}&bucket_name=new-bucket`
);
expect(req.request.method).toBe('DELETE');
req.flush(null);
}
getSyncPolicy(bucketName?: string, zonegroup?: string, fetchAllPolicy = false) {
- let params = new HttpParams();
- if (bucketName) {
- params = params.append('bucket_name', bucketName);
- }
- if (zonegroup) {
- params = params.append('zonegroup_name', zonegroup);
- }
- // fetchAllPolicy - if true, will fetch all the policy either linked or not linked with the buckets
- params = params.append('all_policy', fetchAllPolicy);
- return this.http.get(`${this.url}/sync-policy`, { params });
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (bucketName) {
+ params = params.append('bucket_name', bucketName);
+ }
+ if (zonegroup) {
+ params = params.append('zonegroup_name', zonegroup);
+ }
+ // fetchAllPolicy - if true, will fetch all the policy either linked or not linked with the buckets
+ params = params.append('all_policy', fetchAllPolicy);
+ return this.http.get(`${this.url}/sync-policy`, { params });
+ });
}
getSyncPolicyGroup(group_id: string, bucket_name?: string) {
- let params = new HttpParams();
- if (bucket_name) {
- params = params.append('bucket_name', bucket_name);
- }
- return this.http.get(`${this.url}/sync-policy-group/${group_id}`, { params });
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (bucket_name) {
+ params = params.append('bucket_name', bucket_name);
+ }
+ return this.http.get(`${this.url}/sync-policy-group/${group_id}`, { params });
+ });
}
createSyncPolicyGroup(payload: { group_id: string; status: string; bucket_name?: string }) {
- return this.http.post(`${this.url}/sync-policy-group`, payload);
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ return this.http.post(`${this.url}/sync-policy-group`, payload, { params: params });
+ });
}
modifySyncPolicyGroup(payload: { group_id: string; status: string; bucket_name?: string }) {
- return this.http.put(`${this.url}/sync-policy-group`, payload);
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ return this.http.put(`${this.url}/sync-policy-group`, payload, { params: params });
+ });
}
removeSyncPolicyGroup(group_id: string, bucket_name?: string) {
- let params = new HttpParams();
- if (bucket_name) {
- params = params.append('bucket_name', bucket_name);
- }
- return this.http.delete(`${this.url}/sync-policy-group/${group_id}`, { params });
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (bucket_name) {
+ params = params.append('bucket_name', bucket_name);
+ }
+ return this.http.delete(`${this.url}/sync-policy-group/${group_id}`, { params });
+ });
}
setUpMultisiteReplication(
}
createEditSyncFlow(payload: any) {
- return this.http.put(`${this.url}/sync-flow`, payload);
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ return this.http.put(`${this.url}/sync-flow`, payload, { params: params });
+ });
}
removeSyncFlow(flow_id: string, flow_type: string, group_id: string, bucket_name?: string) {
- let params = new HttpParams();
- if (bucket_name) {
- params = params.append('bucket_name', encodeURIComponent(bucket_name));
- }
- return this.http.delete(
- `${this.url}/sync-flow/${encodeURIComponent(flow_id)}/${flow_type}/${encodeURIComponent(
- group_id
- )}`,
- { params }
- );
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (bucket_name) {
+ params = params.append('bucket_name', encodeURIComponent(bucket_name));
+ }
+ return this.http.delete(
+ `${this.url}/sync-flow/${encodeURIComponent(flow_id)}/${flow_type}/${encodeURIComponent(
+ group_id
+ )}`,
+ { params }
+ );
+ });
}
createEditSyncPipe(payload: any, user?: string, mode?: string) {
- let params = new HttpParams();
- if (user) {
- params = params.append('user', user);
- }
- if (mode) {
- params = params.append('mode', mode);
- }
- return this.http.put(`${this.url}/sync-pipe`, payload, { params });
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (user) {
+ params = params.append('user', user);
+ }
+ if (mode) {
+ params = params.append('mode', mode);
+ }
+ return this.http.put(`${this.url}/sync-pipe`, payload, { params });
+ });
}
removeSyncPipe(pipe_id: string, group_id: string, bucket_name?: string) {
- let params = new HttpParams();
- if (bucket_name) {
- params = params.append('bucket_name', encodeURIComponent(bucket_name));
- }
- return this.http.delete(
- `${this.url}/sync-pipe/${encodeURIComponent(group_id)}/${encodeURIComponent(pipe_id)}`,
- { params }
- );
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ if (bucket_name) {
+ params = params.append('bucket_name', encodeURIComponent(bucket_name));
+ }
+ return this.http.delete(
+ `${this.url}/sync-pipe/${encodeURIComponent(group_id)}/${encodeURIComponent(pipe_id)}`,
+ { params }
+ );
+ });
}
setRestartGatewayMessage(value: boolean): void {
bucket_name:
default: ''
type: string
+ daemon_name:
+ default: ''
+ type: string
destination_zone:
type: string
flow_id:
name: bucket_name
schema:
type: string
+ - default: ''
+ in: query
+ name: daemon_name
+ schema:
+ type: string
responses:
'202':
content:
bucket_name:
default: ''
type: string
+ daemon_name:
+ default: ''
+ type: string
destination_bucket:
default: ''
type: string
name: bucket_name
schema:
type: string
+ - default: ''
+ in: query
+ name: daemon_name
+ schema:
+ type: string
responses:
'202':
content:
name: all_policy
schema:
type: string
+ - default: ''
+ in: query
+ name: daemon_name
+ schema:
+ type: string
responses:
'200':
content:
bucket_name:
default: ''
type: string
+ daemon_name:
+ default: ''
+ type: string
group_id:
type: string
status:
bucket_name:
default: ''
type: string
+ daemon_name:
+ default: ''
+ type: string
group_id:
type: string
status:
name: bucket_name
schema:
type: string
+ - default: ''
+ in: query
+ name: daemon_name
+ schema:
+ type: string
responses:
'202':
content:
name: bucket_name
schema:
type: string
+ - default: ''
+ in: query
+ name: daemon_name
+ schema:
+ type: string
responses:
'200':
content:
try:
rgw_multisite_instance = RgwMultisite()
self.update_progress(
- f"Initializing multi-site configuration || Creating realm: {realm}, \
- zonegroup: {zg}, and zone: {zone} along \
- with system user: {username}"
+ f"Initializing multi-site configuration || Creating realm: "
+ f"{realm}, zonegroup: {zg}, and zone: {zone} along "
+ f"with system user: {username}"
)
rgw_multisite_instance.create_realm(realm_name=realm, default=True)
rgw_multisite_instance.create_zonegroup(realm_name=realm, zonegroup_name=zg,
self.progress_done += 1
self.update_progress(
- f"Verifying system user and completing replication setup on \
- cluster {cluster_fsid} || Ensuring presence of user '{username}' \
- and assigning necessary RGW credentials"
+ f"Verifying system user and completing replication setup on "
+ f"cluster {cluster_fsid} || Ensuring presence of user "
+ f"'{username}' and assigning necessary RGW credentials"
)
self._verify_user_and_daemons(cluster_url, cluster_token, realm_name,
raise DashboardException(error, http_status_code=500, component='rgw')
return user_list
+ def get_realm_from_daemon(self, daemon_name: Optional[str] = None) -> Optional[str]:
+ """Extract realm_name from daemon if daemon_name is provided."""
+ if not daemon_name:
+ return None
+ try:
+ daemons = _get_daemons()
+ return daemons[daemon_name].realm_name
+ except (KeyError, AttributeError):
+ # If daemon not found or has no realm_name, return None
+ return None
+
+ def get_zonegroup_from_daemon(self, daemon_name: Optional[str] = None) -> Optional[str]:
+ """Extract zonegroup_name from daemon if daemon_name is provided."""
+ if not daemon_name:
+ return None
+ try:
+ daemons = _get_daemons()
+ return daemons[daemon_name].zonegroup_name
+ except (KeyError, AttributeError):
+ # If daemon not found or has no zonegroup_name, return None
+ return None
+
+ def _add_realm_zonegroup_args(
+ self, cmd: List[str], daemon_name: Optional[str] = None,
+ explicit_zonegroup: str = '') -> None:
+ """
+ Add --rgw-realm and --rgw-zonegroup arguments to a radosgw-admin command.
+
+ Args:
+ cmd: The command list to append arguments to
+ daemon_name: The daemon name to extract realm/zonegroup from
+ explicit_zonegroup: If provided, use this instead of daemon's zonegroup
+ """
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ if realm_name:
+ cmd += ['--rgw-realm', realm_name]
+
+ if explicit_zonegroup:
+ zonegroup_name: Optional[str] = explicit_zonegroup
+ else:
+ zonegroup_name = self.get_zonegroup_from_daemon(daemon_name)
+ if zonegroup_name:
+ cmd += ['--rgw-zonegroup', zonegroup_name]
+
def get_multisite_status(self):
is_multisite_configured = True
rgw_realm_list = self.list_realms()
return ''
- def get_sync_policy(self, bucket_name: str = '', zonegroup_name: str = ''):
+ def get_sync_policy(self, bucket_name: str = '',
+ zonegroup_name: str = '',
+ daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'policy', 'get']
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
- if zonegroup_name:
- rgw_sync_policy_cmd += ['--rgw-zonegroup', zonegroup_name]
+ # Use daemon's realm and zonegroup when daemon_name is provided
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ if realm_name:
+ rgw_sync_policy_cmd += ['--rgw-realm', realm_name]
+ # Using daemon's zonegroup if no explicit zonegroup provided
+ target_zonegroup = zonegroup_name or self.get_zonegroup_from_daemon(daemon_name)
+ if target_zonegroup:
+ rgw_sync_policy_cmd += ['--rgw-zonegroup', target_zonegroup]
try:
exit_code, out, err = mgr.send_rgwadmin_command(rgw_sync_policy_cmd)
if exit_code > 0:
raise DashboardException(error, http_status_code=500, component='rgw')
def get_sync_policy_group(self, group_id: str, bucket_name: str = '',
- zonegroup_name: str = ''):
+ zonegroup_name: str = '', daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'get', '--group-id', group_id]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
- if zonegroup_name:
- rgw_sync_policy_cmd += ['--rgw-zonegroup', zonegroup_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name, zonegroup_name)
try:
exit_code, out, err = mgr.send_rgwadmin_command(rgw_sync_policy_cmd)
if exit_code > 0:
raise DashboardException(error, http_status_code=500, component='rgw')
def create_sync_policy_group(self, group_id: str, status: str, bucket_name: str = '',
- update_period=False):
+ update_period=False, daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'create', '--group-id', group_id,
'--status', SyncStatus[status].value]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
try:
exit_code, _, err = mgr.send_rgwadmin_command(rgw_sync_policy_cmd)
if exit_code > 0:
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
def update_sync_policy_group(self, group_id: str, status: str, bucket_name: str = '',
- update_period=False):
+ update_period=False, daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'modify', '--group-id', group_id,
'--status', SyncStatus[status].value]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
try:
exit_code, _, err = mgr.send_rgwadmin_command(rgw_sync_policy_cmd)
if exit_code > 0:
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
- def remove_sync_policy_group(self, group_id: str, bucket_name='', update_period=False):
+ def remove_sync_policy_group(self, group_id: str, bucket_name='',
+ update_period=False,
+ daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'remove', '--group-id', group_id]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
try:
exit_code, _, err = mgr.send_rgwadmin_command(rgw_sync_policy_cmd)
if exit_code > 0:
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
def create_sync_flow(self, group_id: str, flow_id: str, flow_type: str,
zones: Optional[Dict[str, List]] = None, bucket_name: str = '',
source_zone: Optional[str] = None,
destination_zone: Optional[str] = None,
- update_period=False):
+ update_period=False, daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'flow', 'create', '--group-id', group_id,
'--flow-id', flow_id, '--flow-type', SyncFlowTypes[flow_type].value]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
if SyncFlowTypes[flow_type].value == 'directional':
raise DashboardException(error, http_status_code=500, component='rgw')
if len(zones['removed']) > 0:
- self.remove_sync_flow(group_id, flow_id, flow_type, source_zone,
- destination_zone, zones['removed'], bucket_name)
+ self.remove_sync_flow(
+ group_id, flow_id, flow_type, source_zone,
+ destination_zone, zones['removed'], bucket_name,
+ daemon_name=daemon_name)
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
def remove_sync_flow(self, group_id: str, flow_id: str, flow_type: str,
source_zone='', destination_zone='',
zones: Optional[List[str]] = None, bucket_name: str = '',
- update_period=False):
+ update_period=False, daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'flow', 'remove', '--group-id', group_id,
'--flow-id', flow_id, '--flow-type', SyncFlowTypes[flow_type].value]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
logger.info("Removing sync flow! %s", rgw_sync_policy_cmd)
try:
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
def create_sync_pipe(self, group_id: str, pipe_id: str,
source_zones: Dict[str, Any],
destination_bucket: str = '',
bucket_name: str = '',
update_period=False,
- user: str = '', mode: str = ''):
+ user: str = '', mode: str = '', daemon_name: Optional[str] = None):
if source_zones['added'] or destination_zones['added']:
rgw_sync_policy_cmd = ['sync', 'group', 'pipe', 'create',
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
rgw_sync_policy_cmd += ['--source-bucket', source_bucket]
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
if ((source_zones['removed'] and '*' not in source_zones['added'])
or (destination_zones['removed'] and '*' not in destination_zones['added'])):
self.remove_sync_pipe(group_id, pipe_id, source_zones['removed'],
destination_zones['removed'],
- bucket_name, True)
+ bucket_name, True, daemon_name=daemon_name)
def remove_sync_pipe(self, group_id: str, pipe_id: str,
source_zones: Optional[List[str]] = None,
destination_zones: Optional[List[str]] = None,
bucket_name: str = '',
- update_period=False):
+ update_period=False, daemon_name: Optional[str] = None):
rgw_sync_policy_cmd = ['sync', 'group', 'pipe', 'remove',
'--group-id', group_id, '--pipe-id', pipe_id]
if bucket_name:
rgw_sync_policy_cmd += ['--bucket', bucket_name]
+ self._add_realm_zonegroup_args(rgw_sync_policy_cmd, daemon_name)
if source_zones:
rgw_sync_policy_cmd += ['--source-zones', ','.join(source_zones)]
except SubprocessError as error:
raise DashboardException(error, http_status_code=500, component='rgw')
if not bucket_name and update_period:
- self.update_period()
+ realm_name = self.get_realm_from_daemon(daemon_name)
+ self.update_period(realm_name=realm_name)
def create_dashboard_admin_sync_group(self, zonegroup_name: str = ''):
def push_endpoint_password(push_endpoint: str) -> str:
parsed = urlparse(push_endpoint)
if parsed.username and parsed.password:
- netloc = f"{parsed.username}:****@{parsed.hostname}"
+ netloc = f"{parsed.username}: ****@{parsed.hostname}"
if parsed.port:
- netloc += f":{parsed.port}"
+ netloc += f": {parsed.port}"
parsed = parsed._replace(netloc=netloc)
return urlunparse(parsed)
return push_endpoint
from .. import mgr
from ..exceptions import DashboardException
from ..services.rgw_client import NoRgwDaemonsException, RgwClient, \
- _determine_rgw_addr, _parse_frontend_config
+ RgwMultisite, _determine_rgw_addr, _parse_frontend_config
from ..services.service import NoCredentialsException
from ..settings import Settings
from ..tests import CLICommandTestMixin, RgwStub
expected_xml = "<name>Foo</name>\n<age>30</age>\n"
result = RgwClient.dict_to_xml(data)
self.assertEqual(result, expected_xml)
+
+
+class RgwMultisiteTest(TestCase):
+ """Test cases for RgwMultisite class with daemon_name parameter support."""
+
+ def setUp(self):
+ RgwStub.get_daemons()
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_sync_policy_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test get_sync_policy with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, {'groups': []}, '')
+
+ multisite = RgwMultisite()
+ result = multisite.get_sync_policy(bucket_name='test_bucket', daemon_name='test_daemon')
+
+ self.assertEqual(result, {'groups': []})
+ # Verify the command includes realm and zonegroup
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('--rgw-realm', call_args)
+ self.assertIn('test_realm', call_args)
+ self.assertIn('--rgw-zonegroup', call_args)
+ self.assertIn('test_zonegroup', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_sync_policy_group_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test get_sync_policy_group with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, {'id': 'test_group'}, '')
+
+ multisite = RgwMultisite()
+ result = multisite.get_sync_policy_group('test_group', daemon_name='test_daemon')
+
+ self.assertEqual(result, {'id': 'test_group'})
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('--rgw-realm', call_args)
+ self.assertIn('test_realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_create_sync_policy_group_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test create_sync_policy_group with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.create_sync_policy_group('test_group', 'enabled', daemon_name='test_daemon')
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('create', call_args)
+ self.assertIn('--rgw-realm', call_args)
+ self.assertIn('test_realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_update_sync_policy_group_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test update_sync_policy_group with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.update_sync_policy_group('test_group', 'enabled', daemon_name='test_daemon')
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('modify', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_remove_sync_policy_group_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test remove_sync_policy_group with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.remove_sync_policy_group('test_group', daemon_name='test_daemon')
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('remove', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_create_sync_flow_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test create_sync_flow with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ # For symmetrical flow, need to provide zones with added zones
+ multisite.create_sync_flow(
+ 'test_group', 'test_flow', 'symmetrical',
+ zones={'added': ['zone1', 'zone2'], 'removed': []},
+ daemon_name='test_daemon'
+ )
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('flow', call_args)
+ self.assertIn('create', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_remove_sync_flow_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test remove_sync_flow with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.remove_sync_flow(
+ 'test_group', 'test_flow', 'symmetrical',
+ daemon_name='test_daemon'
+ )
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('flow', call_args)
+ self.assertIn('remove', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_create_sync_pipe_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test create_sync_pipe with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.create_sync_pipe(
+ 'test_group', 'test_pipe',
+ {'added': ['zone1'], 'removed': []},
+ {'added': ['zone2'], 'removed': []},
+ 'source_bucket',
+ daemon_name='test_daemon'
+ )
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('pipe', call_args)
+ self.assertIn('create', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client.mgr.send_rgwadmin_command')
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_remove_sync_pipe_with_daemon_name(self, mock_get_daemons, mock_send_command):
+ """Test remove_sync_pipe with daemon_name parameter."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ mock_send_command.return_value = (0, '', '')
+
+ multisite = RgwMultisite()
+ multisite.remove_sync_pipe(
+ 'test_group', 'test_pipe',
+ source_zones=['zone1'],
+ destination_zones=['zone2'],
+ daemon_name='test_daemon'
+ )
+
+ call_args = mock_send_command.call_args[0][0]
+ self.assertIn('sync', call_args)
+ self.assertIn('group', call_args)
+ self.assertIn('pipe', call_args)
+ self.assertIn('remove', call_args)
+ self.assertIn('--rgw-realm', call_args)
+
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_zonegroup_from_daemon(self, mock_get_daemons):
+ """Test get_zonegroup_from_daemon method."""
+ mock_daemon = Mock()
+ mock_daemon.zonegroup_name = 'test_zonegroup'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ multisite = RgwMultisite()
+ result = multisite.get_zonegroup_from_daemon('test_daemon')
+
+ self.assertEqual(result, 'test_zonegroup')
+
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_zonegroup_from_daemon_not_found(self, mock_get_daemons):
+ """Test get_zonegroup_from_daemon with non-existent daemon."""
+ mock_get_daemons.return_value = {}
+
+ multisite = RgwMultisite()
+ result = multisite.get_zonegroup_from_daemon('non_existent_daemon')
+
+ self.assertIsNone(result)
+
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_realm_from_daemon(self, mock_get_daemons):
+ """Test get_realm_from_daemon method."""
+ mock_daemon = Mock()
+ mock_daemon.realm_name = 'test_realm'
+ mock_get_daemons.return_value = {'test_daemon': mock_daemon}
+
+ multisite = RgwMultisite()
+ result = multisite.get_realm_from_daemon('test_daemon')
+
+ self.assertEqual(result, 'test_realm')
+
+ @patch('dashboard.services.rgw_client._get_daemons')
+ def test_get_realm_from_daemon_not_found(self, mock_get_daemons):
+ """Test get_realm_from_daemon with non-existent daemon."""
+ mock_get_daemons.return_value = {}
+
+ multisite = RgwMultisite()
+ result = multisite.get_realm_from_daemon('non_existent_daemon')
+
+ self.assertIsNone(result)