1#
2# This file is part of pysnmp software.
3#
4# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
5# License: http://snmplabs.com/pysnmp/license.html
6#
7import sys
8from pysnmp.smi.indices import OrderedDict, OidOrderedDict
9from pysnmp.smi import error
10from pysnmp import debug
11
12__all__ = ['MibViewController']
13
14if sys.version_info[0] <= 2:
15    import types
16
17    classTypes = (types.ClassType, type)
18    instanceTypes = (types.InstanceType, object)
19else:
20    classTypes = (type,)
21    instanceTypes = (object,)
22
23
24class MibViewController(object):
25    def __init__(self, mibBuilder):
26        self.mibBuilder = mibBuilder
27        self.lastBuildId = -1
28        self.__mibSymbolsIdx = OrderedDict()
29
30    # Indexing part
31
32    def indexMib(self):
33        if self.lastBuildId == self.mibBuilder.lastBuildId:
34            return
35
36        debug.logger & debug.flagMIB and debug.logger('indexMib: re-indexing MIB view')
37
38        MibScalarInstance, = self.mibBuilder.importSymbols(
39            'SNMPv2-SMI', 'MibScalarInstance'
40        )
41
42        #
43        # Create indices
44        #
45
46        # Module name -> module-scope indices
47        self.__mibSymbolsIdx.clear()
48
49        # Oid <-> label indices
50
51        # This is potentially ambiguous mapping. Sort modules in
52        # ascending age for resolution
53        def __sortFun(x, b=self.mibBuilder):
54            if b.moduleID in b.mibSymbols[x]:
55                m = b.mibSymbols[x][b.moduleID]
56                r = m.getRevisions()
57                if r:
58                    return r[0]
59
60            return "1970-01-01 00:00"
61
62        modNames = list(self.mibBuilder.mibSymbols.keys())
63        modNames.sort(key=__sortFun)
64
65        # Index modules names
66        for modName in [''] + modNames:
67            # Modules index
68            self.__mibSymbolsIdx[modName] = mibMod = {
69                'oidToLabelIdx': OidOrderedDict(),
70                'labelToOidIdx': {},
71                'varToNameIdx': {},
72                'typeToModIdx': OrderedDict(),
73                'oidToModIdx': {}
74            }
75
76            if not modName:
77                globMibMod = mibMod
78                continue
79
80            # Types & MIB vars indices
81            for n, v in self.mibBuilder.mibSymbols[modName].items():
82                if n == self.mibBuilder.moduleID:  # do not index this
83                    continue  # special symbol
84                if isinstance(v, classTypes):
85                    if n in mibMod['typeToModIdx']:
86                        raise error.SmiError(
87                            'Duplicate SMI type %s::%s, has %s' % (modName, n, mibMod['typeToModIdx'][n])
88                        )
89                    globMibMod['typeToModIdx'][n] = modName
90                    mibMod['typeToModIdx'][n] = modName
91                elif isinstance(v, instanceTypes):
92                    if isinstance(v, MibScalarInstance):
93                        continue
94                    if n in mibMod['varToNameIdx']:
95                        raise error.SmiError(
96                            'Duplicate MIB variable %s::%s has %s' % (modName, n, mibMod['varToNameIdx'][n])
97                        )
98                    globMibMod['varToNameIdx'][n] = v.name
99                    mibMod['varToNameIdx'][n] = v.name
100                    # Potentionally ambiguous mapping ahead
101                    globMibMod['oidToModIdx'][v.name] = modName
102                    mibMod['oidToModIdx'][v.name] = modName
103                    globMibMod['oidToLabelIdx'][v.name] = (n,)
104                    mibMod['oidToLabelIdx'][v.name] = (n,)
105                else:
106                    raise error.SmiError(
107                        'Unexpected object %s::%s' % (modName, n)
108                    )
109
110        # Build oid->long-label index
111        oidToLabelIdx = self.__mibSymbolsIdx['']['oidToLabelIdx']
112        labelToOidIdx = self.__mibSymbolsIdx['']['labelToOidIdx']
113        prevOid = ()
114        baseLabel = ()
115        for key in oidToLabelIdx.keys():
116            keydiff = len(key) - len(prevOid)
117            if keydiff > 0:
118                if prevOid:
119                    if keydiff == 1:
120                        baseLabel = oidToLabelIdx[prevOid]
121                    else:
122                        baseLabel += key[-keydiff:-1]
123                else:
124                    baseLabel = ()
125            elif keydiff < 0:
126                baseLabel = ()
127                keyLen = len(key)
128                i = keyLen - 1
129                while i:
130                    k = key[:i]
131                    if k in oidToLabelIdx:
132                        baseLabel = oidToLabelIdx[k]
133                        if i != keyLen - 1:
134                            baseLabel += key[i:-1]
135                        break
136                    i -= 1
137            # Build oid->long-label index
138            oidToLabelIdx[key] = baseLabel + oidToLabelIdx[key]
139            # Build label->oid index
140            labelToOidIdx[oidToLabelIdx[key]] = key
141            prevOid = key
142
143        # Build module-scope oid->long-label index
144        for mibMod in self.__mibSymbolsIdx.values():
145            for oid in mibMod['oidToLabelIdx'].keys():
146                mibMod['oidToLabelIdx'][oid] = oidToLabelIdx[oid]
147                mibMod['labelToOidIdx'][oidToLabelIdx[oid]] = oid
148
149        self.lastBuildId = self.mibBuilder.lastBuildId
150
151    # Module management
152
153    def getOrderedModuleName(self, index):
154        self.indexMib()
155        modNames = self.__mibSymbolsIdx.keys()
156        if modNames:
157            return modNames[index]
158        raise error.SmiError('No modules loaded at %s' % self)
159
160    def getFirstModuleName(self):
161        return self.getOrderedModuleName(0)
162
163    def getLastModuleName(self):
164        return self.getOrderedModuleName(-1)
165
166    def getNextModuleName(self, modName):
167        self.indexMib()
168        try:
169            return self.__mibSymbolsIdx.nextKey(modName)
170        except KeyError:
171            raise error.SmiError(
172                'No module next to %s at %s' % (modName, self)
173            )
174
175    # MIB tree node management
176
177    def __getOidLabel(self, nodeName, oidToLabelIdx, labelToOidIdx):
178        """getOidLabel(nodeName) -> (oid, label, suffix)"""
179        if not nodeName:
180            return nodeName, nodeName, ()
181        if nodeName in labelToOidIdx:
182            return labelToOidIdx[nodeName], nodeName, ()
183        if nodeName in oidToLabelIdx:
184            return nodeName, oidToLabelIdx[nodeName], ()
185        if len(nodeName) < 2:
186            return nodeName, nodeName, ()
187        oid, label, suffix = self.__getOidLabel(
188            nodeName[:-1], oidToLabelIdx, labelToOidIdx
189        )
190        suffix = suffix + nodeName[-1:]
191        resLabel = label + tuple([str(x) for x in suffix])
192        if resLabel in labelToOidIdx:
193            return labelToOidIdx[resLabel], resLabel, ()
194        resOid = oid + suffix
195        if resOid in oidToLabelIdx:
196            return resOid, oidToLabelIdx[resOid], ()
197        return oid, label, suffix
198
199    def getNodeNameByOid(self, nodeName, modName=''):
200        self.indexMib()
201        if modName in self.__mibSymbolsIdx:
202            mibMod = self.__mibSymbolsIdx[modName]
203        else:
204            raise error.SmiError('No module %s at %s' % (modName, self))
205        oid, label, suffix = self.__getOidLabel(
206            nodeName, mibMod['oidToLabelIdx'], mibMod['labelToOidIdx']
207        )
208        if oid == label:
209            raise error.NoSuchObjectError(
210                str='Can\'t resolve node name %s::%s at %s' %
211                    (modName, nodeName, self)
212            )
213        debug.logger & debug.flagMIB and debug.logger(
214            'getNodeNameByOid: resolved %s:%s -> %s.%s' % (modName, nodeName, label, suffix))
215        return oid, label, suffix
216
217    def getNodeNameByDesc(self, nodeName, modName=''):
218        self.indexMib()
219        if modName in self.__mibSymbolsIdx:
220            mibMod = self.__mibSymbolsIdx[modName]
221        else:
222            raise error.SmiError('No module %s at %s' % (modName, self))
223        if nodeName in mibMod['varToNameIdx']:
224            oid = mibMod['varToNameIdx'][nodeName]
225        else:
226            raise error.NoSuchObjectError(
227                str='No such symbol %s::%s at %s' % (modName, nodeName, self)
228            )
229        debug.logger & debug.flagMIB and debug.logger(
230            'getNodeNameByDesc: resolved %s:%s -> %s' % (modName, nodeName, oid))
231        return self.getNodeNameByOid(oid, modName)
232
233    def getNodeName(self, nodeName, modName=''):
234        # nodeName may be either an absolute OID/label or a
235        # ( MIB-symbol, su, ff, ix)
236        try:
237            # First try nodeName as an OID/label
238            return self.getNodeNameByOid(nodeName, modName)
239        except error.NoSuchObjectError:
240            # ...on failure, try as MIB symbol
241            oid, label, suffix = self.getNodeNameByDesc(nodeName[0], modName)
242            # ...with trailing suffix
243            return self.getNodeNameByOid(oid + suffix + nodeName[1:], modName)
244
245    def getOrderedNodeName(self, index, modName=''):
246        self.indexMib()
247        if modName in self.__mibSymbolsIdx:
248            mibMod = self.__mibSymbolsIdx[modName]
249        else:
250            raise error.SmiError('No module %s at %s' % (modName, self))
251        if not mibMod['oidToLabelIdx']:
252            raise error.NoSuchObjectError(
253                str='No variables at MIB module %s at %s' % (modName, self)
254            )
255        try:
256            oid, label = mibMod['oidToLabelIdx'].items()[index]
257        except KeyError:
258            raise error.NoSuchObjectError(
259                str='No symbol at position %s in MIB module %s at %s' % (index, modName, self)
260            )
261        return oid, label, ()
262
263    def getFirstNodeName(self, modName=''):
264        return self.getOrderedNodeName(0, modName)
265
266    def getLastNodeName(self, modName=''):
267        return self.getOrderedNodeName(-1, modName)
268
269    def getNextNodeName(self, nodeName, modName=''):
270        oid, label, suffix = self.getNodeName(nodeName, modName)
271        try:
272            return self.getNodeName(
273                self.__mibSymbolsIdx[modName]['oidToLabelIdx'].nextKey(oid) + suffix, modName
274            )
275        except KeyError:
276            raise error.NoSuchObjectError(
277                str='No name next to %s::%s at %s' % (modName, nodeName, self)
278            )
279
280    def getParentNodeName(self, nodeName, modName=''):
281        oid, label, suffix = self.getNodeName(nodeName, modName)
282        if len(oid) < 2:
283            raise error.NoSuchObjectError(
284                str='No parent name for %s::%s at %s' %
285                    (modName, nodeName, self)
286            )
287        return oid[:-1], label[:-1], oid[-1:] + suffix
288
289    def getNodeLocation(self, nodeName, modName=''):
290        oid, label, suffix = self.getNodeName(nodeName, modName)
291        return self.__mibSymbolsIdx['']['oidToModIdx'][oid], label[-1], suffix
292
293    # MIB type management
294
295    def getTypeName(self, typeName, modName=''):
296        self.indexMib()
297        if modName in self.__mibSymbolsIdx:
298            mibMod = self.__mibSymbolsIdx[modName]
299        else:
300            raise error.SmiError(
301                'No module %s at %s' % (modName, self)
302            )
303        if typeName in mibMod['typeToModIdx']:
304            m = mibMod['typeToModIdx'][typeName]
305        else:
306            raise error.NoSuchObjectError(
307                str='No such type %s::%s at %s' % (modName, typeName, self)
308            )
309        return m, typeName
310
311    def getOrderedTypeName(self, index, modName=''):
312        self.indexMib()
313        if modName in self.__mibSymbolsIdx:
314            mibMod = self.__mibSymbolsIdx[modName]
315        else:
316            raise error.SmiError('No module %s at %s' % (modName, self))
317        if not mibMod['typeToModIdx']:
318            raise error.NoSuchObjectError(
319                str='No types at MIB module %s at %s' % (modName, self)
320            )
321        t = mibMod['typeToModIdx'].keys()[index]
322        return mibMod['typeToModIdx'][t], t
323
324    def getFirstTypeName(self, modName=''):
325        return self.getOrderedTypeName(0, modName)
326
327    def getLastTypeName(self, modName=''):
328        return self.getOrderedTypeName(-1, modName)
329
330    def getNextType(self, typeName, modName=''):
331        m, t = self.getTypeName(typeName, modName)
332        try:
333            return self.__mibSymbolsIdx[m]['typeToModIdx'].nextKey(t)
334        except KeyError:
335            raise error.NoSuchObjectError(
336                str='No type next to %s::%s at %s' % (modName, typeName, self)
337            )
338