Ejemplo n.º 1
0
    def _get_vm_nics(self, vm):
        """
        Serialize map of Ethernet network adapters for virtual machine template deployment.

        :param vm: Virtual machine to be created.
        :type vm: :class:`waldur_vmware.models.VirtualMachine`
        :return: list[dict]
        """

        nics = self._get_template_nics(vm.template)
        networks = list(vm.networks.all())

        if is_basic_mode():
            if len(networks) != 1:
                logger.warning(
                    'Skipping network assignment because VM does not have '
                    'exactly one network in basic mode. VM ID: %s', vm.id)
                return
            elif len(nics) != 1:
                logger.warning(
                    'Skipping network assignment because related template does '
                    'not have exactly one NIC in basic mode. VM ID: %s', vm.id)

        if len(networks) != len(nics):
            logger.warning(
                'It is not safe to update network assignment when '
                'number of interfaces and networks do not match. VM ID: %s',
                vm.id)

        return [{
            'key': nic,
            'value': {
                'network': network.backend_id
            }
        } for (nic, network) in zip(nics, networks)]
Ejemplo n.º 2
0
    def clean(self):
        """
        When basic mode is activated we should require one service property
        (network, cluster and folder) defined per customer
        per shared service setting.
        """
        super(CustomerInlineFormset, self).clean()
        if is_basic_mode():
            enabled_settings = {}
            for form in self.forms:
                cleaned_data = getattr(form, 'cleaned_data', None)

                # Skip empty form
                if not cleaned_data:
                    continue

                # Skip deleted form
                if cleaned_data.get('DELETE'):
                    continue

                # Ensure that the same service settings are not used multiple times
                service_settings = cleaned_data[
                    self.service_property_field].settings
                if service_settings in enabled_settings:
                    raise ValidationError(
                        _('There should be exactly one property '
                          'assigned to the each service settings.'))
                else:
                    enabled_settings[service_settings] = True
Ejemplo n.º 3
0
    def pull_templates(self):
        """
        Pull VMware templates for virtual machine provisioning from content library
        using VMware REST API to the local database.
        """
        try:
            backend_templates = self.client.list_all_templates()
        except VMwareError as e:
            raise VMwareBackendError(e)

        if is_basic_mode():
            # If basic mode is enabled, we should filter out templates which have more than 1 NIC
            backend_templates = [
                template for template in backend_templates
                if len(template['template']['nics']) == 1
            ]

        backend_templates_map = {
            item['library_item']['id']: item
            for item in backend_templates
        }

        frontend_templates_map = {
            p.backend_id: p
            for p in models.Template.objects.filter(settings=self.settings)
        }

        stale_ids = set(frontend_templates_map.keys()) - set(
            backend_templates_map.keys())
        new_ids = set(backend_templates_map.keys()) - set(
            frontend_templates_map.keys())
        common_ids = set(backend_templates_map.keys()) & set(
            frontend_templates_map.keys())

        for library_item_id in new_ids:
            template = self._backend_template_to_template(
                backend_templates_map[library_item_id])
            template.save()

        for library_item_id in common_ids:
            backend_template = self._backend_template_to_template(
                backend_templates_map[library_item_id])
            frontend_template = frontend_templates_map[library_item_id]
            fields = (
                'cores',
                'cores_per_socket',
                'ram',
                'disk',
                'guest_os',
                'modified',
                'description',
            )
            update_pulled_fields(frontend_template, backend_template, fields)

        models.Template.objects.filter(settings=self.settings,
                                       backend_id__in=stale_ids).delete()
Ejemplo n.º 4
0
    def _validate_datastore(self, attrs):
        """
        If basic mode is activated, match datastore by customer and service.
        Otherwise use datastore provided by user if it is valid with respect to its size.
        """
        service_settings = attrs['service_settings']
        project = attrs['project']
        template = attrs.get('template')

        if is_basic_mode():
            customer = project.customer
            datastore = (models.Datastore.objects.filter(
                settings=service_settings,
                customerdatastore__customer=customer).order_by(
                    '-free_space').first())
            if not datastore:
                raise serializers.ValidationError(
                    'There is no datastore assigned to the current customer.')
            elif template and template.disk > datastore.free_space:
                raise serializers.ValidationError(
                    'There is no datastore with enough free space available for current customer.'
                )
            else:
                attrs['datastore'] = datastore
            return attrs

        datastore = attrs.get('datastore')

        if datastore:
            if datastore.settings != service_settings:
                raise serializers.ValidationError(
                    'This datastore is not available for this service.')

            if not datastore.customerdatastore_set.filter(
                    customer=project.customer).exists():
                raise serializers.ValidationError(
                    'This datastore is not available for this customer.')

            if template and template.disk > datastore.free_space:
                raise serializers.ValidationError(
                    'There is no datastore with enough free space available for current customer.'
                )
        return attrs
