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)]
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
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()
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
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
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
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
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