예제 #1
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(description=(
        "Plot and compare reports against statistical data. "
        "Returns non-zero exit code if any threshold is exceeded."))

    cli.add_arg_stats(parser)
    cli.add_arg_report(parser)
    cli.add_arg_config(parser)
    parser.add_argument(
        '-l',
        '--label',
        default='fields_overview',
        help='Set plot label. It is also used for the filename.')

    args = parser.parse_args()
    sumstats = args.stats
    field_weights = args.cfg['report']['field_weights']

    try:
        summaries = cli.load_summaries(args.report)
    except ValueError:
        sys.exit(1)

    logging.info('Start Comparison: %s', args.label)
    passed = plot_overview(sumstats, field_weights, summaries, args.label)

    if not passed:
        sys.exit(3)
    else:
        sys.exit(0)
예제 #2
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description='Check if new samples belong to reference '
        'distribution. Ends with exitcode 0 if belong, '
        '1 if not,')
    parser.add_argument('-r',
                        '--reference',
                        type=cli.read_stats,
                        help='json statistics file with reference data')
    cli.add_arg_report_filename(parser)
    parser.add_argument(
        '-c',
        '--coef',
        type=float,
        default=DEFAULT_COEF,
        help=(
            'coeficient for comparation (new belongs to refference if '
            'its median is closer than COEF * standart deviation of reference '
            'from reference mean) (default: {})'.format(DEFAULT_COEF)))
    args = parser.parse_args()
    reports = cli.get_reports_from_filenames(args)

    try:
        newstats = SummaryStatistics(reports)
    except ValueError as exc:
        logging.critical(exc)
        sys.exit(2)

    if not belongs_to_all(args.reference, newstats, args.coef):
        sys.exit(1)
    sys.exit(0)
예제 #3
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        description='Convert queries data from standard input and store '
        'wire format into LMDB "queries" DB.')
    cli.add_arg_envdir(parser)
    parser.add_argument(
        '-f',
        '--in-format',
        type=str,
        choices=['text', 'pcap'],
        default='text',
        help='define format for input data, default value is text\n'
        'Expected input for "text" is: "<qname> <RR type>", '
        'one query per line.\n'
        'Expected input for "pcap" is content of the pcap file.')
    parser.add_argument('--pcap-file', type=argparse.FileType('rb'))

    args = parser.parse_args()

    if args.in_format == 'text' and args.pcap_file:
        logging.critical(
            "Argument --pcap-file can be use only in combination with -f pcap")
        sys.exit(1)
    if args.in_format == 'pcap' and not args.pcap_file:
        logging.critical("Missing path to pcap file, use argument --pcap-file")
        sys.exit(1)

    with LMDB(args.envdir) as lmdb:
        qdb = lmdb.open_db(LMDB.QUERIES, create=True, check_notexists=True)
        txn = lmdb.env.begin(qdb, write=True)
        try:
            with pool.Pool(initializer=lambda: signal.signal(
                    signal.SIGINT, signal.SIG_IGN)) as workers:
                if args.in_format == 'text':
                    data_stream = read_lines(sys.stdin)
                    method = wrk_process_line
                elif args.in_format == 'pcap':
                    data_stream = parse_pcap(args.pcap_file)
                    method = wrk_process_frame
                else:
                    logging.error('unknown in-format, use "text" or "pcap"')
                    sys.exit(1)
                for qid, wire in workers.imap(method,
                                              data_stream,
                                              chunksize=1000):
                    if qid is not None:
                        key = qid2key(qid)
                        txn.put(key, wire)
        except KeyboardInterrupt:
            logging.info('SIGINT received, exiting...')
            sys.exit(130)
        except RuntimeError as err:
            logging.error(err)
            sys.exit(1)
        finally:
            # attempt to preserve data if something went wrong (or not)
            logging.debug('Comitting LMDB transaction...')
            txn.commit()
