[-]
[+]
|
Deleted |
zenoss-2.5.1.spec
|
@@ -1,449 +0,0 @@
-
-#
-# This spec file defines the input constraints and build system for
-# Zenoss when it is installed onto an RPM based system.
-#
-# For information on what arguments are passed to %pre, %post, %preun,
-# and %postun see the following URL:
-# http://tinyurl.com/sjyfp
-#
-
-# the location where zenoss is installed
-%define zenhome /opt/zenoss
-
-# the location where zope is installed
-%define zopehome %{zenhome}
-
-# mysql is used for the events database
-%define mysql_username zenoss
-%define mysql_passwd zenoss
-%define mysql_database events
-
-# zope is used for the web interface
-%define zope_passwd zenoss
-
-# a shell account controls the zenoss processes
-%define os_username zenoss
-%define os_uid 1337
-%define os_home /home/zenoss
-%define os_shell /bin/bash
-
-# the version of python we require
-%define python_required_version 2.4
-
-# set to 1 if the version of the software to be built is the trunk
-# if trunk is set to 0 the version will be extrapolated from the
-# rpm information contained in the %{version} and %{release} vars
-%define trunk 0
-
-# the location of the snmp configuration file and mysql configuration
-%define snmpd_conf /etc/snmp/snmpd.conf
-%define my_cnf /etc/my.cnf
-
-# don't terminate the build when there are unpackaged (but installed) files
-%define _unpackaged_files_terminate_build 0
-
-# avoid advertising Zenoss as the provider of libs we package for internal consumption only (see #5354)
-%define __find_provides %{nil}
-
-%define pkgrelease 440.el5
-# the RPM meta information
-Name: zenoss
-Summary: The Open Source Network Management System
-URL: http://www.zenoss.com/
-Version: 2.5.1
-Release: %{pkgrelease}
-License: GPLv2
-Vendor: Zenoss, Inc.
-Packager: Glenn Streiff <glenn@zenoss.com>
-Group: Applications/System
-Source0: %{name}-%{version}-%{pkgrelease}.tar.bz2
-Requires(pre): shadow-utils
-BuildRequires: gcc-c++
-BuildRequires: gcc
-BuildRequires: subversion
-BuildRequires: zlib-devel
-BuildRequires: make
-BuildRequires: patch
-BuildRequires: autoconf >= 2.53
-BuildRequires: swig >= 1.3
-
-BuildRequires: which
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
-Requires: net-snmp
-Requires: binutils
-Requires: libgcj
-Requires: libgomp
-Requires: liberation-fonts
-# additional provides
-BuildRequires: mysql-devel >= 5.0.22
-BuildRequires: mysql >= 5.0.22
-Requires: mysql-server >= 5.0.22
-Requires: mysql >= 5.0.22
-Requires: python >= 2.3.4
-Requires: net-snmp-utils
-
-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-%description
-Zenoss is an IT infrastructure monitoring product that allows you to
-monitor your entire infrastructure within a single, integrated
-software application.
-
-Key features include:
- * Monitors the entire stack
- o networks, servers, applications, services, power, environment, etc...
- * Monitors across all perspectives
- o discovery, configuration, availability, performance, events, alerts, etc.
- * Affordable and easy to use
- o unlike the big suites offered by IBM, HP, BMC, CA, etc...
- o unlike first generation open source tools...
- * Complete open source package
- o complete solution available as free, open source software
-
-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-%prep
-
-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-%setup
-
-
-# changelog-section
-
-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-%build
-
-replace() {
- SEARCH=$1
- REPLACE=$2
- FILE=$3
- TEMP=/tmp/`basename $FILE`
-
- sed -e "s%${SEARCH}%${REPLACE}%g" < ${FILE} > ${TEMP}
- mv ${TEMP} ${FILE}
-}
-
-# clean up the old build if it is still laying around
-rm -rf ${RPM_BUILD_ROOT}
-
-# map environment variables to .spec variables
-export ZENHOME=%{zenhome}
-export ZOPEPASSWORD=%{zope_passwd}
-export DESTDIR=${RPM_BUILD_ROOT}
-
-# load up the build functions
-ZEN_BUILD_DIR=${RPM_BUILD_DIR}/%{name}-%{version}
-. ${ZEN_BUILD_DIR}/build-functions.sh
-
-# set up make and the python path
-set_make
-
-# set the SVNTAG variable and honor the trunk flag
-if [ "%{trunk}" = "1" ]; then
- SVNTAG="trunk"
-else
- SVNTAG="tags/%{name}-%{version}"
-fi
-
-# do NOT setuid zensocket
-export SETUID=""
-
-# set the BUILD_DIR variable in order to optimize the RPM assembly process
-export OPTIMIZED_BUILD_DIR=/tmp/zenoss-externallibs
-
-# optionally set the PLATFORM_PYTHON if this platform runs python 2.4
-PLATFORM_PYTHON=""
-for TEST_PYTHON in \
- `which python%{python_required_version}` \
- `which python`
-do
- PLATFORM_PYTHON_VERSION=`${TEST_PYTHON} -c "import sys; print '.'.join(map(str, sys.version_info[:2]))"`
- echo "${TEST_PYTHON} - ${PLATFORM_PYTHON_VERSION}"
- if [ "${PLATFORM_PYTHON_VERSION}" = "%{python_required_version}" ]; then
- PLATFORM_PYTHON=${TEST_PYTHON}
- break
- fi
-done
-# But if /usr/bin/python is python2.3, we're probably on a box that ships with
-# python2.3, so we should build our own
-SYSPYTHON=`/usr/bin/python -c "import sys; print '.'.join(map(str, sys.version_info[:2]))"`
-if [ "$SYSPYTHON" != "%{python_required_version}" ]; then
- PLATFORM_PYTHON=""
-fi
-find_python
-
-# compile external libs and pull down zenoss
-compile || cat zenbuild.log
-
-# remove the nagios-plugins perl scripts that force ugly deps
-DIR=${RPM_BUILD_ROOT}/${ZENHOME}/libexec
-DIRNAME=${RPM_BUILD_ROOT}/${ZENHOME}/libexec
-
-for file in \
- `grep perl $DIRNAME/* | awk '{print $1}' | cut -d: -f1 | sort -u`
-do
- rm -f $file
-done
-
-
-# remove the CM artifacts
-ZEN_INST_DIR=${RPM_BUILD_ROOT}/${ZENHOME}
-find ${ZEN_INST_DIR} -name .svn | xargs rm -rf
-
-# remove the .conf files but leave the .conf.examples
-rm ${ZEN_INST_DIR}/etc/*.conf
-
-# create the .manifest file
|
[-]
[+]
|
Added |
zenoss.spec
^
|
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/GNUmakefile
^
|
@@ -610,7 +610,7 @@
$(DESTDIR)$(PYLIBDIR)/sitecustomize.py:
@mkdir -p $(@D)
- @echo "import sys, os, site; sys.setdefaultencoding('latin-1'); site.addsitedir(os.path.join(os.getenv('ZENHOME'), 'ZenPacks')); import warnings; warnings.filterwarnings('ignore', '.*', DeprecationWarning)" > $@
+ @echo "import sys, os, site; sys.setdefaultencoding('utf-8'); site.addsitedir(os.path.join(os.getenv('ZENHOME'), 'ZenPacks')); import warnings; warnings.filterwarnings('ignore', '.*', DeprecationWarning)" > $@
# optional installs
|
|
Added |
zenoss-2.5.2.tar.bz2/externallibs/sitecustomize-1.2.tar.gz
^
|
|
Added |
zenoss-2.5.2.tar.bz2/externallibs/wmi-1.3.6.tar.bz2
^
|
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products.tar.gz
^
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/.buildbot-sourcedata
^
|
@@ -1 +1 @@
-http://dev.zenoss.org/svn/tags/builds/zenoss-build-440/Products
+http://dev.zenoss.org/svn/tags/builds/zenoss-build-590/Products
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/ApplyDataMap.py
^
|
@@ -11,6 +11,7 @@
#
###########################################################################
+import sys
import types
import threading
import Queue
@@ -281,15 +282,28 @@
self.logEvent(device, obj,Change_Set_Blocked,msg,Event.Warning)
return changed
for attname, value in objmap.items():
- if type(value) == type(''):
+ if attname.startswith('_'):
+ continue
+ if isinstance(value, basestring):
try:
- value.encode('ascii')
- except UnicodeEncodeError:
- decoding = obj.zCollectorDecoding
- value = value.decode(decoding)
+ # This looks confusing, and it is. The scenario is:
+ # A collector gathers some data as a raw byte stream,
+ # but really it has a specific encoding specified by
+ # by the zCollectorDecoding zProperty. Say, latin-1 or
+ # utf-16, etc. We need to decode that byte stream to get
+ # back a UnicodeString object. But, this version of Zope
+ # doesn't like UnicodeString objects for a variety of
+ # fields, such as object ids, so we then need to convert
+ # that UnicodeString back into a regular string of bytes,
+ # and for that we use the system default encoding, which
+ # is now utf-8.
+ codec = obj.zCollectorDecoding or sys.getdefaultencoding()
+ value = value.decode(codec)
+ value = value.encode(sys.getdefaultencoding())
except UnicodeDecodeError:
+ # We don't know what to do with this, so don't set the
+ # value
continue
- if attname[0] == '_': continue
att = getattr(aq_base(obj), attname, zenmarker)
if att == zenmarker:
log.warn('The attribute %s was not found on object %s from device %s',
@@ -299,9 +313,9 @@
setter = getattr(obj, attname)
gettername = attname.replace("set","get")
getter = getattr(obj, gettername, None)
-
+
if not getter:
-
+
log.warn("getter '%s' not found on obj '%s', "
"skipping", gettername, obj.id)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/SnmpClient.py
^
|
@@ -116,8 +116,11 @@
self._tabledata[pname] = {}
log.debug("sending queries for plugin %s", pname)
if plugin.snmpGetMap:
- yield self.proxy.get(plugin.snmpGetMap.getoids())
- self._getdata[pname] = driver.next()
+ results = {}
+ for oid in plugin.snmpGetMap.getoids():
+ yield self.proxy.get([oid])
+ results.update(driver.next())
+ self._getdata[pname] = results
for tmap in plugin.snmpGetTableMaps:
rowSize = len(tmap.getoids())
maxRepetitions = max(DEFAULT_MAX_OIDS_BACK / rowSize, 1)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/TelnetClient.py
^
|
@@ -24,6 +24,8 @@
zTelnetCommandTimeout - default timeout when executing a command default: 5
zTelnetLoginRegex - regex to match the login prompt default: 'ogin:.$'
zTelnetPasswordRegex - regext to match the password prompt default: 'assword:.$'
+zTelnetEnable - should enable mode should be entered: default False
+zTelnetEnableRegex - regext to match the enable prompt default: 'assword:.$'
Other Parameters that are used by both TelnetClient and SshClient:
zCommandPathList - list of path to check for a command
@@ -56,6 +58,8 @@
defaultLoginRegex = 'ogin:.$'
defaultPasswordRegex = 'assword:'
defaultEnable = False
+defaultEnableRegex = 'assword:'
+defaultEnablePassword = ''
defaultTermLength = False
responseMap = ("WILL", "WONT", "DO", "DONT")
@@ -112,6 +116,10 @@
self.startTimeout(self.factory.loginTimeout, self.loginTimeout)
self.protocol = telnet.TelnetProtocol()
+ if not self.factory.username:
+ # It's possible to go straight to the password prompt.
+ self.mode = 'Password'
+
def iac_DO(self, feature):
"""
@@ -355,10 +363,10 @@
"""
log.debug('Search for password regex (%s) in (%s) finds: %r' % \
(self.factory.passwordRegex, data, \
- re.search(self.factory.loginRegex, data)))
+ re.search(self.factory.passwordRegex, data)))
if not re.search(self.factory.passwordRegex, data): # look for pw prompt
return 'Password'
- log.debug("Sending password %s" % self.factory.password)
+ log.debug("Sending password")
self.write(self.factory.password + '\n')
self.startTimeout(self.factory.promptTimeout)
return 'FindPrompt'
@@ -375,7 +383,31 @@
"""
self.write('enable\n')
self.startTimeout(self.factory.loginTimeout, self.loginTimeout)
- return "Password"
+ return "EnablePassword"
+
+
+ def telnet_EnablePassword(self, data):
+ """
+ Called when the enable password prompt is expected
+
+ @param data: data sent back from the remote device
+ @type data: string
+ @return: next state (EnablePassword, FindPrompt)
+ @rtype: string
+ """
+ log.debug('Search for enable password regex (%s) in (%s) finds: %r' % \
+ (self.factory.enableRegex, data, \
+ re.search(self.factory.enableRegex, data)))
+ if not re.search(self.factory.enableRegex, data):
+ return 'EnablePassword'
+
+ # Use password if enable password is blank for backwards compatibility.
+ password = self.factory.enablePassword or self.factory.password
+
+ log.debug("Sending enable password")
+ self.write(password + '\n')
+ self.startTimeout(self.factory.promptTimeout)
+ return 'FindPrompt'
def telnet_FindPrompt(self, data):
@@ -533,6 +565,8 @@
defaultLoginRegex = options.loginRegex
defaultPasswordRegex = options.passwordRegex
defaultEnable = options.enable
+ defaultEnableRegex = options.enableRegex
+ defaultEnablePassword = options.enablePassword
if device: # if we are in Zope look for zProperties
self.promptTimeout = getattr(device,
@@ -543,6 +577,10 @@
'zTelnetPasswordRegex', defaultPasswordRegex)
self.enable = getattr(device,
'zTelnetEnable', defaultEnable)
+ self.enableRegex = getattr(device,
+ 'zTelnetEnableRegex', defaultEnableRegex)
+ self.enablePassword = getattr(device,
+ 'zEnablePassword', defaultEnablePassword)
self.termlen = getattr(device,
'zTelnetTermLength', defaultTermLength)
@@ -551,6 +589,8 @@
self.loginRegex = defaultLoginRegex
self.passwordRegex = defaultPasswordRegex
self.enable = defaultEnable
+ self.enableRegex = defaultEnableRegex
+ self.enablePassword = defaultEnablePassword
self.termlen = defaultTermLength
self.modeRegex['Login'] = self.loginRegex
@@ -562,7 +602,12 @@
Start telnet collection.
"""
if self.termlen:
+ # Cisco ASA
+ self._commands.insert(0, "terminal pager 0")
+
+ # Cisco IOS
self._commands.insert(0, "terminal length 0")
+
reactor.connectTCP(self.ip, self.port, self)
@@ -616,6 +661,14 @@
parser.add_option('--enable',
dest='enable', action='store_true', default=False,
help="Enter 'enable' mode on a Cisco device")
+ parser.add_option('--enableRegex',
+ dest='enableRegex',
+ default=defaultEnableRegex,
+ help='Python regex that will find the enable prompt')
+ parser.add_option('--enablePassword',
+ dest='enablePassword',
+ default=defaultEnablePassword,
+ help='Enable password')
parser.add_option('--termlen',
dest='termlen', action='store_true', default=False,
help="Enter 'send terminal length 0' on a Cisco device")
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/plugins/CollectorPlugin.py
^
|
@@ -184,6 +184,9 @@
'zTelnetPasswordRegex',
'zTelnetSuccessRegexList',
'zTelnetTermLength',
+ 'zTelnetEnable',
+ 'zTelnetEnableRegex',
+ 'zEnablePassword',
)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/plugins/zenoss/cmd/linux/netstat_an.py
^
|
@@ -12,7 +12,7 @@
###########################################################################
__doc__ = """netstat_an
-Collect running ip services using netstat -an on a linux box.
+Collect running IP services using netstat -a
"""
from Products.DataCollector.plugins.CollectorPlugin import LinuxCommandPlugin
@@ -23,19 +23,32 @@
compname = "os"
relname = "ipservices"
modname = "Products.ZenModel.IpService"
-
-
+ deviceProperties = LinuxCommandPlugin.deviceProperties + (
+ 'zIpServiceMapMaxPort', )
+
+
def process(self, device, results, log):
- log.info('Collecting Ip Services for device %s' % device.id)
+ log.info('Modeler %s processing data for device %s', self.name(), device.id)
+
+ if not results.strip(): # No output
+ log.error("No output from the command: %s", self.command)
+ return
+
+ try:
+ maxport = getattr(device, 'zIpServiceMapMaxPort', 1024)
+ maxport = int(maxport)
+ except ValueError:
+ maxport = 1024
+
rm = self.relMap()
- rlines = results.split("\n")
- services = {}
- # normalize on address 0.0.0.0 means all addresses
- for line in rlines:
+ ports = {}
+ for line in results.split("\n"):
aline = line.split()
if len(aline) < 5: continue
try:
proto = aline[0]
+ if proto == "raw":
+ continue
listar = aline[3].split(":")
addr = port = ""
if len(listar) == 2:
@@ -43,26 +56,42 @@
elif len(listar) == 4:
addr = "0.0.0.0"
port = listar[-1]
- if not port: continue
- if addr == "0.0.0.0" or not services.has_key(port):
- services[port] = (addr, proto)
+ else:
+ continue
+
+ log.debug("Got %s %s port %s", addr, proto, port)
+ if addr == "127.0.0.1" or not port: # Can't monitor things we can't reach
+ continue
+
+ port = int(port)
+ if port > maxport:
+ log.debug("Ignoring entry greater than zIpServiceMapMaxPort (%s): %s %s %s",
+ maxport, addr, proto, port)
+ continue
except ValueError:
- log.exception("failed to parse ipservice information")
- ports = {}
- for port, value in services.items():
- addr, proto = value
- if proto == "raw": continue
+ log.exception("Failed to parse IPService information '%s'",
+ line)
+ continue
+
om = ports.get((proto, port), None)
if om:
+ if addr in om.ipaddresses:
+ continue
+ log.debug("Adding %s to the list of addresses listening to %s port %s",
+ addr, proto, port)
om.ipaddresses.append(addr)
else:
om = self.objectMap()
om.protocol = proto
om.port = int(port)
om.id = '%s_%05d' % (om.protocol,om.port)
+ log.debug("Found %s listening to %s port %s (%s)",
+ addr, proto, port, om.id)
om.setServiceClass = {'protocol': proto, 'port':om.port}
om.ipaddresses = [addr,]
om.discoveryAgent = self.name()
ports[(proto, port)] = om
rm.append(om)
+
+ log.debug("Found %d IPServices", len(ports.keys()))
return rm
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/plugins/zenoss/snmp/CiscoMap.py
^
|
@@ -1,7 +1,7 @@
###########################################################################
#
# This program is part of Zenoss Core, an open source monitoring platform.
-# Copyright (C) 2007, 2009, Zenoss Inc.
+# Copyright (C) 2007, 2009, 2010, Zenoss Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
@@ -13,12 +13,15 @@
__doc__ = """CiscoMap
-CiscoMap maps cisco serialnumber information
+Models Cisco device attributes.
+ * Serial Number
+ * Total Memory
"""
import re
import sys
+from Products.DataCollector.plugins.DataMaps import ObjectMap
from Products.DataCollector.plugins.CollectorPlugin \
import SnmpPlugin, GetMap, GetTableMap
@@ -27,8 +30,11 @@
maptype = "CiscoDeviceMap"
snmpGetMap = GetMap({
- '.1.3.6.1.4.1.9.3.6.3.0' : 'setHWSerialNumber',
- })
+ '.1.3.6.1.2.1.1.2.0': 'snmpOid',
+ '.1.3.6.1.4.1.9.3.6.3.0': '_serialNumber',
+ '.1.3.6.1.4.1.9.9.48.1.1.1.5.1': '_memUsed',
+ '.1.3.6.1.4.1.9.9.48.1.1.1.6.1': '_memFree',
+ })
snmpGetTableMaps = (
GetTableMap('entPhysicalTable', '.1.3.6.1.2.1.47.1.1.1.1', {
@@ -59,26 +65,56 @@
r'^(CISCO|C|Cat|CAT)?\d{4}[A-Z](-\d{0,3})?$',
]
+ snmpOidPreferEntity = [
+ '.1.3.6.1.4.1.9.1.525', # ciscoAIRAP1210
+ ]
+
def process(self, device, results, log):
"""collect snmp information from this device"""
log.info('processing %s for device %s', self.name(), device.id)
getdata, tabledata = results
- om = self.objectMap(getdata)
+
+ maps = []
+
+ serialNumber = self.getSerialNumber(getdata, tabledata)
+ if serialNumber is not None:
+ maps.append(ObjectMap({'setHWSerialNumber': serialNumber}))
+
+ totalMemory = self.getTotalMemory(getdata)
+ if totalMemory is not None:
+ maps.append(ObjectMap({'totalMemory': totalMemory}, compname='hw'))
+
+ return maps
+
+
+ def getSerialNumber(self, getdata, tabledata):
+ serialNumber = getdata.get('_serialNumber', None)
# In most cases we want to prefer the serial number in the Cisco
# enterprise MIB if it is available.
- if getattr(om, 'setHWSerialNumber', False):
+ preferEntityMib = False
+ if getdata.get('_snmpOid', None) in self.snmpOidPreferEntity:
+ preferEntityMib = True
+
+ if serialNumber and not preferEntityMib:
# If there's a space in the serial we only want the first part.
- om.setHWSerialNumber = om.setHWSerialNumber.split(' ', 1)[0]
+ serialNumber = serialNumber.split(' ', 1)[0]
+
+ # There are Cisco devices out there that return invalid serial
+ # numbers with non-ASCII characters. Note them.
+ try:
+ unused = serialNumber.encode('ascii')
+ except (UnicodeEncodeError, UnicodeDecodeError):
+ serialNumber = 'Invalid'
# Some Cisco devices return a bogus serial. Ignore them.
for pattern in self.badSerialPatterns:
- if re.match(pattern, om.setHWSerialNumber):
+ if re.match(pattern, serialNumber):
break
else:
- return om
+ return serialNumber
# Some Cisco devices expose their serial number via the ENTITY-MIB.
entPhysicalTable = tabledata.get('entPhysicalTable', {})
@@ -87,13 +123,18 @@
for index, entry in entPhysicalTable.items():
serialNum = entry.get('serialNum', None)
if serialNum and int(index) < lowestIndex:
- om.setHWSerialNumber = serialNum
+ serialNumber = serialNum
lowestIndex = int(index)
# Return the serial number if we found one. Otherwise don't overwrite
# a value provided by another modeler plugin.
- if getattr(om, 'setHWSerialNumber', False):
- return om
- else:
- return None
+ return serialNumber
+
+
+ def getTotalMemory(self, getdata):
+ used = getdata.get('_memUsed', None)
+ free = getdata.get('_memFree', None)
+ if used is not None and free is not None:
+ return used + free
+ return None
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/plugins/zenoss/snmp/InterfaceMap.py
^
|
@@ -84,14 +84,15 @@
log.debug( "%s tabledata = %s" % (device.id,tabledata) )
rm = self.relMap()
iptable = tabledata.get("ipAddrTable")
+ sourceTable = 'ipAddrTable'
if not iptable:
iptable = tabledata.get("ipNetToMediaTable")
if iptable:
log.info("Unable to use ipAddrTable -- using ipNetToMediaTable instead")
+ sourceTable = 'ipNetToMediaTable'
else:
- log.error("Unable to get data for %s from either ipAddrTable or"
- " ipNetToMediaTable -- skipping model" % device.id)
- return None
+ log.warn("Unable to get data for %s from either ipAddrTable or"
+ " ipNetToMediaTable" % device.id)
iftable = tabledata.get("iftable")
if iftable is None:
@@ -105,34 +106,43 @@
omtable = {}
for ip, row in iptable.items():
#FIXME - not getting ifindex back from HP printer
- if not row.has_key("ifindex"):
+ if 'ifindex' not in row:
log.debug( "IP entry for %s is missing ifindex" % ip)
continue
- # Fix data up if it is from the ipNetToMediaTable.
- if len(ip.split('.')) == 5:
+ ip_parts = ip.split('.')
+ # If the ipAddrTable key has five octets, that probably
+ # means this is a classless subnet (that is, <256). Usually,
+ # the first 4 octets will be the ipAddress we care about.
+ # Regardless, we will be using the ip address in the row
+ # later anyway.
+ if len(ip_parts) == 5 and sourceTable == 'ipAddrTable':
+ ip = '.'.join(ip_parts[:-1])
+ # If we are using the ipNetToMediaTable, we use the
+ # last 4 octets.
+ elif len(ip_parts) == 5 and sourceTable == 'ipNetToMediaTable':
if row['iptype'] != 1:
log.debug("iptype (%s) is not 1 -- skipping" % (
row['iptype'] ))
continue
- ip = '.'.join(ip.split('.')[1:])
+ ip = '.'.join(ip_parts[1:])
log.warn("Can't find netmask -- using /24")
row['netmask'] = '255.255.255.0'
strindex = str(row['ifindex'])
- if not omtable.has_key(strindex) and not iftable.has_key(strindex):
+ if strindex not in omtable and strindex not in iftable:
log.warn("Skipping %s as it points to missing ifindex %s",
row.get('ipAddress',""), row.get('ifindex',""))
continue
- if not omtable.has_key(strindex):
+ if strindex not in omtable:
om = self.processInt(log, device, iftable[strindex])
if not om:
continue
rm.append(om)
omtable[strindex] = om
del iftable[strindex]
- elif omtable.has_key(strindex):
+ elif strindex in omtable:
om = omtable[strindex]
else:
log.warn("The IP %s points to missing ifindex %s -- skipping" % (
@@ -141,9 +151,9 @@
if not hasattr(om, 'setIpAddresses'):
om.setIpAddresses = []
- if row.has_key('ipAddress'):
+ if 'ipAddress' in row:
ip = row['ipAddress']
- if row.has_key('netmask'):
+ if 'netmask' in row:
ip = ip + "/" + str(self.maskToBits(row['netmask'].strip()))
# Ignore IP addresses with a 0.0.0.0 netmask.
@@ -166,7 +176,7 @@
"""
for ifidx, data in ifalias.items():
log.debug( "ifalias %s raw data = %s" % (ifidx,data) )
- if not iftable.has_key(ifidx):
+ if ifidx not in iftable:
log.debug( "ifidx %s is not in iftable -- skipping" % (
ifidx))
continue
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/plugins/zenoss/snmp/PhysicalEntityMap.py
^
|
@@ -0,0 +1,111 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2007, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+__doc__ = """PhysicalEntityMap
+Use ENTITY-MIB to determine physical entities as expansion cards
+"""
+
+from Products.DataCollector.plugins.CollectorPlugin import SnmpPlugin, \
+ GetTableMap
+from Products.DataCollector.plugins.DataMaps import MultiArgs
+
+class Card(object):
+
+ _CARDS = set()
+
+ @classmethod
+ def getAllCards(cls):
+ return tuple(cls._CARDS)
+
+ @property
+ def slot(self):
+ if not self.parent:
+ return str(self._slot)
+ for card in Card._CARDS:
+ if card.index == self.parent:
+ return "%s.%d" % (card.slot, self._slot)
+ raise ValueError("No parent exists!")
+
+ def __init__(self, i, index=None, descr="", parent=0, slot=0, name="", serial="", manuf="", model=""):
+ # Required values
+ self.index = index is None and i or index
+ self.name = name or descr or str(self.index)
+ self.snmpindex = i
+
+ # Optional values
+ self.parent = slot >= 0 and parent or None
+ self.serial = serial
+ self.manuf = manuf
+ self.model = model
+ self._slot = slot >= 0 and slot or 0
+
+ self.__class__._CARDS.add(self)
+
+ def toOM(self, obj):
+ om = obj.objectMap()
+ om.id = obj.prepId(self.name)
+ om.snmpindex = self.snmpindex
+ om.slot = self.slot
+ om.serialNumber = self.serial
+ if self.manuf and self.model:
+ om.setProductKey = MultiArgs(self.model, self.manuf)
+ return om
+
+ def __lt__(self, other):
+ return map(int, self.slot.split('.')) < map(int, other.slot.split('.'))
+
+ def __gt__(self, other):
+ return map(int, self.slot.split('.')) > map(int, other.slot.split('.'))
+
+ def __eq__(self, other):
+ return self.slot == other.slot
+
+class PhysicalEntityMap(SnmpPlugin):
+ """Map Physical Entities to model."""
+
+ maptype = "PhysicalEntityMap"
+ modname = "Products.ZenModel.ExpansionCard"
+ relname = "cards"
+ compname = "hw"
+
+ columns = {
+ '.1': 'index',
+ '.2': 'descr',
+ '.4': 'parent',
+ '.6': 'slot',
+ '.7': 'name',
+ '.11': 'serial',
+ '.12': 'manuf',
+ '.13': 'model',
+ }
+
+ snmpGetTableMaps = (
+ GetTableMap('peTable', '.1.3.6.1.2.1.47.1.1.1.1', columns),
+ )
+
+ def process(self, device, results, log):
+ """collect snmp information from this device"""
+ log.info('processing %s for device %s', self.name(), device.id)
+ getdata, tabledata = results
+ petable = tabledata.get("peTable")
+ if not petable: return
+
+ # Process all of the cards
+ for i,c in petable.iteritems():
+ Card(int(i), **c)
+
+ # Create the maps
+ rm = self.relMap()
+ for card in sorted(Card.getAllCards()):
+ rm.append(card.toOM(self))
+ return rm
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/tests/testApplyDataMap.py
^
|
@@ -36,10 +36,17 @@
_p_changed = False
ascii_objmap = { 'a': 'abcdefg', 'b': 'hijklmn', 'c': 'opqrstu' }
-utf8_objmap = { 'a': u'\xe0', 'b': u'\xe0', 'c': u'\xe0' }
+utf8_objmap = { 'a': u'\xe0'.encode('utf-8'),
+ 'b': u'\xe0'.encode('utf-8'),
+ 'c': u'\xe0'.encode('utf-8') }
latin1_objmap = { 'a': u'\xe0'.encode('latin-1'),
'b': u'\xe0'.encode('latin-1'),
'c': u'\xe0'.encode('latin-1') }
+utf16_objmap = { 'a': u'\xff\xfeabcdef'.encode('utf-16'),
+ 'b': u'\xff\xfexyzwow'.encode('utf-16'),
+ # "水z𝄞" (water, z, G clef), UTF-16 encoded,
+ # little-endian with BOM
+ 'c': '\xff\xfe\x34\x6c\x7a\x00\x34\xd8\x13\xdd' }
class ApplyDataMapTest(unittest.TestCase):
@@ -47,13 +54,13 @@
self.adm = ApplyDataMap()
def test_updateObject_encoding(self):
- for enc in ('ascii', 'latin-1', 'utf-8'):
+ for enc in ('ascii', 'latin-1', 'utf-8', 'utf-16'):
obj = _obj()
obj.zCollectorDecoding = enc
objmap = eval(enc.replace('-','')+'_objmap')
self.adm._updateObject(obj, objmap)
for key in objmap:
- self.assertEqual(getattr(obj, key), objmap[key])
+ self.assertEqual(getattr(obj, key), objmap[key].decode(enc))
def test_suite():
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/tests/testCiscoMap.py
^
|
@@ -0,0 +1,53 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+import logging
+from cPickle import loads
+
+from Products.ZenTestCase.BaseTestCase import BaseTestCase
+from Products.DataCollector.ApplyDataMap import ApplyDataMap
+from Products.DataCollector.plugins.zenoss.snmp.CiscoMap import CiscoMap
+
+
+log = logging.getLogger("zen.testcases")
+
+
+class TestCiscoMap(BaseTestCase):
+ def setUp(self):
+ BaseTestCase.setUp(self)
+ self.adm = ApplyDataMap()
+ self.cmap = CiscoMap()
+ self.device = self.dmd.Devices.createInstance('testDevice')
+
+
+ def testNonAsciiSerial(self):
+ results = loads("((dp0\nS'_serialNumber'\np1\nS'\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\np2\nsS'_memFree'\np3\nL690209880L\nsS'_memUsed'\np4\nL136548740L\nsS'snmpOid'\np5\nS'.1.3.6.1.4.1.9.1.222'\np6\ns(dp7\nS'entPhysicalTable'\np8\n(dp9\nstp10\n.")
+
+ # Verify that the modeler plugin processes the data properly.
+ om = self.cmap.process(self.device, results, log)[0]
+ self.assertEquals(om.setHWSerialNumber, 'Invalid')
+
+
+ def testTotalMemory(self):
+ results = loads("((dp0\nS'_serialNumber'\np1\nS'\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\np2\nsS'_memFree'\np3\nL690209880L\nsS'_memUsed'\np4\nL136548740L\nsS'snmpOid'\np5\nS'.1.3.6.1.4.1.9.1.222'\np6\ns(dp7\nS'entPhysicalTable'\np8\n(dp9\nstp10\n.")
+
+ om = self.cmap.process(self.device, results, log)[1]
+ self.assertEquals(om.compname, 'hw')
+ self.assertEquals(om.totalMemory, 826758620)
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestCiscoMap))
+ return suite
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/DataCollector/tests/testInterfaceMap.py
^
|
@@ -112,8 +112,13 @@
'ifalias': {'1': {'highSpeed': 0, 'description': ''}, '3': {'highSpeed': 0, 'description': ''}, '2': {'highSpeed': 0, 'description': ''}},
'iftable': {'1': {'adminStatus': 1, 'macaddress': '', 'operStatus': 1, 'speed': 10000000, 'mtu': 16436, 'ifindex': 1, 'type': 24, 'id': 'lo'},
'3': {'adminStatus': 2, 'macaddress': '', 'operStatus': 2, 'speed': 0, 'mtu': 1480, 'ifindex': 3, 'type': 131, 'id': 'sit0'},
- '2': {'adminStatus': 1, 'macaddress': '\x00\x0c\x8d\xfd\x22\xd3', 'operStatus': 1, 'speed': 10000000, 'mtu': 1500, 'ifindex': 2, 'type': 6, 'id': 'eth0'}},
- 'ipAddrTable': {'10.1.254.8': {'ifindex': 2, 'netmask': '255.255.128.0', 'ipAddress': '10.1.254.8'}, '127.0.0.1': {'ifindex': 1, 'netmask': '255.0.0.0', 'ipAddress': '127.0.0.1'}},
+ '2': {'adminStatus': 1, 'macaddress': '\x00\x0c\x8d\xfd\x22\xd3', 'operStatus': 1, 'speed': 10000000, 'mtu': 1500, 'ifindex': 2, 'type': 6, 'id': 'eth0'},
+ '4': {'adminStatus': 1, 'macaddress': '', 'operStatus': 1, 'speed': 10000000, 'mtu': 16436, 'ifindex': 4, 'type': 6, 'id': 'vlan1'},
+ '5': {'adminStatus': 1, 'macaddress': '', 'operStatus': 1, 'speed': 10000000, 'mtu': 16436, 'ifindex': 5, 'type': 6, 'id': 'vlan2'}},
+ 'ipAddrTable': {'10.1.254.8': {'ifindex': 2, 'netmask': '255.255.128.0', 'ipAddress': '10.1.254.8'},
+ '127.0.0.1': {'ifindex': 1, 'netmask': '255.0.0.0', 'ipAddress': '127.0.0.1'},
+ '119.120.121.170.1':{'ifindex': 4, 'netmask': '255.255.255.248', 'ipAddress': '119.120.121.170'},
+ '122.123.124.170.1':{'ifindex': 5, 'netmask': '255.255.255.248'}},
'ipNetToMediaTable': {
'2.10.1.254.107': {'ifindex': 2, 'ipaddress': '10.1.254.107', 'iptype': 3},
'2.10.1.254.254': {'ifindex': 2, 'ipaddress': '10.1.254.254', 'iptype': 3},
@@ -149,6 +154,20 @@
'mtu':1480,
'interfaceName':'sit0',
'type':'Encapsulation Interface', },
+ 4: {
+ 'id':'vlan1',
+ 'speed':10000000,
+ 'mtu':16436,
+ 'interfaceName':'vlan1',
+ 'setIpAddresses':['119.120.121.170/29'],
+ 'type':'ethernetCsmacd', },
+ 5: {
+ 'id':'vlan2',
+ 'speed':10000000,
+ 'mtu':16436,
+ 'interfaceName':'vlan2',
+ 'setIpAddresses':['122.123.124.170/29'],
+ 'type':'ethernetCsmacd', },
}
for om in relmap:
@@ -156,6 +175,54 @@
for attr in parsed_data[index].keys():
self.assertEquals( getattr(om, attr), parsed_data[index][attr] )
+ def testNoIpInterfaces(self):
+ tabledata = {
+ 'ifalias': {
+ '1': {'highSpeed': 2000L, 'description': '', 'ifHCInUcastPkts': 483106528092L, 'ifHCInOctets': 123411643670667L},
+ '2': {'highSpeed': 0L, 'description': '', 'ifHCInUcastPkts': 9531554L, 'ifHCInOctets': 1211994446L},
+ '3': {'highSpeed': 2000L, 'description': '', 'ifHCInUcastPkts': 506823808298L, 'ifHCInOctets': 561671987287258L},
+ '4': {'highSpeed': 1000L, 'description': '', 'ifHCInUcastPkts': 253321260579L, 'ifHCInOctets': 280765628338066L},
+ },
+ 'iftable': {
+ '1': {'adminStatus': 1, 'macaddress': '\x02\xd0h\x18U\xe1', 'operStatus': 1, 'speed': 2000000000L, 'mtu': 1500, 'ifindex': 1, 'type': 6, 'id': 'LA/1'},
+ '2': {'adminStatus': 1, 'macaddress': '\x00\xd0h\x18U\xdf', 'operStatus': 1, 'speed': 0L, 'mtu': 1500, 'ifindex': 2, 'type': 6, 'id': 'LO/1'},
+ '3': {'adminStatus': 1, 'macaddress': '\x02\xd0h\x18U\xe2', 'operStatus': 1, 'speed': 2000000000L, 'mtu': 1500, 'ifindex': 3, 'type': 6, 'id': 'LA/2'},
+ '4': {'adminStatus': 1, 'macaddress': '\x00\xd0h\x18U\xe1', 'operStatus': 1, 'speed': 1000000000L, 'mtu': 1500, 'ifindex': 4, 'type': 6, 'id': '1/3'},
+ },
+ 'ipAddrTable': {},
+ 'ipNetToMediaTable': {}
+ }
+ results = ('ignored', tabledata)
+ logging.disable(logging.WARN)
+ relmap = InterfaceMap().process(self.device, results, log)
+
+ parsed_data = {
+ 1: {
+ 'id': 'LA_1', 'interfaceName': 'LA/1', 'title': 'LA/1',
+ 'type': 'ethernetCsmacd_64', 'macaddress': '02:D0:68:18:55:E1',
+ 'setIpAddresses': [], 'speed': 2000000000L, 'mtu': 1500,
+ },
+ 2: {
+ 'id': 'LO_1', 'interfaceName': 'LO/1', 'title': 'LO/1',
+ 'type': 'ethernetCsmacd_64', 'macaddress': '00:D0:68:18:55:DF',
+ 'setIpAddresses': [], 'speed': 0L, 'mtu': 1500,
+ },
+ 3: {
+ 'id': 'LA_2', 'interfaceName': 'LA/2', 'title': 'LA/2',
+ 'type': 'ethernetCsmacd_64', 'macaddress': '02:D0:68:18:55:E2',
+ 'setIpAddresses': [], 'speed': 2000000000L, 'mtu': 1500,
+ },
+ 4: {
+ 'id': '1_3', 'interfaceName': '1/3', 'title': '1/3',
+ 'type': 'ethernetCsmacd_64', 'macaddress': '00:D0:68:18:55:E1',
+ 'setIpAddresses': [], 'speed': 1000000000L, 'mtu': 1500,
+ },
+ }
+
+ for om in relmap:
+ index = om.ifindex
+ for attr in parsed_data[index].keys():
+ self.assertEquals( getattr(om, attr), parsed_data[index][attr] )
def test_suite():
from unittest import TestSuite, makeSuite
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenCollector/daemon.py
^
|
@@ -410,11 +410,15 @@
return result
def _reschedule(result):
+ if isinstance( result, Failure ):
+ self.log.debug( "Rescheduling next configuration check " +
+ "despite failure: %s" %
+ result.getErrorMessage() )
interval = _configCycleInterval()
self.log.debug("Rescheduling configuration check in %d seconds",
interval)
reactor.callLater(interval, self._configCycle)
- return result
+ return defer.succeed(None)
def _configure():
devices = []
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/Availability.py
^
|
@@ -43,7 +43,12 @@
self.device = device
self.systems = systems
self.component = component
- self.availability = max(0, 1 - (downtime / total))
+
+ # Guard against endDate being equal to or less than startDate.
+ if total <= 0:
+ self.availability = downtime and 0 or 1
+ else:
+ self.availability = max(0, 1 - (downtime / total))
def floatStr(self):
return '%2.3f%%' % (self.availability * 100)
@@ -91,18 +96,39 @@
eventClass=Status_Ping,
severity=5,
device=None,
- component=''):
+ component='',
+ prodState=1000,
+ manager=None,
+ agent=None,
+ DeviceClass=None,
+ Location=None,
+ System=None,
+ DeviceGroup=None,
+ DevicePriority=None,
+ monitor=None):
self.startDate = _round(startDate)
self.endDate = _round(endDate)
self.eventClass = eventClass
self.severity = severity
self.device = device
self.component = component
+ self.prodState = prodState
+ self.manager = manager
+ self.agent = agent
+ self.DeviceClass = DeviceClass
+ self.Location = Location
+ self.System = System
+ self.DeviceGroup = DeviceGroup
+ self.DevicePriority = DevicePriority
+ self.monitor = monitor
def tuple(self):
- return (self.startDate, self.endDate, self.eventClass,
- self.severity, self.device, self.component)
+ return (
+ self.startDate, self.endDate, self.eventClass, self.severity,
+ self.device, self.component, self.prodState, self.manager,
+ self.agent, self.DeviceClass, self.Location, self.System,
+ self.DeviceGroup, self.DevicePriority, self.monitor)
def __hash__(self):
return hash(self.tuple())
@@ -130,11 +156,27 @@
w += ' AND firstTime <= %(endDate)s '
w += ' AND firstTime != lastTime '
w += " AND eventClass = '%(eventClass)s' "
- w += " AND prodState >= 1000 "
+ w += " AND prodState >= %(prodState)s "
if self.device:
w += " AND device = '%(device)s' "
if self.component:
w += " AND component like '%%%(component)s%%' "
+ if self.manager is not None:
+ w += " AND manager = '%(manager)s' "
+ if self.agent is not None:
+ w += " AND agent = '%(agent)s' "
+ if self.DeviceClass is not None:
+ w += " AND DeviceClass = '%(DeviceClass)s' "
+ if self.Location is not None:
+ w += " AND Location = '%(Location)s' "
+ if self.System is not None:
+ w += " AND Systems LIKE '%%%(System)s%%' "
+ if self.DeviceGroup is not None:
+ w += " AND DeviceGroups LIKE '%%%(DeviceGroup)s%%' "
+ if self.DevicePriority is not None:
+ w += " AND DevicePriority = %(DevicePriority)s "
+ if self.monitor is not None:
+ w += " AND monitor = '%(monitor)s' "
env['w'] = w % env
s = ('SELECT %(cols)s FROM ( '
' SELECT %(cols)s FROM history %(w)s '
@@ -168,7 +210,64 @@
deviceList = [device]
devices.setdefault( (self.device, self.component), 0)
else:
- deviceList = [d for d in dmd.Devices.getSubDevices()]
+ deviceList = []
+ if not self.DeviceClass and not self.Location \
+ and not self.System and not self.DeviceGroup:
+ deviceList = dmd.Devices.getSubDevices()
+ else:
+ allDevices = {}
+ for d in dmd.Devices.getSubDevices():
+ allDevices[d.id] = d
+
+ deviceClassDevices = set()
+ if self.DeviceClass:
+ try:
+ org = dmd.Devices.getOrganizer(self.DeviceClass)
+ for d in org.getSubDevices():
+ deviceClassDevices.add(d.id)
+ except KeyError:
+ pass
+ else:
+ deviceClassDevices = set(allDevices.keys())
+
+ locationDevices = set()
+ if self.Location:
+ try:
+ org = dmd.Locations.getOrganizer(self.Location)
+ for d in org.getSubDevices():
+ locationDevices.add(d.id)
+ except KeyError:
+ pass
+ else:
+ locationDevices = set(allDevices.keys())
+
+ systemDevices = set()
+ if self.System:
+ try:
+ org = dmd.Systems.getOrganizer(self.System)
+ for d in org.getSubDevices():
+ systemDevices.add(d.id)
+ except KeyError:
+ pass
+ else:
+ systemDevices = set(allDevices.keys())
+
+ deviceGroupDevices = set()
+ if self.DeviceGroup:
+ try:
+ org = dmd.Groups.getOrganizer(self.DeviceGroup)
+ for d in org.getSubDevices():
+ deviceGroupDevices.add(d.id)
+ except KeyError:
+ pass
+ else:
+ deviceGroupDevices = set(allDevices.keys())
+
+ # Intersect all of the organizers.
+ for deviceId in (deviceClassDevices & locationDevices & \
+ systemDevices & deviceGroupDevices):
+ deviceList.append(allDevices[deviceId])
+
if not self.component:
for d in dmd.Devices.getSubDevices():
devices.setdefault( (d.id, self.component), 0)
@@ -176,15 +275,17 @@
result = []
for (d, c), v in devices.items():
dev = deviceLookup.get(d, None)
- sys = (dev and dev.getSystemNamesString()) or ''
+ if dev is None:
+ continue
+ sys = dev.getSystemNamesString()
result.append( Availability(d, c, v, total, sys) )
# add in the devices that have the component, but no events
if self.component:
for d in deviceList:
for c in d.getMonitoredComponents():
if c.name().find(self.component) >= 0:
- a = Availability(d.id, c.name(), 0, total,
- d.getSystemNamesString())
+ a = Availability(d.id, c.name(), 0, total,
+ d.getSystemNamesString())
result.append(a)
return result
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/EventManagerBase.py
^
|
@@ -363,7 +363,7 @@
**kwargs)
- def filteredWhere(self, where, filters, values = None):
+ def filteredWhere(self, where, filters, values):
"""
Create SQL representing conditions that match passed filters and append
it to a given where clause.
@@ -391,11 +391,16 @@
@param filters: Values for which to create filters (e.g.,
{'device':'^loc.*$', 'severity':[4, 5]})
@type filters: dict or JSON str representing dict
- @param values: if not none the returned where clause will be
- parameterized and the values will be populated with the values,
- if none the values will be in the where string
+ @param values: Clause will be parameterized and the values list will be
+ populated with the values for the query
@type values: list
"""
+
+ def toTimestamp(value):
+ timeStr = value.replace('T', ' ')
+ timeTuple = time.strptime(timeStr, '%Y-%m-%d %H:%M:%S')
+ timestamp = time.mktime(timeTuple)
+ return timestamp
queryValues = []
newwhere = ''
if filters is None: filters = {}
@@ -417,26 +422,22 @@
newwhere += ' and (%s REGEXP %%s) ' % (k,)
queryValues.append(v)
elif k=='firstTime':
- v = self.dateDB(v.replace('T', ' '))
+ v2 = toTimestamp(v)
newwhere += ' and %s >= %%s ' % (k,)
- queryValues.append(v)
+ queryValues.append(v2)
elif k=='lastTime':
- v = self.dateDB(v.replace('T', ' '))
- newwhere += ' and %s <= %%s ' % (k, )
- queryValues.append(v)
+ v2 = toTimestamp(v)
+ newwhere += ' and %s >= %%s ' % (k, )
+ queryValues.append(v2)
elif ftype=='multiselectmenu':
if isinstance(v, basestring): v = (v,)
sevstr = ' or '.join(['%s=%%s' % (k,) for s in v])
queryValues.extend(v)
newwhere += ' and (%s) ' % sevstr
- if values is not None:
- values.extend(queryValues)
- else:
- newwhere = newwhere % tuple(queryValues)
+ values.extend(queryValues)
return where + newwhere
-
- def getEventIDsFromRanges(self, context, sort, direction, start=None,
+ def getEventIDsFromRanges(self, context, orderby, start=None,
limit=None, filters=None, evids=None,
ranges=None, asof=None):
"""
@@ -462,7 +463,6 @@
getWhere = getattr(context, 'getWhere',
lambda:self.lookupManagedEntityWhere(context))
where = getWhere()
- where = self.filteredWhere(where, filters)
if asof:
where += (" and not (UNIX_TIMESTAMP(stateChange)>%s"
" and eventState=0)") % self.dateDB(asof)
@@ -471,14 +471,18 @@
if not ranges:
return evids
else:
- orderby = "%s %s, lastTime DESC" % (sort, direction)
# Iterate through the ranges, adding event IDs for each
events = []
for s,e in ranges:
+ #number of rows to get. ranges passed in seem to be inclusive
+ #and 0 based, so [100,100] really means one row, the 101st.
+ #mysql is not inclusive and is 1 based so needs to be translated
+ #to "limit 100, 1"
+ rows = e-s+1
events += self.getEventList(resultFields=('evid',),
where=where, orderby=orderby,
severity=-1, state=2, offset=s,
- rows=e)
+ rows=rows, filters=filters)
# Uniqueify the list of event IDs and return as a list
evids = set(e.evid for e in events) | set(evids)
@@ -657,20 +661,10 @@
orderby = self.defaultOrderby
if orderby:
#validate orderby is a valid field
- values = []
- for x in orderby.split(','):
- values.extend(x.split(' '))
- values = [x for x in values if x]
- log.debug("orderby is %s" % orderby)
- log.debug("values is %s" % values)
-
- for col in values:
- col = col.lower()
- if col not in ['desc','asc'] and col not in fieldList:
- raise ("order by value %s not valid" % col)
-
+ orderby = self._scrubOrderby(orderby)
select.append("order by")
select.append(orderby)
+
if rows:
#validate offset and rows are ints
int(offset)
@@ -687,6 +681,7 @@
conn = self.connect()
try:
curs = conn.cursor()
+ log.debug(select % tuple(paramValues))
curs.execute(select, paramValues)
retdata = []
# iterate through the data results and convert to python
@@ -1039,12 +1034,21 @@
def getAvailability(self, state, **kw):
import Availability
- for name in "device", "component", "eventClass", "systems", "severity":
+ allowedFilters = (
+ "device", "component", "eventClass", "systems", "severity",
+ "prodState", "manager", "agent", "DeviceClass", "Location",
+ "System", "DeviceGroup", "DevicePriority", "monitor")
+
+ for name in allowedFilters:
if hasattr(state, name):
kw.setdefault(name, getattr(state, name))
- for name in "startDate", "endDate":
- if hasattr(state, name):
- kw.setdefault(name, Time.ParseUSDate(getattr(state, name)))
+ if getattr(state, 'startDate', None) is not None:
+ kw.setdefault('startDate', Time.ParseUSDate(state.startDate))
+ if getattr(state, 'endDate', None) is not None:
+ # End date needs to be inclusive of events that occurred on that
+ # date. So we advance to the last second of the day.
+ kw.setdefault('endDate', Time.getEndOfDay(Time.ParseUSDate(
+ state.endDate)))
kw.setdefault('startDate',
time.time() - 60*60*24*self.defaultAvailabilityDays)
return Availability.query(self.dmd, **kw)
@@ -1326,6 +1330,34 @@
startdate = self.dateDB(startdate)
enddate = self.dateDB(enddate)
return startdate, enddate
+
+ def _scrubOrderby(self, orderby):
+ """
+ validates the syntax and columns for an orderby used on the status and
+ history tables. Also removes adjacent sorts of the same columns as an
+ optimization
+ @return: orderby by clause
+ @raise exception: if order by clause is invalid
+ """
+ sortValues = []
+ prevSortCol = None
+ fieldList = [x.lower() for x in self.getFieldList()]
+ log.debug("orderby is %s" % orderby)
+ for x in orderby.split(','):
+ col, dir = x.split()
+ col = col.lower()
+ dir = dir.upper()
+ if dir not in ['DESC','ASC'] or col not in fieldList:
+ raise ("order by value %s %s not valid" % (col, dir))
+ #remove adjacent sorts of same column;
+ if not prevSortCol or prevSortCol != col:
+ sortValues.append((col, dir))
+ prevSortCol = col
+
+ orderby = ', '.join([' '.join(x) for x in sortValues])
+
+ log.debug("final orderby is %s" % orderby)
+ return orderby
def getAvgFieldLength(self, fieldname):
conn = self.connect()
@@ -1714,13 +1746,15 @@
# We are abandoning this proc to do it's thing. or not. We don't
# want to block because we would delay user feedback on a device
# delete when this might take a while to perform.
- unused(proc)
if REQUEST:
messaging.IMessageSender(self).sendToBrowser(
'Events Deleted',
'Historical events have been deleted.'
)
return self.callZenScreen(REQUEST)
+ else:
+ # Maybe, just maybe, we might want to wait on the subprocess...
+ return proc
security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHeartbeat')
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/SyslogProcessing.py
^
|
@@ -18,14 +18,17 @@
import re
import logging
slog = logging.getLogger("zen.Syslog")
+import socket
import Globals
from Products.ZenEvents.syslog_h import *
from Products.ZenUtils.IpUtil import isip
-import socket
# Regular expressions that parse syslog tags from different sources
+# A tuple can also be specified, in which case the second item in the
+# tuple is a boolean which tells whether or not to keep the entry (default)
+# or to discard the entry and not create an event.
parsers = (
# generic mark
r"^(?P<summary>-- (?P<eventClassKey>MARK) --)",
@@ -45,6 +48,10 @@
# netscreen device msg
r"device_id=\S+\s+\[\S+\](?P<eventClassKey>\S+\d+):\s+(?P<summary>.*)\s+\((?P<originalTime>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\)",
+# NetApp
+# [deviceName: 10/100/1000/e1a:warning]: Client 10.0.0.101 (xid 4251521131) is trying to access an unexported mount (fileid 64, snapid 0, generation 6111516 and flags 0x0 on volume 0xc97d89a [No volume name available])
+r"^\[[^:]+: (?P<component>[^:]+)[^\]]+\]: (?P<summary>.*)",
+
# unix syslog with pid
r"(?P<component>\S+)\[(?P<pid>\d+)\]:\s*(?P<summary>.*)",
@@ -74,9 +81,12 @@
# compile regex parsers on load
compiledParsers = []
for regex in parsers:
+ keepEntry = True
+ if isinstance(regex, tuple):
+ regex, keepEntry = regex
try:
compiled = re.compile(regex)
- compiledParsers.append(compiled)
+ compiledParsers.append((compiled, keepEntry))
except:
pass
@@ -135,10 +145,11 @@
evt, msg = self.parseHEADER(evt, msg)
evt = self.parseTag(evt, msg)
- #rest of msg now in summary of event
- evt = self.buildEventClassKey(evt)
- evt['monitor'] = self.monitor
- self.sendEvent(evt)
+ if evt:
+ #rest of msg now in summary of event
+ evt = self.buildEventClassKey(evt)
+ evt['monitor'] = self.monitor
+ self.sendEvent(evt)
def parsePRI(self, evt, msg):
@@ -238,10 +249,14 @@
@type: dictionary
"""
slog.debug(msg)
- for parser in compiledParsers:
+ for parser, keepEntry in compiledParsers:
slog.debug("tag regex: %s", parser.pattern)
m = parser.search(msg)
- if not m: continue
+ if not m:
+ continue
+ elif not keepEntry:
+ slog.debug("Dropping syslog message due to parser rule.")
+ return None
slog.debug("tag match: %s", m.groupdict())
evt.update(m.groupdict())
break
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/tests/testEventMaintenance.py
^
|
@@ -0,0 +1,78 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+import time
+from Products.ZenTestCase.BaseTestCase import BaseTestCase
+from Products.ZenEvents.Exceptions import *
+
+class testEventMaintenance(BaseTestCase):
+
+ def _getEventCount(self, table):
+ """
+ Returns the number of rows in the specified table.
+ """
+ zem = self.dmd.ZenEventManager
+ conn = zem.connect()
+ count = None
+ try:
+ curs = conn.cursor()
+ curs.execute('SELECT COUNT(*) FROM %s' % table)
+ while 1:
+ rows = curs.fetchmany()
+ if not rows: break
+ count = rows[0][0]
+ finally:
+ zem.close(conn)
+
+ return count
+
+
+ def testDeleteHistory(self):
+ zem = self.dmd.ZenEventManager
+
+ # Get initial row counts for comparison purposes.
+ history_count = self._getEventCount('history')
+ detail_count = self._getEventCount('detail')
+ log_count = self._getEventCount('log')
+
+ # Create old event with detail and log.
+ evid = zem.sendEvent(dict(
+ rcvtime=time.time() - (86400 * 7),
+ device='testDevice',
+ summary='test summary',
+ severity=2,
+ nonStandardField='test detail',
+ ))
+ zem.manage_deleteEvents((evid,))
+
+ # Verify that the event, detail and log were added properly.
+ self.assertEquals(history_count + 1, self._getEventCount('history'))
+ self.assertEquals(detail_count + 1, self._getEventCount('detail'))
+ self.assertEquals(log_count + 1, self._getEventCount('log'))
+
+ # This call doesn't block, but we can wait for the subprocess
+ # to finish.
+ proc = zem.manage_deleteHistoricalEvents(agedDays=1)
+ proc.wait()
+
+ # Verify that the event, detail and log were all deleted.
+ self.assertEquals(history_count, self._getEventCount('history'))
+ self.assertEquals(detail_count, self._getEventCount('detail'))
+ self.assertEquals(log_count, self._getEventCount('log'))
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(testEventMaintenance))
+ return suite
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/tests/testSyslogProcessing.py
^
|
@@ -75,6 +75,18 @@
self.assertEquals( evt.get('eventClassKey'), 'HTTP/42' )
self.assertEquals( evt.get('summary'), 'Error on socket accept.' )
+
+ def testNetAppSyslogParser(self):
+ """
+ Test NetApp syslog parser.
+ """
+ msg = '[deviceName: 10/100/1000/e1a:warning]: Client 10.0.0.101 (xid 4251521131) is trying to access an unexported mount (fileid 64, snapid 0, generation 6111516 and flags 0x0 on volume 0xc97d89a [No volume name available])'
+ s = SyslogProcessor(self.sendEvent, 6, False, 'localhost', 3)
+ evt = s.parseTag({}, msg)
+ self.assertEquals(evt.get('component'), '10/100/1000/e1a')
+ self.assertEquals(evt.get('summary'), 'Client 10.0.0.101 (xid 4251521131) is trying to access an unexported mount (fileid 64, snapid 0, generation 6111516 and flags 0x0 on volume 0xc97d89a [No volume name available])')
+
+
def test_suite():
suite = TestSuite()
suite.addTest(makeSuite(SyslogProcessingTest))
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/tests/testZenActions.py
^
|
@@ -0,0 +1,83 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+import unittest
+import Globals
+import transaction
+
+from Products.ZenTestCase.BaseTestCase import BaseTestCase
+from Products.ZenEvents.zenactions import BaseZenActions
+
+
+class MockLogger(object):
+
+ def debug(self, *args, **kwargs):
+ pass
+
+
+class MockZenActions(BaseZenActions):
+
+ def __init__(self, dmd):
+ self.dmd = dmd
+
+ log = MockLogger()
+
+
+class ZenActionsTest(BaseTestCase):
+
+ def setUp(self):
+ BaseTestCase.setUp(self)
+ self.zenActions = MockZenActions(self.dmd)
+
+ def tearDown(self):
+ self.zenActions = None
+ zem = self.dmd.ZenEventManager
+ conn = zem.connect()
+ try:
+ curs = conn.cursor()
+ curs.execute("truncate status")
+ curs.execute("truncate detail")
+ curs.execute("truncate log")
+ curs.execute("truncate history")
+ finally:
+ zem.close(conn)
+ BaseTestCase.tearDown(self)
+
+ def testDescribe(self):
+ stmt = 'SELECT * FROM status LIMIT 0'
+ description = self.zenActions._describe(stmt)
+ self.assertEqual(self.zenActions.lastCommand, stmt)
+
+ for (name,
+ type_code,
+ display_size,
+ internal_size,
+ precision,
+ scale,
+ null_ok) in description:
+
+ self.assert_(isinstance(name, basestring),
+ 'name must be a string')
+
+ def testColumnNames(self):
+ columnNames = self.zenActions._columnNames('status')
+ for columnName in columnNames:
+ self.assert_(isinstance(columnName, basestring),
+ 'columnName must be a string')
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(ZenActionsTest))
+ return suite
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/zenactions.py
^
|
@@ -111,7 +111,7 @@
self.error += text
-class ZenActions(ZCmdBase):
+class BaseZenActions(object):
"""
Take actions based on events in the event manager.
Start off by sending emails and pages.
@@ -148,20 +148,6 @@
" AND event.evid = '%s'")
- def __init__(self):
- ZCmdBase.__init__(self)
- self.schedule = Schedule(self.options, self.dmd)
- self.schedule.sendEvent = self.dmd.ZenEventManager.sendEvent
- self.schedule.monitor = self.options.monitor
-
- self.actions = []
- self.loadActionRules()
- self.updateCheck = UpdateCheck()
- self.sendEvent(Event.Event(device=self.options.monitor,
- eventClass=App_Start,
- summary="zenactions started",
- severity=0, component="zenactions"))
-
def loadActionRules(self):
"""Load the ActionRules into the system.
"""
@@ -173,7 +159,11 @@
self.log.debug("action:%s for:%s loaded", ar.getId(), userid)
- def execute(self, stmt):
+ def _getCursor(self, stmt, callback):
+ """
+ Get a cursor for the ZenEventManager connection. Execute statement
+ and call the callback with the cursor and number of row affected.
+ """
result = None
self.lastCommand = stmt
self.log.debug(stmt)
@@ -181,23 +171,49 @@
conn = zem.connect()
try:
curs = conn.cursor()
- result = curs.execute(stmt)
- finally: zem.close(conn)
+ rowsAffected = curs.execute(stmt)
+ result = callback(cursor=curs, rowsAffected=rowsAffected)
+ finally:
+ zem.close(conn)
return result
+ def execute(self, stmt):
+ """
+ Execute stmt against ZenEventManager connection and return the number
+ of rows that were affected.
+ """
+ def callback(rowsAffected, **unused):
+ return rowsAffected
+ return self._getCursor(stmt, callback)
+
+
def query(self, stmt):
- result = None
- self.lastCommand = stmt
- self.log.debug(stmt)
- zem = self.dmd.ZenEventManager
- conn = zem.connect()
- try:
- curs = conn.cursor()
- curs.execute(stmt)
- result = curs.fetchall()
- finally: zem.close(conn)
- return result
+ """
+ Execute stmt against ZenEventManager connection and fetch all results.
+ """
+ def callback(cursor, **unused):
+ return cursor.fetchall()
+ return self._getCursor(stmt, callback)
+
+
+ def _describe(self, stmt):
+ """
+ Execute stmt against ZenEventManager connection and return the cursor
+ description.
+ """
+ def callback(cursor, **unused):
+ return cursor.description
+ return self._getCursor(stmt, callback)
+
+
+ def _columnNames(self, table):
+ """
+ Returns the column names for the table using a ZenEventManager
+ connection.
+ """
+ description = self._describe("SELECT * FROM %s LIMIT 0" % table)
+ return [d[0] for d in description]
def getBaseUrl(self, device=None):
@@ -255,7 +271,9 @@
transaction.commit()
def processEvent(self, zem, context, action):
- fields = context.getEventFields()
+ userFields = context.getEventFields()
+ columnNames = self._columnNames('status')
+ fields = [f for f in userFields if f in columnNames]
userid = context.getUserid()
# get new events
nwhere = context.where.strip() or '1 = 1'
@@ -647,6 +665,10 @@
"""Send and event to a pager. Return True if we think page was sent,
False otherwise.
"""
+ if self.options.cycle and not reactor.running:
+ # Give the reactor time to startup if necessary.
+ return False
+
fmt, body = self.format(action, data, clear)
recipients = action.getAddresses()
if not recipients:
@@ -657,9 +679,10 @@
result = False
for recipient in recipients:
- success, errorMsg = Utils.sendPage(recipient,
- fmt,
- self.dmd.pageCommand)
+ success, errorMsg = Utils.sendPage(
+ recipient, fmt, self.dmd.pageCommand,
+ deferred=self.options.cycle)
+
if success:
self.log.info('sent page to %s: %s', recipient, fmt)
# return True if anyone got the page
@@ -740,6 +763,24 @@
except SystemExit:
reactor.stop()
+
+class ZenActions(BaseZenActions, ZCmdBase):
+
+ def __init__(self):
+ ZCmdBase.__init__(self)
+ self.schedule = Schedule(self.options, self.dmd)
+ self.schedule.sendEvent = self.dmd.ZenEventManager.sendEvent
+ self.schedule.monitor = self.options.monitor
+
+ self.actions = []
+ self.loadActionRules()
+ self.updateCheck = UpdateCheck()
+ self.sendEvent(Event.Event(device=self.options.monitor,
+ eventClass=App_Start,
+ summary="zenactions started",
+ severity=0, component="zenactions"))
+
+
if __name__ == "__main__":
za = ZenActions()
import logging
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/zentestsyslogrules.py
^
|
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+__doc__ = """zentestsyslogrules
+Apply the zensyslog regexes against messages captured by zensyslog
+(default filename: $ZENHOME/log/origsyslog.log) and report on the
+frequency of each message.
+This allows one to test the effectiveness of the drop filtering.
+"""
+
+import Globals
+from Products.ZenEvents.SyslogProcessing import SyslogProcessor
+from Products.ZenUtils.CmdBase import CmdBase
+from Products.ZenUtils.Utils import zenPath
+defaultInfile = zenPath("log/origsyslog.log")
+
+# The format of the capture syslog message is:
+# timestamp timestamp ip_address: message
+#Nov 30 13:45:03 Nov 30 2009 13:43:49 10.10.10.62 : %ASA-7-609001: Built local-host outside:10.10.10.10
+# We just want the message
+ORID_SYSLOG_METADATA_END_POS = 53
+
+
+class ZenTestSyslogRules(CmdBase):
+
+ def __init__(self):
+ CmdBase.__init__(self)
+ self.processor = SyslogProcessor(self.sendEvent,
+ self.options.minpriority, False,
+ "localhost", 2)
+ self.keptEvent = False
+ self.totalSent = 0
+
+ def sendEvent(self, evt, *args, **kwargs):
+ self.keptEvent = True
+ self.totalSent += 1
+
+ def getSyslogMsg(self, line):
+ # Throw out captured meta-data
+ return line[ORID_SYSLOG_METADATA_END_POS:-1]
+
+ def run(self):
+ # Apply the regex rules against all captured syslog messages
+ totalEvents = 0
+ counts= {}
+ for line in open(self.options.infile):
+ line = line.strip()
+ if not line or line.startswith("#"):
+ continue
+
+ totalEvents += 1
+ self.keptEvent = False
+ self.processor.process(line, "127.0.0.1", "localhost", None)
+ if self.keptEvent:
+ key = self.getSyslogMsg(line)
+ counts.setdefault(key, 0)
+ counts[key] += 1
+
+ # Print the output sorted by the number of times each message
+ # has occurred in the original input
+ for key, count in sorted(counts.items(), key=lambda x: x[1]):
+ print "%d %s" % (count, key)
+ droppedCount = totalEvents - self.totalSent
+ droppedPct = 0
+ if totalEvents:
+ droppedPct = droppedCount * 100.0 / totalEvents
+ self.log.info("Test event stats: dropped=%d sent=%d total=%d dropped_pct=%.1f",
+ droppedCount, self.totalSent, totalEvents, droppedPct)
+
+ def buildOptions(self):
+ CmdBase.buildOptions(self)
+ self.parser.add_option('--infile',
+ dest='infile', default=defaultInfile,
+ help="File containing captured syslog events.")
+ self.parser.add_option('--minpriority', dest='minpriority',
+ default=6, type='int',
+ help='Minimum priority message that zensyslog will accept')
+
+if __name__ == "__main__":
+ sender = ZenTestSyslogRules()
+ try:
+ sender.run()
+ except (IOError, KeyboardInterrupt): pass
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenEvents/zentrap.py
^
|
@@ -33,6 +33,7 @@
from pynetsnmp import netsnmp, twistedsnmp
from twisted.internet import defer, reactor
+from Products.ZenHub.PBDaemon import FakeRemote
from Products.ZenUtils.Driver import drive
from Products.ZenUtils.captureReplay import CaptureReplay
@@ -78,16 +79,17 @@
class ZenTrap(EventServer, CaptureReplay):
"""
Listen for SNMP traps and turn them into events
- Connects to the EventService service in zenhub.
+ Connects to the TrapService service in zenhub.
"""
name = 'zentrap'
+ initialServices = EventServer.initialServices + ['TrapService']
+ oidMap = {}
+ haveOids = False
def __init__(self):
EventServer.__init__(self)
- self.oidCache = {}
-
# Command-line argument sanity checking
self.processCaptureReplayOptions()
@@ -108,6 +110,36 @@
twistedsnmp.updateReactor()
+ def configure(self):
+ def inner(driver):
+ self.log.info("fetching default RRDCreateCommand")
+ yield self.model().callRemote('getDefaultRRDCreateCommand')
+ createCommand = driver.next()
+
+ self.log.info("getting threshold classes")
+ yield self.model().callRemote('getThresholdClasses')
+ self.remote_updateThresholdClasses(driver.next())
+
+ self.log.info("getting collector thresholds")
+ yield self.model().callRemote('getCollectorThresholds')
+ self.rrdStats.config(self.options.monitor, self.name,
+ driver.next(), createCommand)
+
+ self.log.info("getting OID -> name mappings")
+ yield self.getServiceNow('TrapService').callRemote('getOidMap')
+ self.oidMap = driver.next()
+ self.haveOids = True
+
+ self.heartbeat()
+ self.reportCycle()
+
+ d = drive(inner)
+ def error(result):
+ self.log.error("Unexpected error in configure: %s" % result)
+ d.addErrback(error)
+ return d
+
+
def getEnterpriseString(self, pdu):
"""
Get the enterprise string from the PDU or replayed packet
@@ -201,7 +233,7 @@
def oid2name(self, oid, exactMatch=True, strip=False):
"""
- Get OID name from cache or ZenHub
+ Returns a MIB name based on an OID and special handling flags.
@param oid: SNMP Object IDentifier
@type oid: string
@@ -214,30 +246,27 @@
"""
if type(oid) == type(()):
oid = '.'.join(map(str, oid))
- cacheKey = "%s:%r:%r" % (oid, exactMatch, strip)
- if self.oidCache.has_key(cacheKey):
- return defer.succeed(self.oidCache[cacheKey])
-
- self.log.debug("OID cache miss on %s (exactMatch=%r, strip=%r)" % (
- oid, exactMatch, strip))
- d = self.model().callRemote('oid2name', oid, exactMatch, strip)
- def cache(name, key):
- """
- Twisted callback to cache and return the name
+ oid = oid.strip('.')
+ if exactMatch:
+ if oid in self.oidMap:
+ return self.oidMap[oid]
+ else:
+ return oid
- @param name: human-readable-name form of OID
- @type name: string
- @param key: key of OID and params
- @type key: string
- @return: the name parameter
- @rtype: string
- """
- self.oidCache[key] = name
- return name
+ oidlist = oid.split('.')
+ for i in range(len(oidlist), 0, -1):
+ name = self.oidMap.get('.'.join(oidlist[:i]), None)
+ if name is None:
+ continue
+
+ oid_trail = oidlist[i:]
+ if len(oid_trail) > 0 and not strip:
+ return "%s.%s" % (name, '.'.join(oid_trail))
+ else:
+ return name
- d.addCallback(cache, cacheKey)
- return d
+ return oid
def receiveTrap(self, pdu):
@@ -248,6 +277,9 @@
@param pdu: Net-SNMP object
@type pdu: netsnmp_pdu object
"""
+ if not self.haveOids:
+ return
+
ts = time.time()
# Is it a trap?
@@ -341,22 +373,23 @@
oid = '.'.join(map(str, oid))
# SNMPv2-MIB/snmpTrapOID
if oid == '1.3.6.1.6.3.1.1.4.1.0':
- yield self.oid2name(value, exactMatch=False, strip=False)
- eventType = driver.next()
+ eventType = self.oid2name(
+ value, exactMatch=False, strip=False)
else:
# Add a detail for the variable binding.
- yield self.oid2name(oid, exactMatch=False, strip=False)
- result[driver.next()] = value
+ r = self.oid2name(oid, exactMatch=False, strip=False)
+ result[r] = value
# Add a detail for the index-stripped variable binding.
- yield self.oid2name(oid, exactMatch=False, strip=True)
- result[driver.next()] = value
+ r = self.oid2name(oid, exactMatch=False, strip=True)
+ result[r] = value
elif pdu.version == 0:
# SNMP v1
variables = self.getResult(pdu)
addr[0] = '.'.join(map(str, [pdu.agent_addr[i] for i in range(4)]))
enterprise = self.getEnterpriseString(pdu)
- eventType = driver.next()
+ eventType = self.oid2name(
+ enterprise, exactMatch=False, strip=False)
generic = pdu.trap_type
specific = pdu.specific_type
@@ -364,15 +397,13 @@
# specific OID. It seems that MIBs frequently expect this .0.
# to exist, but the device's don't send it in the trap.
oid = "%s.0.%d" % (enterprise, specific)
- yield self.oid2name(oid, exactMatch=True, strip=False)
- name = driver.next()
+ name = self.oid2name(oid, exactMatch=True, strip=False)
# If we didn't get a match with the .0. inserted we will try
# resolving withing the .0. inserted and allow partial matches.
if name == oid:
oid = "%s.%d" % (enterprise, specific)
- yield self.oid2name(oid, exactMatch=False, strip=False)
- name = driver.next()
+ name = self.oid2name(oid, exactMatch=False, strip=False)
# Look for the standard trap types and decode them without
# relying on any MIBs being loaded.
@@ -391,11 +422,11 @@
for oid, value in variables:
oid = '.'.join(map(str, oid))
# Add a detail for the variable binding.
- yield self.oid2name(oid, exactMatch=False, strip=False)
- result[driver.next()] = value
+ r = self.oid2name(oid, exactMatch=False, strip=False)
+ result[r] = value
# Add a detail for the index-stripped variable binding.
- yield self.oid2name(oid, exactMatch=False, strip=True)
- result[driver.next()] = value
+ r = self.oid2name(oid, exactMatch=False, strip=True)
+ result[r] = value
else:
self.log.error("Unable to handle trap version %d", pdu.version)
return
@@ -438,6 +469,9 @@
self.session.sess)
netsnmp.lib.snmp_free_pdu(reply)
sess.close()
+
+ yield defer.succeed(True)
+ driver.next()
return drive(inner)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenHub/services/DiscoverService.py
^
|
@@ -137,9 +137,18 @@
try:
netroot = getNetworkRoot(self.dmd,
kw.get('performanceMonitor', 'localhost'))
- ipobj = netroot.createIp(ip)
- # If we're not supposed to discover this ip, return None
- if not force and not getattr(ipobj, 'zAutoDiscover', True):
+ netobj = netroot.getNet(ip)
+ netmask = 24
+ if netobj is not None:
+ netmask = netobj.netmask
+ else:
+ defaultNetmasks = getattr(netroot, 'zDefaultNetworkTree', [])
+ if defaultNetmasks:
+ netmask = defaultNetmasks[0]
+ netroot.createIp(ip, netmask)
+ autoDiscover = getattr(netobj, 'zAutoDiscover', True)
+ # If we're not supposed to discover this IP, return None
+ if not force and not autoDiscover:
return None, False
kw['manageIp'] = ip
dev = manage_createDevice(self.dmd, **kw)
@@ -154,6 +163,7 @@
# Make and return a device proxy
return self.createDeviceProxy(e.dev), False
except Exception, ex:
+ log.exception("IP address %s (kw = %s) encountered error", ip, kw)
raise pb.CopyableFailure(ex)
transaction.commit()
return self.createDeviceProxy(dev), True
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenHub/services/EventService.py
^
|
@@ -68,9 +68,3 @@
@translateError
def remote_getDefaultPriority(self):
return self.zem.defaultPriority
-
- @translateError
- def remote_oid2name(self, oid, exactMatch=True, strip=False):
- "get oids, even if we're handed slightly wrong values"
- name = self.dmd.Mibs.oid2name(oid, exactMatch, strip)
- return name or oid
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenHub/services/TrapService.py
^
|
@@ -0,0 +1,29 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+from Products.ZenHub.HubService import HubService
+from Products.ZenHub.PBDaemon import translateError
+
+
+class TrapService(HubService):
+
+ @translateError
+ def remote_getOidMap(self):
+ """
+ Return a dictionary containing all OID -> Name mappings from /Mibs.
+ """
+ oidMap = {}
+ for brain in [ b for b in self.dmd.Mibs.mibSearch() if b.oid ]:
+ oidMap[brain.oid] = brain.id
+
+ return oidMap
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenHub/zenhub.py
^
|
@@ -256,10 +256,9 @@
self.changes = []
self.workers = []
self.workList = []
+ self.worker_processes=set()
ZCmdBase.__init__(self)
- import Products
- Products.Five.zcml.load_config('event.zcml', Products.Five)
self.zem = self.dmd.ZenEventManager
loadPlugins(self.dmd)
self.services = {}
@@ -272,6 +271,7 @@
xmlsvc = AuthXmlRpcService(self.dmd, checker)
reactor.listenTCP(self.options.xmlrpcport, server.Site(xmlsvc))
+
self.sendEvent(eventClass=App_Start,
summary="%s started" % self.name,
severity=0)
@@ -541,13 +541,14 @@
def processEnded(s, reason):
os.unlink(tmp)
+ self.worker_processes.discard(s)
self.log.warning("Worker exited with status: %d (%s)",
reason.value.exitCode,
getExitMessage(reason.value.exitCode))
args = (exe, 'run', '-C', tmp)
self.log.debug("Starting %s", ' '.join(args))
- reactor.spawnProcess(WorkerRunningProtocol(), exe, args, os.environ)
-
+ proc = reactor.spawnProcess(WorkerRunningProtocol(), exe, args, os.environ)
+ self.worker_processes.add(proc)
def heartbeat(self):
"""
Since we don't do anything on a regular basis, just
@@ -578,9 +579,8 @@
if self.options.cycle:
self.heartbeat()
reactor.run()
- for worker in self.workers:
- worker.transport.signalProcess('KILL')
-
+ for proc in self.worker_processes:
+ proc.signalProcess('KILL')
def buildOptions(self):
"""
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/BatchDeviceLoader.py
^
|
@@ -58,7 +58,7 @@
# defaults to the /Devices/Discovered device class.
device0 comments="A simple device"
# All settings must be seperated by a comma.
-device1 comments="A simple device" zSnmpCommunity='blue', zSnmpVer='v1'
+device1 comments="A simple device", zSnmpCommunity='blue', zSnmpVer='v1'
# Notes for this file:
# * Organizer names cannot contain spaces
@@ -67,11 +67,11 @@
/Devices/Server/Linux zSnmpPort=1543
# Python strings can use either ' or " -- there's no difference.
# As a special case, it is also possible to specify the IP address
-linux_device1 manageIp=10.10.10.77, zSnmpCommunity='blue', zSnmpVer="v2c"
+linux_device1 manageIp='10.10.10.77', zSnmpCommunity='blue', zSnmpVer="v2c"
# A '\' at the end of the line allows you to place more
# expressions on a new line. Don't forget the comma...
linux_device2 discoverProto='none', zLinks="<a href='http://example.org'>Support site</a>", \
-zTelnetEnable=True \
+zTelnetEnable=True, \
zTelnetPromptTimeout=15.3
# A new organizer drops all previous settings, and allows
@@ -179,7 +179,8 @@
self.applyZProps(devobj, device_specs)
# Default is discoverProto == 'snmp'
- if device_specs.get('discoverProto', '') != 'none':
+ if not self.options.nocommit and \
+ device_specs.get('discoverProto', '') != 'none':
# What if zSnmpCommunity isn't set in the file?
devobj.manage_snmpCommunity()
@@ -193,7 +194,8 @@
except Exception, ex:
self.log.exception("Modeling error" )
- commit()
+ if not self.options.nocommit:
+ commit()
processed += 1
self.log.info( "Processed %d of %d devices" % (processed, len(device_list)))
@@ -257,6 +259,11 @@
action="store_false",
help="Show modelling activity")
+ self.parser.add_option('--nocommit',
+ dest="nocommit", default=False,
+ action="store_true",
+ help="Don't commit changes to the ZODB. Use for verifying config file.")
+
def parseDevices(self, data):
"""
From the list of strings in rawDevices, construct a list
@@ -270,7 +277,7 @@
if not data:
return []
- comment = re.compile(r'#.*')
+ comment = re.compile(r'^\s*#.*')
defaults = {'devicePath':"/Discovered" }
finalList = []
@@ -323,7 +330,8 @@
if options:
try:
- configs.update( eval( 'dict(' + options + ')' ) )
+ # Add a newline to allow for trailing comments
+ configs.update( eval( 'dict(' + options + '\n)' ) )
except:
self.log.error( "Unable to parse the entry for %s -- skipping" % name )
self.log.error( "Raw string: %s" % options )
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/Device.py
^
|
@@ -27,8 +27,7 @@
from urllib import quote as urlquote
-from Products.ZenUtils.Utils import isXmlRpc
-from Products.ZenUtils.Utils import unused
+from Products.ZenUtils.Utils import isXmlRpc, unused, getObjectsFromCatalog
from Products.ZenUtils import Time
import RRDView
from Products.ZenUtils.IpUtil import checkip, IpAddressError, maskToBits
@@ -619,8 +618,8 @@
query['monitored'] = monitored
if type is not None:
query['meta_type'] = type
- brains = self.componentSearch(query)
- return [c.getObject() for c in brains]
+
+ return list(getObjectsFromCatalog(self.componentSearch, query, log))
def getDeviceComponentsNoIndexGen(self):
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/DeviceOrganizer.py
^
|
@@ -35,7 +35,7 @@
from ZenossSecurity import *
-from Products.ZenUtils.Utils import unused
+from Products.ZenUtils.Utils import unused, getObjectsFromCatalog
from Products.ZenWidgets import messaging
import logging
@@ -108,8 +108,8 @@
LOG.warn('Please run zenmigrate to create device path indexes.')
return self.getSubDevices_recursive(devfilter)
- brains = catalog(path="/".join(self.getPhysicalPath()))
- devices = (b.getObject() for b in brains)
+ devices = getObjectsFromCatalog(catalog, {
+ 'path': "/".join(self.getPhysicalPath())}, LOG)
devices = ifilter(lambda dev:self.checkRemotePerm(ZEN_VIEW, dev),
devices)
devices = ifilter(devfilter, devices)
@@ -124,8 +124,8 @@
LOG.warn('Please run zenmigrate to create device path indexes.')
yield self.getSubDevicesGen_recursive(devfilter)
- brains = catalog(path="/".join(self.getPhysicalPath()))
- devices = (b.getObject() for b in brains)
+ devices = getObjectsFromCatalog(catalog, {
+ 'path': "/".join(self.getPhysicalPath())}, LOG)
devices = ifilter(lambda dev:self.checkRemotePerm(ZEN_VIEW, dev),
devices)
for device in devices:
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/ExpansionCard.py
^
|
@@ -39,7 +39,7 @@
monitor = False
_properties = HWComponent._properties + (
- {'id':'slot', 'type':'int', 'mode':'w'},
+ {'id':'slot', 'type':'string', 'mode':'w'},
)
_relations = HWComponent._relations + (
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/IpInterface.py
^
|
@@ -455,23 +455,35 @@
def getRRDTemplates(self):
"""
Return a list containing the appropriate RRDTemplate for this
- IpInterface. If none is found then the list will contain None.
+ IpInterface. If none is found then the list will be empty.
+
+ Order of preference if the interface supports 64bit counters.
+ 1. <type>_64
+ 2. ethernetCsmacd_64
+ 3. <type>
+ 4. ethernetCsmacd
+
+ Order of preference if the interface doesn't support 64bit counters.
+ 1. <type>
+ 2. ethernetCsmacd
"""
templateName = self.getRRDTemplateName()
- default = self.getRRDTemplateByName(templateName)
-
- # If this interface supports 64bit interfaces, but no 64bit specific
- # template exists for it, fall back to the 32bit version.
- if not default and templateName.endswith("_64"):
- default = self.getRRDTemplateByName(templateName[:-3])
-
- # If no specific template exists for this type of interface default to
- # the ethernetCsmacd template.
- if not default:
- default = self.getRRDTemplateByName("ethernetCsmacd")
-
- if default:
- return [default]
+
+ order = ['ethernetCsmacd']
+ if templateName.endswith('_64'):
+ order.insert(0, 'ethernetCsmacd_64')
+ if templateName not in order:
+ order.insert(0, templateName)
+ order.insert(2, templateName[:-3])
+ else:
+ if templateName not in order:
+ order.insert(0, templateName)
+
+ for name in order:
+ template = self.getRRDTemplateByName(name)
+ if template:
+ return [template]
+
return []
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/IpNetwork.py
^
|
@@ -136,7 +136,15 @@
self.netmask = maskToBits(netmask)
self.description = description
-
+ security.declareProtected('Change Network', 'manage_addIpNetwork')
+ def manage_addIpNetwork(self, newPath, REQUEST=None):
+ """
+ From the GUI, create a new subnet (if necessary)
+ """
+ net = self.createNet(newPath)
+ if REQUEST is not None:
+ REQUEST['RESPONSE'].redirect(net.absolute_url())
+
def checkValidId(self, id, prep_id = False):
"""Checks a valid id
"""
@@ -174,10 +182,11 @@
#hook method do not remove!
netroot = self.getNetworkRoot()
netobj = netroot.getNet(netip)
- if netobj and netobj.netmask == netmask: # Network already exists.
- return netobj
if netmask == 0:
raise ValueError("netip '%s' without netmask" % netip)
+ if netobj and netobj.netmask >= netmask: # Network already exists.
+ return netobj
+
netip = getnetstr(netip,netmask)
netTree = getattr(self, 'zDefaultNetworkTree', defaultNetworkTree)
netTree = map(int, netTree)
@@ -190,14 +199,34 @@
for treemask in netTree:
if treemask >= netmask:
+ netobjParent = netobj
netobj = netobj.addSubNetwork(netip, netmask)
+ self.rebalance(netobjParent, netobj)
break
else:
supnetip = getnetstr(netip, treemask)
+ netobjParent = netobj
netobj = netobj.addSubNetwork(supnetip, treemask)
+ self.rebalance(netobjParent, netobj)
+
return netobj
+ def rebalance(self, netobjParent, netobj):
+ """
+ Look for children of the netobj at this level and move them to the
+ right spot.
+ """
+ moveList = []
+ for subnetOrIp in netobjParent.children():
+ if subnetOrIp == netobj:
+ continue
+ if netobj.hasIp(subnetOrIp.id):
+ moveList.append(subnetOrIp.id)
+ if moveList:
+ netobjPath = netobj.getOrganizerName()[1:]
+ netobjParent.moveOrganizer(netobjPath, moveList)
+
def findNet(self, netip, netmask=0):
"""
Find and return the subnet of this IpNetwork that matches the requested
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/Location.py
^
|
@@ -123,7 +123,10 @@
into a data structure appropriate for JS consumption by another method
(specifically, getChildGeomapData, below).
"""
- address = self.address
+ try:
+ address = self.address.decode().encode('iso-8859-1')
+ except (UnicodeDecodeError, UnicodeEncodeError):
+ address = self.address
psthresh = self.dmd.prodStateDashboardThresh
summary = self.getEventSummary(prodState=psthresh)
colors = 'red orange yellow green green'.split()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/MibBase.py
^
|
@@ -18,7 +18,7 @@
from ZenPackable import ZenPackable
class MibBase(ZenModelRM, ZenPackable):
- implements(IIndexed)
+ implements(IIndexed)
default_catalog = 'mibSearch'
_relations = ZenPackable._relations[:]
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/OperatingSystem.py
^
|
@@ -92,16 +92,33 @@
return self.totalSwap and convToUnits(self.totalSwap) or 'unknown'
def traceRoute(self, target, ippath):
- """Trace the route to target using our routing table.
"""
- log.debug("device %s target %s", self.getDeviceName(), target)
+ Trace the route to the target from this device using our routing
+ table and return an array of IP address strings showing the route.
+
+ If the route is not traversable (ie a gap in the routing table),
+ then return the incomplete path with a final value of None.
+
+ @parameter target: destination IP to find
+ @type target: DMD device object
+ @parameter ippath: used to store intermediate results in calls. Call with []
+ @type ippath: array-like object used for
+ @return: IP addresses to target device
+ @rtype: array of strings
+ """
+ log.debug("traceRoute from device %s to target %s",
+ self.getDeviceName(), target)
+ # Try to find a specific route to the target device
nextdev = None
for route in self.getRouteObjs():
ip = route.getNextHopIp()
- log.debug("target %s next hop %s", route.getTarget(), ip)
+ log.debug("Route %s next hop %s", route.getTarget(), ip)
+ # Have a host-route
if ip == target.getManageIp():
ippath.append(ip)
return ippath
+
+ # Have a net-route
if route.matchTarget(target.getManageIp()):
if route.routetype == 'direct':
nextdev = target
@@ -109,20 +126,27 @@
nextdev = route.getNextHopDevice()
break
else:
- log.debug("device %s default route", self.getDeviceName())
+ # No routes matched -- try the default route (if any)
+ log.debug("Device %s default route", self.getDeviceName())
ip = ""
default = self.routes._getOb("0.0.0.0_0", None)
if default:
ip = default.getNextHopIp()
nextdev = default.getNextHopDevice()
- if target == nextdev or ip=="0.0.0.0":
+
+ if target == nextdev or ip == "0.0.0.0":
ippath.append(target.id)
return ippath
+
if nextdev:
ippath.append(ip)
return nextdev.traceRoute(target, ippath)
- raise TraceRouteGap("unable to trace to %s, gap at %s" % (target.id,
- self.getDeviceName()))
+
+ # Oops! No route!
+ log.debug("Unable to trace to %s, gap at %s", target.id,
+ self.getDeviceName())
+ ippath.append(None)
+ return ippath
def getRouteObjs(self):
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/RRDView.py
^
|
@@ -334,22 +334,13 @@
thresh = threshold.createThresholdInstance(self)
result.append(thresh)
except pythonThresholdException, ex:
- import transaction
- trans = transaction.get()
- threshold.manage_changeProperties(enabled=False)
- trans.setUser('zenhub')
- trans.note("Disabled threshold as it has errors.")
- trans.commit()
-
log.warn(ex)
- log.info("Disabled threshold %s", threshold.id)
-
zem = self.primaryAq().getEventManager()
import socket
device = socket.gethostname()
path = template.absolute_url_path()
msg = \
-"The threshold %s in template %s has been disabled." % (threshold.id, path)
+"The threshold %s in template %s has caused an exception." % (threshold.id, path)
evt = dict(summary=str(ex), severity=3,
component='zenhub', message=msg,
dedupid='zenhub|' + str(ex),
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/ReportClass.py
^
|
@@ -27,7 +27,7 @@
from Organizer import Organizer
from ZenPackable import ZenPackable
-from ZenossSecurity import ZEN_COMMON
+from ZenossSecurity import ZEN_COMMON, ZEN_MANAGE_DMD
from Products.ZenRelations.RelSchema import *
from Products.ZenUtils.Utils import unused
from Products.ZenWidgets import messaging
@@ -108,6 +108,7 @@
return ReportClass
+ security.declareProtected(ZEN_MANAGE_DMD, 'manage_addReportClass')
def manage_addReportClass(self, id, title = None, REQUEST = None):
"""make a device class"""
rClass = self.getReportClass()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/UserSettings.py
^
|
@@ -1199,7 +1199,14 @@
for u in self._getG().listAssignedPrincipals(self.id) ]
def getMemberUserIds(self):
- return [ u[0] for u in self._getG().listAssignedPrincipals(self.id) ]
+ """
+ Returns a list of user ids that are members of this group.
+ """
+ # We must using reverse mapping of all users to their groups rather
+ # than going directly to the group's assigned principals because
+ # some group backends don't support listAssignedPrincipals.
+ return [ u.id for u in self.ZenUsers.getAllUserSettings()
+ if self.id in u.getUserGroupSettingsNames() ]
def printUsers(self):
return ", ".join(self.getMemberUserIds())
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/ZVersion.py
^
|
@@ -10,4 +10,4 @@
# For complete information please visit: http://www.zenoss.com/oss/
#
###########################################################################
-VERSION="2.5.1"
+VERSION="2.5.2"
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/data/devices.xml
^
|
@@ -101,7 +101,7 @@
test -f %s
</property>
<property visible="True" type="string" id="zCommandPath" >
-/usr/local/zenoss/libexec
+$ZENHOME/libexec
</property>
<property visible="True" type="string" id="zTelnetLoginRegex" >
ogin:.$
@@ -157,6 +157,8 @@
<property visible="True" type="int" id="zSshConcurrentSessions" >
10
</property>
+<property visible="True" type="password" id="zEnablePassword" >
+</property>
<tomanycont id='zenMenus'>
<object id='More' module='Products.ZenModel.ZenMenu' class='ZenMenu'>
<tomanycont id='zenMenuItems'>
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/migrate/__init__.py
^
|
@@ -190,3 +190,5 @@
import deletedevicepermission
import reindexProducts
import addenc
+import stringifyExpansionCardSlot
+import zEnablePassword
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/migrate/stringifyExpansionCardSlot.py
^
|
@@ -0,0 +1,34 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009 Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+
+import Migrate
+
+import logging
+log = logging.getLogger('zen.migrate')
+
+
+class StringifyExpansionCardSlot(Migrate.Step):
+ version = Migrate.Version(2, 5, 1)
+
+ def cutover(self, dmd):
+ try:
+ for brain in dmd.Devices.componentSearch(meta_type='ExpansionCard'):
+ card = brain.getObject()
+ if isinstance(card.slot, int):
+ card.slot = str(card.slot)
+ except Exception, ex:
+ log.error('Error converting expansion card slots to strings: %s',
+ ex)
+
+
+StringifyExpansionCardSlot()
|
[-]
[+]
|
Added |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/migrate/zEnablePassword.py
^
|
@@ -0,0 +1,27 @@
+###########################################################################
+#
+# This program is part of Zenoss Core, an open source monitoring platform.
+# Copyright (C) 2009, Zenoss Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# For complete information please visit: http://www.zenoss.com/oss/
+#
+###########################################################################
+__doc__='''
+
+Add zEnablePassword to DeviceClass.
+
+'''
+import Migrate
+
+class zEnablePassword(Migrate.Step):
+ version = Migrate.Version(2, 5, 2)
+
+ def cutover(self, dmd):
+ if not dmd.Devices.hasProperty('zEnablePassword'):
+ dmd.Devices._setProperty('zEnablePassword', '', type='password')
+
+zEnablePassword()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/skins/zenmodel/deviceHardwareDetail.pt
^
|
@@ -266,17 +266,23 @@
<th tal:replace="structure python:here.ZenTableManager.getTableHeader(
tableName,'slot','Slot')"/>
<th tal:replace="structure python:here.ZenTableManager.getTableHeader(
+ tableName,'id','Name')"/>
+ <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
tableName,'getManufacturerName','Manufacturer')"/>
<th tal:replace="structure python:here.ZenTableManager.getTableHeader(
tableName,'getProductName','Model')"/>
+ <th tal:replace="structure python:here.ZenTableManager.getTableHeader(
+ tableName,'serialNumber','Serial #')"/>
</tr>
<tal:block tal:repeat="card batch">
<tr tal:define="odd repeat/card/odd"
tal:attributes="class python:test(odd, 'odd', 'even')">
<td class="tablevalues" tal:content="card/slot"/>
+ <td class="tablevalues" tal:content="card/id"/>
<td class="tablevalues"
tal:content="structure card/getManufacturerLink"/>
<td class="tablevalues" tal:content="structure card/getProductLink"/>
+ <td class="tablevalues" tal:content="card/serialNumber"/>
</tr>
</tal:block>
<tr>
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/skins/zenmodel/login_form.pt
^
|
@@ -119,7 +119,7 @@
<input type="submit" name="submitbutton"
class="submitbutton" value=""/>
<div id="copyright">
- <p>Copyright © 2005-2009 Zenoss, Inc. | Version
+ <p>Copyright © 2005-2010 Zenoss, Inc. | Version
<span tal:content="here/getZenossVersionShort"/>
</p>
</div>
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/tests/testIndexing.py
^
|
@@ -123,6 +123,22 @@
self.assertEqual(brains[0].getObject(), dcmDevice)
+ def testNonExistentDeviceInCatalog(self):
+ """
+ Verify that stale catalog entries won't result in tracebacks.
+ """
+ from zExceptions import NotFound
+ d = self.dmd.Devices.createInstance('catTestDevice')
+ d.index_object()
+ device_count = len(self.dmd.Devices.getSubDevices())
+ self.dmd.Devices.devices._objects.pop('catTestDevice')
+ try:
+ self.assertEqual(len(self.dmd.Devices.getSubDevices()),
+ device_count - 1)
+ except (NotFound, KeyError, AttributeError), ex:
+ self.assertEqual(ex, None)
+
+
class TestComponentIndexing(ZenModelBaseTest):
def setUp(self):
@@ -301,6 +317,24 @@
self.assertEqual(components[0].device().id, 'dcmDevice')
+ def testNonExistentComponentInCatalog(self):
+ """
+ Verify that stale catalog entries won't result in tracebacks.
+ """
+ from zExceptions import NotFound
+ d = self.dmd.Devices.createInstance('catTestDevice')
+ d.index_object()
+ d.os.addIpInterface('catTestComponent', True)
+ c = d.os.interfaces._getOb('catTestComponent')
+ c.index_object()
+ component_count = len(d.getDeviceComponents())
+ d.os.interfaces._objects.pop('catTestComponent')
+ try:
+ self.assertEqual(len(d.getDeviceComponents()), component_count - 1)
+ except (NotFound, KeyError, AttributeError), ex:
+ self.assertEqual(ex, None)
+
+
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/tests/testIpInterface.py
^
|
@@ -138,6 +138,39 @@
self.assert_(not self.iface.getIpAddresses())
+ def testGetRRDTemplates(self):
+ matrix = [
+ [ 'ethernetCsmacd', [
+ ['ethernetCsmacd', 'ethernetCsmacd'],
+ ['ethernetCsmacd_64', 'ethernetCsmacd'],
+ ['propVirtual', 'ethernetCsmacd'],
+ ['propVirtual_64', 'ethernetCsmacd']]
+ ],[ 'ethernetCsmacd_64', [
+ ['ethernetCsmacd', 'ethernetCsmacd'],
+ ['ethernetCsmacd_64', 'ethernetCsmacd_64'],
+ ['propVirtual', 'ethernetCsmacd'],
+ ['propVirtual_64', 'ethernetCsmacd_64']]
+ ],[ 'propVirtual', [
+ ['ethernetCsmacd', 'ethernetCsmacd'],
+ ['ethernetCsmacd_64', 'ethernetCsmacd_64'],
+ ['propVirtual', 'propVirtual'],
+ ['propVirtual_64', 'ethernetCsmacd_64']]
+ ],[ 'propVirtual_64', [
+ ['ethernetCsmacd', 'ethernetCsmacd'],
+ ['ethernetCsmacd_64', 'ethernetCsmacd_64'],
+ ['propVirtual', 'propVirtual'],
+ ['propVirtual_64', 'propVirtual_64']]
+ ]
+ ]
+
+ for name, tests in matrix:
+ self.dmd.Devices.manage_addRRDTemplate(name)
+ for iftype, template_id in tests:
+ self.iface.type = iftype
+ self.assertEquals(
+ template_id, self.iface.getRRDTemplates()[0].id)
+
+
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/tests/testIpNetwork.py
^
|
@@ -119,7 +119,79 @@
net = dmdNet.findNet('1.2.0.0')
self.assert_(dmdNet.findNet('1.2.3.0') in net.children())
self.assert_(dmdNet.findNet('1.2.4.0') in net.children())
+
+ def testCreateIpWithLessSpecificMask(self):
+ """
+ See ticket #3646.
+
+ Add network 42.67.128.0/17, then create IP address 42.67.129.14 with a
+ netmask of 3. The important thing is that the netmask of the IP
+ address has a less specific mask length than the network. The IP
+ address should be created directly under the network.
+ """
+ dmdNet = self.dmd.Networks
+ # set this explicitly to the default. it should not affect this test.
+ # but it is used by createIp, so just to be safe.
+ dmdNet._updateProperty('zDefaultNetworkTree', (24, 32))
+
+ subnet = dmdNet.addSubNetwork("42.67.128.0", 17)
+ # make sure it is in the right place
+ self.assertEqual(subnet, dmdNet._getOb("42.67.128.0"))
+ # make sure it has the right netmask
+ self.assertEqual(17, subnet.netmask)
+
+ ip = dmdNet.createIp("42.67.129.14", 3)
+ # make sure the IP address has the correct netmask
+ self.assertEqual(3, ip.netmask)
+ # make sure the subnet still has the right netmask
+ self.assertEqual(17, subnet.netmask)
+ # make sure the IP address is in the right place
+ msg = "42.67.129.14 should be in subnet.ipaddresses(), but it is " \
+ "not. subnet.ipaddresses() = %s" % subnet.ipaddresses()
+ self.assert_(ip in subnet.ipaddresses(), msg)
+ # make sure no new networks have been created under the subnet
+ self.assertEqual([], subnet.children())
+ def testAutoRebalanceBackwards(self):
+ """
+ When creating networks, if (say) a /24 is created before a /16,
+ then we need to move the /24 network tree under the new /16 network.
+ Any IP addresses that are created at one level should be moved to
+ the correct location.
+ """
+ dmdNet = self.dmd.Networks
+ # Add in the 'wrong' order
+ ip = dmdNet.createIp("10.10.10.1", 27)
+ subnet24 = dmdNet.createNet("10.10.10.0", 24)
+ ip = dmdNet.createIp("10.10.10.2", 27)
+ subnet16 = dmdNet.createNet("10.10.0.0", 16)
+ ip = dmdNet.createIp("10.10.10.3", 27)
+ subnet8 = dmdNet.createNet("10.0.0.0", 8)
+
+ # getPrimaryPath() ==> ('', 'zport', 'dmd', 'Networks', '10.175.211.0')
+ self.assertEqual(1, len(dmdNet.children()))
+ self.assertEqual(subnet8.id, dmdNet.children()[0].id)
+ self.assertEqual(1, len(subnet8.children()))
+ self.assertEqual(subnet16.id, subnet8.children()[0].id)
+ self.assertEqual(1, len(subnet16.children()))
+ self.assertEqual(subnet24.id, subnet16.children()[0].id)
+
+ def testAutoRebalanceRandom(self):
+ """
+ What if the networks are created in random order?
+ """
+ dmdNet = self.dmd.Networks
+ # Add in the 'wrong' order
+ subnet24 = dmdNet.createNet("10.10.10.0", 24)
+ subnet8 = dmdNet.createNet("10.0.0.0", 8)
+ subnet16 = dmdNet.createNet("10.10.0.0", 16)
+
+ self.assertEqual(1, len(dmdNet.children()))
+ self.assertEqual(subnet8.id, dmdNet.children()[0].id)
+ self.assertEqual(1, len(subnet8.children()))
+ self.assertEqual(subnet16.id, subnet8.children()[0].id)
+ self.assertEqual(1, len(subnet16.children()))
+ self.assertEqual(subnet24.id, subnet16.children()[0].id)
def test_suite():
from unittest import TestSuite, makeSuite
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/tests/testzenmib.py
^
|
@@ -16,9 +16,7 @@
import logging
from Products.ZenTestCase.BaseTestCase import BaseTestCase
-from Products.ZenModel.zenmib import zenmib
-from Products.ZenModel.Exceptions import *
-
+from Products.ZenModel.zenmib import ZenMib, MibFile, PackageManager
class FakeConfigs: pass
@@ -46,38 +44,35 @@
# a handler object to logging.getLogger().addHandler(handler),
# but that doesn't seem to work.
- self.zmib = zenmib(noopts=1)
+ self.zmib = ZenMib(noopts=1)
self.zmib.options = FakeOptions()
+ self.mfo = MibFile('filename', '')
+ self.log = logging.getLogger("zen.ZenMib")
+
- def testFindSingleDependency(self):
+ def testFindDependencies(self):
"""
Given a MIB, find out what it relies on
"""
- mib = """
+ mib1 = """
RFC1213-MIB DEFINITIONS ::= BEGIN
IMPORTS
experimental, OBJECT-TYPE, Counter
FROM RFC1155-SMI;
--- contact IANA for actual number
-
root OBJECT IDENTIFIER ::= { experimental xx }
END
"""
- name, depends = self.zmib.map_mib_to_dependents(mib)
- self.assertEquals(name, 'RFC1213-MIB')
+ mfo = MibFile('filename', mib1)
+ self.assert_('RFC1213-MIB' in mfo.mibs)
+ depends = mfo.mibToDeps['RFC1213-MIB']
self.assert_(len(depends) == 1)
- self.assertEquals(depends[0], 'RFC1155-SMI')
-
+ self.assert_('RFC1155-SMI' in depends)
- def testFindDependencies(self):
- """
- Given a MIB, find out what it relies on
- """
- mib = """
+ mib2 = """
RFC1213-MIB DEFINITIONS ::= BEGIN
IMPORTS
@@ -91,12 +86,16 @@
END
"""
- name, depends = self.zmib.map_mib_to_dependents(mib)
- self.assertEquals(name, 'RFC1213-MIB')
+ mfo = MibFile('filename', mib2)
+ self.assert_('RFC1213-MIB' in mfo.mibs)
+ depends = mfo.mibToDeps['RFC1213-MIB']
self.assert_(len(depends) == 3)
+ self.assertEquals(depends,
+ set(['SNMPv2-SMI', 'RFC1213-MIB', 'SNMPv2-CONF']))
- def testFilterOutNonAscii(self):
+
+ def XtestFilterOutNonAscii(self):
"""
Currently, we don't support multi-byte language exports
when ZenPacks are exported. Control characters cause
@@ -104,18 +103,20 @@
"""
pass # Not implemented yet
+
def testComments(self):
"""
Ignore comments
"""
mib = """
RFC1213-MIB DEFINITIONS ::= BEGIN
+aaa
--sss--
bbb --ttt
ccc --uuu-- ddd
eee --vvv--
"This is text" --www-- "This is ""quoted"" text"
-"This is more quoted ""text"" followed by file.
+"This is more quoted ""text"" " followed by file.
------------------------------------------
-- Lines with all dashes are not valid ASN.1 comments
-- We ignore them anyway
@@ -142,28 +143,74 @@
/*they seem like they should cut out without*/
*/
*/
+Just some more text here.
+
END
"""
- name, depends = self.zmib.map_mib_to_dependents(mib)
- self.assertEquals(name, 'RFC1213-MIB')
+ noComments = """
+RFC1213-MIB DEFINITIONS ::= BEGIN
+aaa
+
+bbb
+ccc ddd
+eee
+"This is text" "This is ""quoted"" text"
+"This is more quoted ""text"" " followed by file.
+
+
+
+
+
+Text with an empty comment in the middle
+ But wait, here's text
+And now more text
+Finally, the last
+
+ finish a
+ single line comment inside of a double quote
+
+But what happens
+comment within */ a single line comment. There
+is also the issue of a "block comment /* within
+a */ string"
+
+
+Just some more text here.
+
+END
+"""
+
+ justMib = self.mfo.removeMibComments(mib)
+ self.assertEquals(justMib, noComments)
- def testMultipleMIBs(self):
+
+ def testMIBSplits(self):
"""
- What happens if there's more than one MIB in a file?
+ Can we split properly?
"""
- mib = """
+ mib1 = """
+IMPORT1 DEFINITIONS ::= BEGIN
+
+ IMPORTS
+ myroot FROM NOIMPORT1;
+
+ level OBJECT IDENTIFIER ::= { myroot 1 }
+END
+
+"""
+ mib2 = """
IMPORT1 DEFINITIONS ::= BEGIN
IMPORTS
myroot FROM NOIMPORT1;
- level1 OBJECT IDENTIFIER ::= { myroot 1 }
+ level OBJECT IDENTIFIER ::= { myroot 1 }
END
IMPORT2 DEFINITIONS ::= BEGIN
IMPORTS
- level1 FROM IMPORT1
+ level FROM IMPORT1
myroot FROM NOIMPORT1;
level2 OBJECT IDENTIFIER ::= { level1 1 }
@@ -171,8 +218,11 @@
END
"""
- name, depends = self.zmib.map_mib_to_dependents(mib)
- self.assertEquals(name, 'IMPORT1')
+ mibs = self.mfo.splitFileToMIBs(mib1)
+ self.assertEquals(len(mibs), 1)
+
+ mibs = self.mfo.splitFileToMIBs(mib2)
+ self.assertEquals(len(mibs), 2)
def test_suite():
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenModel/zenmib.py
^
|
@@ -1,7 +1,7 @@
###########################################################################
#
# This program is part of Zenoss Core, an open source monitoring platform.
-# Copyright (C) 2007, Zenoss Inc.
+# Copyright (C) 2007, 2009 Zenoss Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
@@ -10,7 +10,7 @@
# For complete information please visit: http://www.zenoss.com/oss/
#
###########################################################################
-__doc__= """zenmib
+__doc__ = """zenmib
The zenmib program converts MIBs into python data structures and then
(by default) adds the data to the Zenoss DMD. Essentially, zenmib is a
wrapper program around the smidump program, whose output (python code) is
@@ -58,7 +58,15 @@
import sys
import glob
import re
+import logging
from subprocess import Popen, PIPE
+import tempfile
+import urllib
+import tarfile
+import zipfile
+import signal
+from urllib2 import urlopen
+from urlparse import urljoin, urlsplit
import Globals
import transaction
@@ -68,449 +76,967 @@
from zExceptions import BadRequest
-def walk(*dirs):
+class MibFile:
"""
- Generator function to create a list of absolute paths
- for all files in a directory tree starting from the list
- of directories given as arguments to this function.
-
- @param *dirs: list of directories to investigate
- @type *dirs: list of strings
- @return: directory to investigate
- @rtype: string
+ A MIB file has the meta-data for a MIB inside of it.
"""
- for dir in dirs:
- for dirname, _, filenames in os.walk(dir):
- for filename in filenames:
- yield os.path.join(dirname, filename)
+ def __init__(self, fileName, fileContents=""):
+ self.fileName = fileName
+ self.mibs = [] # Order of MIBs defined in the file
+ self.mibToDeps = {} # Dependency defintions for each MIB
+ self.fileContents = self.removeMibComments(fileContents)
+ self.mibDefinitions = self.splitFileToMIBs(self.fileContents)
+ self.mapMibToDependents(self.mibDefinitions)
+
+ # Reclaim some memory
+ self.fileContents = ""
+ self.mibDefinitions = ""
+ def removeMibComments(self, fileContents):
+ """
+ Parses the string provided as an argument and extracts
+ all of the ASN.1 comments from the string.
+ Assumes that fileContents contains the contents of a well-formed
+ (no errors) MIB file.
-class DependencyMap:
- """
- A dependency is a reference to another part of the MIB tree.
- All MIB definitions start from the base of the tree (ie .1).
- Generally dependencies are from MIB definitions external to
- the MIB under inspection.
- """
+ @param fileContents: entire contents of a MIB file
+ @type fileContents: string
+ @return: text without any comments
+ @rtype: string
+ """
+ def findSingleLineCommentEndPos(startPos):
+ """
+ Beginning at startPos + 2, searches fileContents for the end of
+ a single line comment. If comment ends with a newline
+ character, the newline is not included in the comment.
+
+ MIB single line comment rules:
+ 1. Begins with '--'
+ 2. Ends with '--' or newline
+ 3. Any characters between the beginning and end of the comment
+ are ignored as part of the comment, including quotes and
+ start/end delimiters for block comments ( '/*' and '*/')
+
+ @param startPos: character position of the beginning of the single
+ line comment within fileContents
+ @type fileContents: string
+ @return: startPos, endPos (character position of the last character
+ in the comment + 1)
+ @rtype: tuple (integer, integer)
+ """
+ commentEndPosDash = fileContents.find('--', startPos + 2)
+ commentEndPosNewline = fileContents.find('\n', startPos + 2)
+ if commentEndPosDash != -1:
+ if commentEndPosNewline != -1:
+ if commentEndPosDash < commentEndPosNewline:
+ endPos = commentEndPosDash + 2
+ else:
+ endPos = commentEndPosNewline
+ else:
+ endPos = commentEndPosDash + 2
+ else:
+ if commentEndPosNewline != -1:
+ endPos = commentEndPosNewline
+ else:
+ endPos = len(fileContents)
- def __init__(self):
- self.fileMap = {}
- self.depMap = {}
+ return startPos, endPos
+ def findBlockCommentEndPos(searchStartPos):
+ """
+ Beginning at startPos + 2, searches fileContents for the end of
+ a block comment. If block comments are nested, the
+ function interates into each block comment by calling itself.
+
+ MIB block comment rules:
+ 1. Begins with '/*'
+ 2. Ends with '*/'
+ 3. Block comments can be nested
+ 3. Any characters between the beginning and end of the comment
+ are ignored as part of the comment, including quotes and
+ start/end delimiters for single line comments ('--'). Newlines
+ are included as part of the block comment.
+
+ @param startPos: character position of the beginning of the block
+ comment within fileContents
+ @type fileContents: string
+ @return: startPos, endPos (character position of the last character
+ in the comment + 1)
+ @rtype: tuple (integer, integer)
+ """
+ # Locate the next start and end markers
+ nextBlockStartPos = fileContents.find('/*', searchStartPos + 2)
+ nextBlockEndPos = fileContents.find('*/', searchStartPos + 2)
+
+ # If a nested comment exists, find the end
+ if nextBlockStartPos != -1 and \
+ nextBlockStartPos < nextBlockEndPos:
+ nestedComment = findBlockCommentEndPos(nextBlockStartPos)
+ nextBlockEndPos = fileContents.find('*/', nestedComment[1])
+
+ return searchStartPos, nextBlockEndPos + 2
+
+ # START removeMibComments
+ if not fileContents:
+ return fileContents
+
+ # Get rid of any lines that are completely made up of hyphens
+ fileContents = re.sub(r'[ \t]*-{2}[ \t]*$', '', fileContents)
+
+ # commentRanges holds a list of tuples in the form (startPos, endPos)
+ # that define the beginning and end of comments within fileContents
+ commentRanges = []
+ searchStartPos = 0 # character position within fileContents
+ functions = {'SINGLE': findSingleLineCommentEndPos,
+ 'BLOCK': findBlockCommentEndPos}
+
+ # Parse fileContents, looking for single line comments, block comments
+ # and string literals
+ while searchStartPos < len(fileContents):
+ # Find the beginning of the next occurrance of each item
+ singleLineStartPos = fileContents.find('--', searchStartPos)
+ blockStartPos = fileContents.find('/*', searchStartPos)
+ stringStartPos = fileContents.find('\"', searchStartPos)
+
+ nextItemPos = sys.maxint
+ nextItemType = ''
+
+ # Compare the next starting point for each item type.
+ if singleLineStartPos != -1 and \
+ singleLineStartPos < nextItemPos:
+ nextItemPos = singleLineStartPos
+ nextItemType = 'SINGLE'
+
+ if blockStartPos != -1 and \
+ blockStartPos < nextItemPos:
+ nextItemPos = blockStartPos
+ nextItemType = 'BLOCK'
+
+ # If the next item type is a string literal, just search for the
+ # next double quote and continue from there. This works because
+ # all double quotes (that are not part of a comment) appear in
+ # pairs. Even double-double quotes (escaped quotes) will work
+ # with this method since the first double quote will look like a
+ # string literal close quote and the second double quote will look
+ # like the beginning of a string literal.
+ if stringStartPos != -1 and \
+ stringStartPos < nextItemPos:
+ newSearchStartPos = \
+ fileContents.find('\"', stringStartPos + 1) + 1
+ if newSearchStartPos > searchStartPos:
+ searchStartPos = newSearchStartPos
+ else: # Weird error case
+ break
+
+ # If the next item is a comment, use the functions dictionary
+ # to call the appropriate function
+ elif nextItemPos != sys.maxint:
+ commentRange = functions[nextItemType](nextItemPos)
+ commentRanges.append(commentRange)
+ #searchStartPos = commentRange[1]
+ if commentRange[1] > 0:
+ searchStartPos = commentRange[1]
- def add(self, filename, name, dependencies):
+ else: # No other items are found!
+ break
+
+ startPos = 0
+ mibParts = []
+
+ # Iterate through each comment, adding the non-comment parts
+ # to mibParts. Finally, return the text without comments.
+ for commentRange in commentRanges:
+ mibParts.append(fileContents[startPos:(commentRange[0])])
+ startPos = commentRange[1]
+ if startPos != len(fileContents):
+ mibParts.append(fileContents[startPos:(len(fileContents))])
+ return ''.join(mibParts)
+
+ def splitFileToMIBs(self, fileContents):
"""
- Add a dependency to the dependency tree if it's not already there.
+ Isolates each MIB definition in fileContents into a separate string
- @param filename: name of MIB file to import
- @type filename: string
- @param name: name of MIB
- @type name: string
- @param dependencies: dependency
- @type dependencies: dependency object
+ @param fileContents: the complete contents of a MIB file
+ @type fileContents: string
+ @return: MIB definition strings
+ @rtype: list of strings
"""
- if name not in self.depMap:
- self.fileMap[filename] = name
- self.depMap[name] = (filename, dependencies)
+ if fileContents is None:
+ return []
+ DEFINITIONS = re.compile(r'([A-Za-z-0-9]+\s+DEFINITIONS'
+ '(\s+EXPLICIT TAGS|\s+IMPLICIT TAGS|\s+AUTOMATIC TAGS|\s*)'
+ '(\s+EXTENSIBILITY IMPLIED|\s*)\s*::=\s*BEGIN)')
+
+ definitionSpans = []
+ for definitionMatch in DEFINITIONS.finditer(fileContents):
+ definitionSpans.append(list(definitionMatch.span()))
+ # If more than one definiton in the file, set the end of the
+ # last span to the beginning of the current span
+ if len(definitionSpans) > 1:
+ definitionSpans[-2][1] = definitionSpans[-1][0]
+
+ # Use the start and end positions to create a string for each
+ # MIB definition
+ mibDefinitions = []
+ if definitionSpans:
+ # Set the end of the last span to the end of the file
+ definitionSpans[-1][1] = len(fileContents)
+ for definitionSpan in definitionSpans:
+ mibDefinitions.append(
+ fileContents[definitionSpan[0]:definitionSpan[1]])
- def getName(self, filename):
+ return mibDefinitions
+
+ def mapMibToDependents(self, mibDefinitions):
+ # ASN.1 syntax regular expressions for declaring MIBs
+ #
+ # An example from http://www.faqs.org/rfcs/rfc1212.html
+ #
+ # RFC1213-MIB DEFINITIONS ::= BEGIN
+ #
+ # IMPORTS
+ # experimental, OBJECT-TYPE, Counter
+ # FROM RFC1155-SMI;
+ #
+ # -- a MIB may or may not have an IMPORTS section
+ #
+ # root OBJECT IDENTIFIER ::= { experimental xx }
+ #
+ # END
+ IMPORTS = re.compile(r'\sIMPORTS\s.+;', re.DOTALL)
+ DEPENDENCIES = re.compile(
+ r'\sFROM\s+(?P<dependency>[A-Za-z-0-9]+)')
+
+ mibDependencies = []
+ for definition in mibDefinitions:
+ mibName = re.split(r'([A-Za-z0-9-]+)', definition)[1]
+ dependencies = set()
+ importsMatch = IMPORTS.search(definition)
+ if importsMatch:
+ imports = importsMatch.group()
+ for dependencyMatch in DEPENDENCIES.finditer(imports):
+ dependencies.add(dependencyMatch.group('dependency'))
+ self.mibs.append(mibName)
+ self.mibToDeps[mibName] = dependencies
+
+class PackageManager:
+ """
+ Given an URL, filename or archive (eg zip, tar), extract the files from
+ the package and return a list of filenames.
+ """
+ def __init__(self, log, downloaddir, extractdir):
"""
- Given a filename, return the name of the MIB tree defined in it.
- Makes the assumption that there's only one MIB tree per file.
+ Initialize the packagae manager.
- @param filename: MIB filename
- @type filename: string
- @return: MIB name
- @rtype: string
- @todo: to allow for multiple MIBs in a file, should return a list
+ @parameter log: logging object
+ @type log: logging class object
+ @parameter downloaddir: directory name to store downloads
+ @type downloaddir: string
+ @parameter extractdir: directory name to store downloads
+ @type extractdir: string
"""
- return self.fileMap.get(filename, None)
+ self.log = log
+ self.downloaddir = downloaddir
+ if self.downloaddir[-1] != '/':
+ self.downloaddir += '/'
+ self.extractdir = extractdir
+ if self.extractdir[-1] != '/':
+ self.extractdir += '/'
+ def downloadExtract(self, url):
+ """
+ Download and extract the list of filenames.
+ """
+ try:
+ localFile = self.download(url)
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.error("Problems downloading the file from %s: %s" % (
+ url, sys.exc_info()[1] ) )
+ return []
+ self.log.debug("Will attempt to load %s", localFile)
+ return self.processPackage(localFile)
- def getDependencies(self, name):
+ def download(self, url):
"""
- Given a name of the MIB tree, return the filename that it's from
- and its dependencies.
+ Download the package from the given URL, or if it's a filename,
+ return the filename.
+ """
+ urlParts = urlsplit(url)
+ schema = urlParts[0]
+ path = urlParts[2]
+ if not schema:
+ return os.path.abspath(url)
+ file = path.split(os.sep)[-1]
+ os.makedirs(self.downloaddir)
+ downloadFile = os.path.join(self.downloaddir, file)
+ self.log.debug("Downloading to file '%s'", downloadFile)
+ filename, _ = urllib.urlretrieve(url, downloadFile)
+ return filename
- @param name: name of MIB
- @type name: string
- @return: dependency tree
- @rtype: tuple of ( name, dependency object)
+ def processPackage(self, pkgFileName):
+ """
+ Figure out what type of file we have and extract out any
+ files and then enumerate the file names.
"""
- return self.depMap.get(name, None)
+ self.log.debug("Determining file type of %s" % pkgFileName)
+ if zipfile.is_zipfile(pkgFileName):
+ return self.unbundlePackage(pkgFileName, self.unzip)
+ elif tarfile.is_tarfile(pkgFileName):
+ return self.unbundlePackage(pkgFileName, self.untar)
+ elif os.path.isdir(pkgFileName):
+ return self.processDir(pkgFileName)
-class zenmib(ZCmdBase):
- """
- Wrapper around the smidump utilities to load MIB files into
- the DMD tree.
- """
- def map_file_to_dependents(self, mibfile):
+ else:
+ return [ os.path.abspath(pkgFileName) ]
+
+ def unzip(self, file):
+ """
+ Unzip the given file into the current directory and return
+ the directory in which files can be loaded.
"""
- Scan the MIB file to determine what MIB trees the file is dependent on.
+ pkgZip= zipfile.ZipFile(file, 'r')
+ if pkgZip.testzip() != None:
+ self.log.error("File %s is corrupted -- please download again", file)
+ return
- @param mibfile: MIB filename
- @type mibfile: string
- @return: dependency tree
- @rtype: tuple of ( name, dependency object)
- """
- # Slurp in the whole file
- fp = open(mibfile)
- mib = fp.read()
- fp.close()
- return self.map_mib_to_dependents(mib)
+ for file in pkgZip.namelist():
+ self.log.debug("Unzipping file/dir %s..." % file)
+ try:
+ if re.search(r'/$', file) != None:
+ os.makedirs(file)
+ else:
+ contents = pkgZip.read(file)
+ try:
+ unzipped = open(file, "w")
+ except IOError: # Directory missing?
+ os.makedirs(os.path.dirname(file))
+ unzipped = open(file, "w")
+ unzipped.write(contents)
+ unzipped.close()
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.error("Error in extracting %s because %s" % (
+ file, sys.exc_info()[1] ) )
+ return
- def map_mib_to_dependents(self, mib):
- # ASN.1 syntax regular expressions for declaring MIBs
- #
- # An example from http://www.faqs.org/rfcs/rfc1212.html
- #
- # RFC1213-MIB DEFINITIONS ::= BEGIN
- #
- # IMPORTS
- # experimental, OBJECT-TYPE, Counter
- # FROM RFC1155-SMI;
- #
- # -- contact IANA for actual number
- #
- # root OBJECT IDENTIFIER ::= { experimental xx }
- #
- # END
+ return os.getcwd()
- DEFINITIONS = re.compile(r'(?P<mib_name>[A-Za-z-0-9]+) +DEFINITIONS *::= *BEGIN')
- DEPENDS = re.compile(r'FROM *(?P<mib_dependency>[A-Za-z-0-9]+)')
+ def untar(self, file):
+ """
+ Given a tar file, extract from the tar into the current directory.
+ """
+ try:
+ self.log.debug("Extracting files from tar...")
+ pkgTar = tarfile.open(file, 'r')
+ for tarInfo in pkgTar:
+ pkgTar.extract(tarInfo)
+ pkgTar.close()
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.error("Error in un-tarring %s because %s" % ( file,
+ sys.exc_info()[1] ) )
+ return
+ return os.getcwd()
- # Split up the file and determine what OIDs need what other OIDs
- #
- # TODO: Fix the code so that it takes care of the case where there are multiple
- # OBJECT IDENTIFIER sections in the MIB.
- parts = mib.split('OBJECT IDENTIFIER', 1)
- mib_prologue = parts[0]
- match = DEFINITIONS.search(mib_prologue)
- if not match:
- # Special case: bootstrap MIB for the root of the MIB tree
- return None, []
+ def processDir(self, dir):
+ """
+ Note all of the files in a directory.
+ """
+ fileList = []
+ self.log.debug("Enumerating files in %s", dir)
+ if not os.path.isdir(dir):
+ self.log.debug("%s is not a directory", dir)
+ return []
+ for directoryName, _, fileNames in os.walk(dir):
+ for fileName in fileNames:
+ fileList.append(os.path.join(directoryName, fileName))
+ return fileList
- # Search through the prologue to find all of the IMPORTS.
- # An example from a real MIB
- #
- # IMPORTS
- # MODULE-IDENTITY, OBJECT-TYPE, enterprises, Integer32,
- # TimeTicks,NOTIFICATION-TYPE FROM SNMPv2-SMI
- # DisplayString FROM RFC1213-MIB
- # MODULE-COMPLIANCE, OBJECT-GROUP,
- # NOTIFICATION-GROUP FROM SNMPv2-CONF;
- depends = []
- name = match.group('mib_name')
- start = match.end(0) # Jump to past the first FROM token
- while 1:
- match = DEPENDS.search(mib_prologue, start)
- if not match:
- break
+ def unbundlePackage(self, package, unpackageMethod):
+ """
+ Extract the files and then add to the list of files.
+ """
+ self.makeExtractionDir()
+ baseDir = unpackageMethod(package)
+ if baseDir is not None:
+ return self.processDir(baseDir)
+ return []
+
+ def makeExtractionDir(self):
+ """
+ Create an uniquely named extraction directory starting from a base
+ extraction directory.
+ """
+ try:
+ if not os.path.isdir(self.extractdir):
+ os.makedirs(self.extractdir)
+ extractDir = tempfile.mkdtemp(prefix=self.extractdir)
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.error("Error in creating temp dir because %s",
+ sys.exc_info()[1] )
+ sys.exit(1)
+ os.chdir(extractDir)
+
+ def cleanup(self):
+ """
+ Remove any clutter left over from the installation.
+ """
+ self.cleanupDir(self.downloaddir)
+ self.cleanupDir(self.extractdir)
- depends.append(match.group('mib_dependency'))
+ def cleanupDir(self, dirName):
+ for root, dirs, files in os.walk(dirName, topdown=False):
+ if root == os.sep: # Should *never* get here
+ break
+ for name in files:
+ os.remove(os.path.join(root, name))
+ for name in dirs:
+ try:
+ os.removedirs(os.path.join(root, name))
+ except OSError:
+ pass
+ try:
+ os.removedirs(dirName)
+ except OSError:
+ pass
- # Skip to just past the next FROM token
- start = match.end(0)
- return name, depends
-
+class ZenMib(ZCmdBase):
+ """
+ Wrapper around the smidump utilities used to convert MIB definitions into
+ python code which is in turn loaded into the DMD tree.
+ """
+ def makeMibFileObj(self, fileName):
+ """
+ Scan the MIB file to determine what MIBs are defined in the file and
+ what their dependencies are.
+ @param fileName: MIB fileName
+ @type fileName: string
+ @return: dependencyDict, indexDict
+ dependencyDict - a dictionary that associates MIB definitions
+ found in fileName with their dependencies
+ indexDict - a dictionary that associates MIB definitions with their
+ ordinal position within fileName}
+ @rtype:
+ dependencyDict = {mibName: [string list of MIB definition names
+ that mibName is dependant on]}
+ indexDict = {mibname: number}
+ """
+ # Retrieve the entire contents of the MIB file
+ self.log.debug("Processing %s", fileName)
+ file = open(fileName)
+ fileContents = file.read()
+ file.close()
+ return MibFile(fileName, fileContents)
- def dependencies(self, filenames):
+ def populateDependencyMap(self, importFileNames, depFileNames):
"""
- Create a dependency map for all MIB files.
+ Populates the self.mibToMibFile instance object with data.
Exit the program if we're missing any files.
- @param filenames: names of MIB files to import
- @type filenames: list of strings
- @return: dependency tree
- @rtype: DependencyMap
- """
- missing_files = 0
- result = DependencyMap()
- for filename in filenames:
+ @param importFileNames: fully qualified file names of MIB files to import
+ @type importFileNames: list of strings
+ @param depFileNames: fully qualified file names of all MIB files
+ @type depFileNames: list of strings
+ @return: mibFileObjects of files to import
+ @rtype: MibFile
+ """
+ self.log.debug("Collecting MIB meta-data and creating depedency map.")
+ toImportMibFileObjs = []
+ for fileName in depFileNames.union(importFileNames):
try:
- defines, depends = self.map_file_to_dependents(filename)
-
+ mibFileObj = self.makeMibFileObj(fileName)
except IOError:
- self.log.error( "Couldn't open file %s", filename)
- missing_files += 1
+ self.log.error("Couldn't open file %s", fileName)
continue
- if defines == None:
- self.log.debug( "Unable to parse information from %s -- skipping", filename)
- else:
- result.add(filename, defines, depends)
-
- if missing_files > 0:
- self.log.error( "Missing %s files", missing_files )
- sys.exit(1)
-
- return result
+ mibDependencies = mibFileObj.mibToDeps
+ if not mibDependencies:
+ self.log.warn("Unable to parse information from "
+ "%s -- skipping", fileName)
+ continue
+ if fileName in importFileNames:
+ toImportMibFileObjs.append(mibFileObj)
+ for mibName, dependencies in mibDependencies.items():
+ self.mibToMibFile[mibName] = mibFileObj
+ return toImportMibFileObjs
- def getDependencies(self, filename, depMap):
+ def getDependencyFileNames(self, mibFileObj):
"""
smidump needs to know the list of dependent files for a MIB file in
order to properly resolve external references.
- @param filename: name of MIB file to import
- @type filename: string
- @param depMap: dependency tree
- @type depMap: DependencyMap
- @return: list of dependencies
+ @param mibFileObj: MibFile object
+ @type mibFileObj: MibFile
+ @return: list of dependency fileNames
@rtype: list of strings
"""
- # Sanity check: if a file doesn't need anything else, it
- # has no dependencies. Avoid further work.
- name = depMap.getName(filename)
- if not name:
- return []
+ dependencies = []
+ dependencyFileNames = set()
- # Find the files required by the OID tree in the file.
- deps = []
- def dependency_search(name):
+ def dependencySearch(mibName):
"""
- Create a list of files required by an OID.
+ Create a list of files required by a MIB definition.
- @param name: name of OID
- @type name: string
+ @param mibName: name of MIB definition
+ @type mibName: string
"""
- fileAndDeps = depMap.getDependencies(name)
- if not fileAndDeps:
- self.log.warn( "Unable to find a file providing the OID %s", name)
+ dependencies.append(mibName)
+ mibFileObj = self.mibToMibFile.get(mibName)
+ if not mibFileObj:
+ self.log.warn("Unable to find a file that defines %s", mibName)
return
- mib_file, dependent_oids = fileAndDeps
- if mib_file and mib_file not in deps:
- deps.append(mib_file)
-
- for unresolved_oid in dependent_oids:
- dependency_search(unresolved_oid)
-
- # Search the dependency map
- dependency_search(name)
- if deps[1:]:
- return deps[1:]
+ dependencyFileNames.add(mibFileObj.fileName)
+ for dependency in mibFileObj.mibToDeps[mibName]:
+ if dependency not in dependencies:
+ dependencySearch(dependency)
- return []
+ for mibName in mibFileObj.mibs:
+ dependencySearch(mibName)
+ dependencyFileNames.discard(mibFileObj.fileName)
+ return dependencyFileNames
-
- def generate_python_from_mib( self, mibname, dependencies ):
+ def savePythonCode(self, pythonCode, fileName):
"""
- Use the smidump program to convert a MIB into Python code"
+ Stores the smidump-generated Python code to a file.
+ """
+ if not os.path.exists(self.options.pythoncodedir):
+ self.options.keeppythoncode = False
+ self.log.warn('The directory %s to store converted MIB file code '
+ 'does not exist.' % self.options.pythoncodedir)
+ return
+ try:
+ pythonFileName = os.path.join(self.options.pythoncodedir,
+ os.path.basename(fileName) ) + '.py'
+ pythonFile = open(pythonFileName, 'w')
+ pythonFile.write(pythonCode)
+ pythonFile.close()
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.warn('Could not output converted MIB to %s' %
+ pythonFileName)
- @param mibname: name of the MIB
- @type mibname: string
- @param dependencies: list of dependent files
- @type dependencies: list of strings
- @return: the newly created MIB
- @rtype: MIB object
+ def generatePythonFromMib(self, fileName, dependencyFileNames,
+ mibNamesInFile):
"""
- dump_command = [ 'smidump', '-k', '-fpython' ]
- for dep in dependencies[1:]:
- # Add command-line flag for our dependent files
- dump_command.append( '-p')
- dump_command.append( dep )
+ Use the smidump program to convert a MIB into Python code.
- dump_command.append( mibname )
- self.log.debug('Running %s', ' '.join( dump_command))
- proc = Popen( dump_command, stdout=PIPE, stderr=PIPE )
+ One major caveat: smidump by default only outputs the last MIB
+ definition in a file. For that matter, it always outputs the last MIB
+ definition in a file whether it is requested or not. Therefore, if
+ there are multiple MIB definitions in a file, all but the last must be
+ explicitly named on the command line. If you name the last, it will
+ come out twice. We don't want that.
+
+ OK, so we need to determine if there are multiple MIB definitions
+ in fileName and then add all but the last to the command line. That
+ works except the resulting python code will create a dictionary
+ for each MIB definition, all of them named MIB. Executing the code is
+ equivalent to running a=1; a=2; a=3. You only wind up with a=3.
+ Therefore, we separate each dictionary definition into its own string
+ and return a list of strings so each one can be executed individually.
+
+ @param fileName: name of the file containing MIB definitions
+ @type fileName: string
+ @param dependencyFileNames: list of fileNames that fileName is
+ dependent on
+ @type dependencyFileNames: list of strings
+ @param mibNamesInFile: names of MIB definitions in file
+ @type mibNamesInFile: list of strings
+ @return: list of dictionaries. Each dictionary containing the contents
+ of a MIB definition. [ {'mibName': MIB data} ]
+ @rtype: list
+ """
+ def infiniteLoopHandler(signum, frame):
+ """
+ Kills any smidump commands that have probably locked themselves
+ into an infinite loop.
+ """
+ log.error("The command %s has probably gone into an infinite loop",
+ ' '.join(dumpCommand))
+ log.error("Killing process id %s ...", proc.pid)
+ try:
+ os.kill(proc.pid, signal.SIGKILL)
+ except OSError:
+ pass
+
- python_code, warnings = proc.communicate()
+ dumpCommand = ['smidump', '--keep-going', '--format', 'python']
+ for dependencyFileName in dependencyFileNames:
+ # Add command-line flag for our dependent files
+ dumpCommand.append('--preload')
+ dumpCommand.append(dependencyFileName)
+ dumpCommand.append(fileName)
+
+ # If more than one MIB definition exists in the file, name all but the
+ # last on the command line. (See method description for reasons.)
+ if len(mibNamesInFile) > 1:
+ dumpCommand += mibNamesInFile[:-1]
+
+ self.log.debug('Running %s', ' '.join(dumpCommand))
+ proc = Popen(dumpCommand, stdout=PIPE, stderr=PIPE)
+
+ log = self.log
+ signal.signal(signal.SIGALRM, infiniteLoopHandler)
+ signal.alarm(self.options.smidumptimeout)
+ pythonCode, warnings = proc.communicate()
proc.wait()
+ signal.alarm(0) # Disable the alarm
if proc.returncode:
- self.log.error(warnings)
+ if warnings.strip():
+ self.log.error(warnings)
return None
- if len(warnings) > 0:
+ if warnings:
self.log.debug("Found warnings while trying to import MIB:\n%s" \
% warnings)
- # Now we'll be brave and try to execute the MIB-to-python code
- # and store the resulting dictionary in 'result'
- result = {}
- try:
- exec python_code in result
+ if self.options.keeppythoncode:
+ self.savePythonCode(pythonCode, fileName)
- except (SystemExit, KeyboardInterrupt): raise
- except:
- self.log.exception("Unable to import Pythonized-MIB: %s", mibname)
- return None
+ return self.evalPythonToMibs(pythonCode, fileName)
+
+ def evalPythonToMibs(self, pythonCode, name):
+ """
+ Evaluate the code and return an array of MIB dictionaries.
+ """
+ def executePythonCode(pythonCode, name):
+ """
+ Executes the python code generated smidump
+
+ @param pythonCode: Code generated by smidump
+ @type pythonCode: string
+ @return: a dictionary which contains one key: MIB
+ @rtype: dictionary
+ """
+ result = {}
+ try:
+ exec pythonCode in result
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.exception("Unable to import Pythonized-MIB: %s",
+ name)
+ return result.get('MIB', None)
+
+ # If more than one MIB definition exists in a file, pythonCode will
+ # contain a 'MIB = {...}' section for each MIB definition. We must
+ # split each section into its own string and return a string list.
+ smiMibDelim = 'MIB = {'
+ mibCodeParts = pythonCode.split(smiMibDelim)
+ mibDicts = []
+ if len(mibCodeParts) > 1:
+ for mibCodePart in mibCodeParts[1:]:
+ mibDict = executePythonCode(smiMibDelim + mibCodePart, name)
+ if mibDict is not None:
+ mibDicts.append(mibDict)
+ else:
+ mibDict = executePythonCode(pythonCode, name)
+ if mibDict is not None:
+ mibDicts = [mibDict]
+
+ return mibDicts
+
+ def evalAddSavedPythonCode(self):
+ """
+ Read the file named in the command-line option, evaluate it, and add
+ it to our list of MIBs.
+ """
+ pythonMibs = []
+ for fileName in set(self.options.evalSavedPython):
+ try:
+ pythonCode = open(fileName).read()
+ except IOError:
+ self.log.warn("Unable to open '%s' -- skipping",
+ fileName)
+ pythonMibs += self.evalPythonToMibs(pythonCode, fileName)
- # Now look for the start of the MIB
- mib = result.get( 'MIB', None)
- return mib
+ self.loadPythonMibs(pythonMibs)
+ def getDmdMibDict(self, dmdMibDict, mibOrganizer):
+ """
+ Populate a dictionary containing the MIB definitions that have been
+ loaded into the DMD Mibs directory
- # Standard MIB attributes that we expect in all MIBs
- MIB_MOD_ATTS = ('language', 'contact', 'description')
+ @param dmdMibDict: maps a MIB definition to the path where
+ it is located with in the DMD.
+ Format:
+ {'mibName': 'DMD path where mibName is stored'}
+ Example: MIB-Dell-10892 is located in the DMD tree at
+ Mibs/SITE/Dell, Directory entry is
+ {'MIB-Dell-10892': '/SITE/Dell'] }
+ @param mibOrganizer: the DMD directory to be searched
+ @type mibOrganizer: MibOrganizer
+ """
+ organizerPath = mibOrganizer.getOrganizerName()
- def load_mib(self, mibs, mibname, depmap):
+ # Record each module from this organizer as a dictionary entry.
+ # mibOrganizer.mibs.objectItems() returns tuple:
+ # ('mibName', <MibModule at /zport/dmd/Mibs/...>)
+ for mibModule in mibOrganizer.mibs.objectItems():
+ mibName = mibModule[0]
+ if mibName not in dmdMibDict:
+ dmdMibDict[mibName] = organizerPath
+ else:
+ self.log.warn('\nFound two copies of %s:'
+ ' %s and %s' %
+ (mibName, dmdMibDict[mibName],
+ mibOrganizer.getOrganizerName()))
+
+ # If there are suborganizers, recurse into them
+ for childOrganizer in mibOrganizer.children():
+ self.getDmdMibDict(dmdMibDict, childOrganizer)
+
+ def addMibEntries(self, leafType, pythonMib, mibModule):
"""
- Attempt to load a MIB after generating its dependency tree
+ Add the different MIB leaves (ie nodes, notifications) into the DMD.
- @param mibs: filenames of the MIBs to load
- @type mibs: list of strings
- @param mibname: name of the MIB
- @type mibname: string
- @param dependencies: list of dependent files
- @type dependencies: string
+ @paramater leafType: 'nodes', 'notifications'
+ @type leafType: string
+ @paramater pythonMib: dictionary of nodes and notifications
+ @type pythonMib: dictionary
+ @paramater mibModule: class containing functions to load leaves
+ @type mibModule: class
+ @return: number of leaves added
+ @rtype: int
+ """
+ entriesAdded = 0
+ functor = { 'nodes':mibModule.createMibNode,
+ 'notifications':mibModule.createMibNotification,
+ }.get(leafType, None)
+ if not functor or leafType not in pythonMib:
+ return entriesAdded
+
+ for name, values in pythonMib[leafType].items():
+ try:
+ functor(name, **values)
+ entriesAdded += 1
+ except BadRequest:
+ try:
+ self.log.warn("Unable to add %s id '%s' as this"
+ " name is reserved for use by Zope",
+ leafType, name)
+ newName = '_'.join([name, mibName])
+ self.log.warn("Trying to add %s '%s' as '%s'",
+ leafType, name, newName)
+ functor(newName, **values)
+ self.log.warn("Renamed '%s' to '%s' and added to"
+ " MIB %s", name, newName, leafType)
+ except (SystemExit, KeyboardInterrupt): raise
+ except:
+ self.log.warn("Unable to add %s id '%s' -- skipping",
+ leafType, name)
+ return entriesAdded
+
+ def loadMibFile(self, mibFileObj, dmdMibDict):
+ """
+ Attempt to load the MIB definitions in fileName into DMD
+
+ @param fileName: name of the MIB file to be loaded
+ @type fileName: string
@return: whether the MIB load was successful or not
@rtype: boolean
"""
- dependencies = self.getDependencies(mibname, depmap)
+ fileName = mibFileObj.fileName
+ self.log.debug('Attempting to load %s' % fileName)
- mib = self.generate_python_from_mib( mibname, dependencies )
- if not mib:
+ # Check to see if any MIB definitions in fileName have already
+ # been loaded into Zenoss. If so, warn but don't fail
+ mibNamesInFile = mibFileObj.mibs
+ for mibName in mibNamesInFile:
+ if mibName in dmdMibDict:
+ dmdMibPath = dmdMibDict[mibName]
+ self.log.warn('MIB definition %s found in %s is already '
+ 'loaded at %s.' % (mibName, fileName, dmdMibPath))
+
+ # Retrieve a list of all the files containing MIB definitions that are
+ # required by the MIB definitions in fileName
+ dependencyFileNames = self.getDependencyFileNames(mibFileObj)
+
+ # Convert the MIB file data into python dictionaries. pythonMibs
+ # contains a list of dictionaries, one for each MIB definition in
+ # fileName.
+ pythonMibs = self.generatePythonFromMib(fileName, dependencyFileNames,
+ mibNamesInFile)
+ if not pythonMibs:
return False
- # Check for duplicates -- or maybe not...
- modname = mib['moduleName']
- # TODO: Find out Why this is commented out
- #mod = mibs.findMibModule(modname)
- mod = None
- if mod:
- self.log.warn( "Skipping %s as it is already loaded", modname)
- return False
+ self.loadPythonMibs(pythonMibs)
+ return True
- # Create the container for the MIBs and define meta-data
- # In the DMD this creates another container class which
- # contains mibnodes. These data types are found in
- # Products.ZenModel.MibModule and Products.ZenModel.MibNode
- mod = mibs.createMibModule(modname, self.options.path)
- for key, val in mib[modname].items():
- if key in self.MIB_MOD_ATTS:
- setattr(mod, key, val)
-
- # Add regular OIDs to the mibmodule + mibnode relationship tree
- nodes_added = 0
- if 'nodes' in mib:
- for name, values in mib['nodes'].items():
- try:
- mod.createMibNode(name, **values)
- nodes_added += 1
- except BadRequest:
- try:
- self.log.warn("Unable to add node id '%s' as this"
- " name is reserved for use by Zope",
- name)
- newName = '_'.join([name, modname])
- self.log.warn("Trying to add node '%s' as '%s'",
- name, newName)
- mod.createMibNode(newName, **values)
- self.log.warn("Renamed '%s' to '%s' and added to MIB"
- " nodes", name, newName)
- except:
- self.log.warn("Unable to add '%s' -- skipping",
- name)
-
- # Put SNMP trap information into Products.ZenModel.MibNotification
- traps_added = 0
- if 'notifications' in mib:
- for name, values in mib['notifications'].items():
- try:
- mod.createMibNotification(name, **values)
- traps_added += 1
- except BadRequest:
- try:
- self.log.warn("Unable to add trap id '%s' as this"
- " name is reserved for use by Zope",
- name)
- newName = '_'.join([name, modname])
- self.log.warn("Trying to add trap '%s' as '%s'",
- name, newName)
- mod.createMibNotification(newName, **values)
- self.log.warn("Renamed '%s' to '%s' and added to MIB"
- " traps", name, newName)
- except:
- self.log.warn("Unable to add '%s' -- skipping",
- name)
-
- self.log.info("Parsed %d nodes and %d notifications", nodes_added,
- traps_added)
-
- # Add the MIB tree permanently to the DMD except if we get the
- # --nocommit flag.
- if not self.options.nocommit:
- trans = transaction.get()
- trans.setUser( "zenmib" )
- trans.note("Loaded MIB %s into the DMD" % modname)
- trans.commit()
- self.log.info("Loaded MIB %s into the DMD", modname)
+ def loadPythonMibs(self, pythonMibs):
+ """
+ Walk through the MIB dictionaries and add the MIBs to the DMD.
+ """
+ # Standard MIB attributes that we expect in all MIBs
+ MIB_MOD_ATTS = ('language', 'contact', 'description')
- return True
+ # Add the MIB data for each MIB into Zenoss
+ for pythonMib in pythonMibs:
+ mibName = pythonMib['moduleName']
+
+ # Create the container for the MIBs and define meta-data.
+ # In the DMD this creates another container class which
+ # contains mibnodes. These data types are found in
+ # Products.ZenModel.MibModule and Products.ZenModel.MibNode
+ mibModule = self.dmd.Mibs.createMibModule(
+ mibName, self.options.path)
+ for key, val in pythonMib[mibName].items():
+ if key in MIB_MOD_ATTS:
+ setattr(mibModule, key, val)
+
+ nodesAdded = self.addMibEntries('nodes', pythonMib, mibModule)
+ trapsAdded = self.addMibEntries('notifications', pythonMib, mibModule)
+ self.log.info("Parsed %d nodes and %d notifications from %s",
+ nodesAdded, trapsAdded, mibName)
+
+ # Add the MIB tree permanently to the DMD unless --nocommit flag.
+ if not self.options.nocommit:
+ trans = transaction.get()
+ trans.setUser("zenmib")
+ trans.note("Loaded MIB %s into the DMD" % mibName)
+ trans.commit()
+ self.log.info("Loaded MIB %s into the DMD", mibName)
+ def getAllMibDepFileNames(self):
+ """
+ Use command line parameters to create a list of files containing MIB
+ definitions that will be used as a reference list for the files being
+ loaded into the DMD
+ @return: set of file names
+ @rtype: set
+ """
+ defaultMibDepDirs = [ 'ietf', 'iana', 'irtf', 'tubs', 'site' ]
+ mibDepFileNames = set()
+ for subdir in defaultMibDepDirs:
+ depDir = os.path.join(self.options.mibdepsdir, subdir)
+ mibDepFileNames.update(self.pkgMgr.processDir(depDir))
+ return mibDepFileNames
- def main(self):
+ def getMibsToImport(self):
"""
- Main loop of the program
+ Uses command-line parameters to create a list of files containing MIB
+ definitions that are to be loaded into the DMD
+
+ @return: list of file names that are to be loaded into the DMD
+ @rtype: list
"""
- # Prepare to load the default MIBs
- smimibdir = self.options.mibsdir
- if not os.path.exists( smimibdir ):
- self.log.error("The directory %s doesn't exist!" % smimibdir )
- sys.exit(1)
+ loadFileNames = []
+ if self.args:
+ for fileName in self.args:
+ loadFileNames.extend(self.pkgMgr.downloadExtract(fileName))
+ else:
+ loadFileNames = self.pkgMgr.processDir(self.options.mibsdir)
- ietf, iana, irtf, tubs, site = \
- map(lambda x: os.path.join(smimibdir, x),
- 'ietf iana irtf tubs site'.split())
-
- # Either load MIBs from the command-line or from the default
- # location where MIBs are stored by Zenoss.
- if len(self.args) > 0:
- mibnames = self.args
- depMap = self.dependencies(list(walk(ietf, iana, irtf, tubs))
- + mibnames)
+ if loadFileNames:
+ self.log.debug("Will attempt to load the following files: %s",
+ loadFileNames)
else:
- mibnames = glob.glob(os.path.join(smimibdir, 'site', '*'))
- depMap = self.dependencies(walk(ietf, iana, irtf, tubs, site))
+ self.log.error("No MIB files to load!")
+ sys.exit(1)
- # Make connection to the DMD at the start of the MIB tree
- mibs = self.dmd.Mibs
+ return set(loadFileNames)
- # Process all of the MIBs that we've found
- loaded_mib_files = 0
- for mibname in mibnames:
- try:
- if self.load_mib( mibs, mibname, depMap):
- loaded_mib_files += 1
+ def main(self):
+ """
+ Main loop of the program
+ """
+ if self.options.evalSavedPython:
+ self.evalAddSavedPythonCode()
+ return
+
+ # Verify MIBs search directory is valid. Fail if not
+ if not os.path.exists(self.options.mibsdir):
+ self.log.error("The directory %s doesn't exist!" %
+ self.options.mibsdir )
+ sys.exit(1)
+ self.pkgMgr = PackageManager(self.log, self.options.downloaddir,
+ self.options.extractdir)
+ self.mibToMibFile = {}
+
+ requestedFiles = self.getMibsToImport()
+ mibDepFileNames = self.getAllMibDepFileNames()
+ mibFileObjs = self.populateDependencyMap(requestedFiles, mibDepFileNames)
+
+ # dmdMibDict = {'mibName': 'DMD path to MIB'}
+ dmdMibDict = {}
+ self.getDmdMibDict(dmdMibDict, self.dmd.Mibs)
+
+ # Load the MIB files
+ self.log.info("Found %d MIBs to import.", len(mibFileObjs))
+ loadedMibFiles = 0
+ for mibFileObj in mibFileObjs:
+ try:
+ if self.loadMibFile(mibFileObj, dmdMibDict):
+ loadedMibFiles += 1
except (SystemExit, KeyboardInterrupt): raise
except Exception, ex:
- self.log.exception("Failed to load MIB: %s" % mibname)
+ self.log.exception("Failed to load MIB: %s", mibFileObj.fileName)
action = "Loaded"
if self.options.nocommit:
action = "Processed"
- self.log.info( "%s %d MIB file(s)" % ( action, loaded_mib_files))
+
+ self.log.info("%s %d MIB file(s)" % (action, loadedMibFiles))
+ self.pkgMgr.cleanup()
+
sys.exit(0)
-
def buildOptions(self):
"""
Command-line options
"""
ZCmdBase.buildOptions(self)
- self.parser.add_option('--mibsdir',
- dest='mibsdir', default=zenPath('share/mibs'),
- help="Directory of input MIB files [ default: %default ]")
- self.parser.add_option('--path',
+ self.parser.add_option('--mibsdir',
+ dest='mibsdir', default=zenPath('share/mibs/site'),
+ help="Directory of input MIB files [ default: %default ]")
+ self.parser.add_option('--mibdepsdir',
+ dest='mibdepsdir', default=zenPath('share/mibs'),
+ help="Directory of input MIB files [ default: %default ]")
+ self.parser.add_option('--path',
dest='path', default="/",
help="Path to load MIB into the DMD")
self.parser.add_option('--nocommit', action='store_true',
dest='nocommit', default=False,
- help="Don't commit the MIB to the DMD after loading")
+ help="Don't commit the MIB to the DMD after loading")
+ self.parser.add_option('--keeppythoncode', action='store_true',
+ dest='keeppythoncode', default=False,
+ help="Don't commit the MIB to the DMD after loading")
+ self.parser.add_option('--pythoncodedir', dest='pythoncodedir',
+ default=tempfile.gettempdir() + "/mib_pythoncode/",
+ help="This is the directory where the converted MIB will be output. " \
+ "[ default: %default ]")
+ self.parser.add_option('--downloaddir', dest='downloaddir',
+ default=tempfile.gettempdir() + "/mib_downloads/",
+ help="This is the directory where the MIB will be downloaded. " \
+ "[ default: %default ]")
+ self.parser.add_option('--extractdir', dest='extractdir',
+ default=tempfile.gettempdir() + "/mib_extract/",
+ help="This is the directory where unzipped MIB files will be stored. " \
+ "[ default: %default ]")
+ self.parser.add_option('--smidumptimeout', dest='smidumptimeout',
+ default=60,
+ help="Kill smidump after this many seconds to " \
+ "stop infinite loops.")
+ self.parser.add_option('--evalSavedPython', dest='evalSavedPython',
+ default=[], action='append',
+ help="Execute the Python code previously generated" \
+ " and saved.")
if __name__ == '__main__':
- zm = zenmib()
+ zm = ZenMib()
zm.main()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenRRD/zenperfsnmp.py
^
|
@@ -553,14 +553,15 @@
log.info("Getting threshold classes...")
yield self.model().callRemote('getThresholdClasses')
self.remote_updateThresholdClasses(driver.next())
-
- log.info("Checking for outdated configs...")
- current = [(k, v.lastChange) for k, v in self.proxies.items()]
- yield self.model().callRemote('getDeviceUpdates', current)
- devices = driver.next()
+ devices = []
if self.options.device:
devices = [self.options.device]
+ else:
+ log.info("Checking for outdated configs...")
+ current = [(k, v.lastChange) for k, v in self.proxies.items()]
+ yield self.model().callRemote('getDeviceUpdates', current)
+ devices = driver.next()
log.info("Fetching configs for %s", repr(devices)[0:800]+'...')
yield self.model().callRemote('getDevices', devices)
@@ -702,6 +703,9 @@
"""
Allows zenhub to delete a device from our configuration
"""
+ if self.options.device and doomed != self.options.device:
+ return
+
self.log.debug("Async delete device %s" % doomed)
if doomed in self.proxies:
del self.proxies[doomed]
@@ -711,6 +715,9 @@
"""
Allows zenhub to update our device configuration
"""
+ if self.options.device and snmpTargets.device != self.options.device:
+ return
+
self.log.debug("Device updates from zenhub received")
self.updateDeviceConfig(snmpTargets)
@@ -722,7 +729,6 @@
as well as its pickle file.
If no SNMP proxy created for the device, create one.
"""
-
self.log.debug("Received config for %s", configs.device)
p = self.updateAgentProxy(configs.device, configs.connInfo)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/ReportMail.py
^
|
@@ -20,6 +20,7 @@
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
+from email.MIMEBase import MIMEBase
import Globals
from Products.ZenUtils.ZenScriptBase import ZenScriptBase
@@ -37,7 +38,7 @@
content pane. Url references are turned into absolute references,
and images are sent with the page."""
- def __init__(self, user, passwd, div):
+ def __init__(self, user, passwd, div, comment):
HTMLParser.__init__(self)
self.user = user
self.passwd = passwd
@@ -47,6 +48,8 @@
self.inTitle = False
self.title = ''
self.div = div
+ self.comment = comment
+ self.csv = None
def fetchImage(self, url):
return self.slurp(url).read()
@@ -116,6 +119,9 @@
if self.inTitle:
self.title += data
+ def handleCSV(self, data):
+ self.csv = data
+
def slurp(self, url):
req = urllib2.Request(url)
encoded = base64.encodestring('%s:%s' % (self.user, self.passwd))[:-1]
@@ -129,13 +135,31 @@
def fetch(self, url):
self.base = url.strip()
- self.feed(self.slurp(url).read())
+ response = self.slurp(url)
+
+ # Handle CSV.
+ if hasattr(response, 'headers') and \
+ response.headers.get('Content-Type') == 'application/vnd.ms-excel':
+ self.handleCSV(response.read())
+ else:
+ # Handle everything else as HTML.
+ self.feed(response.read())
def mail(self):
msg = MIMEMultipart('related')
msg.preamble = 'This is a multi-part message in MIME format'
- txt = MIMEText(''.join(self.html), 'html')
- msg.attach(txt)
+ if self.csv is not None:
+ txt = MIMEText(self.comment, 'plain')
+ msg.attach(txt)
+ csv = MIMEBase('application', 'vnd.ms-excel')
+ csv.add_header('Content-ID', '<Zenoss Report>')
+ csv.add_header('Content-Disposition', 'attachment',
+ filename='zenoss_report.csv')
+ csv.set_payload(self.csv)
+ msg.attach(csv)
+ else:
+ txt = MIMEText(''.join(self.html), 'html')
+ msg.attach(txt)
for url, (name, img) in self.images.items():
ctype, encoding = mimetypes.guess_type(url)
if ctype == None:
@@ -144,7 +168,6 @@
img = MIMEImage(img, subtype)
img.add_header('Content-ID', '<%s>' % name)
msg.attach(img)
- msg['Subject'] = self.title
return msg
class NoDestinationAddressForUser(Exception): pass
@@ -170,11 +193,15 @@
if not o.addresses:
self.log.error("No address for user %s" % o.user)
sys.exit(1)
- page = Page(o.user, o.passwd, o.div)
+ page = Page(o.user, o.passwd, o.div, o.comment)
page.fetch(o.url)
msg = page.mail()
if o.subject:
msg['Subject'] = o.subject
+ elif page.title:
+ msg['Subject'] = page.title
+ else:
+ msg['Subject'] = 'Zenoss Report'
msg['From'] = o.fromAddress
msg['To'] = ', '.join(o.addresses)
result, errorMsg = Utils.sendEmail(msg,
@@ -224,6 +251,10 @@
dest='div',
default='contentPane',
help='DIV to extract from URL')
+ self.parser.add_option('--comment', '-c',
+ dest='comment',
+ default='Report CSV attached.',
+ help='Comment to include in body of CSV reports')
if __name__ == '__main__':
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/Utilization.py
^
|
@@ -17,6 +17,7 @@
startDate = args.get('startDate', zem.defaultAvailabilityStart())
endDate = args.get('endDate', zem.defaultAvailabilityEnd())
startDate, endDate = map(Time.ParseUSDate, (startDate, endDate))
+ endDate = Time.getEndOfDay(endDate)
startDate = min(startDate, endDate - 1)
how = args.get('how', 'AVERAGE')
return dict(start=startDate, end=endDate, function=how)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/reports/Performance_Reports/CPU_Utilization.rpt
^
|
@@ -11,7 +11,7 @@
endDate python:sts(tableName, 'endDate',
zem.defaultAvailabilityEnd());
how python:sts(tableName, 'how', 'AVERAGE');
- summaryOptions python:('AVERAGE', 'MAXIMUM');
+ summaryOptions python:('AVERAGE', 'MAXIMUM', 'MINIMUM', 'LAST');
deviceClass python:sts(tableName, 'deviceClass', '/');
deviceFilter python:sts(tableName, 'deviceFilter', '');
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/reports/Performance_Reports/Filesystem_Util_Report.rpt
^
|
@@ -11,7 +11,7 @@
endDate python:sts(tableName, 'endDate',
zem.defaultAvailabilityEnd());
how python:sts(tableName, 'how', 'AVERAGE');
- summaryOptions python:('AVERAGE', 'MAXIMUM');
+ summaryOptions python:('AVERAGE', 'MAXIMUM', 'MINIMUM', 'LAST');
deviceClass python:sts(tableName, 'deviceClass', '/');
deviceFilter python:sts(tableName, 'deviceFilter', '');
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/reports/Performance_Reports/Interface_Utilization.rpt
^
|
@@ -11,7 +11,7 @@
endDate python:sts(tableName, 'endDate',
zem.defaultAvailabilityEnd());
how python:sts(tableName, 'how', 'AVERAGE');
- summaryOptions python:('AVERAGE', 'MAXIMUM');
+ summaryOptions python:('AVERAGE', 'MAXIMUM', 'MINIMUM', 'LAST');
deviceClass python:sts(tableName, 'deviceClass', '/');
deviceFilter python:sts(tableName, 'deviceFilter', '');
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenReports/reports/Performance_Reports/Memory_Utilization.rpt
^
|
@@ -11,7 +11,7 @@
endDate python:sts(tableName, 'endDate',
zem.defaultAvailabilityEnd());
how python:sts(tableName, 'how', 'AVERAGE');
- summaryOptions python:('AVERAGE', 'MAXIMUM');
+ summaryOptions python:('AVERAGE', 'MAXIMUM', 'MINIMUM', 'LAST');
deviceClass python:sts(tableName, 'deviceClass', '/');
deviceFilter python:sts(tableName, 'deviceFilter', '');
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenStatus/zenstatus.py
^
|
@@ -407,6 +407,7 @@
@parameter job: device and TCP service to test
@type job: ZenTcpClient object
"""
+ self.runSomeJobs()
key = job.cfg.device, job.cfg.component
evt = job.getEvent()
if evt:
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/console.pt
^
|
@@ -28,12 +28,12 @@
<table>
<tr><td class="dt">Device:</td>
<td><tpl if="device"><a href="{device_url}"
- class="resource-link">{device}</a></tpl></td>
+ class="resource-link">{device_title}</a></tpl></td>
</tr>
<tr><td class="dt">Component:</td>
<td><tpl if="component">
<a href="{component_url}"
- class="resource-link">{component}</a></tpl></td>
+ class="resource-link">{component_title}</a></tpl></td>
</tr>
<tr><td class="dt">Event Class:</td>
<td><tpl if="eventClass"><a href="{eventClass_url}"
@@ -329,6 +329,7 @@
* ACKNOWLEDGE BUTTON
*/
//text: 'Acknowledge',
+ id: 'ack-button',
iconCls: 'acknowledge',
tooltip: 'Acknowledge selected events',
handler: function() {
@@ -351,6 +352,7 @@
* CLOSE BUTTON
*/
//text: 'Close',
+ id: 'close-button',
iconCls: 'close',
tooltip: 'Close selected events',
handler: function(){
@@ -372,11 +374,13 @@
* ClASSIFY BUTTON
*/
//text: 'Classify',
+ id: 'classify-button',
tooltip: 'Map selected events to an event class',
iconCls: 'classify',
handler: showClassifyDialog
},{
//text: 'Unacknowledge',
+ id: 'unack-button',
iconCls: 'unacknowledge',
tooltip: 'Return selected events to New status',
handler: function() {
@@ -396,6 +400,7 @@
* ADD BUTTON
*/
//text: 'Add',
+ id: 'add-button',
tooltip: 'Add an event',
iconCls: 'add',
handler: showAddEventDialog
@@ -504,6 +509,16 @@
view.toggleRowColors(checked);
}
},{
+ id: 'livesearch_checkitem',
+ checked: true,
+ xtype: 'menucheckitem',
+ text: 'Enable live search',
+ handler: function(checkitem) {
+ var checked = !checkitem.checked;
+ var view = Ext.getCmp('events_grid').getView();
+ view.toggleLiveSearch(checked);
+ }
+ },{
id: 'clearfilters',
text: 'Clear filters',
listeners: {
@@ -593,15 +608,34 @@
var myView = new Zenoss.FilterGridView({
nearLimit : 20,
filterbutton: 'showfilters',
+ defaultFilters: {
+ severity: [5, 4, 3, 2],
+ eventState: [0, 1]
+ },
rowcoloritem: 'rowcolors_checkitem',
+ livesearchitem: 'livesearch_checkitem',
loadMask : { msg : 'Loading. Please wait...' }
});
// Store to hold the events data
var console_store = new Ext.ux.grid.livegrid.Store({
autoLoad: true,
- proxy: new Ext.data.DirectProxy({
- directFn:Zenoss.remote.EventConsole.query
+ proxy: new Zenoss.ThrottlingProxy({
+ directFn:Zenoss.remote.EventConsole.query,
+ listeners: {'load': function(proxy, transaction, options) {
+ var disabled = transaction.result.disabled;
+ var buttonIds = [
+ 'ack-button',
+ 'close-button',
+ 'classify-button',
+ 'unack-button',
+ 'add-button'
+ ];
+ Ext.each(buttonIds, function(buttonId) {
+ var button = Ext.getCmp(buttonId);
+ button.setDisabled(disabled);
+ });
+ }}
}),
bufferSize: 100,
defaultSort: {field:'severity', direction:'DESC'},
@@ -616,8 +650,10 @@
'evid',
'device',
'device_url',
+ 'device_title',
'component',
'component_url',
+ 'component_title',
'summary',
'eventState',
'eventClass',
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/detail.pt
^
|
@@ -22,12 +22,12 @@
<table>
<tr><td class="dt">Device:</td>
<td><tpl if="device"><a href="{device_url}"
- class="resource-link">{device}</a></tpl></td>
+ class="resource-link">{device_title}</a></tpl></td>
</tr>
<tr><td class="dt">Component:</td>
<td><tpl if="component">
<a href="{component_url}"
- class="resource-link">{component}</a></tpl></td>
+ class="resource-link">{component_title}</a></tpl></td>
</tr>
<tr><td class="dt">Event Class:</td>
<td><tpl if="eventClass"><a href="{eventClass_url}"
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/events.py
^
|
@@ -47,12 +47,16 @@
def _get_device_url(self, devname):
dev = self.context.dmd.Devices.findDevice(devname)
if dev:
- return dev.absolute_url_path()
+ return dev.absolute_url_path(), dev.titleOrId()
+ else:
+ return None, None
def _get_component_url(self, dev, comp):
comps = self.context.dmd.searchComponents(dev, comp)
if comps:
- return comps[0].absolute_url_path()
+ return comps[0].absolute_url_path(), comps[0].titleOrId()
+ else:
+ return None, None
def _get_eventClass_url(self, evclass):
return '/zport/dmd/Events' + evclass
@@ -71,13 +75,21 @@
elif field == 'eventClass':
data['eventClass_url'] = self._get_eventClass_url(value)
elif field == 'device':
- url = self._get_device_url(value)
+ url, titleOrId = self._get_device_url(value)
if url: data['device_url'] = url
+ if titleOrId:
+ data['device_title'] = titleOrId
+ else:
+ data['device_title'] = value
elif field == 'component':
dev = getattr(zevent, 'device', None)
if dev:
- url = self._get_component_url(dev, value)
+ url, titleOrId = self._get_component_url(dev, value)
if url: data['component_url'] = url
+ if titleOrId:
+ data['component_title'] = titleOrId
+ else:
+ data['component_title'] = value
else:
value = _shortvalue
data[field] = value
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/grid.py
^
|
@@ -13,6 +13,7 @@
from zope.interface import implements
import time
+import AccessControl
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
@@ -43,8 +44,32 @@
def _evmgr(self, forceHistory=False):
return IEventManagerProxy(self).event_manager(forceHistory)
- # BEGIN PUBLIC METHODS
+ def _checkPermission(self, permission):
+ """
+ Return true if the current user has the specified permission on dmd,
+ otherwise return false.
+ """
+ manager = AccessControl.getSecurityManager()
+ return manager.checkPermission(permission, self.context)
+
+ def _require(self, permission, functionName):
+ """
+ Raise if the current user doesn't have the specified permission for
+ the context.
+ """
+ if not self._checkPermission(permission):
+ msg = 'Calling %s requires "%s" permission.'
+ args = (functionName, permission)
+ raise Exception(msg % args)
+ # BEGIN PUBLIC METHODS
+ def _getOrderByClause(self, sort, dir):
+ secondarySort = self._evmgr().defaultOrderby
+ if not secondarySort:
+ secondarySort = 'lastTime DESC'
+ orderBy = "%s %s, %s" % (sort, dir, secondarySort)
+ return orderBy
+
def query(self, limit, start, sort, dir, evid=None, params=None):
"""
Data that populates the event console.
@@ -55,25 +80,25 @@
start = max(start, 0)
if hasattr(context, 'getResultFields'):
- fields = context.getResultFields()
+ fields = context.getResultFields()
else:
- # Use default result fields
- if hasattr(context, 'event_key'):
- base = context
- else:
- base = zem.dmd.Events
- fields = zem.lookupManagedEntityResultFields(base.event_key)
+ # Use default result fields
+ if hasattr(context, 'event_key'):
+ base = context
+ else:
+ base = zem.dmd.Events
+ fields = zem.lookupManagedEntityResultFields(base.event_key)
if not params:
params = {}
-
+
args = dict(
offset=start,
rows=limit,
resultFields=fields,
getTotalCount=True,
sort=sort,
- orderby="%s %s, lastTime DESC" % (sort, dir),
+ orderby=self._getOrderByClause(sort, dir),
filters=params
)
if evid: args['evid'] = evid
@@ -84,15 +109,20 @@
self._set_asof(time.time())
+ disabled = not self._checkPermission('Manage Events')
+
return {
'events': results,
'totalCount': totalCount,
+ 'disabled': disabled
}
def acknowledge(self, evids=None, ranges=None, start=None, limit=None,
field=None, direction=None, params=None):
+ self._require('Manage Events', 'acknowledge')
zem = self._evmgr()
- range_evids = zem.getEventIDsFromRanges(self.context, field, direction,
+ orderby = self._getOrderByClause(field, direction);
+ range_evids = zem.getEventIDsFromRanges(self.context, orderby,
start, limit, params, evids,
ranges, asof=self._asof)
zem.manage_ackEvents(range_evids)
@@ -100,8 +130,10 @@
def unacknowledge(self, evids=None, ranges=None, start=None, limit=None,
field=None, direction=None, params=None):
+ self._require('Manage Events', 'unacknowledge')
zem = self._evmgr()
- range_evids = zem.getEventIDsFromRanges(self.context, field, direction,
+ orderby = self._getOrderByClause(field, direction);
+ range_evids = zem.getEventIDsFromRanges(self.context, orderby,
start, limit, params, evids,
ranges, asof=self._asof)
zem.manage_unackEvents(range_evids)
@@ -109,8 +141,10 @@
def reopen(self, evids=None, ranges=None, start=None, limit=None,
field=None, direction=None, params=None):
+ self._require('Manage Events', 'reopen')
zem = self._evmgr()
- range_evids = zem.getEventIDsFromRanges(self.context, field, direction,
+ orderby = self._getOrderByClause(field, direction);
+ range_evids = zem.getEventIDsFromRanges(self.context, orderby,
start, limit, params, evids,
ranges, asof=self._asof)
zem.manage_undeleteEvents(range_evids)
@@ -118,8 +152,10 @@
def close(self, evids=None, ranges=None, start=None, limit=None,
field=None, direction=None, params=None):
+ self._require('Manage Events', 'close')
zem = self._evmgr()
- range_evids = zem.getEventIDsFromRanges(self.context, field, direction,
+ orderby = self._getOrderByClause(field, direction);
+ range_evids = zem.getEventIDsFromRanges(self.context, orderby,
start, limit, params, evids,
ranges, asof=self._asof)
zem.manage_deleteEvents(range_evids)
@@ -179,20 +215,25 @@
params = unjson(params)
zem = self._evmgr()
where = zem.lookupManagedEntityWhere(self.context)
- where = zem.filteredWhere(where, params)
+ #escape any % in the where clause because of format eval later
+ where = where.replace('%', '%%')
+
+ values = []
+ where = zem.filteredWhere(where, params, values)
if self._asof:
where += (" and not (UNIX_TIMESTAMP(stateChange)>%s"
" and eventState=0)") % zem.dateDB(self._asof)
table = IEventManagerProxy(self).is_history and 'history' or 'status'
q = 'select eventState from %s where %s ' % (table, where)
- q += 'order by %s %s, lastTime DESC' % (field, direction)
+ orderby = self._getOrderByClause(field, direction)
+ q += 'order by %s' % zem._scrubOrderby(orderby)
query = query_tpl % q
try:
conn = zem.connect()
curs = conn.cursor()
curs.execute("set @row:=0;")
curs.execute("set @marker:=999;")
- curs.execute(query)
+ curs.execute(query, values)
result = curs.fetchall()
finally:
curs.close()
@@ -219,22 +260,30 @@
properties = [{'key':key,'value':value} for key, value in properties]
event['properties'] = properties
event['log'] = details._logs
- for f in ('device', 'component', 'eventClass'):
- func = getattr(IEventManagerProxy(self), '_get_%s_url' % f)
- if f=='component':
- args = event['device'], event['component']
- else:
- args = [event[f]]
- url = func(*args)
- if url:
- event[f+'_url'] = url
+ # device url and title
+ deviceUrl, deviceTitle = IEventManagerProxy(self)._get_device_url(event['device'])
+ if deviceUrl:
+ event['device_url'] = deviceUrl
+ event['device_title'] = deviceTitle or event['device']
+ # component url and title
+ componentUrl, componentTitle = \
+ IEventManagerProxy(self)._get_component_url(event['device'],event['component'])
+ if componentUrl:
+ event['component_url'] = componentUrl
+ event['component_title'] = componentTitle or event['component']
+ # eventClass url and title
+ eventClassUrl = IEventManagerProxy(self)._get_eventClass_url(event['eventClass'])
+ if eventClassUrl:
+ event['eventClass_url'] = eventClassUrl
return { 'event': [event] }
def write_log(self, evid=None, message=None, history=False):
+ self._require('Manage Events', 'write_log')
zem = self._evmgr(history)
zem.manage_addLogMessage(evid, message)
def classify(self, evids, evclass):
+ self._require('Manage Events', 'classify')
zem = self._evmgr()
msg, url = zem.manage_createEventMap(evclass, evids)
if url:
@@ -243,6 +292,7 @@
def add_event(self, summary, device, component, severity, evclasskey,
evclass):
+ self._require('Manage Events', 'add_event')
zem = self._evmgr()
if isinstance(severity, basestring):
sevs = ['Clear', 'Debug', 'Info', 'Warning', 'Error', 'Critical']
@@ -320,4 +370,5 @@
result.append(']});')
result = '\n'.join(result)
return result
+
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/historyconsole.pt
^
|
@@ -28,12 +28,12 @@
<table>
<tr><td class="dt">Device:</td>
<td><tpl if="device"><a href="{device_url}"
- class="resource-link">{device}</a></tpl></td>
+ class="resource-link">{device_title}</a></tpl></td>
</tr>
<tr><td class="dt">Component:</td>
<td><tpl if="component">
<a href="{component_url}"
- class="resource-link">{component}</a></tpl></td>
+ class="resource-link">{component_title}</a></tpl></td>
</tr>
<tr><td class="dt">Event Class:</td>
<td><tpl if="eventClass"><a href="{eventClass_url}"
@@ -248,11 +248,13 @@
* ClASSIFY BUTTON
*/
//text: 'Classify',
+ id: 'classify-button',
tooltip: 'Map selected events to an event class',
iconCls: 'classify',
handler: showClassifyDialog
},{
//text: 'Reopen',
+ id: 'reopen-button',
tooltip: 'Return selected events to the active event console',
iconCls: 'reopen',
handler: function(){
@@ -357,6 +359,16 @@
view.toggleRowColors(checked);
}
},{
+ id: 'livesearch_checkitem',
+ checked: true,
+ xtype: 'menucheckitem',
+ text: 'Enable live search',
+ handler: function(checkitem) {
+ var checked = !checkitem.checked;
+ var view = Ext.getCmp('events_grid').getView();
+ view.toggleLiveSearch(checked);
+ }
+ },{
id: 'clearfilters',
text: 'Clear filters',
listeners: {
@@ -443,10 +455,18 @@
});
// View to render the grid
+ var yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
var myView = new Zenoss.FilterGridView({
nearLimit : 20,
filterbutton: 'showfilters',
+ defaultFilters: {
+ severity: [5, 4, 3, 2],
+ eventState: [0, 1],
+ lastTime: yesterday
+ },
rowcoloritem: 'rowcolors_checkitem',
+ livesearchitem: 'livesearch_checkitem',
loadMask : { msg : 'Loading. Please wait...' }
});
@@ -457,11 +477,23 @@
// Store to hold the events data
var console_store = new Ext.ux.grid.livegrid.Store({
autoLoad: true,
- proxy: new Ext.data.DirectProxy({
- directFn:Zenoss.remote.EventConsole.query
+ proxy: new Zenoss.ThrottlingProxy({
+ directFn:Zenoss.remote.EventConsole.query,
+ listeners: {'load': function(proxy, transaction, options) {
+ var disabled = transaction.result.disabled;
+ var buttonIds = [
+ 'classify-button',
+ 'reopen-button'
+ ];
+ Ext.each(buttonIds, function(buttonId) {
+ var button = Ext.getCmp(buttonId);
+ button.setDisabled(disabled);
+ });
+ }}
}),
bufferSize: 100,
sortInfo: {field:'lastTime', direction:'DESC'},
+ defaultSort: {field:'lastTime', direction:'DESC'},
reader: new Ext.ux.grid.livegrid.JsonReader({
root: 'events',
totalProperty: 'totalCount'
@@ -472,8 +504,10 @@
'evid',
'device',
'device_url',
+ 'device_title',
'component',
'component_url',
+ 'component_title',
'summary',
'eventState',
'eventClass',
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/eventconsole/historydetail.pt
^
|
@@ -22,12 +22,12 @@
<table>
<tr><td class="dt">Device:</td>
<td><tpl if="device"><a href="{device_url}"
- class="resource-link">{device}</a></tpl></td>
+ class="resource-link">{device_title}</a></tpl></td>
</tr>
<tr><td class="dt">Component:</td>
<td><tpl if="component">
<a href="{component_url}"
- class="resource-link">{component}</a></tpl></td>
+ class="resource-link">{component_title}</a></tpl></td>
</tr>
<tr><td class="dt">Event Class:</td>
<td><tpl if="eventClass"><a href="{eventClass_url}"
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUI3/browser/resources/js/zenoss.js
^
|
@@ -239,12 +239,15 @@
* @constructor
*/
Zenoss.FilterGridView = Ext.extend(Ext.ux.grid.livegrid.GridView, {
+ rowHeight: 22,
rowColors: false,
+ liveSearch: true,
constructor: function(config) {
if (typeof(config.displayFilters)=='undefined')
config.displayFilters = true;
Zenoss.FilterGridView.superclass.constructor.apply(this,
arguments);
+ Ext.applyIf(this.lastOptions, this.defaultFilters || {});
},
lastOptions: {},
initEvents: function(){
@@ -261,27 +264,28 @@
this.on('beforebuffer', this.onBeforeBuffer, this);
cm.on('hiddenchange', this.updateHeaders, this);
- Zenoss.FilterGridView.superclass.initData.call(this, ds, cm);
- },
- // Gather the current values of the filter and apply them to a given
- // object.
- applyFilterParams: function(options) {
- var params = this.lastOptions || {};
- for(i=0;i<this.filters.length;i++){
- var filter = this.filters[i];
- var oldformat;
- query = filter.getValue();
- if (query) {
- params[filter.id] = query;
- if (filter.xtype=='datefield'){
- dt = new Date(query);
- query = dt.format(
- Zenoss.date.UniversalSortableDateTime)
- }
- } else {
- delete params[filter.id];
+ Zenoss.FilterGridView.superclass.initData.call(this, ds, cm);
+ },
+ // Gather the current values of the filter and apply them to a given
+ // object.
+ applyFilterParams: function(options) {
+ var options = options || {},
+ params = this.lastOptions || {};
+ for(i=0;i<this.filters.length;i++){
+ var filter = this.filters[i];
+ var oldformat;
+ query = filter.getValue();
+ if (query) {
+ params[filter.id] = query;
+ if (filter.xtype=='datefield'){
+ dt = new Date(query);
+ query = dt.format(Zenoss.date.UniversalSortableDateTime)
}
}
+ else {
+ delete params[filter.id];
+ }
+ }
Ext.apply(options.params, {
params: Ext.util.JSON.encode(params)
});
@@ -378,6 +382,7 @@
},
renderEditors: function() {
Ext.each(this.filters, function(ob){ob.destroy()});
+ this.filters = [];
var cs = this.getColumnData();
for (i=0,len=cs.length; i<len; i++) {
if (this.cm.isHidden(i)) continue;
@@ -408,18 +413,20 @@
this.filters[this.filters.length] = filter;
filter.validationTask = new Ext.util.DelayedTask(function(){
this.fireEvent('filterchange', this);
- this.nonDisruptiveReset();
+ if (this.liveSearch){
+ this.nonDisruptiveReset();
+ }
}, this);
if (filter.xtype=='textfield') {
filter.on('keyup', function(field, e) {
- if(!e.isNavKeyPress()) this.validationTask.delay(250);
+ if(!e.isNavKeyPress()) this.validationTask.delay(1000);
}, filter);
} else {
filter.on('select', function(field, e) {
- this.validationTask.delay(250);
+ this.validationTask.delay(1000);
}, filter);
filter.on('change', function(field, e) {
- this.validationTask.delay(250);
+ this.validationTask.delay(1000);
}, filter);
}
@@ -440,9 +447,34 @@
return html;
},
getState: function(){
+ // Update last options from the filter widgets
+ this.applyFilterParams();
+ // Iterate over last options, setting defaults where appropriate
+ var options = {};
+ Ext.iterate(this.lastOptions, function(k){
+ var defaults = this.defaultFilters || {},
+ dflt = defaults[k],
+ opt = this.lastOptions[k];
+ if (dflt) {
+ var match;
+ if (Ext.isDate(dflt) && Ext.isDate(opt)) {
+ var delta = Math.abs(dflt.getTime() - opt.getTime());
+ // If they're within a second, they match. We don't
+ // have finer resolution in the UI.
+ match = delta <= 1000;
+ } else {
+ match = dflt==opt;
+ }
+ if (!match) {
+ options[k] = opt;
+ }
+ } else {
+ options[k] = opt;
+ }
+ }, this);
return {
displayFilters: this.displayFilters,
- options: this.lastOptions
+ options: options
};
},
getFilterButton: function(){
@@ -461,6 +493,11 @@
Ext.state.Manager.set('rowcolor', bool);
this.updateLiveRows(this.rowIndex, true, false);
},
+ toggleLiveSearch: function(bool){
+ this.liveSearch = bool;
+ Ext.state.Manager.set('livesearch', bool);
+ },
+
applyState: function(state) {
// For now, always show the filters The rest of the filter-hiding
// stuff is still in place, so just remove this and put back the
@@ -468,6 +505,8 @@
this.displayFilters = true; //state.displayFilters;
// End always show filters
this.lastOptions = state.options;
+ // Apply any default filters specified in the constructor
+ Ext.applyIf(this.lastOptions, this.defaultFilters || {});
/*
var btn = Ext.getCmp(this.filterbutton);
btn.on('render', function(){
@@ -477,6 +516,7 @@
},
resetFilters: function(){
this.lastOptions = {};
+ Ext.applyIf(this.lastOptions, this.defaultFilters || {});
//this.getFilterButton().setChecked(false);
}
});
@@ -532,6 +572,16 @@
initState: function() {
Zenoss.FilterGridPanel.superclass.initState.apply(this, arguments);
this.restoreURLState();
+ var livesearchitem = Ext.getCmp(this.view.livesearchitem);
+ var liveSearch = Ext.state.Manager.get('livesearch');
+ if (!Ext.isDefined(liveSearch)){
+ liveSearch = livesearchitem.checked;
+ }else{
+ livesearchitem.on('render', function(){
+ this.setChecked(liveSearch)
+ },livesearchitem)
+ }
+ this.view.liveSearch = liveSearch;
var rowColors = Ext.state.Manager.get('rowcolor');
this.view.rowColors = rowColors;
var rowcoloritem = Ext.getCmp(this.view.rowcoloritem);
@@ -556,7 +606,9 @@
if (availcols.indexOf(col.id)>-1) cols.push(col);
});
Ext.iterate(state.filters.options, function(op){
- if (availcols.indexOf(op.id)>-1) filters[op.id] = op;
+ if (availcols.indexOf(op)>-1) {
+ filters[op] = state.filters.options[op];
+ }
});
state.columns = cols;
state.filters.options = filters;
@@ -729,6 +781,8 @@
this.menu.on('itemclick', function(item){
this.setInterval(item.value);
}, this);
+ //60 is the default interval; it matches the checked item above
+ this.setInterval(60);
},
setInterval: function(interval) {
this.interval = interval;
@@ -1016,10 +1070,11 @@
Zenoss.util.render_linkable = function(name, col, record) {
var url = record.data[col.id + '_url'];
+ var title = record.data[col.id + '_title'] || name;
if (url) {
- return '<a href="'+url+'">'+name+'</a>'
+ return '<a href="'+url+'">'+title+'</a>'
} else {
- return name;
+ return title;
}
}
@@ -1105,4 +1160,25 @@
YearMonth: "F, Y"
});
+ Zenoss.ThrottlingProxy = Ext.extend(Ext.data.DirectProxy, {
+ constructor: function(config){
+ Zenoss.ThrottlingProxy.superclass.constructor.apply(this, arguments);
+ this.loading = false;
+ //add event listeners for throttling
+ this.addListener('beforeload', function(proxy, options){
+ if (!proxy.loading){
+ proxy.loading = true;
+ return true;
+ }
+ return false;
+ });
+ this.addListener('load', function(proxy, options){
+ proxy.loading = false;
+ });
+ this.addListener('exception', function(proxy, options){
+ proxy.loading = false;
+ });
+
+ }
+ });
});
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/CmdBase.py
^
|
@@ -44,11 +44,11 @@
"""
Class used for all Zenoss commands
"""
-
+
doesLogging = True
def __init__(self, noopts=0):
-
+
# We must import ZenossStartup at this point so that all Zenoss daemons
# and tools will have any ZenPack monkey-patched methods available.
zope.component.provideAdapter(DefaultTraversable, (None,))
@@ -64,7 +64,7 @@
pass
import Products.ZenossStartup
unused(Products.ZenossStartup)
-
+
self.usage = "%prog [options]"
self.noopts = noopts
self.args = []
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/IpUtil.py
^
|
@@ -1,7 +1,7 @@
###########################################################################
#
# This program is part of Zenoss Core, an open source monitoring platform.
-# Copyright (C) 2007, Zenoss Inc.
+# Copyright (C) 2007, 2009 Zenoss Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
@@ -11,15 +11,14 @@
#
###########################################################################
-__doc__="""Util
+__doc__ = """IpUtil
-Utility functions for the Confmon Product
+IPv4 utility functions
"""
import types
import re
-import string
from Products.ZenUtils.Exceptions import ZentinelException
@@ -27,13 +26,37 @@
class IpAddressError(ZentinelException): pass
-
-isip = re.compile("^\d+\.\d+\.\d+\.\d+$").search
-"""return match if this is an ip."""
+class InvalidIPRangeError(Exception):
+ """
+ Attempted to parse an invalid IP range.
+ """
+# Match if this is an IPv4 address
+isip = re.compile("^\d+\.\d+\.\d+\.\d+$").search
def checkip(ip):
- """check that an ip is valid"""
+ """
+ Check that an IPv4 address is valid. Return true
+ or raise an exception(!)
+
+ >>> checkip('10.10.20.5')
+ True
+ >>> try: checkip(10)
+ ... except IpAddressError, ex: print ex
+ 10 is not a dot delimited address
+ >>> try: checkip('10')
+ ... except IpAddressError, ex: print ex
+ 10 is an invalid address
+ >>> try: checkip('10.10.20.500')
+ ... except IpAddressError, ex: print ex
+ 10.10.20.500 is an invalid address
+ >>> checkip('10.10.20.00')
+ True
+ >>> checkip('10.10.20.0')
+ True
+ >>> checkip('10.10.20.255')
+ True
+ """
success = True
if ip == '':
success = False
@@ -57,7 +80,25 @@
def numbip(ip):
- """convert a string ip to number"""
+ """
+ Convert a string IP to a decimal representation easier for
+ calculating netmasks etc.
+
+ Deprecated in favour of ipToDecimal()
+ """
+ return ipToDecimal(ip)
+
+def ipToDecimal(ip):
+ """
+ Convert a string IP to a decimal representation easier for
+ calculating netmasks etc.
+
+ >>> ipToDecimal('10.10.20.5')
+ 168432645L
+ >>> try: ipToDecimal('10.10.20.500')
+ ... except IpAddressError, ex: print ex
+ 10.10.20.500 is an invalid address
+ """
checkip(ip)
octs = ip.split('.')
octs.reverse()
@@ -66,21 +107,41 @@
i += (256l ** j) * int(octs[j])
return i
-_masks = (
- 0x000000ffL,
- 0x0000ff00L,
- 0x00ff0000L,
- 0xff000000L,
- )
-
def ipFromIpMask(ipmask):
- """get just the ip from an ip mask pair like 1.1.1.1/24"""
+ """
+ Get just the IP address from an CIDR string like 1.1.1.1/24
+
+ >>> ipFromIpMask('1.1.1.1')
+ '1.1.1.1'
+ >>> ipFromIpMask('1.1.1.1/24')
+ '1.1.1.1'
+ """
return ipmask.split("/")[0]
def strip(ip):
- """convert a number ip to a string"""
+ """
+ Convert a numeric IP address to a string
+
+ Deprecated in favour of decimalIpToStr()
+ """
+ return decimalIpToStr(ip)
+
+def decimalIpToStr(ip):
+ """
+ Convert a decimal IP address (as returned by ipToDecimal)
+ to a regular IPv4 dotted quad address.
+
+ >>> decimalIpToStr(ipToDecimal('10.23.44.57'))
+ '10.23.44.57'
+ """
+ _masks = (
+ 0x000000ffL,
+ 0x0000ff00L,
+ 0x00ff0000L,
+ 0xff000000L,
+ )
o = []
for i in range(len(_masks)):
t = ip & _masks[i]
@@ -91,14 +152,31 @@
def hexToBits(hex):
- """convert hex number (0xff000000 of netbits to numeric netmask (8)"""
+ """
+ Convert hex netbits (0xff000000) to decimal netmask (8)
+
+ >>> hexToBits("0xff000000")
+ 8
+ >>> hexToBits("0xffffff00")
+ 24
+ """
return maskToBits(hexToMask(hex))
def hexToMask(hex):
- '''converts a netmask represented in hex to octets represented in
- decimal. e.g. "0xffffff00" -> "255.255.255.0"'''
-
+ """
+ Converts a netmask represented in hex to octets represented in
+ decimal.
+
+ >>> hexToMask("0xffffff00")
+ '255.255.255.0'
+ >>> hexToMask("0xffffffff")
+ '255.255.255.255'
+ >>> hexToMask("0x00000000")
+ '0.0.0.0'
+ >>> hexToMask("trash")
+ '255.255.255.255'
+ """
if hex.find('x') < 0:
return "255.255.255.255"
@@ -107,19 +185,28 @@
while len(hex) > 0:
snippit = list(hex.pop() + hex.pop())
snippit.reverse()
- decimal = int(string.join(snippit, ''), 16)
+ decimal = int(''.join(snippit), 16)
octets.append(str(decimal))
octets.reverse()
- return string.join(octets, '.')
+ return '.'.join(octets)
def maskToBits(netmask):
- """convert string rep of netmask to number of bits"""
+ """
+ Convert string rep of netmask to number of bits
+
+ >>> maskToBits('255.255.255.255')
+ 32
+ >>> maskToBits('255.255.224.0')
+ 19
+ >>> maskToBits('0.0.0.0')
+ 0
+ """
if type(netmask) == types.StringType and netmask.find('.') > -1:
test = 0xffffffffL
if netmask[0]=='0': return 0
- masknumb = numbip(netmask)
+ masknumb = ipToDecimal(netmask)
for i in range(32):
if test == masknumb: return 32-i
test = test - 2 ** i
@@ -129,7 +216,24 @@
def bitsToMaskNumb(netbits):
- """convert integer number of netbits to string netmask"""
+ """
+ Convert integer number of netbits to a decimal number
+
+ Deprecated in favour of bitsToDecimalMask()
+ """
+ return bitsToDecimalMask(netbits)
+
+def bitsToDecimalMask(netbits):
+ """
+ Convert integer number of netbits to a decimal number
+
+ >>> bitsToDecimalMask(32)
+ 4294967295L
+ >>> bitsToDecimalMask(19)
+ 4294959104L
+ >>> bitsToDecimalMask(0)
+ 0L
+ """
masknumb = 0L
netbits=int(netbits)
for i in range(32-netbits, 32):
@@ -138,26 +242,73 @@
def bitsToMask(netbits):
- return strip(bitsToMaskNumb(netbits))
+ """
+ Convert netbits into a dotted-quad subnetmask
+
+ >>> bitsToMask(12)
+ '255.240.0.0'
+ >>> bitsToMask(0)
+ '0.0.0.0'
+ >>> bitsToMask(32)
+ '255.255.255.255'
+ """
+ return decimalIpToStr(bitsToDecimalMask(netbits))
def getnet(ip, netmask):
- """get network address of ip as string netmask is in form 255.255.255.0"""
+ """
+ Deprecated in favour of decimalNetFromIpAndNet()
+ """
+ return decimalNetFromIpAndNet(ip, netmask)
+
+def decimalNetFromIpAndNet(ip, netmask):
+ """
+ Get network address of IP as string netmask as in the form 255.255.255.0
+
+ >>> getnet('10.12.25.33', 24)
+ 168564992L
+ >>> getnet('10.12.25.33', '255.255.255.0')
+ 168564992L
+ """
checkip(ip)
- ip = numbip(ip)
- if 0 < int(netmask) <= 32:
- netmask = bitsToMaskNumb(netmask)
+ ip = ipToDecimal(ip)
+
+ try: netbits = int(netmask)
+ except ValueError: netbits = -1
+
+ if 0 < netbits <= 32:
+ netmask = bitsToDecimalMask(netbits)
else:
checkip(netmask)
- netmask = numbip(netmask)
+ netmask = ipToDecimal(netmask)
return ip & netmask
def getnetstr(ip, netmask):
- """return network number as string"""
- return strip(getnet(ip, netmask))
+ """
+ Deprecated in favour of netFromIpAndNet()
+ """
+ return netFromIpAndNet(ip, netmask)
+
+def netFromIpAndNet(ip, netmask):
+ """
+ Return network number as string
+
+ >>> netFromIpAndNet('10.12.25.33', 24)
+ '10.12.25.0'
+ >>> netFromIpAndNet('250.12.25.33', 1)
+ '128.0.0.0'
+ >>> netFromIpAndNet('10.12.25.33', 16)
+ '10.12.0.0'
+ >>> netFromIpAndNet('10.12.25.33', 32)
+ '10.12.25.33'
+ """
+ return decimalIpToStr(getnet(ip, netmask))
def asyncNameLookup(address, uselibcresolver = True):
+ """
+ Turn IP addreses into names using deferreds
+ """
if uselibcresolver:
# This is the most reliable way to do a lookup use it
from twisted.internet import threads
@@ -184,11 +335,6 @@
return threads.deferToThread(lambda : socket.gethostbyname(name))
-class InvalidIPRangeError(Exception):
- """
- Attempted to parse an invalid IP range.
- """
-
def parse_iprange(iprange):
"""
Turn a string specifying an IP range into a list of IPs.
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/Time.py
^
|
@@ -72,4 +72,12 @@
result = '%d days %s' % (seconds, result)
return result
-
+
+def getBeginningOfDay(gmtSecondsSince1970=None):
+ value = _maybenow(gmtSecondsSince1970)
+ return time.mktime(time.localtime(value)[:3] + (0,0,0,0,0,0))
+
+
+def getEndOfDay(gmtSecondsSince1970=None):
+ value = _maybenow(gmtSecondsSince1970)
+ return time.mktime(time.localtime(value)[:3] + (23,59,59,0,0,0))
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/Utils.py
^
|
@@ -664,9 +664,34 @@
else:
result = (True, '')
return result
-
-
-def sendPage(recipient, msg, pageCommand):
+
+
+from twisted.internet.protocol import ProcessProtocol
+class SendPageProtocol(ProcessProtocol):
+ out = ''
+ err = ''
+ code = None
+
+ def __init__(self, msg):
+ self.msg = msg
+ self.out = ''
+ self.err = ''
+
+ def connectionMade(self):
+ self.transport.write(self.msg)
+ self.transport.closeStdin()
+
+ def outReceived(self, data):
+ self.out += data
+
+ def errReceived(self, data):
+ self.err += data
+
+ def processEnded(self, reason):
+ self.code = reason.value.exitCode
+
+
+def sendPage(recipient, msg, pageCommand, deferred=False):
"""
Send a page. Return a tuple: (success, message) where
sucess is True or False.
@@ -683,14 +708,27 @@
import subprocess
env = dict(os.environ)
env["RECIPIENT"] = recipient
- p = subprocess.Popen(pageCommand,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- shell=True,
- env=env)
- p.stdin.write(msg)
- p.stdin.close()
- response = p.stdout.read()
+ if deferred:
+ from twisted.internet import reactor
+ protocol = SendPageProtocol(msg)
+ d = reactor.spawnProcess(
+ protocol, '/bin/sh', ('/bin/sh', '-c', pageCommand), env)
+
+ # Bad practice to block on a deferred. This is done because our call
+ # chain is not asynchronous and we need to behave like a blocking call.
+ while protocol.code is None:
+ reactor.iterate(0.1)
+
+ return (not protocol.code, protocol.out)
+ else:
+ p = subprocess.Popen(pageCommand,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ shell=True,
+ env=env)
+ p.stdin.write(msg)
+ p.stdin.close()
+ response = p.stdout.read()
return (not p.wait(), response)
@@ -1473,3 +1511,17 @@
errback = cbs[1][0]
errbackName = "%s.%s" % (errback.__module__, errback.func_name)
print "%-39.39s %-39.39s" % (callbackName, errbackName)
+
+def getObjectsFromCatalog(catalog, query=None, log=None):
+ """
+ Generator that can be used to load all objects of out a catalog and skip
+ any objects that are no longer able to be loaded.
+ """
+
+ for brain in catalog(query):
+ try:
+ ob = brain.getObject()
+ yield ob
+ except (NotFound, KeyError, AttributeError):
+ if log:
+ log.warn("Stale %s record: %s", catalog.id, brain.getPath())
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/ZCmdBase.py
^
|
@@ -49,6 +49,8 @@
def __init__(self, noopts=0, app=None, keeproot=False):
ZenDaemon.__init__(self, noopts, keeproot)
+ import Products.Five
+ Products.Five.zcml.load_config('event.zcml', Products.Five)
self.dataroot = None
self.app = app
self.db = None
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/ZenDaemon.py
^
|
@@ -49,7 +49,7 @@
"""
pidfile = None
-
+
def __init__(self, noopts=0, keeproot=False):
"""
Initializer that takes care of basic daemon options.
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/ZenDeleteHistory.py
^
|
@@ -2,7 +2,7 @@
###########################################################################
#
# This program is part of Zenoss Core, an open source monitoring platform.
-# Copyright (C) 2007, Zenoss Inc.
+# Copyright (C) 2007, 2010 Zenoss Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
@@ -13,65 +13,267 @@
###########################################################################
__doc__="""
-ZenDeleteHistory
+ZenDeleteHistory performs cleanup and other maintenane tasks on the MySQL
+events database.
"""
-from ZenScriptBase import ZenScriptBase
+import logging
+log = logging.getLogger('zen.deleteHistory')
+
+import time
+
+import Globals
+from Products.ZenUtils.ZenScriptBase import ZenScriptBase
+from _mysql_exceptions import OperationalError
class ZenDeleteHistory(ZenScriptBase):
"""
- Delete events from the history table
+ Delete events and performance other maintenance tasks on the events
+ database.
"""
def buildOptions(self):
+ self.parser.add_option('-n', '--numDays', dest='numDays',
+ default=None,
+ help='Number of days of history to keep')
+ self.parser.add_option('-d', '--device', dest='device',
+ action='append', default=[],
+ help='Specific device for which to delete events (optional)')
+ self.parser.add_option('--severity', dest='severity',
+ action='append', default=[],
+ help='Only delete events of this severity.')
+ self.parser.add_option('--cleanup', dest='cleanup',
+ action='store_true', default=False,
+ help='Cleanup alert_state, log and detail tables')
+ self.parser.add_option('--optimize', dest='optimize',
+ action='store_true', default=False,
+ help='Optimize tables after performing other operations')
+ self.parser.add_option('--truncate', dest='truncate',
+ action='store_true', default=False,
+ help='Truncate (ERASE) entire history table')
+ self.parser.add_option('--really', dest='really',
+ action='store_true', default=False,
+ help='You must be REALLY sure you want to truncate history')
+ ZenScriptBase.buildOptions(self)
+
+
+ def run(self):
+ self.connect()
+
+ if self.options.truncate:
+ if self.options.really:
+ self.options.cleanup = True
+ self.truncateHistory()
+ else:
+ log.warn((
+ "Specifying the --truncate option will permanently erase "
+ "all archived events. You must specify the --really "
+ "option if you really want to do this."))
+ return
+ else:
+ # Input validation for numDays option.
+ if self.options.numDays:
+ try:
+ self.options.numDays = int(self.options.numDays)
+ except ValueError:
+ log.critical("numDays argument must be an interger")
+ return
+ else:
+ log.critical("The numDays argument must be provided")
+ return
+
+ # Input validation for severity option.
+ if len(self.options.severity) > 0:
+ severityMap = dict([(a, b) for a, b in \
+ self.dmd.ZenEventManager.severityConversions])
+
+ severities = []
+ for s in self.options.severity:
+ try:
+ severity = int(s)
+ if severity >= 0 and severity <= 5:
+ severities.append(severity)
+ else:
+ log.critical("Severity must be 0-5.")
+ return
+ except ValueError:
+ s = s.capitalize()
+ if s in severityMap:
+ severities.append(severityMap[s])
+ else:
+ log.critical("%s is not a valid severity.", s)
+ return
+
+ self.options.severity = severities
+
+ self.deleteHistory()
+
+ if self.options.cleanup:
+ self.cleanupTables()
+
+ if self.options.optimize:
+ self.optimizeTables()
+
+ self.analyzeTables()
+
+
+ def executeStatements(self, statements):
"""
- Setup the command line options
+ Executes a list of statements within a transaction.
"""
- self.parser.add_option('-n', '--numDays',
- dest='numDays', default=None,
- help='Number of days of history to keep')
- self.parser.add_option('-d', '--device',
- dest='device', default=None,
- help='Devide id for which to delete events')
- ZenScriptBase.buildOptions(self)
+ zem = self.dmd.ZenEventManager
+ conn = zem.connect()
+ try:
+ conn.autocommit(0)
+ try:
+ curs = conn.cursor()
+ for statement in statements:
+ begin = time.time()
+ log.debug("Executing: %s", statement)
+ count = curs.execute(statement)
+ log.debug((
+ "Last statement took %.3f seconds to execute and "
+ "affected %s rows."), time.time() - begin, count)
+
+ conn.commit()
+ except OperationalError, ex:
+ log.error('MySQL error: (%s) %s', ex.args[0], ex.args[1])
+ log.error("Rolling back transaction.")
+ conn.rollback()
+ finally:
+ conn.autocommit(1)
+ zem.close(conn)
def deleteHistory(self):
"""
- Delete historical events. If device is given then only delete
- events for that device. If numDays is given then only delete
- events that are older than that many days.
- device and numDays are mutually exclusive. No real reason for this
- other than there is no current need to use both in same call and I
- don't want to test the combination.
+ Deletes events more than X days old where X is the number specified by
+ the "numDays" command line argument. Optionally restricts the
+ deletion to the device specified with the "device" command line
+ argument.
"""
- if self.options.numDays:
- try:
- self.options.numDays = int(self.options.numDays)
- except ValueError:
- raise ValueError('numDays argument must be an integer')
+ earliest_time = time.time() - (86400 * self.options.numDays)
+
+ device_filter = ""
+ if len(self.options.device) > 0:
+ log.info("Deleting historical events older than %s days for %s.",
+ self.options.numDays, self.options.device)
+ device_filter = " AND device IN (%s)" % ','.join([
+ "'%s'" % d for d in self.options.device])
+ else:
+ log.info("Deleting historical events older than %s days.",
+ self.options.numDays)
+
+ severity_filter = ""
+ if len(self.options.severity) > 0:
+ severity_filter = " AND severity IN (%s)" % ','.join(
+ map(str, self.options.severity))
+
+ statements = [
+ "DROP TABLE IF EXISTS delete_evids",
+ ("CREATE TEMPORARY TABLE delete_evids "
+ "SELECT evid FROM history WHERE lastTime < %s%s%s" % (
+ earliest_time, device_filter, severity_filter)),
+ "CREATE INDEX evid ON delete_evids (evid)",
+ ]
+
+ for table in ("history", "detail", "log", "alert_state"):
+ statements.append((
+ "DELETE t FROM %s t "
+ "RIGHT JOIN delete_evids d ON d.evid = t.evid" % table))
+
+ statements.append("DROP TABLE IF EXISTS delete_evids")
+
+ begin = time.time()
+ self.executeStatements(statements)
+ log.info("Historical event deletion took %.3f seconds.",
+ time.time() - begin)
+
+
+ def truncateHistory(self):
+ """
+ Truncates the entire history table. This will also force a cleanup run
+ to delete all orphaned rows in the accessory tables.
+ """
+ log.info("Truncating history table.")
+
+ statements = [
+ "TRUNCATE TABLE history",
+ ]
+
+ begin = time.time()
+ self.executeStatements(statements)
+ log.info("History table truncated in %.3f seconds.",
+ time.time() - begin)
+
+
+ def cleanupTables(self):
+ """
+ Cleans up the detail, log and alert_state accessory tables. If events
+ are deleted from the history table without considering these tables,
+ rows can be orphaned. This method cleans up these orphaned rows.
+ """
+ log.info("Cleaning up orphaned rows in accessory tables.")
+
+ statements = [
+ "DROP TABLE IF EXISTS cleanup_evids",
+ "CREATE TEMPORARY TABLE cleanup_evids SELECT evid FROM status",
+ "INSERT INTO cleanup_evids SELECT evid FROM history",
+ "CREATE INDEX evid ON cleanup_evids (evid)",
+ ]
+
+ for table in ("log", "detail", "alert_state"):
+ statements.append((
+ "DELETE t FROM cleanup_evids c "
+ "RIGHT JOIN %s t USING (evid) WHERE c.evid IS NULL" % table))
+
+ statements.append("DROP TABLE IF EXISTS cleanup_evids")
+
+ begin = time.time()
+ self.executeStatements(statements)
+ log.info("Accessory tables cleaned up in %.3f seconds.",
+ time.time() - begin)
+
+
+ def optimizeTables(self):
+ """
+ Manually optimizing tables after large amounts of rows have been
+ deleted can improve their performance and reclaim unused space.
- self.connect()
+ NOTE: Optimizing a table places a write-lock on it, and it can be a
+ lengthy process.
+ """
+ log.info("Optimizing tables to reclaim unused space.")
+
+ statements = [
+ "OPTIMIZE TABLE alert_state, status, log, detail, history",
+ ]
+
+ begin = time.time()
+ self.executeStatements(statements)
+ log.info("Tables optimized in %.3f seconds.", time.time() - begin)
+
+
+ def analyzeTables(self):
+ """
+ Manually analyzing tables is recommended after large deletions so that
+ the optimizer can plan queries properly.
- if self.options.device:
- statement = 'delete from history '
- whereClause = 'where device = "%s"' % self.options.device
- reason = 'Deleting events for device %s' % self.options.device
- toLog = True
- elif self.options.numDays > 0:
- statement = ('delete h,j,d from history h '
- 'LEFT JOIN log j ON h.evid = j.evid '
- 'LEFT JOIN detail d ON h.evid = d.evid ')
- whereClause = ('WHERE StateChange < DATE_SUB(NOW(), '
- 'INTERVAL %s day)' % self.options.numDays)
- reason = ''
- toLog = False
- else:
- return
- print '%s%s' % (statement, whereClause)
- self.dmd.ZenEventManager.updateEvents(statement, whereClause, reason,
- toLog=toLog, table='history')
+ NOTE: Analyzing an InnoBD tables places a write-lock on it. However,
+ this is typically a quick process.
+ """
+ log.info("Analyzing tables for optimal queries.")
+
+ statements = [
+ "ANALYZE TABLE alert_state, status, log, detail, history",
+ ]
+
+ begin = time.time()
+ self.executeStatements(statements)
+ log.info("Tables analyzed in %.3f seconds.", time.time() - begin)
+
if __name__ == '__main__':
- ZenDeleteHistory().deleteHistory()
+ zdh = ZenDeleteHistory()
+ zdh.run()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/ZenPackCmd.py
^
|
@@ -291,11 +291,16 @@
out, err = p.communicate()
p.wait()
if p.returncode:
+ DoEasyUninstall(eggPath)
raise ZenPackException('Error installing the egg (%s): %s' %
(p.returncode, err))
zpDists = AddDistToWorkingSet(eggPath)
else:
- zpDists = DoEasyInstall(eggPath)
+ try:
+ zpDists = DoEasyInstall(eggPath)
+ except:
+ DoEasyUninstall(eggPath)
+ raise
# cmd = 'easy_install --always-unzip --site-dirs=%s -d %s %s' % (
# zenPackDir,
# zenPackDir,
@@ -307,7 +312,6 @@
# p.wait()
# eggName = os.path.split(eggPath)[1]
# eggPath = os.path.join(zenPackDir, eggName)
-
return zpDists
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/ZenScriptBase.py
^
|
@@ -32,12 +32,13 @@
def __init__(self, noopts=0, app=None, connect=False):
CmdBase.__init__(self, noopts)
+ import Products.Five
+ Products.Five.zcml.load_config('event.zcml', Products.Five)
self.dataroot = None
self.app = app
self.db = None
if connect:
self.connect()
-
def connect(self):
if not self.app:
@@ -52,7 +53,7 @@
self.poollock = Lock()
self.getDataRoot()
self.login()
- if not getattr(self.dmd, 'propertyTransformers', None):
+ if getattr(self.dmd, 'propertyTransformers', None) is None:
self.dmd.propertyTransformers = {}
commit()
setDescriptors(self.dmd.propertyTransformers)
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/json.py
^
|
@@ -38,7 +38,8 @@
if callable(value):
# Decorate the given callable
def inner(*args, **kwargs):
- return simplejson.dumps(value(*args, **kwargs))
+ return simplejson.dumps(value(*args, **kwargs),
+ encoding='iso-8859-1')
# Well-behaved decorators look like the decorated function
inner.__name__ = value.__name__
inner.__dict__.update(value.__dict__)
@@ -46,7 +47,7 @@
return inner
else:
# Simply serialize the value passed
- return simplejson.dumps(value)
+ return simplejson.dumps(value, encoding='iso-8859-1')
def unjson(value):
"""
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/tests/testUtils.py
^
|
@@ -20,4 +20,5 @@
suite = DocTestSuite('Products.ZenUtils.Utils')
jsonsuite = DocTestSuite('Products.ZenUtils.json')
guidsuite = DocTestSuite('Products.ZenUtils.guid')
- return unittest.TestSuite([suite, jsonsuite, guidsuite])
+ iputilsuite = DocTestSuite('Products.ZenUtils.IpUtil')
+ return unittest.TestSuite([suite, jsonsuite, guidsuite, iputilsuite])
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenUtils/zenpack.py
^
|
@@ -74,9 +74,6 @@
def run(self):
"Execute the user's request"
- import Products
- Products.Five.zcml.load_config('event.zcml', Products.Five)
-
if self.args:
print "Require one of --install, --remove or --list flags."
self.parser.print_help()
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenWidgets/browser/quickstart/templates/quickstart_macros.pt
^
|
@@ -51,7 +51,7 @@
</tal:block>
</div>
</div>
- <div id="footer">© 2009 Zenoss, Inc.</div>
+ <div id="footer">© 2005-2010 Zenoss, Inc.</div>
</div>
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenWidgets/skins/zenui/dialog_addNetwork.pt
^
|
@@ -11,8 +11,8 @@
<input tal:attributes="id string:dialog_submit;
type string:submit;
value string:OK;
- onclick string:return $$('dialog').submit_form_and_check('${here/getPrimaryUrlPath}')"
- name="manage_addOrganizer:method" />
+ onclick string:return $$('dialog').submit_form_and_check('/zport/dmd/Networks')"
+ name="manage_addIpNetwork:method" />
<input tal:attributes="id string:dialog_cancel;
type string:button;
value string:Cancel;
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenWin/services/WinServiceConfig.py
^
|
@@ -80,7 +80,8 @@
proxy.services = {}
for service in genServices(device):
if service.isMonitored:
- proxy.services[service.name] = service.severity
+ proxy.services[service.name] = (service.getStatus(),
+ service.severity)
# don't bother adding this device proxy if there aren't any services
# to monitor
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenWin/zeneventlog.py
^
|
@@ -42,7 +42,7 @@
from Products.ZenCollector.tasks import SimpleTaskFactory,\
SimpleTaskSplitter,\
TaskStates
-from Products.ZenEvents.ZenEventClasses import Error, Warning, Info, \
+from Products.ZenEvents.ZenEventClasses import Clear, Error, Warning, Info, \
Debug, Status_Wmi
from Products.ZenUtils.observable import ObservableMixin
from Products.ZenWin.Watcher import Watcher
@@ -335,6 +335,16 @@
d.addCallback(self._collectCallback)
return d
+ def _deviceUp(self, result):
+ msg = 'WMI connection to %s up.' % self._devId
+ self._eventService.sendEvent(dict(
+ summary=msg,
+ eventClass=Status_Wmi,
+ device=self._devId,
+ severity=Clear,
+ component='zeneventlog'))
+ return result
+
def _collectCallback(self, result):
"""
Callback called after a connect or previous collection so that another
@@ -346,6 +356,7 @@
self.state = ZenEventLogTask.STATE_POLLING
d = self._watcher.getEvents(self._queryTimeout, self._batchSize)
d.addCallbacks(self._collectSuccessful, self._failure)
+ d.addCallbacks(self._deviceUp)
return d
def _connectCallback(self, result):
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/Products/ZenWin/zenwin.py
^
|
@@ -41,7 +41,7 @@
from Products.ZenCollector.tasks import SimpleTaskFactory,\
SimpleTaskSplitter,\
TaskStates
-from Products.ZenEvents.ZenEventClasses import Error, Clear, Status_WinService
+from Products.ZenEvents.ZenEventClasses import Error, Clear, Status_WinService, Status_Wmi
from Products.ZenUtils.observable import ObservableMixin
from Products.ZenWin.WMIClient import WMIClient
from Products.ZenWin.Watcher import Watcher
@@ -121,6 +121,10 @@
STATE_WATCHER_QUERY = 'WATCHER_QUERY'
STATE_WATCHER_PROCESS = 'WATCHER_PROCESS'
+ # windows service states from wmi queries (lowercased)
+ RUNNING = "running"
+ STOPPED = "stopped"
+
def __init__(self,
deviceId,
taskName,
@@ -215,15 +219,24 @@
self._eventService.sendEvent(dict(
summary=summary,
component='zenwin',
- eventClass=Status_WinService,
+ eventClass=Status_Wmi,
device=self._devId,
severity=Error,
- agent='zenwin',
))
# give the result to the rest of the errback chain
return result
+ def _sendWinServiceEvent(self, name, summary, severity):
+ event = {'summary': summary,
+ 'eventClass': Status_WinService,
+ 'device': self._devId,
+ 'severity': severity,
+ 'agent': 'zenwin',
+ 'component': name,
+ 'eventGroup': 'StatusTest'}
+ self._eventService.sendEvent(event)
+
def _handleResult(self, name, state):
"""
Handle a result from the wmi query. Results from both the initial WMI
@@ -231,27 +244,21 @@
this method. Log running and stopped transitions. Send an event if the
service is monitored.
"""
- services = self._taskConfig.services
- stateDct = {'running': (Clear, log.info),
- 'stopped': (services.get(name), log.critical)}
- if state in stateDct:
- summary = "Windows service '%s' is %s" % (name, state)
- if name in services:
- # monitoring is enabled
- severity, writeToLog = stateDct[state]
- event = {'summary': summary,
- 'eventClass': Status_WinService,
- 'device': self._devId,
- 'severity': severity,
- 'agent': 'newzenwin',
- 'component': name,
- 'eventGroup': 'StatusTest'}
- self._eventService.sendEvent(event)
- else:
- # monitoring is disabled
- writeToLog = log.debug
- writeToLog('%s on %s' % (summary, self._devId))
-
+ state = state.lower()
+ summary = "Windows service '%s' is %s" % (name, state)
+ logLevel = logging.DEBUG
+ if name in self._taskConfig.services:
+ eventCount, stoppedSeverity = self._taskConfig.services[name]
+ if state == self.RUNNING:
+ self._sendWinServiceEvent(name, summary, Clear)
+ logLevel = logging.INFO
+ self._taskConfig.services[name] = (0, stoppedSeverity)
+ elif state == self.STOPPED:
+ self._sendWinServiceEvent(name, summary, stoppedSeverity)
+ logLevel = logging.CRITICAL
+ self._taskConfig.services[name] = (eventCount + 1, stoppedSeverity)
+ log.log(logLevel, '%s on %s', summary, self._devId)
+
def _collectSuccessful(self, results):
"""
Callback for a successful fetch of services from the remote device.
@@ -261,11 +268,23 @@
log.debug("Successful collection from %s [%s], results=%s",
self._devId, self._manageIp, results)
+ # make a local copy of monitored services list
+ services = self._taskConfig.services.copy()
if results:
for result in [r.targetInstance for r in results]:
if result.state:
- self._handleResult(result.name, result.state.lower())
-
+ if result.name in services:
+ # remove service from local copy
+ del services[result.name]
+ self._handleResult(result.name, result.state)
+ # send events for the services that did not show up in results
+ for name, (eventCount, stoppedSeverity) in services.items():
+ if eventCount == 0:
+ state = self.RUNNING
+ else:
+ state = self.STOPPED
+ self._handleResult(name, state)
+ if results:
# schedule another immediate collection so that we'll keep eating
# events as long as they are ready for us; using callLater ensures
# it goes to the end of the immediate work-queue so that other
@@ -277,6 +296,16 @@
d.addCallback(self._collectCallback)
return d
+ def _deviceUp(self, result):
+ msg = 'WMI connection to %s up.' % self._devId
+ self._eventService.sendEvent(dict(
+ summary=msg,
+ eventClass=Status_Wmi,
+ device=self._devId,
+ severity=Clear,
+ component='zenwin'))
+ return result
+
def _collectCallback(self, result):
"""
Callback called after a connect or previous collection so that another
@@ -288,6 +317,7 @@
self.state = ZenWinTask.STATE_WATCHER_QUERY
d = self._watcher.getEvents(self._queryTimeout, self._batchSize)
d.addCallbacks(self._collectSuccessful, self._failure)
+ d.addCallbacks(self._deviceUp)
return d
def _connectCallback(self, result):
@@ -298,12 +328,8 @@
def _connectWatcher(self, result):
self.state = ZenWinTask.STATE_WMIC_PROCESS
- running = [service.name for service in result['query']]
- for name in running:
- self._handleResult(name, 'running')
- for name in self._taskConfig.services:
- if name not in running:
- self._handleResult(name, 'stopped')
+ for service in result['query']:
+ self._handleResult(service.name, service.state)
self._wmic.close()
self._wmic = None
self.state = ZenWinTask.STATE_WATCHER_CONNECT
@@ -314,7 +340,7 @@
def _initialQuery(self, result):
self.state = ZenWinTask.STATE_WMIC_QUERY
- wql = "SELECT Name FROM Win32_Service WHERE State='Running'"
+ wql = "SELECT Name, State FROM Win32_Service"
d = self._wmic.query({'query': wql})
d.addCallback(self._connectWatcher)
return d
|
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/bin.tar.gz
^
|
[-]
[+]
|
Changed |
zenoss-2.5.2.tar.bz2/libzenoss/bin/.buildbot-sourcedata
^
|
@@ -1 +1 @@
-http://dev.zenoss.org/svn/tags/builds/zenoss-build-440/bin
+http://dev.zenoss.org/svn/tags/builds/zenoss-build-590/bin
|