#!/bin/bash ### ### IETFMEASUREMENTS - Get various AD work load and past performance statistics ### ### Version 1.0.0 ### ### Written in 2007-2011 by Jari Arkko ### Donated to the public domain. ### ### Changelog: ### ### 1.0.0 - Initial implementation ### 1.0.1 - Template improvements, html cleanup ### 2.0.0 - Added RFC publication speed measurements in millibits ### ### Usage: ### ### ietfmeasurements [ --ad pattern ] [ --doc pattern ] [ --copy ] [ --quiet ] [ --quick ] ### ### ### Process arguments ### THISPROG=$0 DEBUG=0 AD=".*" DOC=".*" COPY=0 QUIET=0 QUICK=0 FETCH=1 RUNCALC=1 TIMESTAMP=0 PREFFAMILY=IPv6 while [ $# -gt 0 ] do case $1 in --ad) shift; AD="$1"; shift;; --doc) shift; DOC="$1"; shift;; --copy) shift; COPY=1;; --quiet) shift; QUIET=1;; --quick) shift; QUICK=1;; --debug) shift; DEBUG=1;; --nofetch) shift; FETCH=0;; --nocalc) shift; RUNCALC=0;; --timestamp) shift; TIMESTAMP=1;; --prefer-family=IPv4) shift; PREFFAMILY=IPv4;; --prefer-family=IPv6) shift; PREFFAMILY=IPv6;; *) echo 'ietfmeasurements: Unrecognized argument ('$1') -- exit'; exit 1;; esac done ### ### Initialize ### LANG=en export LANG #LC_ALL=en #export LC_ALL TIMINGDATABASEURL=http://www.arkko.com/tools/admeasurements/draft-timing.txt NONADTIMINGDATABASEURL=http://www.arkko.com/tools/admeasurements/draft-timing-nonad.txt ADDRAFTDATABASEURL=http://arkko.com/tools/admeasurements/adwork.txt CURYEAR=`date +%Y` CURMON=`date +%m` CURDAY=`date +%d` if [ $CURDAY -le 22 ] then if [ $CURMON -eq 1 ] then CURYEARCOMPLETE=`expr $CURYEAR - 1` CURMONCOMPLETE=12 else CURYEARCOMPLETE=$CURYEAR CURMONCOMPLETE=`expr $CURMON - 1` fi else CURYEARCOMPLETE=$CURYEAR CURMONCOMPLETE=$CURMON fi tmpdir=/tmp/im_tmp if [ $FETCH = 1 ] then rm -rf $tmpdir mkdir $tmpdir else rm -f $tmpdir/*.html fi cd $tmpdir WGETOPTS="--prefer-family="$PREFFAMILY" -q" export WGETOPTS if [ $TIMESTAMP = 1 ] then echo 'Running ietfmeasurements on '`date`'...' fi ### ### Fetch database ### if [ $FETCH = 1 ] then echo Fetching database files... if wget $WGETOPTS -O draft-timing.txt $TIMINGDATABASEURL then nop=nop else echo 'ietfmeasurements: cannot access '$TIMINGDATABASEURL' - exit' exit 1 fi if wget $WGETOPTS -O draft-timing-nonad.txt $NONADTIMINGDATABASEURL then nop=nop else echo 'ietfmeasurements: cannot access '$NONADTIMINGDATABASEURL' - exit' exit 1 fi if wget $WGETOPTS -O adwork.txt $ADDRAFTDATABASEURL then nop=nop else echo 'ietfmeasurements: cannot access '$ADDRAFTDATABASEURL' - exit' exit 1 fi fi ### ### Quick select of the relevant docs ### if [ $RUNCALC = 1 ] then cat draft-timing.txt | grep -e "$DOC" > draft-timing-selected.txt mv draft-timing.txt draft-timing-orig.txt mv draft-timing-selected.txt draft-timing.txt cat draft-timing-nonad.txt | grep -e "$DOC" > draft-timing-nonad-selected.txt mv draft-timing-nonad.txt draft-timing-nonad-orig.txt mv draft-timing-nonad-selected.txt draft-timing-nonad.txt fi ### ### FUNCTION: Common AWK functions ### COMMONFUNCS=' function draftnameisofficial(d) { return(d ~ /^draft-ietf-/ || d ~ /^draft-iab-/ || d ~ /^draft-iesg-/ || d ~ /^draft-irtf-/); } function dateyear(d) { split(d,d1a,"-"); return(d1a[1]); } function quarter(d) { mo = datemonth(d); if (mo <= 3) mo = 1; else if (mo <= 6) mo = 4; else if (mo <= 9) mo = 7; else mo = 10; return(dateyear(d) "-" mo); } function quartername(d) { if (datemonth(d) <= 3) na = "Q1"; else if (datemonth(d) <= 6) na = "Q2"; else if (datemonth(d) <= 9) na = "Q3"; else na = "Q4"; return(na " " dateyear(d)); } function quarternumeric(d) { if (datemonth(d) <= 3) nu = 0.0; else if (datemonth(d) <= 6) nu = 0.25; else if (datemonth(d) <= 9) nu = 0.5; else nu = 0.75; return(dateyear(d) + nu); } function datemonth(d) { split(d,d1a,"-"); return(d1a[2]); } function dateyearmonth(d) { return(dateyear(d) "-" datemonth(d)); } function addmonth(d) { y = dateyear(d); m = datemonth(d); if (m == 12) return(sprintf("%04d-%02d",y+1,1)); else return(sprintf("%04d-%02d",y,m+1)); } function datelt(d1,d2) { split(d1,d1a,"-"); split(d2,d2a,"-"); y1 = d1a[1] + 0; m1 = d1a[2] + 0; d1 = d1a[3] + 0; y2 = d2a[1] + 0; m2 = d2a[2] + 0; d2 = d2a[3] + 0; if (y1 < y2) return(1); if (y1 > y2) return(0); if (m1 < m2) return(1); if (m1 > m2) return(0); if (d1 < d2) return(1); if (d1 > d2) return(0); return(0); } function datele(d1,d2) { split(d1,d1a,"-"); split(d2,d2a,"-"); y1 = d1a[1] + 0; m1 = d1a[2] + 0; d1 = d1a[3] + 0; y2 = d2a[1] + 0; m2 = d2a[2] + 0; d2 = d2a[3] + 0; if (y1 < y2) return(1); if (y1 > y2) return(0); if (m1 < m2) return(1); if (m1 > m2) return(0); if (d1 < d2) return(1); if (d1 > d2) return(0); return(1); } function html_file_css_head(title,csshtmlfile,slogan) { csshtmlfileseensection[csshtmlfile] = 0; if (substr(csshtmlfile,1,5) != "/dev/") close(csshtmlfile); printf("\n", 34, 34, 34, 34) > csshtmlfile; printf("\n", 34, 34, 34, 34, 34, 34) >> csshtmlfile; printf("
\n") >> csshtmlfile; printf("\n", 34, 34, 34, 34) >> csshtmlfile; printf("\n", 34, 34, 34, 34, 34, 34) >> csshtmlfile; printf("%s:
\n", $3); next; } /^timing:.*/ { draft = $2; split($0,comps,":"); unittouse = (overrideunit == "" ? unit : overrideunit); if (unittouse ~ /millibit/) { printf(""; disclaimer = disclaimer sprintf("Statistics run taken %d.%d.%d by the ", curday, curmonth, curyear); disclaimer = disclaimer sprintf("ietfmeasurements", 34, 34); disclaimer = disclaimer " tool."; disclaimer = disclaimer "
\n"; disclaimer = disclaimer "\n"; disclaimer = disclaimer "Please be aware that this tool measures only what has been entered to the tracker."; disclaimer = disclaimer " This often does not correspond to reality."; disclaimer = disclaimer "
\n"; html_file_css_head("Lifecycle Analysis of WG RFC Production","/dev/stdout","End-to-End Delay from draft-smith to an RFC"); developmentgraph("Development over the Years",0); developmentgraph("Development over the Years, WG Phase Only",1); developmentgraph("Development over the Years, IESG Phase Only",2); developmentgraph("Development over the Years, RFC Editor Phase Only",3); } END { html_file_css_section("Data Freshness and Reliability","/dev/stdout"); printf("%s\n", disclaimer); html_file_css_end("/dev/stdout"); } ' < /dev/null > $htmlfile } ### ### FUNCTION: List lifecycles for a set of given drafts ### function listdraftlifecycles() { heading="$1"; inputfile=$2; outputfile=$3; sort < $inputfile | awk -v heading="$heading" ' '"$COMMONFUNCS"' BEGIN { FS = ":"; html_file_css_head(heading,"/dev/stdout","End-to-End Delay from draft-smith to an RFC"); currentheading = ""; } /^timing:/ { draft = $2; if (draft ~ /^draft-ietf-/) { split(draft,comps,"-"); thisheading = "ietf-" comps[3]; } else { thisheading = substr(draft,7,1); } if (currentheading != thisheading) { currentheading = thisheading; if (currentheading == "ietf-mobike") { html_file_css_section_nextcol(thisheading,"/dev/stdout"); } else { html_file_css_section(thisheading,"/dev/stdout"); } } printf("The following graph shows the distribution of processing time for categories\n"); printf("of 0 to 30 days, 30 to 60 days, ... and 570 to infinity days.\n"); printf("All WG documents published as RFCs are included in the count, if they were processed by one of the current IESG members.
\n"); cmdfilereal = fieldindex "-" cmdfile; printf("set output %c%s%c\n", 34, fieldindex "-" epsfile, 34) > cmdfilereal; printf("set terminal postscript eps enhanced color solid\n", 34, 34) >> cmdfilereal; printf("set style data boxes\n") >> cmdfilereal; printf("set title %c%s Time%c\n", 34, title, 34) >> cmdfilereal; printf("set xlabel %cDays%c\n", 34, 34) >> cmdfilereal; printf("set ylabel %cNumber of documents%c\n", 34, 34) >> cmdfilereal; printf("set style fill solid .75 noborder\n") >> cmdfilereal; printf("set boxwidth 0.8 relative\n") >> cmdfilereal; printf("set size 2.3,1.7\n") >> cmdfilereal; printf("set pointsize 2\n") >> cmdfilereal; printf("set view ,,2\n") >> cmdfilereal; printf("set xtics (%c0-30%c 0, %c270-300%c 10, %c570-%c 20)\n", 34, 34, 34, 34, 34, 34) >> cmdfilereal; printf("plot %c%s%c using %d notitle\n", 34, datafile, 34, fieldindex) >> cmdfilereal; close(cmdfilereal); plottojpg(cmdfilereal,fieldindex "-" epsfile,fieldindex "-" jpgfile); printf("\n", 34, fieldindex "-" jpgfile, 34, 34, 34); } BEGIN { FS = ":"; disclaimer = ""; disclaimer = disclaimer sprintf("Statistics run taken %d.%d.%d by the ", curday, curmonth, curyear); disclaimer = disclaimer sprintf("ietfmeasurements", 34, 34); disclaimer = disclaimer " tool."; disclaimer = disclaimer "
\n"; disclaimer = disclaimer "\n"; disclaimer = disclaimer "Please be aware that this tool measures only what has been entered to the tracker."; disclaimer = disclaimer " This often does not correspond to reality."; disclaimer = disclaimer "
\n"; html_file_css_head("Distribution of Processing Times","/dev/stdout","End-to-End Delay from draft-smith to an RFC"); distributiongraph("WG and individual",4); distributiongraph("IESG",5); distributiongraph("RFC Editor",6); } END { html_file_css_section("Data Freshness and Reliability","/dev/stdout"); printf("%s\n", disclaimer); html_file_css_end("/dev/stdout"); } ' < /dev/null > $htmlfile } ### ### Make the main report ### if [ $RUNCALC = 1 ] then echo Calculating lifecycle limits... limitlifecycletiming echo Calculating lifecycle times... datalifecycletiming draft-timing-limited-wg.txt timing-development-wg.dat timing-development-wg-distribution.dat fi echo Reporting timing development... reporttiming timing-development-wg wgdocs.html echo Reporting timing distribution... reporttimingdistribution timing-development-wg-distribution wgdistr.html ### ### Make the report of shortest and longest process times ### echo Sorting results... sort -n --key=9,9 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyiesg.txt sort -n --key=15,15 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbywg.txt sort -n --key=17,17 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyrfced.txt sort -n --key=18,18 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyindwg.txt sort -n --key=19,19 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyoverall.txt sort -n --key=23,23 --field-separator=: < draft-timing-limited.txt > draft-timing-limited-sortbypages.txt sort -n --key=24,24 --field-separator=: < draft-timing-limited.txt > draft-timing-limited-sortbybytes.txt sort -n --key=25,25 --field-separator=: < draft-timing-limited.txt > draft-timing-limited-sortbybitspeed.txt awk ' BEGIN { FS = ":"; } /^timing:.*/ { if ($13 != "") printf("%s\n", $0); } ' < draft-timing-limited-wg.txt | sort -n --key=12,12 --field-separator=: > draft-timing-limited-wg-sortbyind.txt awk ' '"$COMMONFUNCS"' BEGIN { FS = ":"; } /^timing:.*/ { if ($13 == "") printf("%s\n", $0); } ' < draft-timing-limited-ind.txt | sort -n --key=18,18 --field-separator=: > draft-timing-limited-ind-sortbyindad.txt sort -n --key=19,19 --field-separator=: < draft-timing-limited-ind.txt > draft-timing-limited-ind-sortbyoverall.txt echo Finding extreme results... ( echo 'ietfmeasurements:header:Overall Process Time (WG Drafts):19' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbyoverall.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbyoverall.txt; echo 'ietfmeasurements:header:Overall Process Time (Individual Drafts):19' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-ind-sortbyoverall.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-ind-sortbyoverall.txt; echo 'ietfmeasurements:header:Individual and WG Process Time:18' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbyindwg.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbyindwg.txt; echo 'ietfmeasurements:header:Wait Before WG Adoption (WG Drafts):12' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbyind.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbyind.txt; echo 'ietfmeasurements:header_nextcol:Wait Before AD Adoption (Individual Submissions):18' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-ind-sortbyindad.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-ind-sortbyindad.txt; echo 'ietfmeasurements:header:WG Process Time:15' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbywg.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbywg.txt echo 'ietfmeasurements:header:IESG Process Time:9' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbyiesg.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbyiesg.txt echo 'ietfmeasurements:header:RFC Editor Process Time:17' echo 'ietfmeasurements:subheader:Shortest' head -25 draft-timing-limited-wg-sortbyrfced.txt; echo 'ietfmeasurements:subheader:Longest' tail -25 draft-timing-limited-wg-sortbyrfced.txt ) | reportextremes "Shortest and Processing Times" "days" > extremes.html ( echo 'ietfmeasurements:header:Number of pages (All Drafts):23:pages' echo 'ietfmeasurements:subheader:Shortest' head -50 draft-timing-limited-sortbypages.txt; echo 'ietfmeasurements:subheader:Longest' tail -50 draft-timing-limited-sortbypages.txt; echo 'ietfmeasurements:header:Number of bytes (All Drafts):24:bytes' echo 'ietfmeasurements:subheader:Shortest' head -50 draft-timing-limited-sortbybytes.txt; echo 'ietfmeasurements:subheader:Longest' tail -50 draft-timing-limited-sortbybytes.txt; echo 'ietfmeasurements:header_nextcol:Publication speed in millibits/s (All Drafts):25:millibits/s' echo 'ietfmeasurements:subheader:Slowest' head -150 draft-timing-limited-sortbybitspeed.txt; echo 'ietfmeasurements:subheader:Fastest' tail -150 draft-timing-limited-sortbybitspeed.txt; ) | reportextremes "Document Size and Processing Times" "none" > sizematters.html sort -n --key=10,10 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyiesgrevs.txt sort -n --key=20,20 --field-separator=: < draft-timing-limited-wg.txt > draft-timing-limited-wg-sortbyrevs.txt awk ' BEGIN { FS = ":"; } /^timing:.*/ { if ($20 > 1) printf("%s\n", $0); } ' < draft-timing-limited-wg.txt | (LC_ALL=C; export LC_ALL; sort -g --key=21,21 --field-separator=:) > draft-timing-limited-wg-sortbyrevperiod.txt ( echo 'ietfmeasurements:header:Document Revisions in IESG Phase:10' echo 'ietfmeasurements:subheader:Most' tail -30 draft-timing-limited-wg-sortbyiesgrevs.txt; echo 'ietfmeasurements:header:Document Revisions:20' echo 'ietfmeasurements:subheader:Least' head -30 draft-timing-limited-wg-sortbyrevs.txt; echo 'ietfmeasurements:subheader:Most' tail -30 draft-timing-limited-wg-sortbyrevs.txt; echo 'ietfmeasurements:header_nextcol:Average Time Between Revisions:21:days' echo 'ietfmeasurements:subheader:Shortest' head -40 draft-timing-limited-wg-sortbyrevperiod.txt; echo 'ietfmeasurements:subheader:Longest' tail -40 draft-timing-limited-wg-sortbyrevperiod.txt; ) | reportextremes "Exceptional Drafts" "revisions" > exceptional.html ### ### Make the listing of all published RFCs, drafts and their lifecycles ### echo Making lists of drafts... listdraftlifecycles "Lifecycle Analysis of Published RFCs" draft-timing-limited.txt rfcs.html listdraftlifecycles "Lifecycle Analysis of All Drafts" draft-timing-limited-all.txt drafts.html ### ### Copy ### echo Copying results... if [ $COPY = 1 ] then if [ $QUIET = 0 ] then SCPOPTS= else SCPOPTS=-q fi scp $SCPOPTS \ /home/jar/LME/IETF/Tools/lifecyclestats.html \ jarkko@users.piuha.net:public_html/tools/ scp $SCPOPTS \ draft-timing-limited-all.txt \ jarkko@users.piuha.net:public_html/tools/draft-timing-withspeeds.txt scp $SCPOPTS \ drafts.html \ rfcs.html \ wgdocs.html \ wgdistr.html \ extremes.html \ sizematters.html \ exceptional.html \ *.jpg \ jarkko@users.piuha.net:public_html/tools/lifecycle fi ### ### Cleanup ### exit 0