예제 #4
0
파일: qexport.py 프로젝트: l1kw1d/respdiff
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(description="export queries from reports' summaries")
    cli.add_arg_report_filename(parser, nargs='+')
    parser.add_argument('--envdir', type=str,
                        help="LMDB environment (required when output format isn't 'qid')")
    parser.add_argument('-f', '--format', type=str, choices=['query', 'qid', 'domain', 'base64url'],
                        default='domain', help="output data format")
    parser.add_argument('-o', '--output', type=str, help='output file')
    parser.add_argument('--failing', action='store_true', help="get target disagreements")
    parser.add_argument('--unstable', action='store_true', help="get upstream unstable")
    parser.add_argument('--qidlist', type=str, help='path to file with list of QIDs to export')

    args = parser.parse_args()

    if args.format != 'qid' and not args.envdir:
        logging.critical("--envdir required when output format isn't 'qid'")
        sys.exit(1)

    if not args.failing and not args.unstable and not args.qidlist:
        logging.critical('No filter selected!')
        sys.exit(1)

    reports = cli.get_reports_from_filenames(args)
    if not reports:
        logging.critical('No reports found!')
        sys.exit(1)

    try:
        qids = get_qids_to_export(args, reports)
    except ValueError as exc:
        logging.critical(str(exc))
        sys.exit(1)

    with cli.smart_open(args.output) as fh:
        if args.format == 'qid':
            export_qids(qids, fh)
        else:
            with LMDB(args.envdir, readonly=True) as lmdb:
                lmdb.open_db(LMDB.QUERIES)

                if args.format == 'query':
                    export_qids_to_qname_qtype(qids, lmdb, fh)
                elif args.format == 'domain':
                    export_qids_to_qname(qids, lmdb, fh)
                elif args.format == 'base64url':
                    export_qids_to_base64url(qids, lmdb, fh)
                else:
                    raise ValueError('unsupported output format')
예제 #5
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description='read queries from LMDB, send them in parallel to servers '
        'listed in configuration file, and record answers into LMDB')
    cli.add_arg_envdir(parser)
    cli.add_arg_config(parser)
    parser.add_argument(
        '--ignore-timeout',
        action="store_true",
        help='continue despite consecutive timeouts from resolvers')

    args = parser.parse_args()
    sendrecv.module_init(args)

    with LMDB(args.envdir) as lmdb:
        meta = MetaDatabase(lmdb, args.cfg['servers']['names'], create=True)
        meta.write_version()
        meta.write_start_time()

        lmdb.open_db(LMDB.QUERIES)
        adb = lmdb.open_db(LMDB.ANSWERS, create=True, check_notexists=True)

        qstream = lmdb.key_value_stream(LMDB.QUERIES)
        txn = lmdb.env.begin(adb, write=True)
        try:
            # process queries in parallel
            with pool.Pool(processes=args.cfg['sendrecv']['jobs'],
                           initializer=sendrecv.worker_init) as p:
                i = 0
                for qkey, blob in p.imap(sendrecv.worker_perform_query,
                                         qstream,
                                         chunksize=100):
                    i += 1
                    if i % 10000 == 0:
                        logging.info('Received {:d} answers'.format(i))
                    txn.put(qkey, blob)
        except KeyboardInterrupt:
            logging.info('SIGINT received, exiting...')
            sys.exit(130)
        except RuntimeError as err:
            logging.error(err)
            sys.exit(1)
        finally:
            # attempt to preserve data if something went wrong (or not)
            logging.debug('Comitting LMDB transaction...')
            txn.commit()
            meta.write_end_time()
