run_inventory.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from Exscript.protocols.Exception import LoginFailure
  2. from getpass import getpass
  3. from ncclient.transport.errors import AuthenticationError
  4. from django.conf import settings
  5. from django.core.management.base import BaseCommand, CommandError
  6. from django.db import transaction
  7. from dcim.models import Device, Module, Site
  8. class Command(BaseCommand):
  9. help = "Update inventory information for specified devices"
  10. username = settings.NETBOX_USERNAME
  11. password = settings.NETBOX_PASSWORD
  12. def add_arguments(self, parser):
  13. parser.add_argument('-u', '--username', dest='username', help="Specify the username to use")
  14. parser.add_argument('-p', '--password', action='store_true', default=False, help="Prompt for password to use")
  15. parser.add_argument('-s', '--site', dest='site', action='append', help="Filter devices by site (include argument once per site)")
  16. parser.add_argument('-n', '--name', dest='name', help="Filter devices by name (regular expression)")
  17. parser.add_argument('--full', action='store_true', default=False, help="For inventory update for all devices")
  18. parser.add_argument('--fake', action='store_true', default=False, help="Do not actually update database")
  19. def handle(self, *args, **options):
  20. def create_modules(modules, parent=None):
  21. for module in modules:
  22. m = Module(device=device, parent=parent, name=module['name'], part_id=module['part_id'],
  23. serial=module['serial'])
  24. m.save()
  25. create_modules(module.get('modules', []), parent=m)
  26. # Credentials
  27. if options['username']:
  28. self.username = options['username']
  29. if options['password']:
  30. self.password = getpass("Password: ")
  31. device_list = Device.objects.filter()
  32. # --site: Include only devices belonging to specified site(s)
  33. if options['site']:
  34. sites = Site.objects.filter(slug__in=options['site'])
  35. if sites:
  36. site_names = [s.name for s in sites]
  37. self.stdout.write("Running inventory for these sites: {}".format(', '.join(site_names)))
  38. else:
  39. raise CommandError("One or more sites specified but none found.")
  40. device_list = device_list.filter(rack__site__in=sites)
  41. # --name: Filter devices by name matching a regex
  42. if options['name']:
  43. device_list = device_list.filter(name__iregex=options['name'])
  44. # --full: Gather inventory data for *all* devices
  45. if options['full']:
  46. self.stdout.write("WARNING: Running inventory for all devices! Prior data will be overwritten. (--full)")
  47. # --fake: Gathering data but not updating the database
  48. if options['fake']:
  49. self.stdout.write("WARNING: Inventory data will not be saved! (--fake)")
  50. device_count = device_list.count()
  51. self.stdout.write("** Found {} devices...".format(device_count))
  52. for i, device in enumerate(device_list, start=1):
  53. self.stdout.write("[{}/{}] {}: ".format(i, device_count, device.name), ending='')
  54. # Skip inactive devices
  55. if not device.status:
  56. self.stdout.write("Skipped (inactive)")
  57. continue
  58. # Skip devices without primary_ip set
  59. if not device.primary_ip:
  60. self.stdout.write("Skipped (no primary IP set)")
  61. continue
  62. # Skip devices which have already been inventoried if not doing a full update
  63. if device.serial and not options['full']:
  64. self.stdout.write("Skipped (Serial: {})".format(device.serial))
  65. continue
  66. RPC = device.get_rpc_client()
  67. if not RPC:
  68. self.stdout.write("Skipped (no RPC client available for platform {})".format(device.platform))
  69. continue
  70. # Connect to device and retrieve inventory info
  71. try:
  72. with RPC(device, self.username, self.password) as rpc_client:
  73. inventory = rpc_client.get_inventory()
  74. except KeyboardInterrupt:
  75. raise
  76. except (AuthenticationError, LoginFailure):
  77. self.stdout.write("Authentication error!")
  78. continue
  79. except Exception as e:
  80. self.stdout.write("Error for {} ({}): {}".format(device, device.primary_ip.address.ip, e))
  81. continue
  82. self.stdout.write("")
  83. self.stdout.write("\tSerial: {}".format(inventory['chassis']['serial']))
  84. self.stdout.write("\tDescription: {}".format(inventory['chassis']['description']))
  85. for module in inventory['modules']:
  86. self.stdout.write("\tModule: {} / {} ({})".format(module['name'], module['part_id'], module['serial']))
  87. if not options['fake']:
  88. with transaction.atomic():
  89. if inventory['chassis']['serial']:
  90. device.serial = inventory['chassis']['serial']
  91. device.save()
  92. Module.objects.filter(device=device).delete()
  93. create_modules(inventory.get('modules', []))
  94. self.stdout.write("Finished!")