Exemple #1
0
    def validate(self, attrs):
        target_hostname_or_uuid = attrs.get('target_hostname_or_uuid', None)
        target_disk_id = attrs.get('target_disk_id', None)

        if target_hostname_or_uuid and not target_disk_id:
            err_msg = _(
                'This field is required when target_hostname_or_uuid is specified.'
            )
            self._errors['target_disk_id'] = s.ErrorList([err_msg])
            return attrs
        elif not target_hostname_or_uuid and target_disk_id:
            err_msg = _(
                'This field is required when target_disk_id is specified.')
            self._errors['target_hostname_or_uuid'] = s.ErrorList([err_msg])
            return attrs
        elif target_hostname_or_uuid and target_disk_id:
            try:
                self.target_vm = get_vm(self.request,
                                        target_hostname_or_uuid,
                                        exists_ok=True,
                                        noexists_fail=True,
                                        check_node_status=None)
            except ObjectNotFound as exc:
                self._errors['target_hostname_or_uuid'] = s.ErrorList(
                    [exc.detail])
            else:
                try:
                    self.target_vm_disk_id, self.target_vm_real_disk_id, self.target_vm_disk_zfs_filesystem = \
                        get_disk_id(self.request, self.target_vm, disk_id=target_disk_id)
                except InvalidInput as exc:
                    self._errors['target_disk_id'] = s.ErrorList([exc.detail])

        return attrs
Exemple #2
0
    def validate(self, attrs):
        if attrs.get('show_all', False):
            attrs['dc_bound'] = False

        request = self.request
        since = attrs.get('since', None)
        until = attrs.get('until', None)

        if until and not since:
            self._errors['since'] = s.ErrorList([_('Missing value.')])
            return attrs

        if since and not until:
            attrs['until'] = s.TimeStampField.now()

        dc_bound = attrs['dc_bound']
        vm_uuids = attrs.get('vm_uuids', None)
        vm_hostnames = attrs.get('vm_hostnames', None)
        node_uuids = attrs.get('node_uuids', None)
        node_hostnames = attrs.get('node_hostnames', None)

        if vm_hostnames is not None or vm_uuids is not None:
            vms_qs = Vm.objects.exclude(status=Vm.NOTCREATED).filter(slavevm__isnull=True)\
                       .filter(Q(hostname__in=vm_hostnames or ()) | Q(uuid__in=vm_uuids or ()))
        else:
            vms_qs = None

        if dc_bound:
            if node_uuids is not None:
                self._errors['node_uuids'] = s.ErrorList([PERMISSION_DENIED])
                return attrs

            if node_hostnames is not None:
                self._errors['node_hostnames'] = s.ErrorList(
                    [PERMISSION_DENIED])
                return attrs

            if vms_qs:
                vms_qs = vms_qs.filter(dc=request.dc)
        else:
            if node_hostnames is not None or node_uuids is not None:
                qs = Node.objects.filter(
                    Q(hostname__in=node_hostnames or ())
                    | Q(uuid__in=node_uuids or ()))
                self.nodes = map(
                    str,
                    qs.order_by('uuid').values_list('uuid', flat=True))

        if vms_qs:
            self.vms = map(
                str,
                vms_qs.order_by('uuid').values_list('uuid', flat=True))

        return attrs
Exemple #3
0
    def validate(self, attrs):
        if attrs.get('EMAIL_USE_TLS', None) and attrs.get('EMAIL_USE_SSL', None):
            self._errors['EMAIL_USE_TLS'] = self._errors['EMAIL_USE_SSL'] = s.ErrorList([
                _('Cannot enable EMAIL_USE_TLS and EMAIL_USE_SSL together.')
            ])

        # Do not allow SMS registration without the SMS module
        if (attrs.get('SMS_REGISTRATION_ENABLED', None) and
                not attrs.get('SMS_ENABLED', self.request.dc.settings.SMS_ENABLED)):
            self._errors['SMS_REGISTRATION_ENABLED'] = s.ErrorList([_('SMS support must be enabled first.')])

        return super(DefaultDcSettingsSerializer, self).validate(attrs)