예제 #6
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description='attempt to reproduce original diffs from JSON report')
    cli.add_arg_envdir(parser)
    cli.add_arg_config(parser)
    cli.add_arg_datafile(parser)
    parser.add_argument('--sequential', action='store_true', default=False,
                        help='send one query at a time (slower, but more reliable)')

    args = parser.parse_args()
    sendrecv.module_init(args)
    datafile = cli.get_datafile(args)
    report = DiffReport.from_json(datafile)
    restart_scripts = repro.get_restart_scripts(args.cfg)
    servers = args.cfg['servers']['names']
    dnsreplies_factory = DNSRepliesFactory(servers)

    if args.sequential:
        nproc = 1
    else:
        nproc = args.cfg['sendrecv']['jobs']

    if report.reprodata is None:
        report.reprodata = ReproData()

    with LMDB(args.envdir, readonly=True) as lmdb:
        lmdb.open_db(LMDB.QUERIES)
        cli.check_metadb_servers_version(lmdb, servers)

        dstream = repro.query_stream_from_disagreements(lmdb, report)
        try:
            repro.reproduce_queries(
                dstream, report, dnsreplies_factory, args.cfg['diff']['criteria'],
                args.cfg['diff']['target'], restart_scripts, nproc)
        finally:
            # make sure data is saved in case of interrupt
            report.export_json(datafile)
예제 #7
0
파일: msgdiff.py 프로젝트: l1kw1d/respdiff
def main():
    global lmdb

    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description=
        'compute diff from answers stored in LMDB and write diffs to LMDB')
    cli.add_arg_envdir(parser)
    cli.add_arg_config(parser)
    cli.add_arg_datafile(parser)

    args = parser.parse_args()
    datafile = cli.get_datafile(args, check_exists=False)
    criteria = args.cfg['diff']['criteria']
    target = args.cfg['diff']['target']
    servers = args.cfg['servers']['names']

    with LMDB(args.envdir) as lmdb_:
        # NOTE: To avoid an lmdb.BadRslotError, probably caused by weird
        # interaction when using multiple transaction / processes, open a separate
        # environment. Also, any dbs have to be opened before using MetaDatabase().
        report = prepare_report(lmdb_, servers)
        cli.check_metadb_servers_version(lmdb_, servers)

    with LMDB(args.envdir, fast=True) as lmdb_:
        lmdb = lmdb_
        lmdb.open_db(LMDB.ANSWERS)
        lmdb.open_db(LMDB.DIFFS, create=True, drop=True)
        qid_stream = lmdb.key_stream(LMDB.ANSWERS)

        dnsreplies_factory = DNSRepliesFactory(servers)
        compare_func = partial(compare_lmdb_wrapper, criteria, target,
                               dnsreplies_factory)
        with pool.Pool() as p:
            for _ in p.imap_unordered(compare_func, qid_stream, chunksize=10):
                pass
        export_json(datafile, report)
예제 #8
0
파일: dnsviz.py 프로젝트: l1kw1d/respdiff
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description=
        "use dnsviz to categorize domains (perfect, warnings, errors)")
    cli.add_arg_config(parser)
    cli.add_arg_dnsviz(parser)
    parser.add_argument('input',
                        type=str,
                        help='input file with domains (one qname per line)')
    args = parser.parse_args()

    njobs = args.cfg['sendrecv']['jobs']
    try:
        probe = subprocess.run([
            'dnsviz', 'probe', '-A', '-R', respdiff.dnsviz.TYPES, '-f',
            args.input, '-t',
            str(njobs)
        ],
                               check=True,
                               stdout=subprocess.PIPE)
    except subprocess.CalledProcessError as exc:
        logging.critical("dnsviz probe failed: %s", exc)
        sys.exit(1)
    except FileNotFoundError:
        logging.critical("'dnsviz' tool is not installed!")
        sys.exit(1)

    try:
        subprocess.run(['dnsviz', 'grok', '-o', args.dnsviz],
                       input=probe.stdout,
                       check=True,
                       stdout=subprocess.PIPE)
    except subprocess.CalledProcessError as exc:
        logging.critical("dnsviz grok failed: %s", exc)
        sys.exit(1)
