aboutsummaryrefslogtreecommitdiff
path: root/09e67fb2d364c2b6b2ed87b7b7459d15dfea44c3
diff options
context:
space:
mode:
Diffstat (limited to '09e67fb2d364c2b6b2ed87b7b7459d15dfea44c3')
0 files changed, 0 insertions, 0 deletions
href='#n30'>30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 26 (0x1a)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=TEST ENTITY LIR3
        Validity
            Not Before: Oct 29 16:32:33 2007 GMT
            Not After : Oct 28 16:32:33 2008 GMT
        Subject: CN=TEST ENTITY ISP5b
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:bf:8f:59:d8:fc:fa:1d:04:70:29:ce:7b:01:64:
                    21:40:dc:5c:43:fe:4c:af:88:c8:62:9b:be:9c:72:
                    8f:8a:a5:34:8a:3b:23:8d:9b:8a:4f:bf:66:ac:68:
                    71:9c:fd:68:59:f5:bf:9f:4d:2e:b5:d6:e3:fa:bd:
                    f3:60:53:5c:b7:11:ac:95:0b:c0:87:cd:99:9e:94:
                    57:8d:ec:05:b8:df:aa:fc:8e:38:d3:0f:65:6d:09:
                    60:f2:e1:98:81:72:d8:51:3e:41:91:b3:10:95:f5:
                    f5:d0:f9:e5:5c:a1:85:fa:71:26:85:e3:d1:4c:02:
                    7f:14:e2:1e:4a:8a:96:68:9e:d6:16:a5:ef:ad:b5:
                    83:62:cd:23:74:7c:82:56:b4:d1:34:53:5a:8a:7a:
                    61:9f:ae:54:5b:ef:f9:56:de:87:6b:42:92:bc:49:
                    f4:b5:c3:35:07:4a:18:47:d2:92:c6:1c:16:74:74:
                    b1:e9:39:3c:53:12:05:9d:eb:dc:9c:72:2b:97:4d:
                    27:21:77:96:7d:4c:ce:79:0c:fb:a7:b8:99:6b:66:
                    20:2e:56:9c:44:b4:e3:5e:80:c4:7d:78:a1:b4:05:
                    f7:20:7d:26:1e:44:bf:5d:69:15:3c:7a:24:67:bd:
                    b9:b5:08:0f:33:4d:af:3b:2d:e7:b9:ab:1d:2b:d6:
                    fb:73
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier: 
                6C:B3:65:94:FE:C6:9F:4A:50:9D:4D:8B:40:1A:A1:FD:97:17:97:92
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            Subject Information Access: 
                1.3.6.1.5.5.7.48.5 - URI:rsync://wombats-r-us.hactrn.net/ISP5b/

            Authority Information Access: 
                CA Issuers - URI:rsync://wombats-r-us.hactrn.net/LIR3.cer

            sbgp-ipAddrBlock: critical
                IPv4:
                  10.3.0.0/24
                IPv6:
                  2001:db8:0:0:0:0:a03::/120

    Signature Algorithm: sha256WithRSAEncryption
        3a:6d:f2:b8:e4:50:4d:f6:f0:f3:04:1b:73:bf:36:13:d5:e6:
        70:d9:31:b4:47:b7:5e:ca:8a:25:93:fc:6c:dd:63:5e:09:dc:
        47:d9:d4:43:39:f3:ed:c5:f2:64:d5:ac:72:02:76:f2:07:ca:
        a5:d9:1a:e3:9d:6a:7d:90:4c:d5:c7:09:c9:64:dd:38:f0:2f:
        ab:0d:5f:e4:13:02:2c:85:02:56:f4:6d:31:07:f9:0b:c7:e9:
        a4:0a:ee:13:03:18:9d:81:b8:78:68:d2:99:a3:e4:4f:e7:96:
        86:99:06:10:8c:b5:c2:39:03:8a:94:2e:21:00:67:82:f5:25:
        6c:cb:71:6b:8c:e6:31:0a:19:ed:1a:34:0c:a9:48:ca:c8:69:
        fc:91:4e:f9:0c:e5:24:2b:70:52:1c:ff:1c:cf:38:28:17:3a:
        3d:22:a7:fa:93:dd:8f:46:03:2e:b0:ce:10:57:4a:3c:fc:a8:
        1a:a6:c1:0e:fa:09:49:9e:d1:89:b8:4c:b0:7a:5b:76:25:05:
        fe:80:d9:8d:c1:9e:84:0b:83:53:16:9a:1e:2d:55:9a:b7:81:
        d4:3f:0a:c7:56:ac:87:58:fa:3b:27:77:c6:f6:31:c1:c8:56:
        4a:28:6a:de:20:32:c4:80:b0:d1:36:25:ac:2c:94:28:8a:b8:
        2b:f2:04:f0
