def __init__(self, host, locationTemplate, timeout=5, ssl=False, port=21, user='******', password='******'): """Create an instance of *FtpReader* bound to specific FTP server directory. Args: host (str): domain name or IP address of web server locationTemplate (str): location part of the directory containing @mib@ magic placeholder to be replaced with MIB name fetch. Keyword Args: timeout (int): response timeout ssl (bool): access HTTPS web site port (int): TCP port web server is listening user (str): username at FTP server password (str): password for *username* at FTP server """ self._host = host self._locationTemplate = locationTemplate self._timeout = timeout self._ssl = ssl self._port = port self._user = user self._password = password if '@mib@' not in locationTemplate: raise error.PySmiError( '@mib@ placeholder not specified in location at %s' % self)
def __init__(self, path, ignoreErrors=True): """Create an instance of *ZipReader* serving a ZIP archive. Args: path (str): path to ZIP archive containing MIB files Keyword Args: ignoreErrors (bool): ignore ZIP archive access errors """ self._name = path self._members = {} self._pendingError = None try: self._members = self._readZipDirectory(fileObj=open(path, 'rb')) except Exception: debug.logger & debug.flagReader and debug.logger( 'ZIP file %s open failure: %s' % (self._name, sys.exc_info()[1])) if not ignoreErrors: self._pendingError = error.PySmiError( 'file %s access error: %s' % (self._name, sys.exc_info()[1]))
def getMibRevision(mibDir, mibFile): mibCompiler = MibCompiler( mibParser, codeGenerator, fileWriter ) mibCompiler.addSources( FileReader(mibDir, recursive=False, ignoreErrors=ignoreErrorsFlag), *getReadersFromUrls(*mibSources) ) try: processed = mibCompiler.compile( mibFile, **dict(noDeps=True, rebuild=True, fuzzyMatching=False, ignoreErrors=ignoreErrorsFlag) ) except error.PySmiError: sys.stderr.write('ERROR: %s\r\n' % sys.exc_info()[1]) sys.exit(EX_SOFTWARE) for canonicalMibName in processed: if (processed[canonicalMibName] == 'compiled' and processed[canonicalMibName].path == 'file://' + os.path.join(mibDir, mibFile)): try: revision = datetime.strptime(processed[canonicalMibName].revision, '%Y-%m-%d %H:%M') except Exception: revision = datetime.fromtimestamp(0) return canonicalMibName, revision raise error.PySmiError('Can\'t read or parse MIB "%s"' % os.path.join(mibDir, mibFile))
def __init__(self, *flags, **options): self._flags = flagNone if options.get('printer') is not None: self._printer = options.get('printer') elif self.defaultPrinter is not None: self._printer = self.defaultPrinter else: if 'loggerName' in options: # route our logs to parent logger self._printer = Printer(logger=logging.getLogger( options['loggerName']), handler=NullHandler()) else: self._printer = Printer() self('running pysmi version %s' % __version__) for f in flags: inverse = f and f[0] in ('!', '~') if inverse: f = f[1:] try: if inverse: self._flags &= ~flagMap[f] else: self._flags |= flagMap[f] except KeyError: raise error.PySmiError('bad debug flag %s' % f) self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled'))
def __init__(self, startSym='mibFile', tempdir=''): if tempdir: tempdir = os.path.join(tempdir, startSym) try: os.makedirs(tempdir) except OSError: if sys.exc_info()[1].errno != 17: raise error.PySmiError( 'Failed to create cache directory %s: %s' % (tempdir, sys.exc_info()[1])) self.lexer = self.defaultLexer(tempdir=tempdir) # tokens are required for parser self.tokens = self.lexer.tokens if debug.logger & debug.flagParser: logger = debug.logger.getCurrentLogger() else: logger = yacc.NullLogger() if debug.logger & debug.flagGrammar: debuglogger = debug.logger.getCurrentLogger() else: debuglogger = None self.parser = yacc.yacc(module=self, start=startSym, write_tables=bool(tempdir), debug=False, outputdir=tempdir, debuglog=debuglogger, errorlog=logger)
def getData(self, mibname, **options): debug.logger & debug.flagReader and debug.logger( '%slooking for MIB %s' % (self._recursive and 'recursively ' or '', mibname)) for path in self.getSubdirs(self._path, self._recursive, self._ignoreErrors): for mibalias, mibfile in self.getMibVariants(mibname, **options): f = os.path.join(decode(path), decode(mibfile)) debug.logger & debug.flagReader and debug.logger( 'trying MIB %s' % f) if os.path.exists(f) and os.path.isfile(f): try: mtime = os.stat(f)[8] debug.logger & debug.flagReader and debug.logger( 'source MIB %s mtime is %s, fetching data...' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime)))) fp = open(f, mode='rb') mibData = fp.read(self.maxMibSize) fp.close() if len(mibData) == self.maxMibSize: raise IOError('MIB %s too large' % f) return MibInfo(path='file://%s' % f, file=mibfile, name=mibalias, mtime=mtime), decode(mibData) except (OSError, IOError): debug.logger & debug.flagReader and debug.logger( 'source file %s open failure: %s' % (f, sys.exc_info()[1])) if not self._ignoreErrors: raise error.PySmiError('file %s access error: %s' % (f, sys.exc_info()[1])) raise error.PySmiReaderFileNotModifiedError( 'source MIB %s is older than needed' % f, reader=self) raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
def __init__(self, path, ignoreErrors=True): self._name = path self._members = {} self._pendingError = None try: self._members = self._readZipDirectory(fileObj=open(path, 'rb')) except Exception: debug.logger & debug.flagReader and debug.logger( 'ZIP file %s open failure: %s' % (self._name, sys.exc_info()[1])) if not ignoreErrors: self._pendingError = error.PySmiError('file %s access error: %s' % (self._name, sys.exc_info()[1]))
def lexerFactory(**grammarOptions): classAttr = {} for option in grammarOptions: if grammarOptions[option]: if option not in relaxedGrammar: raise error.PySmiError('Unknown lexer relaxation option: %s' % option) for func in relaxedGrammar[option]: if sys.version_info[0] > 2: classAttr[func.__name__] = func() else: classAttr[func.func_name] = func() return type('SmiLexer', (SmiV2Lexer, ), classAttr)
def getSubdirs(self, path, recursive=True, ignoreErrors=True): if not recursive: return [path] dirs = [path] try: subdirs = os.listdir(path) except OSError: if ignoreErrors: return dirs else: raise error.PySmiError('directory %s access error: %s' % (path, sys.exc_info()[1])) for d in subdirs: d = os.path.join(decode(path), decode(d)) if os.path.isdir(d): dirs.extend(self.getSubdirs(d, recursive)) return dirs
def parserFactory(**grammarOptions): """Factory function producing custom specializations of base *SmiV2Parser* class. Keyword Args: grammarOptions: a list of (bool) typed optional keyword parameters enabling particular set of SMIv2 grammar relaxations. Returns: Specialized copy of *SmiV2Parser* class. Notes: The following SMIv2 grammar relaxation parameters are defined: * supportSmiV1Keywords - parses SMIv1 grammar * supportIndex - tolerates ASN.1 types in INDEX clause * commaAtTheEndOfImport - tolerates stray comma at the end of IMPORT section * commaAtTheEndOfSequence - tolerates stray comma at the end of sequence of elements in MIB * mixOfCommasAndSpaces - tolerate a mix of comma and spaces in MIB enumerations * uppercaseIdentifier - tolerate uppercased MIB identifiers * lowcaseIdentifier - tolerate lowercase MIB identifiers * curlyBracesAroundEnterpriseInTrap - tolerate curly braces around enterprise ID in TRAP MACRO * noCells - tolerate missing cells (XXX) Examples: >>> from pysmi.parser import smi >>> SmiV1Parser = smi.parserFactory(supportSmiV1Keywords=True, supportIndex=True) """ classAttr = {} for option in grammarOptions: if grammarOptions[option]: if option not in relaxedGrammar: raise error.PySmiError('Unknown parser relaxation option: %s' % option) for func in relaxedGrammar[option]: if sys.version_info[0] > 2: classAttr[func.__name__] = func else: classAttr[func.func_name] = func classAttr['defaultLexer'] = lexerFactory(**grammarOptions) return type('SmiParser', (SmiV2Parser, ), classAttr)
def __init__(self, host, port, locationTemplate, timeout=5, ssl=False): """Create an instance of *HttpReader* bound to specific URL. Args: host (str): domain name or IP address of web server port (int): TCP port web server is listening locationTemplate (str): location part of the URL containing @mib@ magic placeholder to be replaced with MIB name fetch. Keyword Args: timeout (int): response timeout ssl (bool): access HTTPS web site """ self._schema = ssl and 'https' or 'http' self._host = host self._port = port self._locationTemplate = decode(locationTemplate) self._timeout = timeout if '@mib@' not in locationTemplate: raise error.PySmiError('@mib@ placeholder not specified in location at %s' % self)
def getReadersFromUrls(*sourceUrls, **options): readers = [] for sourceUrl in sourceUrls: mibSource = urlparse.urlparse(sourceUrl) if sys.version_info[0:2] < (2, 5): class ParseResult(tuple): pass mibSource = ParseResult(mibSource) for k, v in zip( ('scheme', 'netloc', 'path', 'params', 'query', 'fragment', 'username', 'password', 'hostname', 'port'), mibSource + ('', '', '', None)): setattr(mibSource, k, v) if not mibSource.scheme or mibSource.scheme == 'file': readers.append(FileReader(mibSource.path).setOptions(**options)) elif mibSource.scheme in ('http', 'https'): readers.append( HttpReader( mibSource.hostname or mibSource.netloc, mibSource.port or 80, mibSource.path, ssl=mibSource.scheme == 'https').setOptions(**options)) elif mibSource.scheme in ('ftp', 'sftp'): readers.append( FtpReader(mibSource.hostname or mibSource.netloc, mibSource.path, ssl=mibSource.scheme == 'sftp', port=mibSource.port or 21, user=mibSource.username or 'anonymous', password=mibSource.password or 'anonymous@').setOptions(**options)) else: raise error.PySmiError('Unsupported URL scheme %s' % sourceUrl) return readers
def compile(self, *mibnames, **options): """Transform requested and possibly referred MIBs. The *compile* method should be invoked when *MibCompiler* object is operational meaning at least *sources* are specified. Once called with a MIB module name, *compile* will: * fetch ASN.1 MIB module with given name by calling *sources* * make sure no such transformed MIB already exists (with *searchers*) * parse ASN.1 MIB text with *parser* * perform actual MIB transformation into target format with *code generator* * may attempt to borrow pre-transformed MIB through *borrowers* * write transformed MIB through *writer* The above sequence will be performed for each MIB name given in *mibnames* and may be performed for all MIBs referred to from MIBs being processed. Args: mibnames: list of ASN.1 MIBs names options: options that affect the way PySMI components work Returns: A dictionary of MIB module names processed (keys) and *MibStatus* class instances (values) """ processed = {} parsedMibs = {} failedMibs = {} borrowedMibs = {} builtMibs = {} symbolTableMap = {} mibsToParse = [x for x in mibnames] canonicalMibNames = {} while mibsToParse: mibname = mibsToParse.pop(0) if mibname in parsedMibs: debug.logger & debug.flagCompiler and debug.logger( 'MIB %s already parsed' % mibname) continue if mibname in failedMibs: debug.logger & debug.flagCompiler and debug.logger( 'MIB %s already failed' % mibname) continue for source in self._sources: debug.logger & debug.flagCompiler and debug.logger( 'trying source %s' % source) try: fileInfo, fileData = source.getData(mibname) for mibTree in self._parser.parse(fileData): mibInfo, symbolTable = self._symbolgen.genCode( mibTree, symbolTableMap) symbolTableMap[mibInfo.name] = symbolTable parsedMibs[mibInfo.name] = fileInfo, mibInfo, mibTree if mibname in failedMibs: del failedMibs[mibname] mibsToParse.extend(mibInfo.imported) if fileInfo.name in mibnames: if mibInfo.name not in canonicalMibNames: canonicalMibNames[mibInfo.name] = [] canonicalMibNames[mibInfo.name].append( fileInfo.name) debug.logger & debug.flagCompiler and debug.logger( '%s (%s) read from %s, immediate dependencies: %s' % (mibInfo.name, mibname, fileInfo.path, ', '.join(mibInfo.imported) or '<none>')) break except error.PySmiReaderFileNotFoundError: debug.logger & debug.flagCompiler and debug.logger( 'no %s found at %s' % (mibname, source)) continue except error.PySmiError: exc_class, exc, tb = sys.exc_info() exc.source = source exc.mibname = mibname exc.msg += ' at MIB %s' % mibname debug.logger & debug.flagCompiler and debug.logger( '%serror %s from %s' % (options.get('ignoreErrors') and 'ignoring ' or 'failing on ', exc, source)) failedMibs[mibname] = exc processed[mibname] = statusFailed.setOptions(error=exc) else: exc = error.PySmiError('MIB source %s not found' % mibname) exc.mibname = mibname debug.logger & debug.flagCompiler and debug.logger( 'no %s found everywhere' % mibname) if mibname not in failedMibs: failedMibs[mibname] = exc if mibname not in processed: processed[mibname] = statusMissing debug.logger & debug.flagCompiler and debug.logger( 'MIBs analyzed %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs))) # # See what MIBs need generating # for mibname in tuple(parsedMibs): fileInfo, mibInfo, mibTree = parsedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( 'checking if %s requires updating' % mibname) for searcher in self._searchers: try: searcher.fileExists(mibname, fileInfo.mtime, rebuild=options.get('rebuild')) except error.PySmiFileNotFoundError: debug.logger & debug.flagCompiler and debug.logger( 'no compiled MIB %s available through %s' % (mibname, searcher)) continue except error.PySmiFileNotModifiedError: debug.logger & debug.flagCompiler and debug.logger( 'will be using existing compiled MIB %s found by %s' % (mibname, searcher)) del parsedMibs[mibname] processed[mibname] = statusUntouched break except error.PySmiError: exc_class, exc, tb = sys.exc_info() exc.searcher = searcher exc.mibname = mibname exc.msg += ' at MIB %s' % mibname debug.logger & debug.flagCompiler and debug.logger( 'error from %s: %s' % (searcher, exc)) continue else: debug.logger & debug.flagCompiler and debug.logger( 'no suitable compiled MIB %s found anywhere' % mibname) if options.get('noDeps') and mibname not in canonicalMibNames: debug.logger & debug.flagCompiler and debug.logger( 'excluding imported MIB %s from code generation' % mibname) del parsedMibs[mibname] processed[mibname] = statusUntouched continue debug.logger & debug.flagCompiler and debug.logger( 'MIBs parsed %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs))) # # Generate code for parsed MIBs # for mibname in parsedMibs.copy(): fileInfo, mibInfo, mibTree = parsedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( 'compiling %s read from %s' % (mibname, fileInfo.path)) platform_info, user_info = self._get_system_info() comments = [ 'ASN.1 source %s' % fileInfo.path, 'Produced by %s-%s at %s' % (packageName, packageVersion, time.asctime()), 'On host %s platform %s version %s by user %s' % (platform_info[1], platform_info[0], platform_info[2], user_info[0]), 'Using Python version %s' % sys.version.split('\n')[0] ] try: mibInfo, mibData = self._codegen.genCode( mibTree, symbolTableMap, comments=comments, genTexts=options.get('genTexts'), textFilter=options.get('textFilter')) builtMibs[mibname] = fileInfo, mibInfo, mibData del parsedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( '%s read from %s and compiled by %s' % (mibname, fileInfo.path, self._writer)) except error.PySmiError: exc_class, exc, tb = sys.exc_info() exc.handler = self._codegen exc.mibname = mibname exc.msg += ' at MIB %s' % mibname debug.logger & debug.flagCompiler and debug.logger( 'error from %s: %s' % (self._codegen, exc)) processed[mibname] = statusFailed.setOptions(error=exc) failedMibs[mibname] = exc del parsedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( 'MIBs built %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs))) # # Try to borrow pre-compiled MIBs for failed ones # for mibname in failedMibs.copy(): if options.get('noDeps') and mibname not in canonicalMibNames: debug.logger & debug.flagCompiler and debug.logger( 'excluding imported MIB %s from borrowing' % mibname) continue for borrower in self._borrowers: debug.logger & debug.flagCompiler and debug.logger( 'trying to borrow %s from %s' % (mibname, borrower)) try: fileInfo, fileData = borrower.getData( mibname, genTexts=options.get('genTexts')) borrowedMibs[mibname] = fileInfo, MibInfo( name=mibname, imported=[]), fileData del failedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( '%s borrowed with %s' % (mibname, borrower)) break except error.PySmiError: debug.logger & debug.flagCompiler and debug.logger( 'error from %s: %s' % (borrower, sys.exc_info()[1])) debug.logger & debug.flagCompiler and debug.logger( 'MIBs available for borrowing %s, MIBs failed %s' % (len(borrowedMibs), len(failedMibs))) # # See what MIBs need borrowing # for mibname in borrowedMibs.copy(): debug.logger & debug.flagCompiler and debug.logger( 'checking if failed MIB %s requires borrowing' % mibname) fileInfo, mibInfo, mibData = borrowedMibs[mibname] for searcher in self._searchers: try: searcher.fileExists(mibname, fileInfo.mtime, rebuild=options.get('rebuild')) except error.PySmiFileNotFoundError: debug.logger & debug.flagCompiler and debug.logger( 'no compiled MIB %s available through %s' % (mibname, searcher)) continue except error.PySmiFileNotModifiedError: debug.logger & debug.flagCompiler and debug.logger( 'will be using existing compiled MIB %s found by %s' % (mibname, searcher)) del borrowedMibs[mibname] processed[mibname] = statusUntouched break except error.PySmiError: exc_class, exc, tb = sys.exc_info() exc.searcher = searcher exc.mibname = mibname exc.msg += ' at MIB %s' % mibname debug.logger & debug.flagCompiler and debug.logger( 'error from %s: %s' % (searcher, exc)) continue else: debug.logger & debug.flagCompiler and debug.logger( 'no suitable compiled MIB %s found anywhere' % mibname) if options.get('noDeps') and mibname not in canonicalMibNames: debug.logger & debug.flagCompiler and debug.logger( 'excluding imported MIB %s from borrowing' % mibname) processed[mibname] = statusUntouched else: debug.logger & debug.flagCompiler and debug.logger( 'will borrow MIB %s' % mibname) builtMibs[mibname] = borrowedMibs[mibname] processed[mibname] = statusBorrowed.setOptions( path=fileInfo.path, file=fileInfo.file, alias=fileInfo.name) del borrowedMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( 'MIBs built %s, MIBs failed %s' % (len(builtMibs), len(failedMibs))) # # We could attempt to ignore missing/failed MIBs # if failedMibs and not options.get('ignoreErrors'): debug.logger & debug.flagCompiler and debug.logger( 'failing with problem MIBs %s' % ', '.join(failedMibs)) for mibname in builtMibs: processed[mibname] = statusUnprocessed return processed debug.logger & debug.flagCompiler and debug.logger( 'proceeding with built MIBs %s, failed MIBs %s' % (', '.join(builtMibs), ', '.join(failedMibs))) # # Store compiled MIBs # for mibname in builtMibs.copy(): fileInfo, mibInfo, mibData = builtMibs[mibname] try: if options.get('writeMibs', True): self._writer.putData(mibname, mibData, dryRun=options.get('dryRun')) debug.logger & debug.flagCompiler and debug.logger( '%s stored by %s' % (mibname, self._writer)) del builtMibs[mibname] if mibname not in processed: processed[mibname] = statusCompiled.setOptions( path=fileInfo.path, file=fileInfo.file, alias=fileInfo.name, oid=mibInfo.oid, oids=mibInfo.oids, identity=mibInfo.identity, revision=mibInfo.revision, enterprise=mibInfo.enterprise, compliance=mibInfo.compliance, ) except error.PySmiError: exc_class, exc, tb = sys.exc_info() exc.handler = self._codegen exc.mibname = mibname exc.msg += ' at MIB %s' % mibname debug.logger & debug.flagCompiler and debug.logger( 'error %s from %s' % (exc, self._writer)) processed[mibname] = statusFailed.setOptions(error=exc) failedMibs[mibname] = exc del builtMibs[mibname] debug.logger & debug.flagCompiler and debug.logger( 'MIBs modified: %s' % ', '.join([ x for x in processed if processed[x] in ('compiled', 'borrowed') ])) return processed