diff --git a/README.md b/README.md index 3e48d91..8786e0e 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ rm -r /usr/share/icingaweb2/modules/netbox Download and extract the new release, then move the module into the icingaweb2 modules path. -For example for version 3.1.16.8: +For example for version 3.1.16.9: ``` -curl -L https://github.com/sol1/icingaweb2-module-netbox/archive/v3.1.16.8.tar.gz | tar xz -mv icingaweb2-module-netbox-3.1.16.8 /usr/share/icingaweb2/modules/netbox +curl -L https://github.com/sol1/icingaweb2-module-netbox/archive/v3.1.16.9.tar.gz | tar xz +mv icingaweb2-module-netbox-3.1.16.9 /usr/share/icingaweb2/modules/netbox icingacli module enable netbox ``` diff --git a/library/Netbox/Netbox.php b/library/Netbox/Netbox.php index 8612ff3..0473502 100644 --- a/library/Netbox/Netbox.php +++ b/library/Netbox/Netbox.php @@ -2,12 +2,22 @@ namespace Icinga\Module\Netbox; +// use Icinga\Module\Director\Daemon\Logger; + class Netbox { public $object_type; public $type_map = array(); public $prefix = 'nb'; + // Netbox now stores some linked object in a generic 'object' which then has a type to say + // what kind of object it is, this maps those values to the $object types used in this module + public $netbox_object_map = array( + "virtualization.virtualmachine" => "vm", + "dcim.device" => "device", + "dcim.site" => "site" + ); + function __construct($baseurl, $token, $proxy, $flattenseparator, $flattenkeys, $munge) { $this->baseurl = $baseurl; @@ -18,8 +28,6 @@ function __construct($baseurl, $token, $proxy, $flattenseparator, $flattenkeys, $this->munge = $munge; } - - private function httpget(string $url) { $ch = curl_init($url); @@ -130,6 +138,15 @@ private function makeHelperKeys(array $in) { $row->keyid = $this->keymaker($row->name); } + // Contact assignment uses linked object, build a key id from the contact and object names + if ($this->object_type == 'contact_assignment') { + if (property_exists($row, 'object') and property_exists($row, 'contact')) { + if (property_exists($row->contact, 'name') and property_exists($row->object, 'name')) { + $row->keyid = $this->keymaker($row->contact->name . ' ' . $row->object->name); + } + } + } + // Extract the address for the primary ip // TODO: ipv6 ?? $row->primary_ip_address = NULL; // Make empty field for column headings if no values exist @@ -192,6 +209,13 @@ private function makeHelperKeys(array $in) { } } + // This turns a linked object into it's proper key id if we know how to match that. + if (property_exists($row, 'object') and property_exists($row, 'content_type') and array_key_exists($row->content_type, $this->netbox_object_map)) { + if (property_exists($row->object, 'name')) { + $row->object_keyid = $this->keymaker($row->object->name, $this->netbox_object_map[$row->content_type]); + } + } + // Custom fields for Netbox // Icinga satellite /* @@ -598,13 +622,18 @@ public function contactGroups($filter, int $limit = 0) return $this->get_netbox("/tenancy/contact-groups/?" . $this->default_filter($filter, ""), $limit); } - public function contactModes($filter, int $limit = 0) + public function contactRoles($filter, int $limit = 0) { $this->object_type = 'contact_role'; return $this->get_netbox("/tenancy/contact-roles/?" . $this->default_filter($filter, ""), $limit); } - // TODO: contactAssignement + public function contactAssignments($filter, int $limit = 0) + { + $this->object_type = 'contact_assignment'; + return $this->get_netbox("/tenancy/contact-assignments/?" . $this->default_filter($filter, ""), $limit); + } + // Other public function platforms($filter, int $limit = 0) diff --git a/library/Netbox/ProvidedHook/Director/ImportSource.php b/library/Netbox/ProvidedHook/Director/ImportSource.php index 6d1fbaa..b82127d 100644 --- a/library/Netbox/ProvidedHook/Director/ImportSource.php +++ b/library/Netbox/ProvidedHook/Director/ImportSource.php @@ -45,6 +45,7 @@ class ImportSource extends ImportSourceHook const ContactMode = 54; const ContactGroupMode = 56; const ContactRoleMode = 58; + const ContactAssignmentMode = 59; // Other const PlatformMode = 60; @@ -70,6 +71,26 @@ class ImportSource extends ImportSourceHook // const FHRPAssociation = 30; + + // Assume the list of contacts assignments passed in are all the same type + // as the things passed in. + private function get_contact_assignments($contact_assignments, $things) { + $output = array(); + foreach ($things as $thing) { + // make an array here for a list of contacts + $thing->contacts = array(); + $thing->contact_keyids = array(); + foreach ($contact_assignments as $contact_assignment) { + if ($contact_assignment->object->id == $thing->id) { + array_push($thing->contacts, $contact_assignment->contact->name); + array_push($thing->contact_keyids, strtolower("nbcontact " . preg_replace('/__+/i', '_', preg_replace('/[^0-9a-zA-Z_\-. ]+/i', '_', $contact_assignment->contact->name)))); + } + } + $output = array_merge($output, [(object)$thing]); + } + return $output; + } + // TODO: VRF is linked to devices/vm's through ip's. If we need VRF's then we should // create an array in the import of all the linked ip's and vrf inside the importer // rather than leaving it to the user to create host templates to link it all together. @@ -238,6 +259,7 @@ public static function addSettingsFormFields(QuickForm $form) self::ContactMode => $form->translate('Contacts'), self::ContactGroupMode => $form->translate('Contact Groups'), self::ContactRoleMode => $form->translate('Contact Roles'), + self::ContactAssignmentMode => $form->translate('Contact Assignments'), // Other self::PlatformMode => $form->translate('Platforms'), @@ -283,6 +305,18 @@ public static function addSettingsFormFields(QuickForm $form) 'description' => $form->translate('Optional search filter to the url to limit netbox data returned (Default: status=active is added without a filter selected)') )); + $form->addElement('checkbox', 'linked_services', array( + 'label' => $form->translate('Link Services'), + 'required' => false, + 'description' => $form->translate('Checking this box will link Service objects for devices and virtual machines during their import. WARNING: This could increase API load to Netbox if you have a lot of services.') + )); + + $form->addElement('checkbox', 'linked_contacts', array( + 'label' => $form->translate('Link Contacts'), + 'required' => false, + 'description' => $form->translate('Checking this box will link Contact objects for devices and virtual machines during their import. WARNING: This could increase API load to Netbox if you have a lot of contact assignements.') + )); + // $form->addElement('multiCheckbox', 'associations', array( // 'label' => $form->translate("Associate additional data"), // 'required' => false, @@ -296,6 +330,22 @@ public static function addSettingsFormFields(QuickForm $form) } + private function getLinkedObjects($baseurl, $apitoken, $proxy, $linkservices, $linkcontacts, $content_type, $things) + { + $netboxLinked = new Netbox($baseurl, $apitoken, $proxy, "", "", ""); + $services = array(); + if ($linkservices) { + $services = $netboxLinked->allservices("", 0); + } + $contact_assignments = array(); + if ($linkcontacts) { + $contact_assignments = $netboxLinked->contactAssignments("content_type=" . $content_type, 0); + } + $ranges = $netboxLinked->ipRanges("", 0); + return $this->devices_with_services($services, $this->get_contact_assignments($contact_assignments, $this->get_ip_range($ranges, $things))); + + } + public function fetchData(int $limit = 0) { $baseurl = $this->getSetting('baseurl'); @@ -306,15 +356,13 @@ public function fetchData(int $limit = 0) $flatten = (string)$this->getSetting('flatten'); $flattenkeys = ((string)$this->getSetting('flattenkeys') == '') ? array() : explode(",", (string)$this->getSetting('flattenkeys')); $munge = ((string)$this->getSetting('munge') == '') ? array() : explode(",", (string)$this->getSetting('munge')); + $linkcontacts = $this->getSetting('linked_contacts'); + $linkservices = $this->getSetting('linked_services'); $netbox = new Netbox($baseurl, $apitoken, $proxy, $flatten, $flattenkeys, $munge); switch ($mode) { // VM's case self::VMMode: - $netboxLinked = new Netbox($baseurl, $apitoken, $proxy, "", "", ""); - $services = $netboxLinked->allservices("", 0); - $ranges = $netboxLinked->ipRanges("", 0); - $vms = $this->get_ip_range($ranges, $netbox->virtualMachines($filter, $limit)); - return $this->devices_with_services($services, $vms); + return $this->getLinkedObjects($baseurl, $apitoken, $proxy, $linkservices, $linkcontacts, "virtualization.virtualmachine", $netbox->virtualMachines($filter, $limit)); case self::ClusterMode: return $netbox->clusters($filter, $limit); case self::ClusterGroupMode: @@ -326,11 +374,7 @@ public function fetchData(int $limit = 0) // Device case self::DeviceMode: - $netboxLinked = new Netbox($baseurl, $apitoken, $proxy, "", "", ""); - $services = $netboxLinked->allservices("", 0); - $ranges = $netboxLinked->ipRanges("", 0); - $devices = $this->get_ip_range($ranges, $netbox->devices($filter, $limit)); - return $this->devices_with_services($services, $devices, $filter); + return $this->getLinkedObjects($baseurl, $apitoken, $proxy, $linkservices, $linkcontacts, "dcim.device", $netbox->devices($filter, $limit)); case self::DeviceRoleMode: return $netbox->deviceRoles($filter, $limit); case self::DeviceTypeMode: @@ -371,8 +415,10 @@ public function fetchData(int $limit = 0) case self::ContactGroupMode: return $netbox->contactGroups($filter, $limit); case self::ContactRoleMode: - return $netbox->contactModes($filter, $limit); - + return $netbox->contactRoles($filter, $limit); + case self::ContactAssignmentMode: + return $netbox->contactAssignments($filter, $limit); + // Other case self::PlatformMode: return $netbox->platforms($filter, $limit); diff --git a/module.info b/module.info index 170a478..131722d 100644 --- a/module.info +++ b/module.info @@ -1,4 +1,4 @@ Name: Netbox -Version: 3.1.16.8 +Version: 3.1.16.9 Depends: director Description: Import devices, sites and other objects from Netbox