PKLuF2pybindxml/__init__.py PK½FU"U"pybindxml/reader.py"""Library to parse the various versions of BIND statstics XML.""" from bs4 import BeautifulSoup try: from urllib.request import urlopen from urllib.error import URLError except ImportError: from urllib2 import urlopen from urllib2 import URLError class XmlError(Exception): """Base class of raising XML errors when reading BIND XML.""" def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class BindXmlReader(object): """Superclass for reading/processing BIND statistics XML.""" def __init__(self, host=None, port=8053, xml_filepath=None): self.bs_xml = None self.host = host self.port = port self.raw_xml = None self.stats = None self.xml_filepath = xml_filepath self.xml_version = None def gather_xml(self): """Attempt to read the XML, whether from a file on-disk or via host:port. TODO: handle when you cant gather stats, due to bad hostname """ if self.xml_filepath: with open(self.xml_filepath, "r") as xml_fh: self.raw_xml = xml_fh.read() self.bs_xml = BeautifulSoup(self.raw_xml) else: try: req = urlopen('http://%s:%s' % (self.host, self.port)) self.raw_xml = req.read() self.bs_xml = BeautifulSoup(self.raw_xml, 'xml') except URLError as u_error: raise XmlError('Unable to query BIND (%s:%s) for statistics. Reason: %s.' % (self.host, self.port, u_error)) def get_stats(self): """Given XML version, parse create XMLAbstract object and sets xml_stats attribute.""" self.gather_xml() self.xml_version = self.bs_xml.find('statistics')['version'] if self.xml_version is None: raise XmlError("Unable to determine XML version via 'statistics' tag.") if self.xml_version == '2.2': self.stats = XmlV22(self.bs_xml) elif self.xml_version == '3.0': self.stats = XmlV30(self.bs_xml) elif self.xml_version == '3.3': self.stats = XmlV33(self.bs_xml) elif self.xml_version == '3.5': self.stats = XmlV35(self.bs_xml) else: raise XmlError('Support must be added before being able to support newly-encountered XML version %s.' % self.xml_version) class XmlAbstract(object): """Abstract class for the various XML versions to be parsed.""" def __init__(self, xml): self.bs_xml = xml self.memory_stats = self.set_memory_stats() self.query_stats = self.set_query_stats() self.zone_stats = self.set_zone_stats() def set_memory_stats(self): """Return dict of memory counter and value for BIND process. Returns: Dict { memory_counter: Int value in bytes } """ raise NotImplementedError('You must implement your own set_memory_stats method.') def set_query_stats(self): """Return a dict of query type and count from BIND statistics XML. Returns: Dict { query_type: Integer count } """ raise NotImplementedError('You must implement your own set_query_stats method.') def set_zone_stats(self): """List the DNS zones and attributes. Returns: List of Dicts { String view_name, String zone_name, String zone_class, Int serial, Dict counters (which contains key/values for all counter values} """ raise NotImplementedError('You must implement your own set_zone_stats method.') class XmlV22(XmlAbstract): """Class for implementing methods for parsing BIND version 2.2 XML.""" def __init__(self, xml): super(XmlV22, self).__init__(xml) def set_memory_stats(self): stats_dict = {} stats = self.bs_xml.find('bind').find('statistics').find('memory').find('summary') for stat in stats.contents: if stat == '\n': continue if stat: stats_dict[stat.name] = int(stat.string) return stats_dict def set_query_stats(self): stats_dict = {} stats = self.bs_xml.find('server') for stat in stats.find_all('opcode'): if stat == '\n': continue if stat: stats_dict[stat.find('name').string] = int(stat.find('counter').string) for stat in stats.find('queries-in').find_all('rdtype'): if stat == '\n': continue if stat: stats_dict[stat.find('name').string] = int(stat.find('counter').string) return stats_dict def set_zone_stats(self): zone_dict = {} views = self.bs_xml.find('views').find_all('view') for view in views: view_name = view.find('name').string for zone in view.findAll('zone'): zone_name = zone.find('name').string.split('/',1)[0] zone_class = zone.find('rdataclass').string if zone_class != 'IN': continue serial = zone.find('serial').string zone_dict[zone_name] = {} zone_dict[zone_name][view_name] = {} zone_dict[zone_name][view_name].update({ 'serial': serial, }) counters = zone.find('counters') if counters: for counter in counters.children: if counter == '\n': continue zone_dict[zone_name][view_name][counter.name] = {} zone_dict[zone_name][view_name][counter.name].update({ 'type': None, 'value': int(counter.string)}) return zone_dict class XmlV30(XmlAbstract): """Class for implementing methods for parsing BIND version 3.0 XML.""" def __init__(self, xml): super(XmlV30, self).__init__(xml) def set_memory_stats(self): stats_dict = {} stats = self.bs_xml.find('memory').find('summary') for stat in stats.contents: if stat == '\n': continue if stat: stats_dict[stat.name] = int(stat.string) return stats_dict def set_query_stats(self): stats_dict = {} stats = self.bs_xml.find('server') for stat in stats.find(type='qtype'): if stat == '\n': continue stats_dict[stat['name']] = int(stat.string) for stat in stats.find(type='opcode'): if stat == '\n': continue stats_dict[stat['name']] = int(stat.string) return stats_dict def set_zone_stats(self): zone_dict = {} views = self.bs_xml.find_all('view') for view in views: for zone in view.find_all('zone'): if zone['rdataclass'] != 'IN': continue zone_dict[zone['name']] = {} zone_dict[zone['name']][view['name']] = {} zone_dict[zone['name']][view['name']].update({ 'serial': zone.find('serial').string }) counters = zone.find_all('counters') if counters: for counter_type in counters: # rcode, qtype, etc. for counter in counter_type.find_all('counter'): # UpdateDone, PTR, etc. zone_dict[zone['name']][view['name']][counter['name']] = {} zone_dict[zone['name']][view['name']][counter['name']].update({ 'type': counter_type['type'], 'value': int(counter.string)}) return zone_dict class XmlV33(XmlV30): """Class for implementing methods for parsing BIND version 3.3 XML.""" def __init__(self, xml): super(XmlV33, self).__init__(xml) class XmlV35(XmlV30): """Class for implementing methods for parsing BIND version 3.5 XML.""" def __init__(self, xml): super(XmlV35, self).__init__(xml) def set_query_stats(self): stats_dict = {} counter_stats = self.bs_xml.find('server').find_all('counters') for counter_group in counter_stats: # counter_type currently unused counter_type = counter_group['type'] for counter in counter_group.find_all('counter'): stats_dict[counter['name']] = int(counter.string) return stats_dict PK MH^- 'pybindxml-0.5.dist-info/DESCRIPTION.rstUNKNOWN PK MHb%pybindxml-0.5.dist-info/metadata.json{"extensions": {"python.details": {"contacts": [{"email": "code@jeffreyforman.net", "name": "Jeffrey Forman", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http:://github.com/jforman/pybindxml"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "license": "MIT", "metadata_version": "2.0", "name": "pybindxml", "run_requires": [{"requires": ["beautifulsoup4"]}], "summary": "Read ISC BIND's statistics XML for processing.", "version": "0.5"}PK MHv\ %pybindxml-0.5.dist-info/top_level.txtpybindxml PK MH''\\pybindxml-0.5.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PK MH0 pybindxml-0.5.dist-info/METADATAMetadata-Version: 2.0 Name: pybindxml Version: 0.5 Summary: Read ISC BIND's statistics XML for processing. Home-page: http:://github.com/jforman/pybindxml Author: Jeffrey Forman Author-email: code@jeffreyforman.net License: MIT Platform: UNKNOWN Requires-Dist: beautifulsoup4 UNKNOWN PK MHFpybindxml-0.5.dist-info/RECORDpybindxml/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 pybindxml/reader.py,sha256=fKiHi5AM0ftsC6A_I8DNl0tT5pnSV18nFeYfmOFxPE0,8789 pybindxml-0.5.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 pybindxml-0.5.dist-info/METADATA,sha256=zxm7adEoZEa2beWw3AmneZci6-b_O-fNNnSX1X9qpDE,287 pybindxml-0.5.dist-info/RECORD,, pybindxml-0.5.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 pybindxml-0.5.dist-info/metadata.json,sha256=rcthg7ok5dZaH3C4o5lA7JEC60TQpYq0hI_H6KAERko,499 pybindxml-0.5.dist-info/top_level.txt,sha256=WcmtH4srPLZdyYy2Xpydbv8p9UgfiOYnrQSihhQ3JBk,10 PKLuF2pybindxml/__init__.pyPK½FU"U"4pybindxml/reader.pyPK MH^- '"pybindxml-0.5.dist-info/DESCRIPTION.rstPK MHb% #pybindxml-0.5.dist-info/metadata.jsonPK MHv\ %?%pybindxml-0.5.dist-info/top_level.txtPK MH''\\%pybindxml-0.5.dist-info/WHEELPK MH0 #&pybindxml-0.5.dist-info/METADATAPK MHF'pybindxml-0.5.dist-info/RECORDPKd?*