├── .gitignore ├── .travis.yml ├── HISTORY.rst ├── MANIFEST.in ├── README.md ├── README.rst ├── TODO.md ├── changelog.txt ├── compare-bash ├── nk-compare-configs.sh └── settings ├── lib └── nelkit │ ├── __init__.py │ ├── args │ ├── __init__.py │ ├── base.py │ └── snmp.py │ ├── cli │ ├── __init__.py │ ├── compare_configs.py │ └── snmp_deviceinfo.py │ ├── exceptions.py │ ├── globals.py │ ├── modules │ ├── __init__.py │ └── compare_configs │ │ ├── __init__.py │ │ └── settings.py │ ├── parsing │ ├── __init__.py │ └── yaml │ │ ├── __init__.py │ │ └── loader.py │ └── snmp │ ├── __init__.py │ └── handler.py ├── pylama.ini ├── setup.py ├── subscribe.html ├── templates └── network-device-template.numbers │ ├── Index.zip │ ├── Metadata │ ├── BuildVersionHistory.plist │ ├── DocumentIdentifier │ └── Properties.plist │ ├── preview-micro.jpg │ ├── preview-web.jpg │ └── preview.jpg ├── tests ├── modules │ └── compare_configs │ │ ├── data │ │ ├── base_rules.yml │ │ ├── configs │ │ │ └── basic │ │ │ │ ├── file_a.ios │ │ │ │ ├── file_b.ios │ │ │ │ └── file_c.ios │ │ └── invalid_rule_between_missing_start.yml │ │ └── test_compare_configs.py └── parsing │ └── yaml │ ├── data │ ├── base.yml │ └── invalid_yaml.yml │ └── test_parsing_yaml.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .coverage 3 | *.pyc 4 | dist/ 5 | *egg-info/ 6 | .tox 7 | dev/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: 4 | - 2.7 5 | - 3.5 6 | - 3.6 7 | 8 | install: 9 | - pip install tox-travis 10 | - pip install coveralls 11 | script: 12 | - tox 13 | after_success: 14 | - coveralls 15 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | Release History 4 | --------------- 5 | 6 | 0.0.5 7 | +++++ 8 | 9 | * Allow configuration of baseline file for nk-compare-configs 10 | 11 | 0.0.4 12 | +++++ 13 | 14 | * Bugfixes for nk-snmp-deviceinfo 15 | 16 | 0.0.3 17 | +++++ 18 | 19 | * Speed improvements for nk-compare-configs 20 | 21 | 0.0.2 22 | +++++ 23 | 24 | * Added description for the tests used by nk-compare-configs 25 | 26 | 27 | 0.0.1 28 | +++++ 29 | 30 | First initial release 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include HISTORY.rst README.rst 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/networklore/nelkit.svg?branch=master)](https://travis-ci.org/networklore/nelkit) 2 | [![Coverage Status](https://coveralls.io/repos/networklore/nelkit/badge.svg?branch=master&service=github)](https://coveralls.io/github/networklore/nelkit?branch=master) 3 | [![Saythanks](https://img.shields.io/badge/Say%20Thanks!-%F0%9F%A6%89-1EAEDB.svg)](https://saythanks.io/to/ogenstad) 4 | 5 | NELKIT: A toolkit for network engineers 6 | ======================================= 7 | 8 | [Nelkit](https://networklore.com/nelkit/) is a collection of tools aimed to help network engineers. 9 | 10 | ### Installation 11 | 12 | Install using pip: 13 | 14 | ```bash 15 | 16 | $ pip install nelkit 17 | ``` 18 | 19 | ### Tools 20 | 21 | These tools are currently included: 22 | 23 | * [nk-compare-configs](https://networklore.com/nk-compare-configs/) - Preforms audits of configuration files for network devices 24 | * nk-snmp-deviceinfo - Polls a network device using SNMP to show vendor and version information 25 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | NELKIT: A toolkit for network engineers 2 | ======================================= 3 | 4 | Nelkit is a collection of tools aimed to help network engineers. 5 | 6 | Installation 7 | ------------ 8 | 9 | Install using pip: 10 | 11 | .. code-block:: bash 12 | 13 | $ pip install nelkit 14 | 15 | Tools 16 | ----- 17 | 18 | These tools are currently included: 19 | 20 | * nk-compare-configs - Preforms audits of configuration files for network devices 21 | * nk-snmp-deviceinfo - Polls a network device using SNMP to show vendor and version information 22 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | directory to configs 2 | 3 | rules file 4 | 5 | configs: 6 | - '/path/to/configs1' 7 | - '/path/to/configs2' 8 | 9 | 10 | functions 11 | - match 12 | - match_exclude 13 | - match_sort 14 | - match_exclude_sort 15 | - start_end 16 | - start_end_exclude 17 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | - 2014-02-18 ------- 2 | 3 | Added the compare-bash directory 4 | nk-compare-configs.sh version 1.0 5 | 6 | - 2014-01-07 ------- 7 | 8 | First initial release 9 | network-template-device.numbers version 1.0 -------------------------------------------------------------------------------- /compare-bash/nk-compare-configs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #========================================================== 3 | # LANG : Bash 4 | # NAME : nk-compare-configs.sh 5 | # AUTHOR : Patrick Ogenstad 6 | # VERSION : 1.0 7 | # DATE : 2014-02-18 8 | # Description : Parses network device configuration files 9 | # and checks to see if your devices are configured in the 10 | # same way. 11 | # 12 | # The Script is part of Nelkit (NetworkLore Toolkit) 13 | # http://networklore.com/nelkit/ 14 | # 15 | # Guidelines and updates: 16 | # http://networklore.com/compare-router-configs/ 17 | # 18 | # Feedback: Please send feedback: 19 | # http://networklore.com/contact/ 20 | # 21 | #========================================================== 22 | #========================================================== 23 | 24 | # Default settings 25 | RULESFILE="settings" 26 | WORKDIR="work" 27 | BASELINE="" 28 | 29 | SCRIPTVERSION="1.0" 30 | 31 | bFINDWORKDIR="false" 32 | bFINDBASELINE="false" 33 | bFINDDEVICELIST="false" 34 | 35 | #========================================================== 36 | # Define functions 37 | #========================================================== 38 | 39 | function check_distress { 40 | # Check to see if help has been requested 41 | if [ `echo "$1" | grep -E -c "^-h$|^help$|^--help$|^-help$"` -gt 0 ]; then 42 | display_help 43 | fi 44 | } 45 | 46 | function display_help { 47 | echo "" 48 | echo "nk-compare-configs.sh v.$SCRIPTVERSION" 49 | echo "" 50 | echo "Usage:" 51 | echo "./nk-compare-configs.sh" 52 | echo "./nk-compare-configs.sh rulesfile" 53 | echo "./nk-compare-configs.sh rulesfile configdir" 54 | echo "./nk-compare-configs.sh rulesfile configdir baselineconfig" 55 | echo "./nk-compare-configs.sh rulesfile configdir baselineconfig devlicelist" 56 | echo "" 57 | echo "Guidelines:" 58 | echo "You can run the script with 0-4 arguments. If you don't use any arguments" 59 | echo "the script will default to using a file called 'settings' in the current" 60 | echo "directory. The script will also default to looking in a directory called" 61 | echo "'work'. If you don't specify a baseline config file the script will choose" 62 | echo "the first file from the work directory. If you don't want the script to parse" 63 | echo "all of the files in the work directory you can point to a file containing" 64 | echo "a subset of your devices." 65 | echo "" 66 | echo "For more information and a howto guide which help you setup your settings" 67 | echo "file, visit:" 68 | echo "" 69 | echo "http://networklore.com/compare-router-configs/" 70 | echo "" 71 | exit 72 | } 73 | 74 | function match { 75 | MATCHSTRING="$1" 76 | RUN=1 77 | echo "" 78 | echo "--------------------------------------" 79 | echo "match" 80 | echo "string: $MATCHSTRING" 81 | echo "--------------------------------------" 82 | if [ "$bUSEBASELINE" = "true" ] ; then 83 | REFERENCE=`grep -E -- "$MATCHSTRING" $BASELINE` 84 | fi 85 | 86 | for DEVICE in $COLLECTION 87 | do 88 | if [ "$bUSEBASELINE" = "false" ] ; then 89 | if [ $RUN -eq 1 ]; then 90 | REFERENCE=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE` 91 | fi 92 | fi 93 | 94 | CURRENT=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE` 95 | 96 | RUN=`expr $RUN + 1` 97 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 98 | if [ `echo ${#DIFF}` -gt 0 ]; then 99 | echo "$DEVICE differs from baseline" 100 | fi 101 | done 102 | } 103 | 104 | function match_exclude { 105 | MATCHSTRING="$1" 106 | EXCLUDESTRING="$2" 107 | RUN=1 108 | 109 | echo "" 110 | echo "--------------------------------------" 111 | echo "match_exclude" 112 | echo "match: $MATCHSTRING" 113 | echo "exclude: $EXCLUDESTRING" 114 | echo "--------------------------------------" 115 | if [ "$bUSEBASELINE" = "true" ] ; then 116 | REFERENCE=`grep -E -- "$MATCHSTRING" $BASELINE | grep -E -v -- "$EXCLUDESTRING"` 117 | fi 118 | 119 | for DEVICE in $COLLECTION 120 | do 121 | if [ "$bUSEBASELINE" = "false" ] ; then 122 | if [ $RUN -eq 1 ]; then 123 | REFERENCE=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING"` 124 | fi 125 | fi 126 | CURRENT=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING"` 127 | 128 | RUN=`expr $RUN + 1` 129 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 130 | if [ `echo ${#DIFF}` -gt 0 ]; then 131 | echo "$DEVICE differs from baseline" 132 | fi 133 | done 134 | } 135 | 136 | function match_sort { 137 | MATCHSTRING="$1" 138 | RUN=1 139 | echo "" 140 | echo "--------------------------------------" 141 | echo "match_sort" 142 | echo "string: $MATCHSTRING" 143 | echo "--------------------------------------" 144 | if [ "$bUSEBASELINE" = "true" ] ; then 145 | REFERENCE=`grep -E -- "$MATCHSTRING" $BASELINE | sort` 146 | fi 147 | 148 | for DEVICE in $COLLECTION 149 | do 150 | if [ "$bUSEBASELINE" = "false" ] ; then 151 | if [ $RUN -eq 1 ]; then 152 | REFERENCE=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | sort` 153 | fi 154 | fi 155 | CURRENT=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | sort` 156 | 157 | RUN=`expr $RUN + 1` 158 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 159 | if [ `echo ${#DIFF}` -gt 0 ]; then 160 | echo "$DEVICE differs from baseline" 161 | fi 162 | done 163 | } 164 | 165 | function match_exclude_sort { 166 | MATCHSTRING="$1" 167 | EXCLUDESTRING="$2" 168 | RUN=1 169 | 170 | echo "" 171 | echo "--------------------------------------" 172 | echo "match_exclude_sort" 173 | echo "match: $MATCHSTRING" 174 | echo "exclude: $EXCLUDESTRING" 175 | echo "--------------------------------------" 176 | if [ "$bUSEBASELINE" = "true" ] ; then 177 | REFERENCE=`grep -E -- "$MATCHSTRING" $BASELINE | grep -E -v -- "$EXCLUDESTRING" | sort` 178 | fi 179 | 180 | for DEVICE in $COLLECTION 181 | do 182 | if [ "$bUSEBASELINE" = "false" ] ; then 183 | if [ $RUN -eq 1 ]; then 184 | REFERENCE=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING" | sort` 185 | fi 186 | fi 187 | CURRENT=`grep -E -- "$MATCHSTRING" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING" | sort` 188 | 189 | RUN=`expr $RUN + 1` 190 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 191 | if [ `echo ${#DIFF}` -gt 0 ]; then 192 | echo "$DEVICE differs from baseline" 193 | fi 194 | done 195 | } 196 | 197 | function start_end { 198 | BEGINSTRING="$1" 199 | ENDSTRING="$2" 200 | RUN=1 201 | 202 | echo "" 203 | echo "--------------------------------------" 204 | echo "start_end" 205 | echo "start: $BEGINSTRING" 206 | echo "end: $ENDSTRING" 207 | echo "--------------------------------------" 208 | if [ "$bUSEBASELINE" = "true" ] ; then 209 | REFERENCE=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $BASELINE` 210 | if [ "${#REFERENCE}" = "0" ] ; then 211 | echo "No match found in reference" 212 | fi 213 | fi 214 | 215 | for DEVICE in $COLLECTION 216 | do 217 | if [ "$bUSEBASELINE" = "false" ] ; then 218 | if [ $RUN -eq 1 ]; then 219 | REFERENCE=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $WORKDIR/$DEVICE` 220 | if [ "${#REFERENCE}" = "0" ] ; then 221 | echo "No match found in reference" 222 | fi 223 | fi 224 | fi 225 | 226 | CURRENT=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $WORKDIR/$DEVICE` 227 | RUN=`expr $RUN + 1` 228 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 229 | if [ `echo ${#DIFF}` -gt 0 ]; then 230 | echo "$DEVICE differs from baseline" 231 | fi 232 | done 233 | } 234 | 235 | function start_end_exclude { 236 | BEGINSTRING="$1" 237 | ENDSTRING="$2" 238 | EXCLUDESTRING="$3" 239 | RUN=1 240 | 241 | echo "" 242 | echo "--------------------------------------" 243 | echo "start_end_exclude" 244 | echo "start: $BEGINSTRING" 245 | echo "end: $ENDSTRING" 246 | echo "exclude: $EXCLUDESTRING" 247 | echo "--------------------------------------" 248 | if [ "$bUSEBASELINE" = "true" ] ; then 249 | REFERENCE=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $BASELINE | grep -E -v -- "$EXCLUDESTRING"` 250 | if [ "${#REFERENCE}" = "0" ] ; then 251 | echo "No match found in reference" 252 | fi 253 | fi 254 | 255 | for DEVICE in $COLLECTION 256 | do 257 | if [ "$bUSEBASELINE" = "false" ] ; then 258 | if [ $RUN -eq 1 ]; then 259 | REFERENCE=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING"` 260 | if [ "${#REFERENCE}" = "0" ] ; then 261 | echo "No match found in reference" 262 | fi 263 | fi 264 | fi 265 | 266 | CURRENT=`sed -n "/$BEGINSTRING/,/$ENDSTRING/p" $WORKDIR/$DEVICE | grep -E -v -- "$EXCLUDESTRING"` 267 | RUN=`expr $RUN + 1` 268 | DIFF=`diff <(echo "$REFERENCE" ) <(echo "$CURRENT")` 269 | if [ `echo ${#DIFF}` -gt 0 ]; then 270 | echo "$DEVICE differs from baseline" 271 | fi 272 | done 273 | } 274 | 275 | function verify_parameters { 276 | 277 | # Check to see if the rulesfile is readable 278 | if [ ! -r "$RULESFILE" ] ; then 279 | echo "" 280 | echo "## ERROR ########################################" 281 | echo "Unable to read settings from: $RULESFILE" 282 | echo "#################################################" 283 | display_help 284 | fi 285 | 286 | # Check to see if the working directory should be read from the settings file 287 | if [ "$bFINDWORKDIR" = "true" ] ; then 288 | SETTINGSWORKDIR=`grep -- "^CONFIGDIR" "$RULESFILE" | cut -f 2 -d "=" | sed -r 's/"//g'` 289 | if [ `echo ${#SETTINGSWORKDIR}` -gt 0 ]; then 290 | WORKDIR="$SETTINGSWORKDIR" 291 | fi 292 | fi 293 | 294 | # Check to see if the working directory exists 295 | if [ ! -d "$WORKDIR" ]; then 296 | # Control will enter here if $DIRECTORY doesn't exist. 297 | echo "" 298 | echo "## ERROR ########################################" 299 | echo "Unable to read working directory: $WORKDIR" 300 | echo "#################################################" 301 | display_help 302 | fi 303 | 304 | # Check to see if the baseline file should be read from the settings file 305 | if [ "$bFINDBASELINE" = "true" ] ; then 306 | SETTINGSBASELINE=`grep -- "^BASECONFIG" $RULESFILE | cut -f 2 -d "=" | sed -r 's/"//g'` 307 | if [ `echo ${#SETTINGSBASELINE}` -gt 0 ]; then 308 | BASELINE="$SETTINGSBASELINE" 309 | fi 310 | fi 311 | 312 | if [ `echo ${#BASELINE}` -gt 0 ]; then 313 | bUSEBASELINE="true" 314 | if [ `echo $BASELINE | grep -c "/"` -eq 0 ]; then 315 | # A / is not included in the baseline setting, use baseline from workdir 316 | BASELINE="$WORKDIR/$BASELINE" 317 | fi 318 | if [ ! -r "$BASELINE" ] ; then 319 | echo "" 320 | echo "## ERROR ########################################" 321 | echo "Unable to read baseline file: $BASELINE" 322 | echo "#################################################" 323 | display_help 324 | fi 325 | 326 | else 327 | bUSEBASELINE="false" 328 | fi 329 | 330 | # Check to see if a devicelist should be read from the settings file 331 | if [ "$bFINDDEVICELIST" = "true" ] ; then 332 | SETTINGSDEVICELIST=`grep -- "^DEVICEFILE" "$RULESFILE" | cut -f 2 -d "=" | sed -r 's/"//g'` 333 | if [ `echo ${#SETTINGSDEVICELIST}` -gt 0 ]; then 334 | DEVICELIST="$SETTINGSDEVICELIST" 335 | fi 336 | fi 337 | 338 | if [ `echo ${#DEVICELIST}` -gt 0 ]; then 339 | # Read from device list 340 | if [ ! -r "$DEVICELIST" ] ; then 341 | echo "" 342 | echo "## ERROR ########################################" 343 | echo "Unable to read devicelist: $DEVICELIST" 344 | echo "#################################################" 345 | display_help 346 | fi 347 | # Use a specific subset of working directory 348 | COLLECTION=`cat $DEVICELIST` 349 | else 350 | # Use all of the devices in the workdir 351 | COLLECTION=`ls -1 $WORKDIR` 352 | fi 353 | 354 | #Verify that we can read our collection 355 | for FILE in $COLLECTION 356 | do 357 | 358 | if [ ! -r "$WORKDIR/$FILE" ] ; then 359 | echo "## ERROR ########################################" 360 | echo "Unable to read the file: $WORKDIR/$FILE" 361 | echo "#################################################" 362 | display_help 363 | fi 364 | done 365 | 366 | } 367 | 368 | #========================================================== 369 | # Main Script 370 | #========================================================== 371 | 372 | # Parse script arguments 373 | 374 | if [ $# -gt 4 ] ; then 375 | echo "You have enterned too many arguments" 376 | display_help 377 | elif [ $# -eq 4 ] ; then 378 | RULESFILE="$1" 379 | WORKDIR="$2" 380 | BASELINE="$3" 381 | DEVICELIST="$4" 382 | elif [ $# -eq 3 ] ; then 383 | RULESFILE="$1" 384 | WORKDIR="$2" 385 | BASELINE="$3" 386 | bFINDDEVICELIST="true" 387 | elif [ $# -eq 2 ] ; then 388 | RULESFILE="$1" 389 | WORKDIR="$2" 390 | bFINDBASELINE="true" 391 | bFINDDEVICELIST="true" 392 | elif [ $# -eq 1 ] ; then 393 | RULESFILE="$1" 394 | bFINDWORKDIR="true" 395 | bFINDBASELINE="true" 396 | bFINDDEVICELIST="true" 397 | elif [ $# -eq 0 ] ; then 398 | bFINDWORKDIR="true" 399 | bFINDBASELINE="true" 400 | bFINDDEVICELIST="true" 401 | fi 402 | 403 | check_distress $RULESFILE 404 | check_distress $WORKDIR 405 | check_distress $BASELINE 406 | check_distress $DEVICELIST 407 | 408 | verify_parameters 409 | 410 | . $RULESFILE 411 | 412 | -------------------------------------------------------------------------------- /compare-bash/settings: -------------------------------------------------------------------------------- 1 | ################################################# 2 | # For a guide to help you configure your 3 | # settings file please visit: 4 | # http://networklore.com/compare-router-configs/ 5 | ################################################# 6 | 7 | 8 | # Match all lines beginning with "ntp" 9 | match "^ntp" 10 | 11 | # Match all lines beginning with "snmp-server", but exclude "location" 12 | match_exclude "^snmp-server" "location" 13 | 14 | # Match all lines containing "logging" 15 | match "logging" 16 | 17 | # Match from a line starting with "line con" until a line starting 18 | # with "!" 19 | start_end "^line con" "^!" 20 | 21 | # Match from a line starting with "interface GigabitEthernet0/0" 22 | # (note the escaped "/" character) until a line starting with "!" 23 | # but ignore the "ip address" line or any line containing "flow" 24 | start_end_exclude "^interface GigabitEthernet0\/0" "^!" "ip address|flow" 25 | 26 | # Match from a line starting with "ip access-list extended ACL-INTERNET-V6" 27 | # match until a line starts with "!" or "i" (as in a new access-list), 28 | # but also exclude lines starting with either "!" or "i" 29 | start_end_exclude "^ip access-list extended ACL-INTERNET-V6" "^[!i]" "^[!i]" 30 | 31 | # Match lines starting with "service" or "no service" 32 | match_sort "^service|^no service" 33 | 34 | # Match lines starting with "ip inspect name", but don't care about the 35 | # order the commands appear in 36 | match_sort "^ip inspect name" 37 | 38 | # Match lines starting with "ip inspect name", but don't care about the 39 | # order the commands appear in. Also exclude lines containing DMZFW 40 | match_exclude_sort "^ip inspect name" "DMZFW" 41 | 42 | -------------------------------------------------------------------------------- /lib/nelkit/__init__.py: -------------------------------------------------------------------------------- 1 | """Nelkit - Networklore Toolkit.""" 2 | 3 | __author__ = 'Patrick Ogenstad' 4 | __version__ = '0.0.5' 5 | -------------------------------------------------------------------------------- /lib/nelkit/args/__init__.py: -------------------------------------------------------------------------------- 1 | """Argument module.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/args/base.py: -------------------------------------------------------------------------------- 1 | """Base argument module.""" 2 | from argparse import ArgumentParser, RawTextHelpFormatter 3 | 4 | 5 | # class NkArgumentParser(ArgumentParser): 6 | # pass 7 | 8 | 9 | class HelpText(object): 10 | """Nelkit specific helptext.""" 11 | 12 | def __init__(self, description, epilog): 13 | """Nelkit specific helptext.""" 14 | desc_prefix = '#' * 75 15 | desc_prefix += '\n' 16 | desc_suffix = '#' * 75 17 | self.description = desc_prefix + description + '\n' + desc_suffix 18 | epilog_suffix = '#' * 75 19 | epilog_suffix += '\n' 20 | epilog_suffix += 'This tool is part of Nelkit:\n' 21 | epilog_suffix += 'https://networklore.com/nelkit\n' 22 | epilog_suffix += '\n' 23 | self.epilog = epilog_suffix + epilog 24 | 25 | 26 | class BaseArgs(object): 27 | """Nelkit base argument class.""" 28 | 29 | def __init__(self, description, epilog=''): 30 | """Nelkit base argument class.""" 31 | helptext = HelpText(description, epilog) 32 | self.parser = ArgumentParser( 33 | description=helptext.description, 34 | epilog=helptext.epilog, 35 | formatter_class=RawTextHelpFormatter) 36 | 37 | self.parser.add_argument( 38 | '-V', 39 | help='Show version', 40 | action='store_true') 41 | self.parser.add_argument( 42 | '-O', 43 | help='Output format', 44 | choices=['standard', 'with_status'], 45 | default='standard' 46 | ) 47 | 48 | self._add_local_args() 49 | 50 | def _add_local_args(self): 51 | pass 52 | -------------------------------------------------------------------------------- /lib/nelkit/args/snmp.py: -------------------------------------------------------------------------------- 1 | """Module to handle snmp arguments for cli tools.""" 2 | from nelkit.args.base import BaseArgs 3 | 4 | 5 | class SnmpArgs(BaseArgs): 6 | """Argument class for snmp tools.""" 7 | 8 | def __init__(self, description, epilog=''): 9 | """Argument class for snmp tools.""" 10 | super(SnmpArgs, self).__init__(description, epilog) 11 | 12 | def _add_local_args(self): 13 | self.parser.add_argument('-H', help='Target host', required=True) 14 | self.parser.add_argument( 15 | '-p', help="Port number (default: 161)", default=161) 16 | self.parser.add_argument( 17 | '-P', help="SNMP protocol version", choices=['2c', '3'], required=True) 18 | self.parser.add_argument('-C', help="SNMP Community string") 19 | self.parser.add_argument( 20 | '-L', help="SNMPv3 Security level", 21 | choices=['authNoPriv', 'authPriv']) 22 | self.parser.add_argument( 23 | '-a', help="SNMPv3 authentiction protocol", 24 | choices=['MD5', 'SHA']) 25 | self.parser.add_argument( 26 | '-x', help="SNMPv3 privacy protocol", 27 | choices=['DES', 'AES']) 28 | self.parser.add_argument('-U', help="SNMPv3 username") 29 | self.parser.add_argument('-A', help="SNMPv3 authentication password") 30 | self.parser.add_argument('-X', help="SNMPv3 privacy password") 31 | -------------------------------------------------------------------------------- /lib/nelkit/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """Nelkit command line tools.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/cli/compare_configs.py: -------------------------------------------------------------------------------- 1 | """nk-compare-configs.""" 2 | from nelkit.args.base import BaseArgs 3 | from nelkit.globals import NelkitGlobals 4 | from nelkit.modules.compare_configs.settings import CompareConfigs 5 | 6 | description = 'Compare configurations against a baseline' 7 | 8 | 9 | def main(): 10 | """Launch nk-compare-configs.""" 11 | NelkitGlobals(FRIENDLY_EXCEPTION=True) 12 | 13 | argparser = BaseArgs(description) 14 | argparser.parser.add_argument( 15 | '-c', 16 | help='Configuration file', 17 | type=str, 18 | required=True) 19 | args = argparser.parser.parse_args() 20 | cc = CompareConfigs(settings_file=args.c) 21 | cc.output_diff() 22 | 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /lib/nelkit/cli/snmp_deviceinfo.py: -------------------------------------------------------------------------------- 1 | """nk-snmp-deviceinfo - command line tool.""" 2 | from nelkit.args.snmp import SnmpArgs 3 | from nelkit.snmp.handler import NelkitSnmp 4 | from nelsnmp.hostinfo.device import HostInfo 5 | 6 | description = 'Collects Device info using SNMP' 7 | 8 | 9 | def main(): 10 | """run nk-snmp-deviceinfo.""" 11 | argparser = SnmpArgs(description) 12 | args = argparser.parser.parse_args() 13 | snmp = NelkitSnmp(args) 14 | hostinfo = HostInfo(snmp) 15 | try: 16 | hostinfo.get_all() 17 | print('OS: %s' % hostinfo.os) 18 | print('Version: %s' % hostinfo.version) 19 | print('Vendor: %s' % hostinfo.vendor) 20 | print('Description: %s' % hostinfo.description) 21 | except Exception as e: 22 | print('ERROR: {0}'.format(e)) 23 | 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /lib/nelkit/exceptions.py: -------------------------------------------------------------------------------- 1 | """This module contains exceptions available to nelkit.""" 2 | 3 | import sys 4 | from nelkit.globals import NelkitGlobals 5 | 6 | 7 | class NelkitException(Exception): 8 | """Base nelkit exception class. 9 | 10 | By default an exception will be raised, this can be overwritten using 11 | nelkit.globals.NelkitGlobals so that the error messages are printed out 12 | to the screen instead. This is done from the cli tools. 13 | """ 14 | 15 | def __init__(self, message): 16 | """Base nelkit exception class.""" 17 | if NelkitGlobals.FRIENDLY_EXCEPTION: 18 | print(message) 19 | sys.exit() 20 | else: 21 | super(NelkitException, self).__init__(message) 22 | 23 | 24 | class ArgumentError(NelkitException): 25 | """Raised when passing invalid arguments using cli tools.""" 26 | 27 | pass 28 | 29 | 30 | class FileNotFound(NelkitException): 31 | """Raised when trying to open a file which doesn't exist.""" 32 | 33 | pass 34 | 35 | 36 | class ParsingError(NelkitException): 37 | """Raised when trying to parse a file with invalid formatting.""" 38 | 39 | pass 40 | -------------------------------------------------------------------------------- /lib/nelkit/globals.py: -------------------------------------------------------------------------------- 1 | """This module contains the NelkitGlobals class used to override standard settings.""" 2 | 3 | 4 | class NelkitGlobals(object): 5 | """Nelkit global class to override standard settings.""" 6 | 7 | FRIENDLY_EXCEPTION = None 8 | 9 | def __init__(self, **kwargs): 10 | """Override standard settings. 11 | 12 | :param FRIENDLY_EXCEPTION: (optional) Boolean value to control if errors are to be printed or raised. 13 | """ 14 | for key in kwargs: 15 | if key == 'FRIENDLY_EXCEPTION': 16 | NelkitGlobals.FRIENDLY_EXCEPTION = kwargs[key] 17 | -------------------------------------------------------------------------------- /lib/nelkit/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """nelkit.modules.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/modules/compare_configs/__init__.py: -------------------------------------------------------------------------------- 1 | """nelkit.modules.compare_configs.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/modules/compare_configs/settings.py: -------------------------------------------------------------------------------- 1 | """Module to compare different configurations files.""" 2 | import difflib 3 | import itertools 4 | import os 5 | import re 6 | from glob import glob 7 | from nelkit.exceptions import FileNotFound, NelkitException 8 | from nelkit.parsing.yaml.loader import YamlLoader 9 | 10 | 11 | class CompareConfigs: 12 | """Compare configs class, used in nk-compare-configs.""" 13 | 14 | def __init__(self, settings_file=None, config_dir=None, baseline=None): 15 | """Compare configs class, used in nk-compare-configs.""" 16 | self._baseline = baseline 17 | self._settings = settings_file 18 | self._config_dir = config_dir 19 | self.rules = {} 20 | self._between = {} 21 | self._num_rules = 0 22 | self._diff = {} 23 | self._parse_settings() 24 | 25 | if not self._baseline: 26 | if len(self._config_files) > 0: 27 | self._baseline = self._config_files[0] 28 | else: 29 | raise FileNotFound('No config files found') 30 | else: 31 | if '/' not in self._baseline: 32 | self._baseline = self._config_dir + '/' + self._baseline 33 | 34 | self._parse_config_files() 35 | self._parse_sort_rules() 36 | self._compare_configs() 37 | 38 | def _compare_configs(self): 39 | for host in self._config_files: 40 | for rule in self._baseline_matches: 41 | if self._baseline_matches[rule] != self._matches[host][rule]: 42 | node = host.split(os.path.sep)[-1] 43 | diff = difflib.unified_diff( 44 | self._baseline_matches[rule], 45 | self._matches[host][rule], 46 | fromfile='baseline', 47 | tofile=node) 48 | diff = os.linesep.join([x for x in diff]) 49 | if host not in self._diff.keys(): 50 | self._diff[host] = {} 51 | 52 | self._diff[host][rule] = diff 53 | 54 | def _parse_between_rule(self, rule): 55 | self._num_rules += 1 56 | if 'start' not in rule.keys(): 57 | raise NelkitException('start key missing in between rule') 58 | if not isinstance(rule['start'], str): 59 | raise NelkitException('"start" under between rule has the wrong format') 60 | match = {} 61 | match['rule_type'] = 'between' 62 | match['start'] = rule['start'] 63 | match['start_re'] = re.compile(rule['start']) 64 | match['end'] = rule.get('end') 65 | match['sort'] = rule.get('sort') 66 | match['until_not'] = rule.get('until_not') 67 | if match['end']: 68 | match['end_re'] = re.compile(match['end']) 69 | if match['until_not']: 70 | match['until_not_re'] = re.compile(match['until_not']) 71 | match['description'] = rule.get('description') 72 | 73 | if match['end'] and match['until_not']: 74 | raise NelkitException('"between" rule can not have both end and until_not') 75 | elif not match['end'] and not match['until_not']: 76 | raise NelkitException('"between" rule must have end or until_not') 77 | 78 | if 'exclude' in rule.keys(): 79 | match['exclude'] = rule['exclude'] 80 | else: 81 | match['exclude'] = None 82 | if match['exclude']: 83 | match['exclude_re'] = re.compile(match['exclude']) 84 | 85 | self.rules[self._num_rules] = match 86 | 87 | def _parse_configs_dir(self, config_setting, data): 88 | 89 | if config_setting: 90 | config_dir = config_setting 91 | else: 92 | if 'configs' in data.keys(): 93 | config_dir = data['configs'] 94 | else: 95 | raise NelkitException('config key not found in file') 96 | self._config_dir = config_dir 97 | 98 | if not isinstance(config_dir, list): 99 | config_dir = [config_dir] 100 | 101 | config_files = [] 102 | for cur_path in config_dir: 103 | if not os.path.isdir(cur_path): 104 | raise NelkitException('%s is not a valid config directory' % cur_path) 105 | if cur_path[-1] != os.path.sep: 106 | cur_path += os.path.sep 107 | config_files.append(glob('%s*' % cur_path)) 108 | self._config_files = list(itertools.chain.from_iterable(config_files)) 109 | 110 | def _parse_config_files(self): 111 | self._matches = {} 112 | for config in self._config_files: 113 | if config not in self._matches.keys(): 114 | self._matches[config] = {} 115 | 116 | with open(config) as f: 117 | for line in f: 118 | for rule in self.rules: 119 | if rule not in self._matches[config].keys(): 120 | self._matches[config][rule] = [] 121 | if self.rules[rule]['rule_type'] == 'match': 122 | self._run_match_rule(rule, config, line) 123 | elif self.rules[rule]['rule_type'] == 'between': 124 | self._run_between_rule(rule, config, line) 125 | 126 | def _parse_match_rule(self, rule): 127 | self._num_rules += 1 128 | if 'string' not in rule.keys(): 129 | raise NelkitException('string missing in match rule') 130 | if not isinstance(rule['string'], str): 131 | raise NelkitException('"string" under match rule has the wrong format') 132 | match = {} 133 | match['rule_type'] = 'match' 134 | match['description'] = rule.get('description') 135 | match['string'] = rule['string'] 136 | match['string_re'] = re.compile(rule['string']) 137 | match['sort'] = rule.get('sort') 138 | if 'exclude' in rule.keys(): 139 | match['exclude'] = rule['exclude'] 140 | else: 141 | match['exclude'] = None 142 | self.rules[self._num_rules] = match 143 | 144 | def _parse_settings(self): 145 | l = YamlLoader(filename=self._settings) 146 | data = l.data 147 | self._parse_configs_dir(self._config_dir, data) 148 | 149 | if not self._baseline: 150 | self._baseline = data.get('baseline') 151 | 152 | if 'rules' not in data.keys(): 153 | raise NelkitException('rules key not found in file') 154 | rules = data['rules'] 155 | 156 | if not isinstance(rules, list): 157 | raise NelkitException('rules has to be a list') 158 | 159 | for rule in rules: 160 | if not isinstance(rule, dict): 161 | raise NelkitException('rule not a dict!!!!') 162 | 163 | for criteria in rule: 164 | if not isinstance(rule[criteria], dict): 165 | raise NelkitException('NOoOooooo') 166 | if criteria == 'between': 167 | self._parse_between_rule(rule[criteria]) 168 | elif criteria == 'match': 169 | self._parse_match_rule(rule[criteria]) 170 | else: 171 | raise NelkitException('%s is not a valid rule type' % criteria) 172 | 173 | def _parse_sort_rules(self): 174 | 175 | for host in self._matches: 176 | for rule in self._matches[host]: 177 | if self.rules[rule]['sort']: 178 | self._matches[host][rule] = sorted(self._matches[host][rule]) 179 | 180 | if self._baseline not in self._matches.keys(): 181 | raise NelkitException('Unable to find baseline') 182 | else: 183 | self._baseline_matches = self._matches[self._baseline] 184 | 185 | def _run_between_rule(self, rule, config, line): 186 | if config not in self._between.keys(): 187 | self._between[config] = {} 188 | if rule not in self._between[config].keys(): 189 | self._between[config][rule] = {} 190 | self._between[config][rule]['in'] = False 191 | if self.rules[rule]['end']: 192 | if self._between[config][rule]['in']: 193 | if self.rules[rule]['end_re'].match(line): 194 | self._between[config][rule]['in'] = False 195 | if self.rules[rule]['exclude']: 196 | if self.rules[rule]['exclude_re'].match(line): 197 | return 198 | self._matches[config][rule].append(line.rstrip()) 199 | 200 | if self.rules[rule]['until_not']: 201 | if self._between[config][rule]['in']: 202 | if self.rules[rule]['until_not_re'].match(line): 203 | if self.rules[rule]['exclude']: 204 | if self.rules[rule]['exclude_re'].match(line): 205 | return 206 | self._matches[config][rule].append(line.rstrip()) 207 | else: 208 | self._between[config][rule]['in'] = False 209 | 210 | if self.rules[rule]['start_re'].match(line): 211 | self._between[config][rule]['in'] = True 212 | if self.rules[rule]['exclude']: 213 | exclude = re.compile(self.rules[rule]['exclude']) 214 | if exclude.match(line): 215 | return 216 | self._matches[config][rule].append(line.rstrip()) 217 | 218 | def _run_match_rule(self, rule, config, line): 219 | if self.rules[rule]['string_re'].match(line): 220 | if self.rules[rule]['exclude']: 221 | exclude = re.compile(self.rules[rule]['exclude']) 222 | if exclude.match(line): 223 | return 224 | self._matches[config][rule].append(line.rstrip()) 225 | 226 | def output_diff(self): 227 | """Print output with diffs.""" 228 | for host in sorted(self._diff.keys()): 229 | node = host.split(os.path.sep)[-1] 230 | print('#########################') 231 | print('# %s' % node) 232 | print('#########################') 233 | for rule in self._diff[host]: 234 | print(self._diff[host][rule]) 235 | print('#########################') 236 | print('') 237 | -------------------------------------------------------------------------------- /lib/nelkit/parsing/__init__.py: -------------------------------------------------------------------------------- 1 | """nelkit.parsing.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/parsing/yaml/__init__.py: -------------------------------------------------------------------------------- 1 | """nelkit.parsing.yaml.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/parsing/yaml/loader.py: -------------------------------------------------------------------------------- 1 | """Load yaml files.""" 2 | import os 3 | import yaml 4 | from nelkit.exceptions import FileNotFound, ParsingError 5 | 6 | 7 | class YamlLoader: 8 | """Load yaml files.""" 9 | 10 | def __init__(self, filename=None): 11 | """Load yaml files. 12 | 13 | :param filename: The yaml file to load 14 | 15 | Usage:: 16 | >>> from nelkit.parsing.yaml.loader import YamlLoader 17 | >>> y = YamlLoader('my_file.yml') 18 | >>> content = y.data 19 | """ 20 | self._filename = filename 21 | self._load() 22 | 23 | def _load(self): 24 | if not os.path.isfile(self._filename): 25 | raise FileNotFound('Unable to read: %s' % self._filename) 26 | try: 27 | with open(self._filename) as f: 28 | self.data = yaml.load(f.read()) 29 | except: 30 | raise ParsingError('%s is not a valid yaml file' % self._filename) 31 | -------------------------------------------------------------------------------- /lib/nelkit/snmp/__init__.py: -------------------------------------------------------------------------------- 1 | """nelkit.snmp.""" 2 | -------------------------------------------------------------------------------- /lib/nelkit/snmp/handler.py: -------------------------------------------------------------------------------- 1 | """Create an SNMP handler using nelsnmp.""" 2 | from nelkit.exceptions import ArgumentError 3 | from nelsnmp.snmp import cmdgen, SnmpHandler 4 | 5 | 6 | class NelkitSnmp(SnmpHandler): 7 | """Nelkit version of SnmpHandler from nelsnmp.""" 8 | 9 | def __init__(self, args): 10 | """Return a nelsnmp SnmpHandler.""" 11 | self._verify_snmp_arguments(args) 12 | self._set_snmp_parameters(args) 13 | 14 | def _set_snmp_parameters(self, args): 15 | self.version = args.P 16 | if args.P == "2c": 17 | self.snmp_auth = cmdgen.CommunityData(args.C) 18 | 19 | elif args.P == "3": 20 | self.username = args.U 21 | if args.a == "SHA": 22 | self.integrity = cmdgen.usmHMACSHAAuthProtocol 23 | elif args.a == "MD5": 24 | self.integrity = cmdgen.usmHMACMD5AuthProtocol 25 | 26 | if args.x == "AES": 27 | self.privacy = cmdgen.usmAesCfb128Protocol 28 | elif args.x == "DES": 29 | self.privacy = cmdgen.usmDESPrivProtocol 30 | 31 | self.authkey = args.A 32 | 33 | if args.L == "authPriv": 34 | self.privkey = args.X 35 | self.snmp_auth = cmdgen.UsmUserData( 36 | args.U, 37 | authKey=args.A, 38 | authProtocol=self.integrity, 39 | privKey=args.X, 40 | privProtocol=self.privacy) 41 | else: 42 | self.snmp_auth = cmdgen.UsmUserData( 43 | args.U, 44 | authKey=args.A, 45 | authProtocol=self.integrity) 46 | 47 | self.host = args.H 48 | self.port = int(args.p) 49 | self.timeout = 1 50 | self.retries = 5 51 | 52 | def _verify_snmp_arguments(self, args): 53 | if args.P == "2c" and args.C is None: 54 | ArgumentError('Specify community when using SNMP 2c') 55 | if args.P == "3" and args.U is None: 56 | ArgumentError('Specify username when using SNMP 3') 57 | if args.P == "3" and args.L is None: 58 | ArgumentError('Specify security level when using SNMP 3') 59 | if args.L == "authNoPriv" and args.a is None: 60 | ArgumentError('Specify authentication protocol when using authNoPriv') 61 | if args.L == "authNoPriv" and args.A is None: 62 | ArgumentError('Specify authentication password when using authNoPriv') 63 | if args.L == "authPriv" and args.a is None: 64 | ArgumentError('Specify authentication protocol when using authPriv') 65 | if args.L == "authPriv" and args.A is None: 66 | ArgumentError('Specify authentication password when using authPriv') 67 | if args.L == "authPriv" and args.x is None: 68 | ArgumentError('Specify privacy protocol when using authPriv') 69 | if args.L == "authPriv" and args.X is None: 70 | ArgumentError('Specify privacy password when using authPriv') 71 | -------------------------------------------------------------------------------- /pylama.ini: -------------------------------------------------------------------------------- 1 | [pylama] 2 | linters = mccabe,pep257,pep8,pyflakes 3 | ignore = D203, 4 | skip = .tox/* 5 | 6 | [pylama:pep8] 7 | max_line_length = 110 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """setup.py.""" 2 | import re 3 | 4 | from codecs import open 5 | from setuptools import setup, find_packages 6 | 7 | version = '' 8 | with open('lib/nelkit/__init__.py', 'r') as fd: 9 | version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', 10 | fd.read(), re.MULTILINE).group(1) 11 | 12 | if not version: 13 | raise RuntimeError('Cannot find version information') 14 | 15 | with open('README.rst', 'r', 'utf-8') as f: 16 | readme = f.read() 17 | 18 | with open('HISTORY.rst', 'r', 'utf-8') as f: 19 | history = f.read() 20 | 21 | long_description = readme + '\n\n' + history 22 | 23 | console_scripts = [ 24 | 'nk-compare-configs=nelkit.cli.compare_configs:main', 25 | 'nk-snmp-deviceinfo=nelkit.cli.snmp_deviceinfo:main' 26 | ] 27 | 28 | config = { 29 | 'name': 'nelkit', 30 | 'package_dir': {'': 'lib'}, 31 | 'packages': find_packages('lib'), 32 | 'entry_points': {'console_scripts': console_scripts}, 33 | 'version': version, 34 | 'description': 'A Toolkit for network engineers', 35 | 'long_description': long_description, 36 | 'author': 'Patrick Ogenstad', 37 | 'author_email': 'patrick@ogenstad.com', 38 | 'license': 'Apache', 39 | 'url': 'https://networklore.com/nelkit/', 40 | 'install_requires': ['argparse', 'nelsnmp >= 0.2.7', 'PyYAML'], 41 | 'classifiers': ['Development Status :: 4 - Beta', 42 | 'Intended Audience :: Developers', 43 | 'Intended Audience :: System Administrators'] 44 | } 45 | 46 | setup(**config) 47 | -------------------------------------------------------------------------------- /subscribe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Stay Updated with the Networklore Newsletter 10 | 11 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 219 |

47 |
48 |

The Networklore Newsletter can help you
49 |
50 | 51 |
52 |

53 |

What I have to offer

54 |
    55 |
      56 |
    • 57 |
      The Latest news about the development of Nelkit
      58 |
      59 | 60 |
      61 |
    • 62 |
    • 63 |
      Tips and guides about networking
      64 |
      65 |
      66 |
    • 67 |
    • 68 |
      Information about other tools such as Nelmon
      69 |
      70 |
      71 |
    • 72 | 73 | 74 |
    75 |
76 |

 

77 |

What I want you to do

78 | 79 |
80 | 81 | 82 | 83 | 86 | 87 |
84 |

Just add your name and email address and you'll have instant access to the newsletter. If you like the Nelkit tools, this newletter is the best way to stay up to date.

85 |
88 |
89 | 90 |
91 | 92 | 93 | 140 |
141 |
142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
153 |

 

154 |
155 |
156 | 157 |
158 |
159 |
160 |
161 | 162 |
163 |
164 |
165 | 166 |
167 |
168 |
169 | 191 | 192 |
193 | 194 | 195 | 196 |

What I Guarantee

197 |
    198 |
      199 |
    • 200 |
      I will never sell your email address
      201 | 202 |
      203 |
      204 |
    • 205 |
    • 206 |
      I will never send you any SPAM
      207 |
      208 |
      209 |
    • 210 | 211 |
    • 212 |
      If you decide to unsubscribe there will be a link in every email I send out.
      213 |
      214 |
      215 |
    • 216 |
    217 |
218 |
220 | 221 |

 

222 |
223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 |

 

231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /templates/network-device-template.numbers/Index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networklore/nelkit/17472c4e00e0cf226a2ba48c140d93adcaca024a/templates/network-device-template.numbers/Index.zip -------------------------------------------------------------------------------- /templates/network-device-template.numbers/Metadata/BuildVersionHistory.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Template: Blank (2013-09-27 16:27) 6 | M3.0.1-1483-1 7 | 8 | 9 | -------------------------------------------------------------------------------- /templates/network-device-template.numbers/Metadata/DocumentIdentifier: -------------------------------------------------------------------------------- 1 | 4232BA5E-ACC2-4DA2-B514-F5DB7F502131 -------------------------------------------------------------------------------- /templates/network-device-template.numbers/Metadata/Properties.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networklore/nelkit/17472c4e00e0cf226a2ba48c140d93adcaca024a/templates/network-device-template.numbers/Metadata/Properties.plist -------------------------------------------------------------------------------- /templates/network-device-template.numbers/preview-micro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networklore/nelkit/17472c4e00e0cf226a2ba48c140d93adcaca024a/templates/network-device-template.numbers/preview-micro.jpg -------------------------------------------------------------------------------- /templates/network-device-template.numbers/preview-web.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networklore/nelkit/17472c4e00e0cf226a2ba48c140d93adcaca024a/templates/network-device-template.numbers/preview-web.jpg -------------------------------------------------------------------------------- /templates/network-device-template.numbers/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networklore/nelkit/17472c4e00e0cf226a2ba48c140d93adcaca024a/templates/network-device-template.numbers/preview.jpg -------------------------------------------------------------------------------- /tests/modules/compare_configs/data/base_rules.yml: -------------------------------------------------------------------------------- 1 | configs: 'tests/modules/compare_configs/data/configs/basic' 2 | 3 | rules: 4 | # Match SNMP Server 5 | - match: 6 | description: Match SNMP settings, ignore location 7 | string: '^snmp-server' 8 | exclude: '^snmp-server location' 9 | 10 | - between: 11 | start: '^ip access-list extended ACCESS-IN' 12 | until_not: '^ ' 13 | -------------------------------------------------------------------------------- /tests/modules/compare_configs/data/configs/basic/file_a.ios: -------------------------------------------------------------------------------- 1 | 2 | ip access-list extended ACCESS-IN 3 | remark # Allow ICMP 4 | permit icmp any any echo 5 | permit icmp any any echo-reply 6 | deny icmp any any 7 | remark # Allow HTTP 8 | permit tcp any any eq www 9 | deny ip any any 10 | ! 11 | snmp-server trap-source Loopback0 12 | snmp-server location Stockholm 13 | -------------------------------------------------------------------------------- /tests/modules/compare_configs/data/configs/basic/file_b.ios: -------------------------------------------------------------------------------- 1 | ip access-list extended ACCESS-IN 2 | remark # Allow ICMP 3 | permit icmp any any echo 4 | permit icmp any any echo-reply 5 | deny icmp any any 6 | remark # Allow HTTP 7 | permit tcp any any eq www 8 | ! 9 | snmp-server trap-source Loopback0 10 | snmp-server location Gothenburg 11 | -------------------------------------------------------------------------------- /tests/modules/compare_configs/data/configs/basic/file_c.ios: -------------------------------------------------------------------------------- 1 | ip access-list extended ACCESS-IN 2 | remark # Allow ICMP 3 | permit icmp any any echo 4 | permit icmp any any echo-reply 5 | deny icmp any any 6 | remark # Allow HTTP 7 | permit tcp any any eq www 8 | deny ip any any 9 | ! 10 | snmp-server trap-source Loopback0 11 | snmp-server location Malmo 12 | snmp-server enable traps config 13 | -------------------------------------------------------------------------------- /tests/modules/compare_configs/data/invalid_rule_between_missing_start.yml: -------------------------------------------------------------------------------- 1 | configs: 'tests/modules/compare_configs/data/configs/basic' 2 | 3 | rules: 4 | # Match SNMP Server 5 | - match: 6 | string: '^snmp-server' 7 | exclude: '^snmp-server location' 8 | 9 | - between: 10 | until_not: '^ ' 11 | -------------------------------------------------------------------------------- /tests/modules/compare_configs/test_compare_configs.py: -------------------------------------------------------------------------------- 1 | """Tests for compare module.""" 2 | import pytest 3 | from nelkit.exceptions import NelkitException 4 | from nelkit.modules.compare_configs.settings import CompareConfigs 5 | 6 | 7 | BASE_DIR = 'tests/modules/compare_configs/data/' 8 | BASE_RULES = 'tests/modules/compare_configs/data/base_rules.yml' 9 | 10 | 11 | def test_base_function(): 12 | """Basic test of the compare module, and check that no exceptions are raised.""" 13 | CompareConfigs(settings_file=BASE_RULES) 14 | assert True 15 | 16 | 17 | def test_invalid_config_between_missing_start(): 18 | """Fail when loading a yaml file without a start key.""" 19 | with pytest.raises(NelkitException) as excinfo: 20 | CompareConfigs(settings_file='%s/invalid_rule_between_missing_start.yml' % BASE_DIR) 21 | assert 'start key missing in between rule' == str(excinfo.value) 22 | 23 | 24 | basic_diff = """--- baseline 25 | 26 | +++ file_b.ios 27 | 28 | @@ -5,4 +5,3 @@ 29 | 30 | deny icmp any any 31 | remark # Allow HTTP 32 | permit tcp any any eq www 33 | - deny ip any any""" 34 | 35 | 36 | def test_basic_diff(): 37 | """Basic basic diff of the compare module, and check that no exceptions are raised.""" 38 | run = CompareConfigs(settings_file=BASE_RULES) 39 | assert basic_diff == run._diff['tests/modules/compare_configs/data/configs/basic/file_b.ios'][2] 40 | -------------------------------------------------------------------------------- /tests/parsing/yaml/data/base.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | 3 | - match: 4 | string: '^snmp-server' 5 | exclude: '^snmp-server location' 6 | -------------------------------------------------------------------------------- /tests/parsing/yaml/data/invalid_yaml.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | 3 | - match: 4 | string: '^snmp-server 5 | exclude: '^snmp-server location' 6 | -------------------------------------------------------------------------------- /tests/parsing/yaml/test_parsing_yaml.py: -------------------------------------------------------------------------------- 1 | """Tests to validate yaml features.""" 2 | from nelkit.exceptions import FileNotFound, ParsingError 3 | from nelkit.parsing.yaml.loader import YamlLoader 4 | import pytest 5 | 6 | 7 | def test_YamlLoader(): 8 | """Test to see that the YamlLoader returns a dict.""" 9 | yl = YamlLoader(filename='tests/parsing/yaml/data/base.yml') 10 | assert isinstance(yl.data, dict) 11 | 12 | 13 | def test_load_missing_file(): 14 | """Test to verify that an exception is raised when trying to load a non-existing file.""" 15 | with pytest.raises(FileNotFound) as excinfo: 16 | YamlLoader(filename='tests/parsing/yaml/data/file_that_does_not_exist.yml') 17 | assert 'Unable to read: tests/parsing/yaml/data/file_that_does_not_exist.yml' == str(excinfo.value) 18 | 19 | 20 | def test_load_invalid_file(): 21 | """Test to verify that an exception is raised when the yaml file is invalid.""" 22 | with pytest.raises(ParsingError) as excinfo: 23 | YamlLoader(filename='tests/parsing/yaml/data/invalid_yaml.yml') 24 | assert 'tests/parsing/yaml/data/invalid_yaml.yml is not a valid yaml file' == str(excinfo.value) 25 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py35, py36 3 | [testenv] 4 | deps = 5 | pytest 6 | pytest-cov 7 | commands= 8 | py.test --cov=nelkit --cov-report term-missing 9 | --------------------------------------------------------------------------------