def create(self, vm, snap): """Create [POST] image from VM snapshot (ImageAdmin). This is always a DC bound task, but the task_id has a DC_UNBOUND task group flag, because socket.io will inform any admin regardless of the current admin DC. The callback is responsible for attaching the image into current DC. """ img, data, request = self.img, self.data, self.request assert request.dc == vm.dc if vm.uuid in settings.VMS_INTERNAL: # Bug #chili-792 raise PreconditionRequired('Internal VM can\'t be used for creating images') data.pop('dc_bound', None) # Default DC binding cannot be changed when creating Image for the first time img.dc_bound = vm.dc # Default DC binding set to VM DC (cannot be changed, ^^^) img.ostype = vm.ostype # Default ostype inherited from VM (cannot be changed) img.size = snap.disk_size # Default disk size inherited from VM (cannot be changed) img.owner = request.user # Default user (can be changed) img.alias = img.name # Default alias (can be changed) img.status = Image.OK # Set status for preliminary checks # Validate data (manifest info) ser = ImageSerializer(request, img, data) if not ser.is_valid(): return FailureTaskResponse(request, ser.errors, dc_bound=self.dc_bound) # Preliminary checks self._run_checks(img_server_must_exist=True) # This sets self.img_server to ImageVm() if vm.status not in (vm.RUNNING, vm.STOPPED, vm.STOPPING, vm.FROZEN): raise VmIsNotOperational if snap.status != snap.OK: raise ExpectationFailed('VM snapshot status is not OK') # Build manifest and set PENDING status # noinspection PyUnusedLocal data = ser.data img.manifest = img.build_manifest() img.status = Image.PENDING img.src_vm = vm img.src_snap = snap img.save() # Set snapshot status to PENDING snap.save_status(snap.PENDING) # Build command cmd_add = ' ; e=$?; cat %s/%s/manifest 2>&1; exit $e' % (self.img_server.datasets_dir, img.uuid) cmd = 'esimg create -s %s@%s' % (snap.zfs_filesystem, snap.zfs_name) if self.img_server.node != vm.node: cmd += ' -H %s' % vm.node.address return self._run_execute(LOG_IMAGE_CREATE, cmd, stdin=img.manifest.dump(), delete_on_error=True, vm=vm, snap=snap, error_fun=lambda: snap.save_status(snap.OK), detail_dict=ser.detail_dict(), cmd_add=cmd_add)
def put(self): """Update [PUT] image manifest in DB and on image server if needed. The task group is always DC unbound, but the current DC depends on the dc_bound flag: - dc_bound=False: task DC is default DC - dc_bound=[DC]: task DC is dc_bound DC The callback is responsible for restoring the active manifest if something goes wrong. """ img = self.img ser = ImageSerializer(self.request, img, self.data, partial=True) img_backup = ser.create_img_backup() if not ser.is_valid(): return FailureTaskResponse(self.request, ser.errors, dc_bound=self.dc_bound) # Preliminary checks self._run_checks() # This sets self.img_server to ImageVm() ser_data = ser.data if ser.update_manifest: # Rebuild manifest img.manifest = img.build_manifest() if self.img_server and ser.update_manifest: img.status = Image.PENDING img.backup = img_backup img.save() return self._run_execute(LOG_IMAGE_UPDATE, 'esimg update', stdin=img.manifest.dump(), recover_on_error=img_backup, detail_dict=ser.detail_dict()) else: # Just save new data img.manifest_active = img.manifest img.save() return SuccessTaskResponse(self.request, ser_data, obj=img, msg=LOG_IMAGE_UPDATE, detail_dict=ser.detail_dict(), dc_bound=self.dc_bound)
def post(self): """Import [POST] image from URL. This is always a DC bound task, but the task_id has a DC_UNBOUND task group flag, because socket.io will inform any admin regardless of the current admin DC. The callback is responsible for attaching the image into current DC if the image is dc_bound. """ img, data, request = self.img, self.data, self.request # ImageImportAdmin permission is required if not request.user.has_permission(request, ImageImportAdminPermission.name): raise PermissionDenied # Validate URL and file URL ser_import = ImportImageSerializer(img, data=data) if not ser_import.is_valid(): return FailureTaskResponse(request, ser_import.errors, dc_bound=self.dc_bound) if not request.user.is_staff: self.data.pop( 'dc_bound', None ) # default DC binding cannot be changed when creating object img.manifest = ser_import.manifest # Load imported manifest img.owner = request.user # Default user (can be changed) img.alias = img.name # Default alias (can be changed) img.status = Image.OK # Set status for preliminary checks # More default fields retrieved from the downloaded image manifest for img_field in ('version', 'desc', 'resize', 'deploy', 'tags'): if img_field not in data: def_value = getattr(img, img_field, None) if def_value: data[img_field] = def_value # Validate data for overriding manifest info ser = ImageSerializer(request, img, data) if not ser.is_valid(): return FailureTaskResponse(request, ser.errors, dc_bound=self.dc_bound) # Preliminary checks self._run_checks() # Build new manifest img.manifest = img.build_manifest() # Add URL into detail dict ser_data = ser.data dd = ser.detail_dict() dd.update(ser_import.detail_dict()) if self.img_server: img.status = Image.PENDING img.save() if ser_import.img_file_url.startswith(self.img_server.repo_url): logger.info( 'Importing image from local image server - assuming that image exists on server' ) cmd = 'esimg update -c' else: cmd = 'esimg import -f %s' % ser_import.img_file_url return self._run_execute(LOG_IMAGE_IMPORT, cmd, stdin=img.manifest.dump(), delete_on_error=True, detail_dict=dd) else: img.status = Image.OK img.manifest_active = img.manifest img.save() return SuccessTaskResponse(self.request, ser_data, obj=img, msg=LOG_IMAGE_IMPORT, detail_dict=dd, dc_bound=self.dc_bound)