#!/opt/imh-python/bin/python3.9
# This file is part of pysmi software.
# Copyright (c) 2015-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pysmi/license.html
# SNMP SMI/MIB copying tool
from datetime import datetime
from pysmi.reader import FileReader, getReadersFromUrls
from pysmi.writer import CallbackWriter
from pysmi.parser import SmiV1CompatParser
from pysmi.codegen import JsonCodeGen
from pysmi.compiler import MibCompiler
[--cache-directory=<DIRECTORY>]
<SOURCE [SOURCE...]> <DESTINATION>
URI - file, zip, http, https, ftp, sftp schemes are supported.
Use @mib@ placeholder token in URI to refer directly to
the required MIB module when source does not support
directory listing (e.g. HTTP).
'|'.join([x for x in sorted(debug.flagMap)])
# TODO(etingof): add the option to copy MIBs into enterprise-indexed subdirs
opts, inputMibs = getopt.getopt(
['help', 'version', 'verbose', 'quiet', 'debug=',
'mib-source=', 'mib-stub=',
'cache-directory=', 'ignore-errors', 'dry-run']
except getopt.GetoptError:
if opt[0] == '-h' or opt[0] == '--help':
SNMP SMI/MIB files copying tool. When given MIB file(s) or directory(ies)
on input and a destination directory, the tool parses MIBs to figure out
their canonical MIB module name and the latest revision date, then
copies MIB module on input into the destination directory under its
MIB module name *if* there is no such file already or its revision date
http://snmplabs.com/pysmi
if opt[0] == '-v' or opt[0] == '--version':
from pysmi import __version__
SNMP SMI/MIB library version %s, written by Ilya Etingof <etingof@gmail.com>
Software documentation and support at http://snmplabs.com/pysmi
""" % (__version__, sys.version, helpMessage))
if opt[0] == '--verbose':
debug.setLogger(debug.Debug(*opt[1].split(',')))
if opt[0] == '--mib-source':
mibSources.append(opt[1])
if opt[0] == '--cache-directory':
if opt[0] == '--ignore-errors':
mibSources = ['file:///usr/share/snmp/mibs',
'http://mibs.snmplabs.com/asn1/@mib@']
sys.stderr.write('ERROR: MIB source and/or destination arguments not given\r\n%s\r\n' % helpMessage)
dstDirectory = inputMibs.pop()
if os.path.exists(dstDirectory) and not os.path.isdir(dstDirectory):
sys.stderr.write('ERROR: given destination is not a directory\r\n%s\r\n' % helpMessage)
os.makedirs(dstDirectory, mode=0o755)
# Compiler infrastructure
codeGenerator = JsonCodeGen()
mibParser = SmiV1CompatParser(tempdir=cacheDirectory)
fileWriter = CallbackWriter(lambda *x: None)
def getMibRevision(mibDir, mibFile):
mibCompiler = MibCompiler(
FileReader(mibDir, recursive=False, ignoreErrors=ignoreErrorsFlag),
*getReadersFromUrls(*mibSources)
processed = mibCompiler.compile(
mibFile, **dict(noDeps=True, rebuild=True, fuzzyMatching=False, ignoreErrors=ignoreErrorsFlag)
sys.stderr.write('ERROR: %s\r\n' % sys.exc_info()[1])
for canonicalMibName in processed:
if (processed[canonicalMibName] == 'compiled' and
processed[canonicalMibName].path == 'file://' + os.path.join(mibDir, mibFile)):
revision = datetime.strptime(processed[canonicalMibName].revision, '%Y-%m-%d %H:%M')
revision = datetime.fromtimestamp(0)
return canonicalMibName, revision
raise error.PySmiError('Can\'t read or parse MIB "%s"' % os.path.join(mibDir, mibFile))
def shortenPath(path, maxLength=45):
if len(path) > maxLength:
return '...' + path[-maxLength:]
mibsSeen = mibsCopied = mibsFailed = 0
for srcDirectory in inputMibs:
sys.stderr.write('Reading "%s"...\r\n' % srcDirectory)
if os.path.isfile(srcDirectory):
mibFiles = [(os.path.abspath(os.path.dirname(srcDirectory)), os.path.basename(srcDirectory))]
mibFiles = [(os.path.abspath(dirName), mibFile)
for dirName, _, mibFiles in os.walk(srcDirectory)
for srcDirectory, mibFile in mibFiles:
# TODO(etingof): also check module OID to make sure there is no name collision
mibName, srcMibRevision = getMibRevision(srcDirectory, mibFile)
except error.PySmiError as ex:
sys.stderr.write('Failed to read source MIB "%s": %s\r\n' % (os.path.join(srcDirectory, mibFile), ex))
sys.stderr.write('FAILED %s\r\n' % shortenPath(os.path.join(srcDirectory, mibFile)))
if mibName in mibsRevisions:
dstMibRevision = mibsRevisions[mibName]
_, dstMibRevision = getMibRevision(dstDirectory, mibName)
except error.PySmiError as ex:
sys.stderr.write('MIB "%s" is not available at the '
'destination directory "%s": %s\r\n' % (os.path.join(srcDirectory, mibFile),
dstMibRevision = datetime.fromtimestamp(0)
mibsRevisions[mibName] = dstMibRevision
if dstMibRevision >= srcMibRevision:
sys.stderr.write('Destination MIB "%s" has the same or newer revision as the '
'source MIB "%s"\r\n' % (os.path.join(dstDirectory, mibName),
os.path.join(srcDirectory, mibFile)))
sys.stderr.write('NOT COPIED %s (%s)\r\n' % (
shortenPath(os.path.join(srcDirectory, mibFile)), mibName))
mibsRevisions[mibName] = srcMibRevision
sys.stderr.write('Copying "%s" (revision "%s") -> "%s" (revision "%s")\r\n' % (
os.path.join(srcDirectory, mibFile), srcMibRevision,
os.path.join(dstDirectory, mibName), dstMibRevision))
shutil.copy(os.path.join(srcDirectory, mibFile), os.path.join(dstDirectory, mibName))
sys.stderr.write('Failed to copy MIB "%s" -> "%s" (%s): "%s"\r\n' % (
os.path.join(srcDirectory, mibFile), os.path.join(dstDirectory, mibName), mibName, ex))
sys.stderr.write('FAILED %s (%s)\r\n' % (
shortenPath(os.path.join(srcDirectory, mibFile)), mibName))
sys.stderr.write('COPIED %s (%s)\r\n' % (
shortenPath(os.path.join(srcDirectory, mibFile)), mibName))
sys.stderr.write("MIBs seen: %d, copied: %d, failed: %d\r\n" % (mibsSeen, mibsCopied, mibsFailed))