blob: dd019b0e934dccd74073e4196a63c68f4cbd901d [file] [log] [blame]
#!/usr/bin/env python
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Adverbl concepts
# * Multiple log files may be displayed at the same time.
# Each log file gets a letter prefix: A, B, C, ...
# * Log AMQP proton trace channel numbers get prefix
# [1] becomes [A-1]
# * The log file line numbers are equivalent to a wireshark trace frame number.
# * There's no concept of client and server because the logs are from inside
# a router.
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import argparse
import os
import sys
import traceback
import amqp_detail
import common
import datetime
from log_splitter import main_except as splitter_main
import parser
import router
import text
def time_offset(ttest, t0):
"""
Return a string time delta between two datetime objects in seconds formatted
to six significant decimal places.
:param ttest:
:param t0:
:return:
"""
delta = ttest - t0
t = float(delta.seconds) + float(delta.microseconds) / 1000000.0
return "%0.06f" % t
def show_noteworthy_line(plf, comn):
"""
Given a log line, print the noteworthy display line
:param plf: parsed log line
:param comn:
:return:
"""
rid = plf.router.iname
id = "[%s]" % plf.data.conn_id
peerconnid = "[%s]" % comn.conn_peers_connid.get(plf.data.conn_id, "")
peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id
print("%s %s %s %s %s %s %s<br>" %
(plf.adverbl_link_to(), rid, id, plf.data.direction, peerconnid, peer,
plf.data.web_show_str))
#
#
def main_except(argv):
# Instantiate a common block
comn = common.Common()
# optparse - look for data-inhibit and program mode control
p = argparse.ArgumentParser()
p.add_argument('--skip-all-data', '-sa',
action='store_true',
help='Max load shedding: do not store/index transfer, disposition, flow or EMPTY_FRAME data')
p.add_argument('--skip-detail', '-sd',
action='store_true',
help='Load shedding: do not produce Connection Details tables')
p.add_argument('--skip-msg-progress', '-sm',
action='store_true',
help='Load shedding: do not produce Message Progress tables')
p.add_argument('--split', '-sp',
action='store_true',
help='A single file is split into per-connection data.')
p.add_argument('--time-start', '-ts',
help='Ignore log records earlier than this. Format: "2018-08-13 13:15:00.123456"')
p.add_argument('--time-end', '-te',
help='Ignore log records later than this. Format: "2018-08-13 13:15:15.123456"')
p.add_argument('--sequence', '-sq',
action='store_true',
help='Write sequence diagram raw data to end of web page. Edit what you need for input to seq-diag-gen utility.')
p.add_argument('--log-modules', '-lm',
help='Include list of named modules'' log entries in main log display. Specify multiple modules as a CSV string. Defaults to "SCRAPER"')
p.add_argument('--files', '-f', nargs="+")
del argv[0]
comn.args = p.parse_args(argv)
if comn.args.time_start is not None:
try:
comn.args.time_start = datetime.datetime.strptime(comn.args.time_start, "%Y-%m-%d %H:%M:%S.%f")
except:
sys.exit("ERROR: Failed to parse time_start '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_start)
if comn.args.time_end is not None:
try:
comn.args.time_end = datetime.datetime.strptime(comn.args.time_end, "%Y-%m-%d %H:%M:%S.%f")
except:
sys.exit("ERROR: Failed to parse time_end '%s'. Use format 'YYYY-MM-DD HH:MM:SS.n_uS'" % comn.args.time_end)
if comn.args.log_modules is not None:
l = [x.strip() for x in comn.args.log_modules.split(",")]
comn.verbatim_include_list = [x for x in l if len(x) > 0]
# process split function
if comn.args.split:
# Split processes only a single file
if len(comn.args.files) > 1:
sys.exit('--split mode takes only one file name')
return splitter_main(comn.args.files[0])
# process the log files and add the results to router_array
for log_i in range(len(comn.args.files)):
arg_log_file = comn.args.files[log_i]
comn.log_fns.append(arg_log_file)
comn.n_logs += 1
if not os.path.exists(arg_log_file):
sys.exit('ERROR: log file %s was not found!' % arg_log_file)
# parse the log file
rtrs = parser.parse_log_file(arg_log_file, log_i, comn)
comn.routers.append(rtrs)
# Create lists of various things sorted by time
tree = [] # log line
ls_tree = [] # link state lines
rr_tree = [] # restart records
for rtrlist in comn.routers:
for rtr in rtrlist:
tree += rtr.lines
ls_tree += rtr.router_ls
rr_tree.append(rtr.restart_rec)
tree = sorted(tree, key=lambda lfl: lfl.datetime)
ls_tree = sorted(ls_tree, key=lambda lfl: lfl.datetime)
rr_tree = sorted(rr_tree, key=lambda lfl: lfl.datetime)
# post_extract the shortened names
comn.shorteners.short_link_names.sort_main()
comn.shorteners.short_data_names.sort_main()
for plf in tree:
plf.post_extract_names()
# marshall connection facts
for rtr_list in comn.routers:
for rtr in rtr_list:
rtr.discover_connection_facts(comn)
# Back-propagate a router name/version/mode to each list's router0.
# Complain if container name or version changes between instances.
# Fill in container_id and shortened display_name tables
for fi in range(comn.n_logs):
rtrlist = comn.routers[fi]
if len(rtrlist) > 1:
if rtrlist[0].container_name is None:
rtrlist[0].container_name = rtrlist[1].container_name
if rtrlist[0].version is None:
rtrlist[0].version = rtrlist[1].version
if rtrlist[0].mode is None:
rtrlist[0].mode = rtrlist[1].mode
for i in range(0, len(rtrlist) - 1):
namei = rtrlist[i].container_name
namej = rtrlist[i + 1].container_name
if namei != namej:
sys.exit('Inconsistent container names, log file %s, instance %d:%s but instance %d:%s' %
(comn.log_fns[fi], i, namei, i + 1, namej))
namei = rtrlist[i].version
namej = rtrlist[i + 1].version
if namei != namej:
sys.exit('Inconsistent router versions, log file %s, instance %d:%s but instance %d:%s' %
(comn.log_fns[fi], i, namei, i + 1, namej))
namei = rtrlist[i].mode
namej = rtrlist[i + 1].mode
if namei != namej:
sys.exit('Inconsistent router modes, log file %s, instance %d:%s but instance %d:%s' %
(comn.log_fns[fi], i, namei, i + 1, namej))
name = rtrlist[0].container_name if len(rtrlist) > 0 and rtrlist[0].container_name is not None else ("Unknown_%d" % fi)
mode = rtrlist[0].mode if len(rtrlist) > 0 and rtrlist[0].mode is not None else "standalone"
comn.router_ids.append(name)
comn.router_display_names.append(comn.shorteners.short_rtr_names.translate(name))
comn.router_modes.append(mode)
# aggregate connection-to-frame maps into big map
for rtrlist in comn.routers:
for rtr in rtrlist:
comn.conn_to_frame_map.update(rtr.conn_to_frame_map)
# generate router-to-router connection peer relationships
peer_list = []
for plf in tree:
if plf.data.name == "open" and plf.data.direction_is_in():
cid = plf.data.conn_id # the router that generated this log file
if "properties" in plf.data.described_type.dict:
peer_conn = plf.data.described_type.dict["properties"].get(':"qd.conn-id"',
"") # router that sent the open
if peer_conn != "" and plf.data.conn_peer != "":
pid_peer = plf.data.conn_peer.strip('\"')
rtr, rtridx = router.which_router_id_tod(comn.routers, pid_peer, plf.datetime)
if rtr is not None:
pid = rtr.conn_id(peer_conn)
hit = sorted((cid, pid))
if hit not in peer_list:
peer_list.append(hit)
for (key, val) in peer_list:
if key in comn.conn_peers_connid:
sys.exit('key val messed up')
if val in comn.conn_peers_connid:
sys.exit('key val messed up')
comn.conn_peers_connid[key] = val
comn.conn_peers_connid[val] = key
cn_k = comn.router_ids[common.index_of_log_letter(key)]
cn_v = comn.router_ids[common.index_of_log_letter(val)]
comn.conn_peers_display[key] = comn.shorteners.short_rtr_names.translate(cn_v)
comn.conn_peers_display[val] = comn.shorteners.short_rtr_names.translate(cn_k)
# sort transfer short name customer lists
comn.shorteners.short_data_names.sort_customers()
comn.shorteners.short_link_names.sort_customers()
# compute settlement and index AMQP addresses
if not comn.args.skip_detail:
for rtrlist in comn.routers:
for rtr in rtrlist:
rtr.details.compute_settlement()
rtr.details.index_addresses()
rtr.details.evaluate_credit()
#
# Start producing the output stream
#
print(text.web_page_head())
#
# Generate javascript
#
# output the frame show/hide functions into the header
for conn_id, plfs in common.dict_iteritems(comn.conn_to_frame_map):
print("function show_%s() {" % conn_id)
for plf in plfs:
print(" javascript:show_node(\'%s\');" % plf.fid)
print("}")
print("function hide_%s() {" % conn_id)
for plf in plfs:
print(" javascript:hide_node(\'%s\');" % plf.fid)
print("}")
# manipulate checkboxes
print("function show_if_cb_sel_%s() {" % conn_id)
print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id)
print(" javascript:show_%s();" % conn_id)
print(" } else {")
print(" javascript:hide_%s();" % conn_id)
print(" }")
print("}")
print("function select_cb_sel_%s() {" % conn_id)
print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id)
print(" javascript:show_%s();" % conn_id)
print("}")
print("function deselect_cb_sel_%s() {" % conn_id)
print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id)
print(" javascript:hide_%s();" % conn_id)
print("}")
print("function toggle_cb_sel_%s() {" % conn_id)
print(" if (document.getElementById(\"cb_sel_%s\").checked) {" % conn_id)
print(" document.getElementById(\"cb_sel_%s\").checked = false;" % conn_id)
print(" } else {")
print(" document.getElementById(\"cb_sel_%s\").checked = true;" % conn_id)
print(" }")
print(" javascript:show_if_cb_sel_%s();" % conn_id)
print("}")
# Select/Deselect/Toggle All Connections functions
print("function select_all() {")
for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map):
print(" javascript:select_cb_sel_%s();" % conn_id)
print("}")
print("function deselect_all() {")
for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map):
print(" javascript:deselect_cb_sel_%s();" % conn_id)
print("}")
print("function toggle_all() {")
for conn_id, frames_ids in common.dict_iteritems(comn.conn_to_frame_map):
print(" javascript:toggle_cb_sel_%s();" % conn_id)
print("}")
#
print("</script>")
print("</head>")
print("<body>")
#
# Table of contents
print(text.web_page_toc())
# Report how much data was skipped if --no-data switch in effect
if comn.args.skip_all_data:
print("--skip-all-data switch is in effect. %d log lines skipped" % comn.data_skipped)
print("<p><hr>")
# file(s) included in this doc
print("<a name=\"c_logfiles\"></a>")
print("<h3>Log files</h3>")
print("<table><tr><th>Log</th> <th>Container name</th> <th>Version</th> <th>Mode</th>"
"<th>Instances</th> <th>Log file path</th></tr>")
for i in range(comn.n_logs):
rtrlist = comn.routers[i]
if len(rtrlist) > 0:
print("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" %
(common.log_letter_of(i), rtrlist[0].container_name, rtrlist[0].version, rtrlist[0].mode,
str(len(rtrlist)), os.path.abspath(comn.log_fns[i])))
else:
print("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" %
(common.log_letter_of(i), text.nbsp(), text.nbsp(),
str(len(rtrlist)), os.path.abspath(comn.log_fns[i])))
print("</table>")
print("<hr>")
# reboot chronology
print("<a name=\"c_rtrinstances\"></a>")
print("<h3>Router Reboot Chronology</h3>")
print("<table><tr><th>Log</th> <th>Time</th> <th>Container name</th> ")
for i in range(len(comn.routers)):
print("<td>%s</td>" % common.log_letter_of(i))
print("</tr>")
for rr in rr_tree:
print("<tr><td>%s</td><td>%s</td><td>%s</td>" %
(rr.router.iname, rr.datetime, rr.router.container_name))
for i in range(len(comn.routers)):
print("<td>%s</td> " % (rr.router.iname if i == rr.router.log_index else text.nbsp()))
print("</tr>")
print("</table>")
print("<hr>")
# print the connection peer tables
#
# +------+--------------------+-----+--------------------+-------+-------+----------+--------+-------+
# | View | Router | Dir | Peer | Log | N | Transfer | AMQP | unset |
# | +-----------+--------+ +--------+-----------+ lines | links | bytes | errors | tled |
# | | container | connid | | connid | container | | | | | |
# +------+-----------+--------+-----+--------+-----------+-------+-------+----------+--------+-------+
print("<a name=\"c_connections\"></a>")
print("<h3>Connections</h3>")
print("<p>")
print("<button onclick=\"javascript:select_all()\">Select All</button>")
print("<button onclick=\"javascript:deselect_all()\">Deselect All</button>")
print("<button onclick=\"javascript:toggle_all()\">Toggle All</button>")
print("</p>")
print("<h3>Connections by ConnectionId</h3>")
print(
"<table><tr> <th rowspan=\"2\">View</th> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Log lines</th> "
"<th rowspan=\"2\">N links</th><th rowspan=\"2\">Transfer bytes</th> %s </tr>" % amqp_detail.Counts.show_table_heads1())
print("<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th> %s </tr>" %
amqp_detail.Counts.show_table_heads2())
tConn = 0
tLines = 0
tBytes = 0
tErrs = 0
tLinks = 0
for rtrlist in comn.routers:
for rtr in rtrlist:
rid = rtr.container_name
for conn in rtr.conn_list:
tConn += 1
id = rtr.conn_id(conn) # this router's full connid 'A0_3'
peer = rtr.conn_peer_display.get(id, "") # peer container id
peerconnid = comn.conn_peers_connid.get(id, "")
n_links = rtr.details.links_in_connection(id)
tLinks += n_links
conn_details = rtr.details.conn_details[id]
tErrs += conn_details.counts.errors
print("<tr>")
print("<td> <input type=\"checkbox\" id=\"cb_sel_%s\" " % id)
print("checked=\"true\" onclick=\"javascript:show_if_cb_sel_%s()\"> </td>" % (id))
print("<td>%s</td><td><a href=\"#cd_%s\">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
"<td>%d</td><td>%s</td> %s </tr>" %
(rid, id, id, rtr.conn_dir[id], peerconnid, peer, rtr.conn_log_lines[id], n_links,
rtr.conn_xfer_bytes[id], conn_details.counts.show_table_data()))
tLines += rtr.conn_log_lines[id]
tBytes += rtr.conn_xfer_bytes[id]
print(
"<td>Total</td><td>%d</td><td> </td><td> </td><td> </td><td> </td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>" %
(tConn, tLines, tLinks, tBytes, tErrs))
print("</table>")
print("<a name=\"c_connectchrono\"></a>")
print("<h3>Router Restart and Connection chronology</h3>")
cl = []
for rtrlist in comn.routers:
for rtr in rtrlist:
rid = rtr.container_name
cl.append(common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime))
for conn in rtr.conn_list:
id = rtr.conn_id(conn)
if id in rtr.conn_open_time:
cl.append(common.RestartRec(id, rtr, "open", rtr.conn_open_time[id].datetime))
if id in rtr.conn_close_time:
cl.append(common.RestartRec(id, rtr, "close", rtr.conn_close_time[id].datetime))
cl = sorted(cl, key=lambda lfl: lfl.datetime)
print("<table><tr> <th>Time</th> <th>Id</th> <th>Event</th> <th>container</th> <th>connid</th> "
"<th>Dir</th> <th>connid</th> <th>container</th>")
for i in range(len(comn.routers)):
print("<td>%s</td>" % common.log_letter_of(i))
print("</tr>")
for c in cl:
if c.event == "restart":
rid = c.router.container_name
print("<tr><td>%s</td> <td>%s</td> <td><span style=\"background-color:yellow\">%s</span></td><td>%s</td> "
"<td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" %
(c.datetime, c.id, c.event, rid, "", "", "", ""))
for i in range(len(comn.routers)):
print("<td>%s</td> " % (c.id if i == c.router.log_index else text.nbsp()))
print("</tr>")
else:
rid = c.router.container_name
cdir = c.router.conn_dir[c.id]
peer = c.router.conn_peer_display.get(c.id, "") # peer container id
peerconnid = comn.conn_peers_connid.get(c.id, "")
print("<tr><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td> <td>%s</td><td>%s</td> <td>%s</td>" %
(c.datetime, c.id, c.event, rid, c.id, cdir, peerconnid, peer))
for i in range(len(comn.routers)):
print("<td>%s</td> " % (text.nbsp()))
print("</tr>")
print("</table>")
print("<hr>")
# address overview
print("<a name=\"c_addresses\"></a>")
print("<h3>AMQP Addresses Overview</h3>")
# compute with and without data lists
w_data = []
wo_data = []
for i in range(0, comn.shorteners.short_addr_names.len()):
sname = comn.shorteners.short_addr_names.shortname(i)
lname = comn.shorteners.short_addr_names.longnames[i]
links = comn.shorteners.short_addr_names.customers(sname)
showthis = ("<a href=\"javascript:toggle_node('@@addr_%d')\">%s</a>" %
(i, text.lozenge()))
visitthis = ("<a href=\"#@@addr_%d_data\">%s</a>" %
(i, lname))
n_frames = sum(len(linkd.frame_list) for linkd in links)
n_transfers = 0
for linkd in links:
n_transfers += sum(1 for plf in linkd.frame_list if plf.data.transfer)
n_unsettled = sum(linkd.counts.unsettled for linkd in links)
line = ("<tr><td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> <td>%d</td></tr>" %
(showthis, visitthis, len(links), n_frames, n_transfers, n_unsettled))
if n_transfers == 0:
wo_data.append(line)
else:
w_data.append(line)
# loop to print table with no expanded data
print("<h4>With transfer data (N=%d)</h4>" % len(w_data))
print("<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>")
for line in w_data:
print(line)
print("</table>")
print("<h4>With no transfer data (N=%d)</h4>" % len(wo_data))
print("<table><tr><th>Address</th> <th>N Links</th> <th>Frames</th> <th>Transfers</th> <th>Unsettled</th> </tr>")
for line in wo_data:
print(line)
print("</table>")
# loop to print expandable sub tables
print("<h3>AMQP Addresses Details</h3>")
for i in range(0, comn.shorteners.short_addr_names.len()):
sname = comn.shorteners.short_addr_names.shortname(i)
lname = comn.shorteners.short_addr_names.longnames[i]
links = comn.shorteners.short_addr_names.customers(sname)
print("<div id=\"@@addr_%d\" style=\"display:none; margin-top: 2px; margin-bottom: 2px; margin-left: 10px\">" %
(i))
print("<a name=\"@@addr_%d_data\"></a>" % (i))
print("<h4>Address %s - %s</h4>" % (sname, lname))
print("<table><tr> <th colspan=\"2\">Router</th> <th rowspan=\"2\">Dir</th> <th colspan=\"2\">Peer</th> <th rowspan=\"2\">Role</th> "
"<th rowspan=\"2\">Link</th> <th rowspan=\"2\">Frames</th> <th rowspan=\"2\">Transfers</th> <th rowspan=\"2\">Unsettled</th></tr>")
print("<tr> <th>container</th> <th>connid</th> <th>connid</th> <th>container</th></tr>")
for linkd in links:
# linkd # LinkDetail
sessd = linkd.session_detail # SessionDetail
connd = sessd.conn_detail # ConnectionDetail
rtr = connd.router # Router
conn = connd.conn # connection number type int
rid = rtr.container_name
id = rtr.conn_id(conn)
peer = rtr.conn_peer_display.get(id, "") # peer container id
peerconnid = comn.conn_peers_connid.get(id, "")
role = "receiver" if linkd.is_receiver else "sender"
transfers = sum(1 for plf in linkd.frame_list if plf.data.transfer)
showall = ("<a href=\"javascript:void(0)\" onclick=\"show_node(%s_data); show_node(%s_sess_%s); show_node(%s_sess_%s_link_%s)\">%s</a>" %
(id, id, sessd.conn_epoch, id, sessd.conn_epoch, linkd.session_seq, text.lozenge()))
visitthis = ("<a href=\"#%s_sess_%s_link_%s_data\">%s</a>" %
(id, sessd.conn_epoch, linkd.session_seq, linkd.display_name))
print("<tr> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s %s</td> <td>%d</td> <td>%d</td> <td>%d</td> </tr>" %
(rid, id, rtr.conn_dir[id], peerconnid, peer, role, showall, visitthis, len(linkd.frame_list), transfers, linkd.counts.unsettled))
print("</table>")
print("</div>")
print("<hr>")
# connection details
print("<a name=\"c_conndetails\"></a>")
print("<h3>Connection Details</h3>")
if not comn.args.skip_detail:
for rtrlist in comn.routers:
for rtr in rtrlist:
rtr.details.show_html()
else:
print("details suppressed<br>")
print("<hr>")
# noteworthy log lines: highlight errors and stuff
print("<a name=\"c_noteworthy\"></a>")
print("<h3>Noteworthy</h3>")
n_errors = 0
n_settled = 0
n_more = 0
n_resume = 0
n_aborted = 0
n_drain = 0
n_unsettled = 0
n_unsettled_no_parent = 0 # without link/session can't match xfers with dispositions
for plf in tree:
if plf.data.amqp_error:
n_errors += 1
if plf.data.transfer_settled:
n_settled += 1
if plf.data.transfer_more:
n_more += 1
if plf.data.transfer_resume:
n_resume += 1
if plf.data.transfer_aborted:
n_aborted += 1
if plf.data.flow_drain:
n_drain += 1
if common.transfer_is_possibly_unsettled(plf):
if plf.data.no_parent_link:
n_unsettled_no_parent += 1 # possibly unsettled
else:
n_unsettled += 1 # probably unsettled
# amqp errors
print("<a href=\"javascript:toggle_node('noteworthy_errors')\">%s%s</a> AMQP errors: %d<br>" %
(text.lozenge(), text.nbsp(), n_errors))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_errors\">")
for plf in tree:
if plf.data.amqp_error:
show_noteworthy_line(plf, comn)
print("</div>")
# transfers with settled=true
print("<a href=\"javascript:toggle_node('noteworthy_settled')\">%s%s</a> Presettled transfers: %d<br>" %
(text.lozenge(), text.nbsp(), n_settled))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_settled\">")
for plf in tree:
if plf.data.transfer_settled:
show_noteworthy_line(plf, comn)
print("</div>")
# transfers with more=true
print("<a href=\"javascript:toggle_node('noteworthy_more')\">%s%s</a> Partial transfers with 'more' set: %d<br>" %
(text.lozenge(), text.nbsp(), n_more))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_more\">")
for plf in tree:
if plf.data.transfer_more:
show_noteworthy_line(plf, comn)
print("</div>")
# transfers with resume=true, whatever that is
print("<a href=\"javascript:toggle_node('noteworthy_resume')\">%s%s</a> Resumed transfers: %d<br>" %
(text.lozenge(), text.nbsp(), n_resume))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_resume\">")
for plf in tree:
if plf.data.transfer_resume:
show_noteworthy_line(plf, comn)
print("</div>")
# transfers with abort=true
print("<a href=\"javascript:toggle_node('noteworthy_aborts')\">%s%s</a> Aborted transfers: %d<br>" %
(text.lozenge(), text.nbsp(), n_aborted))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_aborts\">")
for plf in tree:
if plf.data.transfer_aborted:
show_noteworthy_line(plf, comn)
print("</div>")
# flow with drain=true
print("<a href=\"javascript:toggle_node('noteworthy_drain')\">%s%s</a> Flow with 'drain' set: %d<br>" %
(text.lozenge(), text.nbsp(), n_drain))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_drain\">")
for plf in tree:
if plf.data.flow_drain:
show_noteworthy_line(plf, comn)
print("</div>")
# probable unsettled transfers
print("<a href=\"javascript:toggle_node('noteworthy_unsettled')\">%s%s</a> Probable unsettled transfers: %d<br>" %
(text.lozenge(), text.nbsp(), n_unsettled))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_unsettled\">")
for plf in tree:
if common.transfer_is_possibly_unsettled(plf) and not plf.data.no_parent_link:
show_noteworthy_line(plf, comn)
print("</div>")
# possible unsettled transfers
print("<a href=\"javascript:toggle_node('noteworthy_unsettled_qm')\">%s%s</a> Possible unsettled transfers: %d<br>" %
(text.lozenge(), text.nbsp(), n_unsettled_no_parent))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"noteworthy_unsettled_qm\">")
for plf in tree:
if common.transfer_is_possibly_unsettled(plf) and plf.data.no_parent_link:
show_noteworthy_line(plf, comn)
print("</div>")
print("<hr>")
# the proton log lines
# log lines in f_A_116
# log line details in f_A_116_d
print("<a name=\"c_logdata\"></a>")
print("<h3>Log data</h3>")
for plf in tree:
l_dict = plf.data.described_type.dict
print("<div width=\"100%%\" style=\"display:block margin-bottom: 2px\" id=\"%s\">" % plf.fid)
print("<a name=\"%s\"></a>" % plf.fid)
detailname = plf.fid + "_d" # type: str
loz = "<a href=\"javascript:toggle_node('%s')\">%s%s</a>" % (detailname, text.lozenge(), text.nbsp())
rtr = plf.router
rid = comn.router_display_names[rtr.log_index]
peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "")
peer = rtr.conn_peer_display.get(plf.data.conn_id, "") # peer container id
print(loz, plf.datetime, ("%s#%d" % (plf.prefixi, plf.lineno)), rid, ("[%s]" % plf.data.conn_id),
plf.data.direction, ("[%s]" % peerconnid), peer,
plf.data.web_show_str, plf.data.disposition_display, "<br>")
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"%s\">" %
detailname)
for key in sorted(common.dict_iterkeys(l_dict)):
val = l_dict[key]
print("%s : %s <br>" % (key, common.html_escape(str(val))))
if plf.data.name == "transfer":
print("Header and annotations : %s <br>" % plf.data.transfer_hdr_annos)
print("</div>")
print("</div>")
print("<hr>")
# data traversing network
print("<a name=\"c_messageprogress\"></a>")
print("<h3>Message progress</h3>")
if not comn.args.skip_msg_progress:
for i in range(0, comn.shorteners.short_data_names.len()):
sname = comn.shorteners.short_data_names.shortname(i)
size = 0
for plf in comn.shorteners.short_data_names.customers(sname):
size = plf.data.transfer_size
break
print("<a name=\"%s\"></a> <h4>%s (%s)" % (sname, sname, size))
print(" <span> <a href=\"javascript:toggle_node('%s')\"> %s</a>" % ("data_" + sname, text.lozenge()))
print(" <div width=\"100%%\"; style=\"display:none; font-weight: normal; margin-bottom: 2px\" id=\"%s\">" %
("data_" + sname))
print(" ", comn.shorteners.short_data_names.longname(i, True))
print("</div> </span>")
print("</h4>")
print("<table>")
print(
"<tr><th>Src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId</th> <th>Peer</th> "
"<th>T delta</th> <th>T elapsed</th><th>Settlement</th><th>S elapsed</th></tr>")
t0 = None
tlast = None
for plf in comn.shorteners.short_data_names.customers(sname):
if t0 is None:
t0 = plf.datetime
tlast = plf.datetime
delta = "0.000000"
epsed = "0.000000"
else:
delta = time_offset(plf.datetime, tlast)
epsed = time_offset(plf.datetime, t0)
tlast = plf.datetime
sepsed = ""
if plf.data.final_disposition is not None:
sepsed = time_offset(plf.data.final_disposition.datetime, t0)
rid = plf.router.iname
peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "")
peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id
print("<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> "
"<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> </tr>" %
(plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction,
peerconnid, peer, delta, epsed,
plf.data.disposition_display, sepsed))
print("</table>")
print("<hr>")
# link names traversing network
print("<a name=\"c_linkprogress\"></a>")
print("<h3>Link name propagation</h3>")
for i in range(0, comn.shorteners.short_link_names.len()):
if comn.shorteners.short_link_names.len() == 0:
break
sname = comn.shorteners.short_link_names.prefixname(i)
print("<a name=\"%s\"></a> <h4>%s" % (sname, sname))
print(" <span> <div width=\"100%%\"; style=\"display:block; font-weight: normal; margin-bottom: 2px\" >")
print(comn.shorteners.short_link_names.longname(i, True))
print("</div> </span>")
print("</h4>")
print("<table>")
print("<tr><th>src</th> <th>Time</th> <th>Router</th> <th>ConnId</th> <th>Dir</th> <th>ConnId> <th>Peer</th> "
"<th>T delta</th> <th>T elapsed</th></tr>")
t0 = None
tlast = None
for plf in comn.shorteners.short_link_names.customers(sname):
if t0 is None:
t0 = plf.datetime
delta = "0.000000"
epsed = "0.000000"
else:
delta = time_offset(plf.datetime, tlast)
epsed = time_offset(plf.datetime, t0)
tlast = plf.datetime
rid = plf.router.iname
peerconnid = "%s" % comn.conn_peers_connid.get(plf.data.conn_id, "")
peer = plf.router.conn_peer_display.get(plf.data.conn_id, "") # peer container id
print("<tr><td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> "
"<td>%s</td> <td>%s</td> <td>%s</td></tr>" %
(plf.adverbl_link_to(), plf.datetime, rid, plf.data.conn_id, plf.data.direction, peerconnid, peer,
delta, epsed))
print("</table>")
print("<hr>")
# short data index
print("<a name=\"c_rtrdump\"></a>")
comn.shorteners.short_rtr_names.htmlDump(False)
print("<hr>")
print("<a name=\"c_peerdump\"></a>")
comn.shorteners.short_peer_names.htmlDump(False)
print("<hr>")
print("<a name=\"c_linkdump\"></a>")
comn.shorteners.short_link_names.htmlDump(True)
print("<hr>")
print("<a name=\"c_msgdump\"></a>")
comn.shorteners.short_data_names.htmlDump(with_link=True, log_strings=True)
print("<hr>")
# link state info
# merge link state and restart records into single time based list
cl = []
for rtrlist in comn.routers:
for rtr in rtrlist:
rid = rtr.container_name
cl.append(common.RestartRec(rtr.iname, rtr, "restart", rtr.restart_rec.datetime))
for plf in ls_tree:
if "costs" in plf.line:
cl.append(common.RestartRec("ls", plf, "ls", plf.datetime))
cl = sorted(cl, key=lambda lfl: lfl.datetime)
# create a map of lists for each router
# the list holds the name of other routers for which the router publishes a cost
costs_pub = {}
for i in range(0, comn.n_logs):
costs_pub[comn.router_ids[i]] = []
# cur_costs is a 2D array of costs used to tell when cost calcs have stabilized
# Each incoming LS cost line replaces a row in this table
# cur_costs tracks only interior routers
interior_rtrs = []
for rtrs in comn.routers:
if rtrs[0].is_interior():
interior_rtrs.append(rtrs[0].container_name)
PEER_COST_REBOOT = -1
PEER_COST_ABSENT = 0
def new_costs_row(val):
"""
return a costs row.
:param val: -1 when router reboots, 0 when router log line processed
:return:
"""
res = {}
for rtr in interior_rtrs:
res[rtr] = val
return res
cur_costs = {}
for rtr in interior_rtrs:
cur_costs[rtr] = new_costs_row(PEER_COST_REBOOT)
print("<a name=\"c_ls\"></a>")
print("<h3>Routing link state</h3>")
print("<h4>Link state costs</h4>")
print("<table>")
print("<tr><th>Time</th> <th>Router</th>")
for i in range(0, comn.n_logs):
print("<th>%s</th>" % common.log_letter_of(i))
print("</tr>")
for c in cl:
if c.event == "ls":
# link state computed costs and router reachability
plf = c.router # cruel overload here: router is a parsed line not a router
# Processing: Computed costs: {u'A': 1, u'C': 51L, u'B': 101L}
print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno))))
try:
line = plf.line
sti = line.find("{")
line = line[sti:]
try:
l_dict = common.ls_eval(line)
except:
traceback.print_exc()
sys.stderr.write("Failed to parse router %s, line %d : %s. Analysis continuing...\n" % (plf.router.iname, plf.lineno, plf.line))
l_dict = {}
costs_row = new_costs_row(PEER_COST_ABSENT)
for i in range(0, comn.n_logs):
if len(comn.routers[i]) > 0:
tst_name = comn.routers[i][0].container_name
if tst_name in l_dict:
val = l_dict[tst_name]
costs_row[tst_name] = val
elif i == plf.router.log_index:
val = text.nbsp()
else:
val = "<span style=\"background-color:orange\">%s</span>" % (text.nbsp() * 2)
else:
val = "<span style=\"background-color:orange\">%s</span>" % (text.nbsp() * 2)
print("<td>%s</td>" % val)
# track costs published when there is no column to put the number
tgts = costs_pub[c.router.router.container_name]
for k, v in common.dict_iteritems(l_dict):
if k not in comn.router_ids:
if k not in tgts:
tgts.append(k) # this cost went unreported
# update this router's cost view in running table
if plf.router.is_interior():
cur_costs[plf.router.container_name] = costs_row
except:
raise
print("</tr>")
# if the costs are stable across all routers then put an indicator in table
costs_stable = True
for c_rtr in interior_rtrs:
for r_rtr in interior_rtrs:
if r_rtr != c_rtr \
and (cur_costs[r_rtr][c_rtr] != cur_costs[c_rtr][r_rtr]
or cur_costs[c_rtr][r_rtr] <= PEER_COST_ABSENT):
costs_stable = False
break
if not costs_stable:
break
if costs_stable:
print("<tr><td><span style=\"background-color:green\">stable</span></td></tr>")
else:
# restart
print("<tr><td>%s</td> <td>%s</td>" % (c.datetime, ("%s restart" % (c.router.iname))))
for i in range(0, comn.n_logs):
color = "green" if i == c.router.log_index else "orange"
print("<td><span style=\"background-color:%s\">%s</span></td>" % (color, text.nbsp() * 2))
print("</tr>")
if c.router.is_interior():
cur_costs[c.router.container_name] = new_costs_row(PEER_COST_REBOOT)
print("</table>")
print("<br>")
# maybe display cost declarations that were not displayed
costs_clean = True
for k, v in common.dict_iteritems(costs_pub):
if len(v) > 0:
costs_clean = False
break
if not costs_clean:
print("<h4>Router costs declared in logs but not displayed in Link state cost table</h4>")
print("<table>")
print("<tr><th>Router</th><Peers whose logs are absent</th></tr>")
for k, v in common.dict_iteritems(costs_pub):
if len(v) > 0:
print("<tr><td>%s</td><td>%s</td></tr>" % (k, str(v)))
print("</table>")
print("<br>")
print("<a href=\"javascript:toggle_node('ls_costs')\">%s%s</a> Link state costs data<br>" %
(text.lozenge(), text.nbsp()))
print(" <div width=\"100%%\"; "
"style=\"display:none; font-weight: normal; margin-bottom: 2px; margin-left: 10px\" "
"id=\"ls_costs\">")
print("<table>")
print("<tr><th>Time</th> <th>Router</th> <th>Name</th> <th>Log</th></tr>")
for plf in ls_tree:
if "costs" in plf.line:
print("<tr><td>%s</td> <td>%s</td>" % (plf.datetime, ("%s#%d" % (plf.router.iname, plf.lineno))))
print("<td>%s</td>" % plf.router.container_name)
print("<td>%s</td></tr>" % plf.line)
print("</table>")
print("</div>")
print("<hr>")
# Emit data for source to be processed by seq-diag-gen utility
if comn.args.sequence:
print("<a name=\"c_sequence\"></a>")
print("<h3>sequence diagram data</h3>")
for plf in tree:
rtr = plf.router
rid = comn.router_display_names[rtr.log_index]
peer = rtr.conn_peer_display.get(plf.data.conn_id, "")
if peer.startswith("<"):
peer = peer[peer.find(">") + 1:]
peer = peer[:peer.find("<")]
# TODO: differentiate between peer sender and peer receiver
# This is not so easy as test code uses the same container id for all connections
# Easiest thing to do is append peer with router_id. Does not fix tests with sender/receiver
# to same router.
if peer.startswith("peer"):
peer += "_" + rid
# TODO handle EOS, connection open/close lines
if (not plf.data.sdorg_str == "" and
not plf.data.direction == "" and
not plf.data.sdorg_str.startswith("HELP")):
print("%s|%s|%s|%s|%s|%s|<br>" % (plf.datetime, rid, plf.data.direction, peer, plf.data.sdorg_str, ("%s#%d" % (plf.prefixi, plf.lineno))))
else:
if plf.data.is_scraper:
print("%s|%s|%s|%s|%s|%s|<br>" % (plf.datetime, rid, "->", rid, plf.data.sdorg_str,
("%s#%d" % (plf.prefixi, plf.lineno))))
#import pdb
# pdb.set_trace()
print("<hr>")
print("</body>")
def main(argv):
try:
main_except(argv)
return 0
except Exception as e:
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv))