class NetworkIPPlanSerializer(NetworkIPSerializer): """ Read-only serializer. Includes subnet/netmask and network name to NetworkIPSerializer. """ net = s.Field(source='subnet.name') subnet = s.Field(source='subnet.ip_network.with_prefixlen') vlan_id = s.IntegerField(source='subnet.vlan_id', read_only=True)
class NodeGeolocationSerializer(serializers.ModelSerializer): coordinates = serializers.CharField(source='geolocation') lat = serializers.Field() lon = serializers.Field() class Meta: model = NodeGeolocation fields = ('address', 'coordinates', 'lat', 'lon')
class SliverSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field(source='api_id') interfaces = SliverIfaceSerializer(required=False, many=True, allow_add_remove=True) properties = serializers.PropertyField() data_uri = FakeFileField(field='data', required=False) instance_sn = serializers.IntegerField(read_only=True) mgmt_net = serializers.Field() # FIXME remove when api.aggregate supports nested serializers # is only required because SliverDefaultsSerializer imports resources # serializers, and breaks api.aggregate functionality based on # api._registry (see class SliverDefaultsSerializer) if is_installed('resources'): from resources.serializers import ResourceReqSerializer resources = ResourceReqSerializer(many=True, required=False) class Meta: model = Sliver exclude = ('data',) def to_native(self, obj): """ hack for implementing dynamic file_uri's on FakeFile """ self.__object__ = obj return super(SliverSerializer, self).to_native(obj) def validate(self, attrs): """ workaround about nested serialization sliverifaces need to be validated with an associated sliver """ super(SliverSerializer, self).validate(attrs) ifaces = attrs.get('interfaces', []) or [] for iface in ifaces: Sliver.get_registered_ifaces()[iface.type].clean_model(iface) return attrs def validate_interfaces(self, attrs, source): """Check that one interface of type private has been defined.""" interfaces = attrs.get(source, []) # check that mandatory private interface has been defined priv_ifaces = 0 for iface in interfaces: if iface.type == 'private': priv_ifaces += 1 if priv_ifaces > 1: raise serializers.ValidationError('There can only be one interface of type private.') if priv_ifaces == 0: raise serializers.ValidationError('There must exist one interface of type private.') validate_private_iface(interfaces) for name, iface_cls in Sliver.get_registered_ifaces().items(): validate_ifaces_nr(name, iface_cls, interfaces) return attrs
class TaskLogEntrySerializer(s.ModelSerializer): """ Serializes vms.models.TaskLogEntry """ username = s.Field(source='get_username') object_name = s.Field(source='get_object_name') object_alias = s.Field(source='get_object_alias') object_type = s.Field(source='object_type') class Meta: model = TaskLogEntry fields = ('time', 'task', 'status', 'username', 'msg', 'detail', 'object_name', 'object_alias', 'object_type', 'flag')
class StateSerializer(serializers.ModelSerializer): current = serializers.Field() last_change_on = serializers.Field() url = serializers.URLField(source='get_url') verified = serializers.Field(source='ssl_verified') metadata = serializers.JSONField() data = serializers.JSONField() class Meta: model = State fields = ('url', 'current', 'last_change_on', 'last_seen_on', 'last_try_on', 'verified', 'metadata', 'data') read_only_fields = ('last_seen_on', 'last_try_on')
class RecordSerializer(s.InstanceSerializer): """ pdns.models.Record """ _model_ = Record _update_fields_ = ('name', 'type', 'content', 'ttl', 'prio', 'disabled') _null_fields_ = frozenset({'content', 'ttl', 'prio'}) id = s.Field() domain = s.Field() name = s.CharField(max_length=253) # Validated via pdns.validators type = s.ChoiceField(choices=Record.TYPE) content = s.CharField(max_length=65535, required=False) ttl = s.IntegerField(default=Record.TTL, required=False, min_value=0, max_value=2147483647) prio = s.IntegerField(default=Record.PRIO, required=False, min_value=0, max_value=65535) disabled = s.BooleanField(default=False) changed = s.DateTimeField(read_only=True, required=False) # noinspection PyMethodMayBeStatic def validate_name(self, attrs, source): if source in attrs: name = attrs[source] if name == '@': name = self.object.domain.name attrs[source] = name.lower() # The record name must be always lower-cased (DB requirement) return attrs def validate(self, attrs): record = self.object try: run_record_validator(record.domain, attrs.get('type', record.type), attrs.get('name', record.name), attrs.get('content', record.content)) except RecordValidationError as exc: self._errors = exc.message_dict return attrs def detail_dict(self, **kwargs): """Always include id and name""" dd = super(RecordSerializer, self).detail_dict(**kwargs) dd['id'] = self.object.id dd['name'] = self.object.name return dd
class ResourceSerializer(serializers.ModelSerializer): avail = serializers.Field() unit = serializers.CharField(read_only=True) class Meta: model = Resource fields = ['name', 'max_req', 'dflt_req', 'unit', 'avail']
class NodeSerializer(s.Serializer): """ Node details serializer (read-only). """ hostname = s.Field() address = s.Field() status = s.IntegerChoiceField(choices=Node.STATUS_DB, read_only=True) node_status = s.DisplayChoiceField(source='status', choices=Node.STATUS_DB, read_only=True) owner = s.SlugRelatedField(slug_field='username', read_only=True) is_head = s.BooleanField(read_only=True) cpu = s.IntegerField(source='cpu_total', read_only=True) ram = s.IntegerField(source='ram_total', read_only=True) cpu_free = s.IntegerField(read_only=True) ram_free = s.IntegerField(read_only=True) ram_kvm_overhead = s.IntegerField(read_only=True)
class UserCreateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() auth_tokens = AuthTokenField(required=False) date_joined = serializers.DateTimeField(read_only=True) last_login = serializers.DateTimeField(read_only=True) class Meta: model = User exclude = ['password', 'groups', 'username', 'email', 'is_active', 'is_superuser']
class ServerSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() api = ServerApiSerializer(many=True, allow_add_remove=True) properties = serializers.PropertyField() class Meta: model = Server def validate_tinc(self, attrs, source): from tinc.serializers import validate_tinc return validate_tinc(self, attrs, source)
class HostCreateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() mgmt_net = MgmtNetConfRelatedField(source='related_mgmtnet') tinc = TincHostRelatedField(source='related_tinc', required=False) class Meta: model = Host exclude = ('owner', ) def validate_tinc(self, attrs, source): return validate_tinc(self, attrs, source)
class SliverIfaceSerializer(serializers.ModelSerializer): parent_name = serializers.Field(source='parent') class Meta: model = SliverIface fields = ('nr', 'name', 'type', 'parent_name') def get_identity(self, data): try: return data.get('nr', None) except AttributeError: return data
class FirmwareSerializer(serializers.ModelSerializer): state = serializers.Field() progress = serializers.SerializerMethodField('get_progress') next = serializers.SerializerMethodField('get_next') description = serializers.SerializerMethodField('get_description') content_message = serializers.SerializerMethodField('get_content_message') image_url = serializers.HyperlinkedFileField(source='image', read_only=True) class Meta: model = Build fields = ('state', 'progress', 'next', 'description', 'content_message', 'image_url', 'date') def get_state(self, instance): if self.build: return self.build.state return None def get_task_info(self, info): if self.object: task = self.object.task result = task.result or {} try: return result.get(info, None) except AttributeError: # result is an error or exception return result return None def get_progress(self, instance): return self.get_task_info('progress') def get_next(self, instance): return self.get_task_info('next') def get_description(self, instance): # TODO move to model ? if self.object: task = self.object.task result = task.result or {} if self.get_progress(instance) == 100: return "Building process finished" try: return "%s ..." % result.get( 'description', 'Waiting for your building task to begin.') except AttributeError: # result is an error or exception return result return "" def get_content_message(self, instance): if self.object: return self.object.state_description return ""
class VmSerializer(VmBaseSerializer): """ VM details (read-only) """ hostname = s.Field() uuid = s.CharField(read_only=True) alias = s.Field() node = s.SlugRelatedField(slug_field='hostname', read_only=True, required=False) owner = s.SlugRelatedField(slug_field='username', read_only=True) status = s.DisplayChoiceField(choices=Vm.STATUS, read_only=True) node_status = s.DisplayChoiceField(source='node.status', choices=Node.STATUS_DB, read_only=True) vcpus = s.IntegerField(read_only=True) ram = s.IntegerField(read_only=True) disk = s.IntegerField(read_only=True) ips = s.ArrayField(read_only=True) uptime = s.IntegerField(source='uptime_actual', read_only=True) locked = s.BooleanField(read_only=True)
class NodeCreateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() properties = serializers.PropertyField() arch = serializers.ChoiceField(choices=settings.NODES_NODE_ARCHS, required=True) slivers = serializers.RelHyperlinkedRelatedField(many=True, read_only=True, view_name='sliver-detail') direct_ifaces = DirectIfaceSerializer(required=False, many=True, allow_add_remove=True) cert = serializers.Field(source='api.cert') boot_sn = serializers.IntegerField(read_only=True) api = NodeApiSerializer(required=False) class Meta: model = Node exclude = ('set_state',) + FW_CONFIG_FIELDS def get_fields(self, *args, **kwargs): """ Filter groups: the user creating this node must be a group or node administrator of this group, and the group must have node creation allowed (/allow_nodes=true). """ fields = super(NodeCreateSerializer, self).get_fields(*args, **kwargs) try: user = self.context['view'].request.user except KeyError: # avoid error when used out of Rest API return fields queryset = fields['group'].queryset if not user.is_superuser: msg = " Check if you have group or node administrator roles at the provided group." fields['group'].error_messages['does_not_exist'] += msg # bug #321: filter by user.id (None for Anonymous users) fields['group'].queryset = queryset.filter( Q(roles__is_group_admin=True) | Q(roles__is_node_admin=True), allow_nodes=True, roles__user=user.id) return fields def validate_tinc(self, attrs, source): from tinc.serializers import validate_tinc return validate_tinc(self, attrs, source)
class DcNodeStorageSerializer(s.InstanceSerializer): """ vms.models.NodeStorage """ _model_ = NodeStorage node = s.Field(source='node.hostname') zpool = s.Field() alias = s.Field(source='storage.alias') owner = s.Field(source='storage.owner.username') access = s.IntegerField(source='storage.access', read_only=True) type = s.IntegerField(source='storage.type', read_only=True) size = s.IntegerField( read_only=True) # storage.size_total or dc_node.disk if local storage size_free = s.IntegerField( read_only=True ) # storage.size_free or dc_node.disk_free if local storage desc = s.Field(source='storage.desc') def detail_dict(self, **kwargs): # Add dc into detail dict return {'dc': self.request.dc}
class SliceCreateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() expires_on = serializers.DateTimeField(read_only=True) instance_sn = serializers.IntegerField(read_only=True) properties = serializers.PropertyField() isolated_vlan_tag = serializers.IntegerField(read_only=True) sliver_defaults = SliverDefaultsSerializer() slivers = serializers.RelHyperlinkedRelatedField(many=True, read_only=True, view_name='sliver-detail') class Meta: model = Slice exclude = ('set_state',)
class TemplateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() image_uri = FakeFileField(field='image') node_archs = serializers.MultiSelectField(choices=NODES_NODE_ARCHS) is_active = serializers.BooleanField() class Meta: model = Template exclude = ['image'] def to_native(self, obj): """ hack for implementing dynamic file_uri's on FakeFile """ self.__object__ = obj return super(TemplateSerializer, self).to_native(obj)
class GroupCreateSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() class Meta: model = Group exclude = ('allow_nodes', 'allow_slices', 'user_roles')
class NodeStorageSerializer(s.InstanceSerializer): """ vms.models.NodeStorage """ error_negative_resources = s.ErrorList([_('Value is too low because of existing virtual machines.')]) _model_ = NodeStorage _default_fields_ = ('alias', 'owner', 'size_coef', 'zpool') node = s.Field(source='node.hostname') zpool = s.ChoiceField(source='zpool') alias = s.SafeCharField(source='storage.alias', max_length=32) owner = s.SlugRelatedField(source='storage.owner', slug_field='username', queryset=User.objects, required=False) access = s.IntegerChoiceField(source='storage.access', choices=Storage.ACCESS, default=Storage.PRIVATE) type = s.IntegerChoiceField(source='storage.type', choices=Storage.TYPE, default=Storage.LOCAL) size = s.IntegerField(source='storage.size_total', read_only=True) size_coef = s.DecimalField(source='storage.size_coef', min_value=0, max_digits=4, decimal_places=2) size_free = s.IntegerField(source='storage.size_free', read_only=True) created = s.DateTimeField(source='storage.created', read_only=True, required=False) desc = s.SafeCharField(source='storage.desc', max_length=128, required=False) def __init__(self, request, instance, *args, **kwargs): self._update_fields_ = ['alias', 'owner', 'access', 'desc', 'type', 'size_coef'] super(NodeStorageSerializer, self).__init__(request, instance, *args, **kwargs) if not kwargs.get('many', False): self._size_coef = instance.storage.size_coef self.fields['owner'].queryset = get_owners(request) if request.method == 'POST': self.fields['zpool'].choices = [(i, i) for i in instance.node.zpools.keys()] self._update_fields_.append('zpool') else: self.fields['zpool'].read_only = True def validate_owner(self, attrs, source): """Cannot change owner while pending tasks exist""" validate_owner(self.object, attrs.get(source, None), _('Storage')) return attrs def validate_alias(self, attrs, source): try: value = attrs[source] except KeyError: pass else: validate_alias(self.object, value, field_comparison='storage__alias__iexact') return attrs def validate(self, attrs): # Default owner is request.user, but setting this in __init__ does not work if 'storage.owner' in attrs and attrs['storage.owner'] is None: if self.object.pk: del attrs['storage.owner'] else: attrs['storage.owner'] = self.request.user return attrs @property def update_storage_resources(self): """True if size_coef changed""" return not(self.object.storage.size_coef == self._size_coef)
class NodeDefineSerializer(s.InstanceSerializer): """ vms.models.Node """ error_negative_resources = s.ErrorList( [_('Value is too low because of existing virtual machines.')]) _model_ = Node _update_fields_ = ('status', 'owner', 'is_compute', 'is_backup', 'cpu_coef', 'ram_coef', 'monitoring_hostgroups', 'monitoring_templates') hostname = s.CharField(read_only=True) uuid = s.CharField(read_only=True) address = s.CharField(read_only=True) status = s.IntegerChoiceField(choices=Node.STATUS_DB) node_status = s.DisplayChoiceField(source='status', choices=Node.STATUS_DB, read_only=True) owner = s.SlugRelatedField(slug_field='username', queryset=User.objects, read_only=False) is_head = s.BooleanField(read_only=True) is_compute = s.BooleanField() is_backup = s.BooleanField() cpu = s.IntegerField(source='cpu_total', read_only=True) ram = s.IntegerField(source='ram_total', read_only=True) cpu_coef = s.DecimalField(min_value=0, max_digits=4, decimal_places=2) ram_coef = s.DecimalField(min_value=0, max_value=1, max_digits=4, decimal_places=2) cpu_free = s.IntegerField(read_only=True) ram_free = s.IntegerField(read_only=True) ram_kvm_overhead = s.IntegerField(read_only=True) sysinfo = s.Field( source='api_sysinfo') # Field is read_only=True by default monitoring_hostgroups = s.ArrayField(max_items=16, default=[]) monitoring_templates = s.ArrayField(max_items=32, default=[]) created = s.DateTimeField(read_only=True, required=False) def __init__(self, request, instance, *args, **kwargs): super(NodeDefineSerializer, self).__init__(request, instance, *args, **kwargs) self.clear_cache = False self.status_changed = False self.monitoring_changed = False if not kwargs.get('many', False): # Used for update_node_resources() self._cpu_coef = instance.cpu_coef self._ram_coef = instance.ram_coef # Only active users self.fields['owner'].queryset = get_owners(request) def validate_owner(self, attrs, source): """Cannot change owner while pending tasks exist""" validate_owner(self.object, attrs.get(source, None), _('Compute node')) return attrs def validate_status(self, attrs, source): """Mark the status change -> used for triggering the signal. Do not allow a manual status change from unlicensed status.""" try: value = attrs[source] except KeyError: return attrs if self.object.status != value: node = self.object if node.is_unlicensed(): raise s.ValidationError( _('Cannot change status. Please add a valid license first.' )) if node.is_unreachable() or node.is_offline( ): # Manual switch from unreachable and offline state if settings.DEBUG: logger.warning( 'DEBUG mode on => skipping status checking of node %s', self.object) elif not node_ping(self.object, all_workers=False ): # requires that node is really online raise s.ValidationError( _('Cannot change status. Compute node is down.')) self.clear_cache = True self.status_changed = value return attrs def validate_is_compute(self, attrs, source): """Search for defined VMs when turning compute capability off""" if source in attrs and self.object.is_compute != attrs[source]: if self.object.vm_set.exists(): raise s.ValidationError(_('Found existing VMs on node.')) self.clear_cache = True return attrs def validate_is_backup(self, attrs, source): """Search for existing backup definitions, which are using this node""" if source in attrs and self.object.is_backup != attrs[source]: if self.object.backupdefine_set.exists(): raise s.ValidationError( _('Found existing VM backup definitions.')) self.clear_cache = True # Check existing backups when removing node if self.request.method == 'DELETE': if self.object.backup_set.exists(): raise s.ValidationError(_('Found existing VM backups.')) self.clear_cache = True return attrs def validate_monitoring_hostgroups(self, attrs, source): """Mark the monitoring change -> used for triggering the signal""" if source in attrs and self.object.monitoring_hostgroups != attrs[ source]: self.monitoring_changed = True return attrs def validate_monitoring_templates(self, attrs, source): """Mark the monitoring change -> used for triggering the signal""" if source in attrs and self.object.monitoring_templates != attrs[ source]: self.monitoring_changed = True return attrs @property def update_node_resources(self): """True if cpu_coef or ram_coef changed""" return not (self.object.cpu_coef == self._cpu_coef and self.object.ram_coef == self._ram_coef)
class IslandSerializer(serializers.UriHyperlinkedModelSerializer): id = serializers.Field() class Meta: model = Island
class NodeVersionSerializer(s.Serializer): hostname = s.Field() version = s.Field(source='system_version')
class VmSerializer(_VmSerializer): dc = s.Field(source='dc.name')
class NodeDefineSerializer(s.InstanceSerializer): """ vms.models.Node """ error_negative_resources = s.ErrorList( [_('Value is too low because of existing virtual machines.')]) _model_ = Node _update_fields_ = ('status', 'owner', 'address', 'is_compute', 'is_backup', 'note', 'cpu_coef', 'ram_coef', 'monitoring_hostgroups', 'monitoring_templates') hostname = s.CharField(read_only=True) uuid = s.CharField(read_only=True) address = s.ChoiceField() status = s.IntegerChoiceField(choices=Node.STATUS_DB) node_status = s.DisplayChoiceField(source='status', choices=Node.STATUS_DB, read_only=True) owner = s.SlugRelatedField(slug_field='username', queryset=User.objects, read_only=False) is_head = s.BooleanField(read_only=True) is_compute = s.BooleanField() is_backup = s.BooleanField() note = s.CharField(required=False) cpu = s.IntegerField(source='cpu_total', read_only=True) ram = s.IntegerField(source='ram_total', read_only=True) cpu_coef = s.DecimalField(min_value=0, max_digits=4, decimal_places=2) ram_coef = s.DecimalField(min_value=0, max_value=1, max_digits=4, decimal_places=2) cpu_free = s.IntegerField(read_only=True) ram_free = s.IntegerField(read_only=True) ram_kvm_overhead = s.IntegerField(read_only=True) sysinfo = s.Field( source='api_sysinfo') # Field is read_only=True by default monitoring_hostgroups = s.ArrayField( max_items=16, default=[], validators=(RegexValidator( regex=MonitoringBackend.RE_MONITORING_HOSTGROUPS), )) monitoring_templates = s.ArrayField(max_items=32, default=[]) created = s.DateTimeField(read_only=True, required=False) def __init__(self, request, instance, *args, **kwargs): super(NodeDefineSerializer, self).__init__(request, instance, *args, **kwargs) self.clear_cache = False self.status_changed = False self.address_changed = False self.old_ip_address = None self.monitoring_changed = False if not kwargs.get('many', False): # Valid node IP addresses self.fields['address'].choices = [(ip, ip) for ip in instance.ips] # Used for update_node_resources() self._cpu_coef = instance.cpu_coef self._ram_coef = instance.ram_coef # Only active users self.fields['owner'].queryset = get_owners(request) def validate_owner(self, attrs, source): """Cannot change owner while pending tasks exist""" validate_owner(self.object, attrs.get(source, None), _('Compute node')) return attrs def validate_address(self, attrs, source): """Mark that node IP address is going to change""" new_address = attrs.get(source, None) if new_address and self.object.address != new_address: self.address_changed = True try: self.old_ip_address = self.object.ip_address except ObjectDoesNotExist: self.old_ip_address = None return attrs def validate_status(self, attrs, source): """Mark the status change -> used for triggering the signal. Do not allow a manual status change from unlicensed status.""" try: value = attrs[source] except KeyError: return attrs if self.object.status != value: node = self.object if node.is_unlicensed(): raise s.ValidationError( _('Cannot change status. Please add a valid license first.' )) if node.is_unreachable() or node.is_offline( ): # Manual switch from unreachable and offline state if settings.DEBUG: logger.warning( 'DEBUG mode on => skipping status checking of node %s', self.object) elif not node_ping(self.object, all_workers=False ): # requires that node is really online raise s.ValidationError( _('Cannot change status. Compute node is down.')) self.clear_cache = True self.status_changed = value return attrs def validate_is_compute(self, attrs, source): """Search for defined VMs when turning compute capability off""" if source in attrs and self.object.is_compute != attrs[source]: if self.object.vm_set.exists(): raise s.ValidationError(_('Found existing VMs on node.')) self.clear_cache = True return attrs def validate_is_backup(self, attrs, source): """Search for existing backup definitions, which are using this node""" if source in attrs and self.object.is_backup != attrs[source]: if self.object.backupdefine_set.exists(): raise s.ValidationError( _('Found existing VM backup definitions.')) self.clear_cache = True # Check existing backups when removing node if self.request.method == 'DELETE': if self.object.backup_set.exists(): raise s.ValidationError(_('Found existing VM backups.')) self.clear_cache = True return attrs def validate_monitoring_hostgroups(self, attrs, source): """Mark the monitoring change -> used for triggering the signal""" if source in attrs and self.object.monitoring_hostgroups != attrs[ source]: self.monitoring_changed = True return attrs def validate_monitoring_templates(self, attrs, source): """Mark the monitoring change -> used for triggering the signal""" if source in attrs and self.object.monitoring_templates != attrs[ source]: self.monitoring_changed = True return attrs @property def update_node_resources(self): """True if cpu_coef or ram_coef changed""" return not (self.object.cpu_coef == self._cpu_coef and self.object.ram_coef == self._ram_coef) def save(self): """Update compute node attributes in database""" node = self.object # NOTE: # Changing cpu or disk coefficients can lead to negative numbers in node.cpu/ram_free or dc_node.cpu/ram_free try: with transaction.atomic(): node.save(update_resources=self.update_node_resources, clear_cache=self.clear_cache) if self.update_node_resources: if node.cpu_free < 0 or node.dcnode_set.filter( cpu_free__lt=0).exists(): raise IntegrityError('cpu_check') if node.ram_free < 0 or node.dcnode_set.filter( ram_free__lt=0).exists(): raise IntegrityError('ram_check') except IntegrityError as exc: errors = {} exc_error = str(exc) # ram or cpu constraint was violated on vms_dcnode (can happen when DcNode strategy is set to RESERVED) # OR a an exception was raised above if 'ram_check' in exc_error: errors['ram_coef'] = self.error_negative_resources if 'cpu_check' in exc_error: errors['cpu_coef'] = self.error_negative_resources if not errors: raise exc return errors if self.update_node_resources: # cpu_free or ram_free changed self.reload() return None
class ExtendedVmSerializer(_ExtendedVmSerializer): dc = s.Field(source='dc.name')
class DcNodeSerializer(s.InstanceSerializer): """ vms.models.DcNode """ _model_ = DcNode _update_fields_ = ('strategy', 'cpu', 'ram', 'disk', 'priority') _default_fields_ = ('cpu', 'ram', 'disk') hostname = s.Field(source='node.hostname') strategy = s.IntegerChoiceField(choices=DcNode.STRATEGY, default=DcNode.SHARED) priority = s.IntegerField(min_value=0, max_value=9999, default=100) cpu = s.IntegerField() ram = s.IntegerField() disk = s.IntegerField() cpu_free = s.IntegerField(read_only=True) ram_free = s.IntegerField(read_only=True) disk_free = s.IntegerField(read_only=True) ram_kvm_overhead = s.IntegerField(read_only=True) def __init__(self, request, instance, *args, **kwargs): super(DcNodeSerializer, self).__init__(request, instance, *args, **kwargs) if not kwargs.get('many', False): # Maximum = node resources cpu_n, ram_n, disk_n = instance.node.resources self.fields['cpu'].validators.append( validators.MaxValueValidator(int(cpu_n))) self.fields['ram'].validators.append( validators.MaxValueValidator(int(ram_n))) self.fields['disk'].validators.append( validators.MaxValueValidator(int(disk_n))) if request.method == 'PUT': # Minimum = used resources in this DC (recalculate from node) cpu_min, ram_min, disk_min = instance.node.get_used_resources( request.dc) else: # Minimum = used resources in this DC (DcNode set - DcNode free) cpu_min = (instance.cpu or 0) - instance.cpu_free ram_min = (instance.ram or 0) - instance.ram_free disk_min = (instance.disk or 0) - instance.disk_free self.fields['cpu'].validators.append( validators.MinValueValidator(cpu_min)) self.fields['ram'].validators.append( validators.MinValueValidator(ram_min)) self.fields['disk'].validators.append( validators.MinValueValidator(disk_min)) def validate(self, attrs): strategy = int(attrs.get('strategy', self.object.strategy)) if strategy == DcNode.RESERVED: cpu = int(attrs.get('cpu', self.object.cpu)) ram = int(attrs.get('ram', self.object.ram)) disk = int(attrs.get('disk', self.object.disk)) cpu_nf, ram_nf, disk_nf = self.object.get_nonreserved_free_resources( exclude_this_dc=True) if cpu > cpu_nf: self._errors['cpu'] = s.ErrorList( [_('Not enough free CPUs on node.')]) if ram > ram_nf: self._errors['ram'] = s.ErrorList( [_('Not enough free RAM on node.')]) if disk > disk_nf: self._errors['disk'] = s.ErrorList( [_('Not enough free disk space on node.')]) return attrs def detail_dict(self, **kwargs): # Add dc into detail dict details = super(DcNodeSerializer, self).detail_dict() details['dc'] = self.request.dc return details