1# 2# This file is part of snmpclitools software. 3# 4# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com> 5# License: http://snmplabs.com/snmpclitools/license.html 6# 7# C/L interface to MIB variables. Mimics Net-SNMP CLI. 8# 9import os 10from pyasn1.type import univ 11from pyasn1.type import namedval 12from snmpclitools.cli import base 13from pysnmp.proto import rfc1902 14from pysnmp.smi import builder, compiler 15from pysnmp import error 16 17defaultMibSourceUrl = 'http://mibs.snmplabs.com/asn1/@mib@' 18defaultMibBorrowerUrl = 'http://mibs.snmplabs.com/pysnmp/fulltexts/@mib@' 19 20 21def getUsage(): 22 return """\ 23MIB options: 24 -m MIB[:...] load given list of MIBs (ALL loads all compiled MIBs) 25 -M DIR[:...] look in given list of directories for MIBs 26 -P MIBOPTS Toggle various defaults controlling MIB parsing: 27 XS: search for ASN.1 MIBs in remote directories specified 28 in URL form. The @mib@ token in the URL is substituted 29 with actual MIB to be downloaded. Default repository 30 address is %s 31 XB: search for pysnmp MIBs in remote directories specified 32 in URL form. The @mib@ token in the URL is substituted 33 with actual MIB to be downloaded. Default repository 34 address is %s 35 -O OUTOPTS Toggle various defaults controlling output display: 36 q: removes the equal sign and type information 37 Q: removes the type information 38 f: print full OIDs on output 39 s: print only last symbolic element of OID 40 S: print MIB module-id plus last element 41 u: print OIDs using UCD-style prefix suppression 42 n: print OIDs numerically 43 e: print enums numerically 44 b: do not break OID indexes down 45 E: include a " to escape the quotes in indices 46 X: place square brackets around each index 47 T: print value in hex 48 v: print values only (not OID = value) 49 U: don't print units 50 t: output timeticks values as raw numbers 51 -I INOPTS Toggle various defaults controlling input parsing: 52 h: don't apply DISPLAY-HINTs 53 u: top-level OIDs must have '.' prefix (UCD-style) 54""" % (defaultMibSourceUrl, defaultMibBorrowerUrl) 55 56# Scanner 57 58 59class MibViewScannerMixIn: 60 def t_mibfiles(self, s): 61 r' -m ' 62 self.rv.append(base.ConfigToken('mibfiles')) 63 64 def t_mibdirs(self, s): 65 r' -M ' 66 self.rv.append(base.ConfigToken('mibdirs')) 67 68 def t_parseropts(self, s): 69 r' -P ' 70 self.rv.append(base.ConfigToken('parseropts')) 71 72 def t_outputopts(self, s): 73 r' -O ' 74 self.rv.append(base.ConfigToken('outputopts')) 75 76 def t_inputopts(self, s): 77 r' -I ' 78 self.rv.append(base.ConfigToken('inputopts')) 79 80# Parser 81 82 83class MibViewParserMixIn: 84 def p_mibView(self, args): 85 ''' 86 Option ::= GeneralOption 87 Option ::= ParserOption 88 Option ::= OutputOption 89 Option ::= InputOption 90 91 GeneralOption ::= MibDirList 92 MibDirList ::= mibdirs MibDirs 93 MibDirList ::= mibdirs whitespace MibDirs 94 MibDirs ::= MibDir semicolon MibDirs 95 MibDirs ::= MibDir 96 MibDir ::= string 97 GeneralOption ::= MibFileList 98 MibFileList ::= mibfiles MibFiles 99 MibFileList ::= mibfiles whitespace MibFiles 100 MibFiles ::= MibFile semicolon MibFiles 101 MibFiles ::= MibFile 102 MibFile ::= string 103 104 ParserOption ::= parseropts string 105 ParserOption ::= parseropts whitespace string 106 ParserOption ::= parseropts string whitespace Url 107 ParserOption ::= parseropts whitespace string whitespace Url 108 Url ::= string semicolon string 109 110 OutputOption ::= outputopts string 111 OutputOption ::= outputopts whitespace string 112 113 InputOption ::= inputopts string 114 InputOption ::= inputopts whitespace string 115 ''' 116 117# Generator 118 119 120class __MibViewGenerator(base.GeneratorTemplate): 121 # Load MIB modules 122 def n_MibFile(self, cbCtx, node): 123 snmpEngine, ctx = cbCtx 124 mibBuilder = snmpEngine.getMibBuilder() 125 if node[0].attr.lower() == 'all': 126 mibBuilder.loadModules() 127 else: 128 mibBuilder.loadModules(node[0].attr) 129 130 def n_MibDir(self, cbCtx, node): 131 snmpEngine, ctx = cbCtx 132 if 'MibDir' not in ctx: 133 ctx['MibDir'] = [] 134 ctx['MibDir'].append(node[0].attr) 135 136 def n_Url(self, cbCtx, node): 137 snmpEngine, ctx = cbCtx 138 ctx['Url'] = node[0].attr + ':' + node[2].attr 139 140 def n_ParserOption_exit(self, cbCtx, node): 141 snmpEngine, ctx = cbCtx 142 opt = node[1].attr or node[2].attr 143 for c in opt: 144 if c == 'XS': 145 if 'MibDir' not in ctx: 146 ctx['MibDir'] = [] 147 if 'Url' not in ctx: 148 raise error.PySnmpError('Missing URL for option') 149 ctx['MibDir'].append(ctx['Url']) 150 del ctx['Url'] 151 elif c == 'XB': 152 if 'MibBorrowers' not in ctx: 153 ctx['MibBorrowers'] = [] 154 if 'Url' not in ctx: 155 raise error.PySnmpError('Missing URL for option') 156 ctx['MibBorrowers'].append(ctx['Url']) 157 del ctx['Url'] 158 159 def n_OutputOption(self, cbCtx, node): 160 snmpEngine, ctx = cbCtx 161 mibViewProxy = ctx['mibViewProxy'] 162 opt = node[1].attr or node[2].attr 163 for c in opt: 164 if c == 'q': 165 mibViewProxy.buildEqualSign = False 166 mibViewProxy.buildTypeInfo = False 167 elif c == 'Q': 168 mibViewProxy.buildTypeInfo = False 169 elif c == 'f': 170 mibViewProxy.buildModInfo = False 171 mibViewProxy.buildObjectDesc = False 172 mibViewProxy.buildAbsoluteName = True 173 elif c == 's': 174 mibViewProxy.buildModInfo = False 175 mibViewProxy.buildObjectDesc = True 176 elif c == 'S': 177 mibViewProxy.buildObjectDesc = True 178 elif c == 'n': 179 mibViewProxy.buildObjectDesc = False 180 mibViewProxy.buildModInfo = False 181 mibViewProxy.buildNumericName = True 182 mibViewProxy.buildNumericIndices = True 183 mibViewProxy.buildAbsoluteName = True 184 elif c == 'e': 185 mibViewProxy.buildEnums = False 186 elif c == 'b': 187 mibViewProxy.buildNumericIndices = True 188 elif c == 'E': 189 mibViewProxy.buildEscQuotes = True 190 elif c == 'X': 191 mibViewProxy.buildSquareBrackets = True 192 elif c == 'T': 193 mibViewProxy.buildHexVals = True 194 elif c == 'v': 195 mibViewProxy.buildObjectName = False 196 elif c == 'U': 197 mibViewProxy.buildUnits = False 198 elif c == 't': 199 mibViewProxy.buildRawTimeTicks = True 200 pass 201 elif c == 'R': 202 mibViewProxy.buildRawVals = True 203 else: 204 raise error.PySnmpError( 205 'Unknown output option %s at %s' % (c, self) 206 ) 207 208 def n_InputOption(self, cbCtx, node): 209 snmpEngine, ctx = cbCtx 210 mibViewProxy = ctx['mibViewProxy'] 211 opt = node[1].attr or node[2].attr 212 for c in opt: 213 if c == 'R': 214 pass 215 elif c == 'b': 216 pass 217 elif c == 'u': 218 mibViewProxy.defaultOidPrefix = ( 219 'iso', 'org', 'dod', 'internet', 'mgmt', 'mib-2' 220 ) 221 elif c == 'r': 222 pass 223 elif c == 'h': 224 pass 225 else: 226 raise error.PySnmpError( 227 'Unknown input option %s at %s' % (c, self) 228 ) 229 230 231def generator(cbCtx, ast): 232 snmpEngine, ctx = cbCtx 233 if 'mibViewProxy' not in ctx: 234 ctx['mibViewProxy'] = MibViewProxy(ctx['mibViewController']) 235 236 compiler.addMibCompiler(snmpEngine.getMibBuilder()) 237 238 snmpEngine, ctx = __MibViewGenerator().preorder((snmpEngine, ctx), ast) 239 240 if 'MibDir' not in ctx: 241 ctx['MibDir'] = [defaultMibSourceUrl] 242 if 'MibBorrowers' not in ctx: 243 ctx['MibBorrowers'] = [defaultMibBorrowerUrl] 244 245 compiler.addMibCompiler(snmpEngine.getMibBuilder(), 246 sources=ctx['MibDir'], 247 borrowers=ctx['MibBorrowers']) 248 return snmpEngine, ctx 249 250 251class UnknownSyntax: 252 def prettyOut(self, val): 253 return str(val) 254unknownSyntax = UnknownSyntax() 255 256# Proxy MIB view 257 258 259class MibViewProxy: 260 # Defaults 261 defaultOidPrefix = ( 262 'iso', 'org', 'dod', 'internet', 'mgmt', 'mib-2', 'system' 263 ) 264 defaultMibs = ('SNMPv2-MIB',) 265 defaultMibDirs = () 266 267 # MIB parsing options 268 # currently N/A 269 270 # MIB output options 271 buildObjectName = True 272 buildValue = True 273 buildModInfo = True 274 buildObjectDesc = True 275 buildNumericName = False 276 buildAbsoluteName = False 277 buildEnums = True 278 buildNumericIndices = False 279 buildEqualSign = True 280 buildTypeInfo = True 281 buildEscQuotes = False 282 buildSquareBrackets = False 283 buildHexVals = False 284 buildRawVals = False 285 buildRawTimeTicks = False 286 buildGuessedStringVals = True 287 buildUnits = True 288 289 # MIB input options 290 parseAsRandomAccessMib = True 291 parseAsRegExp = False 292 parseAsRelativeOid = True 293 parseAndCheckIndices = True 294 parseAsDisplayHint = True 295 296 def __init__(self, mibViewController): 297 if 'PYSNMPOIDPREFIX' in os.environ: 298 self.defaultOidPrefix = os.environ['PYSNMPOIDPREFIX'] 299 if 'PYSNMPMIBS' in os.environ: 300 self.defaultMibs = os.environ['PYSNMPMIBS'].split(':') 301 if 'PYSNMPMIBDIRS' in os.environ: 302 self.defaultMibDirs = os.environ['PYSNMPMIBDIRS'].split(':') 303 if self.defaultMibDirs: 304 mibViewController.mibBuilder.setMibSources( 305 *(mibViewController.mibBuilder.getMibSources() + 306 tuple([builder.ZipMibSource(m).init() for m in self.defaultMibDirs])) 307 ) 308 if self.defaultMibs: 309 mibViewController.mibBuilder.loadModules(*self.defaultMibs) 310 self.__oidValue = univ.ObjectIdentifier() 311 self.__intValue = univ.Integer() 312 self.__timeValue = rfc1902.TimeTicks() 313 self.__bitsValue = rfc1902.Bits() 314 315 def getPrettyOidVal(self, mibViewController, oid, val): 316 prefix, label, suffix = mibViewController.getNodeName(oid) 317 modName, nodeDesc, _suffix = mibViewController.getNodeLocation(prefix) 318 out = '' 319 # object name 320 if self.buildObjectName: 321 if self.buildModInfo: 322 out = '%s::' % modName 323 if self.buildObjectDesc: 324 out += nodeDesc 325 else: 326 if self.buildNumericName: 327 name = prefix 328 else: 329 name = label 330 if not self.buildAbsoluteName: 331 name = name[len(self.defaultOidPrefix):] 332 out += '.'.join([str(x) for x in name]) 333 334 if suffix: 335 if suffix == (0,): 336 out += '.0' 337 else: 338 m, n, s = mibViewController.getNodeLocation(prefix[:-1]) 339 rowNode, = mibViewController.mibBuilder.importSymbols( 340 m, n 341 ) 342 if self.buildNumericIndices: 343 out += '.' + '.'.join([str(x) for x in suffix]) 344 else: 345 try: 346 for i in rowNode.getIndicesFromInstId(suffix): 347 if self.buildEscQuotes: 348 out += '.\\\"%s\\\"' % i.prettyOut(i) 349 elif self.buildSquareBrackets: 350 out += '.[%s]' % i.prettyOut(i) 351 else: 352 out += '.\"%s\"' % i.prettyOut(i) 353 except Exception: 354 out += '.' + '.'.join( 355 [str(x) for x in suffix] 356 ) 357 358 if self.buildObjectName and self.buildValue: 359 if self.buildEqualSign: 360 out += ' = ' 361 else: 362 out += ' ' 363 364 # Value 365 if self.buildValue: 366 if isinstance(val, univ.Null): 367 return out + val.prettyPrint() 368 mibNode, = mibViewController.mibBuilder.importSymbols( 369 modName, nodeDesc 370 ) 371 if hasattr(mibNode, 'syntax'): 372 syntax = mibNode.syntax 373 else: 374 syntax = val 375 if syntax is None: # lame Agent may return a non-instance OID 376 syntax = unknownSyntax 377 if self.buildTypeInfo: 378 out += '%s: ' % syntax.__class__.__name__ 379 if self.buildRawVals: 380 out += str(val) 381 elif self.buildHexVals: # XXX make it always in hex? 382 if self.__intValue.isSuperTypeOf(val): 383 out += '%x' % int(val) 384 elif self.__timeValue.isSameTypeWith(val): 385 out += '%x' % int(val) 386 elif self.__oidValue.isSuperTypeOf(val): 387 out += ' '.join(['%x' % x for x in tuple(val)]) 388 else: 389 out += ' '.join(['%.2x' % x for x in val.asNumbers()]) 390 elif self.__timeValue.isSameTypeWith(val): 391 if self.buildRawTimeTicks: 392 out += str(int(val)) 393 else: # TimeTicks is not a TC 394 val = int(val) 395 d, m = divmod(val, 8640000) 396 out += '%d days ' % d 397 d, m = divmod(m, 360000) 398 out += '%d:' % d 399 d, m = divmod(m, 6000) 400 out += '%d:' % d 401 d, m = divmod(m, 100) 402 out += '%d.%d' % (d, m) 403 elif self.__oidValue.isSuperTypeOf(val): 404 oid, label, suffix = mibViewController.getNodeName(val) 405 out += '.'.join( 406 label + tuple([str(x) for x in suffix]) 407 ) 408 elif (not self.buildEnums and 409 (self.__intValue.isSuperTypeOf(val) or 410 self.__bitsValue.isSuperTypeOf(val))): 411 out += syntax.clone(val, namedValues=namedval.NamedValues()).prettyPrint() 412 else: 413 out += syntax.clone(val).prettyPrint() 414 415 if self.buildUnits: 416 if hasattr(mibNode, 'getUnits'): 417 out += ' %s' % mibNode.getUnits() 418 419 return out 420 421 def setPrettyOidValue(self, oid, val, t): 422 return oid, val 423