admin.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. __package__ = 'archivebox.machine'
  2. from django.contrib import admin
  3. from django.utils.html import format_html
  4. from archivebox.base_models.admin import BaseModelAdmin, ConfigEditorMixin
  5. from archivebox.machine.models import Machine, NetworkInterface, Binary, Process
  6. class MachineAdmin(ConfigEditorMixin, BaseModelAdmin):
  7. list_display = ('id', 'created_at', 'hostname', 'ips', 'os_platform', 'hw_in_docker', 'hw_in_vm', 'hw_manufacturer', 'hw_product', 'os_arch', 'os_family', 'os_release', 'hw_uuid', 'health')
  8. sort_fields = ('id', 'created_at', 'hostname', 'ips', 'os_platform', 'hw_in_docker', 'hw_in_vm', 'hw_manufacturer', 'hw_product', 'os_arch', 'os_family', 'os_release', 'hw_uuid')
  9. readonly_fields = ('guid', 'created_at', 'modified_at', 'ips')
  10. fieldsets = (
  11. ('Identity', {
  12. 'fields': ('hostname', 'guid', 'ips'),
  13. 'classes': ('card',),
  14. }),
  15. ('Hardware', {
  16. 'fields': ('hw_manufacturer', 'hw_product', 'hw_uuid', 'hw_in_docker', 'hw_in_vm'),
  17. 'classes': ('card',),
  18. }),
  19. ('Operating System', {
  20. 'fields': ('os_platform', 'os_family', 'os_arch', 'os_kernel', 'os_release'),
  21. 'classes': ('card',),
  22. }),
  23. ('Statistics', {
  24. 'fields': ('stats', 'num_uses_succeeded', 'num_uses_failed'),
  25. 'classes': ('card',),
  26. }),
  27. ('Configuration', {
  28. 'fields': ('config',),
  29. 'classes': ('card', 'wide'),
  30. }),
  31. ('Timestamps', {
  32. 'fields': ('created_at', 'modified_at'),
  33. 'classes': ('card',),
  34. }),
  35. )
  36. list_filter = ('hw_in_docker', 'hw_in_vm', 'os_arch', 'os_family', 'os_platform')
  37. ordering = ['-created_at']
  38. list_per_page = 100
  39. actions = ["delete_selected"]
  40. @admin.display(description='Public IP', ordering='networkinterface__ip_public')
  41. def ips(self, machine):
  42. return format_html(
  43. '<a href="/admin/machine/networkinterface/?q={}"><b><code>{}</code></b></a>',
  44. machine.id, ', '.join(machine.networkinterface_set.values_list('ip_public', flat=True)),
  45. )
  46. class NetworkInterfaceAdmin(BaseModelAdmin):
  47. list_display = ('id', 'created_at', 'machine_info', 'ip_public', 'dns_server', 'isp', 'country', 'region', 'city', 'iface', 'ip_local', 'mac_address', 'health')
  48. sort_fields = ('id', 'created_at', 'machine_info', 'ip_public', 'dns_server', 'isp', 'country', 'region', 'city', 'iface', 'ip_local', 'mac_address')
  49. search_fields = ('id', 'machine__id', 'iface', 'ip_public', 'ip_local', 'mac_address', 'dns_server', 'hostname', 'isp', 'city', 'region', 'country')
  50. readonly_fields = ('machine', 'created_at', 'modified_at', 'mac_address', 'ip_public', 'ip_local', 'dns_server')
  51. fieldsets = (
  52. ('Machine', {
  53. 'fields': ('machine',),
  54. 'classes': ('card',),
  55. }),
  56. ('Network', {
  57. 'fields': ('iface', 'ip_public', 'ip_local', 'mac_address', 'dns_server'),
  58. 'classes': ('card',),
  59. }),
  60. ('Location', {
  61. 'fields': ('hostname', 'isp', 'city', 'region', 'country'),
  62. 'classes': ('card',),
  63. }),
  64. ('Usage', {
  65. 'fields': ('num_uses_succeeded', 'num_uses_failed'),
  66. 'classes': ('card',),
  67. }),
  68. ('Timestamps', {
  69. 'fields': ('created_at', 'modified_at'),
  70. 'classes': ('card',),
  71. }),
  72. )
  73. list_filter = ('isp', 'country', 'region')
  74. ordering = ['-created_at']
  75. list_per_page = 100
  76. actions = ["delete_selected"]
  77. @admin.display(description='Machine', ordering='machine__id')
  78. def machine_info(self, iface):
  79. return format_html(
  80. '<a href="/admin/machine/machine/{}/change"><b><code>[{}]</code></b> &nbsp; {}</a>',
  81. iface.machine.id, str(iface.machine.id)[:8], iface.machine.hostname,
  82. )
  83. class BinaryAdmin(BaseModelAdmin):
  84. list_display = ('id', 'created_at', 'machine_info', 'name', 'binprovider', 'version', 'abspath', 'sha256', 'status', 'health')
  85. sort_fields = ('id', 'created_at', 'machine_info', 'name', 'binprovider', 'version', 'abspath', 'sha256', 'status')
  86. search_fields = ('id', 'machine__id', 'name', 'binprovider', 'version', 'abspath', 'sha256')
  87. readonly_fields = ('created_at', 'modified_at')
  88. fieldsets = (
  89. ('Binary Info', {
  90. 'fields': ('name', 'binproviders', 'binprovider', 'overrides'),
  91. 'classes': ('card',),
  92. }),
  93. ('Location', {
  94. 'fields': ('machine', 'abspath'),
  95. 'classes': ('card',),
  96. }),
  97. ('Version', {
  98. 'fields': ('version', 'sha256'),
  99. 'classes': ('card',),
  100. }),
  101. ('State', {
  102. 'fields': ('status', 'retry_at', 'output_dir'),
  103. 'classes': ('card',),
  104. }),
  105. ('Usage', {
  106. 'fields': ('num_uses_succeeded', 'num_uses_failed'),
  107. 'classes': ('card',),
  108. }),
  109. ('Timestamps', {
  110. 'fields': ('created_at', 'modified_at'),
  111. 'classes': ('card',),
  112. }),
  113. )
  114. list_filter = ('name', 'binprovider', 'status', 'machine_id')
  115. ordering = ['-created_at']
  116. list_per_page = 100
  117. actions = ["delete_selected"]
  118. @admin.display(description='Machine', ordering='machine__id')
  119. def machine_info(self, binary):
  120. return format_html(
  121. '<a href="/admin/machine/machine/{}/change"><b><code>[{}]</code></b> &nbsp; {}</a>',
  122. binary.machine.id, str(binary.machine.id)[:8], binary.machine.hostname,
  123. )
  124. class ProcessAdmin(BaseModelAdmin):
  125. list_display = ('id', 'created_at', 'machine_info', 'archiveresult_link', 'cmd_str', 'status', 'exit_code', 'pid', 'binary_info', 'health')
  126. sort_fields = ('id', 'created_at', 'status', 'exit_code', 'pid')
  127. search_fields = ('id', 'machine__id', 'binary__name', 'cmd', 'pwd', 'stdout', 'stderr')
  128. readonly_fields = ('created_at', 'modified_at', 'machine', 'binary', 'iface', 'archiveresult_link')
  129. fieldsets = (
  130. ('Process Info', {
  131. 'fields': ('machine', 'archiveresult_link', 'status', 'retry_at'),
  132. 'classes': ('card',),
  133. }),
  134. ('Command', {
  135. 'fields': ('cmd', 'pwd', 'env', 'timeout'),
  136. 'classes': ('card', 'wide'),
  137. }),
  138. ('Execution', {
  139. 'fields': ('binary', 'iface', 'pid', 'exit_code', 'url'),
  140. 'classes': ('card',),
  141. }),
  142. ('Timing', {
  143. 'fields': ('started_at', 'ended_at'),
  144. 'classes': ('card',),
  145. }),
  146. ('Output', {
  147. 'fields': ('stdout', 'stderr'),
  148. 'classes': ('card', 'wide', 'collapse'),
  149. }),
  150. ('Usage', {
  151. 'fields': ('num_uses_succeeded', 'num_uses_failed'),
  152. 'classes': ('card',),
  153. }),
  154. ('Timestamps', {
  155. 'fields': ('created_at', 'modified_at'),
  156. 'classes': ('card',),
  157. }),
  158. )
  159. list_filter = ('status', 'exit_code', 'machine_id')
  160. ordering = ['-created_at']
  161. list_per_page = 100
  162. actions = ["delete_selected"]
  163. @admin.display(description='Machine', ordering='machine__id')
  164. def machine_info(self, process):
  165. return format_html(
  166. '<a href="/admin/machine/machine/{}/change"><b><code>[{}]</code></b> &nbsp; {}</a>',
  167. process.machine.id, str(process.machine.id)[:8], process.machine.hostname,
  168. )
  169. @admin.display(description='Binary', ordering='binary__name')
  170. def binary_info(self, process):
  171. if not process.binary:
  172. return '-'
  173. return format_html(
  174. '<a href="/admin/machine/binary/{}/change"><code>{}</code> v{}</a>',
  175. process.binary.id, process.binary.name, process.binary.version,
  176. )
  177. @admin.display(description='ArchiveResult')
  178. def archiveresult_link(self, process):
  179. if not hasattr(process, 'archiveresult'):
  180. return '-'
  181. ar = process.archiveresult
  182. return format_html(
  183. '<a href="/admin/core/archiveresult/{}/change"><code>{}</code> → {}</a>',
  184. ar.id, ar.plugin, ar.snapshot.url[:50],
  185. )
  186. @admin.display(description='Command')
  187. def cmd_str(self, process):
  188. if not process.cmd:
  189. return '-'
  190. cmd = ' '.join(process.cmd[:3]) if isinstance(process.cmd, list) else str(process.cmd)
  191. if len(process.cmd) > 3:
  192. cmd += ' ...'
  193. return format_html('<code style="font-size: 0.9em;">{}</code>', cmd[:80])
  194. def register_admin(admin_site):
  195. admin_site.register(Machine, MachineAdmin)
  196. admin_site.register(NetworkInterface, NetworkInterfaceAdmin)
  197. admin_site.register(Binary, BinaryAdmin)
  198. admin_site.register(Process, ProcessAdmin)