nwdiag-1.0.0/0000755000175000017500000000000012224001064014164 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/buildout.cfg0000644000175000017500000000244712222275255016520 0ustar katsuwokatsuwo00000000000000[buildout] parts = nwdiag test coverage tox pyflakes pylint develop = . [nwdiag] recipe = zc.recipe.egg eggs = nwdiag blockdiag interpreter = py [test] recipe = pbp.recipe.noserunner eggs = nwdiag[rst] nwdiag[test] coverage unittest-xml-reporting [coverage] recipe = zc.recipe.egg eggs = coverage [tox] recipe = zc.recipe.egg eggs = tox detox [pyflakes] recipe = zc.recipe.egg eggs = pyflakes scripts = pyflakes entry-points = pyflakes=pyflakes.scripts.pyflakes:main [pylint] recipe = zc.recipe.egg eggs = pylint nwdiag[test] nwdiag[rst] scripts = pylint [test-extra] recipe = iw.recipe.cmd:py on_install = true cmds = >>> url = "http://sourceforge.jp/frs/redir.php?m=jaist&f=%2Fvlgothic%2F46966%2FVLGothic-20100416.zip" >>> buildout_dir = buildout.get('directory', '.') >>> path = os.path.join(buildout_dir, 'src/nwdiag/tests/truetype') >>> if not os.path.exists(path): ... os.makedirs(path) ... import cStringIO, urllib2, zipfile ... archive = urllib2.urlopen(url).read() ... zip = zipfile.ZipFile(cStringIO.StringIO(archive)) ... ttf = zip.read('VLGothic/VL-PGothic-Regular.ttf') ... open(os.path.join(path, 'VL-PGothic-Regular.ttf'), 'wb').write(ttf) nwdiag-1.0.0/PKG-INFO0000644000175000017500000001761012224001064015266 0ustar katsuwokatsuwo00000000000000Metadata-Version: 1.1 Name: nwdiag Version: 1.0.0 Summary: nwdiag generate network-diagram image file from spec-text file. Home-page: http://blockdiag.com/ Author: Takeshi Komiya Author-email: i.tkomiya at gmail.com License: Apache License 2.0 Download-URL: http://pypi.python.org/pypi/nwdiag Description: `nwdiag` generate network-diagram image file from spec-text file. Features ======== * Generate network-diagram from dot like text (basic feature). * Multilingualization for node-label (utf-8 only). You can get some examples and generated images on `blockdiag.com `_ . Setup ===== by easy_install ---------------- Make environment:: $ easy_install nwdiag by buildout ------------ Make environment:: $ hg clone http://bitbucket.org/tk0miya/nwdiag $ cd nwdiag $ python bootstrap.py $ bin/buildout spec-text setting sample ======================== Few examples are available. You can get more examples at `blockdiag.com `_ . simple.diag ------------ simple.diag is simply define nodes and transitions by dot-like text format:: diagram { A -> B -> C; lane you { A; B; } lane me { C; } } Usage ===== Execute nwdiag command:: $ nwdiag simple.diag $ ls simple.png simple.png Requirements ============ * Python 2.6, 2.7, 3.2, 3.3 * Pillow 2.2.1 * funcparserlib 0.3.6 * setuptools License ======= Apache License 2.0 History ======= 1.0.0 (2013-10-05) ------------------ * Support python 3.2 and 3.3 (thanks to @masayuko) * Drop supports for python 2.4 and 2.5 * Replace dependency: PIL -> Pillow 0.9.4 (2012-12-20) ------------------ * Fix bugs 0.9.3 (2012-12-17) ------------------ * [rackdiag] Allow multiple rackitems in same level * Fix bugs 0.9.2 (2012-11-17) ------------------ * [rackdiag] Add auto-numbering feature * Fix bugs 0.9.1 (2012-10-28) ------------------ * Fix bugs 0.9.0 (2012-10-22) ------------------ * Optimize algorithm for rendering shadow * Add options to docutils directive * [packetdiag] represent splitted packets with dashed-line * Fix bugs 0.8.2 (2012-09-29) ------------------ * Fix bugs 0.8.1 (2012-09-08) ------------------ * Add packetdiag_sphinxhelper 0.8.0 (2012-09-06) ------------------ * Add packetdiag which supports generating packet-header diaagram * [nwdiag] Add diagram attribute: external_connector * Update to new package structure (blockdiag >= 1.1.2) * Allow # to comment syntax * Fix bugs 0.7.0 (2011-11-19) ------------------ * Accept N/A rack-unit * Add fontfamily attribute for switching fontface * Fix bugs 0.6.1 (2011-11-06) ------------------ * [rackdiag] Support multiple racks rendering * [rackdiag] Add rack attribute: unit-height, weight, ampere, ascending * [rackdiag] Support putting multiple items to same rack-unit 0.6.0 (2011-11-06) ------------------ * Add rackdiag which supports genarating rack-structure diagram * Add docutils extension * Fix bugs 0.5.3 (2011-11-01) ------------------ * Add class feature (experimental) 0.5.2 (2011-11-01) ------------------ * Follow blockdiag-0.9.7 interface 0.5.1 (2011-10-19) ------------------ * Follow blockdiag-0.9.5 interface 0.5.0 (2011-10-07) ------------------ * Change shape of trunkline like a pipeline * Add network attribute: color * Add diagram attribute: default_network_color 0.4.2 (2011-09-30) ------------------ * Add diagram attributes: default_text_color * Fix bugs 0.4.1 (2011-09-26) ------------------ * Add diagram attributes: default_node_color, default_group_color and default_line_color * Fix bugs 0.4.0 (2011-08-09) ------------------ * Add syntax for peer network 0.3.3 (2011-08-07) ------------------ * Add syntax for peer network (experimental) * Fix bugs 0.3.2 (2011-08-03) ------------------ * Fix bugs 0.3.1 (2011-08-01) ------------------ * Fix bugs 0.3.0 (2011-07-18) ------------------ * Upgrade layout engine * Allow to note ip addresses directly * Allow node_id including hyphen chars * Fix bugs 0.2.7 (2011-07-05) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Allow "." to id token * Support input from stdin * Support multiple node address (using comma) * Do not sort networks (ordered as declarations) * Fix bugs 0.2.5 (2011-06-29) ------------------ * Adjust parameters for span and margin 0.2.4 (2011-05-17) ------------------ * Add --version option * Fix bugs 0.2.3 (2011-05-15) ------------------ * Fix bugs 0.2.2 (2011-05-15) ------------------ * Implement grouping nodes 0.2.1 (2011-05-14) ------------------ * Change license to Apache License 2.0 * Support blockdiag 0.8.1 core interface 0.2.0 (2011-05-02) ------------------ * Rename package to nwdiag 0.1.6 (2011-04-30) ------------------ * Fix bugs 0.1.5 (2011-04-26) ------------------ * Fix bugs 0.1.4 (2011-04-25) ------------------ * Implement jumped edge * Fix bugs 0.1.3 (2011-04-23) ------------------ * Fix sphinxcontrib_netdiag was not worked 0.1.2 (2011-04-23) ------------------ * Support multi-homed host * Drop network-bridge sytanx (cf. net_a -- net_b) 0.1.1 (2011-04-10) ------------------ * Fix bugs 0.1.0 (2011-04-09) ------------------ * First release Keywords: diagram,generator Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Documentation Classifier: Topic :: Text Processing :: Markup nwdiag-1.0.0/setup.py0000644000175000017500000000405612223772154015721 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- from setuptools import setup, find_packages import os, sys sys.path.insert(0, 'src') import nwdiag long_description = open(os.path.join("src","README.txt")).read() classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Topic :: Software Development", "Topic :: Software Development :: Documentation", "Topic :: Text Processing :: Markup", ] test_requires = [ 'Nose', 'pep8>=1.3',] # only for Python2.6 if sys.version_info > (2, 6) and sys.version_info < (2, 7): test_requires.append('unittest2') setup( name='nwdiag', version=nwdiag.__version__, description='nwdiag generate network-diagram image file from spec-text file.', long_description=long_description, classifiers=classifiers, keywords=['diagram','generator'], author='Takeshi Komiya', author_email='i.tkomiya at gmail.com', url='http://blockdiag.com/', download_url='http://pypi.python.org/pypi/nwdiag', license='Apache License 2.0', py_modules=['nwdiag_sphinxhelper', 'rackdiag_sphinxhelper', 'packetdiag_sphinxhelper'], packages=find_packages('src'), package_dir={'': 'src'}, package_data = {'': ['buildout.cfg']}, include_package_data=True, install_requires=[ 'setuptools', 'blockdiag>=1.3.0', # -*- Extra requirements: -*- ], extras_require=dict( test=test_requires, pdf=[ 'reportlab', ], rst=[ 'docutils', ], ), test_suite='nose.collector', tests_require=test_requires, entry_points=""" [console_scripts] nwdiag = nwdiag.command:main rackdiag = rackdiag.command:main packetdiag = packetdiag.command:main [blockdiag_noderenderer] _packet_node = packetdiag.noderenderers """, ) nwdiag-1.0.0/examples/0000755000175000017500000000000012224001064016002 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/examples/packetdiag/0000755000175000017500000000000012224001064020076 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/examples/packetdiag/tcp.diag0000644000175000017500000000072412021251716021523 0ustar katsuwokatsuwo00000000000000{ colwidth = 32 node_height = 72 0-15: Source Port 16-31: Destination Port 32-63: Sequence Number 64-95: Acknowledgment Number 96-99: Data Offset 100-105: Reserved 106: URG [rotate = 270] 107: ACK [rotate = 270] 108: PSH [rotate = 270] 109: RST [rotate = 270] 110: SYN [rotate = 270] 111: FIN [rotate = 270] 112-127: Window 128-143: Checksum 144-159: Urgent Pointer 160-191: (Options and Padding) 192-223: data [colheight = 3] } nwdiag-1.0.0/examples/nwdiag/0000755000175000017500000000000012224001064017253 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/examples/nwdiag/simple.png0000644000175000017500000002704512021251763021272 0ustar katsuwokatsuwo00000000000000PNG  IHDR-IDATxn#ٹU,Rjgxp Ĉr\|+ߋ/!}9pΒI<;=VK"YU9P-umQZ| ş"5W$!?#@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|`o,k=86>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@BCۺ;EiAe 3"̓o`J|H у?qe0)sL 30e>$D_ 37j J"@BϜ:5 N3"@B'Ld)1A y) !Zt0s^/^v VE4}p%aSs>)`5@KRO[e1:!H».KQW[]?1y!ж֜(xyeY!lnX,0ruuus0?:蟱uڶ-=38g}Xy} C6!,vw]jc_׷p?/;`_Wc m_]Y۲,UU*+M\>|2lyNNvoɹ B~27/c]חu].,뺮k{X3\=gxinzζ!^E֜y4E=0_+w}\.?,M?p''jjN9k6oH۷o;==]m[pݖS6gYֆ~؇Hw$ĕݻw?o̲+bX,.UYm7EQd=p,#[txH͏?;ao6u]<99qr~=ҥwy7EQljOOOߟXbqC~71hE}},\na讪pWoy4UuYQۺ/Vv4pcoz K}6y7EQ4eY뺾XVNOO޼ysZu߶I>ϡoNwwnAMŸdfۺ/ޯ[+P+OwCt '+F^u}C?QMR|Nw}P߷SoO ՛E{uuWeY[ESUzTU^.rs@m:eYnu]_ugoZt;(8\?eFǡ|Knh>K<bƥ8{_Cti?~+-r]Uպ֣$C׏o_ Bz5?JG eYMUUW:ұVC|Wr1.uQH EQ4R͎%3?[& e9z#͎sÞa;B-cCt%Cp;\9 g:]#?ik8\./=ҥS4^$bd9V<?:UUm=ҥe/ fCWc!qx[?Oy}'x=l6y< N{|ٯ׮6p@q8W85ٰϘvyw>vm5_9Ӄ?s6BK^,v3wrC}oR/'+(˲ !}v93S} gf-٧"o3}⸌gF J(]x si> DwE6MEnJ3>^Nu-Dt]w]^&!f6`?S^c#H ;33|ƌ>O7Qtfn3?!6R3e 1 1σxSfLy=fF~f !>$DɎG؟./}?]<|H ѢëH !R\vc}̍{N sw< ikަ^_)yC̜s Wk>X_s#7àr9Ǻ3tM9yk}̙ϋ^[}6[mC uY,M)ɛu} l 93|BժѲogggue]emeYvOY}׮ :38:cLOea^/</`6gggu}џʶmw̲뺮RR_2Hb2]ͷۭ~juZ>4MS OͲ1qek\_E X4k޾}wg !(,g:n K}Ϝ 6>!?m _eYnbmۢ&0(׼̭R`ɋ kgξ :|uq\/im7=,@J|pM\>j:_׫fhj..Gy#yz}(?HN§v˓zl6k*K} 6Wkl6v-։<PJ} 6CBញixwz4%kFfX_'b š#m[ ׭<\0@|Lę>|Apcc`0k>{cuF]ks/9L&D}) 5fᅌku_}œA/6à%/E(x;T} .>Gpu_p&U./`Dv(@P}*Oc/` fI$kחY{vѢxLww8/ >Gm$w<<޸^[#$sTƁsKN^h"@B|H  !>$D"!,;Hx=>{c>Y V)63}`cteYօ,˺>u0ݿL `Ct!ܞ3S7my7y7YB;g^L} >p3c?߿}/o߾uuunUue۶7(u4O??ˡyeٶ,M]''' eU_`}̞ϋtYyEQ4eYnTU^,rV!PŶmۢ|k돇smMYbqUUڔe-g3S)HK-]emQMQr\.7"ʲl۪`Y&sqrrcxA\V?.rbWEQlh!LgU}MS*/;zX\,󓓓1|UU^˦iQC9(m߼yr<_,UUaQ^m!k}H W+jSj:?===mUU///O6",_=Mj\.?O~j:MJaC/5ms/؇7}WYju^ͦjC>*Me===={ͻj`N-kzR/q#Uu4Muz^m6zݎ'&?_z 4|Z}899y>S]U_6Hɲ,t]7^-ru:.OXEg։iIgXo k4\f^TUպ,[lZL} U>/n\^ce],W5`Djt/nG]k|u]3쇎}ryq1#p©CYm}@EݱSd b]L UUm=/ԍ/5fa 7ֻXO{SrWؚbR_3HܵRQjd|<̵LUQO鹄I9D)={>s Ϩy??ԞJK` |^EJּMx}f'f_zfYx)R}*Kfs&Fx= 4R >LXUUmgAf=vkc~/G+%1'n m;=v3=wenۼ,v數R/=5~52a6ReO!\q{2>;cu&b9Tnq㘛̂z\ZEf~2G}R_1-`(aN !>$Diy}^sI}X[fgL8^weA"L׃m;5fyf"'@B`^8^/:"8/0,kCw]?.ZmCt/gq~S{+? C~Ɛu](˲fss+,\[8/~alCt/]ϦfY7<O7wA)!_AOim{iw8A뺼mۼmۢPiF?Ac ] Y1؇ЇWD_o7뺾c]E̅eY,˺Z;پa0n3l r5ڡ6oAxm1Őݻo凓E\I-NyvG?QUsY۶y4NOOmۖ!\e}}Gu|xϛ),늢,bqUmMQe2Slq iumUUWz>n۶->^WnF{[UD?p||0!)y>3^"W9ݻ}w}{vv͇x^+pC9-~<8H8W,jaZ/ˏⲪ,7ð?{~?<<{/7,﫯CtBz7X-lv,iTf~aO k=ҥ,ap xp?*ZuM_~03냕 gWa,|6}R_3~@B|H?#`(aNԃ?3B>e>0  ! 3uݡb^* fyጪ~^W ю2?1B>乹 >$ >2aO1zxkujq=wyɞc}r@B|HGJm)=]xȔ&)=]T_>G9d!fce^3yz}G{7s1scL%\=9՗#àr9Ǻ3tM9yk} ]aUnݧh۶8P7eYX_C>Go|UUUUU]u].rBy7]̩|,웺/SٶmYu]5S _k^V_C>Gi`v5n3?|ZWՇiBIYݠ?&~L}ߔ! B}ovB(,m<뺮BR_3S`Ћ<UfX\.ڶ-kBYR}kHpj?Úů㙶oB_u]\.im,M=˪i.Iu] `ÇVU^VzlMTM](qQ5oSLD§v˓zl6kK}W3fmٷN1MRk^V_C >BpI4e<;=x5#3>\-Զm1^pyÌ3kffV_pۤg$Τ+ BؔS31:UjvͮNǵW˲2}2~`6޹y_?uskdx}nXWx9]zsn8t}ץE"@B|H  !>$D"@B|HZe c} 02 1ʲlOǵW>ua1g,B]e]ƺx]pcc`B<`"nayydYW!y=0533[|f/_o7/߾}w߿ǏjnʶmoIngi,]emmQMY۪6UUbnU!Em۶.ohq5Mאb EeY[ESŦbq\.?.f!,7X0=/y.WՏ|\~UQ(,kәeU_ӔJ}"pWqvtGZ/ry~rr>z4M9 `0E7oޝ_.⢪!l"ʫ;5us ywEQ4UUm꺾XV秧gmfYBN7rxrr'?g꼮닪6}+El״ͽvpF`b*rZrTMӔ}uB^\ OmUU8zzzz͛wCÝ"<[T_C>[_ǍU]e4E᫮zlv;n4^:}۾wbZV'''vvJkf__c>G)˲ux].,mu^vܣdCXFϬƣvÙCS_ӕB} N/~Eak۶h6M~=yzIyׇvt:Ym}"@oG]k|u]3쇎=R7GᒅS뇎,̶|wXBh-Ç8\7<c;TBט'}xctWؚbR_3`䮍"5s|ל6&M !Usl3snj9m|ǜ6̏bCx9>$DGC; 3"@B,=b@B|H у@2DOꋹ0  !>$D>'sf !>$D`6< >$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$D"@B|H  !>$Dͱd$TFGIENDB`nwdiag-1.0.0/examples/nwdiag/node_groups1.png0000644000175000017500000005037112021251756022406 0ustar katsuwokatsuwo00000000000000PNG  IHDRPIDATx=,Yz='"2i"5 @HZ%ͺZkPɐ#9vI`e C`AEc4Dр;ʌ8gYu+2"kdWU~F<9a1FIpC&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&٘Y86pj !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&LHv 78(4N{E@B0N~>0!|`B8>11E2*F0a !B0Ɔ1)PƄLzG1tQ=ƌ y ` bXCØs`JOA >L-:rgKL71'fXikc84^B73x`&A%ADOazW}{m?tٮ}&io7;$UUՆ,B7yR<,K\.ݶm9YTSUv>VU~m$=^9}B>S0м-l?~S'$}:>\х\\W+Afqm`/5a9W\Nm'8 f\J~ !_UU֜)!ۀiTι཯ek~sFTIO+cxCE)խܧpg<>}coNeee<ϗyynYVs}N$~jj~ɳ45ɶii?f_)IׯOgg{ߝf7j6e,Dm>o7i/rժy\{j]UU^"Bw5?iTtq΅h?L.ChUs-પy0)_}B&m9Y/t5J>|LAD{UUef(nͲlBXy@TNHjC? yryYUwVpd&{؇&st}n u͕tf!M6l6Z.j5+2ߣm;-LQ}KH5i8-Q~OۿO}˲t?e륕(`lW ݪ}5zT ,qO1c'ۮzQa\(BD_EqmenonN{0_~ʑb!ۚ}c. u, Tual6:;;{Zfʢ(nV,}|}_o޼ycousl&^_}vv~6]EqB~:ϋ1m"sH~d?U{zfl*_UU1FޗvQeQUUwZ|qmt1Tc${_eYf׋ׯ^zX\6eӶD \_u?i$o~)%3sl6X.+Pny_mT?;6`_+:FfH*uemw .g^a[\h*{?jڸ~Q2Y4཯<_vUy/UZ^ݾoۀb|=ߦe*lv3ͮfl6KE?jp5fo6]^o0Ko?|^esz/*ocouT׺>,,[y6e[/)s{Ǟ͕tw\.7*ݏ~TeYU:ҩ~~;~Lk^Hjʬ,ygLUO__sݞak;=czPZWL.NOH14m*wH=ۀb|=Gue`OD JVs8XkNgHY .nݥ2{NET" I;~O5|~s:6`_wy=SC?Avv^O-aT#~m 8e WqηlsvtxN_W=@?`LN`{+t/t߅Xtrݽ#C8#C[ߺmh菽 KJT)Tq'ח!|l<}5;VUeR|ۆe?ȑ h[B}X_?ʴ6МCG!O>=ͪ}'ߙbtRt)_QpөI{I1.L$WeQj֨4*ɂ S,FBCCM)!OSvRzs)b,ƐIoS 5_^~ot0I916:b_e.DY\)̕U֝=0"]' bPLqU(bXyPشLD\^|co{Ͼ?~0]_śoieQf sW2V,5-%ꐯH=<HSxWy r\v\Z\1'ԣ߹V_tgy(h悜+b)W\]YTt$GYUT{L s]|MCPuy*^:BrKRILO~0-O߽sgۺo.F*/nϯ]eY-Ȃ??v&i1#!vB{~XX^L\\2W\e M~'o?rq?^|I.ʹ KblqR'_[(U|iѕ5KPp؆GQN^ ⪈BիLo?ohy:>)VB뀟>8o4R=`Z:.>ѿ{1֮zco寮bbل{.1J٭}^t`OTshbB+,*ε|i}&|n~Źʫ2SB&WXy5}͗Z?߸N?׽|{N*>||Yb3Wyzed5>!Y2*T-sU7ʫs>,T_t[]=͗l;uo|{:C~KUJ\MjsceY=ǖOA#5}u/1z)UVﰗ\&uTm X^LW]u/$uaYNz tx*~o['*J1~7x>c_$pBŰV>[Bp{Һ7O;!~k?NoC{kړ׭=K{0a_fR4;]u͍ڶgx- 8U||m7V~Z5'|m{1o;ol+moc}M: +rip|<]F;N=n%mIRvSwX޺oE< ~mնx?v`j87}&^gܹ G{U{D`ml pbl0~ `omƇ > |^ahO x&IѿCSoW?v}-C>,j{h ?sIN'kF/A>*8OmLʶQ"ew=/ pXo~"&Bb[ZV2ɚwѮضZ[λqid%1wY3]˚Eӏq㢭7/ cGܙh{wCؘ}Uz|m$g3t|<ٮ}[VX:m3PٰFW??c~iXBf%_Jck# Ƌ'yp\ZGhB8% Fkŀr'Ao(\i$F;eЌhhrb}4hhdqPmN?coӡ2էۿVLA˚FWS@4H7tW%ҏ~K%@~i `s0Qfe%9uք^Rj@_U/L.8)l!33>>Xնcn*7[lJ5cWW]]q)2UN18eMN6G">W";1\f\PrX04!in5?|ȑ\eʽWQ:*' Vb݋+]>ʚn{ .ns+||Eɚ!=ȱNF=1 53K.6ico(LӝGMyViU y}Y`[L |1JQxwShrULqe Niթg3>@AuCXaBgy/gw>Rox{ShsUE&[yeSLy0h :#i:%n5Db*C]G.*Q7fcZæb\6ϔ^E4 NUɶ"dwZQX}9 eA, NU DԦӮɐDL.n3Y)_yKETs Eǁիb&َ^Eg^z-+bw@J7 (\phW=BjN>}uiR~MqhWU w' %3yö NeeuTV|0l1lʩl*UP]7Cx&|< vQuZrҩK17cP/isyLO8>P2!׆.u֛o.ROM- 6tZ&Ć}խz)n$|*\#)㹵K֫ldঘLf{t}?6Z' s`c n3M=5t!nLx~Q{ } m8 @}:5x ؆s_ݡ[I%ccѵ lqRzG`G9 $>NCUa$ K/mh'~~#t`(m[dWCӶ_8M|~}?嘆_+6B&L>0!|`Bgr !BfLҲco8:4m}ZxeQN $3I2*k7sз5M'!}\hIq3ؐ92rZt_,ʙl gv/qRٗ2/gL*Sy] g?~Uyf>h򮔷~!wQgu/f˫j3uƗzkuぎ?ٝKљwgEJoqc|YUqE͎ܛ؜r294SvlY\~rsR />(A fNKp8hU*Ck̩`qKȫcoGI~U?<4W7eCmHTc 4HKMg^y3D_Ch0b|XJKA4*:|UhZ(bbuM8&ϳJy&>uE/ ͧE,TRE*IwQ,z/!6ڢLtFgTESwp{7mC6&ͦ`fůRgyR_jBuKmxT$eu>}^ `{jƉw?=&<ں UnW_Y7ذ}EdJU<KzA/ ΔeNEi>7rU8QWr_E)y e>¾uvsMx--66T_d?pF B^_], _frΔy< *fŬYQjw@vt6f0 ƊI)@:dAʃB]WVjױ-y> kWIO:[vg&|3)Q,0䛜YP^by Y3ٶ;oO@At5GLR>bJepMHw?eC|=Wrsê'mߥ(|bdG5U|/E[JjMI!%)ZUtLVZEG6Z1(Ue(3TUʲP|TK=cyf`<hj?3Ҋ:.(/)D)DXCH)O6Vq~>J]޾f|FUzj6ZtF46YS$Vd>֧&;)pL՝ UGj9Z dwWOxu-Iٖ۫tS=~,پlǕ}Gr|eQw^=(ٜ}w%#It/Om:Xn~D{v7 ~zim9 R'w%빥NiC~3_LB֐}s5tO*{AFpOz?Mo ۛ?sF4G=ϯD#Yiۋ;|[(`guAm?{~fKЗza僚7H#_fNו>m0(|^`Bs3FVdwz)3XϔN'f|ep} }mHj>3Ekz"1:SbU:\󕹬˫\^F_kD2Ǿ$?Po38 E:~†qѿƙu_TTrY%+954vI>pHۓ s,dy)l%7_ɯzc)VCr#ٽ?nS;aq0Oŗz}KEz0[+Jsy)KYVɚ*῾XS0!w>uomKb%?_*;Qe.Iyw՝hy8Guю8ܸboVFٍ|iXKs ڶjBcfeW|qlqՕ/$+UKiω]#D@2z|<~N./ar\^V^h{/R/neW|p|ܯ=jY2\%+b)?RI+<+,3+fm'rP3$O cq_: .jR~l>[+>ڲKZ.imA N!bJr,_]ŪlR\2W\e _ c?z?8 O_o޳jw2Z7'}|mw>]J*3MoXx1ƍO#5l/-feGIf~ʪW U˙*S#d;۫cǿ}㏫&m:5Un;TvŅ\qmh x2>5l+_˕rRN\]vcRղP\1T^1{~'v|^N^-'n)岥hm)#sԶd̯!/e KeUc-|)jQlGߤwf]RWezbV|YWz,M}Rb[#<~DaҎURG *):9zm쌾!ϊumt{|mT[rzUvL#n>iiC~[v+wMKut*t֯Lc|u׳8uȏupO"q,Mk?dbT%sMmۖeo=co&lk[88Bm,֫߹?r/߶c✥/vT bLjm.[{0]__oݕrz0xRKdNqwn*nU;~Zsձ76?:D_#}Bo'kp0_S`8ذSwqvމ?Ɏ=ZuӶjmU|<ζ֜c `-}u78u:rP=sb|>!c+*doڍ k;T!c2B>^1XO}_Qa\ncw@ƿ NK7? $~?(:8վA=sb|0TßD#߯;v}wsb|/d2mllg%)q}{UUm\eY{jXEQ۝:WK_r Bۀb|8~pVۛ?~_N۾]oP s.tC~7{CYNZ<ݐz b:o&>;j{0]/j'a?+?-l?~}9B˥~%?*kNې~N'L S>8ޗe u=mǴl1۝ZtZt18??;YY-<_y~2˲U+R5_~539WGITU~SLwBp;;ʲtY<7CR[N7}n '_)Iu_){f6]f32de2c '9V?P~? {~9˻=I-;ɶ7UU%=KK+w}k>8;;+Jjh ќcL&?_5B*wꫯ8??2IrUY/ 7~|x7,zWEQEqe2I/0 ߿ \v>_-˲,B-7I-ɮcn!=Tl6XEGrMgrgrVYYyDլ{ۇ*G z-!mi8-Q~1ѽwm,KNi(ҿaqLM32RJ_1[ߖr C>|c?Ƕ*=rUEH6noޟɟ|oo`1Chs_}Coc]=VJᐿwW_}ݻo}ןWr>"ҖqT[__couW?7=6䬊Y,|>*&AB'=﫞s}n֖?e;?m_e߃n9,r;fj5kUEqZf棢Cz#x͏ .J|tUT{y~~~q~~lvUM MN}t\lkybw=ƶDI]yPwTeUUyѼl6]eYTU՝&>/x6_{0]::)UelvX,>իeS_6m;A>Wy@+vl&6AtΕb\nVݢ|vmt1-v)ngus$M:˲61o9tOzImö ιT~jq(e2`H-:*ezl\r>_hַco~NeNe|-?lhtCKU`b;wMisآivp4ۖ)BٹdA~^Nn|Y=Ch >&a'׷C[ߺzmh菽 8;ߥJA2p7;=0 vޥf}*LېyG.76mKȷu{BbGsTD%OȾ=0uN[;SNN1:{:w:w7p/)ۀ-JB]%Yd ŨRTq O 6Nѣ1b+Lb T&;:A_|*Io7w]ts0!G_nμG Q`+3W|)sd䪺ugGyF~n4s|,|O?2 f\/e٭l)T|coo>`Gޣ+Uj\(,](,*W|I!wwUx?}'ݾ?ؽ/~?w;L1׶?~;zMQr9_EWR6?+I%::z}G{O|MCPuy*^:BrKRILO?sޡtuǿÌvm]7e\^.cvЬdABݟC;׏v}2@9z9R˙rZWiX^Z披L< ~纟};c=^?/GoG\sAJFOXQҢ+])kG襐)X.T^txÙBa)TScO? myz| ӱ'@?w 7:z?_Z*/,Ms٭JǨL*b+P ˅ʫs-o߾7_~۷iyqjm:#Oto};c0>a \VXTXdfW *o15H+DGX*c^U-X,byykݾL7__\mDVӉW?s?;+P^? ؿޡtuqծey%?[){ucM+1;TjaղPVҺm}Y&tt#C=S"m]*WՇ3-/ןOPV^rm͗Z?߸}:||̑>,U}+Jnq2fgVљ)+o\Q{cEb*;.ɍc\! yn^OZO˿xß_d7tkz5*y߼ہi:_}rO\⋿__}[+ Ɯ[k[}dNh~7k >D4}u/1z)UVO]fnsd =N39 lڃGM_+מn tܱ76Gm&Eu^:iQښGm7V~Z5'|m|~mێJ[0X:F\|=>tkk^]fx[$őn1ue+cHf[@ =Uݳ=s[O!xF3vJ?͇Y' =N9Xw /xmeu~/6cWj@ |^ahO ҝv; `@CpFf.+IJ:$[7G!f>ɮ=kW?v}-hNV﷟|ED=/搋םzO^v_b}Thjvw`RUg;z{g?ԗZt'cƎ=0nl!ߎFv|e+dػh͆8iv[wqy?p"Uܼ?&5#K_skY~1n\fC>ݙh{wCؘ}Uz|m$g3 zVU:V`[ T6FW??c~iXBf%_Jck#8QNKk_wvwMނرu~hP$s^ +hWLqMM.B\ ;ӦӄϿ{thLkSвjT`6> <|Q3*I~_Z4(X.8TS9(3Ӳk~;kB~/)to όǗW&M sbƙDmɩ?T&o|k ǮpSze)bpʚvim<Ê|^ARYce.*wQuiB~2ݮk~417#qʔ{t*J'UN>ź|IyeMn]>hfϢdMWŐMX'LBP|^α7t >u'@hxwkUZdB^g,9V/68`REZ\2S\yY䂓oZu팏A!VqBgy/gw>Rox{ShsUE&[yeSLy0h :K)v!J-P:wQʽ)85 MU6CPy**YppNp}B;GuwE(\,뾜ͲyT*v"kji?BNdH?&Ȕעr*CsI@R:Be`h\Utײ)*֡y*^y ~v|c,ڧWO\`nJkV!!Hv|Upg| $ЈԙTHUT6tC>CqcS=ӗ3'tco Ǡ+_{> %~mRg!edۄR;P[CԆ^ЃзU>ō?}Iϭ]^ek,X&X)6JnWn?4ckCC6 >'&x'`=oě>5t1lv|ND?w֨=E`}=xLa[Lk`#@`Ķt! Ou|*:xV F{lt.<5NT7z>5[#E`n}8 _E0!|`B !B&l5=  !BgL8%);0dщ>o#2' >/Ife#({YۇYTݦ>qГfD!ses61Y30}to\Te澼뙽)m2m )TGzWx:{5Aw&&kL )m>o }y5_^WW33_#NL;¢p1,}m*#h=7)s9.(Ae L!bo??~~.r_? |}wfJ}cX_>2!R9)wNryW}x%l$-;|^ 2 AN+mu5ؖ}~FE3zS=:) 3_L}< #+&g&|Xi~Lm`[A NZ7Ys ,yIT*XM[dx{YRzD]UL%UO*K,,4C Ϧ)fT|Ll"+U45'vw u—hQjV1Փ6(U14&xbi}TƝ礐fVQh#J|?%L.+e,',Rp>䤪}̴RK+(c^g֓zWY@X*Sٜ`_ȱ[ޚ&ϋwAy6&ְ)>[av״rj9P>q~>J]޾f|FǜzϚ$P >5޹xgL3ĺhs2Vh)I\ b$~of[nI3^LidfWjmdt=8,\+}g9:ﮤ3|IڦӽD֐}s5tOԎܿξDo@mwCyaWli28]&Y][&_4kbwR\5ց8XvG |i֚u?}So\~\|ɰFvcgkvf-7 {<&xl`gPӾDE_l!>k|)if)j}0ٖFuJ) /a> Bͧ}h[>>tvt.ʹ +sY%W啹&Ջenx~߻HOP'|_~=JWr.kh<<|؞ fY%K(g+J~ՓLru̾qZń{R~ѼV\^RU5D?@f뺷W|/({uc2$TX;Pq|fq7_W_]+{uF~4_9߄| m߀S3_EWŭōWWVTX$_T +/='mUdW#J~RFO߫x}Օō|q+Wf, g5Hpd|_[v~E $XBC36h{Jg\_{S=7'Ҳ/5{Ί/RW2W\G.Ӗ*sTk貕\qme,>ZMRtRU7*S d L\;zBm^ϗ'|yk'_[vv)W\eu?MW>$)hJY4?UsoJ٥빪eԵ+4kY3G'[ԟ});`~v-˗+%=|h&:_Zn+2˂*ٕU3UmIG"ԶĶMDz_|g>X+Ͳ[9_ma{\_˖Rtd旪*!;ՠ6rj9y=ggu{Nq--eDaݖd̯!/e KeUc-|)jQlLR|dg$[Yd{L8i*)ĤWJmtW b6vv}`6q}T[ izYLWm9u}*WL`'ӮӆX8WJ[o{*goqPoa~8!8 WR h1mٲ&Ӵ-o6JrRn8^p2SeӎzAnJ9݃Y e81*nU;~Z?:_#f[Mz &졝r~[=[7>ٽ?co!B`2飂L>0!F{ >0!|`BЃ zaT !B&|`sG>0!|`B =tT !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&L>0!|`B !B&L>0!|`BnRKpIENDB`nwdiag-1.0.0/examples/nwdiag/simple.svg0000644000175000017500000001127312021251763021301 0ustar katsuwokatsuwo00000000000000 blockdiag diagram { network dmz { address = "210.x.x.x/24" web01 [address = "210.x.x.1"]; web02 [address = "210.x.x.2"]; } network internal { address = "172.x.x.x/24"; web01 [address = "172.x.x.1"]; web02 [address = "172.x.x.2"]; db01; db02; } } dmz 210.x.x.x/24 internal 172.x.x.x/24 210.x.x.1 172.x.x.1 web01 210.x.x.2 172.x.x.2 web02 db01 db02 nwdiag-1.0.0/examples/nwdiag/node_groups2.diag0000644000175000017500000000034711655456324022537 0ustar katsuwokatsuwo00000000000000diagram { // define group at outside network definitions group { color = "#FF7777"; web01; web02; db01; } network dmz { web01; web02; } network internal { web01; web02; db01; } } nwdiag-1.0.0/examples/nwdiag/simple.diag0000644000175000017500000000044611655456324021422 0ustar katsuwokatsuwo00000000000000diagram { network dmz { address = "210.x.x.x/24" web01 [address = "210.x.x.1"]; web02 [address = "210.x.x.2"]; } network internal { address = "172.x.x.x/24"; web01 [address = "172.x.x.1"]; web02 [address = "172.x.x.2"]; db01; db02; } } nwdiag-1.0.0/examples/nwdiag/node_groups1.svg0000644000175000017500000001244312021251757022420 0ustar katsuwokatsuwo00000000000000 blockdiag diagram { network Sample_front { address = "192.168.10.0/24"; // define group group web { web01 [address = ".1"]; web02 [address = ".2"]; } } network Sample_back { address = "192.168.20.0/24"; web01 [address = ".1"]; web02 [address = ".2"]; db01 [address = ".101"]; db02 [address = ".102"]; // define network using defined nodes group db { db01; db02; } } } Sample_front 192.168.10.0/24 Sample_back 192.168.20.0/24 .1 .1 web01 .2 .2 web02 .101 db01 .102 db02 nwdiag-1.0.0/examples/nwdiag/node_groups1.diag0000644000175000017500000000067011655456324022535 0ustar katsuwokatsuwo00000000000000diagram { network Sample_front { address = "192.168.10.0/24"; // define group group web { web01 [address = ".1"]; web02 [address = ".2"]; } } network Sample_back { address = "192.168.20.0/24"; web01 [address = ".1"]; web02 [address = ".2"]; db01 [address = ".101"]; db02 [address = ".102"]; // define network using defined nodes group db { db01; db02; } } } nwdiag-1.0.0/examples/nwdiag/node_groups2.png0000644000175000017500000003353612021251760022406 0ustar katsuwokatsuwo00000000000000PNG  IHDR`y7%IDATxMo+}_Uu7I\{k{`1u,o & ;+1v&ęsϹH"jE[34(Q]?ڄ0;\`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`N"`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00wBcpTi\cػ6!tT0OR0 1 oEl`8>`# 9`3CsC9` ،Б88`# 9`3GYSG1 Fl48`# 9`g969`Fp`c燓}ah 'b`g%6!9ms;0qL'1f='\a>vSm,~"ha`PݚxV^x5{pv>tU[iu )!]CL _{[&UUcRߢ1!Ԇm! SA I芛 !X; !x]EL"j}YK–[km}m"|aJ`?C5Mu[8m;<Trykw5ι:c~U0LD:Ԅ7Mcs?O^{u.W_ ~Ieueee<,˪.5ZjCc<6!`]Md1|?8Ij_7X,֋vX-uQ,˪& WÜ0s) Лpoq |WÏ>~ϟ?uwwn,*qqJE<S+{s>~\׵ͲKRUUEճ^ S;?^z3I6Yq^1ƫ1 c#k+T]V,u]<1e6Ep>|æi2cLpUEQdYVz+!paJ'{odaLӧO߷<7,˛o!ȣP3/IWo]뫲,UU- a*'կԁǯx4LCWZ bC;f2NO'Iג+! \/~/\Hc s06ĤÃ`Ml'Yc:>^Vj/?|;ϟ?yww,e,zq?u߁НXE^VWr-b&Y'Aź_B{9.HrَkX^]]}jU(UU-b9?_/JV6ιts}}ǫO6.jmkBrä.Dpmwʹ:i<`sbl6亐kBĤo8_Òι&˲rXܭV߽{qZt2V##tVZqI kmX,֛滲, g?/&jfX-WWW*]e֟c ]`19y^⢯Mro3Gk}}=? eY-ȮOǾvN 'rYuO嶱%*`{~XӮ ueee<Gi=.oev4yout⼯_/?/z!*$'y\x`I{Pc#]<0;;.^NOBG'㍵ya];@ i[ڇѽ9,˻l۵ ~nL{ccMY[5nx "%,/׳Ē.hmJd!Ph`$~_aRwg5 q:Sq8 `eeٝFEYy&Ny:Kr$;ʪ/ qq};PӬTUfT_,UUKuޅ8):Ç:>|Ѿ'!Hk}*ZEq=ؘ6y_˹Z!)#yڡGIj],iJwwkSY^ 5~|@C_ʲREq;V1|Urn-6vc$_}IzVY~u{nofV&SӤÐcstp:/a18\|F$*e٭FYvYsw@yB>lFF/A4zz=~^Ð5ӧ 8cILeYXem E'yw6qff9_ {U UReyZBeRUgC}'޲NN(.} 59_o{fd۰ck5|}o{*k`6Sٌ?6)'n9_xn fVȇk^vWfۃ Ñ~GSv.UA2aq47óP*6i%؜/d^pcї"75Ao!,tǔy(_!p:[b#7NlB&8iCG1Pܡ}{pvg616rYzp>uފɞ8Utr$O9~ t5/a',0DTrg7~beS؁,`T0/ ξ_ aX' *gW0ث=p 0Uݮ7־ʻ_7qU&ICꗿn gLx;.tUА#&{YI֘v$ՌQu+H]Da4rl$m7'v ?5si_`8lg3}nƴ ((5ft9ҾgN  Ǖ ?AǺ6 cVX(=uh_2ޤZZ g۪ZSg醇gut׾_^}Ĕ0&* :Yx_Uvnh, a;L41 ySD!:y,Ug 3F1*U޽۳ 0L ?ҾaV  G"u-IʬZ-Cв 8^CcI/`h0Yɉ8_yxڪiT:{5]o+Y*@׾O/`,g2& D dúi^eD0_0\:džsJ! }1 qb: gp70O0\ tbxU/`xNGܝ}t%:![bX}C0]0\~ןs0t]$`# 00`# 00`# @ĥqJ/` 'wVSh͵}F.1mWhzp /lyǘmƘ{1Fg kFf׾p2Fҿ_~^_6wu[ Ԓ$6?c˿GY癤1VΘvdp3*/?WPF(K*%K%i}M 0]z73F1ʭUmuhANqcj15˜H^,v:PRanYIN[ycua1={KIS[$DW7it|_Q̵}C#YM&8$im)Ǝ1N̨\μLm?5{8S&2`f;+VĶE[i_#sDQݛXkێqF|d*,{gaF]OUoӶ? p`8x0U9U'ܣ{`gB3k_ّ{kmK}ٻ[:~{CC3pꬭՇv{ږD. DJ\ ua 0٣خ3 Ss>([79@~}AA~sqN9.vF`{!ȉÊݯO:9:c0*^`g6!$ 0 kL0 p) K$.81G{vS8nDxa;뿷M~qFdO'a8SaXsD)Ѿ.Dƒ{C;;4 ،~.W?wڏc5XXҧL?"y@p:Ò5֌p|\0*`36\00;u#03`yla<ַ;Tcؙdg&7Y}k‘~= ́{. OTF7T#JWm_t*θ$]h3~y#?r(xo)}OxRD/KN g>_=5\3aɃxb1wAl6C%]~?PW]xϥs=@ޯcJ4]76Cϸ)Ѿ~KגgVE}vJߗ/_M.Pڐ8SjQV޷[GogDCS31t6בֿ*7j+c+E Vք]-Jީiv3v?|[oGai]~qI1& ],UZ{0؅8̈́j?N,IV!8yBU3uqmMPb߷1оtИ]Ū;ieLjY+HOTj1!Ԇ%G5`$ ]q!뽷{罷!o_dYll*BUکiLi[; Ѿ4}N샪*SYlrn)i)+9'Y[vկh1xI]زxkm ]O1=ƐwMd?A&U0*}./T׺zzfʩMo $N</̶lr R]ɘk5M~ϟ?uwwn,*qqNk NmMOoI{ﭵ7Mcs[Iz/y},/㽷ML߿{Iɲº>=H6C,4d=y a1<BPe> aUUm? an//Ç6Mcs*b]&˲{_9 P%CE$yi7=%AzsKӧ[k}ry[M]׷K6c+`,[T5Cgp)mY˪u]LZS1:⑤Ǫaoq`3=Nm\33 ]i%*`I'ya/UU}l4'J?ɥ$cipꇨǾ}gACiJ Uo?߿o>ݻ,ĪLh/E$Qh ijݚPjl{1x5EVoop [5ݬ˹ZUrTme9nVLŸW,Ǫ(jZnmQ<7">y{ׇ| $U6bq{uuEE]źE4yRj׃$jJURU5*,vׄ<| H(w85! ʲFYV(6nswfLs(%BW 9WwoxuuiXE!zq'G¢5<.4Ms^,wfUu]҅e%\+Uյ m6AfTQ][y X#17E1.B*`mFEQiry;'춻wջι&˲rXܭV߽{qZtxؙz,6$^,f]Yq2?ơǥꝪK׵p˵^g*Lu*~Njػv_^EQihjVo\~PTsBiJXZHF6n\~U,| ]BsMefmt#x6-ˢxN X~lZeY(s9w%B(B1Au-5 `~0圔eAE\6*u}_ݻVSQ|Pȹu2 п&`qԨβ|yٛ_v!*Uܻ9kڭ_JewV]n1lq2>`4_fgO*kZmtuuwnt}>(U07>$'y\x`I aԝb KKV5*RUI VMVYnTZɐU4yoUd0k;`;1l+`Ym߳Xs˵V[V7Z,>)?uD^bUn^]6;8+ ZKjʲTiM7.߾9JC,'BXsb絊bbbVQemX&OݮnU|sBgE nП(/NoTץRʲZ5ڸ0nP AO4~s `RvKQ~'폲<.ZRm:\K"=~bz1~Xj䜗1pdomRi`z,'Sj-k>k7,=p8lfDH{ GeB^m!e v[||ۨ"}PQ6,? owLPD%{Գ~ˆY {y\A.>Q|d{umgv~c=}3dU=m1*lX~~!s>k7$]^ޙ~lpzY' R `ؗ6C5 ?HV~HUCfSZ~aS}23vT/&hfCXh0S WA'C(.=`tVHoL~v3mhM5/ۙy_ #y.|I 6F@چPΘ1F!m ~#gPx9_ĮyX{/4ry,1jUNknW͕:sZyӴ ! b{&T`lB Ǻ֢i{yg .Y//`*JQ4+SO@Ï lޗ\u]K\,^:$Ԟ ?۪jW]+^E8Wx{%L|᫛^.@{Sk}4#^sw{ޫWT}!ޅYl>k5E0?ΡF+*crVTrǍF|F7k`DݙVz_Et5Dt5׏̙" NcC_9>?_Jj߆CBCVa["|  =5\8KA`fb%+`zC` J&`KC8l/?{z^c6>3_NO}k```0F ``0F ``0Ƶ 1w\ pꙘ E 9vk5׏f$6 A#cL\dU:k7n=  sÜ/'g$~WPF(K*}W׺^T=({9HHϵI>R0RȬ UQN3F`0Bu]eIʌQnrcTZա.y)u:u(^Z(wiELb0e6u:j霪.|eޫ~,"٫~Ze֪V+kפ<^/ć `12!(m]tNWIʛFjb PlVEiez׽. aS0u\:wJյs̍ZU!lbp,wN,U,ӵsZ9E¦>'ԞZʬժiT{5گ~ŽoDw3Fs*\"zPr ;$Ĺ mij ^bMȹFƭ} SozB3AKWb𲶖sem>S"xg.突meնڅ2ߍ+c@K81ӆ,e|XirIw0Q/?y^(TkZYV**Y[˘fWMpH{!JY׭u!IrR]kmaZA6ʲJErIō b9W& `҄B /6X\,SQhN޷+JURMi6f-=޹F]]}rFYv'JY[ɘFm5Q6eٝFEQUv,UWBMU`@xTKŭZ}˔Z,UQT׋.|1 p8)*`# 00态9_ `# 9`9_ `# 9`9_L 0Fss4*`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 00`# 07.v:IENDB`nwdiag-1.0.0/examples/nwdiag/node_groups2.svg0000644000175000017500000000674612021251760022424 0ustar katsuwokatsuwo00000000000000 blockdiag diagram { // define group at outside network definitions group { color = "#FF7777"; web01; web02; db01; } network dmz { web01; web02; } network internal { web01; web02; db01; } } dmz internal web01 web02 db01 nwdiag-1.0.0/bootstrap.py0000644000175000017500000001306112217032100016551 0ustar katsuwokatsuwo00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os import shutil import sys import tempfile from optparse import OptionParser tmpeggs = tempfile.mkdtemp() usage = '''\ [DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] Bootstraps a buildout-based project. Simply run this script in a directory containing a buildout.cfg, using the Python that you want bin/buildout to use. Note that by using --find-links to point to local resources, you can keep this script from going over the network. ''' parser = OptionParser(usage=usage) parser.add_option("-v", "--version", help="use a specific zc.buildout version") parser.add_option("-t", "--accept-buildout-test-releases", dest='accept_buildout_test_releases', action="store_true", default=False, help=("Normally, if you do not specify a --version, the " "bootstrap script and buildout gets the newest " "*final* versions of zc.buildout and its recipes and " "extensions for you. If you use this flag, " "bootstrap and buildout will get the newest releases " "even if they are alphas or betas.")) parser.add_option("-c", "--config-file", help=("Specify the path to the buildout configuration " "file to be used.")) parser.add_option("-f", "--find-links", help=("Specify a URL to search for buildout releases")) options, args = parser.parse_args() ###################################################################### # load/install setuptools to_reload = False try: import pkg_resources import setuptools except ImportError: ez = {} try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen # XXX use a more permanent ez_setup.py URL when available. exec(urlopen('http://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py' ).read(), ez) setup_args = dict(to_dir=tmpeggs, download_delay=0) ez['use_setuptools'](**setup_args) if to_reload: reload(pkg_resources) import pkg_resources # This does not (always?) update the default working set. We will # do it. for path in sys.path: if path not in pkg_resources.working_set.entries: pkg_resources.working_set.add_entry(path) ###################################################################### # Install buildout ws = pkg_resources.working_set cmd = [sys.executable, '-c', 'from setuptools.command.easy_install import main; main()', '-mZqNxd', tmpeggs] find_links = os.environ.get( 'bootstrap-testing-find-links', options.find_links or ('http://downloads.buildout.org/' if options.accept_buildout_test_releases else None) ) if find_links: cmd.extend(['-f', find_links]) setuptools_path = ws.find( pkg_resources.Requirement.parse('setuptools')).location requirement = 'zc.buildout' version = options.version if version is None and not options.accept_buildout_test_releases: # Figure out the most recent final version of zc.buildout. import setuptools.package_index _final_parts = '*final-', '*final' def _final_version(parsed_version): for part in parsed_version: if (part[:1] == '*') and (part not in _final_parts): return False return True index = setuptools.package_index.PackageIndex( search_path=[setuptools_path]) if find_links: index.add_find_links((find_links,)) req = pkg_resources.Requirement.parse(requirement) if index.obtain(req) is not None: best = [] bestv = None for dist in index[req.project_name]: distv = dist.parsed_version if _final_version(distv): if bestv is None or distv > bestv: best = [dist] bestv = distv elif distv == bestv: best.append(dist) if best: best.sort() version = best[-1].version if version: requirement = '=='.join((requirement, version)) cmd.append(requirement) import subprocess if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0: raise Exception( "Failed to execute command:\n%s", repr(cmd)[1:-1]) ###################################################################### # Import and run buildout ws.add_entry(tmpeggs) ws.require(requirement) import zc.buildout.buildout if not [a for a in args if '=' not in a]: args.append('bootstrap') # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args[0:0] = ['-c', options.config_file] zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) nwdiag-1.0.0/src/0000755000175000017500000000000012224001064014753 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/packetdiag/0000755000175000017500000000000012224001064017047 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/packetdiag/builder.py0000644000175000017500000000747412217032100021060 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division from packetdiag import parser from packetdiag.elements import Diagram, FieldItem, DiagramNode from blockdiag.utils import XY class DiagramTreeBuilder: def build(self, tree): self.diagram = Diagram() self.instantiate(tree) return self.diagram def instantiate(self, tree): for stmt in tree.stmts: if isinstance(stmt, parser.Attr): self.diagram.set_attribute(stmt) elif isinstance(stmt, parser.FieldItem): item = FieldItem(stmt.number, stmt.label) item.set_attributes(stmt.attrs) if item.number is None: if len(self.diagram.fields) == 0: item.number = 0 else: last_item = self.diagram.fields[-1] item.number = last_item.number + last_item.colwidth self.diagram.fields.append(item) elif isinstance(stmt, parser.AttrPlugin): self.diagram.set_plugin(stmt.name, stmt.attrs) class DiagramLayoutManager: def __init__(self, diagram): self.diagram = diagram def split_field_by_column(self): for field in self.diagram.fields: while True: x = field.number % self.diagram.colwidth if x + field.colwidth <= self.diagram.colwidth: break else: colwidth = self.diagram.colwidth - x splitted = field.duplicate() splitted.colwidth = colwidth if self.diagram.scale_direction == "left_to_right": splitted.separated_right = True else: splitted.separated_left = True self.diagram.fields.append(splitted) field.number += colwidth field.colwidth -= colwidth if self.diagram.scale_direction == "left_to_right": field.separated_left = True else: field.separated_right = True yield field def run(self): filled = {} for field in self.split_field_by_column(): x = field.number % self.diagram.colwidth y = field.number // self.diagram.colwidth if filled.get(y) is None: filled[y] = {} for rx in range(x, x + field.colwidth): if filled[y].get(rx): msg = ("Field '%s' is conflicted to other field\n" % field.label) raise AttributeError(msg) filled[y][rx] = True if self.diagram.scale_direction == "right_to_left": x = self.diagram.colwidth - x - field.colwidth field.xy = XY(x, y) self.diagram.fixiate() class ScreenNodeBuilder: @classmethod def build(cls, tree): DiagramNode.clear() Diagram.clear() return cls(tree).run() def __init__(self, tree): self.diagram = DiagramTreeBuilder().build(tree) def run(self): DiagramLayoutManager(self.diagram).run() return self.diagram nwdiag-1.0.0/src/packetdiag/__init__.py0000644000175000017500000000122012013135132021153 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from nwdiag import __version__ __version__ nwdiag-1.0.0/src/packetdiag/drawer.py0000644000175000017500000000334012217034051020711 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import blockdiag.drawer from packetdiag.metrics import DiagramMetrics from blockdiag.utils.compat import u class DiagramDraw(blockdiag.drawer.DiagramDraw): def create_metrics(self, *args, **kwargs): return DiagramMetrics(*args, **kwargs) def _draw_background(self): # do not call blockdiag.DiagramDraw#_draw_background() scale_interval = self.diagram.scale_interval if scale_interval is None: scale_interval = self.diagram.colwidth / 2 # draw measure lines and labels font = self.metrics.font_for(None) for i in range(self.diagram.colwidth + 1): line = self.metrics.measure_line(i) self.drawer.line(line, fill=self.diagram.linecolor) if (i % scale_interval) == 0: box = self.metrics.measure_label(i) if self.diagram.scale_direction == "left_to_right": label = u(str(i)) else: label = u(str(self.diagram.colwidth - i)) self.drawer.textarea(box, label, font, fill=self.diagram.textcolor) nwdiag-1.0.0/src/packetdiag/utils/0000755000175000017500000000000012224001064020207 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/packetdiag/utils/__init__.py0000644000175000017500000000114412022137642022330 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2012 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/packetdiag/utils/rst/0000755000175000017500000000000012224001064021017 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/packetdiag/utils/rst/directives.py0000644000175000017500000001074212222275125023547 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2012 Takeshi KOMIYA # # Licensed 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. import os from docutils import nodes from docutils.parsers import rst from packetdiag import parser from packetdiag.elements import FieldItem from packetdiag.builder import ScreenNodeBuilder from packetdiag.drawer import DiagramDraw from packetdiag.utils.rst.nodes import packetdiag from blockdiag.utils.rst import directives from blockdiag.utils.compat import cmp_to_key directive_options_default = dict(format='PNG', antialias=False, fontpath=None, outputdir=None, nodoctype=False, noviewbox=False, inline_svg=False, ignore_pil=False) directive_options = {} class PacketdiagDirectiveBase(directives.BlockdiagDirectiveBase): """ Directive to insert arbitrary dot markup. """ name = "packetdiag" node_class = packetdiag class PacketdiagDirective(directives.BlockdiagDirective): name = "packetdiag" node_class = packetdiag @property def global_options(self): return directive_options def node2diagram(self, node): tree = parser.parse_string(node['code']) return ScreenNodeBuilder.build(tree) def node2image(self, node, diagram): options = node['options'] filename = self.image_filename(node) fontmap = self.create_fontmap() _format = self.global_options['format'].lower() if _format == 'svg' and self.global_options['inline_svg'] is True: filename = None kwargs = dict(self.global_options) del kwargs['format'] drawer = DiagramDraw(_format, diagram, filename, fontmap=fontmap, **kwargs) if filename is None or not os.path.isfile(filename): drawer.draw() content = drawer.save(None) if _format == 'svg' and self.global_options['inline_svg'] is True: size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] new_size = (options['maxwidth'], int(size[1] * ratio)) content = drawer.save(new_size) return nodes.raw('', content, format='html') size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] thumb_size = (options['maxwidth'], size[1] * ratio) thumb_filename = self.image_filename(node, prefix='_thumb') if not os.path.isfile(thumb_filename): drawer.filename = thumb_filename drawer.draw() drawer.save(thumb_size) image = nodes.image(uri=thumb_filename, target=filename) else: image = nodes.image(uri=filename) if node['alt']: image['alt'] = node['alt'] return image def description_table(self, diagram): nodes = diagram.traverse_nodes widths = [25] + [50] * (len(FieldItem.desctable) - 1) headers = [FieldItem.attrname[name] for name in FieldItem.desctable] descriptions = [n.to_desctable() for n in nodes()] descriptions.sort(key=cmp_to_key(directives.cmp_node_number)) for i in range(len(headers) - 1, -1, -1): if any(desc[i] for desc in descriptions): pass else: widths.pop(i) headers.pop(i) for desc in descriptions: desc.pop(i) return self._description_table(descriptions, widths, headers) def setup(**kwargs): for key, value in directive_options_default.items(): directive_options[key] = kwargs.get(key, value) rst.directives.register_directive("packetdiag", PacketdiagDirective) nwdiag-1.0.0/src/packetdiag/utils/rst/__init__.py0000644000175000017500000000114412022137642023140 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2012 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/packetdiag/utils/rst/nodes.py0000644000175000017500000000127312053437632022522 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2012 Takeshi KOMIYA # # Licensed 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. from docutils import nodes class packetdiag(nodes.General, nodes.Element): pass nwdiag-1.0.0/src/packetdiag/parser.py0000644000175000017500000001310312217037766020737 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2008/2009 Andrey Vlasovskikh # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. r'''A DOT language parser using funcparserlib. The parser is based on [the DOT grammar][1]. It is pretty complete with a few not supported things: * Ports and compass points * XML identifiers At the moment, the parser builds only a parse tree, not an abstract syntax tree (AST) or an API for dealing with DOT. [1]: http://www.graphviz.org/doc/info/lang.html ''' import re import io from re import MULTILINE, DOTALL from collections import namedtuple from funcparserlib.lexer import make_tokenizer, Token, LexerError from funcparserlib.parser import (some, a, maybe, many, finished, skip) from blockdiag.utils.compat import u ENCODING = 'utf-8' Graph = namedtuple('Graph', 'type id stmts') FieldItem = namedtuple('FieldItem', 'number label attrs') Attr = namedtuple('Attr', 'name value') DefAttrs = namedtuple('DefAttrs', 'object attrs') AttrPlugin = namedtuple('AttrPlugin', 'name attrs') class ParseException(Exception): pass def tokenize(string): """str -> Sequence(Token)""" specs = [ ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)), ('Comment', (r'(//|#).*',)), ('NL', (r'[\r\n]+',)), ('Space', (r'[ \t\r\n]+',)), ('DefLabel', (r':[^\r\n\[]+',)), ('Range', (r'[0-9]+-[0-9]+',)), ('Number', (r'[0-9]+',)), ('FieldListItem', (r'[\*\-]\s*[^\r\n\[]+',)), ('Name', (u('[A-Za-z_0-9\u0080-\uffff]') + u('[A-Za-z_\\-.0-9\u0080-\uffff]*'),)), ('Op', (r'[{}:;,=\[\]]',)), ('String', (r'(?P"|\').*?(? object""" unarg = lambda f: lambda args: f(*args) tokval = lambda x: x.value flatten = lambda list: sum(list, []) n = lambda s: a(Token('Name', s)) >> tokval op = lambda s: a(Token('Op', s)) >> tokval op_ = lambda s: skip(op(s)) _id = some(lambda t: t.type in ['Name', 'Number', 'String', 'Units'] ).named('id') >> tokval number = some(lambda t: t.type == 'Number').named('number') >> tokval _range = some(lambda t: t.type == 'Range').named('range') >> tokval field_list_item = some(lambda t: t.type == 'FieldListItem' ).named('itemize') >> tokval deflabel = some(lambda t: t.type == 'DefLabel').named('deflabel') >> tokval field_label = lambda text: re.sub("^:\s*(.*?)\s*;?$", "\\1", text) make_field_item = (lambda no, text, attr: FieldItem(no, field_label(text), attr)) make_field_list_item = (lambda text, attr: FieldItem(None, re.sub("^.\s*", "", text), attr)) a_list = ( _id + maybe(op_('=') + _id) + skip(maybe(op(','))) >> unarg(Attr)) attr_list = ( many(op_('[') + many(a_list) + op_(']')) >> flatten) numbered_field_item_stmt = ( (number | _range) + deflabel + attr_list >> unarg(make_field_item)) nonnumbered_field_item_stmt = ( field_list_item + attr_list >> unarg(make_field_list_item)) field_item_stmt = ( numbered_field_item_stmt | nonnumbered_field_item_stmt ) # plugin definition plugin_stmt = ( skip(n('plugin')) + _id + attr_list >> unarg(AttrPlugin)) stmt = ( field_item_stmt | plugin_stmt | a_list ) stmt_list = many(stmt + skip(maybe(op(';')))) graph = ( maybe(n('diagram') | n('pktdiag')) + maybe(_id) + op_('{') + stmt_list + op_('}') >> unarg(Graph)) dotfile = graph + skip(finished) return dotfile.parse(seq) def sort_tree(tree): def weight(node): if isinstance(node, (Attr, DefAttrs, AttrPlugin)): return 1 else: return 2 if hasattr(tree, 'stmts'): tree.stmts.sort(key=lambda x: weight(x)) for stmt in tree.stmts: sort_tree(stmt) return tree def parse_string(string): try: tree = parse(tokenize(string)) return sort_tree(tree) except LexerError as e: message = "Got unexpected token at line %d column %d" % e.place raise ParseException(message) except Exception as e: raise ParseException(str(e)) def parse_file(path): code = io.open(path, 'r', encoding='utf-8-sig').read() return parse_string(code) nwdiag-1.0.0/src/packetdiag/elements.py0000644000175000017500000000503312222275125021247 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import re import blockdiag.elements class DiagramNode(blockdiag.elements.DiagramNode): pass class FieldItem(blockdiag.elements.DiagramNode): shape = '_packet_node' desctable = ['number', 'label', 'description'] attrname = dict(number='Seq', label='Name', description='Description') def __init__(self, number, label): super(FieldItem, self).__init__(None) self.label = label self.separated_right = False self.separated_left = False if number is None: self.number = None self.colwidth = 1 else: matched = re.match('^(\d+)-(\d+)$', number) if matched: self.number = int(matched.group(1)) self.colwidth = int(matched.group(2)) - self.number + 1 if self.colwidth <= 0: msg = ("Invalid field size definition: %s: %s\n" % (number, label)) raise AttributeError(msg) else: self.number = int(number) self.colwidth = 1 def set_height(self, value): self.colheight = int(value) def set_len(self, value): self.colwidth = int(value) class Diagram(blockdiag.elements.Diagram): _DiagramNode = FieldItem def __init__(self): super(Diagram, self).__init__() self.colwidth = 16 self.scale_interval = None self.scale_direction = "left_to_right" self.int_attrs.append('scale_interval') @property def fields(self): return self.nodes def set_scale_direction(self, value): value = value.lower() if value in ('ltr', 'left_to_right'): self.scale_direction = 'left_to_right' elif value in ('rtl', 'right_to_left'): self.scale_direction = 'right_to_left' else: msg = "WARNING: unknown scale_direction: %s\n" % value raise AttributeError(msg) nwdiag-1.0.0/src/packetdiag/metrics.py0000644000175000017500000000446112217033744021110 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import blockdiag.metrics from collections import defaultdict, namedtuple from blockdiag.utils import Box, XY class DiagramMetrics(blockdiag.metrics.DiagramMetrics): def __init__(self, diagram, **kwargs): span_height = self.span_height span_width = self.span_width self.span_height = 0 self.span_width = 0 self.node_width = self.cellsize * 3 self.colwidth = diagram.colwidth super(DiagramMetrics, self).__init__(diagram, **kwargs) # reset node_width FORCE self.spreadsheet.node_width = defaultdict(lambda: self.node_width) self.spreadsheet.set_span_height(0, span_height * 2) self.spreadsheet.set_span_height(diagram.colheight, span_height) self.spreadsheet.set_span_width(0, span_width) self.spreadsheet.set_span_width(diagram.colwidth, span_width) def measure_line(self, n): _Node = namedtuple('Node', 'xy') if n == self.colwidth: node = _Node(XY(n - 1, 0)) pt = self.spreadsheet._node_topleft(node, use_padding=False) pt = pt.shift(x=self.node_width) else: node = _Node(XY(n, 0)) pt = self.spreadsheet._node_topleft(node, use_padding=False) if n * 2.0 % self.colwidth == 0: return (XY(pt.x, pt.y - self.cellsize * 4), pt) elif n * 4.0 % self.colwidth == 0: return (XY(pt.x, pt.y - self.cellsize * 4), pt) else: return (XY(pt.x, pt.y - self.cellsize * 2), pt) def measure_label(self, n): top, _ = self.measure_line(n) cellsize = self.cellsize return Box(top.x - cellsize * 4, top.y - cellsize * 3, top.x + cellsize * 4, top.y) nwdiag-1.0.0/src/packetdiag/tests/0000755000175000017500000000000012224001064020211 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/packetdiag/tests/test_rst_directives.py0000644000175000017500000004773212222275125024701 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- import sys if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest import os import io import tempfile from blockdiag.tests.utils import stderr_wrapper, with_pil from docutils import nodes from docutils.core import publish_doctree, publish_parts from docutils.parsers.rst import directives as docutils from packetdiag.utils.rst import directives class TestRstDirectives(unittest.TestCase): def setUp(self): docutils.register_directive('packetdiag', directives.PacketdiagDirectiveBase) self.tmpdir = tempfile.mkdtemp() def tearDown(self): if 'packetdiag' in docutils._directives: del docutils._directives['packetdiag'] for file in os.listdir(self.tmpdir): os.unlink(self.tmpdir + "/" + file) os.rmdir(self.tmpdir) def test_setup(self): directives.setup() options = directives.directive_options self.assertIn('packetdiag', docutils._directives) self.assertEqual(directives.PacketdiagDirective, docutils._directives['packetdiag']) self.assertEqual('PNG', options['format']) self.assertEqual(False, options['antialias']) self.assertEqual(None, options['fontpath']) self.assertEqual(False, options['nodoctype']) self.assertEqual(False, options['noviewbox']) self.assertEqual(False, options['inline_svg']) def test_setup_with_args(self): directives.setup(format='SVG', antialias=True, fontpath='/dev/null', nodoctype=True, noviewbox=True, inline_svg=True) options = directives.directive_options self.assertIn('packetdiag', docutils._directives) self.assertEqual(directives.PacketdiagDirective, docutils._directives['packetdiag']) self.assertEqual('SVG', options['format']) self.assertEqual(True, options['antialias']) self.assertEqual('/dev/null', options['fontpath']) self.assertEqual(True, options['nodoctype']) self.assertEqual(True, options['noviewbox']) self.assertEqual(True, options['inline_svg']) @stderr_wrapper def test_base_noargs(self): text = ".. packetdiag::" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_block(self): text = ".. packetdiag::\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.packetdiag, type(doctree[0])) self.assertEqual('{ 1: server\n2: database\n}', doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_emptyblock(self): text = ".. packetdiag::\n\n \n" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_filename(self): dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'diagrams/tcp.diag') text = ".. packetdiag:: %s" % filename doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.packetdiag, type(doctree[0])) self.assertEqual(io.open(filename).read(), doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_filename_not_exists(self): text = ".. packetdiag:: unknown.diag" doctree = publish_doctree(text) self.assertEqual(nodes.system_message, type(doctree[0])) @stderr_wrapper def test_base_with_block_and_filename(self): text = ".. packetdiag:: unknown.diag\n\n { 1: server\n 2: database }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_options(self): text = ".. packetdiag::\n :alt: hello world\n :desctable:\n" + \ " :maxwidth: 100\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.packetdiag, type(doctree[0])) self.assertEqual('{ 1: server\n2: database\n}', doctree[0]['code']) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(None, doctree[0]['options']['desctable']) self.assertEqual(100, doctree[0]['options']['maxwidth']) def test_block(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. packetdiag::\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_alt(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. packetdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_fontpath1(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath=['dummy.ttf'], outputdir=self.tmpdir) text = ".. packetdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" publish_doctree(text) def test_block_fontpath2(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath='dummy.ttf', outputdir=self.tmpdir) text = ".. packetdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" publish_doctree(text) def test_caption(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. packetdiag::\n :caption: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.figure, type(doctree[0])) self.assertEqual(2, len(doctree[0])) self.assertEqual(nodes.image, type(doctree[0][0])) self.assertEqual(nodes.caption, type(doctree[0][1])) self.assertEqual(1, len(doctree[0][1])) self.assertEqual(nodes.Text, type(doctree[0][1][0])) self.assertEqual('hello world', doctree[0][1][0]) def test_block_maxwidth(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. packetdiag::\n :maxwidth: 100\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse(0, doctree[0]['target'].index(self.tmpdir)) def test_block_nodoctype_false(self): directives.setup(format='SVG', outputdir=self.tmpdir, nodoctype=False) text = ".. packetdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) svg = open(doctree[0]['uri']).read() self.assertEqual("\n" "\n" "\n" "`_ . Setup ===== by easy_install ---------------- Make environment:: $ easy_install nwdiag by buildout ------------ Make environment:: $ hg clone http://bitbucket.org/tk0miya/nwdiag $ cd nwdiag $ python bootstrap.py $ bin/buildout spec-text setting sample ======================== Few examples are available. You can get more examples at `blockdiag.com `_ . simple.diag ------------ simple.diag is simply define nodes and transitions by dot-like text format:: diagram { A -> B -> C; lane you { A; B; } lane me { C; } } Usage ===== Execute nwdiag command:: $ nwdiag simple.diag $ ls simple.png simple.png Requirements ============ * Python 2.6, 2.7, 3.2, 3.3 * Pillow 2.2.1 * funcparserlib 0.3.6 * setuptools License ======= Apache License 2.0 History ======= 1.0.0 (2013-10-05) ------------------ * Support python 3.2 and 3.3 (thanks to @masayuko) * Drop supports for python 2.4 and 2.5 * Replace dependency: PIL -> Pillow 0.9.4 (2012-12-20) ------------------ * Fix bugs 0.9.3 (2012-12-17) ------------------ * [rackdiag] Allow multiple rackitems in same level * Fix bugs 0.9.2 (2012-11-17) ------------------ * [rackdiag] Add auto-numbering feature * Fix bugs 0.9.1 (2012-10-28) ------------------ * Fix bugs 0.9.0 (2012-10-22) ------------------ * Optimize algorithm for rendering shadow * Add options to docutils directive * [packetdiag] represent splitted packets with dashed-line * Fix bugs 0.8.2 (2012-09-29) ------------------ * Fix bugs 0.8.1 (2012-09-08) ------------------ * Add packetdiag_sphinxhelper 0.8.0 (2012-09-06) ------------------ * Add packetdiag which supports generating packet-header diaagram * [nwdiag] Add diagram attribute: external_connector * Update to new package structure (blockdiag >= 1.1.2) * Allow # to comment syntax * Fix bugs 0.7.0 (2011-11-19) ------------------ * Accept N/A rack-unit * Add fontfamily attribute for switching fontface * Fix bugs 0.6.1 (2011-11-06) ------------------ * [rackdiag] Support multiple racks rendering * [rackdiag] Add rack attribute: unit-height, weight, ampere, ascending * [rackdiag] Support putting multiple items to same rack-unit 0.6.0 (2011-11-06) ------------------ * Add rackdiag which supports genarating rack-structure diagram * Add docutils extension * Fix bugs 0.5.3 (2011-11-01) ------------------ * Add class feature (experimental) 0.5.2 (2011-11-01) ------------------ * Follow blockdiag-0.9.7 interface 0.5.1 (2011-10-19) ------------------ * Follow blockdiag-0.9.5 interface 0.5.0 (2011-10-07) ------------------ * Change shape of trunkline like a pipeline * Add network attribute: color * Add diagram attribute: default_network_color 0.4.2 (2011-09-30) ------------------ * Add diagram attributes: default_text_color * Fix bugs 0.4.1 (2011-09-26) ------------------ * Add diagram attributes: default_node_color, default_group_color and default_line_color * Fix bugs 0.4.0 (2011-08-09) ------------------ * Add syntax for peer network 0.3.3 (2011-08-07) ------------------ * Add syntax for peer network (experimental) * Fix bugs 0.3.2 (2011-08-03) ------------------ * Fix bugs 0.3.1 (2011-08-01) ------------------ * Fix bugs 0.3.0 (2011-07-18) ------------------ * Upgrade layout engine * Allow to note ip addresses directly * Allow node_id including hyphen chars * Fix bugs 0.2.7 (2011-07-05) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Allow "." to id token * Support input from stdin * Support multiple node address (using comma) * Do not sort networks (ordered as declarations) * Fix bugs 0.2.5 (2011-06-29) ------------------ * Adjust parameters for span and margin 0.2.4 (2011-05-17) ------------------ * Add --version option * Fix bugs 0.2.3 (2011-05-15) ------------------ * Fix bugs 0.2.2 (2011-05-15) ------------------ * Implement grouping nodes 0.2.1 (2011-05-14) ------------------ * Change license to Apache License 2.0 * Support blockdiag 0.8.1 core interface 0.2.0 (2011-05-02) ------------------ * Rename package to nwdiag 0.1.6 (2011-04-30) ------------------ * Fix bugs 0.1.5 (2011-04-26) ------------------ * Fix bugs 0.1.4 (2011-04-25) ------------------ * Implement jumped edge * Fix bugs 0.1.3 (2011-04-23) ------------------ * Fix sphinxcontrib_netdiag was not worked 0.1.2 (2011-04-23) ------------------ * Support multi-homed host * Drop network-bridge sytanx (cf. net_a -- net_b) 0.1.1 (2011-04-10) ------------------ * Fix bugs 0.1.0 (2011-04-09) ------------------ * First release Keywords: diagram,generator Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Documentation Classifier: Topic :: Text Processing :: Markup nwdiag-1.0.0/src/nwdiag.egg-info/dependency_links.txt0000644000175000017500000000000112224001061023761 0ustar katsuwokatsuwo00000000000000 nwdiag-1.0.0/src/nwdiag.egg-info/top_level.txt0000644000175000017500000000013512224001061022444 0ustar katsuwokatsuwo00000000000000packetdiag_sphinxhelper rackdiag packetdiag nwdiag rackdiag_sphinxhelper nwdiag_sphinxhelper nwdiag-1.0.0/src/nwdiag.egg-info/requires.txt0000644000175000017500000000012312224001061022307 0ustar katsuwokatsuwo00000000000000setuptools blockdiag>=1.3.0 [rst] docutils [pdf] reportlab [test] Nose pep8>=1.3nwdiag-1.0.0/src/nwdiag.egg-info/SOURCES.txt0000644000175000017500000000401212224001064021577 0ustar katsuwokatsuwo00000000000000LICENSE MANIFEST.in bootstrap.py buildout.cfg nwdiag.1 rackdiag.1 setup.cfg setup.py examples/nwdiag/node_groups1.diag examples/nwdiag/node_groups1.png examples/nwdiag/node_groups1.svg examples/nwdiag/node_groups2.diag examples/nwdiag/node_groups2.png examples/nwdiag/node_groups2.svg examples/nwdiag/simple.diag examples/nwdiag/simple.png examples/nwdiag/simple.svg examples/packetdiag/tcp.diag src/README.txt src/nwdiag_sphinxhelper.py src/packetdiag_sphinxhelper.py src/rackdiag_sphinxhelper.py src/nwdiag/__init__.py src/nwdiag/builder.py src/nwdiag/command.py src/nwdiag/drawer.py src/nwdiag/elements.py src/nwdiag/metrics.py src/nwdiag/parser.py src/nwdiag.egg-info/PKG-INFO src/nwdiag.egg-info/SOURCES.txt src/nwdiag.egg-info/dependency_links.txt src/nwdiag.egg-info/entry_points.txt src/nwdiag.egg-info/requires.txt src/nwdiag.egg-info/top_level.txt src/nwdiag/tests/__init__.py src/nwdiag/tests/test_builder.py src/nwdiag/tests/test_generate_diagram.py src/nwdiag/tests/test_pep8.py src/nwdiag/tests/test_rst_directives.py src/nwdiag/tests/utils.py src/nwdiag/utils/__init__.py src/nwdiag/utils/rst/__init__.py src/nwdiag/utils/rst/directives.py src/nwdiag/utils/rst/nodes.py src/packetdiag/__init__.py src/packetdiag/builder.py src/packetdiag/command.py src/packetdiag/drawer.py src/packetdiag/elements.py src/packetdiag/metrics.py src/packetdiag/noderenderers.py src/packetdiag/parser.py src/packetdiag/tests/test_pep8.py src/packetdiag/tests/test_rst_directives.py src/packetdiag/utils/__init__.py src/packetdiag/utils/rst/__init__.py src/packetdiag/utils/rst/directives.py src/packetdiag/utils/rst/nodes.py src/rackdiag/__init__.py src/rackdiag/builder.py src/rackdiag/command.py src/rackdiag/drawer.py src/rackdiag/elements.py src/rackdiag/metrics.py src/rackdiag/parser.py src/rackdiag/tests/test_builder.py src/rackdiag/tests/test_pep8.py src/rackdiag/tests/test_rst_directives.py src/rackdiag/utils/__init__.py src/rackdiag/utils/math.py src/rackdiag/utils/rst/__init__.py src/rackdiag/utils/rst/directives.py src/rackdiag/utils/rst/nodes.pynwdiag-1.0.0/src/nwdiag.egg-info/entry_points.txt0000644000175000017500000000035512224001061023214 0ustar katsuwokatsuwo00000000000000 [console_scripts] nwdiag = nwdiag.command:main rackdiag = rackdiag.command:main packetdiag = packetdiag.command:main [blockdiag_noderenderer] _packet_node = packetdiag.noderenderers nwdiag-1.0.0/src/nwdiag/0000755000175000017500000000000012224001064016224 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/nwdiag/builder.py0000644000175000017500000002430612217033660020242 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from collections import namedtuple from nwdiag import parser from nwdiag.elements import (Diagram, DiagramNode, DiagramEdge, Network, Route, NodeGroup) from blockdiag.utils import unquote, XY class DiagramTreeBuilder: def build(self, tree): self.diagram = Diagram() self.instantiate(None, None, tree) for network in self.diagram.networks: nodes = [n for n in self.diagram.nodes if network in n.networks] if len(nodes) == 0: self.diagram.networks.remove(network) for i, network in enumerate(self.diagram.networks): network.xy = XY(0, i) for subgroup in self.diagram.groups: if len(subgroup.nodes) == 0: self.diagram.groups.remove(subgroup) for node in self.diagram.nodes: if len(node.networks) == 0: msg = "DiagramNode %s does not belong to any networks" raise RuntimeError(msg % node.id) # show networks including same nodes for nw in self.diagram.networks: if nw.hidden and len(nw.nodes) == 2: nodes = nw.nodes is_same = lambda x: set(nodes) & set(x.nodes) == set(nodes) for n in self.diagram.networks: if n != nw and is_same(n): nw.hidden = False break # show network for multiple peer networks from same node nodes = [] for nw in self.diagram.networks: if nw.hidden and len(nw.nodes) == 2: nodes.append(nw.nodes[0]) # parent node (FROM node) for node in [n for n in set(nodes) if nodes.count(n) > 1]: for network in node.networks: if len(network.nodes) == 2: network.hidden = False return self.diagram def instantiate(self, network, group, tree): for stmt in tree.stmts: if isinstance(stmt, parser.Node): node = DiagramNode.get(stmt.id) node.set_attributes(network, stmt.attrs) if group: if (node.group and node.group != self.diagram and (group != node.group)): msg = "DiagramNode could not belong to two groups" raise RuntimeError(msg) node.group = group group.nodes.append(node) elif node.group is None: node.group = self.diagram if network is not None: if network not in node.networks: node.networks.append(network) if node not in network.nodes: network.nodes.append(node) if node not in self.diagram.nodes: self.diagram.nodes.append(node) elif isinstance(stmt, parser.Network): subnetwork = Network.get(stmt.id) subnetwork.label = stmt.id if subnetwork not in self.diagram.networks: self.diagram.networks.append(subnetwork) substmt = namedtuple('Statements', 'stmts')([]) for s in stmt.stmts: if isinstance(s, parser.DefAttrs): subnetwork.set_attributes(s.attrs) else: substmt.stmts.append(s) self.instantiate(subnetwork, group, substmt) elif isinstance(stmt, parser.SubGraph): subgroup = NodeGroup.get(stmt.id) if subgroup not in self.diagram.groups: self.diagram.groups.append(subgroup) substmt = namedtuple('Statements', 'stmts')([]) for s in stmt.stmts: if isinstance(s, parser.DefAttrs): subgroup.set_attributes(s.attrs) else: substmt.stmts.append(s) self.instantiate(network, subgroup, substmt) elif isinstance(stmt, parser.Edge): nodes = [DiagramNode.get(n) for n in stmt.nodes] for node in nodes: if node.group is None: node.group = self.diagram if node not in self.diagram.nodes: self.diagram.nodes.append(node) if len(nodes[0].networks) == 0: nw = Network.create_anonymous([nodes[0]]) if nw: self.diagram.networks.append(nw) for i in range(len(nodes) - 1): nw = Network.create_anonymous(nodes[i:i + 2], stmt.attrs) if nw: self.diagram.networks.append(nw) elif isinstance(stmt, parser.Route): nodes = [DiagramNode.get(n) for n in stmt.nodes] for node1, node2 in zip(nodes[:-1], nodes[1:]): route = Route(node1, node2) route.set_attributes(stmt.attrs) self.diagram.routes.append(route) elif isinstance(stmt, parser.DefAttrs): self.diagram.set_attributes(stmt.attrs) elif isinstance(stmt, parser.AttrPlugin): self.diagram.set_plugin(stmt.name, stmt.attrs) elif isinstance(stmt, parser.AttrClass): name = unquote(stmt.name) Diagram.classes[name] = stmt return network class DiagramLayoutManager: def __init__(self, diagram): self.diagram = diagram self.coordinates = [] def run(self): self.do_layout() self.diagram.fixiate() def do_layout(self): self.layout_nodes() self.set_network_size() def layout_nodes(self, group=None): networks = self.diagram.networks if group: nodes = (n for n in self.diagram.nodes if n.group == group) else: nodes = self.diagram.nodes for node in nodes: if node.layouted: continue joined = [g for g in node.networks if g.hidden is False] y1 = min(networks.index(g) for g in node.networks) if joined: y2 = max(networks.index(g) for g in joined) else: y2 = y1 if node.group and node.group != self.diagram and group: starts = min(n.xy.x for n in group.nodes if n.layouted) else: nw = [n for n in node.networks if n.xy.y == y1][0] nodes = [n for n in self.diagram.nodes if nw in n.networks] layouted = [n for n in nodes if n.xy.x > 0] starts = 0 if layouted: layouted.sort(key=lambda a: a.xy.x) basenode = layouted[0] commonnw = set(basenode.networks) & set(node.networks) if basenode.xy.y == y1: starts = basenode.xy.x + 1 elif commonnw and list(commonnw)[0].hidden is True: starts = basenode.xy.x else: starts = basenode.xy.x + 1 - len(nodes) if starts < 0: starts = 0 for x in range(starts, len(self.diagram.nodes)): points = [XY(x, y) for y in range(y1, y2 + 1)] if not set(points) & set(self.coordinates): node.xy = XY(x, y1) node.layouted = True self.coordinates += points break if node.group and node.group != self.diagram and group is None: self.layout_nodes(node.group) if group: self.set_coordinates(group) def set_coordinates(self, group): self.set_group_size(group) xy = group.xy for i in range(xy.x, xy.x + group.colwidth): for j in range(xy.y, xy.y + group.colheight): self.coordinates.append(XY(i, j)) def set_network_size(self): for network in self.diagram.networks: nodes = [n for n in self.diagram.nodes if network in n.networks] nodes.sort(key=lambda a: a.xy.x) x0 = min(n.xy.x for n in nodes) network.xy = XY(x0, network.xy.y) x1 = max(n.xy.x for n in nodes) network.colwidth = x1 - x0 + 1 def set_group_size(self, group): nodes = list(group.nodes) nodes.sort(key=lambda a: a.xy.x) x0 = min(n.xy.x for n in nodes) y0 = min(n.xy.y for n in nodes) group.xy = XY(x0, y0) x1 = max(n.xy.x for n in nodes) y1 = max(n.xy.y for n in nodes) group.colwidth = x1 - x0 + 1 group.colheight = y1 - y0 + 1 class ScreenNodeBuilder: @classmethod def build(cls, tree): DiagramNode.clear() DiagramEdge.clear() NodeGroup.clear() Network.clear() diagram = DiagramTreeBuilder().build(tree) DiagramLayoutManager(diagram).run() diagram = cls.update_network_status(diagram) return diagram @classmethod def update_network_status(cls, diagram): for node in diagram.nodes: above = [nw for nw in node.networks if nw.xy.y <= node.xy.y] if len(above) > 1 and [nw for nw in above if nw.hidden]: for nw in above: nw.hidden = False below = [nw for nw in node.networks if nw.xy.y > node.xy.y] if len(below) > 1 and [nw for nw in below if nw.hidden]: for nw in below: nw.hidden = False return diagram nwdiag-1.0.0/src/nwdiag/__init__.py0000644000175000017500000000121312223771702020346 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. __version__ = '1.0.0' nwdiag-1.0.0/src/nwdiag/drawer.py0000644000175000017500000001501612217032100020062 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division import blockdiag.drawer from nwdiag.metrics import DiagramMetrics from blockdiag.utils import Box, XY class DiagramDraw(blockdiag.drawer.DiagramDraw): def create_metrics(self, *args, **kwargs): return DiagramMetrics(*args, **kwargs) def __init__(self, _format, diagram, filename=None, **kwargs): super(DiagramDraw, self).__init__(_format, diagram, filename, **kwargs) self.drawer.set_options(jump_forward='vertical', jump_radius=self.metrics.jump_radius, jump_shift=self.metrics.jump_shift) @property def groups(self): return self.diagram.groups def pagesize(self, scaled=False): # FIXME: force int'ize xy = super(DiagramDraw, self).pagesize(scaled) return XY(int(xy.x), int(xy.y)) def _draw_background(self): super(DiagramDraw, self)._draw_background() self.trunklines_shadow() def trunklines_shadow(self): for network in self.diagram.networks: if network.hidden is False and network.color != 'none': self.trunkline(network, shadow=True) def trunklines(self): metrics = self.metrics for network in self.diagram.networks: if network.hidden is False: self.trunkline(network) if (self.diagram.external_connector and (network == self.diagram.networks[0])): r = metrics.trunk_diameter // 2 pt = metrics.network(network).top pt0 = XY(pt.x, pt.y - metrics.span_height * 2 // 3) pt1 = XY(pt.x, pt.y - r) self.drawer.line([pt0, pt1], fill=network.linecolor) def trunkline(self, network, shadow=False): metrics = self.metrics m = metrics.network(network) r = metrics.trunk_diameter // 2 pt1, pt2 = m.trunkline box = Box(pt1.x, pt1.y - r, pt2.x, pt2.y + r) if shadow: xdiff = self.metrics.shadow_offset.x ydiff = self.metrics.shadow_offset.y // 2 box = Box(pt1.x + xdiff, pt1.y - r + ydiff, pt2.x + xdiff, pt2.y + r + ydiff) if self.format == 'SVG': from blockdiag.imagedraw.simplesvg import pathdata path = pathdata(box[0], box[1]) path.line(box[2], box[1]) path.ellarc(r // 2, r, 0, 0, 1, box[2], box[3]) path.line(box[0], box[3]) path.ellarc(r // 2, r, 0, 0, 1, box[0], box[1]) if shadow: self.drawer.path(path, fill=self.shadow, filter='blur') else: self.drawer.path(path, fill=network.color, outline=network.linecolor) path = pathdata(box[2], box[3]) path.ellarc(r // 2, r, 0, 0, 1, box[2], box[1]) self.drawer.path(path, fill='none', outline=network.linecolor) # for edge jumping line = (XY(box[0], box[1]), XY(box[2], box[1])) self.drawer.line(line, fill='none', jump=True) else: lsection = Box(box[0] - r // 2, box[1], box[0] + r // 2, box[3]) rsection = Box(box[2] - r // 2, box[1], box[2] + r // 2, box[3]) if shadow: color = self.shadow _filter = 'blur' else: color = network.color _filter = None # fill background self.drawer.rectangle(box, outline=color, fill=color, filter=_filter) self.drawer.ellipse(lsection, outline=color, fill=color, filter=_filter) self.drawer.ellipse(rsection, outline=color, fill=color, filter=_filter) if not shadow: upper = (XY(box[0], box[1]), XY(box[2], box[1])) self.drawer.line(upper, fill=network.linecolor, jump=True) bottom = (XY(box[0], box[3]), XY(box[2], box[3])) self.drawer.line(bottom, fill=network.linecolor, jump=True) self.drawer.arc(lsection, 90, 270, fill=network.linecolor) self.drawer.ellipse(rsection, outline=network.linecolor, fill=network.color) def _draw_elements(self): self.trunklines() for network in self.diagram.networks: self.trunkline_label(network) super(DiagramDraw, self)._draw_elements() def trunkline_label(self, network): if network.display_label: m = self.metrics.network(network) self.drawer.textarea(m.textbox, network.display_label, self.metrics.font_for(network), fill=network.textcolor, halign="right") def node(self, node, **kwargs): m = self.metrics for connector in m.node(node).connectors: self.draw_connector(connector) if hasattr(connector, 'subject'): network = connector.subject.network else: network = connector.network if network in node.address: label = node.address[network] self.drawer.textarea(connector.textbox, label, self.metrics.font_for(node), fill=node.textcolor, halign="left") super(DiagramDraw, self).node(node, **kwargs) def draw_connector(self, connector): self.drawer.line(connector.line, fill=connector.network.linecolor, jump=True) def group_label(self, group): if group.label: m = self.metrics.cell(group) self.drawer.textarea(m.grouplabelbox, group.label, self.metrics.font_for(group), valign='top', fill=group.textcolor) nwdiag-1.0.0/src/nwdiag/utils/0000755000175000017500000000000012224001064017364 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/nwdiag/utils/__init__.py0000644000175000017500000000114411655450402021510 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/nwdiag/utils/rst/0000755000175000017500000000000012224001064020174 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/nwdiag/utils/rst/directives.py0000644000175000017500000000723112222275125022723 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import os from docutils import nodes from docutils.parsers import rst from nwdiag import parser from nwdiag.builder import ScreenNodeBuilder from nwdiag.drawer import DiagramDraw from nwdiag.utils.rst.nodes import nwdiag from blockdiag.utils.rst import directives directive_options_default = dict(format='PNG', antialias=False, fontpath=None, outputdir=None, nodoctype=False, noviewbox=False, inline_svg=False, ignore_pil=False) directive_options = {} class NwdiagDirectiveBase(directives.BlockdiagDirectiveBase): """ Directive to insert arbitrary dot markup. """ name = "nwdiag" node_class = nwdiag class NwdiagDirective(directives.BlockdiagDirective): name = "nwdiag" node_class = nwdiag @property def global_options(self): return directive_options def node2diagram(self, node): tree = parser.parse_string(node['code']) return ScreenNodeBuilder.build(tree) def node2image(self, node, diagram): options = node['options'] filename = self.image_filename(node) fontmap = self.create_fontmap() _format = self.global_options['format'].lower() if _format == 'svg' and self.global_options['inline_svg'] is True: filename = None kwargs = dict(self.global_options) del kwargs['format'] drawer = DiagramDraw(_format, diagram, filename, fontmap=fontmap, **kwargs) if filename is None or not os.path.isfile(filename): drawer.draw() content = drawer.save(None) if _format == 'svg' and self.global_options['inline_svg'] is True: size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] new_size = (options['maxwidth'], int(size[1] * ratio)) content = drawer.save(new_size) return nodes.raw('', content, format='html') size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] thumb_size = (options['maxwidth'], size[1] * ratio) thumb_filename = self.image_filename(node, prefix='_thumb') if not os.path.isfile(thumb_filename): drawer.filename = thumb_filename drawer.draw() drawer.save(thumb_size) image = nodes.image(uri=thumb_filename, target=filename) else: image = nodes.image(uri=filename) if node['alt']: image['alt'] = node['alt'] return image def setup(**kwargs): for key, value in directive_options_default.items(): directive_options[key] = kwargs.get(key, value) rst.directives.register_directive("nwdiag", NwdiagDirective) nwdiag-1.0.0/src/nwdiag/utils/rst/__init__.py0000644000175000017500000000114411655450373022327 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/nwdiag/utils/rst/nodes.py0000644000175000017500000000126712053437632021702 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from docutils import nodes class nwdiag(nodes.General, nodes.Element): pass nwdiag-1.0.0/src/nwdiag/parser.py0000644000175000017500000001454312217040124020103 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2008/2009 Andrey Vlasovskikh # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. r'''A DOT language parser using funcparserlib. The parser is based on [the DOT grammar][1]. It is pretty complete with a few not supported things: * Ports and compass points * XML identifiers At the moment, the parser builds only a parse tree, not an abstract syntax tree (AST) or an API for dealing with DOT. [1]: http://www.graphviz.org/doc/info/lang.html ''' import io from re import MULTILINE, DOTALL from collections import namedtuple from funcparserlib.lexer import make_tokenizer, Token, LexerError from funcparserlib.parser import (some, a, maybe, many, finished, skip) from blockdiag.utils.compat import u ENCODING = 'utf-8' Graph = namedtuple('Graph', 'type id stmts') Network = namedtuple('Network', 'id stmts') SubGraph = namedtuple('SubGraph', 'id stmts') Node = namedtuple('Node', 'id attrs') Attr = namedtuple('Attr', 'name value') Edge = namedtuple('Edge', 'nodes attrs') Route = namedtuple('Route', 'nodes attrs') AttrPlugin = namedtuple('AttrPlugin', 'name attrs') AttrClass = namedtuple('AttrClass', 'name attrs') DefAttrs = namedtuple('DefAttrs', 'object attrs') class ParseException(Exception): pass def tokenize(string): """str -> Sequence(Token)""" specs = [ ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)), ('Comment', (r'(//|#).*',)), ('NL', (r'[\r\n]+',)), ('Space', (r'[ \t\r\n]+',)), ('Name', (u('[A-Za-z_\u0080-\uffff]') + u('[A-Za-z_\\-.0-9\u0080-\uffff]*'),)), ('Op', (r'([{};,=\[\]]|--|->)',)), ('IPAddr', (r'([0-9]+(\.[0-9]+){3}|[:0-9a-fA-F]+)',)), ('Number', (r'-?(\.[0-9]+)|([0-9]+(\.[0-9]*)?)',)), ('String', (r'(?P"|\').*?(? object""" unarg = lambda f: lambda args: f(*args) tokval = lambda x: x.value flatten = lambda list: sum(list, []) n = lambda s: a(Token('Name', s)) >> tokval op = lambda s: a(Token('Op', s)) >> tokval op_ = lambda s: skip(op(s)) _id = some(lambda t: t.type in ['Name', 'IPAddr', 'Number', 'String'] ).named('id') >> tokval make_graph_attr = lambda args: DefAttrs(u('graph'), [Attr(*args)]) make_edge = lambda x, x2, xs, attrs: Edge([x, x2] + xs, attrs) make_route = lambda x, x2, xs, attrs: Route([x, x2] + xs, attrs) node_id = _id # + maybe(port) a_list = ( _id + maybe(op_('=') + _id) + skip(maybe(op(','))) >> unarg(Attr)) attr_list = ( many(op_('[') + many(a_list) + op_(']')) >> flatten) edge_rhs = op_('--') + node_id edge_stmt = ( node_id + edge_rhs + many(edge_rhs) + attr_list >> unarg(make_edge)) graph_attr = _id + op_('=') + _id >> make_graph_attr node_stmt = node_id + attr_list >> unarg(Node) # We use a forward_decl becaue of circular definitions like (stmt_list -> # stmt -> subgraph -> stmt_list) group_stmt = ( graph_attr | node_stmt ) group_stmt_list = many(group_stmt + skip(maybe(op(';')))) group = ( skip(n('group')) + maybe(_id) + op_('{') + group_stmt_list + op_('}') >> unarg(SubGraph)) network_stmt = ( graph_attr | group | node_stmt ) network_stmt_list = many(network_stmt + skip(maybe(op(';')))) network = ( skip(n('network')) + maybe(_id) + op_('{') + network_stmt_list + op_('}') >> unarg(Network)) route_rhs = op_('->') + node_id route = ( skip(n('route')) + op_('{') + node_id + route_rhs + many(route_rhs) + attr_list + skip(maybe(op(';'))) + op_('}') >> unarg(make_route)) class_stmt = ( skip(n('class')) + node_id + attr_list >> unarg(AttrClass)) plugin_stmt = ( skip(n('plugin')) + _id + attr_list >> unarg(AttrPlugin)) stmt = ( network | class_stmt | plugin_stmt | group | graph_attr | route | edge_stmt | node_stmt ) stmt_list = many(stmt + skip(maybe(op(';')))) graph = ( maybe(n('diagram') | n('nwdiag')) + maybe(_id) + op_('{') + stmt_list + op_('}') >> unarg(Graph)) dotfile = graph + skip(finished) return dotfile.parse(seq) def sort_tree(tree): def weight(node): if isinstance(node, (Attr, DefAttrs, AttrPlugin, AttrClass)): return 1 else: return 2 if hasattr(tree, 'stmts'): tree.stmts.sort(key=lambda x: weight(x)) for stmt in tree.stmts: sort_tree(stmt) return tree def parse_string(string): try: tree = parse(tokenize(string)) return sort_tree(tree) except LexerError as e: message = "Got unexpected token at line %d column %d" % e.place raise ParseException(message) except Exception as e: raise ParseException(str(e)) def parse_file(path): code = io.open(path, 'r', encoding='utf-8-sig').read() return parse_string(code) nwdiag-1.0.0/src/nwdiag/elements.py0000644000175000017500000001131412222275125020423 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import re import blockdiag.elements from blockdiag.utils import images, unquote class NodeGroup(blockdiag.elements.NodeGroup): pass class DiagramNode(blockdiag.elements.DiagramNode): def __init__(self, _id): super(DiagramNode, self).__init__(_id) self.address = {} self.networks = [] self.layouted = False def set_attributes(self, network, attrs=None): if attrs is None: attrs, network = network, None for attr in attrs: if attr.name == 'address': address = re.sub('\s*,\s*', '\n', unquote(attr.value)) self.address[network] = address else: self.set_attribute(attr) class DiagramEdge(blockdiag.elements.DiagramEdge): pass class Network(blockdiag.elements.NodeGroup): basecolor = (185, 203, 228) linecolor = (0, 0, 0) @classmethod def set_default_linecolor(cls, color): cls.linecolor = images.color_to_rgb(color) @classmethod def set_default_textcolor(cls, color): cls.textcolor = images.color_to_rgb(color) @classmethod def clear(cls): super(Network, cls).clear() cls.basecolor = (185, 203, 228) cls.linecolor = (0, 0, 0) def __init__(self, _id): super(Network, self).__init__(_id) self.address = None self.hidden = False self.colwidth = 1 self.colheight = 1 @classmethod def create_anonymous(cls, nodes, attrs=None): if len(set(nodes)) != len(nodes): msg = "Do not connect same node to peer network: %s" raise RuntimeError(msg % nodes[0].id) # search networks including same nodes is_same = lambda nw: set(nodes) & set(nw.nodes) == set(nodes) if [nw for nw in nodes[0].networks if nw.hidden and is_same(nw)]: return None network = cls(None) network.hidden = True for node in nodes: node.networks.append(network) network.nodes.append(node) if attrs: node.set_attributes(network, attrs) return network @property def display_label(self): if self.label: if self.address: label = "%s\n%s" % (self.label, self.address) else: label = self.label else: label = self.address return label class Route(blockdiag.elements.DiagramEdge): pass class Diagram(blockdiag.elements.Diagram): _DiagramNode = DiagramNode _Network = Network _Route = Route def set_default_linecolor(self, color): super(Diagram, self).set_default_linecolor(color) self._Network.set_default_linecolor(self.linecolor) def set_default_textcolor(self, color): super(Diagram, self).set_default_textcolor(color) self._Network.set_default_text_color(self.textcolor) def set_default_fontsize(self, fontsize): super(Diagram, self).set_default_fontsize(fontsize) self._Network.set_default_fontsize(fontsize) def set_default_fontfamily(self, familyname): super(Diagram, self).set_default_fontfamily(familyname) self._Network.set_default_fontfamily(familyname) def set_default_network_color(self, color): color = images.color_to_rgb(color) self._Network.set_default_color(color) def __init__(self): super(Diagram, self).__init__() self.orientation = 'portrait' self.external_connector = True self.groups = [] self.networks = [] self.routes = [] def set_external_connector(self, value): value = value.lower() if value == 'none': self.external_connector = False else: msg = "WARNING: unknown external connector: %s\n" % value raise AttributeError(msg) def fixiate(self): self.colwidth = max(n.xy.x + n.colwidth for n in self.nodes) self.colheight = max(n.xy.y + n.colheight for n in self.nodes) colheight = max(nw.xy.y + nw.colheight - 1 for nw in self.networks) if self.colheight < colheight: self.colheight = colheight nwdiag-1.0.0/src/nwdiag/metrics.py0000644000175000017500000001367112217033734020267 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division import math from collections import namedtuple import blockdiag.metrics from nwdiag import elements from blockdiag.utils import Box, XY cellsize = blockdiag.metrics.DiagramMetrics.cellsize class DiagramMetrics(blockdiag.metrics.DiagramMetrics): node_width = cellsize * 13 span_width = cellsize * 6 span_height = cellsize * 13 def __init__(self, diagram, **kwargs): super(DiagramMetrics, self).__init__(diagram, **kwargs) self.networks = diagram.networks self.trunk_diameter = self.cellsize self.jump_shift = self.trunk_diameter // 2 self.jump_radius = self.trunk_diameter self.page_padding = [self.span_height // 2, 0, 0, self.node_width] for node in diagram.nodes: bottom = [n for n in node.networks if n.xy.y > node.xy.y] cnwidth = (len(bottom) + 1) * self.cellsize * 2 if self.cell(node).width < cnwidth: node.width = cnwidth self.spreadsheet.set_node_width(node.xy.x, cnwidth) def node(self, node): n = super(DiagramMetrics, self).cell(node) return NodeMetrics(node, self, n.x1, n.y1, n.x2, n.y2) def cell(self, node): if isinstance(node, elements.Network): metrics = super(DiagramMetrics, self).cell(node, use_padding=False) elif isinstance(node, elements.NodeGroup): n = super(DiagramMetrics, self).cell(node) metrics = GroupMetrics(node, self, n.x1, n.y1, n.x2, n.y2) else: metrics = super(DiagramMetrics, self).cell(node) return metrics def network(self, network): n = self.cell(network) return NetworkMetrics(self, n.x1, n.y1, n.x2, n.y2) class NetworkMetrics(blockdiag.metrics.NodeMetrics): @property def trunkline(self): x0 = self.left.x x1 = self.right.x y = self.top.y return [XY(x0, y), XY(x1, y)] @property def top(self): pt = self.box.top return XY(pt.x, pt.y - self.span_height // 2) @property def left(self): pt = self.box.left return XY(pt.x - self.span_width // 2, pt.y) @property def right(self): pt = self.box.right return XY(pt.x + self.span_width // 2, pt.y) @property def textbox(self): x = self.left.x y = self.top.y width = self.node_width * 3 // 2 height = self.node_height return Box(x - width, y - height // 2, x, y + height // 2) class NodeMetrics(blockdiag.metrics.NodeMetrics): def __init__(self, node, metrics, x1, y1, x2, y2): super(NodeMetrics, self).__init__(metrics, x1, y1, x2, y2) self.node = node @property def connectors(self): above = [n for n in self.node.networks if n.xy.y <= self.node.xy.y] above.sort(key=lambda a: a.xy.y, reverse=True) bottom = [n for n in self.node.networks if n.xy.y > self.node.xy.y] bottom.sort(key=lambda a: a.xy.y) Connector = namedtuple('Connector', 'network line textbox') connectors = [] for networks in [above, bottom]: for network in networks: if network.hidden: span = 0 else: span = self.trunk_diameter // 2 if network.xy.y <= self.node.xy.y: x, y2 = self.top y1 = self.network(network).top.y + span else: x, y1 = self.bottom y2 = self.network(network).top.y - span if len(networks) == 1: dx = 0 else: pos = networks.index(network) base_x = (len(networks) - 1) / 2.0 - pos dx = int(math.floor(base_x * self.cellsize * 2)) width = self.node_width + self.span_width textbox = Box(x + dx + self.cellsize // 2, y2 - self.span_height // 2, x + width - self.cellsize // 2, y2) line = [XY(x + dx, y1), XY(x + dx, y2)] cn = Connector(network, line, textbox) connectors.append(cn) return connectors class GroupMetrics(blockdiag.metrics.NodeMetrics): def __init__(self, group, metrics, x1, y1, x2, y2): super(GroupMetrics, self).__init__(metrics, x1, y1, x2, y2) self.is_root_group = False if group.nodes: networks = group.nodes[0].networks[:] networks.sort(key=lambda a: a.xy.y) network = min(networks) if self.top.x == metrics.network(network).top.x: self.is_root_group = True @property def grouplabelbox(self): box = super(GroupMetrics, self).grouplabelbox span = self.cellsize box = Box(box[0], box[1] + span, box[2], box[3] + span) if self.is_root_group: width = (self.node_width + self.span_width) // 2 box = Box(box[0] + width, box[1], box[2] + width, box[3]) return box @property def marginbox(self): box = super(GroupMetrics, self).box margin_x = self.span_height // 2 - self.cellsize margin_y = self.cellsize return Box(box[0] - margin_y, box[1] - margin_x, box[2] + margin_y, box[3] + margin_x) nwdiag-1.0.0/src/nwdiag/tests/0000755000175000017500000000000012224001064017366 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/nwdiag/tests/test_builder.py0000644000175000017500000001476312222275125022451 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- from __future__ import print_function from nwdiag.tests.utils import BuilderTestCase class TestBuilder(BuilderTestCase): def test_diagram_attributes(self): diagram = self.build('diagram_attributes.diag') self.assertEqual(160, diagram.node_width) self.assertEqual(160, diagram.node_height) self.assertEqual(32, diagram.span_width) self.assertEqual(32, diagram.span_height) self.assertEqual(16, diagram.nodes[0].fontsize) self.assertEqual('serif', diagram.nodes[0].fontfamily) self.assertEqual((255, 0, 0), diagram.networks[0].color) self.assertEqual(16, diagram.networks[0].fontsize) self.assertEqual('serif', diagram.networks[0].fontfamily) def test_node_attributes(self): diagram = self.build('node_attributes.diag') network = diagram.networks[0] assert diagram.nodes[0].address[network] == '192.168.0.1' assert diagram.nodes[1].address[network] == '192.168.0.2\n192.168.0.3' def test_node_address_attribute(self): diagram = self.build('node_address_attribute.diag') network = diagram.networks[0] self.assertEqual('192.168.0.1', diagram.nodes[0].address[network]) self.assertEqual('2001:0db8:bd05:01d2:288a:1fc0:0001:10ee', diagram.nodes[1].address[network]) def test_node_including_hyphen_diagram(self): diagram = self.build('node_including_hyphen.diag') self.assertEqual('web-01', diagram.nodes[0].id) self.assertEqual('web-02', diagram.nodes[1].id) def test_single_network_diagram(self): diagram = self.build('single_network.diag') self.assertEqual(1, len(diagram.nodes)) self.assertEqual(1, len(diagram.networks)) self.assertNodeXY(diagram, {'A': (0, 0)}) def test_two_networks_diagram(self): diagram = self.build('two_networks.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1)}) def test_node_belongs_to_multiple_networks_diagram(self): diagram = self.build('node_belongs_to_multiple_networks.diag') self.assertNodeXY(diagram, {'A': (0, 0)}) def test_connected_networks_diagram(self): diagram = self.build('connected_networks.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 1)}) def test_connected_networks2_diagram(self): diagram = self.build('connected_networks2.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (3, 0), 'E': (4, 0), 'Z': (3, 1)}) def test_connected_networks3_diagram(self): diagram = self.build('connected_networks3.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (3, 0), 'E': (4, 0), 'F': (5, 0), 'G': (6, 0), 'H': (1, 1), 'I': (2, 1), 'J': (4, 2), 'K': (5, 2)}) def test_group_inner_network_diagram(self): diagram = self.build('group_inner_network.diag') self.assertEqual(2, len(diagram.groups[0].nodes)) self.assertEqual(2, diagram.groups[0].colwidth) self.assertEqual(1, diagram.groups[0].colheight) self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0)}) def test_group_outer_network_diagram(self): diagram = self.build('group_outer_network.diag') self.assertEqual(2, len(diagram.groups[0].nodes)) self.assertEqual(2, diagram.groups[0].colwidth) self.assertEqual(1, diagram.groups[0].colheight) self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0)}) def test_group_across_network_diagram(self): diagram = self.build('group_across_network.diag') self.assertEqual(3, len(diagram.groups[0].nodes)) self.assertEqual(3, diagram.groups[0].colwidth) self.assertEqual(2, diagram.groups[0].colheight) self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (2, 1)}) def test_group_network_diagram(self): diagram = self.build('group_network.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (0, 1), 'D': (2, 1), 'E': (3, 1)}) def test_split_group_diagram(self): diagram = self.build('split_group.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (3, 0), 'D': (2, 0), 'E': (4, 0)}) def test_peer_network_diagram(self): diagram = self.build('peer_network.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1), 'C': (1, 2)}) def test_peer_network2_diagram(self): diagram = self.build('peer_network2.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (1, 1)}) def test_peer_network3_diagram(self): diagram = self.build('peer_network3.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (1, 1), 'D': (1, 2)}) def test_peer_network4_diagram(self): diagram = self.build('peer_network4.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 1), 'C': (1, 2)}) def test_peer_network5_diagram(self): diagram = self.build('peer_network5.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (1, 0), 'C': (2, 0), 'D': (2, 1)}) def test_peer_network_branched_diagram(self): diagram = self.build('peer_network_branched.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1), 'C': (1, 2), 'D': (1, 3)}) def test_same_peer_network_diagram(self): diagram = self.build('same_peer_network.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1), 'C': (1, 2), 'D': (0, 3)}) def test_group_and_peer_network_diagram(self): diagram = self.build('group_and_peer_network.diag') self.assertNodeXY(diagram, {'A': (0, 0), 'B': (0, 1), 'C': (1, 2)}) def test_peer_network_in_same_node_diagram(self): with self.assertRaises(RuntimeError): self.build('errors/peer_network_in_same_node.diag') nwdiag-1.0.0/src/nwdiag/tests/__init__.py0000644000175000017500000000000012222275125021476 0ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/nwdiag/tests/utils.py0000644000175000017500000000067512222275125021121 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- import os from nwdiag.builder import ScreenNodeBuilder from nwdiag.parser import parse_file from blockdiag.tests.utils import BuilderTestCase as BlockdiagBuilderTestCase class BuilderTestCase(BlockdiagBuilderTestCase): def build(self, filename): basedir = os.path.dirname(__file__) pathname = os.path.join(basedir, 'diagrams', filename) return ScreenNodeBuilder.build(parse_file(pathname)) nwdiag-1.0.0/src/nwdiag/tests/test_rst_directives.py0000644000175000017500000005155212222275125024051 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- import sys if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest import os import io import tempfile from blockdiag.tests.utils import stderr_wrapper, with_pil from docutils import nodes from docutils.core import publish_doctree, publish_parts from docutils.parsers.rst import directives as docutils from nwdiag.utils.rst import directives class TestRstDirectives(unittest.TestCase): def setUp(self): docutils.register_directive('nwdiag', directives.NwdiagDirectiveBase) self.tmpdir = tempfile.mkdtemp() def tearDown(self): if 'nwdiag' in docutils._directives: del docutils._directives['nwdiag'] for file in os.listdir(self.tmpdir): os.unlink(self.tmpdir + "/" + file) os.rmdir(self.tmpdir) def test_setup(self): directives.setup() options = directives.directive_options self.assertIn('nwdiag', docutils._directives) self.assertEqual(directives.NwdiagDirective, docutils._directives['nwdiag']) self.assertEqual('PNG', options['format']) self.assertEqual(False, options['antialias']) self.assertEqual(None, options['fontpath']) self.assertEqual(False, options['nodoctype']) self.assertEqual(False, options['noviewbox']) self.assertEqual(False, options['inline_svg']) def test_setup_with_args(self): directives.setup(format='SVG', antialias=True, fontpath='/dev/null', nodoctype=True, noviewbox=True, inline_svg=True) options = directives.directive_options self.assertIn('nwdiag', docutils._directives) self.assertEqual(directives.NwdiagDirective, docutils._directives['nwdiag']) self.assertEqual('SVG', options['format']) self.assertEqual(True, options['antialias']) self.assertEqual('/dev/null', options['fontpath']) self.assertEqual(True, options['nodoctype']) self.assertEqual(True, options['noviewbox']) self.assertEqual(True, options['inline_svg']) @stderr_wrapper def test_base_noargs(self): text = ".. nwdiag::" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_block(self): text = ".. nwdiag::\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.nwdiag, type(doctree[0])) self.assertEqual('{ network { A; B } }', doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_emptyblock(self): text = ".. nwdiag::\n\n \n" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_filename(self): dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'diagrams/single_network.diag') text = ".. nwdiag:: %s" % filename doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.nwdiag, type(doctree[0])) self.assertEqual(io.open(filename).read(), doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_filename_not_exists(self): text = ".. nwdiag:: unknown.diag" doctree = publish_doctree(text) self.assertEqual(nodes.system_message, type(doctree[0])) @stderr_wrapper def test_base_with_block_and_filename(self): text = ".. nwdiag:: unknown.diag\n\n { A -> B }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_options(self): text = ".. nwdiag::\n :alt: hello world\n :desctable:\n" + \ " :maxwidth: 100\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.nwdiag, type(doctree[0])) self.assertEqual('{ network { A; B } }', doctree[0]['code']) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(None, doctree[0]['options']['desctable']) self.assertEqual(100, doctree[0]['options']['maxwidth']) def test_block(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. nwdiag::\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_alt(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. nwdiag::\n :alt: hello world\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_fontpath1(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath=['dummy.ttf'], outputdir=self.tmpdir) text = ".. nwdiag::\n :alt: hello world\n\n" + \ " { network { A; B } }" publish_doctree(text) def test_block_fontpath2(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath='dummy.ttf', outputdir=self.tmpdir) text = ".. nwdiag::\n :alt: hello world\n\n" + \ " { network { A; B } }" publish_doctree(text) def test_caption(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. nwdiag::\n :caption: hello world\n\n" + \ " { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.figure, type(doctree[0])) self.assertEqual(2, len(doctree[0])) self.assertEqual(nodes.image, type(doctree[0][0])) self.assertEqual(nodes.caption, type(doctree[0][1])) self.assertEqual(1, len(doctree[0][1])) self.assertEqual(nodes.Text, type(doctree[0][1][0])) self.assertEqual('hello world', doctree[0][1][0]) def test_block_maxwidth(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. nwdiag::\n :maxwidth: 100\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse(0, doctree[0]['target'].index(self.tmpdir)) def test_block_nodoctype_false(self): directives.setup(format='SVG', outputdir=self.tmpdir, nodoctype=False) text = ".. nwdiag::\n :alt: hello world\n\n { network { A; B } }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) svg = open(doctree[0]['uri']).read() self.assertEqual("\n" "\n" "\n" "`_ . Setup ===== by easy_install ---------------- Make environment:: $ easy_install nwdiag by buildout ------------ Make environment:: $ hg clone http://bitbucket.org/tk0miya/nwdiag $ cd nwdiag $ python bootstrap.py $ bin/buildout spec-text setting sample ======================== Few examples are available. You can get more examples at `blockdiag.com `_ . simple.diag ------------ simple.diag is simply define nodes and transitions by dot-like text format:: diagram { A -> B -> C; lane you { A; B; } lane me { C; } } Usage ===== Execute nwdiag command:: $ nwdiag simple.diag $ ls simple.png simple.png Requirements ============ * Python 2.6, 2.7, 3.2, 3.3 * Pillow 2.2.1 * funcparserlib 0.3.6 * setuptools License ======= Apache License 2.0 History ======= 1.0.0 (2013-10-05) ------------------ * Support python 3.2 and 3.3 (thanks to @masayuko) * Drop supports for python 2.4 and 2.5 * Replace dependency: PIL -> Pillow 0.9.4 (2012-12-20) ------------------ * Fix bugs 0.9.3 (2012-12-17) ------------------ * [rackdiag] Allow multiple rackitems in same level * Fix bugs 0.9.2 (2012-11-17) ------------------ * [rackdiag] Add auto-numbering feature * Fix bugs 0.9.1 (2012-10-28) ------------------ * Fix bugs 0.9.0 (2012-10-22) ------------------ * Optimize algorithm for rendering shadow * Add options to docutils directive * [packetdiag] represent splitted packets with dashed-line * Fix bugs 0.8.2 (2012-09-29) ------------------ * Fix bugs 0.8.1 (2012-09-08) ------------------ * Add packetdiag_sphinxhelper 0.8.0 (2012-09-06) ------------------ * Add packetdiag which supports generating packet-header diaagram * [nwdiag] Add diagram attribute: external_connector * Update to new package structure (blockdiag >= 1.1.2) * Allow # to comment syntax * Fix bugs 0.7.0 (2011-11-19) ------------------ * Accept N/A rack-unit * Add fontfamily attribute for switching fontface * Fix bugs 0.6.1 (2011-11-06) ------------------ * [rackdiag] Support multiple racks rendering * [rackdiag] Add rack attribute: unit-height, weight, ampere, ascending * [rackdiag] Support putting multiple items to same rack-unit 0.6.0 (2011-11-06) ------------------ * Add rackdiag which supports genarating rack-structure diagram * Add docutils extension * Fix bugs 0.5.3 (2011-11-01) ------------------ * Add class feature (experimental) 0.5.2 (2011-11-01) ------------------ * Follow blockdiag-0.9.7 interface 0.5.1 (2011-10-19) ------------------ * Follow blockdiag-0.9.5 interface 0.5.0 (2011-10-07) ------------------ * Change shape of trunkline like a pipeline * Add network attribute: color * Add diagram attribute: default_network_color 0.4.2 (2011-09-30) ------------------ * Add diagram attributes: default_text_color * Fix bugs 0.4.1 (2011-09-26) ------------------ * Add diagram attributes: default_node_color, default_group_color and default_line_color * Fix bugs 0.4.0 (2011-08-09) ------------------ * Add syntax for peer network 0.3.3 (2011-08-07) ------------------ * Add syntax for peer network (experimental) * Fix bugs 0.3.2 (2011-08-03) ------------------ * Fix bugs 0.3.1 (2011-08-01) ------------------ * Fix bugs 0.3.0 (2011-07-18) ------------------ * Upgrade layout engine * Allow to note ip addresses directly * Allow node_id including hyphen chars * Fix bugs 0.2.7 (2011-07-05) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Fix bugs 0.2.6 (2011-07-03) ------------------ * Allow "." to id token * Support input from stdin * Support multiple node address (using comma) * Do not sort networks (ordered as declarations) * Fix bugs 0.2.5 (2011-06-29) ------------------ * Adjust parameters for span and margin 0.2.4 (2011-05-17) ------------------ * Add --version option * Fix bugs 0.2.3 (2011-05-15) ------------------ * Fix bugs 0.2.2 (2011-05-15) ------------------ * Implement grouping nodes 0.2.1 (2011-05-14) ------------------ * Change license to Apache License 2.0 * Support blockdiag 0.8.1 core interface 0.2.0 (2011-05-02) ------------------ * Rename package to nwdiag 0.1.6 (2011-04-30) ------------------ * Fix bugs 0.1.5 (2011-04-26) ------------------ * Fix bugs 0.1.4 (2011-04-25) ------------------ * Implement jumped edge * Fix bugs 0.1.3 (2011-04-23) ------------------ * Fix sphinxcontrib_netdiag was not worked 0.1.2 (2011-04-23) ------------------ * Support multi-homed host * Drop network-bridge sytanx (cf. net_a -- net_b) 0.1.1 (2011-04-10) ------------------ * Fix bugs 0.1.0 (2011-04-09) ------------------ * First release nwdiag-1.0.0/src/rackdiag/0000755000175000017500000000000012224001064016520 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/rackdiag/builder.py0000644000175000017500000000735512217033703020541 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from collections import defaultdict from rackdiag import parser from rackdiag.elements import Diagram, Rack, RackItem from blockdiag.utils import XY class DiagramTreeBuilder: def build(self, tree): self.diagram = Diagram() self.instantiate(self.diagram.racks[0], tree) for rack in self.diagram.racks[:]: if len(rack.nodes) == 0: self.diagram.racks.remove(rack) return self.diagram def instantiate(self, rack, tree): for stmt in tree.stmts: if isinstance(stmt, parser.Attr): try: rack.set_attribute(stmt) except AttributeError: self.diagram.set_attribute(stmt) elif isinstance(stmt, parser.RackItem): if stmt.number is None: if len(rack.nodes) > 0: item = rack.nodes[-1] number = item.number + item.colheight else: number = 1 else: number = stmt.number item = RackItem(number, stmt.label) item.set_attributes(stmt.attrs) rack.nodes.append(item) elif isinstance(stmt, parser.Rack): _rack = Rack() self.diagram.racks.append(_rack) self.instantiate(_rack, stmt) elif isinstance(stmt, parser.AttrPlugin): self.diagram.set_plugin(stmt.name, stmt.attrs) class DiagramLayoutManager: def __init__(self, diagram): self.diagram = diagram def run(self): x = 0 for rack in self.diagram.racks: self.layout_rack(rack) rack.xy = XY(x, 0) rack.fixiate() x += rack.colwidth self.diagram.fixiate() def layout_rack(self, rack): usage = defaultdict(bool) for item in rack.nodes: item.xy = XY(-1, -1) for item in rack.nodes: if rack.descending: y = rack.colheight - item.number - item.colheight + 1 else: y = item.number - 1 for x in range(255): r = range(y, y + item.colheight) if len([_ for _ in r if usage[(x, _)]]) == 0: break item.xy = XY(x, y) for dy in range(y, y + item.colheight): usage[(x, dy)] = 1 self.validate_rack(rack, item) def validate_rack(self, rack, item): if item.xy.y < 0: msg = "Rack %d is layouted to underground!\n" % item.number raise AttributeError(msg) elif rack.colheight < item.xy.y + item.colheight: msg = "Rack %d is oversized to rack-height\n" % item.number raise AttributeError(msg) class ScreenNodeBuilder: @classmethod def build(cls, tree): Rack.clear() RackItem.clear() Diagram.clear() return cls(tree).run() def __init__(self, tree): self.diagram = DiagramTreeBuilder().build(tree) def run(self): DiagramLayoutManager(self.diagram).run() return self.diagram nwdiag-1.0.0/src/rackdiag/__init__.py0000644000175000017500000000122011774050122020634 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from nwdiag import __version__ __version__ nwdiag-1.0.0/src/rackdiag/drawer.py0000644000175000017500000000500312217037610020364 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import blockdiag.drawer from rackdiag.metrics import DiagramMetrics from blockdiag.utils import Box from blockdiag.utils.compat import u class DiagramDraw(blockdiag.drawer.DiagramDraw): def create_metrics(self, *args, **kwargs): return DiagramMetrics(*args, **kwargs) def _draw_elements(self, **kwargs): default_font = self.metrics.font_for(self.diagram) for rack in self.diagram.racks: frame = self.metrics.cell(rack, use_padding=False).box self.drawer.rectangle(frame, fill='white', outline=self.diagram.linecolor) for i in range(rack.colheight): box = self.metrics.racknumber(rack, i) number = u("%d") % (i + 1) self.drawer.textarea(box, number, default_font, halign='right', fill=self.diagram.textcolor) if rack.display_label: box = self.metrics.racklabel(rack) self.drawer.textarea(box, rack.display_label, self.metrics.font_for(rack), fill=rack.textcolor) super(DiagramDraw, self)._draw_elements(**kwargs) def _draw_background(self): # do not draw shadow of nodes on super() self.diagram.shadow_style = 'none' super(DiagramDraw, self)._draw_background() # draw shadow of frame dx, dy = self.metrics.shadow_offset for rack in self.diagram.racks: frame = self.metrics.cell(rack, use_padding=False) shadow = Box(frame.x1 + dx, frame.y1 + dy, frame.x2 + dx, frame.y2 + dy) self.drawer.rectangle(shadow, fill=self.shadow, filter='blur') def node(self, node, **kwargs): label, node.label = node.label, node.display_label super(DiagramDraw, self).node(node, **kwargs) node.label = label nwdiag-1.0.0/src/rackdiag/utils/0000755000175000017500000000000012224001064017660 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/rackdiag/utils/__init__.py0000644000175000017500000000114411655461437022016 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/rackdiag/utils/rst/0000755000175000017500000000000012224001064020470 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/rackdiag/utils/rst/directives.py0000644000175000017500000001146112222275125023217 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. import os from docutils import nodes from docutils.parsers import rst from rackdiag import parser from rackdiag.elements import RackItem from rackdiag.builder import ScreenNodeBuilder from rackdiag.drawer import DiagramDraw from rackdiag.utils.rst.nodes import rackdiag from blockdiag.utils.rst import directives from blockdiag.utils.compat import u, cmp_to_key directive_options_default = dict(format='PNG', antialias=False, fontpath=None, outputdir=None, nodoctype=False, noviewbox=False, inline_svg=False, ignore_pil=False) directive_options = {} class RackdiagDirectiveBase(directives.BlockdiagDirectiveBase): """ Directive to insert arbitrary dot markup. """ name = "rackdiag" node_class = rackdiag class RackdiagDirective(directives.BlockdiagDirective): name = "rackdiag" node_class = rackdiag @property def global_options(self): return directive_options def node2diagram(self, node): tree = parser.parse_string(node['code']) return ScreenNodeBuilder.build(tree) def node2image(self, node, diagram): options = node['options'] filename = self.image_filename(node) fontmap = self.create_fontmap() _format = self.global_options['format'].lower() if _format == 'svg' and self.global_options['inline_svg'] is True: filename = None kwargs = dict(self.global_options) del kwargs['format'] drawer = DiagramDraw(_format, diagram, filename, fontmap=fontmap, **kwargs) if filename is None or not os.path.isfile(filename): drawer.draw() content = drawer.save(None) if _format == 'svg' and self.global_options['inline_svg'] is True: size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] new_size = (options['maxwidth'], int(size[1] * ratio)) content = drawer.save(new_size) return nodes.raw('', content, format='html') size = drawer.pagesize() if 'maxwidth' in options and options['maxwidth'] < size[0]: ratio = float(options['maxwidth']) / size[0] thumb_size = (options['maxwidth'], size[1] * ratio) thumb_filename = self.image_filename(node, prefix='_thumb') if not os.path.isfile(thumb_filename): drawer.filename = thumb_filename drawer.draw() drawer.save(thumb_size) image = nodes.image(uri=thumb_filename, target=filename) else: image = nodes.image(uri=filename) if node['alt']: image['alt'] = node['alt'] return image def description_table(self, diagram): nodes = diagram.traverse_nodes widths = [25] + [50] * (len(RackItem.desctable) - 1) headers = [RackItem.attrname[name] for name in RackItem.desctable] descriptions = [n.to_desctable() for n in nodes()] descriptions.sort(key=cmp_to_key(directives.cmp_node_number)) # records for total total = ['-', 'Total'] + [''] * (len(RackItem.desctable) - 2) total[2] = u("%dU") % sum(n.colheight for n in nodes() if n.colheight) total[3] = u("%.1fA") % sum(n.ampere for n in nodes() if n.ampere) total[4] = u("%.1fkg") % sum(n.weight for n in nodes() if n.weight) descriptions.append(total) for i in range(len(headers) - 1, -1, -1): if any(desc[i] for desc in descriptions): pass else: widths.pop(i) headers.pop(i) for desc in descriptions: desc.pop(i) return self._description_table(descriptions, widths, headers) def setup(**kwargs): for key, value in directive_options_default.items(): directive_options[key] = kwargs.get(key, value) rst.directives.register_directive("rackdiag", RackdiagDirective) nwdiag-1.0.0/src/rackdiag/utils/rst/__init__.py0000644000175000017500000000114411655461427022625 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. nwdiag-1.0.0/src/rackdiag/utils/rst/nodes.py0000644000175000017500000000127112053437632022171 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from docutils import nodes class rackdiag(nodes.General, nodes.Element): pass nwdiag-1.0.0/src/rackdiag/utils/math.py0000644000175000017500000000223712217032100021164 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division def gcd(*args): if len(args) == 0: return 0 elif len(args) == 1: return args[0] elif len(args) == 2: if args[1] == 0: return args[0] else: return gcd(args[1], args[0] % args[1]) else: return gcd(gcd(*args[:-1]), args[-1]) def lcm(*args): if len(args) == 0: return 0 elif len(args) == 1: return args[0] elif len(args) == 2: return args[0] * args[1] // gcd(*args) else: return lcm(lcm(*args[:-1]), args[-1]) nwdiag-1.0.0/src/rackdiag/parser.py0000644000175000017500000001362312217037224020404 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2008/2009 Andrey Vlasovskikh # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. r'''A DOT language parser using funcparserlib. The parser is based on [the DOT grammar][1]. It is pretty complete with a few not supported things: * Ports and compass points * XML identifiers At the moment, the parser builds only a parse tree, not an abstract syntax tree (AST) or an API for dealing with DOT. [1]: http://www.graphviz.org/doc/info/lang.html ''' import re import io from re import MULTILINE, DOTALL from collections import namedtuple from funcparserlib.lexer import make_tokenizer, Token, LexerError from funcparserlib.parser import (some, a, maybe, many, finished, skip) from blockdiag.utils.compat import u ENCODING = 'utf-8' Graph = namedtuple('Graph', 'type id stmts') RackItem = namedtuple('RackItem', 'number label attrs') Attr = namedtuple('Attr', 'name value') Rack = namedtuple('Rack', 'id stmts') DefAttrs = namedtuple('DefAttrs', 'object attrs') AttrPlugin = namedtuple('AttrPlugin', 'name attrs') class ParseException(Exception): pass def tokenize(string): """str -> Sequence(Token)""" specs = [ ('Comment', (r'/\*(.|[\r\n])*?\*/', MULTILINE)), ('Comment', (r'(//|#).*',)), ('NL', (r'[\r\n]+',)), ('Space', (r'[ \t\r\n]+',)), ('RackItem', (r':[^\r\n\[]+',)), ('NonnumRackItem', (r'[\*\-]\s*[^\r\n\[]+',)), ('Units', (r'([0-9]+U|[0-9]+(?:\.[0-9]+)?(A|kg))',)), ('Number', (r'[0-9]+',)), ('Name', (u('[A-Za-z_0-9\u0080-\uffff]') + u('[A-Za-z_\\-.0-9\u0080-\uffff]*'),)), ('Op', (r'[{}:;,=\[\]]',)), ('String', (r'(?P"|\').*?(? object""" unarg = lambda f: lambda args: f(*args) tokval = lambda x: x.value flatten = lambda list: sum(list, []) n = lambda s: a(Token('Name', s)) >> tokval op = lambda s: a(Token('Op', s)) >> tokval op_ = lambda s: skip(op(s)) _id = some(lambda t: t.type in ['Name', 'Number', 'String', 'Units'] ).named('id') >> tokval number = some(lambda t: t.type == 'Number').named('number') >> tokval rackitem = some(lambda t: t.type == 'RackItem').named('rackitem') >> tokval nonnum_rackitem = (some(lambda t: t.type == 'NonnumRackItem'). named('nonnum_rackitem') >> tokval) make_graph_attr = lambda args: DefAttrs(u('graph'), [Attr(*args)]) racklabel = lambda text: re.sub("^.\s*(.*?)\s*;?$", "\\1", text) make_rackitem = lambda no, text, attr: RackItem(no, racklabel(text), attr) make_rackitem2 = lambda text, attr: RackItem(None, racklabel(text), attr) a_list = ( _id + maybe(op_('=') + _id) + skip(maybe(op(','))) >> unarg(Attr)) attr_list = ( many(op_('[') + many(a_list) + op_(']')) >> flatten) graph_attr = _id + op_('=') + _id >> make_graph_attr numbered_rackitem_stmt = ( number + rackitem + attr_list >> unarg(make_rackitem)) nonnumbered_rackitem_stmt = ( nonnum_rackitem + attr_list >> unarg(make_rackitem2)) rackitem_stmt = ( numbered_rackitem_stmt | nonnumbered_rackitem_stmt) # rack definition rack_stmt = ( rackitem_stmt | a_list | graph_attr ) rack_stmt_list = many(rack_stmt + skip(maybe(op(';')))) rack = ( skip(n('rack')) + maybe(_id) + op_('{') + rack_stmt_list + op_('}') >> unarg(Rack)) # plugin definition plugin_stmt = ( skip(n('plugin')) + _id + attr_list >> unarg(AttrPlugin)) stmt = ( rack | rackitem_stmt | plugin_stmt | a_list ) stmt_list = many(stmt + skip(maybe(op(';')))) graph = ( maybe(n('diagram') | n('rackdiag')) + maybe(_id) + op_('{') + stmt_list + op_('}') >> unarg(Graph)) dotfile = graph + skip(finished) return dotfile.parse(seq) def sort_tree(tree): def weight(node): if isinstance(node, (Attr, DefAttrs, AttrPlugin)): return 1 else: return 2 if hasattr(tree, 'stmts'): tree.stmts.sort(key=lambda x: weight(x)) for stmt in tree.stmts: sort_tree(stmt) return tree def parse_string(string): try: tree = parse(tokenize(string)) return sort_tree(tree) except LexerError as e: message = "Got unexpected token at line %d column %d" % e.place raise ParseException(message) except Exception as e: raise ParseException(str(e)) def parse_file(path): code = io.open(path, 'r', encoding='utf-8-sig').read() return parse_string(code) nwdiag-1.0.0/src/rackdiag/elements.py0000644000175000017500000001600012222275125020714 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division import re import blockdiag.elements from rackdiag.utils.math import lcm from blockdiag.utils import XY from blockdiag.utils.compat import u class DiagramNode(blockdiag.elements.DiagramNode): pass class RackItem(blockdiag.elements.DiagramNode): desctable = [] attrname = {} @classmethod def clear(cls): super(RackItem, cls).clear() cls.desctable = ['number', 'label', 'units', 'ampere', 'weight', 'description'] cls.attrname = dict(number='No', label='Name', units='Height', ampere='Capacity', weight='Weight', description='Description') def __init__(self, number, label): super(RackItem, self).__init__(None) self.number = int(number) self.label = label self.ampere = None self.weight = None if self.label == 'N/A': self.color = 'gray' @property def display_label(self): attrs = [] if self.colheight > 1: attrs.append(u("%dU") % self.colheight) if self.ampere: attrs.append(u("%.1fA") % self.ampere) if self.weight: attrs.append(u("%.1fkg") % self.weight) labels = [] if self.label: labels.append(self.label) if attrs: labels.append(u("[%s]") % u("/").join(attrs)) return u("\n").join(labels) def set_attribute(self, attr): if re.search('^\d+U$', attr.name): self.colheight = int(attr.name[:-1]) elif re.search('^\d+(\.[0-9]+)?A$', attr.name): self.ampere = float(attr.name[:-1]) elif re.search('^\d+(\.[0-9]+)?kg$', attr.name): self.weight = float(attr.name[:-2]) else: return super(RackItem, self).set_attribute(attr) def set_height(self, value): self.colheight = int(value) def to_desctable(self): attrs = [] for name in self.desctable: if name == 'units': attrs.append(u("%dU") % self.colheight) elif name == 'ampere': if self.ampere is None: attrs.append(u("")) else: attrs.append((u("%.1fA") % self.ampere) or u("")) elif name == 'weight': if self.weight is None: attrs.append(u("")) else: attrs.append((u("%.1fkg") % self.weight) or u("")) else: value = getattr(self, name) if value is None: attrs.append(u("")) elif isinstance(value, int): attrs.append(str(value)) else: attrs.append(getattr(self, name)) return attrs class Rack(blockdiag.elements.NodeGroup): def __init__(self): super(Rack, self).__init__(None) self.colheight = 42 self.description = None self.descending = True def set_default_fontsize(self, value): raise AttributeError() # Ignore and pass-through to Diagram @property def display_label(self): attrs = [] if self.description or attrs: attrs.insert(0, u("%dU") % self.colheight) labels = [] if self.description: labels.append(self.description) if attrs: labels.append(u("[%s]") % u("/").join(attrs)) return u("\n").join(labels) def items(self, levels): if isinstance(levels, int): levels = [levels] return [node for node in self.nodes if node.xy.y in levels] def get_linked_levels(self, level): def get_max_height(self, y): try: return max(n.colheight for n in self.items(y)) except: return 0 height = get_max_height(self, level) for dy in range(1, self.colheight - level + 1): if dy < height: height = max(height, get_max_height(self, level + dy) + dy) return range(level, level + height) def adjust_node_widths(self): i = 0 linked_widths = {} widths = [] while i < self.colheight: levels = self.get_linked_levels(i) if levels: linked_widths[i] = [] for level in levels: nodes = self.items(level) if nodes: width = max(n.xy.x for n in nodes) + 1 widths.append(width) linked_widths[i].append(width) i += len(levels) else: i += 1 i = 0 self.colwidth = lcm(*widths) or 1 while i < self.colheight: levels = self.get_linked_levels(i) if levels: colwidth = max(linked_widths[i]) or 1 for level in levels: nodes = self.items(level) if nodes: width = self.colwidth // colwidth for node in nodes: node.xy = XY(node.xy.x * width, node.xy.y) node.colwidth = width i += len(levels) else: i += 1 def fixiate(self): self.adjust_node_widths() for node in self.nodes: node.xy = node.xy.shift(x=self.xy.x) def set_ascending(self, attr): self.descending = False def set_attribute(self, attr): if re.search('^\d+U$', attr.name): self.colheight = int(attr.name[:-1]) else: return super(Rack, self).set_attribute(attr) class Diagram(blockdiag.elements.Diagram): _DiagramNode = RackItem @classmethod def clear(cls): super(Diagram, cls).clear() cls._DiagramNode.clear() def __init__(self): super(Diagram, self).__init__() self.racks = [Rack()] def set_default_fontsize(self, value): super(Diagram, self).set_default_fontsize(value) self.fontsize = int(value) def set_rackheight(self, value): self.racks[0].colheight = int(value) def fixiate(self): self.colwidth = sum(r.colwidth for r in self.racks) self.colheight = max(r.colheight for r in self.racks) def traverse_nodes(self, **kwargs): for rack in self.racks: for node in rack.traverse_nodes(): yield node nwdiag-1.0.0/src/rackdiag/metrics.py0000644000175000017500000000521612217033756020563 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- # Copyright 2011 Takeshi KOMIYA # # Licensed 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. from __future__ import division from collections import defaultdict import blockdiag.metrics from blockdiag.utils import Box, XY from rackdiag import elements class DiagramMetrics(blockdiag.metrics.DiagramMetrics): def __init__(self, diagram, **kwargs): span_height = self.span_height span_width = self.span_width self.span_height = 0 self.span_width = 0 super(DiagramMetrics, self).__init__(diagram, **kwargs) labelsize = [self.textsize(r.display_label, font=self.font_for(r)) for r in diagram.racks if r.display_label] if labelsize: labelheight = (max(size.height for size in labelsize) + self.line_spacing * 2) else: labelheight = 0 # reset node_width FORCE self.spreadsheet.node_width = defaultdict(lambda: self.node_width) self.spreadsheet.set_span_height(0, span_height + labelheight) self.spreadsheet.set_span_height(diagram.colheight, span_height) self.spreadsheet.set_span_width(0, span_width) for rack in diagram.racks: x = rack.xy.x + rack.colwidth self.spreadsheet.set_span_width(x, span_width) if rack.colwidth > 1: node_width = self.node_width // rack.colwidth for i in range(rack.colwidth): self.spreadsheet.set_node_width(rack.xy.x + i, node_width) def racklabel(self, rack): cell = self.cell(rack) textsize = self.textsize(rack.display_label, font=self.font_for(rack)) y1 = cell.y1 - textsize.height - self.line_spacing * 2 return Box(cell.x1, y1, cell.x2, cell.y1) def racknumber(self, rack, number): if rack.descending: y = rack.colheight - number - 1 else: y = number dummy = elements.DiagramNode(None) dummy.xy = XY(rack.xy.x, y) dummy.colwidth = 1 dummy.colheight = 1 box = self.cell(dummy, use_padding=False).box return Box(0, box[1], box[0], box[3]) nwdiag-1.0.0/src/rackdiag/tests/0000755000175000017500000000000012224001064017662 5ustar katsuwokatsuwo00000000000000nwdiag-1.0.0/src/rackdiag/tests/test_builder.py0000644000175000017500000000527412217036771022750 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- from __future__ import print_function import sys if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest import os from rackdiag.builder import ScreenNodeBuilder from rackdiag.parser import parse_string def build(filename): def decorator(fn): def func(self): testdir = os.path.dirname(__file__) pathname = os.path.join(testdir, 'diagrams', filename) code = open(pathname).read() tree = parse_string(code) self.diagram = ScreenNodeBuilder.build(tree) return fn(self) func.__name__ = fn.__name__ return func return decorator class TestBuildDiagram(unittest.TestCase): def assertAttributes(self, **attributes): for rack in self.diagram.racks: for node in rack.nodes: print(node, node.label) for key, attribute in attributes.items(): value = attribute.get(node.label) self.assertEqual(value, getattr(node, key)) @build('simple.diag') def test_simple(self): positions = {'server 1': (0, 41), 'server 2': (0, 40), 'server 3': (0, 39), 'server 4': (0, 38)} self.assertAttributes(xy=positions) @build('autonumber.diag') def test_autonumber(self): positions = {'server 1': (0, 41), 'server 2': (0, 40), 'server 3': (0, 38), 'server 4': (0, 37)} self.assertAttributes(xy=positions) @build('multi_rackitem.diag') def test_multi_rackitem(self): positions = {'UPS 1': (0, 9), 'UPS 2': (6, 9), 'server 1': (0, 8), 'server 2': (3, 7), 'server 3': (0, 6), 'server 4': (3, 5), 'server 5': (6, 5), 'server 6': (9, 5), 'server 7': (0, 2), 'server 8': (4, 3), 'server 9': (8, 3)} colheights = {'UPS 1': 3, 'UPS 2': 3, 'server 1': 1, 'server 2': 2, 'server 3': 2, 'server 4': 2, 'server 5': 2, 'server 6': 2, 'server 7': 3, 'server 8': 1, 'server 9': 1} self.assertAttributes(xy=positions, colheight=colheights) @build('multi_racks.diag') def test_multi_racks(self): positions = {'server 1': (0, 41), 'server 2': (0, 40), 'server 3': (0, 39), 'server 4': (0, 38), 'server 5': (1, 41), 'server 6': (1, 40), 'server 7': (1, 39), 'server 8': (1, 38)} self.assertAttributes(xy=positions) nwdiag-1.0.0/src/rackdiag/tests/test_rst_directives.py0000644000175000017500000005173312222275125024346 0ustar katsuwokatsuwo00000000000000# -*- coding: utf-8 -*- import sys if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest import os import io import tempfile from blockdiag.tests.utils import stderr_wrapper, with_pil from docutils import nodes from docutils.core import publish_doctree, publish_parts from docutils.parsers.rst import directives as docutils from rackdiag.utils.rst import directives class TestRstDirectives(unittest.TestCase): def setUp(self): docutils.register_directive('rackdiag', directives.RackdiagDirectiveBase) self.tmpdir = tempfile.mkdtemp() def tearDown(self): if 'rackdiag' in docutils._directives: del docutils._directives['rackdiag'] for file in os.listdir(self.tmpdir): os.unlink(self.tmpdir + "/" + file) os.rmdir(self.tmpdir) def test_setup(self): directives.setup() options = directives.directive_options self.assertIn('rackdiag', docutils._directives) self.assertEqual(directives.RackdiagDirective, docutils._directives['rackdiag']) self.assertEqual('PNG', options['format']) self.assertEqual(False, options['antialias']) self.assertEqual(None, options['fontpath']) self.assertEqual(False, options['nodoctype']) self.assertEqual(False, options['noviewbox']) self.assertEqual(False, options['inline_svg']) def test_setup_with_args(self): directives.setup(format='SVG', antialias=True, fontpath='/dev/null', nodoctype=True, noviewbox=True, inline_svg=True) options = directives.directive_options self.assertIn('rackdiag', docutils._directives) self.assertEqual(directives.RackdiagDirective, docutils._directives['rackdiag']) self.assertEqual('SVG', options['format']) self.assertEqual(True, options['antialias']) self.assertEqual('/dev/null', options['fontpath']) self.assertEqual(True, options['nodoctype']) self.assertEqual(True, options['noviewbox']) self.assertEqual(True, options['inline_svg']) @stderr_wrapper def test_base_noargs(self): text = ".. rackdiag::" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_block(self): text = ".. rackdiag::\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.rackdiag, type(doctree[0])) self.assertEqual('{ 1: server\n2: database\n}', doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_emptyblock(self): text = ".. rackdiag::\n\n \n" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_filename(self): dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'diagrams/simple.diag') text = ".. rackdiag:: %s" % filename doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.rackdiag, type(doctree[0])) self.assertEqual(io.open(filename).read(), doctree[0]['code']) self.assertEqual(None, doctree[0]['alt']) self.assertEqual({}, doctree[0]['options']) @stderr_wrapper def test_base_with_filename_not_exists(self): text = ".. rackdiag:: unknown.diag" doctree = publish_doctree(text) self.assertEqual(nodes.system_message, type(doctree[0])) @stderr_wrapper def test_base_with_block_and_filename(self): text = ".. rackdiag:: unknown.diag\n\n { 1: server\n 2: database }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.system_message, type(doctree[0])) def test_base_with_options(self): text = ".. rackdiag::\n :alt: hello world\n :desctable:\n" + \ " :maxwidth: 100\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(directives.rackdiag, type(doctree[0])) self.assertEqual('{ 1: server\n2: database\n}', doctree[0]['code']) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(None, doctree[0]['options']['desctable']) self.assertEqual(100, doctree[0]['options']['maxwidth']) def test_block(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. rackdiag::\n\n { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_alt(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. rackdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertEqual('hello world', doctree[0]['alt']) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse('target' in doctree[0]) def test_block_fontpath1(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath=['dummy.ttf'], outputdir=self.tmpdir) text = ".. rackdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" publish_doctree(text) def test_block_fontpath2(self): with self.assertRaises(RuntimeError): directives.setup(format='SVG', fontpath='dummy.ttf', outputdir=self.tmpdir) text = ".. rackdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" publish_doctree(text) def test_caption(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. rackdiag::\n :caption: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.figure, type(doctree[0])) self.assertEqual(2, len(doctree[0])) self.assertEqual(nodes.image, type(doctree[0][0])) self.assertEqual(nodes.caption, type(doctree[0][1])) self.assertEqual(1, len(doctree[0][1])) self.assertEqual(nodes.Text, type(doctree[0][1][0])) self.assertEqual('hello world', doctree[0][1][0]) def test_block_maxwidth(self): directives.setup(format='SVG', outputdir=self.tmpdir) text = ".. rackdiag::\n :maxwidth: 100\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) self.assertFalse('alt' in doctree[0]) self.assertEqual(0, doctree[0]['uri'].index(self.tmpdir)) self.assertFalse(0, doctree[0]['target'].index(self.tmpdir)) def test_block_nodoctype_false(self): directives.setup(format='SVG', outputdir=self.tmpdir, nodoctype=False) text = ".. rackdiag::\n :alt: hello world\n\n" + \ " { 1: server\n 2: database\n }" doctree = publish_doctree(text) self.assertEqual(1, len(doctree)) self.assertEqual(nodes.image, type(doctree[0])) svg = open(doctree[0]['uri']).read() self.assertEqual("\n" "\n" "\n" " insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME rackdiag \- generate rack-structure-diagram image file from spec-text file. .SH SYNOPSIS .B rackdiag .RI [ options ] " files" .br .SH DESCRIPTION This manual page documents briefly the .B rackdiag commands. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBrackdiag\fP is generate sequence-diagram image file from spec-text file. .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. .TP .B \-h, \-\-help show this help message and exit. .TP .B \-\-version show program's version number and exit. .TP .B \-a, \-\-antialias Pass diagram image to anti-alias filter. .TP .B \-c FILE, \-\-config=FILE read configurations from FILE. .TP .B \-o FILE write diagram to FILE. .TP .B \-f FONT, \-\-font=FONT use FONT to draw diagram. .TP .B \-T TYPE Output diagram as TYPE format. .SH SEE ALSO The programs are documented fully by .br http://blockdiag.com/en/nwdiag/ .SH AUTHOR rackdiag was written by Takeshi Komiya .PP This manual page was written by Kouhei Maeda , for the Debian project (and may be used by others). nwdiag-1.0.0/nwdiag.10000644000175000017500000000371711655463514015552 0ustar katsuwokatsuwo00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH NWDIAG 1 "June 11, 2011" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME nwdiag \- generate network-diagram image file from spec-text file. .SH SYNOPSIS .B nwdiag .RI [ options ] " files" .br .SH DESCRIPTION This manual page documents briefly the .B nwdiag commands. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBnwdiag\fP is generate sequence-diagram image file from spec-text file. .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. .TP .B \-h, \-\-help show this help message and exit. .TP .B \-\-version show program's version number and exit. .TP .B \-a, \-\-antialias Pass diagram image to anti-alias filter. .TP .B \-c FILE, \-\-config=FILE read configurations from FILE. .TP .B \-o FILE write diagram to FILE. .TP .B \-f FONT, \-\-font=FONT use FONT to draw diagram. .TP .B \-T TYPE Output diagram as TYPE format. .SH SEE ALSO The programs are documented fully by .br http://blockdiag.com/en/nwdiag/ .SH AUTHOR nwdiag was written by Takeshi Komiya .PP This manual page was written by Kouhei Maeda , for the Debian project (and may be used by others). nwdiag-1.0.0/LICENSE0000644000175000017500000002613611561164637015224 0ustar katsuwokatsuwo00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed 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.