Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid redundant port counter collection from virtualized instances, such as Cisco VRF #1492

Merged
merged 35 commits into from
Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9cea91e
(Netbox.master): new field to enable a relation to physical masters
lunkwill42 Mar 21, 2017
533cc76
(StatPorts): remove redundant collection in cases of virtualized inst…
lunkwill42 Mar 21, 2017
5cb0f16
(LinkState): modernize plugin to use inlineCallbacks
lunkwill42 Mar 22, 2017
6e5ace1
do not poll interface status for virtualized instances.
lunkwill42 Mar 23, 2017
1439263
(ipdevinfo): display related virtual instances/masters
lunkwill42 Mar 24, 2017
12b86b0
add snmp_version column to netbox bulk import format.
lunkwill42 Mar 24, 2017
0365c9f
Add fields for choosing master and virtual instances on an IP Device
Mar 24, 2017
6f4625b
pylint violation fix.
lunkwill42 Mar 24, 2017
fc9f933
add a new master column to the netbox bulk import format.
lunkwill42 Mar 24, 2017
53a5d35
Hide advanced options
Mar 24, 2017
ca6187c
Merge branch 'vrf' of https://github.com/lunkwill42/nav into vrf
Mar 24, 2017
d5f3779
improve master/instance help texts slightly
lunkwill42 Mar 24, 2017
c09ce44
Disable mutually exclusive fields
Mar 24, 2017
a4ca792
Merge branch 'vrf' of https://github.com/lunkwill42/nav into vrf
Mar 24, 2017
10ac2f7
Fix label
Mar 24, 2017
2434344
Disable fields based on master/instance state
Mar 27, 2017
9a89102
Clean up leftover instances when saving
Mar 27, 2017
95fcf8d
(Netbox.master): new field to enable a relation to physical masters
lunkwill42 Mar 21, 2017
fd31a0f
(StatPorts): remove redundant collection in cases of virtualized inst…
lunkwill42 Mar 21, 2017
490687c
(LinkState): modernize plugin to use inlineCallbacks
lunkwill42 Mar 22, 2017
b6e6798
do not poll interface status for virtualized instances.
lunkwill42 Mar 23, 2017
3c3e89c
(ipdevinfo): display related virtual instances/masters
lunkwill42 Mar 24, 2017
eda89ad
add snmp_version column to netbox bulk import format.
lunkwill42 Mar 24, 2017
728325f
Add fields for choosing master and virtual instances on an IP Device
Mar 24, 2017
9b8044d
pylint violation fix.
lunkwill42 Mar 24, 2017
f905ccc
add a new master column to the netbox bulk import format.
lunkwill42 Mar 24, 2017
f20266a
Hide advanced options
Mar 24, 2017
129f595
improve master/instance help texts slightly
lunkwill42 Mar 24, 2017
e3b5e94
Merge branch 'vrf' of https://github.com/jmbredal/nav into vrf
lunkwill42 May 4, 2017
4f5bb73
(StatSensors): remove redundant collection in cases of virtualized in…
sigmunau May 31, 2017
e69cdc1
(StatSystem): remove redundant collection in cases of virtualized ins…
sigmunau May 31, 2017
4f5ef77
Merge pull request #1 from sigmunau/vrf
lunkwill42 Jun 1, 2017
fd642c6
Enable access to the original model object in SeedDB list templates.
lunkwill42 Jun 2, 2017
1bec10a
Enable customization of just the row contents of SeedDB lists.
lunkwill42 Jun 2, 2017
64dfccd
Add VRF master/instance labels in the sysname column of SeedDB.
lunkwill42 Jun 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ are:
.. _`Rittal liquid cooling package (in-row liquid coolers)`: http://www.rittal.com/com-en/product/list.action?categoryPath=/PG0001/PG0168KLIMA1/PGR1951KLIMA1/PG1023KLIMA1


Changes to bulk import formats
------------------------------

The IP Device (Netbox) bulk import format has changed. Two new columns have
been added, so that the format is now specified as::

