1# 2# This file is part of pysmi software. 3# 4# Copyright (c) 2015-2019, Ilya Etingof <etingof@gmail.com> 5# License: http://snmplabs.com/pysmi/license.html 6# 7import sys 8import re 9from time import strptime, strftime 10from keyword import iskeyword 11from pysmi.mibinfo import MibInfo 12from pysmi.codegen.base import AbstractCodeGen, dorepr 13from pysmi import error 14from pysmi import debug 15 16 17if sys.version_info[0] > 2: 18 # noinspection PyShadowingBuiltins 19 unicode = str 20 # noinspection PyShadowingBuiltins 21 long = int 22 23 24class PySnmpCodeGen(AbstractCodeGen): 25 """Builds PySNMP-specific Python code representing MIB module supplied 26 in form of an Abstract Syntax Tree on input. 27 28 Instance of this class is supposed to be passed to *MibCompiler*, 29 the rest is internal to *MibCompiler*. 30 """ 31 defaultMibPackages = ('pysnmp.smi.mibs', 'pysnmp_mibs') 32 33 symsTable = { 34 'MODULE-IDENTITY': ('ModuleIdentity',), 35 'OBJECT-TYPE': ('MibScalar', 'MibTable', 'MibTableRow', 'MibTableColumn'), 36 'NOTIFICATION-TYPE': ('NotificationType',), 37 'TEXTUAL-CONVENTION': ('TextualConvention',), 38 'MODULE-COMPLIANCE': ('ModuleCompliance',), 39 'OBJECT-GROUP': ('ObjectGroup',), 40 'NOTIFICATION-GROUP': ('NotificationGroup',), 41 'AGENT-CAPABILITIES': ('AgentCapabilities',), 42 'OBJECT-IDENTITY': ('ObjectIdentity',), 43 'TRAP-TYPE': ('NotificationType',), # smidump always uses NotificationType 44 'BITS': ('Bits',), 45 } 46 47 constImports = { 48 'ASN1': ('Integer', 'OctetString', 'ObjectIdentifier'), 49 'ASN1-ENUMERATION': ('NamedValues',), 50 'ASN1-REFINEMENT': ('ConstraintsUnion', 'ConstraintsIntersection', 'SingleValueConstraint', 51 'ValueRangeConstraint', 'ValueSizeConstraint'), 52 'SNMPv2-SMI': ('iso', 53 'Bits', # XXX 54 'Integer32', # XXX 55 'TimeTicks', # bug in some IETF MIBs 56 'Counter32', # bug in some IETF MIBs (e.g. DSA-MIB) 57 'Counter64', # bug in some MIBs (e.g.A3COM-HUAWEI-LswINF-MIB) 58 'NOTIFICATION-TYPE', # bug in some MIBs (e.g. A3COM-HUAWEI-DHCPSNOOP-MIB) 59 'Gauge32', # bug in some IETF MIBs (e.g. DSA-MIB) 60 'MODULE-IDENTITY', 'OBJECT-TYPE', 'OBJECT-IDENTITY', 'Unsigned32', 'IpAddress', # XXX 61 'MibIdentifier'), # OBJECT IDENTIFIER 62 'SNMPv2-TC': ('DisplayString', 'TEXTUAL-CONVENTION',), # XXX 63 'SNMPv2-CONF': ('MODULE-COMPLIANCE', 'NOTIFICATION-GROUP',), # XXX 64 } 65 66 # never compile these, they either: 67 # - define MACROs (implementation supplies them) 68 # - or carry conflicting OIDs (so that all IMPORT's of them will be rewritten) 69 # - or have manual fixes 70 # - or import base ASN.1 types from implementation-specific MIBs 71 fakeMibs = ('ASN1', 72 'ASN1-ENUMERATION', 73 'ASN1-REFINEMENT') 74 baseMibs = ('PYSNMP-USM-MIB', 75 'SNMP-FRAMEWORK-MIB', 76 'SNMP-TARGET-MIB', 77 'TRANSPORT-ADDRESS-MIB', 78 'INET-ADDRESS-MIB') + AbstractCodeGen.baseMibs 79 80 baseTypes = ['Integer', 'Integer32', 'Bits', 'ObjectIdentifier', 'OctetString'] 81 82 typeClasses = { 83 'COUNTER32': 'Counter32', 84 'COUNTER64': 'Counter64', 85 'GAUGE32': 'Gauge32', 86 'INTEGER': 'Integer32', # XXX 87 'INTEGER32': 'Integer32', 88 'IPADDRESS': 'IpAddress', 89 'NETWORKADDRESS': 'IpAddress', 90 'OBJECT IDENTIFIER': 'ObjectIdentifier', 91 'OCTET STRING': 'OctetString', 92 'OPAQUE': 'Opaque', 93 'TIMETICKS': 'TimeTicks', 94 'UNSIGNED32': 'Unsigned32', 95 'Counter': 'Counter32', 96 'Gauge': 'Gauge32', 97 'NetworkAddress': 'IpAddress', # RFC1065-SMI, RFC1155-SMI -> SNMPv2-SMI 98 'nullSpecific': 'zeroDotZero', # RFC1158-MIB -> SNMPv2-SMI 99 'ipRoutingTable': 'ipRouteTable', # RFC1158-MIB -> RFC1213-MIB 100 'snmpEnableAuthTraps': 'snmpEnableAuthenTraps' # RFC1158-MIB -> SNMPv2-MIB 101 } 102 103 smiv1IdxTypes = ['INTEGER', 'OCTET STRING', 'IPADDRESS', 'NETWORKADDRESS'] 104 105 ifTextStr = 'if mibBuilder.loadTexts: ' 106 indent = ' ' * 4 107 fakeidx = 1000 # starting index for fake symbols 108 109 def __init__(self): 110 self._snmpTypes = set(self.typeClasses.values()) 111 self._snmpTypes.add('Bits') 112 self._rows = set() 113 self._cols = {} # k, v = name, datatype 114 self._exports = set() 115 self._seenSyms = set() 116 self._importMap = {} 117 self._out = {} # k, v = name, generated code 118 self._moduleIdentityOid = None 119 self._moduleRevision = None 120 self.moduleName = ['DUMMY'] 121 self.genRules = {'text': True} 122 self.symbolTable = {} 123 124 def symTrans(self, symbol): 125 if symbol in self.symsTable: 126 return self.symsTable[symbol] 127 128 return symbol, 129 130 @staticmethod 131 def transOpers(symbol): 132 if iskeyword(symbol): 133 symbol = 'pysmi_' + symbol 134 135 return symbol.replace('-', '_') 136 137 def prepData(self, pdata, classmode=False): 138 data = [] 139 140 for el in pdata: 141 if not isinstance(el, tuple): 142 data.append(el) 143 144 elif len(el) == 1: 145 data.append(el[0]) 146 147 else: 148 data.append( 149 self.handlersTable[el[0]](self, self.prepData(el[1:], classmode=classmode), classmode=classmode) 150 ) 151 152 return data 153 154 def genImports(self, imports): 155 outStr = '' 156 157 # conversion to SNMPv2 158 toDel = [] 159 for module in list(imports): 160 161 if module in self.convertImportv2: 162 163 for symbol in imports[module]: 164 165 if symbol in self.convertImportv2[module]: 166 toDel.append((module, symbol)) 167 168 for newImport in self.convertImportv2[module][symbol]: 169 newModule, newSymbol = newImport 170 171 if newModule in imports: 172 imports[newModule].append(newSymbol) 173 else: 174 imports[newModule] = [newSymbol] 175 176 # removing converted symbols 177 for d in toDel: 178 imports[d[0]].remove(d[1]) 179 180 # merging mib and constant imports 181 for module in self.constImports: 182 if module in imports: 183 imports[module] += self.constImports[module] 184 else: 185 imports[module] = self.constImports[module] 186 187 for module in sorted(imports): 188 symbols = () 189 190 for symbol in set(imports[module]): 191 symbols += self.symTrans(symbol) 192 193 if symbols: 194 self._seenSyms.update([self.transOpers(s) for s in symbols]) 195 self._importMap.update([(self.transOpers(s), module) for s in symbols]) 196 197 outStr += ', '.join([self.transOpers(s) for s in symbols]) 198 if len(symbols) < 2: 199 outStr += ',' 200 outStr += ' = mibBuilder.importSymbols("%s")\n' % ('", "'.join((module,) + symbols)) 201 202 return outStr, tuple(sorted(imports)) 203 204 def genExports(self, ): 205 exports = list(self._exports) 206 if not exports: 207 return '' 208 209 numFuncCalls = len(exports) // 254 + 1 210 211 outStr = '' 212 213 for idx in range(numFuncCalls): 214 outStr += 'mibBuilder.exportSymbols("' + self.moduleName[0] + '", ' 215 outStr += ', '.join(exports[254 * idx:254 * (idx + 1)]) + ')\n' 216 217 return outStr 218 219 # noinspection PyMethodMayBeStatic 220 def genLabel(self, symbol, classmode=False): 221 if '-' in symbol or iskeyword(symbol): 222 return classmode and 'label = "' + symbol + '"\n' or '.setLabel("' + symbol + '")' 223 224 return '' 225 226 def addToExports(self, symbol, moduleIdentity=0): 227 if moduleIdentity: 228 self._exports.add('PYSNMP_MODULE_ID=%s' % symbol) 229 230 self._exports.add('%s=%s' % (symbol, symbol)) 231 self._seenSyms.add(symbol) 232 233 # noinspection PyUnusedLocal 234 def regSym(self, symbol, outStr, oidStr=None, moduleIdentity=False): 235 if symbol in self._seenSyms and symbol not in self._importMap: 236 raise error.PySmiSemanticError('Duplicate symbol found: %s' % symbol) 237 238 self.addToExports(symbol, moduleIdentity) 239 self._out[symbol] = outStr 240 241 if moduleIdentity: 242 if self._moduleIdentityOid: 243 raise error.PySmiSemanticError('Duplicate module identity') 244 # TODO: turning literal tuple into a string - hackerish 245 self._moduleIdentityOid = '.'.join(oidStr.split(', '))[1:-1] 246 247 def genNumericOid(self, oid): 248 numericOid = () 249 250 for part in oid: 251 if isinstance(part, tuple): 252 parent, module = part 253 254 if parent == 'iso': 255 numericOid += (1,) 256 continue 257 258 if module not in self.symbolTable: 259 # XXX do getname for possible future borrowed mibs 260 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module) 261 262 if parent not in self.symbolTable[module]: 263 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (parent, module)) 264 265 numericOid += self.genNumericOid(self.symbolTable[module][parent]['oid']) 266 267 else: 268 numericOid += (part,) 269 270 return numericOid 271 272 def getBaseType(self, symName, module): 273 if module not in self.symbolTable: 274 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module) 275 276 if symName not in self.symbolTable[module]: 277 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (symName, module)) 278 279 symType, symSubtype = self.symbolTable[module][symName].get('syntax', (('', ''), '')) 280 281 if not symType[0]: 282 raise error.PySmiSemanticError('unknown type for symbol "%s"' % symName) 283 284 if symType[0] in self.baseTypes: 285 return symType, symSubtype 286 287 else: 288 baseSymType, baseSymSubtype = self.getBaseType(*symType) 289 290 if isinstance(baseSymSubtype, list): 291 if isinstance(symSubtype, list): 292 symSubtype += baseSymSubtype 293 else: 294 symSubtype = baseSymSubtype 295 296 return baseSymType, symSubtype 297 298 # Clause generation functions 299 300 # noinspection PyUnusedLocal 301 def genAgentCapabilities(self, data, classmode=False): 302 name, productRelease, status, description, reference, oid = data 303 304 label = self.genLabel(name) 305 name = self.transOpers(name) 306 307 oidStr, parentOid = oid 308 outStr = name + ' = AgentCapabilities(' + oidStr + ')' + label + '\n' 309 310 if productRelease: 311 outStr += """\ 312if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 313 %(name)s = %(name)s%(productRelease)s 314""" % dict(name=name, productRelease=productRelease) 315 316 if status: 317 outStr += """\ 318if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 319 %(name)s = %(name)s%(status)s 320""" % dict(name=name, status=status) 321 322 if self.genRules['text'] and description: 323 outStr += self.ifTextStr + name + description + '\n' 324 325 if self.genRules['text'] and reference: 326 outStr += name + reference + '\n' 327 328 self.regSym(name, outStr, oidStr) 329 330 return outStr 331 332 # noinspection PyUnusedLocal 333 def genModuleIdentity(self, data, classmode=False): 334 name, lastUpdated, organization, contactInfo, description, revisionsAndDescrs, oid = data 335 336 label = self.genLabel(name) 337 name = self.transOpers(name) 338 339 oidStr, parentOid = oid 340 341 outStr = name + ' = ModuleIdentity(' + oidStr + ')' + label + '\n' 342 343 if revisionsAndDescrs: 344 last_revision, revisions, descriptions = revisionsAndDescrs 345 346 self._moduleRevision = last_revision 347 348 if revisions: 349 outStr += name + revisions + '\n' 350 351 if self.genRules['text'] and descriptions: 352 outStr += """ 353if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 354 %(ifTextStr)s%(name)s%(descriptions)s 355""" % dict(ifTextStr=self.ifTextStr, name=name, descriptions=descriptions) 356 357 if lastUpdated: 358 outStr += self.ifTextStr + name + lastUpdated + '\n' 359 360 if organization: 361 outStr += self.ifTextStr + name + organization + '\n' 362 363 if self.genRules['text'] and contactInfo: 364 outStr += self.ifTextStr + name + contactInfo + '\n' 365 366 if self.genRules['text'] and description: 367 outStr += self.ifTextStr + name + description + '\n' 368 369 self.regSym(name, outStr, oidStr, moduleIdentity=True) 370 371 return outStr 372 373 # noinspection PyUnusedLocal 374 def genModuleCompliance(self, data, classmode=False): 375 name, status, description, reference, compliances, oid = data 376 377 label = self.genLabel(name) 378 name = self.transOpers(name) 379 380 oidStr, parentOid = oid 381 outStr = name + ' = ModuleCompliance(' + oidStr + ')' + label 382 outStr += compliances + '\n' 383 384 if status: 385 outStr += """\ 386if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 387 %(name)s = %(name)s%(status)s 388""" % dict(name=name, status=status) 389 390 if self.genRules['text'] and description: 391 outStr += self.ifTextStr + name + description + '\n' 392 393 if self.genRules['text'] and reference: 394 outStr += self.ifTextStr + name + reference + '\n' 395 396 self.regSym(name, outStr, oidStr) 397 398 return outStr 399 400 # noinspection PyUnusedLocal 401 def genNotificationGroup(self, data, classmode=False): 402 name, objects, status, description, reference, oid = data 403 404 label = self.genLabel(name) 405 name = self.transOpers(name) 406 407 oidStr, parentOid = oid 408 409 outStr = name + ' = NotificationGroup(' + oidStr + ')' + label 410 411 if objects: 412 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")' 413 for obj in objects] 414 415 numFuncCalls = len(objects) // 255 + 1 416 417 if numFuncCalls > 1: 418 objStrParts = [] 419 420 for idx in range(numFuncCalls): 421 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']') 422 423 outStr += """ 424for _%(name)s_obj in [%(objects)s]: 425 if getattr(mibBuilder, 'version', 0) < (4, 4, 2): 426 # WARNING: leading objects get lost here! Upgrade your pysnmp version! 427 %(name)s = %(name)s.setObjects(*_%(name)s_obj) 428 else: 429 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\ 430""" % dict(name=name, objects=', '.join(objStrParts)) 431 432 else: 433 outStr += '.setObjects(' + ', '.join(objects) + ')' 434 435 outStr += '\n' 436 437 if status: 438 outStr += """\ 439if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 440 %(name)s = %(name)s%(status)s 441""" % dict(name=name, status=status) 442 443 if self.genRules['text'] and description: 444 outStr += self.ifTextStr + name + description + '\n' 445 446 if self.genRules['text'] and reference: 447 outStr += name + reference + '\n' 448 449 self.regSym(name, outStr, oidStr) 450 451 return outStr 452 453 # noinspection PyUnusedLocal 454 def genNotificationType(self, data, classmode=False): 455 name, objects, status, description, reference, oid = data 456 457 label = self.genLabel(name) 458 name = self.transOpers(name) 459 460 oidStr, parentOid = oid 461 462 outStr = name + ' = NotificationType(' + oidStr + ')' + label 463 464 if objects: 465 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")' 466 for obj in objects] 467 468 numFuncCalls = len(objects) // 255 + 1 469 470 if numFuncCalls > 1: 471 objStrParts = [] 472 473 for idx in range(numFuncCalls): 474 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']') 475 476 outStr += """ 477for _%(name)s_obj in [%(objects)s]: 478 if getattr(mibBuilder, 'version', 0) < (4, 4, 2): 479 # WARNING: leading objects get lost here! Upgrade your pysnmp version! 480 %(name)s = %(name)s.setObjects(*_%(name)s_obj) 481 else: 482 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\ 483""" % dict(name=name, objects=', '.join(objStrParts)) 484 485 else: 486 outStr += '.setObjects(' + ', '.join(objects) + ')' 487 488 outStr += '\n' 489 490 if status: 491 outStr += self.ifTextStr + name + status + '\n' 492 493 if self.genRules['text'] and description: 494 outStr += self.ifTextStr + name + description + '\n' 495 496 if self.genRules['text'] and reference: 497 outStr += self.ifTextStr + name + reference + '\n' 498 499 self.regSym(name, outStr, oidStr) 500 501 return outStr 502 503 # noinspection PyUnusedLocal 504 def genObjectGroup(self, data, classmode=False): 505 name, objects, status, description, reference, oid = data 506 507 label = self.genLabel(name) 508 name = self.transOpers(name) 509 510 oidStr, parentOid = oid 511 512 outStr = name + ' = ObjectGroup(' + oidStr + ')' + label 513 514 if objects: 515 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")' 516 for obj in objects] 517 518 numFuncCalls = len(objects) // 255 + 1 519 520 if numFuncCalls > 1: 521 objStrParts = [] 522 523 for idx in range(numFuncCalls): 524 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']') 525 526 outStr += """ 527for _%(name)s_obj in [%(objects)s]: 528 if getattr(mibBuilder, 'version', 0) < (4, 4, 2): 529 # WARNING: leading objects get lost here! 530 %(name)s = %(name)s.setObjects(*_%(name)s_obj) 531 else: 532 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\ 533""" % dict(name=name, objects=', '.join(objStrParts)) 534 535 else: 536 outStr += '.setObjects(' + ', '.join(objects) + ')' 537 538 outStr += '\n' 539 540 if status: 541 outStr += """\ 542if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0): 543 %(name)s = %(name)s%(status)s 544""" % dict(name=name, status=status) 545 546 if self.genRules['text'] and description: 547 outStr += self.ifTextStr + name + description + '\n' 548 549 if self.genRules['text'] and reference: 550 outStr += self.ifTextStr + name + reference + '\n' 551 552 self.regSym(name, outStr, oidStr) 553 554 return outStr 555 556 # noinspection PyUnusedLocal 557 def genObjectIdentity(self, data, classmode=False): 558 name, status, description, reference, oid = data 559 560 label = self.genLabel(name) 561 name = self.transOpers(name) 562 563 oidStr, parentOid = oid 564 outStr = name + ' = ObjectIdentity(' + oidStr + ')' + label + '\n' 565 566 if status: 567 outStr += self.ifTextStr + name + status + '\n' 568 569 if self.genRules['text'] and description: 570 outStr += self.ifTextStr + name + description + '\n' 571 572 if self.genRules['text'] and reference: 573 outStr += self.ifTextStr + name + reference + '\n' 574 575 self.regSym(name, outStr, oidStr) 576 577 return outStr 578 579 # noinspection PyUnusedLocal 580 def genObjectType(self, data, classmode=False): 581 name, syntax, units, maxaccess, status, description, reference, augmention, index, defval, oid = data 582 583 label = self.genLabel(name) 584 name = self.transOpers(name) 585 586 oidStr, parentOid = oid 587 588 indexStr, fakeStrlist, fakeSyms = index or ('', '', []) 589 subtype = syntax[0] == 'Bits' and 'Bits()' + syntax[1] or syntax[1] # Bits hack #1 590 591 classtype = self.typeClasses.get(syntax[0], syntax[0]) 592 classtype = self.transOpers(classtype) 593 classtype = syntax[0] == 'Bits' and 'MibScalar' or classtype # Bits hack #2 594 classtype = name in self.symbolTable[self.moduleName[0]]['_symtable_cols'] and 'MibTableColumn' or classtype 595 596 defval = self.genDefVal(defval, objname=name) 597 598 outStr = name + ' = ' + classtype + '(' + oidStr + ', ' + subtype + (defval or '') + ')' + label 599 outStr += units or '' 600 outStr += maxaccess or '' 601 outStr += indexStr or '' 602 outStr += '\n' 603 604 if self.genRules['text'] and reference: 605 outStr += self.ifTextStr + name + reference + '\n' 606 607 if augmention: 608 augmention = self.transOpers(augmention) 609 outStr += augmention + '.registerAugmentions(("' + self._importMap.get(name, self.moduleName[0]) + '", "' + name + '"))\n' 610 outStr += name + '.setIndexNames(*' + augmention + '.getIndexNames())\n' 611 612 if status: 613 outStr += self.ifTextStr + name + status + '\n' 614 615 if self.genRules['text'] and description: 616 outStr += self.ifTextStr + name + description + '\n' 617 618 self.regSym(name, outStr, parentOid) 619 620 if fakeSyms: # fake symbols for INDEX to support SMIv1 621 for idx, fakeSym in enumerate(fakeSyms): 622 fakeOutStr = fakeStrlist[idx] % oidStr 623 self.regSym(fakeSym, fakeOutStr, oidStr) 624 625 return outStr 626 627 # noinspection PyUnusedLocal 628 def genTrapType(self, data, classmode=False): 629 name, enterprise, objects, description, reference, value = data 630 631 label = self.genLabel(name) 632 name = self.transOpers(name) 633 634 enterpriseStr, parentOid = enterprise 635 636 outStr = name + ' = NotificationType(' + enterpriseStr + ' + (0,' + str(value) + '))' + label 637 638 if objects: 639 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")' 640 for obj in objects] 641 642 numFuncCalls = len(objects) // 255 + 1 643 644 if numFuncCalls > 1: 645 objStrParts = [] 646 647 for idx in range(numFuncCalls): 648 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']') 649 650 outStr += """ 651for _%(name)s_obj in [%(objects)s]: 652 if getattr(mibBuilder, 'version', 0) < (4, 4, 2): 653 # WARNING: leading objects get lost here! Upgrade your pysnmp version! 654 %(name)s = %(name)s.setObjects(*_%(name)s_obj) 655 else: 656 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\ 657""" % dict(name=name, objects=', '.join(objStrParts)) 658 659 else: 660 outStr += '.setObjects(' + ', '.join(objects) + ')' 661 662 outStr += '\n' 663 664 if self.genRules['text'] and description: 665 outStr += self.ifTextStr + name + description + '\n' 666 667 if self.genRules['text'] and reference: 668 outStr += self.ifTextStr + name + reference + '\n' 669 670 self.regSym(name, outStr, enterpriseStr) 671 672 return outStr 673 674 # noinspection PyUnusedLocal 675 def genTypeDeclaration(self, data, classmode=False): 676 outStr = '' 677 678 name, declaration = data 679 680 if declaration: 681 parentType, attrs = declaration 682 if parentType: # skipping SEQUENCE case 683 name = self.transOpers(name) 684 outStr = 'class ' + name + '(' + parentType + '):\n' + attrs + '\n' 685 self.regSym(name, outStr) 686 687 return outStr 688 689 # noinspection PyUnusedLocal 690 def genValueDeclaration(self, data, classmode=False): 691 name, oid = data 692 693 label = self.genLabel(name) 694 name = self.transOpers(name) 695 696 oidStr, parentOid = oid 697 outStr = name + ' = MibIdentifier(' + oidStr + ')' + label + '\n' 698 699 self.regSym(name, outStr, oidStr) 700 701 return outStr 702 703 # Subparts generation functions 704 705 # noinspection PyMethodMayBeStatic,PyUnusedLocal 706 def ftNames(self, data, classmode=False): 707 names = data[0] 708 return names 709 710 def genBitNames(self, data, classmode=False): 711 names = data[0] 712 return names 713 714 def genBits(self, data, classmode=False): 715 bits = data[0] 716 717 namedval = ['("' + bit[0] + '", ' + str(bit[1]) + ')' for bit in bits] 718 719 numFuncCalls = len(namedval) // 255 + 1 720 721 funcCalls = '' 722 for idx in range(numFuncCalls): 723 funcCalls += 'NamedValues(' + ', '.join(namedval[255 * idx:255 * (idx + 1)]) + ') + ' 724 725 funcCalls = funcCalls[:-3] 726 727 outStr = classmode and self.indent + 'namedValues = ' + funcCalls + '\n' or '.clone(namedValues=' + funcCalls + ')' 728 729 return 'Bits', outStr 730 731 # noinspection PyUnusedLocal 732 def genCompliances(self, data, classmode=False): 733 if not data[0]: 734 return '' 735 736 objects = [] 737 738 for complianceModule in data[0]: 739 name = complianceModule[0] or self.moduleName[0] 740 objects += ['("' + name + '", "' + self.transOpers(compl) + '")' for compl in complianceModule[1]] 741 742 outStr = '' 743 744 numFuncCalls = len(objects) // 255 + 1 745 746 if numFuncCalls > 1: 747 objStrParts = [] 748 749 for idx in range(numFuncCalls): 750 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']') 751 752 outStr += """ 753for _%(name)s_obj in [%(objects)s]: 754 if getattr(mibBuilder, 'version', 0) < (4, 4, 2): 755 # WARNING: leading objects get lost here! Upgrade your pysnmp version! 756 %(name)s = %(name)s.setObjects(*_%(name)s_obj) 757 else: 758 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True)) 759 760""" % dict(name=name, objects=', '.join(objStrParts)) 761 762 else: 763 outStr += '.setObjects(' + ', '.join(objects) + ')\n' 764 765 return outStr 766 767 # noinspection PyUnusedLocal 768 def genConceptualTable(self, data, classmode=False): 769 row = data[0] 770 if row[1] and row[1][-2:] == '()': 771 row = row[1][:-2] 772 self._rows.add(row) 773 774 return 'MibTable', '' 775 776 # noinspection PyMethodMayBeStatic,PyUnusedLocal 777 def genContactInfo(self, data, classmode=False): 778 text = self.textFilter('contact-info', data[0]) 779 return '.setContactInfo(' + dorepr(text) + ')' 780 781 # noinspection PyUnusedLocal 782 def genDisplayHint(self, data, classmode=False): 783 return self.indent + 'displayHint = ' + dorepr(data[0]) + '\n' 784 785 # noinspection PyUnusedLocal 786 def genDefVal(self, data, classmode=False, objname=None): 787 if not data: 788 return '' 789 790 if not objname: 791 return data 792 793 defval = data[0] 794 defvalType = self.getBaseType(objname, self.moduleName[0]) 795 796 if isinstance(defval, (int, long)): # number 797 val = str(defval) 798 799 elif self.isHex(defval): # hex 800 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs 801 val = str(int(defval[1:-2], 16)) 802 else: 803 val = 'hexValue="' + defval[1:-2] + '"' 804 805 elif self.isBinary(defval): # binary 806 binval = defval[1:-2] 807 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs 808 val = str(int(binval or '0', 2)) 809 else: 810 hexval = binval and hex(int(binval, 2))[2:] or '' 811 val = 'hexValue="' + hexval + '"' 812 813 elif defval[0] == defval[-1] and defval[0] == '"': # quoted string 814 if defval[1:-1] == '' and defvalType != 'OctetString': # common bug 815 # a warning should be here 816 return False # we will set no default value 817 818 val = dorepr(defval[1:-1]) 819 820 else: # symbol (oid as defval) or name for enumeration member 821 if (defvalType[0][0] == 'ObjectIdentifier' and 822 (defval in self.symbolTable[self.moduleName[0]] or defval in self._importMap)): # oid 823 module = self._importMap.get(defval, self.moduleName[0]) 824 825 try: 826 val = str(self.genNumericOid(self.symbolTable[module][defval]['oid'])) 827 except: 828 # or no module if it will be borrowed later 829 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (defval, module)) 830 831 # enumeration 832 elif defvalType[0][0] in ('Integer32', 'Integer') and isinstance(defvalType[1], list): 833 if isinstance(defval, list): # buggy MIB: DEFVAL { { ... } } 834 defval = [dv for dv in defval if dv in dict(defvalType[1])] 835 val = defval and dorepr(defval[0]) or '' 836 elif defval in dict(defvalType[1]): # good MIB: DEFVAL { ... } 837 val = dorepr(defval) 838 else: 839 val = '' 840 841 elif defvalType[0][0] == 'Bits': 842 defvalBits = [] 843 bits = dict(defvalType[1]) 844 845 for bit in defval: 846 bitValue = bits.get(bit, None) 847 if bitValue is not None: 848 defvalBits.append((bit, bitValue)) 849 else: 850 raise error.PySmiSemanticError('no such bit as "%s" for symbol "%s"' % (bit, objname)) 851 852 return self.genBits([defvalBits])[1] 853 854 else: 855 raise error.PySmiSemanticError( 856 'unknown type "%s" for defval "%s" of symbol "%s"' % (defvalType, defval, objname)) 857 858 return '.clone(' + val + ')' 859 860 # noinspection PyMethodMayBeStatic,PyUnusedLocal 861 def genDescription(self, data, classmode=False): 862 text = self.textFilter('description', data[0]) 863 return classmode and self.indent + 'description = ' + dorepr(text) + '\n' or '.setDescription(' + dorepr(text) + ')' 864 865 # noinspection PyMethodMayBeStatic 866 def genReference(self, data, classmode=False): 867 text = self.textFilter('reference', data[0]) 868 return classmode and self.indent + 'reference = ' + dorepr(text) + '\n' or '.setReference(' + dorepr(text) + ')' 869 870 # noinspection PyMethodMayBeStatic 871 def genStatus(self, data, classmode=False): 872 text = data[0] 873 return classmode and self.indent + 'status = ' + dorepr(text) + '\n' or '.setStatus(' + dorepr(text) + ')' 874 875 # noinspection PyMethodMayBeStatic 876 def genProductRelease(self, data, classmode=False): 877 text = data[0] 878 return classmode and self.indent + 'productRelease = ' + dorepr(text) + '\n' or '.setProductRelease(' + dorepr(text) + ')' 879 880 def genEnumSpec(self, data, classmode=False): 881 items = data[0] 882 singleval = [str(item[1]) for item in items] 883 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec=' 884 numFuncCalls = len(singleval) / 255 + 1 885 singleCall = numFuncCalls == 1 886 funcCalls = '' 887 888 outStr += not singleCall and 'ConstraintsUnion(' or '' 889 890 for idx in range(int(numFuncCalls)): 891 if funcCalls: 892 funcCalls += ', ' 893 funcCalls += 'SingleValueConstraint(' + ', '.join(singleval[255 * idx:255 * (idx + 1)]) + ')' 894 895 outStr += funcCalls 896 outStr += not singleCall and (classmode and ')\n' or '))') or (not classmode and ')' or '\n') 897 outStr += self.genBits(data, classmode=classmode)[1] 898 899 return outStr 900 901 # noinspection PyUnusedLocal 902 def genTableIndex(self, data, classmode=False): 903 def genFakeSyms(fakeidx, idxType): 904 fakeSymName = 'pysmiFakeCol%s' % fakeidx 905 906 objType = self.typeClasses.get(idxType, idxType) 907 objType = self.transOpers(objType) 908 909 return (fakeSymName + ' = MibTableColumn(%s + (' + str(fakeidx) + 910 ', ), ' + objType + '())\n', # stub for parentOid 911 fakeSymName) 912 913 indexes = data[0] 914 idxStrlist, fakeSyms, fakeStrlist = [], [], [] 915 for idx in indexes: 916 idxName = idx[1] 917 if idxName in self.smiv1IdxTypes: # SMIv1 support 918 idxType = idxName 919 920 fakeSymStr, idxName = genFakeSyms(self.fakeidx, idxType) 921 fakeStrlist.append(fakeSymStr) 922 fakeSyms.append(idxName) 923 self.fakeidx += 1 924 925 idxStrlist.append('(' + str(idx[0]) + ', "' + 926 self._importMap.get(idxName, self.moduleName[0]) + 927 '", "' + idxName + '")') 928 929 return '.setIndexNames(' + ', '.join(idxStrlist) + ')', fakeStrlist, fakeSyms 930 931 def genIntegerSubType(self, data, classmode=False): 932 singleRange = len(data[0]) == 1 933 934 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec=' 935 outStr += not singleRange and 'ConstraintsUnion(' or '' 936 937 for rng in data[0]: 938 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng 939 vmin, vmax = str(self.str2int(vmin)), str(self.str2int(vmax)) 940 outStr += 'ValueRangeConstraint(' + vmin + ', ' + vmax + ')' + (not singleRange and ', ' or '') 941 942 outStr += not singleRange and (classmode and ')' or '))') or (not classmode and ')' or '\n') 943 944 return outStr 945 946 # noinspection PyMethodMayBeStatic,PyUnusedLocal 947 def genMaxAccess(self, data, classmode=False): 948 access = data[0].replace('-', '') 949 return access != 'notaccessible' and '.setMaxAccess("' + access + '")' or '' 950 951 def genOctetStringSubType(self, data, classmode=False): 952 singleRange = len(data[0]) == 1 953 954 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec=' 955 outStr += not singleRange and 'ConstraintsUnion(' or '' 956 957 for rng in data[0]: 958 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng 959 vmin, vmax = str(self.str2int(vmin)), str(self.str2int(vmax)) 960 outStr += ('ValueSizeConstraint(' + vmin + ', ' + vmax + ')' + 961 (not singleRange and ', ' or '')) 962 963 outStr += not singleRange and (classmode and ')' or '))') or (not classmode and ')' or '\n') 964 965 if data[0]: 966 # noinspection PyUnboundLocalVariable 967 outStr += (singleRange 968 and vmin == vmax 969 and (classmode and self.indent + 'fixedLength = ' + vmin + '\n' or '.setFixedLength(' + vmin + ')') or '') 970 971 return outStr 972 973 # noinspection PyUnusedLocal 974 def genOid(self, data, classmode=False): 975 out = () 976 parent = '' 977 for el in data[0]: 978 if isinstance(el, (str, unicode)): 979 parent = self.transOpers(el) 980 out += ((parent, self._importMap.get(parent, self.moduleName[0])),) 981 982 elif isinstance(el, (int, long)): 983 out += (el,) 984 985 elif isinstance(el, tuple): 986 out += (el[1],) # XXX Do we need to create a new object el[0]? 987 988 else: 989 raise error.PySmiSemanticError('unknown datatype for OID: %s' % el) 990 991 return str(self.genNumericOid(out)), parent 992 993 # noinspection PyUnusedLocal 994 def genObjects(self, data, classmode=False): 995 if data[0]: 996 return [self.transOpers(obj) for obj in data[0]] # XXX self.transOpers or not?? 997 return [] 998 999 # noinspection PyMethodMayBeStatic,PyUnusedLocal 1000 def genTime(self, data, classmode=False): 1001 times = [] 1002 for timeStr in data: 1003 if len(timeStr) == 11: 1004 timeStr = '19' + timeStr 1005 # XXX raise in strict mode 1006 # elif lenTimeStr != 13: 1007 # raise error.PySmiSemanticError("Invalid date %s" % t) 1008 try: 1009 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ'))) 1010 1011 except ValueError: 1012 # XXX raise in strict mode 1013 # raise error.PySmiSemanticError("Invalid date %s: %s" % (t, sys.exc_info()[1])) 1014 timeStr = '197001010000Z' # dummy date for dates with typos 1015 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ'))) 1016 1017 return times 1018 1019 # noinspection PyMethodMayBeStatic,PyUnusedLocal 1020 def genLastUpdated(self, data, classmode=False): 1021 text = data[0] 1022 return '.setLastUpdated(' + dorepr(text) + ')' 1023 1024 # noinspection PyMethodMayBeStatic,PyUnusedLocal 1025 def genOrganization(self, data, classmode=False): 1026 text = self.textFilter('organization', data[0]) 1027 return '.setOrganization(' + dorepr(text) + ')' 1028 1029 # noinspection PyUnusedLocal 1030 def genRevisions(self, data, classmode=False): 1031 times = self.genTime([x[0] for x in data[0]]) 1032 times = [dorepr(x) for x in times] 1033 1034 revisions = '.setRevisions((%s,))' % ', '.join(times) 1035 1036 descriptions = '.setRevisionsDescriptions((%s,))' % ', '.join( 1037 [dorepr(self.textFilter('description', x[1][1])) for x in data[0]] 1038 ) 1039 1040 lastRevision = data[0][0][0] 1041 1042 return lastRevision, revisions, descriptions 1043 1044 def genRow(self, data, classmode=False): 1045 row = data[0] 1046 row = self.transOpers(row) 1047 return row in self.symbolTable[self.moduleName[0]]['_symtable_rows'] and ( 1048 'MibTableRow', '') or self.genSimpleSyntax(data, classmode=classmode) 1049 1050 # noinspection PyUnusedLocal 1051 def genSequence(self, data, classmode=False): 1052 cols = data[0] 1053 self._cols.update(cols) 1054 return '', '' 1055 1056 def genSimpleSyntax(self, data, classmode=False): 1057 objType = data[0] 1058 objType = self.typeClasses.get(objType, objType) 1059 objType = self.transOpers(objType) 1060 1061 subtype = len(data) == 2 and data[1] or '' 1062 1063 if classmode: 1064 subtype = '%s' in subtype and subtype % objType or subtype # XXX hack? 1065 return objType, subtype 1066 1067 outStr = objType + '()' + subtype 1068 1069 return 'MibScalar', outStr 1070 1071 # noinspection PyUnusedLocal 1072 def genTypeDeclarationRHS(self, data, classmode=False): 1073 if len(data) == 1: 1074 parentType, attrs = data[0] # just syntax 1075 1076 else: 1077 # Textual convention 1078 display, status, description, reference, syntax = data 1079 parentType, attrs = syntax 1080 1081 if parentType in self._snmpTypes: 1082 parentType = 'TextualConvention, ' + parentType 1083 1084 if display: 1085 attrs = display + attrs 1086 1087 if status: 1088 attrs = status + attrs 1089 1090 if self.genRules['text'] and description: 1091 attrs = description + attrs 1092 1093 if reference: 1094 attrs = reference + attrs 1095 1096 attrs = attrs or self.indent + 'pass\n' 1097 1098 return parentType, attrs 1099 1100 # noinspection PyMethodMayBeStatic,PyUnusedLocal 1101 def genUnits(self, data, classmode=False): 1102 text = data[0] 1103 return '.setUnits(' + dorepr(self.textFilter('units', text)) + ')' 1104 1105 handlersTable = { 1106 'agentCapabilitiesClause': genAgentCapabilities, 1107 'moduleIdentityClause': genModuleIdentity, 1108 'moduleComplianceClause': genModuleCompliance, 1109 'notificationGroupClause': genNotificationGroup, 1110 'notificationTypeClause': genNotificationType, 1111 'objectGroupClause': genObjectGroup, 1112 'objectIdentityClause': genObjectIdentity, 1113 'objectTypeClause': genObjectType, 1114 'trapTypeClause': genTrapType, 1115 'typeDeclaration': genTypeDeclaration, 1116 'valueDeclaration': genValueDeclaration, 1117 1118 'ApplicationSyntax': genSimpleSyntax, 1119 'BitNames': genBitNames, 1120 'BITS': genBits, 1121 'ComplianceModules': genCompliances, 1122 'conceptualTable': genConceptualTable, 1123 'CONTACT-INFO': genContactInfo, 1124 'DISPLAY-HINT': genDisplayHint, 1125 'DEFVAL': genDefVal, 1126 'DESCRIPTION': genDescription, 1127 'REFERENCE': genReference, 1128 'Status': genStatus, 1129 'PRODUCT-RELEASE': genProductRelease, 1130 'enumSpec': genEnumSpec, 1131 'INDEX': genTableIndex, 1132 'integerSubType': genIntegerSubType, 1133 'MaxAccessPart': genMaxAccess, 1134 'Notifications': genObjects, 1135 'octetStringSubType': genOctetStringSubType, 1136 'objectIdentifier': genOid, 1137 'Objects': genObjects, 1138 'LAST-UPDATED': genLastUpdated, 1139 'ORGANIZATION': genOrganization, 1140 'Revisions': genRevisions, 1141 'row': genRow, 1142 'SEQUENCE': genSequence, 1143 'SimpleSyntax': genSimpleSyntax, 1144 'typeDeclarationRHS': genTypeDeclarationRHS, 1145 'UNITS': genUnits, 1146 'VarTypes': genObjects, 1147 # 'a': lambda x: genXXX(x, 'CONSTRAINT') 1148 } 1149 1150 def genCode(self, ast, symbolTable, **kwargs): 1151 self.genRules['text'] = kwargs.get('genTexts', False) 1152 self.textFilter = kwargs.get('textFilter') or (lambda symbol, text: re.sub('\s+', ' ', text)) 1153 self.symbolTable = symbolTable 1154 self._rows.clear() 1155 self._cols.clear() 1156 self._exports.clear() 1157 self._seenSyms.clear() 1158 self._importMap.clear() 1159 self._out.clear() 1160 self._moduleIdentityOid = None 1161 self.moduleName[0], moduleOid, imports, declarations = ast 1162 1163 out, importedModules = self.genImports(imports or {}) 1164 1165 for declr in declarations or []: 1166 if declr: 1167 clausetype = declr[0] 1168 classmode = clausetype == 'typeDeclaration' 1169 self.handlersTable[declr[0]](self, self.prepData(declr[1:], classmode), classmode) 1170 1171 for sym in self.symbolTable[self.moduleName[0]]['_symtable_order']: 1172 if sym not in self._out: 1173 raise error.PySmiCodegenError('No generated code for symbol %s' % sym) 1174 out += self._out[sym] 1175 1176 out += self.genExports() 1177 1178 if 'comments' in kwargs: 1179 out = ''.join(['# %s\n' % x for x in kwargs['comments']]) + '#\n' + out 1180 out = '#\n# PySNMP MIB module %s (http://snmplabs.com/pysmi)\n' % self.moduleName[0] + out 1181 1182 debug.logger & debug.flagCodegen and debug.logger( 1183 'canonical MIB name %s (%s), imported MIB(s) %s, Python code size %s bytes' % ( 1184 self.moduleName[0], moduleOid, ','.join(importedModules) or '<none>', len(out))) 1185 1186 return MibInfo(oid=moduleOid, 1187 identity=self._moduleIdentityOid, 1188 name=self.moduleName[0], 1189 revision=self._moduleRevision, 1190 oids=[], 1191 enterprise=None, 1192 compliance=[], 1193 imported=tuple([x for x in importedModules if x not in self.fakeMibs])), out 1194 1195 def genIndex(self, processed, **kwargs): 1196 out = '\nfrom pysnmp.proto.rfc1902 import ObjectName\n\noidToMibMap = {\n' 1197 count = 0 1198 for module, status in processed.items(): 1199 value = getattr(status, 'oid', None) 1200 if value: 1201 out += 'ObjectName("%s"): "%s",\n' % (value, module) 1202 count += 1 1203 out += '}\n' 1204 1205 if 'comments' in kwargs: 1206 out = ''.join(['# %s\n' % x for x in kwargs['comments']]) + '#\n' + out 1207 out = '#\n# PySNMP MIB indices (http://snmplabs.com/pysmi)\n' + out 1208 1209 debug.logger & debug.flagCodegen and debug.logger( 1210 'OID->MIB index built, %s entries, %s bytes' % (count, len(out))) 1211 1212 return out 1213 1214# backward compatibility 1215baseMibs = PySnmpCodeGen.baseMibs 1216fakeMibs = PySnmpCodeGen.fakeMibs 1217