class VirtualMachineViewSet(structure_views.ResourceViewSet): queryset = models.VirtualMachine.objects.all().order_by('name') filterset_class = filters.VirtualMachineFilter serializer_class = serializers.VirtualMachineSerializer create_executor = executors.VirtualMachineCreateExecutor delete_executor = executors.VirtualMachineDeleteExecutor pull_executor = executors.VirtualMachinePullExecutor @decorators.action(detail=True, methods=['post']) def start(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineStartExecutor().execute(virtual_machine) return response.Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) start_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('stopped'), ] start_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def stop(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineStopExecutor().execute(virtual_machine) return response.Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) stop_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('running'), ] stop_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def restart(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineRestartExecutor().execute(virtual_machine) return response.Response({'status': _('restart was scheduled')}, status=status.HTTP_202_ACCEPTED) restart_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('running'), ] restart_serializer_class = rf_serializers.Serializer
def _can_destroy_volume(volume): if volume.state == models.Volume.States.ERRED: return if volume.state != models.Volume.States.OK: raise core_exceptions.IncorrectStateException( _('Volume should be in OK state.')) core_validators.RuntimeStateValidator('available', 'error', 'error_restoring', 'error_extending', '')(volume)
class InstanceViewSet(structure_views.ImportableResourceViewSet): """ OpenStack instance permissions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Staff members can list all available VM instances in any service. - Customer owners can list all VM instances in all the services that belong to any of the customers they own. - Project administrators can list all VM instances, create new instances and start/stop/restart instances in all the services that are connected to any of the projects they are administrators in. - Project managers can list all VM instances in all the services that are connected to any of the projects they are managers in. """ queryset = models.Instance.objects.all() serializer_class = serializers.InstanceSerializer filterset_class = filters.InstanceFilter filter_backends = structure_views.ResourceViewSet.filter_backends + ( structure_filters.StartTimeFilter, ) pull_executor = executors.InstancePullExecutor pull_serializer_class = rf_serializers.Serializer update_executor = executors.InstanceUpdateExecutor update_validators = partial_update_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] def perform_create(self, serializer): instance = serializer.save() executors.InstanceCreateExecutor.execute( instance, ssh_key=serializer.validated_data.get('ssh_public_key'), flavor=serializer.validated_data['flavor'], is_heavy_task=True, ) def _has_backups(instance): if instance.backups.exists(): raise core_exceptions.IncorrectStateException( _('Cannot delete instance that has backups.')) def _can_destroy_instance(instance): if instance.state == models.Instance.States.ERRED: return if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.SHUTOFF): return if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.ACTIVE): raise core_exceptions.IncorrectStateException( _('Please stop the instance before its removal.')) raise core_exceptions.IncorrectStateException( _('Instance should be shutoff and OK or erred. ' 'Please contact support.')) def destroy(self, request, uuid=None): """ Deletion of an instance is done through sending a **DELETE** request to the instance URI. Valid request example (token is user specific): .. code-block:: http DELETE /api/openstacktenant-instances/abceed63b8e844afacd63daeac855474/ HTTP/1.1 Authorization: Token c84d653b9ec92c6cbac41c706593e66f567a7fa4 Host: example.com Only stopped instances or instances in ERRED state can be deleted. By default when instance is destroyed, all data volumes attached to it are destroyed too. In order to preserve data volumes use query parameter ?delete_volumes=false In this case data volumes are detached from the instance and then instance is destroyed. Note that system volume is deleted anyway. For example: .. code-block:: http DELETE /api/openstacktenant-instances/abceed63b8e844afacd63daeac855474/?delete_volumes=false HTTP/1.1 Authorization: Token c84d653b9ec92c6cbac41c706593e66f567a7fa4 Host: example.com """ serializer = self.get_serializer(data=request.query_params, instance=self.get_object()) serializer.is_valid(raise_exception=True) delete_volumes = serializer.validated_data['delete_volumes'] release_floating_ips = serializer.validated_data[ 'release_floating_ips'] resource = self.get_object() force = resource.state == models.Instance.States.ERRED executors.InstanceDeleteExecutor.execute( resource, force=force, delete_volumes=delete_volumes, release_floating_ips=release_floating_ips, is_async=self.async_executor, ) return response.Response({'status': _('destroy was scheduled')}, status=status.HTTP_202_ACCEPTED) destroy_validators = [_can_destroy_instance, _has_backups] destroy_serializer_class = serializers.InstanceDeleteSerializer @decorators.action(detail=True, methods=['post']) def change_flavor(self, request, uuid=None): instance = self.get_object() old_flavor_name = instance.flavor_name serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() flavor = serializer.validated_data.get('flavor') executors.InstanceFlavorChangeExecutor().execute( instance, flavor=flavor, old_flavor_name=old_flavor_name) return response.Response( {'status': _('change_flavor was scheduled')}, status=status.HTTP_202_ACCEPTED, ) def _can_change_flavor(instance): if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.ACTIVE): raise core_exceptions.IncorrectStateException( _('Please stop the instance before changing its flavor.')) change_flavor_serializer_class = serializers.InstanceFlavorChangeSerializer change_flavor_validators = [ _can_change_flavor, core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator( models.Instance.RuntimeStates.SHUTOFF), ] @decorators.action(detail=True, methods=['post']) def start(self, request, uuid=None): instance = self.get_object() executors.InstanceStartExecutor().execute(instance) return response.Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) def _can_start_instance(instance): if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.ACTIVE): raise core_exceptions.IncorrectStateException( _('Instance is already active.')) start_validators = [ _can_start_instance, core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator( models.Instance.RuntimeStates.SHUTOFF), ] start_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def stop(self, request, uuid=None): instance = self.get_object() executors.InstanceStopExecutor().execute(instance) return response.Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) def _can_stop_instance(instance): if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.SHUTOFF): raise core_exceptions.IncorrectStateException( _('Instance is already stopped.')) stop_validators = [ _can_stop_instance, core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator( models.Instance.RuntimeStates.ACTIVE), ] stop_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def restart(self, request, uuid=None): instance = self.get_object() executors.InstanceRestartExecutor().execute(instance) return response.Response({'status': _('restart was scheduled')}, status=status.HTTP_202_ACCEPTED) def _can_restart_instance(instance): if (instance.state == models.Instance.States.OK and instance.runtime_state == models.Instance.RuntimeStates.SHUTOFF): raise core_exceptions.IncorrectStateException( _('Please start instance first.')) restart_validators = [ _can_restart_instance, core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator( models.Instance.RuntimeStates.ACTIVE), ] restart_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def update_security_groups(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() executors.InstanceUpdateSecurityGroupsExecutor().execute(instance) return response.Response( {'status': _('security groups update was scheduled')}, status=status.HTTP_202_ACCEPTED, ) update_security_groups_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] update_security_groups_serializer_class = ( serializers.InstanceSecurityGroupsUpdateSerializer) @decorators.action(detail=True, methods=['post']) def backup(self, request, uuid=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) backup = serializer.save() executors.BackupCreateExecutor().execute(backup) return response.Response(serializer.data, status=status.HTTP_201_CREATED) backup_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] backup_serializer_class = serializers.BackupSerializer @decorators.action(detail=True, methods=['post']) def create_backup_schedule(self, request, uuid=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return response.Response(serializer.data, status=status.HTTP_201_CREATED) create_backup_schedule_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] create_backup_schedule_serializer_class = serializers.BackupScheduleSerializer @decorators.action(detail=True, methods=['post']) def update_internal_ips_set(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() executors.InstanceInternalIPsSetUpdateExecutor().execute(instance) return response.Response( {'status': _('internal ips update was scheduled')}, status=status.HTTP_202_ACCEPTED, ) update_internal_ips_set_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] update_internal_ips_set_serializer_class = ( serializers.InstanceInternalIPsSetUpdateSerializer) @decorators.action(detail=True, methods=['get']) def internal_ips_set(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer(instance.internal_ips_set.all(), many=True) return response.Response(serializer.data, status=status.HTTP_200_OK) internal_ips_set_serializer_class = serializers.NestedInternalIPSerializer @decorators.action(detail=True, methods=['post']) def update_floating_ips(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() executors.InstanceFloatingIPsUpdateExecutor().execute(instance) return response.Response( {'status': _('floating ips update was scheduled')}, status=status.HTTP_202_ACCEPTED, ) update_floating_ips_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] update_floating_ips_serializer_class = ( serializers.InstanceFloatingIPsUpdateSerializer) @decorators.action(detail=True, methods=['get']) def floating_ips(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer( instance=instance.floating_ips.all(), queryset=models.FloatingIP.objects.all(), many=True, ) return response.Response(serializer.data, status=status.HTTP_200_OK) floating_ips_serializer_class = serializers.NestedFloatingIPSerializer importable_resources_backend_method = 'get_instances_for_import' importable_resources_serializer_class = serializers.InstanceImportableSerializer import_resource_serializer_class = serializers.InstanceImportSerializer import_resource_executor = executors.InstancePullExecutor @decorators.action(detail=True, methods=['get']) def console(self, request, uuid=None): instance = self.get_object() backend = instance.get_backend() try: url = backend.get_console_url(instance) except OpenStackBackendError as e: raise exceptions.ValidationError(str(e)) return response.Response({'url': url}, status=status.HTTP_200_OK) console_validators = [ core_validators.StateValidator(models.Instance.States.OK) ] def check_permissions_for_console(request, view, instance=None): if not instance: return if request.user.is_staff: return if settings.WALDUR_OPENSTACK_TENANT[ 'ALLOW_CUSTOMER_USERS_OPENSTACK_CONSOLE_ACCESS']: structure_permissions.is_administrator(request, view, instance) else: raise exceptions.PermissionDenied() console_permissions = [check_permissions_for_console] @decorators.action(detail=True, methods=['get']) def console_log(self, request, uuid=None): instance = self.get_object() backend = instance.get_backend() serializer = self.get_serializer(data=request.query_params) serializer.is_valid(raise_exception=True) length = serializer.validated_data.get('length') try: log = backend.get_console_output(instance, length) except OpenStackBackendError as e: raise exceptions.ValidationError(str(e)) return response.Response(log, status=status.HTTP_200_OK) console_log_serializer_class = serializers.ConsoleLogSerializer console_log_permissions = [structure_permissions.is_administrator] @decorators.action(detail=True, methods=['delete']) def force_destroy(self, request, uuid=None): """This action completely repeats 'destroy', with the exclusion of validators. Destroy's validators require stopped VM. This requirement has expired. But for compatibility with old documentation, it must be left. """ return self.destroy(request, uuid) force_destroy_validators = [ _has_backups, core_validators.StateValidator(models.Instance.States.OK, models.Instance.States.ERRED), ] force_destroy_serializer_class = destroy_serializer_class
class VolumeViewSet(structure_views.ImportableResourceViewSet): queryset = models.Volume.objects.all() serializer_class = serializers.VolumeSerializer filterset_class = filters.VolumeFilter create_executor = executors.VolumeCreateExecutor update_executor = executors.VolumeUpdateExecutor pull_executor = executors.VolumePullExecutor def _can_destroy_volume(volume): if volume.state == models.Volume.States.ERRED: return if volume.state != models.Volume.States.OK: raise core_exceptions.IncorrectStateException( _('Volume should be in OK state.')) core_validators.RuntimeStateValidator('available', 'error', 'error_restoring', 'error_extending', '')(volume) def _volume_snapshots_exist(volume): if volume.snapshots.exists(): raise core_exceptions.IncorrectStateException( _('Volume has dependent snapshots.')) delete_executor = executors.VolumeDeleteExecutor destroy_validators = [ _can_destroy_volume, _volume_snapshots_exist, ] def _is_volume_bootable(volume): if volume.bootable: raise core_exceptions.IncorrectStateException( _('Volume cannot be bootable.')) def _is_volume_instance_shutoff(volume): if (volume.instance and volume.instance.runtime_state != models.Instance.RuntimeStates.SHUTOFF): raise core_exceptions.IncorrectStateException( _('Volume instance should be in shutoff state.')) def _is_volume_instance_ok(volume): if volume.instance and volume.instance.state != models.Instance.States.OK: raise core_exceptions.IncorrectStateException( _('Volume instance should be in OK state.')) @decorators.action(detail=True, methods=['post']) def extend(self, request, uuid=None): """ Increase volume size """ volume = self.get_object() old_size = volume.size serializer = self.get_serializer(volume, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() volume.refresh_from_db() executors.VolumeExtendExecutor().execute(volume, old_size=old_size, new_size=volume.size) return response.Response({'status': _('extend was scheduled')}, status=status.HTTP_202_ACCEPTED) extend_validators = [ _is_volume_bootable, _is_volume_instance_ok, _is_volume_instance_shutoff, core_validators.StateValidator(models.Volume.States.OK), ] extend_serializer_class = serializers.VolumeExtendSerializer @decorators.action(detail=True, methods=['post']) def snapshot(self, request, uuid=None): """ Create snapshot from volume """ serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) snapshot = serializer.save() executors.SnapshotCreateExecutor().execute(snapshot) return response.Response(serializer.data, status=status.HTTP_201_CREATED) snapshot_serializer_class = serializers.SnapshotSerializer @decorators.action(detail=True, methods=['post']) def create_snapshot_schedule(self, request, uuid=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return response.Response(serializer.data, status=status.HTTP_201_CREATED) create_snapshot_schedule_validators = [ core_validators.StateValidator(models.Volume.States.OK) ] create_snapshot_schedule_serializer_class = serializers.SnapshotScheduleSerializer @decorators.action(detail=True, methods=['post']) def attach(self, request, uuid=None): """ Attach volume to instance """ volume = self.get_object() serializer = self.get_serializer(volume, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() executors.VolumeAttachExecutor().execute(volume) return response.Response({'status': _('attach was scheduled')}, status=status.HTTP_202_ACCEPTED) attach_validators = [ core_validators.RuntimeStateValidator('available'), core_validators.StateValidator(models.Volume.States.OK), ] attach_serializer_class = serializers.VolumeAttachSerializer @decorators.action(detail=True, methods=['post']) def detach(self, request, uuid=None): """ Detach instance from volume """ volume = self.get_object() executors.VolumeDetachExecutor().execute(volume) return response.Response({'status': _('detach was scheduled')}, status=status.HTTP_202_ACCEPTED) detach_validators = [ _is_volume_bootable, core_validators.RuntimeStateValidator('in-use'), core_validators.StateValidator(models.Volume.States.OK), ] @decorators.action(detail=True, methods=['post']) def retype(self, request, uuid=None): """ Retype detached volume """ volume = self.get_object() serializer = self.get_serializer(volume, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() executors.VolumeRetypeExecutor().execute(volume) return response.Response({'status': _('retype was scheduled')}, status=status.HTTP_202_ACCEPTED) retype_validators = [ core_validators.RuntimeStateValidator('available'), core_validators.StateValidator(models.Volume.States.OK), ] retype_serializer_class = serializers.VolumeRetypeSerializer importable_resources_backend_method = 'get_volumes_for_import' importable_resources_serializer_class = serializers.VolumeImportableSerializer import_resource_serializer_class = serializers.VolumeImportSerializer
class VirtualMachineViewSet(structure_views.BaseResourceViewSet): queryset = models.VirtualMachine.objects.all() serializer_class = serializers.VirtualMachineSerializer delete_executor = executors.VirtualMachineDeleteExecutor @decorators.detail_route() def rdp(self, request, uuid=None): vm = self.get_object() try: rdp_endpoint = vm.endpoints.get( name=models.InstanceEndpoint.Name.RDP) except models.InstanceEndpoint.DoesNotExist: raise exceptions.NotFound( "This virtual machine doesn't run remote desktop") response = HttpResponse(content_type='application/x-rdp') response[ 'Content-Disposition'] = 'attachment; filename="{}.rdp"'.format( vm.name) response.write("full address:s:%s.cloudapp.net:%s\n" "prompt for credentials:i:1\n\n" % (vm.service_project_link.cloud_service_name, rdp_endpoint.public_port)) return response rdp_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('running') ] @decorators.detail_route(methods=['post']) def start(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineStartExecutor().execute(virtual_machine) return response.Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) start_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('stopped') ] start_serializer_class = rf_serializers.Serializer @decorators.detail_route(methods=['post']) def stop(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineStopExecutor().execute(virtual_machine) return response.Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) stop_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('running') ] stop_serializer_class = rf_serializers.Serializer @decorators.detail_route(methods=['post']) def restart(self, request, uuid=None): virtual_machine = self.get_object() executors.VirtualMachineRestartExecutor().execute(virtual_machine) return response.Response({'status': _('restart was scheduled')}, status=status.HTTP_202_ACCEPTED) restart_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator('running') ] restart_serializer_class = rf_serializers.Serializer def perform_create(self, serializer): instance = serializer.save() executors.VirtualMachineCreateExecutor.execute( instance, backend_image_id=serializer.validated_data['image'].backend_id, backend_size_id=serializer.validated_data['size'].pk, )
class VirtualMachineViewSet(structure_views.BaseResourceViewSet): queryset = models.VirtualMachine.objects.all() serializer_class = serializers.VirtualMachineSerializer filterset_class = filters.VirtualMachineFilter pull_executor = executors.VirtualMachinePullExecutor create_executor = executors.VirtualMachineCreateExecutor delete_executor = executors.VirtualMachineDeleteExecutor update_executor = executors.VirtualMachineUpdateExecutor update_validators = partial_update_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator(models.VirtualMachine.RuntimeStates.POWERED_OFF), ] destroy_validators = structure_views.BaseResourceViewSet.destroy_validators + [ core_validators.RuntimeStateValidator(models.VirtualMachine.RuntimeStates.POWERED_OFF) ] @action(detail=True, methods=['post']) def start(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineStartExecutor().execute(instance) return Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) start_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator( models.VirtualMachine.RuntimeStates.POWERED_OFF, models.VirtualMachine.RuntimeStates.SUSPENDED, ), ] start_serializer_class = rf_serializers.Serializer @action(detail=True, methods=['post']) def stop(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineStopExecutor().execute(instance) return Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) stop_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator( models.VirtualMachine.RuntimeStates.POWERED_ON, models.VirtualMachine.RuntimeStates.SUSPENDED, ), ] stop_serializer_class = rf_serializers.Serializer @action(detail=True, methods=['post']) def reset(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineResetExecutor().execute(instance) return Response({'status': _('reset was scheduled')}, status=status.HTTP_202_ACCEPTED) reset_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator( models.VirtualMachine.RuntimeStates.POWERED_ON, ), ] reset_serializer_class = rf_serializers.Serializer @action(detail=True, methods=['post']) def suspend(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineSuspendExecutor().execute(instance) return Response({'status': _('suspend was scheduled')}, status=status.HTTP_202_ACCEPTED) suspend_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator( models.VirtualMachine.RuntimeStates.POWERED_ON, ), ] suspend_serializer_class = rf_serializers.Serializer def vm_tools_are_running(vm): if vm.tools_state != models.VirtualMachine.ToolsStates.RUNNING: raise rf_serializers.ValidationError('VMware Tools are not running.') @action(detail=True, methods=['post']) def shutdown_guest(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineShutdownGuestExecutor().execute(instance) return Response({'status': _('shutdown was scheduled')}, status=status.HTTP_202_ACCEPTED) shutdown_guest_validators = reboot_guest_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator( models.VirtualMachine.RuntimeStates.POWERED_ON, ), vm_tools_are_running, ] shutdown_guest_serializer_class = rf_serializers.Serializer @action(detail=True, methods=['post']) def reboot_guest(self, request, uuid=None): instance = self.get_object() executors.VirtualMachineRebootGuestExecutor().execute(instance) return Response({'status': _('reboot was scheduled')}, status=status.HTTP_202_ACCEPTED) reboot_guest_serializer_class = rf_serializers.Serializer @action(detail=True, methods=['post']) def create_port(self, request, uuid=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) port = serializer.save() transaction.on_commit(lambda: executors.PortCreateExecutor().execute(port)) return Response(serializer.data, status=status.HTTP_201_CREATED) def check_number_of_ports(vm): # Limit of the network adapter per VM is 10 in vSphere 6.7, 6.5 and 6.0 if vm.port_set.count() >= 10: raise rf_serializers.ValidationError('Virtual machine can have at most 10 network adapters.') create_port_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), check_number_of_ports, ] create_port_serializer_class = serializers.PortSerializer @action(detail=True, methods=['post']) def create_disk(self, request, uuid=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) disk = serializer.save() transaction.on_commit(lambda: executors.DiskCreateExecutor().execute(disk)) return Response(serializer.data, status=status.HTTP_201_CREATED) def validate_total_size(vm): max_disk_total = serializers.get_int_or_none(vm.service_settings.options, 'max_disk_total') if max_disk_total: remaining_quota = max_disk_total - vm.total_disk if remaining_quota < 1024: raise rf_serializers.ValidationError('Storage quota has been reached.') create_disk_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), validate_total_size, ] create_disk_serializer_class = serializers.DiskSerializer @action(detail=True, methods=['get']) def console(self, request, uuid=None): """ This endpoint provides access to Virtual Machine Remote Console aka VMRC. """ instance = self.get_object() backend = instance.get_backend() try: url = backend.get_console_url(instance) except Exception: logger.exception('Unable to get console URL.') raise rf_serializers.ValidationError('Unable to get console URL.') return Response({'url': url}, status=status.HTTP_200_OK) console_validators = [core_validators.StateValidator(models.VirtualMachine.States.OK)] @action(detail=True, methods=['get']) def web_console(self, request, uuid=None): """ This endpoint provides access to HTML Console aka WMKS. """ instance = self.get_object() backend = instance.get_backend() try: url = backend.get_web_console_url(instance) except Exception: logger.exception('Unable to get web console URL.') raise rf_serializers.ValidationError('Unable to get web console URL.') return Response({'url': url}, status=status.HTTP_200_OK) web_console_validators = [ core_validators.StateValidator(models.VirtualMachine.States.OK), core_validators.RuntimeStateValidator(models.VirtualMachine.RuntimeStates.POWERED_ON) ]
class DropletViewSet(structure_views.ResourceViewSet): queryset = models.Droplet.objects.all() serializer_class = serializers.DropletSerializer filter_class = filters.DropletFilter create_executor = executors.DropletCreateExecutor update_executor = core_executors.EmptyExecutor delete_executor = executors.DropletDeleteExecutor destroy_validators = [ core_validators.StateValidator(models.Droplet.States.OK, models.Droplet.States.ERRED) ] def perform_create(self, serializer): region = serializer.validated_data['region'] image = serializer.validated_data['image'] size = serializer.validated_data['size'] ssh_key = serializer.validated_data.get('ssh_public_key') droplet = serializer.save(cores=size.cores, ram=size.ram, disk=size.disk, transfer=size.transfer) # XXX: We do not operate with backend_id`s in views. # View should pass objects to executor. self.create_executor.execute( droplet, async=self.async_executor, backend_region_id=region.backend_id, backend_image_id=image.backend_id, backend_size_id=size.backend_id, ssh_key_uuid=ssh_key.uuid.hex if ssh_key else None) @decorators.detail_route(methods=['post']) def start(self, request, uuid=None): instance = self.get_object() executors.DropletStartExecutor().execute(instance) return response.Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) start_validators = [ core_validators.StateValidator(models.Droplet.States.OK), core_validators.RuntimeStateValidator( models.Droplet.RuntimeStates.OFFLINE) ] start_serializer_class = rf_serializers.Serializer @decorators.detail_route(methods=['post']) def stop(self, request, uuid=None): instance = self.get_object() executors.DropletStopExecutor().execute(instance) return response.Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) stop_validators = [ core_validators.StateValidator(models.Droplet.States.OK), core_validators.RuntimeStateValidator( models.Droplet.RuntimeStates.ONLINE) ] stop_serializer_class = rf_serializers.Serializer @decorators.detail_route(methods=['post']) def restart(self, request, uuid=None): instance = self.get_object() executors.DropletRestartExecutor().execute(instance) return response.Response({'status': _('restart was scheduled')}, status=status.HTTP_202_ACCEPTED) restart_validators = [ core_validators.StateValidator(models.Droplet.States.OK), core_validators.RuntimeStateValidator( models.Droplet.RuntimeStates.ONLINE) ] restart_serializer_class = rf_serializers.Serializer @decorators.detail_route(methods=['post']) def resize(self, request, uuid=None): """ To resize droplet, submit a **POST** request to the instance URL, specifying URI of a target size. Pass {'disk': true} along with target size in order to perform permanent resizing, which allows you to resize your disk space as well as CPU and RAM. After increasing the disk size, you will not be able to decrease it. Pass {'disk': false} along with target size in order to perform flexible resizing, which only upgrades your CPU and RAM. This option is reversible. Note that instance must be OFFLINE. Example of a valid request: .. code-block:: http POST /api/digitalocean-droplets/6c9b01c251c24174a6691a1f894fae31/resize/ HTTP/1.1 Content-Type: application/json Accept: application/json Authorization: Token c84d653b9ec92c6cbac41c706593e66f567a7fa4 Host: example.com { "size": "http://example.com/api/digitalocean-sizes/1ee385bc043249498cfeb8c7e3e079f0/" } """ droplet = self.get_object() serializer = self.get_serializer(droplet, data=request.data) serializer.is_valid(raise_exception=True) size = serializer.validated_data['size'] disk = serializer.validated_data['disk'] executors.DropletResizeExecutor.execute(droplet, disk=disk, size=size, updated_fields=None, async=self.async_executor) message = _('Droplet {droplet_name} has been scheduled to %s resize.') % \ (disk and _('permanent') or _('flexible')) log.event_logger.droplet_resize.info( message, event_type='droplet_resize_scheduled', event_context={ 'droplet': droplet, 'size': size }) cores_increment = size.cores - droplet.cores ram_increment = size.ram - droplet.ram disk_increment = None droplet.cores = size.cores droplet.ram = size.ram if disk: disk_increment = size.disk - droplet.disk droplet.disk = size.disk droplet.save() spl = droplet.service_project_link if disk_increment: spl.add_quota_usage(spl.Quotas.storage, disk_increment, validate=True) spl.add_quota_usage(spl.Quotas.ram, ram_increment, validate=True) spl.add_quota_usage(spl.Quotas.vcpu, cores_increment, validate=True) return response.Response({'detail': _('resizing was scheduled')}, status=status.HTTP_202_ACCEPTED) resize_validators = [ core_validators.StateValidator(models.Droplet.States.OK) ] resize_serializer_class = serializers.DropletResizeSerializer
class InstanceViewSet(structure_views.ResourceViewSet): queryset = models.Instance.objects.all() filterset_class = filters.InstanceFilter serializer_class = serializers.InstanceSerializer create_executor = executors.InstanceCreateExecutor delete_executor = executors.InstanceDeleteExecutor destroy_validators = [core_validators.StateValidator(models.Instance.States.OK, models.Instance.States.ERRED)] def perform_create(self, serializer): instance = serializer.save() volume = instance.volume_set.first() self.create_executor.execute( instance, image=serializer.validated_data.get('image'), size=serializer.validated_data.get('size'), ssh_key=serializer.validated_data.get('ssh_public_key'), volume=volume ) @decorators.action(detail=True, methods=['post']) def start(self, request, uuid=None): instance = self.get_object() executors.InstanceStartExecutor().execute(instance) return response.Response({'status': _('start was scheduled')}, status=status.HTTP_202_ACCEPTED) start_validators = [core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator('stopped')] start_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def stop(self, request, uuid=None): instance = self.get_object() executors.InstanceStopExecutor().execute(instance) return response.Response({'status': _('stop was scheduled')}, status=status.HTTP_202_ACCEPTED) stop_validators = [core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator('running')] stop_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def restart(self, request, uuid=None): instance = self.get_object() executors.InstanceRestartExecutor().execute(instance) return response.Response({'status': _('restart was scheduled')}, status=status.HTTP_202_ACCEPTED) restart_validators = [core_validators.StateValidator(models.Instance.States.OK), core_validators.RuntimeStateValidator('running')] restart_serializer_class = rf_serializers.Serializer @decorators.action(detail=True, methods=['post']) def resize(self, request, uuid=None): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() new_size = serializer.validated_data.get('size') executors.InstanceResizeExecutor().execute(instance, size=new_size) return response.Response({'status': _('resize was scheduled')}, status=status.HTTP_202_ACCEPTED) resize_validators = [core_validators.StateValidator(models.Instance.States.OK)] resize_serializer_class = serializers.InstanceResizeSerializer