return None
rgw_client = RgwClient.instance(owner, daemon_name)
- zonegroup_name = RgwClient.admin_instance(daemon_name=daemon_name).get_default_zonegroup()
- policy_exists = multisite.policy_group_exists(_SYNC_GROUP_ID, zonegroup_name)
- if replication and not policy_exists:
- multisite.create_dashboard_admin_sync_group(zonegroup_name=zonegroup_name)
+ if replication:
+ zonegroup_name = RgwClient.admin_instance(
+ daemon_name=daemon_name).get_default_zonegroup()
+ policy_exists = multisite.policy_group_exists(_SYNC_GROUP_ID, zonegroup_name)
+ if not policy_exists:
+ multisite.create_dashboard_admin_sync_group(zonegroup_name=zonegroup_name)
+ return rgw_client.set_bucket_replication(bucket_name)
- return rgw_client.set_bucket_replication(bucket_name, replication)
+ return rgw_client.delete_bucket_replication(bucket_name)
def _get_replication(self, bucket_name: str, owner, daemon_name):
+ multisite = RgwMultisite()
rgw_client = RgwClient.instance(owner, daemon_name)
- return rgw_client.get_bucket_replication(bucket_name)
+ replication_config = rgw_client.get_bucket_replication(bucket_name)
+
+ # Check if there's a valid S3 replication config with actual rules
+ replication_rules_configured = bool(replication_config and replication_config.get('Rule'))
+
+ # Check for sync policies
+ sync_policy_active = False
+ try:
+ sync_policy = multisite.get_sync_policy(bucket_name=bucket_name)
+ # Check if there are sync policy groups with 'enabled' status
+ groups = sync_policy.get('groups', [])
+ sync_policy_active = any(g.get('status', '').lower() == 'enabled' for g in groups)
+ except (RequestException, DashboardException):
+ # Silently ignore errors from sync policy check
+ pass
+
+ return {
+ 'sync_policy_active': sync_policy_active,
+ 'replication_rules_configured': replication_rules_configured,
+ 'policy': replication_config if replication_config else {}
+ }
@staticmethod
def strip_tenant_from_bucket_name(bucket_name):
--- /dev/null
+export interface RgwBucketReplication {
+ sync_policy_active: boolean;
+ replication_rules_configured: boolean;
+ policy: any;
+}
<tr>
<td i18n
class="bold w-25">Replication policy</td>
- <td><pre>{{ selection.replication | json}}</pre></td>
+ <td>
+ @if(hasSyncPolicyOnly) {
+ Sync policy configured. View on
+ <a routerLink="/rgw/multisite/sync-policy"
+ i18n>Sync-policy page.</a>
+ } @else {
+ <pre>{{ replicationData?.policy | json}}</pre>
+ }
+ </td>
</tr>
<tr>
<td i18n
import * as xml2js from 'xml2js';
import { RgwRateLimitConfig } from '../models/rgw-rate-limit';
+import { RgwBucketReplication } from '../models/rgw-bucket-replication';
@Component({
selector: 'cd-rgw-bucket-details',
lifecycleFormat: 'json' | 'xml' = 'json';
aclPermissions: Record<string, string[]> = {};
replicationStatus = $localize`Disabled`;
+ hasSyncPolicyOnly = false;
+ replicationData: RgwBucketReplication;
bucketRateLimit: RgwRateLimitConfig;
constructor(private rgwBucketService: RgwBucketService, private cd: ChangeDetectorRef) {}
extraxtDetailsfromResponse() {
this.aclPermissions = this.parseXmlAcl(this.selection.acl, this.selection.owner);
- if (this.selection.replication?.['Rule']?.['Status']) {
- this.replicationStatus = this.selection.replication?.['Rule']?.['Status'];
+
+ this.replicationData = {
+ sync_policy_active: this.selection.replication?.sync_policy_active === true,
+ replication_rules_configured:
+ this.selection.replication?.replication_rules_configured === true,
+ policy: this.selection.replication?.policy || {}
+ };
+
+ this.hasSyncPolicyOnly =
+ this.replicationData.sync_policy_active && !this.replicationData.replication_rules_configured;
+
+ if (this.replicationData.sync_policy_active) {
+ this.replicationStatus = $localize`Enabled`;
+ } else if (
+ this.replicationData.replication_rules_configured &&
+ this.replicationData.policy['Rule']?.['Status']
+ ) {
+ this.replicationStatus = this.replicationData.policy['Rule']['Status'];
+ } else {
+ this.replicationStatus = $localize`Disabled`;
}
this.rgwBucketService.getBucketRateLimit(this.selection.bid).subscribe((resp: any) => {
if (resp && resp.bucket_ratelimit !== undefined) {
<div class="form-item">
<legend class="cd-header"
i18n>Replication</legend>
- <ng-container *ngIf="{status: multisiteStatus$, isDefaultZg: isDefaultZoneGroup$ | async} as multisiteStatus; else loadingTpl">
+ <ng-container *ngIf="{status: multisiteStatus$ | async, isDefaultZg: isDefaultZoneGroup$ | async} as multisiteStatus; else loadingTpl">
<cds-checkbox name="replication"
formControlName="replication"
id="replication"
- [disabled]="!multisiteStatus.isDefaultZg && !multisiteStatus.status.available"
+ [disabled]="!multisiteStatus.isDefaultZg && !multisiteStatus?.status?.available"
+ [title]="!multisiteStatus.isDefaultZg && !multisiteStatus?.status?.available ? 'Please enable the multisite configuration' : ''"
+ i18n-title
i18n-helperText
i18n>Enable
<cd-help-text>Enables replication for the objects in the bucket.</cd-help-text>
}
if (value['replication']) {
const replicationConfig = value['replication'];
- if (replicationConfig?.['Rule']?.['Status'] === 'Enabled') {
- this.bucketForm.get('replication').setValue(true);
- } else {
- this.bucketForm.get('replication').setValue(false);
- }
+ // Only consider S3 replication rules, not sync policies (managed on sync-policy page)
+ const hasReplicationRules =
+ replicationConfig?.['replication_rules_configured'] === true &&
+ replicationConfig?.['policy']?.['Rule']?.['Status'] === 'Enabled';
+
+ this.bucketForm.get('replication').setValue(hasReplicationRules);
}
this.filterAclPermissions();
}
return retention_period_days, retention_period_years
@RestClient.api_put('/{bucket_name}?replication')
- def set_bucket_replication(self, bucket_name, replication: bool, request=None):
+ def set_bucket_replication(self, bucket_name, request=None):
# pGenerate the minimum replication configuration
# required for enabling the replication
root = ET.Element('ReplicationConfiguration',
rule_id.text = _SYNC_PIPE_ID
status = ET.SubElement(rule, 'Status')
- status.text = 'Enabled' if replication else 'Disabled'
+ status.text = 'Enabled'
filter_elem = ET.SubElement(rule, 'Filter')
prefix = ET.SubElement(filter_elem, 'Prefix')
return None
raise e
+ @RestClient.api_delete('/{bucket_name}?replication')
+ def delete_bucket_replication(self, bucket_name, request=None):
+ # pylint: disable=unused-argument
+ try:
+ request()
+ except RequestException as e:
+ if e.content:
+ content = json_str_to_object(e.content)
+ if content.get('Code') == 'ReplicationConfigurationNotFoundError':
+ return None
+ raise DashboardException(msg=str(e), component='rgw')
+ return None
+
@RestClient.api_post('?Action=CreateTopic&Name={name}')
def create_topic(self, request=None, name: str = '',
daemon_name: str = '',
# period update --commit
self.update_period()
+ @staticmethod
+ def _is_not_found_sync_policy_error(error: DashboardException) -> bool:
+ message = str(error).lower()
+ not_found_markers = ['not found', 'no such', 'does not exist']
+ return any(marker in message for marker in not_found_markers)
+
+ def remove_dashboard_admin_sync_group(self, zonegroup_name: str = ''):
+ if not self.policy_group_exists(_SYNC_GROUP_ID, zonegroup_name):
+ return
+
+ try:
+ self.remove_sync_pipe(_SYNC_GROUP_ID, _SYNC_PIPE_ID)
+ except DashboardException as error:
+ if not self._is_not_found_sync_policy_error(error):
+ raise
+
+ try:
+ self.remove_sync_flow(_SYNC_GROUP_ID, _SYNC_FLOW_ID,
+ SyncFlowTypes.symmetrical.value)
+ except DashboardException as error:
+ if not self._is_not_found_sync_policy_error(error):
+ raise
+
+ self.remove_sync_policy_group(_SYNC_GROUP_ID, update_period=True)
+
def policy_group_exists(self, group_name: str, zonegroup_name: str):
try:
_ = self.get_sync_policy_group(