-----BEGIN CERTIFICATE-----
MIIDvTCCAqWgAwIBAgIBGjANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBURVNU
IEVOVElUWSBMSVIzMB4XDTA3MTAyOTE2MzIzM1oXDTA4MTAyODE2MzIzM1owHDEa
MBgGA1UEAxMRVEVTVCBFTlRJVFkgSVNQNWIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC/j1nY/PodBHApznsBZCFA3FxD/kyviMhim76cco+KpTSKOyON
m4pPv2asaHGc/WhZ9b+fTS611uP6vfNgU1y3EayVC8CHzZmelFeN7AW436r8jjjT
D2VtCWDy4ZiBcthRPkGRsxCV9fXQ+eVcoYX6cSaF49FMAn8U4h5KipZontYWpe+t
tYNizSN0fIJWtNE0U1qKemGfrlRb7/lW3odrQpK8SfS1wzUHShhH0pLGHBZ0dLHp
OTxTEgWd69ycciuXTSchd5Z9TM55DPunuJlrZiAuVpxEtONegMR9eKG0BfcgfSYe
RL9daRU8eiRnvbm1CA8zTa87Lee5qx0r1vtzAgMBAAGjggEJMIIBBTAPBgNVHRMB
Af8EBTADAQH/MB0GA1UdDgQWBBRss2WU/safSlCdTYtAGqH9lxeXkjAOBgNVHQ8B
Af8EBAMCAQYwQgYIKwYBBQUHAQsENjA0MDIGCCsGAQUFBzAFhiZyc3luYzovL3dv
bWJhdHMtci11cy5oYWN0cm4ubmV0L0lTUDViLzBEBggrBgEFBQcBAQQ4MDYwNAYI
KwYBBQUHMAKGKHJzeW5jOi8vd29tYmF0cy1yLXVzLmhhY3Rybi5uZXQvTElSMy5j
ZXIwOQYIKwYBBQUHAQcBAf8EKjAoMAwEAgABMAYDBAAKAwAwGAQCAAIwEgMQACAB
DbgAAAAAAAAAAAoDADANBgkqhkiG9w0BAQsFAAOCAQEAOm3yuORQTfbw8wQbc782
E9XmcNkxtEe3XsqKJZP8bN1jXgncR9nUQznz7cXyZNWscgJ28gfKpdka451qfZBM
1ccJyWTdOPAvqw1f5BMCLIUCVvRtMQf5C8fppAruEwMYnYG4eGjSmaPkT+eWhpkG
EIy1wjkDipQuIQBngvUlbMtxa4zmMQoZ7Ro0DKlIyshp/JFO+QzlJCtwUhz/HM84
KBc6PSKn+pPdj0YDLrDOEFdKPPyoGqbBDvoJSZ7RibhMsHpbdiUF/oDZjcGehAuD
UxaaHi1VmreB1D8Kx1ash1j6Oyd3xvYxwchWSihq3iAyxICw0TYlrCyUKIq4K/IE
8A==
-----END CERTIFICATE-----
al.Number.Bin */ .highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */ .highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */ .highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */ .highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */ .highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */ .highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */ .highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */ .highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */ .highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */ .highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */ .highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */ .highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */ .highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */ .highlight .bp { color: #038 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #369 } /* Name.Variable.Class */ .highlight .vg { color: #D70 } /* Name.Variable.Global */ .highlight .vi { color: #33B } /* Name.Variable.Instance */ .highlight .vm { color: #369 } /* Name.Variable.Magic */ .highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
# $Id$
#
# Copyright (C) 2011-2012  Internet Systems Consortium ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

"""
Parse traffic data out of rynic XML output, whack it a bit, print some
summaries and run gnuplot to draw some pictures.
"""

plot_all_hosts = False

window_hours = 72

import mailbox
import sys
import urlparse
import os
import datetime
import subprocess
import shelve

from xml.etree.cElementTree import (ElementTree as ElementTree,
                                    fromstring  as ElementTreeFromString)

def parse_utc(s):
    return datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ")

class Rsync_History(object):
    """
    An Rsync_History object represents one rsync connection.
    """

    def __init__(self, elt):
        self.error = elt.get("error")
        self.uri = elt.text.strip()
        self.hostname = urlparse.urlparse(self.uri).hostname or None
        self.elapsed = parse_utc(elt.get("finished")) - parse_utc(elt.get("started"))

class Host(object):
    """
    A host object represents all the data collected for one host.  Note
    that it (usually) contains a list of all the sessions in which this
    host appears.
    """

    def __init__(self, hostname, session_id):
        self.hostname = hostname
        self.session_id = session_id
        self.elapsed = datetime.timedelta(0)
        self.connection_count = 0
        self.dead_connections = 0
        self.uris = set()
        self.total_connection_time = datetime.timedelta(0)

    def add_rsync_history(self, h):
        self.connection_count      += 1
        self.elapsed               += h.elapsed
        self.dead_connections      += int(h.error is not None)
        self.total_connection_time += h.elapsed

    def add_uri(self, u):
        self.uris.add(u)

    def finalize(self):
        self.object_count = len(self.uris)
        del self.uris

    @property
    def failed(self):
        return 1 if self.dead_connections else 0

    @property
    def seconds_per_object(self):
        if self.failed:
            return None
        else:
            return float(self.elapsed.days * 24 * 60 * 60 +
                         self.elapsed.seconds +
                         self.elapsed.microseconds / 10**6) / float(self.object_count)

    @property
    def objects_per_connection(self):
        if self.failed:
            return None
        else:
            return float(self.object_count) / float(self.connection_count)

    @property
    def average_connection_time(self):
        return float(self.total_connection_time.days * 24 * 60 * 60 +
                     self.total_connection_time.seconds +
                     self.total_connection_time.microseconds / 10**6) / float(self.connection_count)

    class Format(object):

        def __init__(self, attr, title, fmt, ylabel = ""):
            self.attr = attr
            self.title = title
            self.width = len(title) - int("%" in fmt)
            self.fmt = "%%%d%s" % (self.width, fmt)
            self.oops = "*" * self.width
            self.ylabel = ylabel

        def __call__(self, obj):
            try:
                value = getattr(obj, self.attr)
                return None if value is None else self.fmt % value
            except ZeroDivisionError:
                return self.oops

    format = (Format("connection_count",        "Connections",        "d",     "Connections To Repository (Per Session)"),
              Format("object_count",            "Objects",            "d",     "Objects In Repository (Distinct URIs Per Session)"),
              Format("objects_per_connection",  "Objects/Connection", ".3f",   "Objects In Repository / Connections To Repository"),
              Format("seconds_per_object",      "Seconds/Object",     ".3f",   "Seconds To Transfer / Object (Average Per Session)"),
              Format("failure_rate_running",    "Failure Rate",       ".3f%%", "Sessions With Failed Connections Within Last %d Hours" % window_hours),
              Format("average_connection_time", "Average Connection", ".3f",   "Seconds / Connection (Average Per Session)"),
              Format("hostname",                "Hostname",           "s"))

    format_dict = dict((fmt.attr, fmt) for fmt in format)

    def format_field(self, name):
        result = self.format_dict[name](self)
        return None if result is None else result.strip()

class Session(dict):
    """
    A session corresponds to one XML file.  This is a dictionary of Host
    objects, keyed by hostname.
    """

    def __init__(self, session_id, msg_key):
        self.session_id = session_id
        self.msg_key = msg_key
        self.date = parse_utc(session_id)
        self.calculated_failure_history = False

    @property
    def hostnames(self):
        return set(self.iterkeys())

    def get_plot_row(self, name, hostnames):
        return (self.session_id,) + tuple(self[h].format_field(name) if h in self else "" for h in hostnames)

    def add_rsync_history(self, h):
        if h.hostname not in self:
            self[h.hostname] = Host(h.hostname, self.session_id)
        self[h.hostname].add_rsync_history(h)

    def add_uri(self, u):
        h = urlparse.urlparse(u).hostname
        if h and h in self:
            self[h].add_uri(u)

    def finalize(self):
        for h in self.itervalues():
            h.finalize()

    def calculate_failure_history(self, sessions):
        start = self.date - datetime.timedelta(hours = window_hours)
        sessions = tuple(s for s in sessions if s.date <= self.date and s.date > start)
        for hostname, h in self.iteritems():
            i = n = 0
            for s in sessions:
                if hostname in s:
                    i += s[hostname].failed
                    n += 1
            h.failure_rate_running = float(100 * i) / n
        self.calculated_failure_history = True

def plotter(f, hostnames, field, logscale = False):
    plotlines = sorted(session.get_plot_row(field, hostnames) for session in sessions)
    title = Host.format_dict[field].title
    ylabel = Host.format_dict[field].ylabel
    n = len(hostnames) + 1
    assert all(n == len(plotline) for plotline in plotlines)
    if "%%" in Host.format_dict[field].fmt:
        f.write('set format y "%.0f%%"\n')
    else:
        f.write('set format y\n')
    if logscale:
        f.write("set logscale y\n")
    else:
        f.write("unset logscale y\n")
    f.write("""
            set xdata time
            set timefmt '%Y-%m-%dT%H:%M:%SZ'
            #set format x '%m/%d'
            #set format x '%b%d'
            #set format x '%Y-%m-%d'
            set format x '%Y-%m'
            #set title '""" + title + """'
            set ylabel '""" + ylabel + """'
            plot""" + ",".join(" '-' using 1:2 with linespoints pointinterval 500 title '%s'" % h for h in hostnames) + "\n")
    for i in xrange(1, n):
        for plotline in plotlines:
            if plotline[i] is not None:
                f.write("%s %s\n" % (plotline[0], plotline[i].rstrip("%")))
        f.write("e\n")

def plot_hosts(hostnames, fields):
    for field in fields:
        for logscale in (False, True):
            gnuplot = subprocess.Popen(("gnuplot",), stdin = subprocess.PIPE)
            gnuplot.stdin.write("set terminal pdf\n")
            gnuplot.stdin.write("set output '%s/%s-%s.pdf'\n" % (outdir, field, "log" if logscale else "linear"))
            plotter(gnuplot.stdin, hostnames, field, logscale = logscale)
            gnuplot.stdin.close()
            gnuplot.wait()

outdir = "images"

if not os.path.exists(outdir):
    os.makedirs(outdir)

mb = mailbox.Maildir("/u/sra/rpki/rcynic-xml", factory = None, create = False)

if sys.platform == "darwin":            # Sigh
    shelf = shelve.open("rcynic-xml", "c")
else:
    shelf = shelve.open("rcynic-xml.db", "c")

sessions = []

latest = None
parsed = 0

for i, key in enumerate(mb.iterkeys(), 1):
    sys.stderr.write("\r%s %d/%d/%d..." % ("|\\-/"[i & 3], parsed, i, len(mb)))

    if key in shelf:
        session = shelf[key]

    else:
        sys.stderr.write("%s..." % key)
        assert not mb[key].is_multipart()
        input = ElementTreeFromString(mb[key].get_payload())
        date = input.get("date")
        sys.stderr.write("%s..." % date)
        session = Session(date, key)
        for elt in input.findall("rsync_history"):
            session.add_rsync_history(Rsync_History(elt))
        for elt in input.findall("validation_status"):
            if elt.get("generation") == "current":
                session.add_uri(elt.text.strip())
        session.finalize()
        shelf[key] = session
        parsed += 1

    sessions.append(session)
    if latest is None or session.session_id > latest.session_id:
        latest = session

sys.stderr.write("\n")

shelf.sync()

for session in sessions:
    if not getattr(session, "calculated_failure_history", False):
        session.calculate_failure_history(sessions)
        shelf[session.msg_key] = session

if plot_all_hosts:
    hostnames = sorted(reduce(lambda x, y: x | y,
                              (s.hostnames for s in sessions),
                              set()))

else:
    hostnames = ("rpki.apnic.net", "rpki.ripe.net", "repository.lacnic.net", "rpki.afrinic.net", "rpki.arin.net",
                 #"localcert.ripe.net", "arin.rpki.net", "repo0.rpki.net", "rgnet.rpki.net",
                 "ca0.rpki.net")

plot_hosts(hostnames, [fmt.attr for fmt in Host.format if fmt.attr != "hostname"])

if latest is not None:
    f = open("rcynic.xml", "wb")
    f.write(mb[latest.msg_key].get_payload())
    f.close()

shelf.close()