Exemple #4
0
    def validate(self, attrs):
        db_only_manifest_keys = {'dc_bound', 'dc_bound_bool', 'owner'}

        if db_only_manifest_keys.issuperset(attrs.keys()):
            self.update_manifest = False

        try:
            alias = attrs['alias']
        except KeyError:
            alias = self.object.alias

        try:
            version = attrs['version']
        except KeyError:
            version = self.object.version

        qs = Image.objects

        if self.object.pk:
            qs = qs.exclude(pk=self.object.pk)

        if qs.filter(alias__iexact=alias, version=version).exists():
            self._errors['alias'] = s.ErrorList([_('This alias is already in use. '
                                                   'Please supply a different alias or version.')])

        if self.request.method == 'POST' and self._dc_bound:
            limit = self._dc_bound.settings.VMS_IMAGE_LIMIT

            if limit is not None:
                if Image.objects.filter(dc_bound=self._dc_bound).count() >= int(limit):
                    raise s.ValidationError(_('Maximum number of server disk images reached'))

        return super(ImageSerializer, self).validate(attrs)
Exemple #5
0
    def validate(self, attrs):
        # User is or will be bound to this DC
        dc = self._dc_bound

        if attrs.get('dc_bound_bool', self.object.dc_bound_bool) and attrs.get(
                'is_staff', self.object.is_staff):
            self._errors['dc_bound'] = _(
                'A SuperAdmin user cannot be DC-bound.')

        if dc:
            # User is or will be member of these groups
            try:
                groups = attrs['roles_api']
            except KeyError:
                if self.object.pk:
                    groups = self.object.roles.all()
                else:
                    groups = ()

            # A DC-bound user cannot be a member of a group that is assigned to another DC other than user.dc_bound
            if Dc.objects.filter(roles__in=groups).exclude(id=dc.id).exists():
                self._errors['dc_bound'] = s.ErrorList([
                    _("User's group(s) are attached into another datacenter(s)."
                      )
                ])

        return attrs
Exemple #6
0
    def validate(self, attrs):
        object_type = attrs.get('content_type', None)
        object_name = attrs.get('object_name', None)

        # object_name depends on object_type
        if object_name:
            if not object_type:
                self._errors['object_type'] = s.ErrorList([
                    _('object_type attribute is required when '
                      'filtering by object_name.')
                ])
                return attrs

            self._content_type = content_type = ContentType.objects.get(
                model=object_type)
            model_class = content_type.model_class()
            lookup_kwargs = model_class.get_log_name_lookup_kwargs(object_name)
            filter_kwargs = {
                key + '__icontains': val
                for key, val in lookup_kwargs.items()
            }
            self._object_pks = list(
                model_class.objects.filter(**filter_kwargs).values_list(
                    'pk', flat=True))

        return attrs
Exemple #7
0
    def validate(self, attrs):
        try:
            zpool = attrs['zpool']
        except KeyError:
            zpool = self.object.zpool

        try:
            node = attrs['node']
        except KeyError:
            node = self.object.node

        try:
            attrs['zpool'] = get_zpools(self.request).get(node=node,
                                                          zpool=zpool)
        except NodeStorage.DoesNotExist:
            self._errors['zpool'] = s.ErrorList(
                [_('Zpool does not exist on node.')])

        # Check total number of existing backup definitions - Issue #chili-447
        if self.request.method == 'POST':
            limit = self.request.dc.settings.VMS_VM_BACKUP_DEFINE_LIMIT

            if limit is not None:
                total = self._model_.objects.filter(vm=self.object.vm).count()
                if int(limit) <= total:
                    raise s.ValidationError(
                        _('Maximum number of backup definitions reached.'))

        return attrs
Exemple #8
0
    def validate(self, attrs):
        # Check if it is possible to override a boolean setting
        for source, value in attrs.items():
            if source in self._override_disabled_ and not getattr(settings, source, False) and value:
                self._errors[source] = s.ErrorList([_('Cannot override global setting.')])
                del attrs[source]

        return attrs
Exemple #9
0
    def validate(self, attrs):
        if attrs.get('EMAIL_USE_TLS', None) and attrs.get(
                'EMAIL_USE_SSL', None):
            self._errors['EMAIL_USE_TLS'] = self._errors[
                'EMAIL_USE_SSL'] = s.ErrorList([
                    _('Cannot enable EMAIL_USE_TLS and EMAIL_USE_SSL together.'
                      )
                ])

        return attrs