예제 #9
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(description='compare two diff summaries')
    cli.add_arg_config(parser)
    parser.add_argument('old_datafile',
                        type=str,
                        help='report to compare against')
    parser.add_argument('new_datafile',
                        type=str,
                        help='report to compare evaluate')
    cli.add_arg_envdir(
        parser)  # TODO remove when we no longer need to read queries from lmdb
    cli.add_arg_limit(parser)

    args = parser.parse_args()
    report = DiffReport.from_json(cli.get_datafile(args, key='new_datafile'))
    field_weights = args.cfg['report']['field_weights']
    ref_report = DiffReport.from_json(
        cli.get_datafile(args, key='old_datafile'))

    check_report_summary(report)
    check_report_summary(ref_report)
    check_usable_answers(report, ref_report)

    cli.print_global_stats(report, ref_report)
    cli.print_differences_stats(report, ref_report)

    if report.summary or ref_report.summary:  # when there are any differences to report
        field_counters = report.summary.get_field_counters()
        ref_field_counters = ref_report.summary.get_field_counters()

        # make sure "disappeared" fields show up as well
        for field in ref_field_counters:
            if field not in field_counters:
                field_counters[field] = Counter()

        cli.print_fields_overview(field_counters, len(report.summary),
                                  ref_field_counters)

        for field in field_weights:
            if field in field_counters:
                counter = field_counters[field]
                ref_counter = ref_field_counters.get(field, Counter())

                # make sure "disappeared" mismatches show up as well
                for mismatch in ref_counter:
                    if mismatch not in counter:
                        counter[mismatch] = 0

                cli.print_field_mismatch_stats(field, counter,
                                               len(report.summary),
                                               ref_counter)

        # query details
        with LMDB(args.envdir, readonly=True) as lmdb:
            lmdb.open_db(LMDB.QUERIES)

            queries_all = convert_queries(
                get_query_iterator(lmdb, report.summary.keys()))
            ref_queries_all = convert_queries(
                get_query_iterator(lmdb, ref_report.summary.keys()))

            for field in field_weights:
                if field in field_counters:
                    # ensure "disappeared" mismatches are shown
                    field_mismatches = dict(
                        report.summary.get_field_mismatches(field))
                    ref_field_mismatches = dict(
                        ref_report.summary.get_field_mismatches(field))
                    mismatches = set(field_mismatches.keys())
                    mismatches.update(ref_field_mismatches.keys())

                    for mismatch in mismatches:
                        qids = field_mismatches.get(mismatch, set())
                        queries = convert_queries(
                            get_query_iterator(lmdb, qids))
                        ref_queries = convert_queries(
                            get_query_iterator(
                                lmdb,
                                ref_field_mismatches.get(mismatch, set())))
                        cli.print_mismatch_queries(
                            field, mismatch,
                            get_printable_queries_format(
                                queries, queries_all, ref_queries,
                                ref_queries_all), args.limit)
