Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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]))
Exemplo n.º 3
0
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))
Exemplo n.º 4
0
    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'))
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
    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]))
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
 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
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
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
Exemplo n.º 13
0
    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