Exemple #10
0
def validate_dummy_serializer(serializer, value):
    ser = serializer(data=value)
    ser.is_valid()

    for i in ser.init_data:
        if i not in ser.fields:
            # noinspection PyProtectedMember
            ser._errors[i] = s.ErrorList([_('Invalid field.')])

    if ser.errors:
        raise s.NestedValidationError(ser.errors)
Exemple #11
0
    def validate(self, attrs):
        try:
            network = attrs['network']
        except KeyError:
            network = self.object.network

        try:
            netmask = attrs['netmask']
        except KeyError:
            netmask = self.object.netmask

        try:
            ip_network = Subnet.get_ip_network(network, netmask)
            if ip_network.is_reserved:
                raise ValueError
        except ValueError:
            self._errors['network'] = self._errors['netmask'] = \
                s.ErrorList([_('Enter a valid IPv4 network and netmask.')])

        if self.request.method == 'POST' and self._dc_bound:
            limit = self._dc_bound.settings.VMS_NET_LIMIT

            if limit is not None:
                if Subnet.objects.filter(
                        dc_bound=self._dc_bound).count() >= int(limit):
                    raise s.ValidationError(
                        _('Maximum number of networks reached'))

        if self._dc_bound:
            try:
                vlan_id = attrs['vlan_id']
            except KeyError:
                vlan_id = self.object.vlan_id

            dc_settings = self._dc_bound.settings

            if dc_settings.VMS_NET_VLAN_RESTRICT and vlan_id not in dc_settings.VMS_NET_VLAN_ALLOWED:
                self._errors['vlan_id'] = s.ErrorList(
                    [_('VLAN ID is not available in datacenter.')])

        return attrs
Exemple #12
0
    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
Exemple #13
0
    def validate(self, attrs):
        self.img_manifest_url = attrs['manifest_url']
        file_url = attrs.get('file_url', None)

        if not file_url:
            file_url = self.img_manifest_url.strip('/') + '/file'

        self.img_file_url = file_url

        try:
            req_file = HttpClient(file_url)
            req_file.get(timeout=5, max_size=32)
        except TooLarge:
            pass
        except RequestException as e:
            self._errors['file_url'] = s.ErrorList([_('Image file URL is unreachable (%s).') % e])

        return attrs
Exemple #14
0
    def validate(self, attrs):  # noqa: R701
        try:
            network = attrs['network']
        except KeyError:
            network = self.object.network

        try:
            netmask = attrs['netmask']
        except KeyError:
            netmask = self.object.netmask

        try:
            vxlan_id = attrs['vxlan_id']
        except KeyError:
            vxlan_id = self.object.vxlan_id

        try:
            mtu = attrs['mtu']
        except KeyError:
            mtu = self.object.mtu

        try:
            nic_tag = attrs['nic_tag']
        except KeyError:
            nic_tag = self.object.nic_tag

        try:
            ip_network = Subnet.get_ip_network(network, netmask)
            if ip_network.is_reserved:
                raise ValueError
        except ValueError:
            self._errors['network'] = self._errors['netmask'] = \
                s.ErrorList([_('Enter a valid IPv4 network and netmask.')])

        if self.request.method == 'POST' and self._dc_bound:
            limit = self._dc_bound.settings.VMS_NET_LIMIT

            if limit is not None:
                if Subnet.objects.filter(
                        dc_bound=self._dc_bound).count() >= int(limit):
                    raise s.ValidationError(
                        _('Maximum number of networks reached.'))

        nic_tag_type = Node.all_nictags()[nic_tag]
        # retrieve all available nictags and see what is the type of the current nic tag
        # if type is overlay then vxlan is mandatory argument
        if nic_tag_type == 'overlay rule':
            if not vxlan_id:
                self._errors['vxlan_id'] = s.ErrorList([
                    _('VXLAN ID is required when an '
                      'overlay NIC tag is selected.')
                ])
        else:
            attrs['vxlan_id'] = None

        # validate MTU for overlays and etherstubs, and physical nics
        if nic_tag_type == 'overlay rule':
            # if MTU was not set for the overlay
            if not mtu:
                attrs['mtu'] = 1400

            if mtu > 8900:
                self._errors['mtu'] = s.ErrorList([
                    s.IntegerField.default_error_messages['max_value'] % {
                        'limit_value': 8900
                    }
                ])

        if nic_tag_type in ('normal', 'aggr') and mtu and mtu < 1500:
            self._errors['mtu'] = s.ErrorList([
                s.IntegerField.default_error_messages['min_value'] % {
                    'limit_value': 1500
                }
            ])

        if self._dc_bound:
            try:
                vlan_id = attrs['vlan_id']
            except KeyError:
                vlan_id = self.object.vlan_id

            dc_settings = self._dc_bound.settings

            if dc_settings.VMS_NET_VLAN_RESTRICT and vlan_id not in dc_settings.VMS_NET_VLAN_ALLOWED:
                self._errors['vlan_id'] = s.ErrorList(
                    [_('VLAN ID is not available in datacenter.')])

            if dc_settings.VMS_NET_VXLAN_RESTRICT and vxlan_id not in dc_settings.VMS_NET_VXLAN_ALLOWED:
                self._errors['vxlan_id'] = s.ErrorList(
                    [_('VXLAN ID is not available in datacenter.')])

        return super(NetworkSerializer, self).validate(attrs)
