models.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. from django.contrib.auth.models import User
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.db import models
  4. from django.http import HttpResponse
  5. from django.template import Template, Context
  6. from django.utils.safestring import mark_safe
  7. from dcim.models import Site
  8. GRAPH_TYPE_INTERFACE = 100
  9. GRAPH_TYPE_PROVIDER = 200
  10. GRAPH_TYPE_SITE = 300
  11. GRAPH_TYPE_CHOICES = (
  12. (GRAPH_TYPE_INTERFACE, 'Interface'),
  13. (GRAPH_TYPE_PROVIDER, 'Provider'),
  14. (GRAPH_TYPE_SITE, 'Site'),
  15. )
  16. EXPORTTEMPLATE_MODELS = [
  17. 'site', 'rack', 'device', 'consoleport', 'powerport', 'interfaceconnection', # DCIM
  18. 'aggregate', 'prefix', 'ipaddress', 'vlan', # IPAM
  19. 'provider', 'circuit', # Circuits
  20. 'tenant', # Tenants
  21. ]
  22. ACTION_CREATE = 1
  23. ACTION_IMPORT = 2
  24. ACTION_EDIT = 3
  25. ACTION_BULK_EDIT = 4
  26. ACTION_DELETE = 5
  27. ACTION_BULK_DELETE = 6
  28. ACTION_CHOICES = (
  29. (ACTION_CREATE, 'created'),
  30. (ACTION_IMPORT, 'imported'),
  31. (ACTION_EDIT, 'modified'),
  32. (ACTION_BULK_EDIT, 'bulk edited'),
  33. (ACTION_DELETE, 'deleted'),
  34. (ACTION_BULK_DELETE, 'bulk deleted')
  35. )
  36. class Graph(models.Model):
  37. type = models.PositiveSmallIntegerField(choices=GRAPH_TYPE_CHOICES)
  38. weight = models.PositiveSmallIntegerField(default=1000)
  39. name = models.CharField(max_length=100, verbose_name='Name')
  40. source = models.CharField(max_length=500, verbose_name='Source URL')
  41. link = models.URLField(verbose_name='Link URL', blank=True)
  42. class Meta:
  43. ordering = ['type', 'weight', 'name']
  44. def __unicode__(self):
  45. return self.name
  46. def embed_url(self, obj):
  47. template = Template(self.source)
  48. return template.render(Context({'obj': obj}))
  49. def embed_link(self, obj):
  50. if self.link is None:
  51. return ''
  52. template = Template(self.link)
  53. return template.render(Context({'obj': obj}))
  54. class ExportTemplate(models.Model):
  55. content_type = models.ForeignKey(ContentType, limit_choices_to={'model__in': EXPORTTEMPLATE_MODELS})
  56. name = models.CharField(max_length=200)
  57. template_code = models.TextField()
  58. mime_type = models.CharField(max_length=15, blank=True)
  59. file_extension = models.CharField(max_length=15, blank=True)
  60. class Meta:
  61. ordering = ['content_type', 'name']
  62. unique_together = [
  63. ['content_type', 'name']
  64. ]
  65. def __unicode__(self):
  66. return u'{}: {}'.format(self.content_type, self.name)
  67. def to_response(self, context_dict, filename):
  68. """
  69. Render the template to an HTTP response, delivered as a named file attachment
  70. """
  71. template = Template(self.template_code)
  72. mime_type = 'text/plain' if not self.mime_type else self.mime_type
  73. response = HttpResponse(
  74. template.render(Context(context_dict)),
  75. content_type=mime_type
  76. )
  77. if self.file_extension:
  78. filename += '.{}'.format(self.file_extension)
  79. response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
  80. return response
  81. class TopologyMap(models.Model):
  82. name = models.CharField(max_length=50, unique=True)
  83. slug = models.SlugField(unique=True)
  84. site = models.ForeignKey(Site, related_name='topology_maps', blank=True, null=True)
  85. device_patterns = models.TextField(help_text="Identify devices to include in the diagram using regular expressions,"
  86. "one per line. Each line will result in a new tier of the drawing. "
  87. "Separate multiple regexes on a line using commas. Devices will be "
  88. "rendered in the order they are defined.")
  89. description = models.CharField(max_length=100, blank=True)
  90. class Meta:
  91. ordering = ['name']
  92. def __unicode__(self):
  93. return self.name
  94. @property
  95. def device_sets(self):
  96. if not self.device_patterns:
  97. return None
  98. return [line.strip() for line in self.device_patterns.split('\n')]
  99. class UserActionManager(models.Manager):
  100. # Actions affecting a single object
  101. def log_action(self, user, obj, action, message):
  102. self.model.objects.create(
  103. content_type=ContentType.objects.get_for_model(obj),
  104. object_id=obj.pk,
  105. user=user,
  106. action=action,
  107. message=message,
  108. )
  109. def log_create(self, user, obj, message=''):
  110. self.log_action(user, obj, ACTION_CREATE, message)
  111. def log_edit(self, user, obj, message=''):
  112. self.log_action(user, obj, ACTION_EDIT, message)
  113. def log_delete(self, user, obj, message=''):
  114. self.log_action(user, obj, ACTION_DELETE, message)
  115. # Actions affecting multiple objects
  116. def log_bulk_action(self, user, content_type, action, message):
  117. self.model.objects.create(
  118. content_type=content_type,
  119. user=user,
  120. action=action,
  121. message=message,
  122. )
  123. def log_import(self, user, content_type, message=''):
  124. self.log_bulk_action(user, content_type, ACTION_IMPORT, message)
  125. def log_bulk_edit(self, user, content_type, message=''):
  126. self.log_bulk_action(user, content_type, ACTION_BULK_EDIT, message)
  127. def log_bulk_delete(self, user, content_type, message=''):
  128. self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message)
  129. class UserAction(models.Model):
  130. """
  131. A record of an action (add, edit, or delete) performed on an object by a User.
  132. """
  133. time = models.DateTimeField(auto_now_add=True, editable=False)
  134. user = models.ForeignKey(User, related_name='actions', on_delete=models.CASCADE)
  135. content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
  136. object_id = models.PositiveIntegerField(blank=True, null=True)
  137. action = models.PositiveSmallIntegerField(choices=ACTION_CHOICES)
  138. message = models.TextField(blank=True)
  139. objects = UserActionManager()
  140. class Meta:
  141. ordering = ['-time']
  142. def __unicode__(self):
  143. if self.message:
  144. return u'{} {}'.format(self.user, self.message)
  145. return u'{} {} {}'.format(self.user, self.get_action_display(), self.content_type)
  146. def icon(self):
  147. if self.action in [ACTION_CREATE, ACTION_IMPORT]:
  148. return mark_safe('<i class="glyphicon glyphicon-plus text-success"></i>')
  149. elif self.action in [ACTION_EDIT, ACTION_BULK_EDIT]:
  150. return mark_safe('<i class="glyphicon glyphicon-pencil text-warning"></i>')
  151. elif self.action in [ACTION_DELETE, ACTION_BULK_DELETE]:
  152. return mark_safe('<i class="glyphicon glyphicon-remove text-danger"></i>')
  153. else:
  154. return ''