{ "info": { "author": "Ashley Roeckelein", "author_email": "ashley.roeckelein@neustar.biz", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Customer Service", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "Intended Audience :: Telecommunications Industry", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Topic :: Internet :: Name Service (DNS)", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", "Topic :: System :: Networking :: Monitoring", "Topic :: System :: Systems Administration", "Topic :: Utilities" ], "description": "**dns_sprockets**\n=================\n\nOverview\n--------\n\n**dns_sprockets** is a command-line framework for loading and validating DNS zones.\nIt is written in Python and uses the excellent `dnspython `_\nlibrary for much of its functionality.\n\n\"Why call it dns_sprockets?\" Well, if you think of a DNS zone as being a chain \nof resource records (thinking especially of NSEC3 records), this application is a\nset of sprockets that tests every link of the chain. *Hey, it kind of works ;)*\n\nAudience\n''''''''\n\nPossible users include DNS code developers and quality assurance, internet \ncustomer service, system administrators, and end-users who are interested to \nknow if their DNS zones are valid.\n\nFeatures\n''''''''\n\nThe command-line tool returns useful return codes, supporting automated build\nsystems. It is built around the concept of plug-ins for implementing both the\nzone loading and zone validating functions, and easily allows end-users the\nability to define new loaders and validators.\n\n* Zones may be loaded using various means. The framework supports \"loader\" \n plug-ins that pull DNS zone data from any source. Initially provided are\n 'File' and 'Xfr' plugins to pull zone data from host files and XFR servers,\n respectively.\n\n* Validations are modular pieces of code that may be selectively enabled. The\n framework supports \"validator\" plug-ins that operate in one of four views:\n \n - Zone: Some aspect of the entire zone is validated.\n - Node: Every node (i.e. list of RRSets with the same owner name) can be validated.\n - RRSet: Every RRSet can be validated.\n - Record: Every DNS record can be validated.\n \n Initially provided in this package are some basic zone validators, and a\n fairly complete set of DNSSEC-type zone validators.\n\nThe Node, RRSet, and Record views may be optionally filtered by one or more \nresource record types, to simplify and focus the validation code. Additionally,\neach validator can be marked to run for non-DNSSEC zones, NSEC1-style DNSSEC\nzones, or NSEC3-style DNSSEC zones. \n\nInstallation\n------------\n\nEasy! Use pip (preferably in a virtual python environment)::\n\n $ pip install dns_sprockets\n\nUsage\n-----\n\nOnce installed, simply issue the dns_sprockets command. For example to get a\nusage message::\n\n $ dns_sprockets --help\n\n # dns_sprockets (1.1.8) - A DNS Zone validation tool\n usage: dns_sprockets.py [-h] [-z s] [-l s] [-s s] [-i s] [-x s] [-d s] [-f s]\n [-e] [-v]\n\n optional arguments:\n -h, --help show this help message and exit\n -z s, --zone s Name of zone to validate [www.ultradns.com]\n -l s, --loader s Zone loader method to use (one of: file, xfr) [xfr]\n -s s, --source s Loader source to use [127.0.0.1#53]\n -i s, --include s Only include this validator (can use multiple times)\n -x s, --exclude s Exclude this validator (can use multiple times)\n -d s, --define s Define other params (can use multiple times)\n -f s, --force-dnssec-type s\n Use DNSSEC type (one of: detect, unsigned, NSEC,\n NSEC3) [detect]\n -e, --errors-only Show validation errors only [False]\n -v, --verbose Show detailed processing info [False]\n\n Use @filename to read some/all arguments from a file.\n\n Use -d's to define optional, module-specific parameters if desired (e.g. to tell\n 'xfr' loader to use a specific source address, use \"-d xfr_source=1.2.3.4\").\n The optional parameters are listed under each loader and validator description\n in DEFINE lines, if available. The \"short form\" of parameter names (i.e.\n without the module name prefix) may be used instead, if multiple modules define\n the same \"short form\" name (e.g. -d now=20160328120000 can be used, and will set\n both 'rrsig_missing' and 'rrsig_orphan' validators' rrsig_missing_now and\n rrsig_orphan_now values, respectively).\n\n By default, all tests are run. Use -i's to explicitly specify desired tests,\n or -x's to eliminate undesired tests.\n\n The list of available loaders is:\n ---------------------------------------------------------------------------\n LOADER: file - Loads a zone from a file in AXFR-type or Bind host-type format.\n DEFINE: (file_)allow_include - Allow file to include other files (default=1)\n DEFINE: (file_)rdclass - Class of records to pull (default=IN)\n LOADER: xfr - Loads a zone by XFR from a name server.\n DEFINE: (xfr_)af - The address family to use, AF_INET or AF_INET6 (default=None)\n DEFINE: (xfr_)keyalgorithm - The TSIG algorithm to use, one of: HMAC-MD5.SIG-ALG.REG.INT. hmac-sha1. hmac-sha224. hmac-sha256. hmac-sha384. hmac-sha512. (default=HMAC-MD5.SIG-ALG.REG.INT.)\n DEFINE: (xfr_)keyname - The name of the TSIG to use (default=None)\n DEFINE: (xfr_)keyring - The TSIG keyring to use, a text dict of name->base64_secret e.g. \"{'n1':'H477A900','n2':'K845CL21'}\" (default=None)\n DEFINE: (xfr_)lifetime - Total seconds to wait for complete transfer (default=None)\n DEFINE: (xfr_)rdclass - Class of records to pull (default=IN)\n DEFINE: (xfr_)rdtype - Type of XFR to perform, AXFR or IXFR (default=AXFR)\n DEFINE: (xfr_)serial - SOA serial number to use as base for IXFR diff (default=0)\n DEFINE: (xfr_)source - Source address for the transfer (default=None)\n DEFINE: (xfr_)source_port - Source port for the transfer (default=0)\n DEFINE: (xfr_)timeout - Seconds to wait for each response message (default=5.0)\n DEFINE: (xfr_)use_udp - Use UDP for IXFRing (default=0)\n\n The list of available validators is:\n ---------------------------------------------------------------------------\n TEST: dnskey_bits (REC_TEST[DNSKEY]) - Checks DNSKEY flags and protocol.\n TEST: dnskey_origin (ZONE_TEST) - Checks for a ZSK at zone origin.\n TEST: dnssectype_ambiguous (ZONE_TEST) - Checks for existence of both NSEC and NSEC3 in the zone.\n TEST: ns_origin (ZONE_TEST) - Checks for at least one NS at zone origin.\n TEST: nsec3_chain (ZONE_TEST) - Checks for valid NSEC3 chain.\n TEST: nsec3_missing (RRSET_TEST) - Checks that all (non-NSEC3/RRSIG, non-delegated) RRSets are covered with an NSEC3.\n TEST: nsec3_orphan (REC_TEST[NSEC3]) - Checks for orphan or invalid-covers NSEC3s.\n TEST: nsec3param_origin (ZONE_TEST) - Checks for an NSEC3PARAM at zone origin for nsec3-type zones.\n TEST: nsec_chain (ZONE_TEST) - Checks for valid NSEC chain.\n TEST: nsec_missing (RRSET_TEST) - Checks that all (non-NSEC/RRSIG, non-delegated) RRSets are covered with an NSEC.\n TEST: nsec_orphan (REC_TEST[NSEC]) - Checks for orphan or invalid-covers NSECs.\n TEST: nsecx_ttls_match (REC_TEST[NSEC,NSEC3]) - Checks that NSECx TTL's match SOA's minimum.\n TEST: rrsig_covers (REC_TEST[RRSIG]) - Checks RRSIG's don't cover RRSIG's.\n TEST: rrsig_missing (RRSET_TEST) - Checks that all (non-RRSIG, non-delegated) RRSets are covered with an RRSIG.\n DEFINE: (rrsig_missing_)now - Time to use for validating RRSIG time windows, e.g. 20150101123000 (default=None)\n DEFINE: (rrsig_missing_)now_offset - Number of seconds to offset the \"now\" value, e.g. -86400) (default=None)\n TEST: rrsig_orphan (REC_TEST[RRSIG]) - Checks for orphan RRSIGs.\n DEFINE: (rrsig_orphan_)now - Time to use for validating RRSIG time windows, e.g. 20150101123000 (default=None)\n DEFINE: (rrsig_orphan_)now_offset - Number of seconds to offset the \"now\" value, e.g. -86400) (default=None)\n TEST: rrsig_signer_match (REC_TEST[RRSIG]) - Checks RRSIG signers match the zone.\n TEST: rrsig_time (REC_TEST[RRSIG]) - Checks RRSIG's inception <= expiration.\n TEST: rrsig_ttls_match (REC_TEST[RRSIG]) - Checks RRSIG TTL's match original and covered TTL's.\n TEST: soa_origin (ZONE_TEST) - Checks for an SOA at zone origin.\n TEST: soa_unique (ZONE_TEST) - Checks for a single SOA in the zone.\n\nSample Usage\n''''''''''''\n\nLet's say you want to validate and only see errors an NSEC3-style DNSSEC zone\ncalled \"example\", from a file, and wish to run all available/applicable validations.\nSince this will check RRSIG signatures, you'll need to add a few defines to properly\nstate the \"now\" time to use for two of the validators. Assuming a bash-like shell::\n\n $ ZONE_FILE=$VIRTUAL_ENV/lib/python2.7/site-packages/dns_sprockets_lib/tests/data/rfc5155_example.\n \n $ TIME_NOW=20100101000000\n \n $ dns_sprockets -z example -l file -s $ZONE_FILE -e \\\n -d rrsig_missing_now=$TIME_NOW -d rrsig_orphan_now=$TIME_NOW\n \n # dns_sprockets (1.0.0) - A DNS Zone validation tool\n # Checking zone: example.\n # Loader: file from: rfc5155_example. elapsed=0.018354 secs\n # Zone appears to be DNSSEC type: NSEC3\n # Extra defines: ['rrsig_missing_now=20100101000000', 'rrsig_orphan_now=20100101000000']\n # Skipping test: nsec_chain (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Skipping test: nsec_missing (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Skipping test: nsec_orphan (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Running tests: ['dnskey_origin', 'dnssectype_ambiguous', 'ns_origin', 'nsec3_chain', 'nsec3param_origin', 'soa_origin', 'soa_unique', 'nsec3_missing', 'rrsig_missing', 'dnskey_bits', 'nsec3_orphan', 'nsecx_ttls_match', 'rrsig_covers', 'rrsig_orphan', 'rrsig_signer_match', 'rrsig_time', 'rrsig_ttls_match']\n # END RESULT: 0 ERRORS in 229 tests\n # TOTAL ELAPSED TIME: 0.063526 SECS LOAD TIME: 0.018354 SECS TEST TIME: 0.045172 SECS\n \n $ echo $?\n 0\n \n*UPDATE* New in version 1.1.8: Can now just specify \"-d now=$TIME_NOW\" as a\nshortcut for \"-d rrsig_missing_now=$TIME_NOW -d rrsig_orphan_now=$TIME_NOW\"\n\nOK, all tests passed, but that's not too interesting. Let's repeat that, except\nwith a slightly modified zone file: one of the NSEC3's (and its associated RRSIG\nrecord) has been removed::\n\n $ ZONE_FILE=$VIRTUAL_ENV/lib/python2.7/site-packages/dns_sprockets_lib/tests/data/rfc5155_example._nsec3_missing\n \n $ dns_sprockets -z example -l file -s $ZONE_FILE -e \\\n -d rrsig_missing_now=$TIME_NOW -d rrsig_orphan_now=$TIME_NOW\n \n # dns_sprockets (1.0.0) - A DNS Zone validation tool\n # Checking zone: example.\n # Loader: file from: dns_sprockets_lib/tests/data/rfc5155_example._nsec3_missing elapsed=0.023993 secs\n # Zone appears to be DNSSEC type: NSEC3\n # Extra defines: ['rrsig_missing_now=20100101000000', 'rrsig_orphan_now=20100101000000']\n # Skipping test: nsec_chain (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Skipping test: nsec_missing (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Skipping test: nsec_orphan (DNSSEC type for zone: NSEC3, for test: NSEC)\n # Running tests: ['dnskey_origin', 'dnssectype_ambiguous', 'ns_origin', 'nsec3_chain', 'nsec3param_origin', 'soa_origin', 'soa_unique', 'nsec3_missing', 'rrsig_missing', 'dnskey_bits', 'nsec3_orphan', 'nsecx_ttls_match', 'rrsig_covers', 'rrsig_orphan', 'rrsig_signer_match', 'rrsig_time', 'rrsig_ttls_match']\n TEST nsec3_chain(ZONE(example. IN)) => FAIL: Chain broken at R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN (next=T644EBQK9BIBCNA874GIVR6JOJ62MLHV doesn't exist)\n TEST nsec3_missing(RRSET(xx.example. IN A)) => FAIL: No NSEC3's found for name: t644ebqk9bibcna874givr6joj62mlhv.example.\n TEST nsec3_missing(RRSET(xx.example. IN HINFO)) => FAIL: No NSEC3's found for name: t644ebqk9bibcna874givr6joj62mlhv.example.\n TEST nsec3_missing(RRSET(xx.example. IN AAAA)) => FAIL: No NSEC3's found for name: t644ebqk9bibcna874givr6joj62mlhv.example.\n # END RESULT: 4 ERRORS in 221 tests\n # TOTAL ELAPSED TIME: 0.064603 SECS LOAD TIME: 0.023993 SECS TEST TIME: 0.040610 SECS \n \n $ echo $?\n 4\n\nThis time, we get errors from two validators. The nsec3_chain validator issues a\n\"chain broken\" error, and the nsec3_missing validator sees three RRSet's with the\nsame owner name that are \"not covered\" by the missing NSEC3.\n\nIncidentally, these two data files (and others) are included in the package for\nunit testing purposes, but can be useful to play with to see how dns_sprockets\nreports various problems.\n\nReturn Codes\n''''''''''''\n\nThe application returns a numerical value back to the user:\n\n- **0** If there were no failed validations.\n- **1-254** The number of failed validations, up to a limit of 254.\n- **255** A special code for fatal exceptions.\n\nTO-DO's\n'''''''\n\nThe following is a non-exhaustive list of things to do (help anyone?):\n\n- Respect the \"opt-out\" flag in NSEC3 records; right now, assuming none are opt-out.\n- More loaders and (especially) validators!\n- More real-world trials.\n\nDeveloper Information\n---------------------\n\nThis *long* section discusses dns_sprockets for those who may be interested in \nadding more loaders or validators. If that's you, great! Please consider \ncontributing your work to the project, it is most welcome! Especially welcome\nare unit tests that accompany any new code! (currently using Nose for testing).\n\nFramework Architecture\n''''''''''''''''''''''\n\nInspiration for this application comes from a similar tool written in Perl called\n`donuts `_. It too uses the concept of plugins for\nits validators.\n\nThis framework essentially revolves around the two types of plugins: Loaders and\nvalidator plugins, which are stored in two project subfolders \n(dns_sprockets_lib/loaders and dns_sprockets_lib/validators, respectively). At\nruntime, the app scans both folders and makes their contents available for use::\n\n A note on the naming conventions: plugins are stored in files with\n underscore-style names (e.g. nsec3_chain.py) and are expected to\n contain a class that implements the plugin, with a camelcase-style\n name that corresponds to the file name (e.g. Nsec3Chain).\n\nThe main logic of the app resides in the DNSSprocketsImpl.run() method (in \ndns_sprockets_lib/dns_sprockets_impl.py). Pseudo-code is:\n\n- Scan zone loaders and load them into memory as Python classes.\n- Create an instance of the specified zone loader.\n- Scan validators and load them into memory as Python classes.\n- Instantiate specified validators and categorize by validator type.\n- Run the specified zone loader instance to obtain a dns.zone.Zone object.\n- Construct a \"Context\" instance, initialized by the dns.zone.Zone object.\n- Filter-out any validator instances that do not make sense for the DNSSEC type of the zone.\n- Run the zone-type validators against the Context.\n- Iterate Nodes in the zone object:\n - Run the node-type validators against the Context and Node.\n - Iterate RRSets in the Node:\n - Run the RRSet-type validators against the Context and RRSet.\n - Iterate Records in the RRSet:\n - Run the record-type validators against the Context and Record.\n\nThe use of the `dnspython `_ library pervades the \napplication (so if you're familiar with it already, you've got an excellent start):\nThe loaders read from some source and return a dnspython dns.zone.Zone object to\nthe framework. Similarly, the framework presents to the validators the same \ndns.zone.Zone object for examination.\n\nZone Loaders\n''''''''''''\n\nZone loaders are classes derived from dns_sprockets_lib.loaders.ZoneLoader \n(in the dns_sprockets_lib/loaders/__init__.py file), which defines the interface\nexpected by the framework::\n\n class ZoneLoader(object):\n '''\n [Base class for zone loaders]\n '''\n LOADER_NAME = None # Automatically set in __init__.\n LOADER_OPTARGS = {} # Override possible! e.g.: {'now': (None, 'Time to use for now')}\n \n def __init__(self, args):\n '''\n Ctor, caches the arguments used to run the application, and grabs any\n optional test arguments.\n '''\n self.LOADER_NAME = utils.camelcase_to_underscores(self.__class__.__name__)\n self.args = args\n \n utils.process_optargs(self.LOADER_OPTARGS, self.LOADER_NAME, self)\n \n def run(self):\n '''\n Runs the zone loader -- must override!\n \n :return: A dns.zone.Zone instance.\n '''\n pass\n\nTwo class variables are expected:\n\n- **LOADER_NAME** Contains the underscore-style name of the loader, and is \n automatically set up in the __init__() method.\n- **LOADER_OPTARGS** Contains any plugin-specific parameters that may be set from\n the command-line *...more on this later*.\n\nTwo methods are expected:\n\n- **__init__()** Takes the arguments object containing the command-line\n options passed by the user to the application.\n- **run()** Invokes the zone loader functionality and returns a dns.zone.Zone \n object.\n\nAs an example, the code for the File loader is show here. It is almost trivial\nbecause it takes advantage of the built-in host file loading available in the\ndnspython library::\n\n class File(loaders.ZoneLoader):\n '''\n Loads a zone from a file in AXFR-type or Bind host-type format.\n '''\n LOADER_OPTARGS = {\n 'rdclass': ('IN', 'Class of records to pull'),\n 'allow_include': ('1', 'Allow file to include other files')}\n \n def __init__(self, args):\n '''\n Ctor.\n '''\n self.rdclass = None\n self.allow_include = None\n super(File, self).__init__(args)\n \n def run(self):\n '''\n :return: A dns.zone.Zone instance.\n '''\n other_args = {\n 'origin': self.args.zone,\n 'relativize': False,\n 'filename': self.args.source,\n 'check_origin': False,\n 'rdclass': dns.rdataclass.from_text(self.rdclass),\n 'allow_include': bool(int(self.allow_include))}\n \n return dns.zone.from_file(self.args.source, **other_args)\n\nPlease note the __init__() method calls back into the base class to include its\nuseful and necessary functionality! Also be aware that the class docstring is \nused for the description of the loader, as shown in the --help output (keep it\nbrief!)\n\nValidation Context\n''''''''''''''''''\n\nOnce the framework obtains a dns.zone.Zone instance from the specified zone\nloader, it constructs a Context instance from it, which is passed to the \nvalidators. In addition to the application's command-line arguments (as\n**context.args**) and the actual dns.zone.Zone instance created by the loader\n(as **context.zone_obj**), it contains some other attributes for the convenience\nof validators (code for the Context class can be found in the \ndns_sprockets_lib/validators/__init__.py file). Some of these are useful to\nsome validators, but can be ignored if not useful:\n\n- **context.node_names** Contains DNSSEC-ordered list of all node names present\n in the zone (*including* empty-non-terminal names implied by wildcard names).\n- **context.soa_rdataset** Contains the zone's SOA RRSet.\n- **context.dnskey_rdataset** Contains the zone's DNSKEY RRSet.\n- **context.nsec3param_rdataset** Contains the zone's NSEC3PARAM RRSet.\n- **context.delegated_names** Contains list of any delegated names in the zone.\n- **context.dnssec_type** Indicates the DNSSEC type of the zone.\n\nA method called **is_delegated()** is also available, which lets clients easily\ndetermine if a given owner name is delegated.\n\nValidators\n''''''''''\n\nValidators are classes *ultimately* derived from dns_sprockets_lib.validators._Validator \n(in the dns_sprockets_lib/validators/__init__.py file). This is the base class\nfor the four more specialized validator classes (ZoneTest, NodeTest, RRSetTest, and\nRecordTest)::\n\n class _Validator(object):\n '''\n [Base class for validator classes]\n '''\n TEST_NAME = None # Automatically set in __init__.\n TEST_TYPE = None # Override expected! e.g.: ZONE_TEST\n TEST_DNSSECTYPE = None # Override possible! one of: None, True, 'NSEC' or 'NSEC3'\n TEST_RRTYPE = None # Override possible! e.g.: 'A', or 'RRSIG,NSEC3PARAM'\n TEST_OPTARGS = {} # Override possible! e.g.: {'now': (None, 'Time to use for now')}\n \n def __init__(self, args):\n '''\n Ctor, caches the arguments used to run the application, and grabs any\n optional test arguments.\n '''\n self.TEST_NAME = utils.camelcase_to_underscores(self.__class__.__name__)\n self.args = args\n \n utils.process_optargs(self.TEST_OPTARGS, self.TEST_NAME, self)\n\nFive class variables are expected:\n\n- **TEST_NAME** Contains the underscore-style name of the validator, and is \n automatically set up in the __init__() method.\n- **TEST_TYPE** Indicates the type of validator.\n- **TEST_DNSSECTYPE** Indicates the DNSSEC-type of the validator.\n- **TEST_RRTYPE** Indicates zero or more resource record types the validator is\n specialized for. If no types specified, ALL types are assumed.\n- **TEST_OPTARGS** Contains any plugin-specific parameters that may be set from\n the command-line *...more on this later*.\n\nOne method is provided:\n\n- **__init__()** Convenince method for use by sub-classes.\n\nThere are four _Validator-derived classes for use by plugins (also defined in the\ndns_sprockets_lib/validators/__init__.py file). They provide slight convenience\nby defining **TEST_TYPE** properly, but more importantly expose different \n**run()** signatures, specific to each type of validator::\n\n class ZoneTest(_Validator):\n '''\n [Base class for zone-type validators]\n '''\n TEST_TYPE = ZONE_TEST\n \n def run(self, suggested_tested, context):\n '''\n Runs the zone-type validator.\n \n :param str suggested_tested: A suggested tested value.\n :param obj context: The testing context.\n :return: A tuple (tested, result)\n '''\n return ('OOPS!', 'ERROR: run() not overridden for %s' % (self.TEST_NAME))\n \n \n class NodeTest(_Validator):\n '''\n [Base class for node-type validators. Derived classes *may* be restricted\n to specific RRType's by specifying a TEST_RRTYPE]\n '''\n TEST_TYPE = NODE_TEST\n \n def run(self, context, suggested_tested, name, node):\n '''\n Runs the node-type validator. If a TEST_RRTYPE specified, the node\n presented to the validator will be filtered accordingly.\n \n :param obj context: The testing context.\n :param str suggested_tested: A suggested tested value.\n :param str name: The name being tested.\n :param obj node: The dns.Node corresponding to the name.\n :return: A tuple (tested, result)\n '''\n return ('OOPS!', 'ERROR: run() not overridden for %s' % (self.TEST_NAME))\n \n \n class RRSetTest(_Validator):\n '''\n [Base class for rrset-type validators. Derived classes *may* be restricted\n to specific RRType's by specifying a TEST_RRTYPE]\n '''\n TEST_TYPE = RRSET_TEST\n \n def run(self, context, suggested_tested, name, rdataset):\n '''\n Runs the name-type validator. If a TEST_RRTYPE is specified, the RRSet\n presented to the validator will be filtered accordingly.\n \n :param obj context: The testing context.\n :param str suggested_tested: A suggested tested value.\n :param str name: The name being tested.\n :param obj rdataset: The dns.rdataset corresponding to the name.\n :return: A tuple (tested, result)\n '''\n return ('OOPS!', 'ERROR: run() not overridden for %s' % (self.TEST_NAME))\n \n \n class RecTest(_Validator):\n '''\n [Base class for record-type validators. Derived classes *may* be restricted\n to specific RRType's by specifying a TEST_RRTYPE]\n '''\n TEST_TYPE = REC_TEST\n \n def run(self, context, suggested_tested, name, ttl, rdata):\n '''\n Runs the record-type validator. If a TEST_RRTYPE is specified, the\n validator will only see those types of records.\n \n :param obj context: The testing context.\n :param str suggested_tested: A suggested tested value.\n :param str name: The name of the record being tested.\n :param int ttl: The TTL of the record being tested.\n :param obj rdata: The dns.rdata.Rdata object being tested.\n :return: A tuple (tested, result)\n '''\n return ('OOPS!', 'ERROR: run() not overridden for %s' % (self.TEST_NAME))\n\nThe **suggested_tested** string contains a default name of the object being tested,\nbe it a zone, node, RRSet or record. It can be used in most instances as the first\nitem in the returned tuple from **run()**::\n\n Notes on the run() return tuple (tested, result): \n \n - If a validation is skipped for whatever reason, the 'tested' \n value should be None, which causes the framework to ignore the\n run. Otherwise, a value describing the object being tested \n should be set (and as mentioned 'suggested_tested' is a good \n value).\n \n - The actual result of an un-skipped test is returned in \n 'result'. If the test passes, simply return None. Otherwise,\n return a string describing the failure.\n \nAs an example, the code for the RrsigTime validator is as follows. The \n**TEST_DNSSECTYPE** is set to True to indicate the validation only makes sense \nfor DNSSEC-type zones. It is a record-type test, and only receives RRSIG records\ndue to the **TEST_RRTYPE** filtering applied. The \"context\", \"name\" and \"ttl\" \nparameters are ignored for this validation. The \"rdata\" parameter is used,\nand is of type dns.rdata.Rdata (a type defined in dnspython)::\n\n class RrsigTime(validators.RecTest):\n '''\n Checks RRSIG's inception <= expiration.\n '''\n TEST_DNSSECTYPE = True\n TEST_RRTYPE = 'RRSIG'\n \n def run(self, context, suggested_tested, name, ttl, rdata):\n \n result = None\n if rdata.inception > rdata.expiration:\n result = 'Inception time greater than expiration time'\n return (suggested_tested, result)\n\nPlease note that if your validator needs to define an **__init__()** method,\nit must call the base's **__init__()** to receive its useful and necessary\nfunctionality! Also be aware that the class docstring is used for the \ndescription of the validator, as shown in the --help output (keep it brief!)\n\nPlugin-Specific Arguments\n'''''''''''''''''''''''''\n\nLoaders and validators may have parameters that are specific to themselves.\nThe framework's --define command-line switch is used to pass these parameters\nto the plugins. \n\nThe names of the --define parameters are of the form: _\n(e.g. \"rrsig_missing_now\" specifies the \"now\" parameter for the rrsig_missing\nvalidator), and are translated and set as plugin attributes as \n(e.g. self.now in rrsig_missing methods).\n\nAs an example (and shown earlier), the File zone loader plugin defines two \nparameters specific to loading zone files::\n\n LOADER_OPTARGS = {\n 'rdclass': ('IN', 'Class of records to pull'),\n 'allow_include': ('1', 'Allow file to include other files')}\n\n**LOADER_OPTARGS** (and **TEST_OPTARGS** for validators) is a dictionary of \nparameter descriptors; each entry is keyed by , and indexes a 2-tuple\nof (, ). If no --define for the parameter is passed,\nthe will be set. The is used for --help output, \nso keep it brief please!", "description_content_type": null, "docs_url": null, "download_url": null, "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/ultradns/dns_sprockets", "keywords": "DNS zone validation", "license": "Apache License, Version 2.0", "maintainer": null, "maintainer_email": null, "name": "dns_sprockets", "package_url": "https://pypi.org/project/dns_sprockets/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/dns_sprockets/", "project_urls": { "Homepage": "https://github.com/ultradns/dns_sprockets" }, "release_url": "https://pypi.org/project/dns_sprockets/1.1.9/", "requires_dist": null, "requires_python": null, "summary": "Command-line DNS Zone validation tool", "version": "1.1.9" }, "last_serial": 2349873, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "6d4f5da6ac92ba1b48a548a007d06b7f", "sha256": "f095de5d399563cf5c36dc1d64808016e4acd15f4b20d39accf1e7d9c21d39ba" }, "downloads": -1, "filename": "dns_sprockets-1.0.0.tar.gz", "has_sig": false, "md5_digest": "6d4f5da6ac92ba1b48a548a007d06b7f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 70012, "upload_time": "2015-07-07T07:58:42", "url": "https://files.pythonhosted.org/packages/fa/a5/51e93210103a34af8874b05891b3167cd28935bcc79bf400ef0d44486878/dns_sprockets-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "36c2a5d7e5a1a2d6f059f9194855489c", "sha256": "16ee881fe519d1865c61370a76978d64b3a034b3db415ce2a68c56308e76e7b7" }, "downloads": -1, "filename": "dns_sprockets-1.0.1.tar.gz", "has_sig": false, "md5_digest": "36c2a5d7e5a1a2d6f059f9194855489c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 70144, "upload_time": "2015-07-07T08:08:14", "url": "https://files.pythonhosted.org/packages/ca/06/bfdbe3f55022d4229d92810cc65b56436bd2c9e9ee39f776b0073f49c706/dns_sprockets-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "35746bb1e0cb44e6847f51d406769cad", "sha256": "ff40d6b88ca6c6648a7d29eebccb20d5dd5f19e2620806930092279d2a58e57a" }, "downloads": -1, "filename": "dns_sprockets-1.0.2.tar.gz", "has_sig": false, "md5_digest": "35746bb1e0cb44e6847f51d406769cad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 69744, "upload_time": "2015-07-08T20:36:15", "url": "https://files.pythonhosted.org/packages/2a/aa/33ed44b9ad8cbf9fb2f61a263b6c3db869a48db27a4ad50c88c1170fcf2a/dns_sprockets-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "9aa6eb5641ef58a8f2cfb4c010cadb7b", "sha256": "fbce9777ae3d2abb849c5e14701b72f8bdcb3107166dccddc2c1cbe8dc720d31" }, "downloads": -1, "filename": "dns_sprockets-1.0.3.tar.gz", "has_sig": false, "md5_digest": "9aa6eb5641ef58a8f2cfb4c010cadb7b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 459154, "upload_time": "2015-07-10T08:01:38", "url": "https://files.pythonhosted.org/packages/0c/16/c5c3dbffe528fc957555bedee61bb870cad19bd22a280ea31b6ee7a055cd/dns_sprockets-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "e97725f294759a8f5d3079d4047055a9", "sha256": "21be6d98298a64bb1dc72de3f6dbf55c77d500da25b833023ba507c232077afe" }, "downloads": -1, "filename": "dns_sprockets-1.0.4.tar.gz", "has_sig": false, "md5_digest": "e97725f294759a8f5d3079d4047055a9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 459400, "upload_time": "2015-07-16T05:39:24", "url": "https://files.pythonhosted.org/packages/cb/e6/b4e87f2229c58043cdd8ad6aaada6e8497635df2cad650c3cd8cc7f1497e/dns_sprockets-1.0.4.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "73528a152eecec5a565ba6da32c02d5c", "sha256": "6c11489117849f1e7184a1c01e95838d4c0789319e17e69ddebbf88a453af995" }, "downloads": -1, "filename": "dns_sprockets-1.1.0.tar.gz", "has_sig": false, "md5_digest": "73528a152eecec5a565ba6da32c02d5c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 460876, "upload_time": "2015-08-05T06:31:15", "url": "https://files.pythonhosted.org/packages/5a/08/c792288c1cab9f43048d70bc3f056b2449b1d6d30bbd7bdd904d415cf520/dns_sprockets-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "439584b53bf5b71d8157632eb4f811c6", "sha256": "4853bd20d3e64d4b357e519283105d93240e60520972b233d1d585fa577aa9f0" }, "downloads": -1, "filename": "dns_sprockets-1.1.1.tar.gz", "has_sig": false, "md5_digest": "439584b53bf5b71d8157632eb4f811c6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 461117, "upload_time": "2015-08-05T06:51:33", "url": "https://files.pythonhosted.org/packages/bd/91/10def5176bd543ec08c44bd5b6c1bf6e1f560027bd08d6abc07d6ec0d626/dns_sprockets-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "515246ca22b9939fcbf71fbb5d643f21", "sha256": "a4c7675a5a4022d45d86b8d37e5299ba3270d103c2b2443e0d1b0f0978a1533c" }, "downloads": -1, "filename": "dns_sprockets-1.1.2.tar.gz", "has_sig": false, "md5_digest": "515246ca22b9939fcbf71fbb5d643f21", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 462439, "upload_time": "2015-08-12T04:19:14", "url": "https://files.pythonhosted.org/packages/ca/3f/c1eaad4bac576083cfecc52f7940879f5ca2a683a03a9689cbcadbd0db13/dns_sprockets-1.1.2.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "6cc0a82894fbda0f79ac6fd4381b613e", "sha256": "b776d3818888006ee7431f93575eeae08ab9a16ac1ea1cfdd6fad56f7b0741eb" }, "downloads": -1, "filename": "dns_sprockets-1.1.3.tar.gz", "has_sig": false, "md5_digest": "6cc0a82894fbda0f79ac6fd4381b613e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 462580, "upload_time": "2015-08-21T08:08:53", "url": "https://files.pythonhosted.org/packages/0f/36/e10c8d58e8a27fae8de1aa058b8a1c89be7f2480b314a961806582ffc301/dns_sprockets-1.1.3.tar.gz" } ], "1.1.4": [ { "comment_text": "", "digests": { "md5": "c683f15bc104b5e4a045036a87046a71", "sha256": "81310229825faac74ee96bf1c4578fb25a82dc226348e6cd27dc58be641693bb" }, "downloads": -1, "filename": "dns_sprockets-1.1.4.tar.gz", "has_sig": false, "md5_digest": "c683f15bc104b5e4a045036a87046a71", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 462850, "upload_time": "2015-08-25T06:33:21", "url": "https://files.pythonhosted.org/packages/e8/35/6418ab85ccdf8ea496e83bad72f9725464984614f54d9f09d3d5327331ef/dns_sprockets-1.1.4.tar.gz" } ], "1.1.5": [ { "comment_text": "", "digests": { "md5": "d2f31829cf5a2d2084529ace7eefe41c", "sha256": "61534e4942d6f38da32f12b93cc8d14d5de8122c6f1e2528600b014d47474f6d" }, "downloads": -1, "filename": "dns_sprockets-1.1.5.tar.gz", "has_sig": false, "md5_digest": "d2f31829cf5a2d2084529ace7eefe41c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 462886, "upload_time": "2015-08-27T18:11:43", "url": "https://files.pythonhosted.org/packages/9a/56/0bb6a7bcc8753e231de1fe0d7eccab06f96115e16aa5c6ddedd8ab4f640c/dns_sprockets-1.1.5.tar.gz" } ], "1.1.6": [ { "comment_text": "", "digests": { "md5": "6dae3c16918e4866c7c0ba42e8d86991", "sha256": "55ca9bf381600328f64fd2b95a39f25811c8167930a00cd97b3be5cd940473ab" }, "downloads": -1, "filename": "dns_sprockets-1.1.6.tar.gz", "has_sig": false, "md5_digest": "6dae3c16918e4866c7c0ba42e8d86991", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 466402, "upload_time": "2015-09-11T05:49:51", "url": "https://files.pythonhosted.org/packages/f3/12/6a6e30e25efb4aa9329017e0051d3277dfa8d7393e91f0b9d798702c832a/dns_sprockets-1.1.6.tar.gz" } ], "1.1.7": [ { "comment_text": "", "digests": { "md5": "3db97e4df645668c6cd741498aa6915e", "sha256": "5662705ed477b80d91f253f95663615dc279202cec220d5a2a073a968cc6752d" }, "downloads": -1, "filename": "dns_sprockets-1.1.7.tar.gz", "has_sig": false, "md5_digest": "3db97e4df645668c6cd741498aa6915e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 470153, "upload_time": "2016-03-28T21:19:56", "url": "https://files.pythonhosted.org/packages/d4/4f/ed62618cee56a9691087a936cb52b8a62eb8503c8be79d7511ca6f098233/dns_sprockets-1.1.7.tar.gz" } ], "1.1.8": [ { "comment_text": "", "digests": { "md5": "1c866c735208f855b1a9948031ad3093", "sha256": "6ff1e8de1489be2431527ddfa561680a45b2b5e7a6c051d05133ca6ec6fe3c8f" }, "downloads": -1, "filename": "dns_sprockets-1.1.8.tar.gz", "has_sig": false, "md5_digest": "1c866c735208f855b1a9948031ad3093", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 471663, "upload_time": "2016-03-29T06:55:44", "url": "https://files.pythonhosted.org/packages/ca/2b/dad8d0f8787f16ebd9e8c429b8c6aa60316550885600168356184e417797/dns_sprockets-1.1.8.tar.gz" } ], "1.1.9": [ { "comment_text": "", "digests": { "md5": "fd3882941d9f451cdd428413cc138faf", "sha256": "bb68fdd65f4616b64d53d4d1b8119fb891bbed7da26592939dc0eae999e1cc85" }, "downloads": -1, "filename": "dns_sprockets-1.1.9.tar.gz", "has_sig": false, "md5_digest": "fd3882941d9f451cdd428413cc138faf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 471716, "upload_time": "2016-09-19T03:01:23", "url": "https://files.pythonhosted.org/packages/ba/7e/1dbdf53fbd8760cd4a58c6c02e07f13debaf9fa3753618d33a2e3cf64148/dns_sprockets-1.1.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "fd3882941d9f451cdd428413cc138faf", "sha256": "bb68fdd65f4616b64d53d4d1b8119fb891bbed7da26592939dc0eae999e1cc85" }, "downloads": -1, "filename": "dns_sprockets-1.1.9.tar.gz", "has_sig": false, "md5_digest": "fd3882941d9f451cdd428413cc138faf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 471716, "upload_time": "2016-09-19T03:01:23", "url": "https://files.pythonhosted.org/packages/ba/7e/1dbdf53fbd8760cd4a58c6c02e07f13debaf9fa3753618d33a2e3cf64148/dns_sprockets-1.1.9.tar.gz" } ] }