roomid:ip:orgid:catid[:snmp_version:ro:rw:master:function:data:netboxgroup:...]

The new columns are:

snmp_version
Selecting an explicit SNMP version was made compulsory in NAV 4.6, but the
bulk import format was not updated in the same release, so any device added
through the SeedDB bulk import function would default to SNMP v2c. Valid
values here are 1 or 2.

master
If this device is a virtual instance on another physical device, specify the
sysname or IP address of the master in this column. You may have to bulk
import multiple times if the master devices are part of the same bulk import
file.

NAV 4.6
========
Expand Down
6 changes: 4 additions & 2 deletions bin/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ class Handlers(object):
@staticmethod
def netbox():
"""Outputs a line for each netbox in the database"""
header("#roomid:ip:orgid:catid:[ro:rw:function:"
header("#roomid:ip:orgid:catid:[snmp_version:ro:rw:function:"
"key1=value1|key2=value2:"
"devicegroup1:devicegroup2..]")
all_functions = manage.NetboxInfo.objects.filter(key='function')
for box in manage.Netbox.objects.all():
line = [box.room_id, box.ip, box.organization_id, box.category_id,
box.read_only or "", box.read_write or ""]
str(box.snmp_version) if box.snmp_version else "",
box.read_only or "", box.read_write or "",
box.master.sysname if box.master else ""]
functions = all_functions.filter(netbox=box)
functions = str.join(", ", functions)
line.append(functions)
Expand Down
12 changes: 8 additions & 4 deletions htdocs/sass/nav/seeddb.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.readonly {
border: none;
border: none;
}

.advanced {
display: none;
}

/* Style the tables on the index page */
Expand All @@ -21,9 +25,9 @@
}

.required:after {
content: " *";
color: #F00;
font-weight: bold;
content: " *";
color: #F00;
font-weight: bold;
}

/* Style the general info tables */
Expand Down
61 changes: 61 additions & 0 deletions htdocs/static/js/src/seeddb_netbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require(['libs/select2.min'], function() {
$(function() {
var toggleTrigger = $('.advanced-toggle'),
fa = toggleTrigger.find('.fa'),
advanced = $('.advanced'),
storageKey = 'NAV.seeddb.advanced.show';

function isHidden(element) {
return element.offsetParent === null;
}

function toggle() {
advanced.slideToggle(function(){
if (isHidden(this)) {
console.log('Setting storagekey to ', 0);
localStorage.setItem(storageKey, '0');
} else {
console.log('Setting storagekey to ', 1);
localStorage.setItem(storageKey, '1');
}
});
fa.toggleClass('fa-caret-square-o-right fa-caret-square-o-down');
}

toggleTrigger.on('click', function(event) {
event.preventDefault();
toggle();
});

// Show element if localstorage says so
if (+localStorage.getItem(storageKey) === 1) {
advanced.show();
fa.toggleClass('fa-caret-square-o-right fa-caret-square-o-down');
}


// Master- and instancefield are mutually exclusive. Try to enforce that.
var $masterField = $('#id_master').select2();
var $instanceField = $('#id_virtual_instance').select2();

function checkFields() {
if ($masterField.val()) {
$instanceField.select2('enable', false);
} else {
$instanceField.select2('enable', true);
}

if ($instanceField.val() && !$masterField[0].disabled) {
$masterField.select2('enable', false);
} else {
$masterField.select2('enable', true);
}


}

$masterField.on('change', checkFields);
$instanceField.on('change', checkFields);

});
});
13 changes: 12 additions & 1 deletion python/nav/bulkimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from nav.models.manage import Prefix, Vlan, NetType
from nav.models.cabling import Cabling, Patch
from nav.models.service import Service, ServiceProperty
from nav.util import is_valid_ip
from nav.web.servicecheckers import get_description

