runscript.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import json
  2. import logging
  3. import sys
  4. import uuid
  5. from django.core.management.base import BaseCommand, CommandError
  6. from extras.jobs import ScriptJob
  7. from extras.scripts import EXEC_PARAM_FIELDS, get_module_and_script
  8. from users.models import User
  9. from utilities.request import NetBoxFakeRequest
  10. class Command(BaseCommand):
  11. help = "Run a script in NetBox"
  12. def add_arguments(self, parser):
  13. parser.add_argument(
  14. '--loglevel',
  15. help="Logging Level (default: info)",
  16. dest='loglevel',
  17. default='info',
  18. choices=['debug', 'info', 'warning', 'error', 'critical'])
  19. parser.add_argument('--commit', help="Commit this script to database", action='store_true')
  20. parser.add_argument('--user', help="User script is running as")
  21. parser.add_argument('--data', help="Data as a string encapsulated JSON blob")
  22. parser.add_argument('script', help="Script to run")
  23. def handle(self, *args, **options):
  24. # Params
  25. script = options['script']
  26. loglevel = options['loglevel']
  27. commit = options['commit']
  28. try:
  29. data = json.loads(options['data'])
  30. except TypeError:
  31. data = {}
  32. module_name, script_name = script.split('.', 1)
  33. script_obj = get_module_and_script(module_name, script_name)[1]
  34. script = script_obj.python_class
  35. # Take user from command line if provided and exists, other
  36. if options['user']:
  37. try:
  38. user = User.objects.get(username=options['user'])
  39. except User.DoesNotExist:
  40. user = User.objects.filter(is_superuser=True).order_by('pk')[0]
  41. else:
  42. user = User.objects.filter(is_superuser=True).order_by('pk')[0]
  43. # Setup logging to Stdout
  44. formatter = logging.Formatter('[%(asctime)s][%(levelname)s] - %(message)s')
  45. stdouthandler = logging.StreamHandler(sys.stdout)
  46. stdouthandler.setLevel(logging.DEBUG)
  47. stdouthandler.setFormatter(formatter)
  48. logger = logging.getLogger(f"netbox.scripts.{script.full_name}")
  49. logger.addHandler(stdouthandler)
  50. try:
  51. logger.setLevel({
  52. 'critical': logging.CRITICAL,
  53. 'debug': logging.DEBUG,
  54. 'error': logging.ERROR,
  55. 'fatal': logging.FATAL,
  56. 'info': logging.INFO,
  57. 'warning': logging.WARNING,
  58. }[loglevel])
  59. except KeyError: # pragma: no cover
  60. raise CommandError(f"Invalid log level: {loglevel}")
  61. # Initialize the script form
  62. script = script()
  63. form = script.as_form(data, None)
  64. if not form.is_valid():
  65. logger.error('Data is not valid:')
  66. for field, errors in form.errors.get_json_data().items():
  67. for error in errors:
  68. logger.error(f'\t{field}: {error.get("message")}')
  69. raise CommandError()
  70. # Remove exec-parameter fields from ScriptForm before passing data to the script
  71. cleaned_data = form.cleaned_data.copy()
  72. notifications = cleaned_data.pop('_notifications')
  73. for key in EXEC_PARAM_FIELDS:
  74. cleaned_data.pop(key, None)
  75. # Execute the script.
  76. job = ScriptJob.enqueue(
  77. instance=script_obj,
  78. user=user,
  79. immediate=True,
  80. data=cleaned_data,
  81. notifications=notifications,
  82. request=NetBoxFakeRequest({
  83. 'META': {},
  84. 'COOKIES': {},
  85. 'POST': data,
  86. 'GET': {},
  87. 'FILES': {},
  88. 'user': user,
  89. 'method': 'POST',
  90. 'path': '',
  91. 'id': uuid.uuid4()
  92. }),
  93. commit=commit,
  94. )
  95. logger.info(f"Script completed in {job.duration}")