run_inventory.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. from getpass import getpass
  2. from ncclient.transport.errors import AuthenticationError
  3. from paramiko import AuthenticationException
  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, InventoryItem, Site, STATUS_ACTIVE
  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',
  16. help="Filter devices by site (include argument once per site)")
  17. parser.add_argument('-n', '--name', dest='name', help="Filter devices by name (regular expression)")
  18. parser.add_argument('--full', action='store_true', default=False, help="For inventory update for all devices")
  19. parser.add_argument('--fake', action='store_true', default=False, help="Do not actually update database")
  20. def handle(self, *args, **options):
  21. def create_inventory_items(inventory_items, parent=None):
  22. for item in inventory_items:
  23. i = InventoryItem(device=device, parent=parent, name=item['name'], part_id=item['part_id'],
  24. serial=item['serial'], discovered=True)
  25. i.save()
  26. create_inventory_items(item.get('items', []), parent=i)
  27. # Credentials
  28. if options['username']:
  29. self.username = options['username']
  30. if options['password']:
  31. self.password = getpass("Password: ")
  32. # Attempt to inventory only active devices
  33. device_list = Device.objects.filter(status=STATUS_ACTIVE)
  34. # --site: Include only devices belonging to specified site(s)
  35. if options['site']:
  36. sites = Site.objects.filter(slug__in=options['site'])
  37. if sites:
  38. site_names = [s.name for s in sites]
  39. self.stdout.write("Running inventory for these sites: {}".format(', '.join(site_names)))
  40. else:
  41. raise CommandError("One or more sites specified but none found.")
  42. device_list = device_list.filter(site__in=sites)
  43. # --name: Filter devices by name matching a regex
  44. if options['name']:
  45. device_list = device_list.filter(name__iregex=options['name'])
  46. # --full: Gather inventory data for *all* devices
  47. if options['full']:
  48. self.stdout.write("WARNING: Running inventory for all devices! Prior data will be overwritten. (--full)")
  49. # --fake: Gathering data but not updating the database
  50. if options['fake']:
  51. self.stdout.write("WARNING: Inventory data will not be saved! (--fake)")
  52. device_count = device_list.count()
  53. self.stdout.write("** Found {} devices...".format(device_count))
  54. for i, device in enumerate(device_list, start=1):
  55. self.stdout.write("[{}/{}] {}: ".format(i, device_count, device.name), ending='')
  56. # Skip inactive devices
  57. if not device.status:
  58. self.stdout.write("Skipped (not active)")
  59. continue
  60. # Skip devices without primary_ip set
  61. if not device.primary_ip:
  62. self.stdout.write("Skipped (no primary IP set)")
  63. continue
  64. # Skip devices which have already been inventoried if not doing a full update
  65. if device.serial and not options['full']:
  66. self.stdout.write("Skipped (Serial: {})".format(device.serial))
  67. continue
  68. RPC = device.get_rpc_client()
  69. if not RPC:
  70. self.stdout.write("Skipped (no RPC client available for platform {})".format(device.platform))
  71. continue
  72. # Connect to device and retrieve inventory info
  73. try:
  74. with RPC(device, self.username, self.password) as rpc_client:
  75. inventory = rpc_client.get_inventory()
  76. except KeyboardInterrupt:
  77. raise
  78. except (AuthenticationError, AuthenticationException):
  79. self.stdout.write("Authentication error!")
  80. continue
  81. except Exception as e:
  82. self.stdout.write("Error: {}".format(e))
  83. continue
  84. if options['verbosity'] > 1:
  85. self.stdout.write("")
  86. self.stdout.write("\tSerial: {}".format(inventory['chassis']['serial']))
  87. self.stdout.write("\tDescription: {}".format(inventory['chassis']['description']))
  88. for item in inventory['items']:
  89. self.stdout.write("\tItem: {} / {} ({})".format(item['name'], item['part_id'],
  90. item['serial']))
  91. else:
  92. self.stdout.write("{} ({})".format(inventory['chassis']['description'], inventory['chassis']['serial']))
  93. if not options['fake']:
  94. with transaction.atomic():
  95. # Update device serial
  96. if device.serial != inventory['chassis']['serial']:
  97. device.serial = inventory['chassis']['serial']
  98. device.save()
  99. InventoryItem.objects.filter(device=device, discovered=True).delete()
  100. create_inventory_items(inventory.get('items', []))
  101. self.stdout.write("Finished!")