run_inventory.py 5.7 KB

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