Exemple #15
0
 def update_errors(self, fields, err_msg):
     errors = {}
     for i in fields:
         errors[i] = s.ErrorList([err_msg])
     return errors
Exemple #16
0
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)
Exemple #17
0
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)
Exemple #18
0
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
Exemple #19
0
    def validate(self, attrs):
        vm = self.vm
        node = attrs.get('node', vm.node)
        changing_node = attrs.get('node', vm.node) != vm.node

        if self._live:
            if not changing_node:
                self._errors['live'] = s.ErrorList(
                    [_('Live migration cannot be performed locally.')])
                return attrs

            if (node.platform_version_short <
                    MIN_PLATFORM_VERSION_LIVE_MIGRATION
                    or vm.node.platform_version_short <
                    MIN_PLATFORM_VERSION_LIVE_MIGRATION):
                self._errors['live'] = s.ErrorList([
                    _('Source and/or target node platform does not '
                      'support live migration.')
                ])
                return attrs

        # Ghost VM is a copy of a VM used to take up place in DB.
        # When node is changing we have to have all disks in a ghost VM.
        # When changing only disk pools, only the changed disks have to be in a ghost VM.
        ghost_vm = SlaveVm(_master_vm=vm)
        ghost_vm.reserve_resources = changing_node
        ghost_vm.set_migration_hostname()
        ghost_vm.node = node
        ghost_vm_define = SlaveVmDefine(ghost_vm)

        # Validate root_zpool (we can do this after we know the new node)
        root_zpool = attrs.get('root_zpool', None)
        # Every pool must be validated when changing node
        try:
            root_zpool = ghost_vm_define.save_root_zpool(
                root_zpool, save_same_zpool=changing_node)
        except APIValidationError as exc:
            self._errors['node'] = exc.api_errors
            return attrs

        # Validate disk_zpools (we can do this after we know the new node)
        if ghost_vm.vm.is_kvm():
            disk_zpools = attrs.get('disk_zpools', {})
            try:
                disk_zpools = ghost_vm_define.save_disk_zpools(
                    disk_zpools, save_same_zpool=changing_node)
            except APIValidationError as exc:
                self._errors['node'] = exc.api_errors
                return attrs
        else:
            disk_zpools = {}

        # Nothing changed, he?
        if not changing_node and not (root_zpool or disk_zpools):
            raise s.ValidationError(_('Nothing to do.'))

        # Validate dc_node resources
        try:
            ghost_vm_define.validate_node_resources(
                ignore_cpu_ram=not changing_node)
        except APIValidationError as exc:
            self._errors['node'] = exc.api_errors
            return attrs

        # Validate storage resources
        try:
            ghost_vm_define.validate_storage_resources()
        except APIValidationError as exc:
            self._errors['node'] = exc.api_errors
            return attrs

        # Validate images
        self.img_required = ghost_vm_define.check_required_images()

        # Save params
        # noinspection PyAttributeOutsideInit
        self._root_zpool = root_zpool
        # noinspection PyAttributeOutsideInit
        self._disk_zpools = disk_zpools
        # noinspection PyAttributeOutsideInit
        self.ghost_vm_define = ghost_vm_define
        # noinspection PyAttributeOutsideInit
        self.changing_node = changing_node

        return attrs