def put(self): if 'note' in self.data: # Changing snapshot note instead of rollback (not logging) return self._update_note() request, vm, snap = self.request, self.vm, self.snap if vm.node.status not in vm.node.STATUS_OPERATIONAL: raise NodeIsNotOperational if vm.locked: raise VmIsLocked self._check_vm_status() self._check_snap_status() apiview, detail = self._get_apiview_detail() apiview['force'] = bool(ForceSerializer(data=self.data, default=True)) if not apiview['force']: snaplast = Snapshot.objects.only('id').filter( vm=vm, disk_id=snap.disk_id).order_by('-id')[0] if snap.id != snaplast.id: raise ExpectationFailed('VM has more recent snapshots') if vm.status != vm.STOPPED: raise VmIsNotOperational('VM is not stopped') if vm.tasks: raise VmHasPendingTasks msg = LOG_SNAP_UPDATE lock = self.LOCK % (vm.uuid, snap.disk_id) cmd = 'esnapshot rollback "%s@%s" 2>&1' % (self.zfs_filesystem, snap.zfs_name) vm.set_notready() tid, err = execute(request, vm.owner.id, cmd, meta=snap_meta(vm, msg, apiview, detail), lock=lock, callback=snap_callback(vm, snap), queue=vm.node.fast_queue) if err: vm.revert_notready() return FailureTaskResponse(request, err, vm=vm) else: snap.save_status(snap.ROLLBACK) return TaskResponse(request, tid, msg=msg, vm=vm, api_view=apiview, detail=detail, data=self.data)
def post(self): self._check_vm_status() apiview, detail = self._get_apiview_detail() request, vm, snap = self.request, self.vm, self.snap snap.status = snap.PENDING snap.define_id = self.snap_define_id snap.type = self.snaptype ser = SnapshotSerializer(request, snap, data=self.data) fsfreeze = '' if not ser.is_valid(): return FailureTaskResponse(request, ser.errors, vm=vm) if vm.is_hvm() and self.data.get('fsfreeze', False): qga_socket = vm.qga_socket_path if qga_socket: snap.fsfreeze = True if vm.status != vm.STOPPED: fsfreeze = '"%s"' % qga_socket self._check_snap_limit() self._check_snap_size_limit() # Issue #chili-848 self._check_snap_dc_size_limit() # Issue #chili-848 snap.zpool = vm.node.nodestorage_set.get(zpool=self.zpool) snap.save() detail += ', type=%s, fsfreeze=%s' % (self.snaptype, str( snap.fsfreeze).lower()) msg = LOG_SNAP_CREATE lock = self.LOCK % (vm.uuid, snap.disk_id) cmd = 'esnapshot create "%s@%s" "es:snapname=%s" %s 2>&1' % ( self.zfs_filesystem, snap.zfs_name, snap.name, fsfreeze) tid, err = execute(request, vm.owner.id, cmd, meta=snap_meta(vm, msg, apiview, detail), lock=lock, callback=snap_callback(vm, snap), queue=vm.node.fast_queue, tt=self.tt) if err: snap.delete() return FailureTaskResponse(request, err, vm=vm) else: return TaskResponse(request, tid, msg=msg, vm=vm, api_view=apiview, detail=detail, data=self.data)
def delete(self): """Delete multiple snapshots""" request, data, vm = self.request, self.data, self.vm disk_id, real_disk_id, zfs_filesystem = get_disk_id(request, vm, data) # Parse data['snapnames'] snaps, __ = get_snapshots(request, vm, real_disk_id, data) self._check_vm_status() snaps_lost = snaps.filter(status=Snapshot.LOST) msg = LOG_SNAPS_DELETE if snaps_lost: _result = {'message': 'Snapshots successfully deleted from DB'} _detail = "snapnames='%s', disk_id=%s" % (','.join(i.name for i in snaps_lost), disk_id) snaps_lost.delete() res = SuccessTaskResponse(request, _result, msg=msg, vm=vm, detail=_detail) snaps = snaps.filter(status=Snapshot.OK) # Work with OK snapshots from now on if not snaps: return res elif any(i.status != Snapshot.OK for i in snaps): raise ExpectationFailed('VM snapshot status is not OK') # Task type (a = automatic, e = manual) if getattr(request, 'define_id', None): tt = TT_AUTO else: tt = TT_EXEC snapnames = [i.name for i in snaps] _apiview_ = {'view': 'vm_snapshot_list', 'method': request.method, 'hostname': vm.hostname, 'disk_id': disk_id, 'snapnames': snapnames} _detail_ = "snapnames='%s', disk_id=%s" % (','.join(snapnames), disk_id) snap_ids = [snap.id for snap in snaps] zfs_names = ','.join([snap.zfs_name for snap in snaps]) lock = self.LOCK % (vm.uuid, real_disk_id) cmd = 'esnapshot destroy "%s@%s" 2>&1' % (zfs_filesystem, zfs_names) callback = ('api.vm.snapshot.tasks.vm_snapshot_list_cb', {'vm_uuid': vm.uuid, 'snap_ids': snap_ids}) tid, err = execute(request, vm.owner.id, cmd, meta=snap_meta(vm, msg, _apiview_, _detail_), lock=lock, callback=callback, queue=vm.node.fast_queue, tt=tt) if err: return FailureTaskResponse(request, err, vm=vm) else: snaps.update(status=Snapshot.PENDING) return TaskResponse(request, tid, msg=msg, vm=vm, api_view=_apiview_, detail=_detail_, data=self.data)
def delete(self): self._check_vm_status() self._check_snap_status(lost_ok=True) request, vm, snap = self.request, self.vm, self.snap apiview, detail = self._get_apiview_detail() msg = LOG_SNAP_DELETE if snap.status == Snapshot.LOST: snap.delete() res = {'message': 'Snapshot successfully deleted from DB'} return SuccessTaskResponse(request, res, msg=msg, vm=vm, detail=detail) SnapshotSerializer(request, snap) lock = self.LOCK % (vm.uuid, snap.disk_id) cmd = 'esnapshot destroy "%s@%s" 2>&1' % (self.zfs_filesystem, snap.zfs_name) tid, err = execute(request, vm.owner.id, cmd, meta=snap_meta(vm, msg, apiview, detail), lock=lock, callback=snap_callback(vm, snap), queue=vm.node.fast_queue, tt=self.tt) if err: return FailureTaskResponse(request, err, vm=vm) else: snap.save_status(snap.PENDING) return TaskResponse(request, tid, msg=msg, vm=vm, api_view=apiview, detail=detail, data=self.data)
def put(self): if 'note' in self.data: # Changing snapshot note instead of rollback (not logging) return self._update_note() request, vm, snap = self.request, self.vm, self.snap ser = SnapshotRestoreSerializer(request, vm, data=self.data) if not ser.is_valid(): return FailureTaskResponse(self.request, ser.errors) target_vm, target_vm_disk_id = ser.target_vm, ser.target_vm_disk_id if vm.node.status not in vm.node.STATUS_OPERATIONAL: raise NodeIsNotOperational if target_vm.locked: raise VmIsLocked if target_vm != vm: if target_vm.node.status not in target_vm.node.STATUS_OPERATIONAL: raise NodeIsNotOperational self._check_vm_status(vm=target_vm) if not vm.has_compatible_brand(target_vm.brand): raise PreconditionRequired('VM brand mismatch') source_disk = vm.json_active_get_disks()[self.disk_id - 1] target_disk = target_vm.json_active_get_disks()[target_vm_disk_id - 1] if target_disk['size'] != source_disk['size']: raise PreconditionRequired('Disk size mismatch') self._check_vm_status() self._check_snap_status() apiview, detail = self._get_apiview_detail() apiview['force'] = ser.data['force'] if target_vm != vm: detail += ", source_hostname='%s', target_hostname='%s', target_disk_id=%s" % ( vm.hostname, target_vm.hostname, target_vm_disk_id) apiview['source_hostname'] = vm.hostname apiview['target_hostname'] = target_vm.hostname apiview['target_disk_id'] = target_vm_disk_id if not apiview['force']: if Snapshot.objects.only('id').filter( vm=target_vm, disk_id=ser.target_vm_real_disk_id).exists(): raise ExpectationFailed('Target VM has snapshots') elif not apiview['force']: snaplast = Snapshot.objects.only('id').filter( vm=vm, disk_id=snap.disk_id).order_by('-id')[0] if snap.id != snaplast.id: raise ExpectationFailed('VM has more recent snapshots') if target_vm.status != vm.STOPPED: raise VmIsNotOperational('VM is not stopped') if target_vm.tasks: raise VmHasPendingTasks msg = LOG_SNAP_UPDATE lock = self.LOCK % (vm.uuid, snap.disk_id) if target_vm == vm: cmd = 'esnapshot rollback "%s@%s" 2>&1' % (self.zfs_filesystem, snap.zfs_name) else: cmd = 'esbackup snap-restore -s %s@%s -d %s' % ( self.zfs_filesystem, snap.zfs_name, ser.target_vm_disk_zfs_filesystem) if vm.node != target_vm.node: cmd += ' -H %s' % target_vm.node.address vm.set_notready() target_vm.set_notready() tid, err = execute(request, target_vm.owner.id, cmd, meta=snap_meta(target_vm, msg, apiview, detail), lock=lock, callback=snap_callback(target_vm, snap), queue=vm.node.fast_queue) if err: target_vm.revert_notready() if vm != target_vm: vm.revert_notready() return FailureTaskResponse(request, err, vm=target_vm) else: snap.save_status(snap.ROLLBACK) return TaskResponse(request, tid, msg=msg, vm=target_vm, api_view=apiview, detail=detail, data=self.data)