Ejemplo n.º 5
0
    def _validate_cluster(self, attrs):
        """
        If basic mode is activated, match cluster by customer and service.
        Otherwise use cluster provided by user if it is allowed.
        Finally, use default cluster from service settings.
        """
        service_settings = attrs['service_settings']
        project = attrs['project']

        if is_basic_mode():
            customer = project.customer
            try:
                cluster = models.Cluster.objects.filter(
                    settings=service_settings,
                    customercluster__customer=customer).get()
            except ObjectDoesNotExist:
                return self._fallback_to_default_cluster(attrs)
            except MultipleObjectsReturned:
                raise serializers.ValidationError(
                    'There are multiple clusters assigned to the current customer.'
                )
            else:
                attrs['cluster'] = cluster
            return attrs

        cluster = attrs.get('cluster')

        if cluster:
            if cluster.settings != service_settings:
                raise serializers.ValidationError(
                    'This cluster is not available for this service.')

            if not cluster.customercluster_set.filter(
                    customer=project.customer).exists():
                raise serializers.ValidationError(
                    'This cluster is not available for this customer.')
        else:
            return self._fallback_to_default_cluster(attrs)
        return attrs
Ejemplo n.º 6
0
    def _validate_networks(self, attrs):
        """
        If basic mode is activated, match network by customer and service.
        Otherwise use networks provided by user if it is valid with respect to its size.
        """
        service_settings = attrs['service_settings']
        project = attrs['project']

        if is_basic_mode():
            customer = project.customer
            try:
                network = models.Network.objects.filter(
                    settings=service_settings,
                    customernetwork__customer=customer).get()
            except ObjectDoesNotExist:
                raise serializers.ValidationError(
                    'There is no network assigned to the current customer.')
            except MultipleObjectsReturned:
                raise serializers.ValidationError(
                    'There are multiple networks assigned to the current customer.'
                )
            else:
                attrs['networks'] = [network]
            return attrs

        networks = attrs.get('networks', [])

        for network in networks:
            if network.settings != service_settings:
                raise serializers.ValidationError(
                    'This network is not available for this service.')

            if not network.customernetwork_set.filter(
                    customer=project.customer).exists():
                raise serializers.ValidationError(
                    'This network is not available for this customer.')

        return attrs
Ejemplo n.º 7
0
    def _validate_folder(self, attrs):
        """
        If basic mode is activated, match folder by customer and service.
        Otherwise use folder provided by user if it is allowed.
        """
        service_settings = attrs['service_settings']
        project = attrs['project']

        if is_basic_mode():
            customer = project.customer
            try:
                folder = models.Folder.objects.filter(
                    settings=service_settings,
                    customerfolder__customer=customer).get()
            except ObjectDoesNotExist:
                raise serializers.ValidationError(
                    'There is no folder assigned to the current customer.')
            except MultipleObjectsReturned:
                raise serializers.ValidationError(
                    'There are multiple folders assigned to the current customer.'
                )
            else:
                attrs['folder'] = folder
            return attrs

        folder = attrs.get('folder')

        if folder:
            if folder.settings != service_settings:
                raise serializers.ValidationError(
                    'This folder is not available for this service.')

            if not folder.customerfolder_set.filter(
                    customer=project.customer).exists():
                raise serializers.ValidationError(
                    'This folder is not available for this customer.')
        return attrs
Ejemplo n.º 8
0
    def get_fields(self):
        """
        When basic mode is activated, user is not allowed
        to select placement attributes for the new virtual machine.
        """
        fields = super(VirtualMachineSerializer, self).get_fields()

        if 'ram' in fields:
            fields['ram'].factor = 1024
            fields['ram'].units = 'GB'
            fields['ram'].min_value = 1024

        if 'disk' in fields:
            fields['disk'].factor = 1024
            fields['disk'].units = 'GB'

        if 'cores' in fields:
            fields['cores'].min_value = 1

        if 'cores_per_socket' in fields:
            fields['cores_per_socket'].min_value = 1

        if isinstance(self.instance, models.VirtualMachine):
            options = self.instance.service_settings.options

            if 'cores' in fields:
                fields['cores'].max_value = options.get('max_cpu')

            if 'ram' in fields and 'max_ram' in options:
                fields['ram'].max_value = options.get('max_ram')

            if 'cores_per_socket' in fields and 'max_cores_per_socket' in options:
                fields['cores_per_socket'].max_value = options.get(
                    'max_cores_per_socket')

            if 'disk' in fields and 'max_disk' in options:
                fields['disk'].max_value = get_int_or_none(options, 'max_disk')

            if 'disk' in fields and 'max_disk_total' in options:
                if fields['disk'].max_value:
                    fields['disk'].max_value = min(
                        get_int_or_none(options, 'max_disk_total'),
                        get_int_or_none(options, 'max_disk'),
                    )
                else:
                    fields['disk'].max_value = get_int_or_none(
                        options, 'max_disk_total')

        if not is_basic_mode():
            return fields

        try:
            method = self.context['view'].request.method
        except (KeyError, AttributeError):
            return fields

        if method == 'POST':
            read_only_fields = 'cluster', 'networks', 'datastore', 'folder'
            for field in read_only_fields:
                fields[field].read_only = True

        return fields