models.py 6.5 KB

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