def setUp(self): from nodeconductor.structure.tests.factories import UserFactory self.user = UserFactory(is_staff=True) self.request = APIRequestFactory().get('/') self.request.user = self.user self.field = GenericRelatedField(related_models=get_loggable_models()) self.field.root._context = {'request': self.request}
class GenericRelatedFieldTest(APITransactionTestCase): def setUp(self): from nodeconductor.structure.tests.factories import UserFactory self.user = UserFactory(is_staff=True) self.request = APIRequestFactory().get('/') self.request.user = self.user self.field = GenericRelatedField(related_models=get_loggable_models()) self.field.root._context = {'request': self.request} def test_if_related_object_exists_it_is_deserialized(self): from nodeconductor.structure.tests.factories import CustomerFactory customer = CustomerFactory() valid_url = CustomerFactory.get_url(customer) self.assertEqual(self.field.to_internal_value(valid_url), customer) def test_if_related_object_does_not_exist_validation_error_is_raised(self): from nodeconductor.structure.tests.factories import CustomerFactory customer = CustomerFactory() valid_url = CustomerFactory.get_url(customer) customer.delete() self.assertRaises(serializers.ValidationError, self.field.to_internal_value, valid_url) def test_if_user_does_not_have_permissions_for_related_object_validation_error_is_raised(self): from nodeconductor.structure.tests.factories import CustomerFactory customer = CustomerFactory() valid_url = CustomerFactory.get_url(customer) self.user.is_staff = False self.user.save() self.assertRaises(serializers.ValidationError, self.field.to_internal_value, valid_url) def test_if_uuid_is_invalid_validation_error_is_raised(self): invalid_url = 'https://example.com/api/customers/invalid/' self.assertRaises(serializers.ValidationError, self.field.to_internal_value, invalid_url)
class PriceListItemSerializer(AugmentedSerializerMixin, serializers.HyperlinkedModelSerializer): service = GenericRelatedField( related_models=structure_models.Service.get_all_models()) default_price_list_item = serializers.HyperlinkedRelatedField( view_name='defaultpricelistitem-detail', lookup_field='uuid', queryset=models.DefaultPriceListItem.objects.all().select_related( 'resource_content_type')) class Meta: model = models.PriceListItem fields = ('url', 'uuid', 'units', 'value', 'service', 'default_price_list_item') extra_kwargs = { 'url': { 'lookup_field': 'uuid' }, 'default_price_list_item': { 'lookup_field': 'uuid' } } protected_fields = ('service', 'default_price_list_item') def create(self, validated_data): try: return super(PriceListItemSerializer, self).create(validated_data) except IntegrityError: raise serializers.ValidationError( _('Price list item for service already exists.'))
class AlertSerializer(serializers.HyperlinkedModelSerializer): scope = GenericRelatedField(related_models=utils.get_loggable_models()) severity = MappedChoiceField( choices=[(v, k) for k, v in models.Alert.SeverityChoices.CHOICES], choice_mappings={v: k for k, v in models.Alert.SeverityChoices.CHOICES}, ) context = JsonField(read_only=True) class Meta(object): model = models.Alert fields = ( 'url', 'uuid', 'alert_type', 'message', 'severity', 'scope', 'created', 'closed', 'context', 'acknowledged', ) read_only_fields = ('uuid', 'created', 'closed') extra_kwargs = { 'url': {'lookup_field': 'uuid'}, } def create(self, validated_data): alert, _ = log.AlertLogger().process( severity=validated_data['severity'], message_template=validated_data['message'], scope=validated_data['scope'], alert_type=validated_data['alert_type'], ) return alert
class QuotaSerializer(serializers.HyperlinkedModelSerializer): scope = GenericRelatedField(related_models=utils.get_models_with_quotas(), read_only=True) class Meta(object): model = models.Quota fields = ('url', 'uuid', 'name', 'limit', 'usage', 'scope') read_only_fields = ('uuid', 'name', 'usage') extra_kwargs = { 'url': {'lookup_field': 'uuid'}, }
class BackupSerializer(serializers.HyperlinkedModelSerializer): backup_source = GenericRelatedField(related_models=utils.get_backupable_models()) state = serializers.ReadOnlyField(source='get_state_display') backup_source_name = serializers.ReadOnlyField(source='backup_source.name') metadata = JsonField(read_only=True) class Meta(object): model = models.Backup fields = ('url', 'uuid', 'description', 'created_at', 'kept_until', 'backup_source', 'state', 'backup_schedule', 'metadata', 'backup_source_name') read_only_fields = ('created_at', 'kept_until', 'backup_schedule') extra_kwargs = { 'url': {'lookup_field': 'uuid'}, 'backup_schedule': {'lookup_field': 'uuid'}, }
class BackupScheduleSerializer(serializers.HyperlinkedModelSerializer): backup_source = GenericRelatedField(related_models=utils.get_backupable_models()) backup_source_name = serializers.ReadOnlyField(source='backup_source.name') timezone = serializers.ChoiceField(choices=[(t, t) for t in pytz.all_timezones], default=django_timezone.get_current_timezone_name) class Meta(object): model = models.BackupSchedule fields = ('url', 'uuid', 'description', 'backups', 'retention_time', 'timezone', 'backup_source', 'maximal_number_of_backups', 'schedule', 'is_active', 'backup_source_name') read_only_fields = ('is_active', 'backups') extra_kwargs = { 'url': {'lookup_field': 'uuid'}, 'backups': {'lookup_field': 'uuid'}, }
class AlertSerializer(serializers.HyperlinkedModelSerializer): scope = GenericRelatedField(related_models=utils.get_loggable_models()) severity = MappedChoiceField( choices=[(v, k) for k, v in models.Alert.SeverityChoices.CHOICES], choice_mappings={ v: k for k, v in models.Alert.SeverityChoices.CHOICES }, ) context = JsonField(read_only=True) class Meta(object): model = models.Alert fields = ( 'url', 'uuid', 'alert_type', 'message', 'severity', 'scope', 'created', 'closed', 'context', 'acknowledged', ) read_only_fields = ('uuid', 'created', 'closed') extra_kwargs = { 'url': { 'lookup_field': 'uuid' }, } def create(self, validated_data): try: alert, created = loggers.AlertLogger().process( severity=validated_data['severity'], message_template=validated_data['message'], scope=validated_data['scope'], alert_type=validated_data['alert_type'], ) except IntegrityError: # In case of simultaneous requests serializer validation can pass for both alerts, # so we need to handle DB IntegrityError separately. raise serializers.ValidationError( _('Alert with given type and scope already exists.')) else: return alert
class PriceEstimateSerializer(AugmentedSerializerMixin, serializers.HyperlinkedModelSerializer): scope = GenericRelatedField( related_models=models.PriceEstimate.get_editable_estimated_models()) scope_name = serializers.SerializerMethodField() scope_type = serializers.SerializerMethodField() class Meta(object): model = models.PriceEstimate fields = ('url', 'uuid', 'scope', 'total', 'details', 'month', 'year', 'is_manually_input', 'scope_name', 'scope_type') read_only_fields = ('is_manually_input', ) extra_kwargs = { 'url': { 'lookup_field': 'uuid' }, } protected_fields = ('scope', 'year', 'month') def validate(self, data): if self.instance is None and models.PriceEstimate.objects.filter( scope=data['scope'], year=data['year'], month=data['month'], is_manually_input=True).exists(): raise serializers.ValidationError( 'Estimate for given month already exists. Use PATCH request to update it.' ) return data def create(self, validated_data): validated_data['is_manually_input'] = True price_estimate = super(PriceEstimateSerializer, self).create(validated_data) return price_estimate def get_scope_name(self, obj): return str(obj.scope) def get_scope_type(self, obj): models = (structure_models.Resource, structure_models.Service, structure_models.ServiceProjectLink, structure_models.Project, structure_models.Customer) for model in models: if isinstance(obj.scope, model): return model._meta.model_name
class PriceListItemSerializer(serializers.HyperlinkedModelSerializer): service = GenericRelatedField( related_models=structure_models.Service.get_all_models()) class Meta: model = models.PriceListItem fields = ('url', 'uuid', 'key', 'item_type', 'value', 'units', 'service') extra_kwargs = { 'url': { 'lookup_field': 'uuid' }, } def create(self, validated_data): # XXX: This behavior is wrong for services with several resources, find a better approach resource_class = SupportedServices.get_related_models( validated_data['service'])['resources'][0] validated_data[ 'resource_content_type'] = ContentType.objects.get_for_model( resource_class) return super(PriceListItemSerializer, self).create(validated_data)
class PriceEstimateSerializer(AugmentedSerializerMixin, serializers.HyperlinkedModelSerializer): scope = GenericRelatedField( related_models=models.PriceEstimate.get_estimated_models()) scope_name = serializers.SerializerMethodField() scope_type = serializers.SerializerMethodField() resource_type = serializers.SerializerMethodField() consumption_details = serializers.SerializerMethodField( help_text=_('How much of each consumables were used by resource.')) def __init__(self, *args, **kwargs): depth = kwargs.get('context', {}).pop('depth', 0) # allow to modify depth dynamically self.Meta.depth = depth super(PriceEstimateSerializer, self).__init__(*args, **kwargs) class Meta(object): model = models.PriceEstimate fields = ('url', 'uuid', 'scope', 'total', 'consumed', 'month', 'year', 'scope_name', 'scope_type', 'resource_type', 'consumption_details', 'children') extra_kwargs = { 'url': { 'lookup_field': 'uuid' }, } def get_fields(self): display_children = self.Meta.depth > 0 fields = super(PriceEstimateSerializer, self).get_fields() if not display_children: del fields['children'] return fields def build_nested_field(self, field_name, relation_info, nested_depth): """ Use PriceEstimateSerializer to serialize estimate children """ if field_name != 'children': return super(PriceEstimateSerializer, self).build_nested_field(field_name, relation_info, nested_depth) field_class = self.__class__ field_kwargs = { 'read_only': True, 'many': True, 'context': { 'depth': nested_depth - 1 } } return field_class, field_kwargs def get_scope_name(self, obj): if obj.scope: return getattr(obj.scope, 'name', six.text_type(obj.scope)) if obj.details: return obj.details.get('scope_name') def get_scope_type(self, obj): return ScopeTypeFilterBackend.get_scope_type( obj.content_type.model_class()) def get_resource_type(self, obj): if obj.is_resource_estimate(): return SupportedServices.get_name_for_model( obj.content_type.model_class()) def get_consumption_details(self, obj): try: consumption_details = obj.consumption_details except models.ConsumptionDetails.DoesNotExist: return consumed_in_month = consumption_details.consumed_in_month consumable_items = consumed_in_month.keys() pretty_names = models.DefaultPriceListItem.get_consumable_items_pretty_names( obj.content_type, consumable_items) return { pretty_names[item]: consumed_in_month[item] for item in consumable_items }
class HostSerializer(structure_serializers.BaseResourceSerializer): service = serializers.HyperlinkedRelatedField( source='service_project_link.service', view_name='zabbix-detail', read_only=True, lookup_field='uuid') service_project_link = serializers.HyperlinkedRelatedField( view_name='zabbix-spl-detail', queryset=models.ZabbixServiceProjectLink.objects.all(), write_only=True) # visible name could be populated from scope, so we need to mark it as not required visible_name = serializers.CharField(required=False) scope = GenericRelatedField( related_models=structure_models.Resource.get_all_models(), required=False) templates = NestedTemplateSerializer( queryset=models.Template.objects.all().select_related('items'), many=True, required=False) agreed_sla = serializers.FloatField() actual_sla = serializers.SerializerMethodField() class Meta(structure_serializers.BaseResourceSerializer.Meta): model = models.Host view_name = 'zabbix-host-detail' fields = structure_serializers.BaseResourceSerializer.Meta.fields + ( 'visible_name', 'interface_parameters', 'host_group_name', 'scope', 'templates', 'agreed_sla', 'actual_sla') def get_resource_fields(self): return super(HostSerializer, self).get_resource_fields() + ['scope'] def validate(self, attrs): # initiate name and visible name from scope if it is defined and check that they are not empty if 'scope' in attrs: attrs['visible_name'] = models.Host.get_visible_name_from_scope( attrs['scope']) if not attrs.get('visible_name') and self.instance is None: raise serializers.ValidationError( 'Visible name or scope should be defined.') # forbid templates update if self.instance is not None and 'templates' in attrs: raise serializers.ValidationError( 'Its impossible to update host templates') # model validation if self.instance is not None: for name, value in attrs.items(): setattr(self.instance, name, value) self.instance.clean() else: instance = models.Host( **{k: v for k, v in attrs.items() if k != 'templates'}) instance.clean() return attrs def create(self, validated_data): templates = validated_data.pop('templates', None) with transaction.atomic(): host = super(HostSerializer, self).create(validated_data) # get default templates from service settings if they are not defined if templates is None: templates_names = host.service_project_link.service.settings.options.get( 'templates_names', backend.ZabbixRealBackend.DEFAULT_TEMPLATES_NAMES) templates = models.Template.objects.filter( settings=host.service_project_link.service.settings, name__in=templates_names) for template in templates: host.templates.add(template) return host def get_actual_sla(self, host): if 'sla_map' not in self.context: period = self.context.get('period') if period is None: raise AttributeError( 'HostSerializer has to be initialized with `period` in context' ) qs = models.SlaHistory.objects.filter(period=period) if isinstance(self.instance, list): qs = qs.filter(host__in=self.instance) else: qs = qs.filter(host=self.instance) self.context['sla_map'] = {q.host_id: q.value for q in qs} return self.context['sla_map'].get(host.id)
class EventSerializer(serializers.Serializer): level = serializers.ChoiceField( choices=['debug', 'info', 'warning', 'error']) message = serializers.CharField() scope = GenericRelatedField(related_models=utils.get_loggable_models(), required=False)