serializers.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.core.exceptions import ObjectDoesNotExist
  3. from drf_yasg.utils import swagger_serializer_method
  4. from rest_framework import serializers
  5. from dcim.api.nested_serializers import (
  6. NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedRackSerializer,
  7. NestedRegionSerializer, NestedSiteSerializer,
  8. )
  9. from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
  10. from extras.choices import *
  11. from extras.constants import *
  12. from extras.models import (
  13. ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, JobResult, Tag,
  14. )
  15. from extras.utils import FeatureQuery
  16. from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
  17. from tenancy.models import Tenant, TenantGroup
  18. from users.api.nested_serializers import NestedUserSerializer
  19. from utilities.api import (
  20. ChoiceField, ContentTypeField, get_serializer_for_model, SerializerNotFound, SerializedPKRelatedField,
  21. ValidatedModelSerializer,
  22. )
  23. from virtualization.api.nested_serializers import NestedClusterGroupSerializer, NestedClusterSerializer
  24. from virtualization.models import Cluster, ClusterGroup
  25. from .nested_serializers import *
  26. #
  27. # Graphs
  28. #
  29. class GraphSerializer(ValidatedModelSerializer):
  30. type = ContentTypeField(
  31. queryset=ContentType.objects.filter(FeatureQuery('graphs').get_query()),
  32. )
  33. class Meta:
  34. model = Graph
  35. fields = ['id', 'type', 'weight', 'name', 'template_language', 'source', 'link']
  36. class RenderedGraphSerializer(serializers.ModelSerializer):
  37. embed_url = serializers.SerializerMethodField(
  38. read_only=True
  39. )
  40. embed_link = serializers.SerializerMethodField(
  41. read_only=True
  42. )
  43. type = ContentTypeField(
  44. read_only=True
  45. )
  46. class Meta:
  47. model = Graph
  48. fields = ['id', 'type', 'weight', 'name', 'embed_url', 'embed_link']
  49. def get_embed_url(self, obj):
  50. return obj.embed_url(self.context['graphed_object'])
  51. def get_embed_link(self, obj):
  52. return obj.embed_link(self.context['graphed_object'])
  53. #
  54. # Export templates
  55. #
  56. class ExportTemplateSerializer(ValidatedModelSerializer):
  57. content_type = ContentTypeField(
  58. queryset=ContentType.objects.filter(FeatureQuery('export_templates').get_query()),
  59. )
  60. template_language = ChoiceField(
  61. choices=TemplateLanguageChoices,
  62. default=TemplateLanguageChoices.LANGUAGE_JINJA2
  63. )
  64. class Meta:
  65. model = ExportTemplate
  66. fields = [
  67. 'id', 'content_type', 'name', 'description', 'template_language', 'template_code', 'mime_type',
  68. 'file_extension',
  69. ]
  70. #
  71. # Tags
  72. #
  73. class TagSerializer(ValidatedModelSerializer):
  74. tagged_items = serializers.IntegerField(read_only=True)
  75. class Meta:
  76. model = Tag
  77. fields = ['id', 'name', 'slug', 'color', 'description', 'tagged_items']
  78. class TaggedObjectSerializer(serializers.Serializer):
  79. tags = NestedTagSerializer(many=True, required=False)
  80. def create(self, validated_data):
  81. tags = validated_data.pop('tags', [])
  82. instance = super().create(validated_data)
  83. return self._save_tags(instance, tags)
  84. def update(self, instance, validated_data):
  85. tags = validated_data.pop('tags', [])
  86. instance = super().update(instance, validated_data)
  87. return self._save_tags(instance, tags)
  88. def _save_tags(self, instance, tags):
  89. if tags:
  90. instance.tags.set(*[t.name for t in tags])
  91. return instance
  92. #
  93. # Image attachments
  94. #
  95. class ImageAttachmentSerializer(ValidatedModelSerializer):
  96. content_type = ContentTypeField(
  97. queryset=ContentType.objects.all()
  98. )
  99. parent = serializers.SerializerMethodField(read_only=True)
  100. class Meta:
  101. model = ImageAttachment
  102. fields = [
  103. 'id', 'content_type', 'object_id', 'parent', 'name', 'image', 'image_height', 'image_width', 'created',
  104. ]
  105. def validate(self, data):
  106. # Validate that the parent object exists
  107. try:
  108. data['content_type'].get_object_for_this_type(id=data['object_id'])
  109. except ObjectDoesNotExist:
  110. raise serializers.ValidationError(
  111. "Invalid parent object: {} ID {}".format(data['content_type'], data['object_id'])
  112. )
  113. # Enforce model validation
  114. super().validate(data)
  115. return data
  116. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  117. def get_parent(self, obj):
  118. # Static mapping of models to their nested serializers
  119. if isinstance(obj.parent, Device):
  120. serializer = NestedDeviceSerializer
  121. elif isinstance(obj.parent, Rack):
  122. serializer = NestedRackSerializer
  123. elif isinstance(obj.parent, Site):
  124. serializer = NestedSiteSerializer
  125. else:
  126. raise Exception("Unexpected type of parent object for ImageAttachment")
  127. return serializer(obj.parent, context={'request': self.context['request']}).data
  128. #
  129. # Config contexts
  130. #
  131. class ConfigContextSerializer(ValidatedModelSerializer):
  132. regions = SerializedPKRelatedField(
  133. queryset=Region.objects.all(),
  134. serializer=NestedRegionSerializer,
  135. required=False,
  136. many=True
  137. )
  138. sites = SerializedPKRelatedField(
  139. queryset=Site.objects.all(),
  140. serializer=NestedSiteSerializer,
  141. required=False,
  142. many=True
  143. )
  144. roles = SerializedPKRelatedField(
  145. queryset=DeviceRole.objects.all(),
  146. serializer=NestedDeviceRoleSerializer,
  147. required=False,
  148. many=True
  149. )
  150. platforms = SerializedPKRelatedField(
  151. queryset=Platform.objects.all(),
  152. serializer=NestedPlatformSerializer,
  153. required=False,
  154. many=True
  155. )
  156. cluster_groups = SerializedPKRelatedField(
  157. queryset=ClusterGroup.objects.all(),
  158. serializer=NestedClusterGroupSerializer,
  159. required=False,
  160. many=True
  161. )
  162. clusters = SerializedPKRelatedField(
  163. queryset=Cluster.objects.all(),
  164. serializer=NestedClusterSerializer,
  165. required=False,
  166. many=True
  167. )
  168. tenant_groups = SerializedPKRelatedField(
  169. queryset=TenantGroup.objects.all(),
  170. serializer=NestedTenantGroupSerializer,
  171. required=False,
  172. many=True
  173. )
  174. tenants = SerializedPKRelatedField(
  175. queryset=Tenant.objects.all(),
  176. serializer=NestedTenantSerializer,
  177. required=False,
  178. many=True
  179. )
  180. tags = serializers.SlugRelatedField(
  181. queryset=Tag.objects.all(),
  182. slug_field='slug',
  183. required=False,
  184. many=True
  185. )
  186. class Meta:
  187. model = ConfigContext
  188. fields = [
  189. 'id', 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms',
  190. 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
  191. ]
  192. #
  193. # Job Results
  194. #
  195. class JobResultSerializer(serializers.ModelSerializer):
  196. url = serializers.HyperlinkedIdentityField(view_name='extras-api:jobresult-detail')
  197. user = NestedUserSerializer(
  198. read_only=True
  199. )
  200. status = ChoiceField(choices=JobResultStatusChoices, read_only=True)
  201. obj_type = ContentTypeField(
  202. read_only=True
  203. )
  204. class Meta:
  205. model = JobResult
  206. fields = [
  207. 'id', 'url', 'created', 'completed', 'name', 'obj_type', 'status', 'user', 'data', 'job_id',
  208. ]
  209. #
  210. # Reports
  211. #
  212. class ReportSerializer(serializers.Serializer):
  213. id = serializers.CharField(read_only=True, source="full_name")
  214. module = serializers.CharField(max_length=255)
  215. name = serializers.CharField(max_length=255)
  216. description = serializers.CharField(max_length=255, required=False)
  217. test_methods = serializers.ListField(child=serializers.CharField(max_length=255))
  218. result = NestedJobResultSerializer()
  219. class ReportDetailSerializer(ReportSerializer):
  220. result = JobResultSerializer()
  221. #
  222. # Scripts
  223. #
  224. class ScriptSerializer(serializers.Serializer):
  225. id = serializers.CharField(read_only=True, source="full_name")
  226. module = serializers.CharField(max_length=255)
  227. name = serializers.CharField(read_only=True)
  228. description = serializers.CharField(read_only=True)
  229. vars = serializers.SerializerMethodField(read_only=True)
  230. result = NestedJobResultSerializer()
  231. def get_vars(self, instance):
  232. return {
  233. k: v.__class__.__name__ for k, v in instance._get_vars().items()
  234. }
  235. class ScriptDetailSerializer(ScriptSerializer):
  236. result = JobResultSerializer()
  237. class ScriptInputSerializer(serializers.Serializer):
  238. data = serializers.JSONField()
  239. commit = serializers.BooleanField()
  240. class ScriptLogMessageSerializer(serializers.Serializer):
  241. status = serializers.SerializerMethodField(read_only=True)
  242. message = serializers.SerializerMethodField(read_only=True)
  243. def get_status(self, instance):
  244. return instance[0]
  245. def get_message(self, instance):
  246. return instance[1]
  247. class ScriptOutputSerializer(serializers.Serializer):
  248. log = ScriptLogMessageSerializer(many=True, read_only=True)
  249. output = serializers.CharField(read_only=True)
  250. #
  251. # Change logging
  252. #
  253. class ObjectChangeSerializer(serializers.ModelSerializer):
  254. user = NestedUserSerializer(
  255. read_only=True
  256. )
  257. action = ChoiceField(
  258. choices=ObjectChangeActionChoices,
  259. read_only=True
  260. )
  261. changed_object_type = ContentTypeField(
  262. read_only=True
  263. )
  264. changed_object = serializers.SerializerMethodField(
  265. read_only=True
  266. )
  267. class Meta:
  268. model = ObjectChange
  269. fields = [
  270. 'id', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
  271. 'changed_object', 'object_data',
  272. ]
  273. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  274. def get_changed_object(self, obj):
  275. """
  276. Serialize a nested representation of the changed object.
  277. """
  278. if obj.changed_object is None:
  279. return None
  280. try:
  281. serializer = get_serializer_for_model(obj.changed_object, prefix='Nested')
  282. except SerializerNotFound:
  283. return obj.object_repr
  284. context = {
  285. 'request': self.context['request']
  286. }
  287. data = serializer(obj.changed_object, context=context).data
  288. return data