예제 #10
0
def main():
    cli.setup_logging()
    args = parse_args()
    datafile = cli.get_datafile(args)
    report = DiffReport.from_json(datafile)
    field_weights = args.cfg['report']['field_weights']

    check_args(args, report)

    ignore_qids = set()
    if args.without_ref_unstable or args.without_ref_failing:
        try:
            stats = cli.read_stats(args.stats_filename)
        except ValueError as exc:
            logging.critical(str(exc))
            sys.exit(1)
        if args.without_ref_unstable:
            ignore_qids.update(stats.queries.unstable)
        if args.without_ref_failing:
            ignore_qids.update(stats.queries.failing)

    report = DiffReport.from_json(datafile)
    report.summary = Summary.from_report(
        report,
        field_weights,
        without_diffrepro=args.without_diffrepro,
        ignore_qids=ignore_qids)

    # dnsviz filter: by domain -> need to iterate over disagreements to get QIDs
    if args.without_dnsviz_errors:
        try:
            dnsviz_grok = DnsvizGrok.from_json(args.dnsviz)
        except (FileNotFoundError, RuntimeError) as exc:
            logging.critical('Failed to load dnsviz data: %s', exc)
            sys.exit(1)

        error_domains = dnsviz_grok.error_domains()
        with LMDB(args.envdir, readonly=True) as lmdb:
            lmdb.open_db(LMDB.QUERIES)
            # match domain, add QID to ignore
            for qid, wire in get_query_iterator(lmdb, report.summary.keys()):
                msg = dns.message.from_wire(wire)
                if msg.question:
                    if any(msg.question[0].name.is_subdomain(name)
                           for name in error_domains):
                        ignore_qids.add(qid)

        report.summary = Summary.from_report(
            report,
            field_weights,
            without_diffrepro=args.without_diffrepro,
            ignore_qids=ignore_qids)

    cli.print_global_stats(report)
    cli.print_differences_stats(report)

    if report.summary:  # when there are any differences to report
        field_counters = report.summary.get_field_counters()
        cli.print_fields_overview(field_counters, len(report.summary))
        for field in field_weights:
            if field in report.summary.field_labels:
                cli.print_field_mismatch_stats(field, field_counters[field],
                                               len(report.summary))

        # query details
        with LMDB(args.envdir, readonly=True) as lmdb:
            lmdb.open_db(LMDB.QUERIES)

            for field in field_weights:
                if field in report.summary.field_labels:
                    for mismatch, qids in report.summary.get_field_mismatches(
                            field):
                        queries = convert_queries(
                            get_query_iterator(lmdb, qids))
                        cli.print_mismatch_queries(
                            field, mismatch,
                            get_printable_queries_format(queries), args.limit)

    report.export_json(datafile)
예제 #11
0
def main():
    cli.setup_logging()
    parser = argparse.ArgumentParser(
        description='Plot query response time histogram from answers stored '
        'in LMDB')
    parser.add_argument(
        '-o',
        '--output',
        type=str,
        default='histogram',
        help='output directory for image files (default: histogram)')
    parser.add_argument('-f',
                        '--format',
                        type=str,
                        default='png',
                        help='output image format (default: png)')
    parser.add_argument('-c',
                        '--config',
                        default='respdiff.cfg',
                        dest='cfgpath',
                        help='config file (default: respdiff.cfg)')
    parser.add_argument('envdir',
                        type=str,
                        help='LMDB environment to read answers from')
    args = parser.parse_args()
    config = cfg.read_cfg(args.cfgpath)
    servers = config['servers']['names']
    dnsreplies_factory = DNSRepliesFactory(servers)

    with LMDB(args.envdir, readonly=True) as lmdb_:
        adb = lmdb_.open_db(LMDB.ANSWERS)

        try:
            MetaDatabase(lmdb_, servers,
                         create=False)  # check version and servers
        except NotImplementedError as exc:
            logging.critical(exc)
            sys.exit(1)

        with lmdb_.env.begin(adb) as txn:
            data = load_data(txn, dnsreplies_factory)

    def get_filepath(filename) -> str:
        return os.path.join(args.output, filename + '.' + args.format)

    if not os.path.exists(args.output):
        os.makedirs(args.output)
    create_histogram({k: [tup[0] for tup in d]
                      for (k, d) in data.items()}, get_filepath('all'), 'all',
                     config)

    # rcode-specific queries
    with pool.Pool() as p:
        fargs = []
        for rcode in range(HISTOGRAM_RCODE_MAX + 1):
            rcode_text = dns.rcode.to_text(rcode)
            filepath = get_filepath(rcode_text)
            fargs.append((data, filepath, rcode_text, config, rcode))
        p.starmap(histogram_by_rcode, fargs)
    filepath = get_filepath('unparsed')
    histogram_by_rcode(data, filepath, 'unparsed queries', config, None)