from nav.bulkparse import BulkParseError
Expand Down Expand Up @@ -94,11 +95,21 @@ def _create_objects_from_row(self, row):
@staticmethod
def _get_netbox_from_row(row):
netbox = Netbox(ip=row['ip'], read_only=row['ro'],
read_write=row['rw'], snmp_version=2)
read_write=row['rw'],
snmp_version=row['snmp_version'] or 2)
netbox.room = get_object_or_fail(Room, id=row['roomid'])
netbox.organization = get_object_or_fail(Organization, id=row['orgid'])
netbox.category = get_object_or_fail(Category, id=row['catid'])
netbox.sysname = netbox.ip

master = row.get('master')
if master:
if is_valid_ip(master, use_socket_lib=True):
netbox.master = get_object_or_fail(Netbox, ip=master)
else:
netbox.master = get_object_or_fail(Netbox,
sysname__startswith=master)

return netbox

@staticmethod
Expand Down
12 changes: 11 additions & 1 deletion python/nav/bulkparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ def next(self):

class NetboxBulkParser(BulkParser):
"""Parses the netbox bulk format"""
format = ('roomid', 'ip', 'orgid', 'catid', 'ro', 'rw', 'function', 'data')
format = ('roomid', 'ip', 'orgid', 'catid', 'snmp_version', 'ro', 'rw',
'master', 'function', 'data')
required = 4
restkey = 'netboxgroup'

Expand All @@ -150,6 +151,15 @@ def _validate_ip(value):
else:
return True

@staticmethod
def _validate_snmp_version(value):
if not value:
return True # empty values are ok
try:
return int(value) in (1, 2)
except ValueError:
return False

@staticmethod
def _validate_data(datastring):
try:
Expand Down
39 changes: 39 additions & 0 deletions python/nav/eventengine/plugins/linkstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# along with NAV. If not, see <http://www.gnu.org/licenses/>.
#
""""linkState event plugin"""
import copy

from nav.config import ConfigurationError
from nav.eventengine.alerts import AlertGenerator
Expand Down Expand Up @@ -44,6 +45,11 @@ def get_link_partner(self):
"""Returns the link partner of the target interface"""
return self.get_target().to_netbox

def handle(self):
if self._is_a_master_for_virtualized_instances():
self._copy_event_for_instances()
return super(LinkStateHandler, self).handle()

def _handle_end(self):
self._post_event_if_aggregate_restored() # always verify aggregates
return super(LinkStateHandler, self)._handle_end()
Expand Down Expand Up @@ -160,6 +166,39 @@ def _get_aggregate_link_event(self, start):
EventVar(event_queue=event, variable='aggregate_ifalias',
value=target.ifalias or '').save()

#
# Methods to handle duplication of events for virtualized netbox instances
#

def _is_a_master_for_virtualized_instances(self):
ifc = self.get_target()
return ifc and ifc.netbox and ifc.netbox.instances.count() > 0

def _copy_event_for_instances(self):
ifc = self.get_target()
netbox = ifc.netbox
for instance in netbox.instances.all():
self._copy_event_for_instance(netbox, instance, ifc)

def _copy_event_for_instance(self, netbox, instance, ifc):
try:
other_ifc = Interface.objects.get(netbox=instance,
ifname=ifc.ifname)
except Interface.DoesNotExist:
self._logger.info("interface %s does not exist on instance %s",
ifc.ifname, instance)
return

new_event = copy.copy(self.event) # type: nav.models.event.EventQueue
new_event.pk = None
new_event.netbox = instance
new_event.device = None
new_event.subid = other_ifc.pk

self._logger.info('duplicating linkState event for %s to %s',
ifc, instance)
new_event.save()


class LinkStateConfiguration(object):
"""Retrieves configuration options for the LinkStateHandler"""
Expand Down
13 changes: 13 additions & 0 deletions python/nav/ipdevpoll/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,16 @@ def full_name(self):
"""Return the full module and class name of this instance."""
return "%s.%s" % (self.__class__.__module__,
self.__class__.__name__)

