if size and size != image.size():
image.resize(size)
+ mirror_image_info = image.mirror_image_get_info()
+ if enable_mirror and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_DISABLED:
+ RbdMirroringService.enable_image(
+ image_name, pool_name, namespace,
+ MIRROR_IMAGE_MODE[mirror_mode])
+ elif (enable_mirror is False
+ and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_ENABLED):
+ RbdMirroringService.disable_image(
+ image_name, pool_name, namespace)
+
# check enable/disable features
if features is not None:
curr_features = format_bitmask(image.features())
RbdConfiguration(pool_ioctx=ioctx, image_name=image_name).set_configuration(
configuration)
- mirror_image_info = image.mirror_image_get_info()
- if enable_mirror and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_DISABLED:
- RbdMirroringService.enable_image(
- image_name, pool_name, namespace,
- MIRROR_IMAGE_MODE[mirror_mode])
- elif (enable_mirror is False
- and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_ENABLED):
- RbdMirroringService.disable_image(
- image_name, pool_name, namespace)
-
if primary and not mirror_image_info['primary']:
RbdMirroringService.promote_image(
image_name, pool_name, namespace)
icon: Icons.download,
click: () => this.importBootstrapModal(),
name: $localize`Import Bootstrap Token`,
- disable: () => this.peersExist
+ disable: () => false
};
this.tableActions = [createBootstrapAction, importBootstrapAction];
}
this.subs.add(
this.rbdMirroringService.subscribeSummary((data) => {
this.status = data.content_data.status;
-
this.peersExist = !!data.content_data.pools.find((o: Pool) => o['peer_uuids'].length > 0);
})
);
<cd-rbd-snapshot-list [snapshots]="selection.snapshots"
[featuresName]="selection.features_name"
[poolName]="selection.pool_name"
+ [primary]="selection.primary"
[namespace]="selection.namespace"
[mirroring]="selection.mirror_mode"
[rbdName]="selection.name"></cd-rbd-snapshot-list>
<label class="cd-col-form-label"
i18n>Schedule Interval
<cd-helper i18n-html
- html="Create Mirror-Snapshots automatically on a periodic basis. The interval can be specified in days, hours, or minutes using d, h, m suffix respectively.">
+ html="Create Mirror-Snapshots automatically on a periodic basis. The interval can be specified in days, hours, or minutes using d, h, m suffix respectively. To create mirror snapshots, you must import or create and have available peers to mirror">
</cd-helper></label>
<div class="cd-col-form-input">
<input id="schedule"
formControlName="schedule"
i18n-placeholder
placeholder="e.g., 12h or 1d or 10m"
- [attr.disabled]="(mode === rbdFormMode.editing) ? true : null">
+ [attr.disabled]="(peerConfigured === false) ? true : null">
</div>
</div>
it('should set and disable exclusive-lock only for the journal mode', () => {
component.poolMirrorMode = 'pool';
+ component.mirroring = true;
+ const journal = fixture.debugElement.query(By.css('#journal')).nativeElement;
+ journal.click();
fixture.detectChanges();
const exclusiveLocks = fixture.debugElement.query(By.css('#exclusive-lock')).nativeElement;
expect(exclusiveLocks.checked).toBe(true);
component.mirroring = true;
fixture.detectChanges();
const journal = fixture.debugElement.query(By.css('#journal')).nativeElement;
+ journal.click();
expect(journal.checked).toBe(true);
const request = component.createRequest();
expect(request.features).toContain('journaling');
component.mirroring = true;
fixture.detectChanges();
const journal = fixture.debugElement.query(By.css('#journal')).nativeElement;
+ journal.click();
expect(journal.checked).toBe(true);
const request = component.editRequest();
expect(request.features).toContain('journaling');
}>(1);
pool: string;
+ peerConfigured = false;
advancedEnabled = false;
return acc;
}, {})
),
- mirroring: new FormControl(false),
+ mirroring: new FormControl(''),
schedule: new FormControl('', {
validators: [Validators.pattern(/^([0-9]+)d|([0-9]+)h|([0-9]+)m$/)] // check schedule interval to be in format - 1d or 1h or 1m
}),
- mirroringMode: new FormControl(this.mirroringOptions[0]),
+ mirroringMode: new FormControl(''),
stripingUnit: new FormControl(this.defaultStripingUnit),
stripingCount: new FormControl(this.defaultStripingCount, {
updateOn: 'blur'
setMirrorMode() {
this.mirroring = !this.mirroring;
this.setExclusiveLock();
+ this.checkPeersConfigured();
+ }
+
+ checkPeersConfigured(poolname?: string) {
+ var Poolname = poolname ? poolname : this.rbdForm.get('pool').value;
+ this.rbdMirroringService.getPeerForPool(Poolname).subscribe((resp: any) => {
+ if (resp.length > 0) {
+ this.peerConfigured = true;
+ }
+ });
}
setPoolMirrorMode() {
this.mirroring = false;
this.rbdForm.get('mirroring').setValue(this.mirroring);
this.rbdForm.get('mirroring').disable();
- } else if (this.mode !== this.rbdFormMode.editing) {
- this.rbdForm.get('mirroring').enable();
- this.mirroring = true;
- this.rbdForm.get('mirroring').setValue(this.mirroring);
}
});
}
this.snapName = decodeURIComponent(params.snap);
}
promises['rbd'] = this.rbdService.get(imageSpec);
+ this.checkPeersConfigured(imageSpec.poolName);
});
} else {
// New image
request.name = this.rbdForm.getValue('name');
request.schedule_interval = this.rbdForm.getValue('schedule');
request.size = this.formatter.toBytes(this.rbdForm.getValue('size'));
+
if (this.poolMirrorMode === 'image') {
request.mirror_mode = this.rbdForm.getValue('mirroringMode');
}
<span *ngIf="value.length === 3; else probb"
class="badge badge-info">{{ value[0] }}</span>
<span *ngIf="value.length === 3"
- class="badge badge-info"
- [ngbTooltip]="'Next scheduled snapshot on' + ' ' + (value[2] | cdDate)">{{ value[1] }}</span>
+ class="badge badge-info">{{ value[1] }}</span>
<span *ngIf="row.primary === true"
class="badge badge-info"
i18n>primary</span>
</ng-template>
</ng-template>
+<ng-template #ScheduleTpl
+ let-value="value"
+ let-row="row">
+ <span *ngIf="value.length === 3"
+ class="badge badge-info">{{ value[2] | cdDate }}</span>
+</ng-template>
+
<ng-template #flattenTpl
let-value>
You are about to flatten
parentTpl: TemplateRef<any>;
@ViewChild('nameTpl')
nameTpl: TemplateRef<any>;
+ @ViewChild('ScheduleTpl', { static: true })
+ ScheduleTpl: TemplateRef<any>;
@ViewChild('mirroringTpl', { static: true })
mirroringTpl: TemplateRef<any>;
@ViewChild('flattenTpl', { static: true })
flexGrow: 3,
sortable: false,
cellTemplate: this.mirroringTpl
+ },
+ {
+ name: $localize`Next Scheduled Snapshot`,
+ prop: 'mirror_mode',
+ flexGrow: 3,
+ sortable: false,
+ cellTemplate: this.ScheduleTpl
}
];
import { MockComponent } from 'ng-mocks';
import { ToastrModule } from 'ngx-toastr';
import { Subject, throwError as observableThrowError } from 'rxjs';
+import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
import { RbdService } from '~/app/shared/api/rbd.service';
import { ComponentsModule } from '~/app/shared/components/components.module';
describe('api delete request', () => {
let called: boolean;
let rbdService: RbdService;
+ let rbdMirroringService: RbdMirroringService;
let notificationService: NotificationService;
let authStorageService: AuthStorageService;
const modalService = TestBed.inject(ModalService);
const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
called = false;
+ rbdMirroringService = new RbdMirroringService(null, null);
rbdService = new RbdService(null, null);
notificationService = new NotificationService(null, null, null);
authStorageService = new AuthStorageService();
null,
null,
rbdService,
+ rbdMirroringService,
null,
notificationService,
null,
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import moment from 'moment';
import { of } from 'rxjs';
+import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
import { RbdService } from '~/app/shared/api/rbd.service';
import { CdHelperClass } from '~/app/shared/classes/cd-helper.class';
@Input()
mirroring: string;
@Input()
+ primary: boolean;
+ @Input()
rbdName: string;
@ViewChild('nameTpl')
nameTpl: TemplateRef<any>;
modalRef: NgbModalRef;
+ peerConfigured = false;
+
builders = {
'rbd/snap/create': (metadata: any) => {
const model = new RbdSnapshotModel();
private dimlessBinaryPipe: DimlessBinaryPipe,
private cdDatePipe: CdDatePipe,
private rbdService: RbdService,
+ private rbdMirrorService: RbdMirroringService,
private taskManagerService: TaskManagerService,
private notificationService: NotificationService,
private summaryService: SummaryService,
}
];
+ this.rbdMirrorService.getPeerForPool(this.poolName).subscribe((resp: any) => {
+ if (resp.length > 0) {
+ this.peerConfigured = true;
+ }
+ });
+
this.imageSpec = new ImageSpec(this.poolName, this.namespace, this.rbdName);
this.rbdTableActions = new RbdSnapshotActionsModel(
this.actionLabels,
this.featuresName,
this.rbdService
);
+ this.rbdTableActions.create.disable = () =>
+ !this.primary || (!this.peerConfigured && this.mirroring === 'snapshot');
this.rbdTableActions.create.click = () => this.openCreateSnapshotModal();
this.rbdTableActions.rename.click = () => this.openEditSnapshotModal();
this.rbdTableActions.protect.click = () => this.toggleProtection();
return this.http.get(`api/block/mirroring/pool/${poolName}/peer/${peerUUID}`);
}
+ getPeerForPool(poolName: string) {
+ return this.http.get(`api/block/mirroring/pool/${poolName}/peer`);
+ }
+
addPeer(poolName: string, request: any) {
return this.http.post(`api/block/mirroring/pool/${poolName}/peer`, request, {
observe: 'response'
def _rbd_image(cls, ioctx, pool_name, namespace, image_name): # pylint: disable=R0912
with rbd.Image(ioctx, image_name) as img:
stat = img.stat()
+ mirror_info = img.mirror_image_get_info()
mirror_mode = img.mirror_image_get_mode()
- if mirror_mode == rbd.RBD_MIRROR_IMAGE_MODE_JOURNAL:
+ if mirror_mode == rbd.RBD_MIRROR_IMAGE_MODE_JOURNAL and mirror_info['state'] != rbd.RBD_MIRROR_IMAGE_DISABLED: # noqa E501 #pylint: disable=line-too-long
stat['mirror_mode'] = 'journal'
elif mirror_mode == rbd.RBD_MIRROR_IMAGE_MODE_SNAPSHOT:
stat['mirror_mode'] = 'snapshot'
if scheduled_image['image'] == get_image_spec(pool_name, namespace, image_name):
stat['schedule_info'] = scheduled_image
else:
- stat['mirror_mode'] = 'unknown'
+ stat['mirror_mode'] = 'Disabled'
stat['name'] = image_name
- mirror_info = img.mirror_image_get_info()
stat['primary'] = None
if mirror_info['state'] == rbd.RBD_MIRROR_IMAGE_ENABLED:
stat['primary'] = mirror_info['primary']