From e88169fb37131c14e20ab44a6feb91b71482a4f7 Mon Sep 17 00:00:00 2001 From: checktheroads Date: Sat, 11 May 2019 23:28:13 -0700 Subject: [PATCH] complete restructure 2 --- .flask_cache/1842a0ff0c5b4bdbdca506a3489cceed | Bin 0 -> 2848 bytes .flask_cache/2029240f6d1128be89ddc32729463129 | Bin 0 -> 8 bytes .flask_cache/2752cbed1c3342df07c3a4888c00dfdd | Bin 0 -> 14038 bytes .flask_cache/29247a84a0a0a129b1e0ca9dad5c0444 | Bin 0 -> 2848 bytes .flask_cache/3e24c65d0dab693559fbfe16164473e6 | Bin 0 -> 2491 bytes .flask_cache/577248bd1fd6a03775f9c0a64b184799 | Bin 0 -> 52 bytes .flask_cache/74bc75c0764cbc805c70645cef1f026b | Bin 0 -> 241 bytes .flask_cache/bbc9d74b6ce9914445ee9365223c30a1 | Bin 0 -> 53 bytes .flask_cache/f907d0eb682db4a28aa578fee970f056 | Bin 0 -> 3118 bytes docs/configuration.md | 18 --- docs/configuration/authentication.md | 2 +- docs/configuration/blacklist.md | 2 +- docs/configuration/branding.md | 4 +- docs/configuration/commands.md | 2 +- docs/configuration/devices.md | 2 +- docs/configuration/general.md | 2 +- docs/configuration/index.md | 16 +++ docs/configuration/proxy.md | 4 +- docs/configuration/securing-router-access.md | 4 +- docs/development/index.md | 121 +++++++++--------- docs/installation/installing-hyperglass.md | 2 +- docs/installation/wsgi.md | 2 +- hyperglass/command/__init__.py | 3 + .../construct.py} | 63 ++++----- .../{cmd_execute.py => command/execute.py} | 44 +++---- .../{cmd_parser.py => command/parse.py} | 0 .../{config => configuration}/.gitignore | 0 .../{vars.py => configuration/__init__.py} | 46 +++++-- .../blacklist.toml.example | 0 .../commands.toml.example | 0 .../configuration.toml.example} | 0 .../devices.toml.example | 0 .../requires_ipv6_cidr.toml.example | 0 hyperglass/{config => }/gunicorn_config.py | 3 +- hyperglass/{app.py => hyperglass.py} | 62 ++++----- hyperglass/manage.py | 16 +-- hyperglass/render/__init__.py | 107 ++++++++++++++++ hyperglass/{ => render}/templates/415.html | 0 hyperglass/{ => render}/templates/429.html | 0 hyperglass/{ => render}/templates/base.html | 0 hyperglass/{ => render}/templates/footer.html | 0 hyperglass/{ => render}/templates/footer.md | 0 .../{ => render}/templates/hyperglass.scss | 0 hyperglass/{ => render}/templates/index.html | 0 hyperglass/templates.py | 103 --------------- hyperglass/wsgi.py | 6 +- requirements.txt | 1 + 47 files changed, 339 insertions(+), 296 deletions(-) create mode 100644 .flask_cache/1842a0ff0c5b4bdbdca506a3489cceed create mode 100644 .flask_cache/2029240f6d1128be89ddc32729463129 create mode 100644 .flask_cache/2752cbed1c3342df07c3a4888c00dfdd create mode 100644 .flask_cache/29247a84a0a0a129b1e0ca9dad5c0444 create mode 100644 .flask_cache/3e24c65d0dab693559fbfe16164473e6 create mode 100644 .flask_cache/577248bd1fd6a03775f9c0a64b184799 create mode 100644 .flask_cache/74bc75c0764cbc805c70645cef1f026b create mode 100644 .flask_cache/bbc9d74b6ce9914445ee9365223c30a1 create mode 100644 .flask_cache/f907d0eb682db4a28aa578fee970f056 delete mode 100644 docs/configuration.md create mode 100644 docs/configuration/index.md create mode 100644 hyperglass/command/__init__.py rename hyperglass/{cmd_construct.py => command/construct.py} (88%) rename hyperglass/{cmd_execute.py => command/execute.py} (87%) rename hyperglass/{cmd_parser.py => command/parse.py} (100%) rename hyperglass/{config => configuration}/.gitignore (100%) rename hyperglass/{vars.py => configuration/__init__.py} (94%) rename hyperglass/{config => configuration}/blacklist.toml.example (100%) rename hyperglass/{config => configuration}/commands.toml.example (100%) rename hyperglass/{config/config.toml.example => configuration/configuration.toml.example} (100%) rename hyperglass/{config => configuration}/devices.toml.example (100%) rename hyperglass/{config => configuration}/requires_ipv6_cidr.toml.example (100%) rename hyperglass/{config => }/gunicorn_config.py (70%) rename hyperglass/{app.py => hyperglass.py} (78%) create mode 100644 hyperglass/render/__init__.py rename hyperglass/{ => render}/templates/415.html (100%) rename hyperglass/{ => render}/templates/429.html (100%) rename hyperglass/{ => render}/templates/base.html (100%) rename hyperglass/{ => render}/templates/footer.html (100%) rename hyperglass/{ => render}/templates/footer.md (100%) rename hyperglass/{ => render}/templates/hyperglass.scss (100%) rename hyperglass/{ => render}/templates/index.html (100%) delete mode 100644 hyperglass/templates.py diff --git a/.flask_cache/1842a0ff0c5b4bdbdca506a3489cceed b/.flask_cache/1842a0ff0c5b4bdbdca506a3489cceed new file mode 100644 index 0000000000000000000000000000000000000000..220add2d9deb79038afad961f3d85de8ca1c5906 GIT binary patch literal 2848 zcmcguJCf5t5G5d>Ou!Yoio{zb5A&0}QpOT&L9z}BNMvg)PnCWpjlDSI5O4|(#C2%> zuw)zmcD-_0ZuLyhs9(R=<1fMQKi|lY*T3GqeN|mw-=Hztxu962k1!20(Ai4DGSM@? zFu+Lpp&z&efSsVx;O>3`dAc+@3BmZ&NP$XBzJ__4cVaDAsl3o>0)k4Hj~%B9yimaCc4D;12=BwD{1J}-(1V>`>opcGSwam?JH7kRz?x=ZL_i&%|RtNw&ba2%s&|G^A*u*aHF<(i1kN zw9H~G59@xns2er&gTlwr&eThU-!1Kvh2K{4zeQzd!?C}nousdY&vOa?3Yb)U6HOPu zLxbD3?E9`EL%nl{IE1l=#S{-ZdqwsZF5XlLprb!Hs@kvtdOvDHz>% literal 0 HcmV?d00001 diff --git a/.flask_cache/2029240f6d1128be89ddc32729463129 b/.flask_cache/2029240f6d1128be89ddc32729463129 new file mode 100644 index 0000000000000000000000000000000000000000..19194ee9ff9503b8dd90e8248beba5a47b51f168 GIT binary patch literal 8 PcmeZf&}(4v=FkHG2O|N0 literal 0 HcmV?d00001 diff --git a/.flask_cache/2752cbed1c3342df07c3a4888c00dfdd b/.flask_cache/2752cbed1c3342df07c3a4888c00dfdd new file mode 100644 index 0000000000000000000000000000000000000000..b0befc5fdc8a8b995c2523d7266e7da06048bc32 GIT binary patch literal 14038 zcmeHN&61l&5Y8bdy+rz)(z49(Bf-HguVXuMl8tJeze6sp#Cj>SLIr5!tz7aD$?N2n zSIF~Z2Eon%gBZlXrd*=RYlKSO)BSz(O;0m#zxe&%U$s9z|LY(7v+VliZ*ucGiosj2 zV?RmY#=8r`2NxF0dlP=#1S>B|CzFdeOGv%z(1&|JPJ(CyK>`N0Y;4-r6vAlbg%C%( z)Q@5D75x%9j%JvegEcgHmwM?gfmO8j6BiOlcH1p==C7x)hP8LMrFXY*8zgBIKTP3E zxc9<i7;T;fvA-TDOyIq(DTQ9wx z!Zlobs}H;F+5Y1VT>WtNItYo$n!S&i(=-2L;m@Pl2~rZa7v{fTWjbm&dPepDP zBqMp6F5xmt;U?N`)|34>F8%bkDE`3mbVf-=!9qUDwF8LLzbCKRca_jj}25MZp`Z8 z@Vg<#tWvBTqTH6L*}9eqX-BN6*zVQudA?TH;%CBLS{UkUHC^61*^`)HY-~a$=q8t0 z;w0jffP;-It8X|E&apvJDT-DZW@j^fZ&6V>Q?+=QeiFELGbjygqr@Kptc1)+16$a_ zx~U;u%c|Bas9wMdDcnt4u}+FwZhdSK!KXmBHpH5ukO!Uctyw%wX9<+67AZ10)})xs z2iy%AWa;w)83~P%*9FdQLCac387-v|?`BqJMspUk1k^@J9(2OJV@f4(FI${6^egO^ zeuatWWZz(%+7-0h@(H#p@C$wBUd&%2*F85V^Rca)?o0ip?z$McuW%)15z))fM0?z- z29QVi#MkYZ)%;<-FL!*PM$al?xm74!g;-a8b;{CVzC-(C4~~URb89 zB68a5=^VPz8*$S6VT~-V&}&ON@!SvYQrD^Mn30^)oOHi9v`-`#PDb%Ver}&go_9FQ zT&pFH;~E-=Cy`K*?*)+eAf~l2<*CuH@AL2sc9&s`XsE%Sz5w zyp5%K#lK2D7r1AtPO62$RGrQ4X3U46I_rFmM{?$~aaIphsL1qOD(q6qZWei4w#Sih zXFx{J9E{1EGutZOF^;a|`3UfwfBn&_Xv)M~=)QS&4@7z4C|Z-Cu0TK9zZzL@bkAg+ zY{aOpH*5kqIXKnuBE4>S_Q}-nYIyj+T6q}v@$3n>|6~h7_Bu^|4OX@il>Lsr1eD#& zXulfHvznIrEJ&Ac8wY9uR*xQyUUY~CFgoJg*(=#yl%hIU6#j^es~kKlg6Fw6T>DuV zyJ&eFtZ^X-|HQpI0$fd}u2&{RzodIZc7=uB>yZ!GG&^yRKk`~APYr^n;@%VNaE_%gKQzHi>f+V%}B(ZRF|Dtic)cudu1Dk zW4x>DQV6||Iax&m7=LE6!_I8$NEIqS(qOpcMSAvLV4#Bq_RPlQRkBS_sa1{^w`zhi jCiWSGx@KaJQK*>~GaW&XL8z0(1A`cShBS_4@~8G6%7`={ literal 0 HcmV?d00001 diff --git a/.flask_cache/29247a84a0a0a129b1e0ca9dad5c0444 b/.flask_cache/29247a84a0a0a129b1e0ca9dad5c0444 new file mode 100644 index 0000000000000000000000000000000000000000..47935f871b119b1362eff9ef796a2ae4af6beb2f GIT binary patch literal 2848 zcmcguOOo0!5X~Zs6j|g7U6l<~8AU$>_AZmfRI(YCS!83dFskyeY{7(G4w0N92g-HQ z{skKdzZpg@n|4brS+Cz~@n!J)&o}bp^{;ntUzP3a8#IP{4f1932(ussovtOUVm;(;bclT4sl9kbM0LGhz3RG;e4J?wZ6>Gs-Ww}mb5L7yR%tmG4oytw> znWYQ9GAfI7taGF1!$|qPDf)94`iz3N_H9(LEj64Oa&O=!)#M|fk{RfWn|0=E9S>| z$00%zhm#TXBV{r@2OKegMV3U}-qz467T_V%fsWzx{nUawm|74fb1zIYwZNJ1^-<|y ziJ%wu+6%RBfsPTG*bA#Yz^5dNFgIr7;uqT7c4_^~C}*|FC$jMhN=5CJThqn`YA>4n zVmJ`8OwE;ED}R{8;pWBgc~MLlcTP}bgxs(vgh|?Pr0_7CurZPcB~vnE$JghC+cw^k z*fp~*V+U`sLw&Ej>9=^ZTeRDJeTlqfJwx%q@9v?<7S^OQu;Dn?pi^6{V44-3u?{SCQJO3Tmy03ym0J(I z)uqitI}aezRu_p$O=-Llalec3?~+;I;2Gb47X@E@FGr19%(J z6xNhF0-M1ik3y>lGg?|+2GmT3^}V`L8HW-)sa!yXo?mxxM+_yW3z<+bEwTi5tNN%U z6CU#z`m&m{vX6zCnH1jN-8+zITbK!O2jXlJ#(ANqfSigC+RWw_MCC8T*hCKKFQhmP zICWSX^OgpWe!a0m; zUd@_KdLbivy=s`mXyfiOQeCCT@pSZDOZv0Q^?dg@S?@`C)Y|TZ_y3a>M7EWB3hmXj zKO5Rg@30C=+j!dI8g0qa;XXLC)Y|EK7FnUTarNReE87U#38r3G-YJ?_)BCSz{#_#q zzk518!4MV0Au^;-fI2zs)%69XOGTGt3eTZ;EGw=vJ?(jASICW3hH0f9=eeizEbV*W T0S&(=azAZo literal 0 HcmV?d00001 diff --git a/.flask_cache/74bc75c0764cbc805c70645cef1f026b b/.flask_cache/74bc75c0764cbc805c70645cef1f026b new file mode 100644 index 0000000000000000000000000000000000000000..e7d10b7e2d8e43be4e517575c21a806d820ef970 GIT binary patch literal 241 zcmXAiJx&8L5JoAe_7q<_kT!O_u*D)#A&Nvoij84t1^Gum%pYi7M0U9Zk$P#UBq?8bzpt??6 zU5l;}3e{{%wJF9-+o;*dNVa1j{$tKXE8EK&F%)m-Il2M8^HRU=Wv}0H3ni6o(5El) E2SM9QF#rGn literal 0 HcmV?d00001 diff --git a/.flask_cache/bbc9d74b6ce9914445ee9365223c30a1 b/.flask_cache/bbc9d74b6ce9914445ee9365223c30a1 new file mode 100644 index 0000000000000000000000000000000000000000..076318fbb89380fb3f713d1f5d2743e03432771b GIT binary patch literal 53 zcmebJu;F@)UIWWi6$S|CQAkfqNlQ}5ELOqE4!*5~QL z*mVOWgpi~i33D`_i|6xy{^QSj_3NAa{q@gxZ(ddH>st(ZI}OUK{2Io|9L#*_!6Gx0 zsI@O8`DS>DinRLB}17n;4?|rA&6-ljU~^c^f{!~d72d{1t7~=PeiP#@>IG z1LpQO%K&^SOky(lbaClGy##R}%_mVhFZ6W1o&(p~Bv%$F!zWxusfish8M4cwbaOH| z&u24yopz;w3dy7*^*<$y)* z9#o=C1o@H5rZ`cY zpt;b26sonF&>Kp{!pdp}1BF&HY!KZs59^$i_!F+Ii`{WmYMH5u -From `hyperglass/hyperglass/config/config.toml`: +From `hyperglass/hyperglass/configuration/config.toml`: ### site_title @@ -62,7 +62,7 @@ See [primary_asn](#primary_asn) parameter. | ------- | ------------- | | Boolean | `True` | -Enables or disables entire footer element, which contains text defined in `hyperglass/hyperglass/templates/footer.md`. +Enables or disables entire footer element, which contains text defined in `hyperglass/hyperglass/render/templates/footer.md`. ### enable_credit diff --git a/docs/configuration/commands.md b/docs/configuration/commands.md index c97627a..d094483 100644 --- a/docs/configuration/commands.md +++ b/docs/configuration/commands.md @@ -1,4 +1,4 @@ -Commands are defined in `hyperglass/hyperglass/config/commands.toml`. Formatted as a nested array of tables, each table defines the commands that will be used to execute the queries on the routers. +Commands are defined in `hyperglass/hyperglass/configuration/commands.toml`. Formatted as a nested array of tables, each table defines the commands that will be used to execute the queries on the routers. Each table contains three nested tables: diff --git a/docs/configuration/devices.md b/docs/configuration/devices.md index be4cbc2..bdb5786 100644 --- a/docs/configuration/devices.md +++ b/docs/configuration/devices.md @@ -1,4 +1,4 @@ -Devices/routers are defined in `hyperglass/hyperglass/config/devices.toml`. `devices.toml` is effectively an array of hash tables/dictionaries/key value pairs: +Devices/routers are defined in `hyperglass/hyperglass/configuration/devices.toml`. `devices.toml` is effectively an array of hash tables/dictionaries/key value pairs: ```toml [[router]] diff --git a/docs/configuration/general.md b/docs/configuration/general.md index b0d1124..5da0706 100644 --- a/docs/configuration/general.md +++ b/docs/configuration/general.md @@ -1,4 +1,4 @@ -From `hyperglass/hyperglass/config/config.toml`: +From `hyperglass/hyperglass/configuration/config.toml`: ### primary_asn diff --git a/docs/configuration/index.md b/docs/configuration/index.md new file mode 100644 index 0000000..e9e727a --- /dev/null +++ b/docs/configuration/index.md @@ -0,0 +1,16 @@ +# Configuration + +Hyperglass configuration files are stored in `hyperglass/hyperglass/configuration/`, in [TOML](https://github.com/toml-lang/toml) format. + +```console +hyperglass/configuration/ +├── blacklist.toml +├── commands.toml +├── configuration.toml +├── devices.toml +└── requires_ipv6_cidr.toml +``` + +## `requires_ipv6_cidr.toml` + +Some platforms (namely Cisco IOS) are unable to perform a BGP lookup by IPv6 host address (e.g. 2001:db8::1), but must perform the lookup by prefix (e.g. 2001:db8::/48). `requires_ipv6_cidr.toml` is a list (TOML array) of network operating systems that require this (in Netmiko format). diff --git a/docs/configuration/proxy.md b/docs/configuration/proxy.md index 5e8785c..1455843 100644 --- a/docs/configuration/proxy.md +++ b/docs/configuration/proxy.md @@ -1,4 +1,4 @@ -Proxy servers are defined in `hyperglass/hyperglass/config/devices.toml`. Each proxy definition is a unique TOML table, for example: +Proxy servers are defined in `hyperglass/hyperglass/configuration/devices.toml`. Each proxy definition is a unique TOML table, for example: ```toml [proxy.'jumpbox1'] @@ -37,7 +37,7 @@ Plain text password for SSH authentication to the proxy server/jumpbox. Device type/vendor name as recognized by [Netmiko](https://github.com/ktbyers/netmiko). See [supported device types](#supported-device-types) for a full list. -!!! info "Compatibility" +!!! note "Compatibility" Hyperglass has only been tested with `linux_ssh` as of this writing. #### ssh_command diff --git a/docs/configuration/securing-router-access.md b/docs/configuration/securing-router-access.md index 40ad41c..23dd180 100644 --- a/docs/configuration/securing-router-access.md +++ b/docs/configuration/securing-router-access.md @@ -1,4 +1,4 @@ -More than likely, you'll want to "lock down" what commands can be executed with the credentials you've provided in `hyperglass/hyperglass/config/devices.toml`. It is **strongly** recommended to use a low privilege read only account and not your full administrator account. Even though Hyperglass is coded to only run certain commands to begin with, you're more than likely still exposing the server Hyperglass runs on to the internet, and on that server is a plain text file with your router's credentials in it. Take precautions. +More than likely, you'll want to "lock down" what commands can be executed with the credentials you've provided in `hyperglass/hyperglass/configuration/devices.toml`. It is **strongly** recommended to use a low privilege read only account and not your full administrator account. Even though Hyperglass is coded to only run certain commands to begin with, you're more than likely still exposing the server Hyperglass runs on to the internet, and on that server is a plain text file with your router's credentials in it. Take precautions. # Creating Restricted Accounts @@ -18,7 +18,7 @@ parser view hyperglass username hyperglass privilege 15 view hyperglass secret ``` -!!! info "Terminal" +!!! note "Terminal" The `terminal length` and `terminal width` commands are required by Netmiko for session handling. If you remove these, Hyperglass will not work. ## Cisco IOS-XR diff --git a/docs/development/index.md b/docs/development/index.md index 14c570e..e2de51d 100644 --- a/docs/development/index.md +++ b/docs/development/index.md @@ -13,26 +13,57 @@ Under the main `hyperglass/hyperglass/` directory, the following basic structure ``` hyperglass/ ├── __init__.py -├── app.py -├── cmd_construct.py -├── cmd_execute.py -├── cmd_parser.py -├── config/ +├── command/ +├── configuration/ +├── gunicorn_config.py +├── hyperglass.py ├── manage.py +├── render/ ├── static/ -├── templates/ -├── templates.py -└── vars.py +└── wsgi.py ``` +### Scripts + +#### `hyperglass.py` + +Main Flask application. Passes input to the `command.execute` module. + +#### `manage.py` + +Management script for perfoming one-off actions. For now, the only action implemented is a manual clearing of the Flask-cache cache. To clear the cache, run `python3 manage.py --clearcache`. + ### Directories -#### config - -The `config/` directory contains all TOML config files used by Hyperglass: +#### command/ ``` -hyperglass/config/ +hyperglass/command/ +├── __init__.py +├── construct.py +├── execute.py +└── parse.py +``` + +##### `execute.py` + +Matches router name to router IP, OS, and credentials. Passes data to `cmd_construct.py`, uses the results to execute the Netmiko action. Also performs error handling in the event of a [blacklist](/configuration/blacklist) match. + +##### `construct.py` + +Constructs full commands to run on routers from `hyperglass/hyperglass/config/commands.toml`. Also performs error handling in the event of input errors. + +##### `parser.py` + +Parses output before presentation to the user. For the time being, only BGP output from Cisco IOS is parsed. This is because for BGP Community and AS_PATH lookups, Cisco IOS returns results for *all* address families, including VPNv4. This script ensures that only IPv4 and IPv6 address family output is returned. + +#### configuration/ + +The `configuration/` directory contains all TOML config files used by Hyperglass: + +``` +hyperglass/configuration/ +├── __init__.py ├── blacklist.toml ├── commands.toml ├── config.toml @@ -40,25 +71,27 @@ hyperglass/config/ └── requires_ipv6_cidr.toml ``` -#### static +As a module, `configuration` imports configuration from TOML configuration files, defines default values, and exports each as a variable that can be called in other scripts. + +#### static/ The `static/` directory contains all static HTML/CSS/JS files used for serving the site: ``` hyperglass/static/ -├── css +├── css/ │   ├── hyperglass.css │   └── icofont -├── images +├── images/ │   ├── brand.svg │   ├── favicon │   ├── hyperglass-dark.png │   └── hyperglass-light.png -├── js +├── js/ │   ├── hyperglass.js │   ├── jquery-3.4.0.min.js │   └── jquery-3.4.0.min.map -└── sass +└── sass/ ├── base ├── components ├── custom @@ -73,21 +106,25 @@ hyperglass/static/ - `css/icofont/` Completely free alternative to FontAwesome - [Icofont](https://icofont.com/). - `js/hyerpglass.js` Basic Javascript helper to perform AJAX queries necessary to pull in dynamic information and render content. -#### templates +#### render/ -The `templates/` directory contains HTML and Sass Jinja2 templates: +The `render/` directory contains the `render` module, which renders HTML and Sass templates, compiles Sass to CSS. ``` -templates/ -├── 415.html -├── 429.html -├── base.html -├── footer.html -├── footer.md -├── hyperglass.scss -└── index.html +hyperglass/render/ +├── __init__.py +└── templates/ + ├── 415.html + ├── 429.html + ├── base.html + ├── footer.html + ├── footer.md + ├── hyperglass.scss + └── index.html ``` +`render/templates/` contains the Jinja2 templates themselves: + - `415.html` General error page template. - `429.html` Site load rate limit page. - `base.html` Base template inherited by all other templates. Contains HTML `head`, JavaScript, etc. @@ -95,33 +132,3 @@ templates/ - `footer.md` Text that appears in the footer, if enabled. Markdown will be rendered as HTML. - `hyperglass.scss` Generates SCSS file for Bulma and local customizations. - `index.html` Main page template. - -### Scripts - -#### `app.py` - -Main Flask application. Passes input to `cmd_execute.py` - -#### `cmd_execute.py` - -Matches router name to router IP, OS, and credentials. Passes data to `cmd_construct.py`, uses the results to execute the Netmiko action. Also performs error handling in the event of a [blacklist](/configuration/blacklist) match. - -#### `cmd_construct.py` - -Constructs full commands to run on routers from `hyperglass/hyperglass/config/commands.toml`. Also performs error handling in the event of input errors. - -#### `cmd_parser.py` - -Parses output before presentation to the user. For the time being, only BGP output from Cisco IOS is parsed. This is because for BGP Community and AS_PATH lookups, Cisco IOS returns results for *all* address families, including VPNv4. This script ensures that only IPv4 and IPv6 address family output is returned. - -#### `manage.py` - -Management script for perfoming one-off actions. For now, the only action implemented is a manual clearing of the Flask-cache cache. - -#### `templates.py` - -Renders HTML and Sass templates, compiles Sass to CSS. - -#### `vars.py` - -Imports configuration from TOML configuration files, defines default values, and exports each as a variable that can be called in other scripts. diff --git a/docs/installation/installing-hyperglass.md b/docs/installation/installing-hyperglass.md index ad0bccb..a843c45 100644 --- a/docs/installation/installing-hyperglass.md +++ b/docs/installation/installing-hyperglass.md @@ -28,7 +28,7 @@ $ pip3 install -r requirements.txt ## Clone Example Configuration Files ``` -$ cd /opt/hyperglass/hyperglass/config/ +$ cd /opt/hyperglass/hyperglass/configuration/ $ for f in *.example; do cp $f `basename $f .example`; done; ``` diff --git a/docs/installation/wsgi.md b/docs/installation/wsgi.md index b75e212..c4264b1 100644 --- a/docs/installation/wsgi.md +++ b/docs/installation/wsgi.md @@ -11,4 +11,4 @@ Gunicorn is a WSGI server written in Python. ## Configure -Locate your `gunicorn` executable with `which gunicorn` +Locate your `gunicorn` executable with `which gunicorn`. diff --git a/hyperglass/command/__init__.py b/hyperglass/command/__init__.py new file mode 100644 index 0000000..f853070 --- /dev/null +++ b/hyperglass/command/__init__.py @@ -0,0 +1,3 @@ +from hyperglass.command import execute +from hyperglass.command import construct +from hyperglass.command import parse diff --git a/hyperglass/cmd_construct.py b/hyperglass/command/construct.py similarity index 88% rename from hyperglass/cmd_construct.py rename to hyperglass/command/construct.py index 40fb557..4836b2d 100644 --- a/hyperglass/cmd_construct.py +++ b/hyperglass/command/construct.py @@ -1,27 +1,30 @@ -import sys -import logging -import toml import re +import sys +import toml +import logging from netaddr import * +from loguru import logger # Local imports -import vars +from hyperglass import configuration log = logging.getLogger(__name__) # Load TOML config file -devices = toml.load(open("./config/devices.toml")) +devices = configuration.devices() # Load TOML commands file -commands = toml.load(open("./config/commands.toml")) +commands = configuration.commands() # Filter config to router list routers_list = devices["router"] +logger.add(sys.stderr) + # Receives JSON from Flask, constucts the command that will be passed to the router # Also handles input validation & error handling -def cmd_construct(router, cmd, ipprefix): - inputParams = router, cmd, ipprefix - log.warning(*inputParams) +def construct(router, cmd, ipprefix): + input_params = (router, cmd, ipprefix) + logger.info(*input_params) try: # Loop through routers config file, match input router with configured routers, set variables for r in routers_list: @@ -44,8 +47,8 @@ def cmd_construct(router, cmd, ipprefix): if cmd == "Query Type": msg = "You must select a query type." code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) # BGP Community Query elif cmd in ["bgp_community"]: # Extended Communities, new-format @@ -57,7 +60,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return (msg, code, router, type, command) @@ -70,7 +73,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return (msg, code, router, type, command) @@ -86,7 +89,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return (msg, code, router, type, command) @@ -95,8 +98,8 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) # BGP AS_PATH Query elif cmd in ["bgp_aspath"]: if re.match(".*", ipprefix): @@ -107,7 +110,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return (msg, code, router, type, command) @@ -116,8 +119,8 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) # BGP Route Query elif cmd in ["bgp_route"]: try: @@ -130,7 +133,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return ( @@ -149,7 +152,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return ( @@ -165,8 +168,8 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) # Ping/Traceroute elif cmd in ["ping", "traceroute"]: try: @@ -181,7 +184,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return ( @@ -202,7 +205,7 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 200 - log.warning( + logger.warning( msg, code, router, type, command ) return ( @@ -217,15 +220,15 @@ def cmd_construct(router, cmd, ipprefix): i=ipprefix ) code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) else: msg = "Command {i} not found.".format(i=cmd) code = 415 - log.error(msg, code, *inputParams) - return (msg, code, *inputParams) + logger.error(msg, code, *input_params) + return (msg, code) except: - error_msg = log.error( + error_msg = logger.error( "Input router IP {router} does not match the configured router IP of {ip}".format( router=router, ip=r["address"] ) diff --git a/hyperglass/cmd_execute.py b/hyperglass/command/execute.py similarity index 87% rename from hyperglass/cmd_execute.py rename to hyperglass/command/execute.py index 1df3ebb..6028152 100644 --- a/hyperglass/cmd_execute.py +++ b/hyperglass/command/execute.py @@ -1,17 +1,15 @@ import sys -import logging -import toml import time -from netmiko import ConnectHandler -from netmiko import redispatch from netaddr import * -from cmd_construct import cmd_construct -import vars -import cmd_parser as parser +from loguru import logger +from netmiko import redispatch +from netmiko import ConnectHandler +from hyperglass import configuration +from hyperglass.command import construct +from hyperglass.command import parse -log = logging.getLogger(__name__) # Load TOML devices file -devices = toml.load(open("./config/devices.toml")) +devices = configuration.devices() # Filter config to router list routers_list = devices["router"] # Filter config to credential list @@ -19,14 +17,16 @@ credentials_list = devices["credential"] # Filter config to proxy servers proxies_list = devices["proxy"] -blacklist_config = toml.load(open("./config/blacklist.toml")) +blacklist_config = configuration.blacklist() blacklist = IPSet(blacklist_config["blacklist"]) general_error = "Error connecting to device." +logger.add(sys.stderr) -def cmd_execute(lg_data): - log.warning(lg_data) + +def execute(lg_data): + logger.info(lg_data) # Check POST data from JS, if location matches a configured router's # location, use the router's configured IP address to connect for r in routers_list: @@ -39,17 +39,17 @@ def cmd_execute(lg_data): if IPNetwork(lg_data["ipprefix"]).ip in blacklist: msg = "{i} is not allowed.".format(i=lg_data["ipprefix"]) code = 405 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (msg, code, lg_data) # If netaddr library throws an exception, return a user-facing error. except: msg = "{i} is not a valid IP Address.".format(i=lg_data["ipprefix"]) code = 415 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (msg, code, lg_data) - # Send "clean" request to cmd_construct to build the command that will be sent to the router + # Send "clean" request to constructor to build the command that will be sent to the router print(lg_router_address) - msg, status, router, type, command = cmd_construct( + msg, status, router, type, command = construct.construct( lg_router_address, lg_data["cmd"], lg_data["ipprefix"] ) # Loop through proxy config, match configured proxy name for each router with a configured proxy @@ -71,7 +71,7 @@ def cmd_execute(lg_data): else: msg = "Router does not have a proxy configured." code = 415 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (msg, code, lg_data) # Matches router with configured credential @@ -90,7 +90,7 @@ def cmd_execute(lg_data): else: msg = "Credential {i} does not exist".format(i=configured_credential) code = 415 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (general_error, code, lg_data) # Connect to the router via netmiko library, return the command output @@ -102,7 +102,7 @@ def cmd_execute(lg_data): except: msg = "Unable to reach target {l}".format(l=lg_data["router"]) code = 415 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (general_error, code, lg_data) # Connect to the proxy server via netmiko library, then log into the router @@ -141,7 +141,7 @@ def cmd_execute(lg_data): p=nm_proxy["host"], d=nm_host["host"] ) code = 415 - log.error(msg, code, lg_data) + logger.error(msg, code, lg_data) return (general_error, code, lg_data) nm_host = { @@ -163,11 +163,11 @@ def cmd_execute(lg_data): try: if connection_proxied is True: output_proxied = getOutputProxy(configured_proxy) - parsed_output = parser.parse(output_proxied, type, lg_data["cmd"]) + parsed_output = parse.parse(output_proxied, type, lg_data["cmd"]) return parsed_output, status, router, type, command elif connection_proxied is False: output_direct = getOutputDirect() - parsed_output = parser.parse(output_direct, type, lg_data["cmd"]) + parsed_output = parse.parse(output_direct, type, lg_data["cmd"]) return parsed_output, status, router, type, command except: raise diff --git a/hyperglass/cmd_parser.py b/hyperglass/command/parse.py similarity index 100% rename from hyperglass/cmd_parser.py rename to hyperglass/command/parse.py diff --git a/hyperglass/config/.gitignore b/hyperglass/configuration/.gitignore similarity index 100% rename from hyperglass/config/.gitignore rename to hyperglass/configuration/.gitignore diff --git a/hyperglass/vars.py b/hyperglass/configuration/__init__.py similarity index 94% rename from hyperglass/vars.py rename to hyperglass/configuration/__init__.py index 86fbe1d..0bab6fd 100644 --- a/hyperglass/vars.py +++ b/hyperglass/configuration/__init__.py @@ -1,19 +1,47 @@ -import toml +import os import math +import toml + +dir = os.path.dirname(os.path.abspath(__file__)) + + +def blacklist(): + f = os.path.join(dir, "blacklist.toml") + t = toml.load(f) + return t + + +def commands(): + f = os.path.join(dir, "commands.toml") + t = toml.load(f) + return t + + +def configuration(): + f = os.path.join(dir, "configuration.toml") + t = toml.load(f) + return t + + +def devices(): + f = os.path.join(dir, "devices.toml") + t = toml.load(f) + return t + + +def requires_ipv6_cidr(): + f = os.path.join(dir, "requires_ipv6_cidr.toml") + t = toml.load(f) + return t -# Load TOML config file -config = toml.load(open("./config/config.toml")) # Filter config to branding variables -branding = config["branding"] +branding = configuration()["branding"] # Filter config to general variables -general = config["general"] +general = configuration()["general"] -# Load TOML devices file -devices = toml.load(open("./config/devices.toml")) -# Filter config to router list -routers_list = devices["router"] +routers_list = devices()["router"] class dev: diff --git a/hyperglass/config/blacklist.toml.example b/hyperglass/configuration/blacklist.toml.example similarity index 100% rename from hyperglass/config/blacklist.toml.example rename to hyperglass/configuration/blacklist.toml.example diff --git a/hyperglass/config/commands.toml.example b/hyperglass/configuration/commands.toml.example similarity index 100% rename from hyperglass/config/commands.toml.example rename to hyperglass/configuration/commands.toml.example diff --git a/hyperglass/config/config.toml.example b/hyperglass/configuration/configuration.toml.example similarity index 100% rename from hyperglass/config/config.toml.example rename to hyperglass/configuration/configuration.toml.example diff --git a/hyperglass/config/devices.toml.example b/hyperglass/configuration/devices.toml.example similarity index 100% rename from hyperglass/config/devices.toml.example rename to hyperglass/configuration/devices.toml.example diff --git a/hyperglass/config/requires_ipv6_cidr.toml.example b/hyperglass/configuration/requires_ipv6_cidr.toml.example similarity index 100% rename from hyperglass/config/requires_ipv6_cidr.toml.example rename to hyperglass/configuration/requires_ipv6_cidr.toml.example diff --git a/hyperglass/config/gunicorn_config.py b/hyperglass/gunicorn_config.py similarity index 70% rename from hyperglass/config/gunicorn_config.py rename to hyperglass/gunicorn_config.py index 13ff6a9..f7eba13 100644 --- a/hyperglass/config/gunicorn_config.py +++ b/hyperglass/gunicorn_config.py @@ -3,5 +3,6 @@ import multiprocessing command = "/usr/local/bin/gunicorn" pythonpath = "/opt/hyperglass/hyperglass" bind = "[::1]:8001" -workers = multiprocessing.cpu_count() * 2 +workers = 1 # multiprocessing.cpu_count() * 2 user = "www-data" +timeout = 60 diff --git a/hyperglass/app.py b/hyperglass/hyperglass.py similarity index 78% rename from hyperglass/app.py rename to hyperglass/hyperglass.py index b60ec05..1881bb2 100644 --- a/hyperglass/app.py +++ b/hyperglass/hyperglass.py @@ -1,46 +1,45 @@ # Module Imports -import logging -from flask import Flask, request, Response, jsonify, flash -from flask_limiter import Limiter -from flask_limiter.util import get_remote_address -from flask_caching import Cache +import sys import json import toml +from loguru import logger +from flask import Flask, request, Response, jsonify, flash +from flask_caching import Cache +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address # Local Imports -import vars -from cmd_execute import cmd_execute -import templates +from hyperglass import render +from hyperglass import configuration +from hyperglass.command import execute -log = logging.getLogger(__name__) +logger.add(sys.stderr) # Load TOML config file -devices = toml.load(open("./config/devices.toml")) +devices = configuration.devices() # Filter config file to list of routers & subsequent configurations routers_list = devices["router"] # Filter config file to array of operating systems that require IPv6 BGP lookups in CIDR format -ipv6_cidr_list = toml.load(open("./config/requires_ipv6_cidr.toml"))[ - "requires_ipv6_cidr" -] +ipv6_cidr_list = configuration.requires_ipv6_cidr() # Main Flask definition app = Flask(__name__, static_url_path="/static") # Flask-Limiter Config -rate_limit_query = vars.gen.rate_limit_query() + " per minute" -rate_limit_site = vars.gen.rate_limit_site() + "per minute" +rate_limit_query = configuration.gen.rate_limit_query() + " per minute" +rate_limit_site = configuration.gen.rate_limit_site() + "per minute" limiter = Limiter(app, key_func=get_remote_address, default_limits=[rate_limit_site]) # Render Main Flask-Limiter Error Message @app.errorhandler(429) def error429(e): """Renders full error page for too many site queries""" - html = templates.html.renderTemplate("429") + html = render.html.renderTemplate("429") return html, 429 def error415(): """Renders full error page for generic errors""" - html = templates.html.renderTemplate("415") + html = render.html.renderTemplate("415") return html, 415 @@ -59,8 +58,8 @@ cache = Cache( app, config={ "CACHE_TYPE": "filesystem", - "CACHE_DIR": vars.gen.cache_directory(), - "CACHE_DEFAULT_TIMEOUT": vars.gen.cache_timeout(), + "CACHE_DIR": configuration.gen.cache_directory(), + "CACHE_DEFAULT_TIMEOUT": configuration.gen.cache_timeout(), }, ) @@ -79,14 +78,14 @@ def clearCache(): @limiter.limit(rate_limit_site) def site(): """Main front-end web application""" - html = templates.html.renderTemplate("index") + html = render.html.renderTemplate("index") return html # Test route for various tests @app.route("/test", methods=["GET"]) def testRoute(): - html = templates.html.renderTemplate("test") + html = render.html.renderTemplate("test") return html @@ -130,16 +129,18 @@ def lg(): cache_key = str(lg_data) # Check if cached entry exists if cache.get(cache_key) is None: - cache_value = cmd_execute(lg_data) - log.debug(cache_value[1:]) + cache_value = execute.execute(lg_data) + logger.info(cache_value[1:]) value_output = cache_value[0] value_code = cache_value[1] value_params = cache_value[2:] - log.debug("No cache match for: ", cache_key, "\nAdding cache entry...") + logger.info("No cache match for: {cache_key}".format(cache_key=cache_key)) # If it doesn't, create a cache entry try: cache.set(cache_key, value_output) - log.debug("\nAdded cache entry: ", *value_params) + logger.info( + "Added cache entry: {value_params}".format(value_params=value_params) + ) except: raise RuntimeError("Unable to add output to cache.", 415, *value_params) # If 200, return output @@ -150,7 +151,11 @@ def lg(): return Response(cache.get(cache_key), value_code) # If it does, return the cached entry else: - log.debug("Cache match for: ", cache_key, "\nReturning cached entry...") + logger.info( + "Cache match for: {cache_key}, returning cached entry...".format( + cache_key=cache_key + ) + ) try: return cache.get(cache_key) except: @@ -160,8 +165,3 @@ def lg(): ) # Upon exception, render generic error return Response(errorGeneral(id)) - - -if __name__ == "__main__": - templates.css.renderTemplate() - app.run(host="0.0.0.0", debug=vars.gen.debug(), port=5000) diff --git a/hyperglass/manage.py b/hyperglass/manage.py index 8487ded..797930c 100644 --- a/hyperglass/manage.py +++ b/hyperglass/manage.py @@ -1,21 +1,17 @@ import os import sys -import app -import logging +from loguru import logger -log = logging.getLogger(__name__) +# from hyperglass.hyperglass import app - -def clearcache(): - try: - app.clearCache() - except: - raise +logger.add(sys.stderr) for arg in sys.argv: if arg == "clearcache": try: - clearcache() + hyperglass.hyperglass.app.clearcache() + logger.info("Successfully cleared cache.") except: raise + logger.error("Failed to clear cache.") diff --git a/hyperglass/render/__init__.py b/hyperglass/render/__init__.py new file mode 100644 index 0000000..2688d57 --- /dev/null +++ b/hyperglass/render/__init__.py @@ -0,0 +1,107 @@ +import os +import sass +import codecs +import jinja2 +import subprocess +from markdown2 import Markdown +from flask import render_template + +from hyperglass import configuration + +dir = os.path.dirname(os.path.abspath(__file__)) +file_loader = jinja2.FileSystemLoader(dir) +env = jinja2.Environment(loader=file_loader) + +# Converts templates/footer.md from Markdown to HTML +md = Markdown() +# footer_file = os.path.join(dir, "templates/footer.md") +footer_template = env.get_template("templates/footer.md") +footer_jinja = footer_template.render(title=configuration.brand.title()) +footer = footer_jinja + +# Functions for rendering Jinja2 templates & importing variables + + +class html: + def renderTemplate(t): + if t == "index": + template = env.get_template("templates/index.html") + elif t == "429": + template = env.get_template("templates/429.html") + elif t == "415": + template = env.get_template("templates/415.html") + elif t == "test": + template = env.get_template("templates/429.html") + return template.render( + # General + primary_asn=configuration.gen.primary_asn(), + google_analytics=configuration.gen.google_analytics(), + enable_recaptcha=configuration.gen.enable_recaptcha(), + enable_bgp_route=configuration.gen.enable_bgp_route(), + enable_bgp_community=configuration.gen.enable_bgp_community(), + enable_bgp_aspath=configuration.gen.enable_bgp_aspath(), + enable_ping=configuration.gen.enable_ping(), + enable_traceroute=configuration.gen.enable_traceroute(), + cache_timeout=configuration.gen.cache_timeout(), + message_rate_limit_query=configuration.gen.message_rate_limit_query(), + # Branding + site_title=configuration.brand.site_title(), + title=configuration.brand.title(), + subtitle=configuration.brand.subtitle(), + title_mode=configuration.brand.title_mode(), + color_hero=configuration.brand.color_hero(), + enable_credit=configuration.brand.enable_credit(), + enable_footer=configuration.brand.enable_footer(), + footer_content=md.convert(footer), + logo_path=configuration.brand.logo_path(), + logo_width=configuration.brand.logo_width(), + placeholder_prefix=configuration.brand.placeholder_prefix(), + show_peeringdb=configuration.brand.show_peeringdb(), + text_results=configuration.brand.text_results(), + text_location=configuration.brand.text_location(), + text_cache=configuration.brand.text_cache(), + text_415_title=configuration.brand.text_415_title(), + text_415_subtitle=configuration.brand.text_415_subtitle(), + text_415_button=configuration.brand.text_415_button(), + text_help_bgp_route=configuration.brand.text_help_bgp_route(), + text_help_bgp_community=configuration.brand.text_help_bgp_community(), + text_help_bgp_aspath=configuration.brand.text_help_bgp_aspath(), + text_help_ping=configuration.brand.text_help_ping(), + text_help_traceroute=configuration.brand.text_help_traceroute(), + text_limiter_title=configuration.brand.text_limiter_title(), + text_limiter_subtitle=configuration.brand.text_limiter_subtitle(), + # Devices + device_networks=configuration.dev.networks(), + # device_location=configuration.dev.location(), + device_name=configuration.dev.name(), + ) + + +class css: + def renderTemplate(): + try: + template = env.get_template("templates/hyperglass.scss") + rendered_output = template.render( + color_btn_submit=configuration.brand.color_btn_submit(), + color_progressbar=configuration.brand.color_progressbar(), + color_tag_loctitle=configuration.brand.color_tag_loctitle(), + color_tag_cmdtitle=configuration.brand.color_tag_cmdtitle(), + color_tag_cmd=configuration.brand.color_tag_cmd(), + color_tag_loc=configuration.brand.color_tag_loc(), + color_hero=configuration.brand.color_hero(), + primary_font_url=configuration.brand.primary_font_url(), + primary_font_name=configuration.brand.primary_font_name(), + mono_font_url=configuration.brand.mono_font_url(), + mono_font_name=configuration.brand.mono_font_name(), + ) + with open("static/sass/hyperglass.scss", "w") as scss_output: + scss_output.write(rendered_output) + except: + raise TypeError("Error rendering Jinja2 template.") + try: + generated_sass = sass.compile(filename="static/sass/hyperglass.scss") + with open("static/css/hyperglass.css", "w") as css_output: + css_output.write(generated_sass) + print("\n", "* Sass templates rendered to CSS files.", "\n") + except: + raise TypeError("Error rendering Sass template.") diff --git a/hyperglass/templates/415.html b/hyperglass/render/templates/415.html similarity index 100% rename from hyperglass/templates/415.html rename to hyperglass/render/templates/415.html diff --git a/hyperglass/templates/429.html b/hyperglass/render/templates/429.html similarity index 100% rename from hyperglass/templates/429.html rename to hyperglass/render/templates/429.html diff --git a/hyperglass/templates/base.html b/hyperglass/render/templates/base.html similarity index 100% rename from hyperglass/templates/base.html rename to hyperglass/render/templates/base.html diff --git a/hyperglass/templates/footer.html b/hyperglass/render/templates/footer.html similarity index 100% rename from hyperglass/templates/footer.html rename to hyperglass/render/templates/footer.html diff --git a/hyperglass/templates/footer.md b/hyperglass/render/templates/footer.md similarity index 100% rename from hyperglass/templates/footer.md rename to hyperglass/render/templates/footer.md diff --git a/hyperglass/templates/hyperglass.scss b/hyperglass/render/templates/hyperglass.scss similarity index 100% rename from hyperglass/templates/hyperglass.scss rename to hyperglass/render/templates/hyperglass.scss diff --git a/hyperglass/templates/index.html b/hyperglass/render/templates/index.html similarity index 100% rename from hyperglass/templates/index.html rename to hyperglass/render/templates/index.html diff --git a/hyperglass/templates.py b/hyperglass/templates.py deleted file mode 100644 index c1001bd..0000000 --- a/hyperglass/templates.py +++ /dev/null @@ -1,103 +0,0 @@ -from flask import render_template -import vars -import jinja2 -import subprocess -import codecs -from markdown2 import Markdown -import sass - -file_loader = jinja2.FileSystemLoader(".") -env = jinja2.Environment(loader=file_loader) - -# Converts templates/footer.md from Markdown to HTML -md = Markdown() -footer_file = env.get_template("templates/footer.md") -footer_jinja = footer_file.render(title=vars.brand.title()) -footer = footer_jinja - -# Functions for rendering Jinja2 templates & importing variables - - -class html: - def renderTemplate(t): - if t == "index": - template = env.get_template("templates/index.html") - elif t == "429": - template = env.get_template("templates/429.html") - elif t == "415": - template = env.get_template("templates/415.html") - elif t == "test": - template = env.get_template("templates/429.html") - return template.render( - # General - primary_asn=vars.gen.primary_asn(), - google_analytics=vars.gen.google_analytics(), - enable_recaptcha=vars.gen.enable_recaptcha(), - enable_bgp_route=vars.gen.enable_bgp_route(), - enable_bgp_community=vars.gen.enable_bgp_community(), - enable_bgp_aspath=vars.gen.enable_bgp_aspath(), - enable_ping=vars.gen.enable_ping(), - enable_traceroute=vars.gen.enable_traceroute(), - cache_timeout=vars.gen.cache_timeout(), - message_rate_limit_query=vars.gen.message_rate_limit_query(), - # Branding - site_title=vars.brand.site_title(), - title=vars.brand.title(), - subtitle=vars.brand.subtitle(), - title_mode=vars.brand.title_mode(), - color_hero=vars.brand.color_hero(), - enable_credit=vars.brand.enable_credit(), - enable_footer=vars.brand.enable_footer(), - footer_content=md.convert(footer), - logo_path=vars.brand.logo_path(), - logo_width=vars.brand.logo_width(), - placeholder_prefix=vars.brand.placeholder_prefix(), - show_peeringdb=vars.brand.show_peeringdb(), - text_results=vars.brand.text_results(), - text_location=vars.brand.text_location(), - text_cache=vars.brand.text_cache(), - text_415_title=vars.brand.text_415_title(), - text_415_subtitle=vars.brand.text_415_subtitle(), - text_415_button=vars.brand.text_415_button(), - text_help_bgp_route=vars.brand.text_help_bgp_route(), - text_help_bgp_community=vars.brand.text_help_bgp_community(), - text_help_bgp_aspath=vars.brand.text_help_bgp_aspath(), - text_help_ping=vars.brand.text_help_ping(), - text_help_traceroute=vars.brand.text_help_traceroute(), - text_limiter_title=vars.brand.text_limiter_title(), - text_limiter_subtitle=vars.brand.text_limiter_subtitle(), - # Devices - device_networks=vars.dev.networks(), - # device_location=vars.dev.location(), - device_name=vars.dev.name(), - ) - - -class css: - def renderTemplate(): - try: - template = env.get_template("templates/hyperglass.scss") - rendered_output = template.render( - color_btn_submit=vars.brand.color_btn_submit(), - color_progressbar=vars.brand.color_progressbar(), - color_tag_loctitle=vars.brand.color_tag_loctitle(), - color_tag_cmdtitle=vars.brand.color_tag_cmdtitle(), - color_tag_cmd=vars.brand.color_tag_cmd(), - color_tag_loc=vars.brand.color_tag_loc(), - color_hero=vars.brand.color_hero(), - primary_font_url=vars.brand.primary_font_url(), - primary_font_name=vars.brand.primary_font_name(), - mono_font_url=vars.brand.mono_font_url(), - mono_font_name=vars.brand.mono_font_name(), - ) - with open("static/sass/hyperglass.scss", "w") as scss_output: - scss_output.write(rendered_output) - except: - raise TypeError("Error rendering Jinja2 template.") - try: - generated_sass = sass.compile(filename="static/sass/hyperglass.scss") - with open("static/css/hyperglass.css", "w") as css_output: - css_output.write(generated_sass) - print("\n", "* Sass templates rendered to CSS files.", "\n") - except: - raise TypeError("Error rendering Sass template.") diff --git a/hyperglass/wsgi.py b/hyperglass/wsgi.py index e15fce8..72fda07 100644 --- a/hyperglass/wsgi.py +++ b/hyperglass/wsgi.py @@ -1,4 +1,6 @@ -import app +from hyperglass.hyperglass import app + +application = app if __name__ == "__main__": - app.run() + application.run() diff --git a/requirements.txt b/requirements.txt index 6923f7e..1ffd03f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ netmiko netaddr markdown2 libsass +loguru