def _get_netbox_list(self):
"""Returns a list of netbox names to make metrics for. Will return just
the one netbox in most instances, but for situations with multiple
virtual device contexts, all the subdevices will be returned.

"""
netboxes = [self.netbox.sysname]
instances = self.netbox.instances.values_list('sysname', flat=True)
netboxes.extend(instances)
self._logger.debug("duplicating metrics for these netboxes: %s",
netboxes)
return netboxes
20 changes: 13 additions & 7 deletions python/nav/ipdevpoll/plugins/linkstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,30 @@
# License along with NAV. If not, see <http://www.gnu.org/licenses/>.
#
"""Collects interface link states and dispatches NAV events on changes"""
from twisted.internet.defer import inlineCallbacks, returnValue

from nav.mibs import reduce_index
from nav.mibs.if_mib import IfMib

from nav.models.manage import Interface

from nav.ipdevpoll import Plugin
from nav.ipdevpoll import shadows


class LinkState(Plugin):
"""Monitors interface link states"""

@inlineCallbacks
def handle(self):
self.ifmib = IfMib(self.agent)
df = self.ifmib.retrieve_columns(
['ifName', 'ifAdminStatus', 'ifOperStatus'])
df.addCallback(reduce_index)
return df.addCallback(self._put_results)
if self.netbox.master:
self._logger.debug("this is a virtual instance of %s, not polling",
self.netbox.master)
returnValue(None)

ifmib = IfMib(self.agent)
result = yield ifmib.retrieve_columns(
['ifName', 'ifAdminStatus', 'ifOperStatus']).addCallback(
reduce_index)
self._put_results(result)

def _put_results(self, results):
netbox = self.containers.factory(None, shadows.Netbox)
Expand Down
30 changes: 25 additions & 5 deletions python/nav/ipdevpoll/plugins/statports.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import time
from twisted.internet import defer
from nav.ipdevpoll import Plugin
from nav.ipdevpoll import db
from nav.metrics.carbon import send_metrics
from nav.metrics.templates import metric_path_for_interface
from nav.mibs import reduce_index
Expand Down Expand Up @@ -71,9 +72,15 @@ def can_handle(cls, netbox):

@defer.inlineCallbacks
def handle(self):
if self.netbox.master:
yield db.run_in_thread(self._log_instance_details)
defer.returnValue(None)

timestamp = time.time()
stats = yield self._get_stats()
tuples = list(self._make_metrics(stats, timestamp))
netboxes = yield db.run_in_thread(self._get_netbox_list)
tuples = list(self._make_metrics(stats, netboxes=netboxes,
timestamp=timestamp))
if tuples:
self._logger.debug("Counters collected")
send_metrics(tuples)
Expand All @@ -95,7 +102,7 @@ def _get_stats(self):

defer.returnValue(stats)

def _make_metrics(self, stats, timestamp=None):
def _make_metrics(self, stats, netboxes, timestamp=None):
timestamp = timestamp or time.time()
hc_counters = False

Expand All @@ -104,18 +111,31 @@ def _make_metrics(self, stats, timestamp=None):
for key in LOGGED_COUNTERS:
if key not in row:
continue
path = metric_path_for_interface(
self.netbox, row['ifName'] or row['ifDescr'], key)
value = row[key]
if value is not None:
yield (path, (timestamp, value))
for netbox in netboxes:
# duplicate metrics for all involved netboxes
path = metric_path_for_interface(
netbox, row['ifName'] or row['ifDescr'], key)
yield (path, (timestamp, value))

if stats:
if hc_counters:
self._logger.debug("High Capacity counters used")
else:
self._logger.debug("High Capacity counters NOT used")

def _log_instance_details(self):
netbox = self.netbox

my_ifcs = netbox.interface_set.values_list('ifname', flat=True)
masters_ifcs = netbox.master.interface_set.values_list('ifname',
flat=True)
local_ifcs = set(masters_ifcs) - set(my_ifcs)

self._logger.debug("local interfaces (that do not exist on master "
"%s): %r", self.netbox.master, local_ifcs)


def use_hc_counters(row):
"""
Expand Down
Loading