summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornertwork <webmaster@nertwork.com>2016-12-20 10:49:24 -0800
committernertwork <webmaster@nertwork.com>2016-12-20 10:49:24 -0800
commite0621bbb81daab0de9fccc031c3e875031c2b67b (patch)
tree9b527895c0a07d216f67728526b0ed7f82a40d12
parentbcdd40d552cbf5e32dafebf4e531d407eb85bc84 (diff)
parent1466f0f635d1e014ea993179729306d3a9a8d381 (diff)
Merge remote-tracking branch 'upstream/master'
* upstream/master: (109 commits) delete secret password if it is called secret bump up version update changelogs Recursively search from one dir above specified Fix suggested by ytti for issue #610 Remove trailing whitespace and enable prompt detection Update eos.rb exclude time from output New hook: awssns - Publish messages to AWS SNS topics Updated config options Added option to disable ssl verification checks for http source Update ciscosmb.rb Update ciscosmb.rb Update ciscosmb.rb expect prompt after entering enable password add support for PLANET SG switches renamed alvarion -> alvarion.rb This adds support for Hatteras Networks devices This adds support for D-Link switches This adds support for the Casa C1G CMTS ...
-rw-r--r--.gitignore50
-rw-r--r--CHANGELOG.md36
-rw-r--r--Dockerfile2
-rw-r--r--Gemfile.lock14
-rw-r--r--README.md309
-rw-r--r--Rakefile8
-rw-r--r--extra/oxidized.apache214
-rw-r--r--extra/oxidized.nginx14
-rw-r--r--extra/rest_client.rb28
-rw-r--r--lib/oxidized/hook/awssns.rb27
-rw-r--r--lib/oxidized/input/ssh.rb47
-rw-r--r--lib/oxidized/input/tftp.rb41
-rw-r--r--lib/oxidized/model/acos.rb16
-rw-r--r--lib/oxidized/model/alvarion.rb13
-rw-r--r--lib/oxidized/model/aosw.rb23
-rw-r--r--lib/oxidized/model/apc_aos.rb11
-rw-r--r--lib/oxidized/model/asa.rb2
-rw-r--r--lib/oxidized/model/casa.rb46
-rw-r--r--lib/oxidized/model/catos.rb11
-rw-r--r--lib/oxidized/model/ciscosmb.rb9
-rw-r--r--lib/oxidized/model/dlink.rb36
-rw-r--r--lib/oxidized/model/dnos.rb7
-rw-r--r--lib/oxidized/model/eos.rb2
-rw-r--r--lib/oxidized/model/fiberdriver.rb21
-rw-r--r--lib/oxidized/model/fujitsupy.rb42
-rw-r--r--lib/oxidized/model/hatteras.rb52
-rw-r--r--lib/oxidized/model/hpebladesystem.rb83
-rw-r--r--lib/oxidized/model/ios.rb2
-rw-r--r--lib/oxidized/model/ironware.rb12
-rw-r--r--lib/oxidized/model/pfsense.rb16
-rw-r--r--lib/oxidized/model/planet.rb83
-rw-r--r--lib/oxidized/model/powerconnect.rb2
-rw-r--r--lib/oxidized/model/procurve.rb4
-rw-r--r--lib/oxidized/model/routeros.rb3
-rw-r--r--lib/oxidized/model/trango.rb62
-rw-r--r--lib/oxidized/node.rb52
-rw-r--r--lib/oxidized/output/file.rb20
-rw-r--r--lib/oxidized/source/csv.rb10
-rw-r--r--lib/oxidized/source/http.rb9
-rw-r--r--lib/oxidized/source/source.rb12
-rw-r--r--lib/oxidized/source/sql.rb6
-rw-r--r--lib/oxidized/version.rb2
-rw-r--r--oxidized.gemspec2
43 files changed, 1070 insertions, 191 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5e1422c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,50 @@
+*.gem
+*.rbc
+/.config
+/coverage/
+/InstalledFiles
+/pkg/
+/spec/reports/
+/spec/examples.txt
+/test/tmp/
+/test/version_tmp/
+/tmp/
+
+# Used by dotenv library to load environment variables.
+# .env
+
+## Specific to RubyMotion:
+.dat*
+.repl_history
+build/
+*.bridgesupport
+build-iPhoneOS/
+build-iPhoneSimulator/
+
+## Specific to RubyMotion (use of CocoaPods):
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# vendor/Pods/
+
+## Documentation cache and generated files:
+/.yardoc/
+/_yardoc/
+/doc/
+/rdoc/
+
+## Environment normalization:
+/.bundle/
+/vendor/bundle
+/lib/bundler/man/
+
+# for a library or gem, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# Gemfile.lock
+# .ruby-version
+# .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c482508..838d77d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,41 @@
+# 0.19.0
+- FEATURE: allow setting ssh_keys (not relying on openssh config) (@denvera)
+- FEATURE: fujitsupy model (@stokbaek)
+- FEATURE: fiberdriver model (@emjemj)
+- FEATURE: hpbladesystems model (@flokli)
+- FEATURE: planetsgs model (@flokli)
+- FEATURE: trango model (@rfdrake)
+- FEATURE: casa model (@rfdrake)
+- FEATURE: dlink model (@rfdrake)
+- FEATURE: hatteras model (@rfdrake)
+- FEATURE: ability to ignore SSL certs in http (@laf)
+- FEATURE: awsns hooks, publish messages to AWS SNS topics (@natm)
+- BUGFIX: pfsense, dnos, powerconnect, ciscosmb, eos, aosw
+
+# 0.18.0
+- FEATURE: APC model (by @davromaniak )
+- BUGFIX: ironware, aosw
+- BUGFIX: interpolate nil, false, true for node vars too
+
+# 0 17.0
+- FEATURE: "nil", "false" and "true" in source (e.g. router.db) are interpeted as nil, false, true. Empty is now always considered empty string, instead of in some cases nil and some cases empty string.
+- FEATURE: support tftp as input model (@MajesticFalcon)
+- FEATURE: add alvarion model (@MajesticFalcon)
+- FEATURE: detect if ssh wants password terminal/CLI prompt or not
+- FEATURE: node (group, model, username, password) resolution refactoring, supports wider range of use-cases
+- BUGFIX: fetch for file output (@danilopopeye)
+- BUGFIX: net-ssh version specification
+- BUGFIX: routeros, catos, pfsense
+
# 0.16.3
+- FEATURE: pfsense support (by @stokbaek)
- BUGFIX: cumulus prompt not working with default switch configs (by @nertwork)
+- BUGFIX: disconnect ssh when prompt wasn't found (by @andir)
+- BUGFIX: saos, asa, acos, timos updates, cumulus
+
+# 0.16.2
+- BUGFIX: when not using git (by @danilopopeye)
+- BUGFIX: screenos update
# 0.16.2
- BUGFIX: when not using git (by @danilopopeye)
diff --git a/Dockerfile b/Dockerfile
index 9a36e8c..905825e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,7 @@ MAINTAINER Samer Abdel-Hafez <sam@arahant.net>
RUN add-apt-repository ppa:brightbox/ruby-ng && \
apt-get update && \
- apt-get install -y ruby2.1 ruby2.1-dev libsqlite3-dev libssl-dev pkg-config make cmake
+ apt-get install -y ruby2.3 ruby2.3-dev libsqlite3-dev libssl-dev pkg-config make cmake libssh2-1-dev
RUN gem install oxidized oxidized-web --no-ri --no-rdoc
diff --git a/Gemfile.lock b/Gemfile.lock
index 1188627..ab52715 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,9 +1,10 @@
PATH
remote: .
specs:
- oxidized (0.14.3)
+ oxidized (0.18.0)
asetus (~> 0.1)
- net-ssh (>= 3.0.0, < 3.1)
+ net-ssh (~> 3.0.2)
+ net-telnet
rugged (~> 0.21, >= 0.21.4)
slop (~> 3.5)
@@ -14,16 +15,17 @@ GEM
coderay (1.1.0)
metaclass (0.0.4)
method_source (0.8.2)
- minitest (5.8.3)
+ minitest (5.9.0)
mocha (1.1.0)
metaclass (~> 0.0.1)
net-ssh (3.0.2)
+ net-telnet (0.1.1)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
- rake (10.4.2)
- rugged (0.24.0)
+ rake (10.5.0)
+ rugged (0.23.3)
slop (3.6.0)
PLATFORMS
@@ -38,4 +40,4 @@ DEPENDENCIES
rake (~> 10.0)
BUNDLED WITH
- 1.12.4
+ 1.11.2
diff --git a/README.md b/README.md
index 90fd7f8..82c33a5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-# Oxidized [![Build Status](https://travis-ci.org/Shopify/oxidized.svg)](https://travis-ci.org/Shopify/oxidized)
-
-[![Gem Version](https://badge.fury.io/rb/oxidized.svg)](http://badge.fury.io/rb/oxidized)
+# Oxidized [![Build Status](https://travis-ci.org/Shopify/oxidized.svg)](https://travis-ci.org/Shopify/oxidized) [![Gem Version](https://badge.fury.io/rb/oxidized.svg)](http://badge.fury.io/rb/oxidized) [![Join the chat at https://gitter.im/oxidized/Lobby](https://badges.gitter.im/oxidized/Lobby.svg)](https://gitter.im/oxidized/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Oxidized is a network device configuration backup tool. It's a RANCID replacement!
@@ -29,13 +27,16 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
* [Privileged mode](#privileged-mode)
* [Disabling SSH exec channels](#disabling-ssh-exec-channels)
* [Source: CSV](#source-csv)
- * [Source: SQLite](#source-sqlite)
+ * [Source: SQL](#source-sql)
+ * [Source: SQLite](#source-sqlite)
+ * [Source: Mysql](#source-mysql)
* [Source: HTTP](#source-http)
* [Output: GIT](#output-git)
* [Output: HTTP](#output-http)
* [Output: File](#output-file)
* [Output types](#output-types)
* [Advanced Configuration](#advanced-configuration)
+ * [Advanced Group Configuration](#advanced-group-configuration)
7. [Ruby API](#ruby-api)
* [Input](#input)
* [Output](#output)
@@ -43,99 +44,118 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
* [Model](#model)
# Supported OS types
+ * Vendor
+ * OS model
* A10 Networks
- * ACOS
+ * [ACOS](lib/oxidized/model/acos.rb)
* Alcatel-Lucent
- * AOS
- * AOS7
- * ISAM
+ * [AOS](lib/oxidized/model/aos.rb)
+ * [AOS7](lib/oxidized/model/aos7.rb)
+ * [ISAM](lib/oxidized/model/isam.rb)
* Wireless
+ * Alvarion
+ * [BreezeACCESS](lib/oxidized/model/alvarion.rb)
+ * APC
+ * [AOS](lib/oxidized/model/apc_aos.rb)
* Arista
- * EOS
+ * [EOS](lib/oxidized/model/eos.rb)
* Arris
- * C4CMTS
+ * [C4CMTS](lib/oxidized/model/c4cmts.rb)
* Aruba
- * AOSW
+ * [AOSW](lib/oxidized/model/aosw.rb)
* Brocade
- * FabricOS
- * Ironware
- * NOS (Network Operating System)
- * Vyatta
- * 6910
+ * [FabricOS](lib/oxidized/model/fabricos.rb)
+ * [Ironware](lib/oxidized/model/ironware.rb)
+ * [NOS (Network Operating System)](lib/oxidized/model/nos.rb)
+ * [Vyatta](lib/oxidized/model/vyatta.rb)
+ * [6910](lib/oxidized/model/br6910.rb)
+ * Casa
+ * [Casa](lib/oxidized/model/casa.rb)
* Check Point
- * GaiaOS
+ * [GaiaOS](lib/oxidized/model/gaiaos.rb)
* Ciena
- * SOAS
+ * [SOAS](lib/oxidized/model/saos.rb)
* Cisco
- * AireOS
- * ASA
- * CatOS
- * IOS
- * IOSXR
- * NXOS
- * SMB (Nikola series)
+ * [AireOS](lib/oxidized/model/aireos.rb)
+ * [ASA](lib/oxidized/model/asa.rb)
+ * [CatOS](lib/oxidized/model/catos.rb)
+ * [IOS](lib/oxidized/model/ios.rb)
+ * [IOSXR](lib/oxidized/model/iosxr.rb)
+ * [NXOS](lib/oxidized/model/nxos.rb)
+ * [SMB (Nikola series)](lib/oxidized/model/ciscosmb.rb)
* Citrix
- * NetScaler (Virtual Applicance)
+ * [NetScaler (Virtual Applicance)](lib/oxidized/model/netscaler.rb)
* Coriant (former Tellabs)
- * TMOS (8800)
- * 8600
+ * [TMOS (8800)](lib/oxidized/model/corianttmos.rb)
+ * [8600](lib/oxidized/model/coriant8600.rb)
* Cumulus
- * Linux
+ * [Linux](lib/oxidized/model/cumulus.rb)
* DataCom
- * DmSwitch 3000
+ * [DmSwitch 3000](lib/oxidized/model/datacom.rb)
* DELL
- * PowerConnect
- * AOSW
+ * [PowerConnect](lib/oxidized/model/powerconnect.rb)
+ * [AOSW](lib/oxidized/model/aosw.rb)
+ * D-Link
+ * [D-Link](lib/oxidized/model/dlink.rb)
* Ericsson/Redback
- * IPOS (former SEOS)
+ * [IPOS (former SEOS)](lib/oxidized/model/ipos.rb)
* Extreme Networks
- * XOS
- * WM
+ * [XOS](lib/oxidized/model/xos.rb)
+ * [WM](lib/oxidized/model/mtrlrfs.rb)
* F5
- * TMOS
+ * [TMOS](lib/oxidized/model/tmos.rb)
* Force10
- * DNOS
- * FTOS
+ * [DNOS](lib/oxidized/model/dnos.rb)
+ * [FTOS](lib/oxidized/model/ftos.rb)
* FortiGate
- * FortiOS
+ * [FortiOS](lib/oxidized/model/fortios.rb)
+ * Fujitsu
+ * [PRIMERGY Blade switch 1/10Gbe](lib/oxidized/model/fujitsupy.rb)
+ * Hatteras
+ * [Hatteras](lib/oxidized/model/hatteras.rb)
* HP
- * Comware (HP A-series, H3C, 3Com)
- * Procurve
+ * [Comware (HP A-series, H3C, 3Com)](lib/oxidized/model/comware.rb)
+ * [Procurve](lib/oxidized/model/procurve.rb)
+ * [BladeSystem (Onboard Administrator)](lib/oxidized/model/hpebladesystem.rb)
* Huawei
- * VRP
+ * [VRP](lib/oxidized/model/vrp.rb)
* Juniper
- * JunOS
- * ScreenOS (Netscreen)
+ * [JunOS](lib/oxidized/model/junos.rb)
+ * [ScreenOS (Netscreen)](lib/oxidized/model/screenos.rb)
* Mellanox
- * MLNX-OS
+ * [MLNX-OS](lib/oxidized/model/mlnxos.rb)
* Mikrotik
- * RouterOS
+ * [RouterOS](lib/oxidized/model/routeros.rb)
* Motorola
- * RFS
+ * [RFS](lib/oxidized/model/mtrlrfs.rb)
* MRV
- * MasterOS
+ * [MasterOS](lib/oxidized/model/masteros.rb)
+ * [FiberDriver](lib/oxidized/model/fiberdriver.rb)
* Netonix
- * WISP Switch (As Netonix)
+ * [WISP Switch (As Netonix)](lib/oxidized/model/netonix.rb)
* Nokia (formerly TiMetra, Alcatel, Alcatel-Lucent)
- * SR OS (TiMOS)
+ * [SR OS (TiMOS)](lib/oxidized/model/timos.rb)
* Opengear
- * Opengear
+ * [Opengear](lib/oxidized/model/opengear.rb)
* Palo Alto
- * PANOS
- * pfSense
+ * [PANOS](lib/oxidized/model/panos.rb)
+ * [PLANET SG/SGS Switches](lib/oxidized/model/planet.rb)
+ * [pfSense](lib/oxidized/model/pfsense.rb)
* Quanta
- * Quanta / VxWorks 6.6 (1.1.0.8)
+ * [Quanta / VxWorks 6.6 (1.1.0.8)](lib/oxidized/model/quantaos.rb)
* Supermicro
- * Supermicro
+ * [Supermicro](lib/oxidized/model/supermicro.rb)
+ * Trango Systems
+ * [Trango](lib/oxidized/model/trango.rb)
* Ubiquiti
- * AirOS
- * Edgeos
- * EdgeSwitch
+ * [AirOS](lib/oxidized/model/airos.rb)
+ * [Edgeos](lib/oxidized/model/edgeos.rb)
+ * [EdgeSwitch](lib/oxidized/model/edgeswitch.rb)
* Watchguard
- * Fireware OS
+ * [Fireware OS](lib/oxidized/model/firewareos.rb)
* Zyxel
- * ZyNOS
+ * [ZyNOS](lib/oxidized/model/zynos.rb)
# Installation
@@ -149,7 +169,7 @@ gem install oxidized-script oxidized-web # if you don't install oxidized-web, ma
```
## CentOS, Oracle Linux, Red Hat Linux
-On CentOS 6 / RHEL 6, install Ruby 1.9.3 or greater (for Ruby 2.1.2 installation instructions see "Installing Ruby 2.1.2 using RVM"), then install Oxidized dependencies
+On CentOS 6 / RHEL 6, install Ruby greater than 1.9.3 (for Ruby 2.1.2 installation instructions see "Installing Ruby 2.1.2 using RVM"), then install Oxidized dependencies
```shell
yum install cmake sqlite-devel openssl-devel libssh2-devel
```
@@ -256,46 +276,72 @@ rvm use --default 2.1.2
```
# Running with Docker
-1. clone git repo:
+
+clone git repo:
```
- root@bla:~# git clone https://github.com/ytti/oxidized
+git clone https://github.com/ytti/oxidized
+```
+
+build container locally:
+
```
-2. build container locally:
+docker build -q -t oxidized/oxidized:latest oxidized/
```
- root@bla:~# docker build -q -t oxidized/oxidized:latest oxidized/
+
+create config directory in main system:
+
```
-3. create config directory in main system:
+mkdir /etc/oxidized
```
- root@bla~:# mkdir /etc/oxidized
+
+run container the first time:
+_Note: this step in only needed for creating Oxidized's configuration file and can be skipped if you already have it
+
```
-4. run container the first time:
+docker run --rm -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
```
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
+If the RESTful API and Web Interface are enabled, on the docker host running the container
+edit /etc/oxidized/config and modify 'rest: 127.0.0.1:8888' by 'rest: 0.0.0.0:8888'
+this will bind port 8888 to all interfaces then expose port out. (Issue #445)
+
+You can also use docker-compose to launch oxidized container:
```
-5. add 'router.db' to /etc/oxidized:
+# docker-compose.yml
+# docker-compose file example for oxidized that will start along with docker daemon
+oxidized:
+ restart: always
+ image: oxidized/oxidized:latest
+ ports:
+ - 8888:8888/tcp
+ environment:
+ CONFIG_RELOAD_INTERVAL: 600
+ volumes:
+ - /etc/oxidized:/root/.config/oxidized
```
- root@bla:~# vim /etc/oxidized/router.db
- [ ... ]
- root@bla:~#
+
+create the `/etc/oxidized/router.db`
+
```
-6. run container again:
+vim /etc/oxidized/router.db
```
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest
- oxidized[1]: Oxidized starting, running as pid 1
- oxidized[1]: Loaded 1 nodes
- Puma 2.13.4 starting...
- * Min threads: 0, max threads: 16
- * Environment: development
- * Listening on tcp://0.0.0.0:8888
- ^C
- root@bla:~#
+run container again:
+
+```
+docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest
+oxidized[1]: Oxidized starting, running as pid 1
+oxidized[1]: Loaded 1 nodes
+Puma 2.13.4 starting...
+* Min threads: 0, max threads: 16
+* Environment: development
+* Listening on tcp://0.0.0.0:8888
```
If you want to have the config automatically reloaded (e.g. when using a http source that changes)
+
```
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -e CONFIG_RELOAD_INTERVAL=3600 -t oxidized/oxidized:latest
+docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -e CONFIG_RELOAD_INTERVAL=3600 -t oxidized/oxidized:latest
```
## Cookbook
@@ -334,10 +380,10 @@ Device models can contain substitution filters to remove potentially sensitive d
As a partial example from ios.rb:
-```
+```
cmd :secret do |cfg|
cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
- (...)
+ (...)
cfg
end
```
@@ -388,6 +434,31 @@ vars_map:
ssh_proxy: 3
...
```
+### Source: SQL
+ Oxidized uses the `sequel` ruby gem. You can use a variety of databases that aren't explicitly listed. For more information visit https://github.com/jeremyevans/sequel Make sure you have the correct adapter!
+### Source: MYSQL
+
+```sudo apt-get install libmysqlclient-dev```
+
+The values correspond to your fields in the DB such that ip, model, etc are field names in the DB
+
+```
+source:
+ default: sql
+ sql:
+ adapter: mysql2
+ database: oxidized
+ table: nodes
+ username: root
+ password: rootpass
+ map:
+ name: ip
+ model: model
+ username: username
+ password: password
+ vars_map:
+ enable: enable
+```
### Source: SQLite
@@ -435,6 +506,17 @@ source:
X-Auth-Token: 'somerandomstring'
```
+You can also pass `secure: false` if you want to disable ssl certificate verification:
+
+```
+source:
+ default: http
+ http:
+ url: https://url/api
+ scheme: https
+ secure: false
+```
+
### Output: File
Parent directory needs to be created manually, one file per device, with most recent running config.
@@ -574,7 +656,7 @@ rest: 10.0.0.1:8000/oxidized
### Advanced Configuration
-Below is an advanced example configuration. You will be able to (optinally) override options per device. The router.db format used is ```hostname:model:username:password:enable_password```. Hostname and model will be the only required options, all others override the global configuration sections.
+Below is an advanced example configuration. You will be able to (optionally) override options per device. The router.db format used is ```hostname:model:username:password:enable_password```. Hostname and model will be the only required options, all others override the global configuration sections.
```
---
@@ -619,6 +701,28 @@ source:
model_map:
cisco: ios
juniper: junos
+
+```
+
+### Advanced Group Configuration
+
+For group specific credentials
+
+```
+groups:
+ mikrotik:
+ username: admin
+ password: blank
+ ubiquiti:
+ username: ubnt
+ password: ubnt
+```
+and add group mapping
+```
+map:
+ model: 0
+ name: 1
+ group: 2
```
# Hooks
@@ -711,6 +815,35 @@ hooks:
password: pass
```
+## Hook type: awssns
+
+The `awssns` hook publishes messages to AWS SNS topics. This allows you to notify other systems of device configuration changes, for example a config orchestration pipeline. Multiple services can subscribe to the same AWS topic.
+
+Fields sent in the message:
+
+ * `event`: Event type (e.g. `node_success`)
+ * `group`: Group name
+ * `model`: Model name (e.g. `eos`)
+ * `node`: Device hostname
+
+Configuration example:
+
+``` yaml
+hooks:
+ hook_script:
+ type: awssns
+ events: [node_fail,node_success,post_store]
+ region: us-east-1
+ topic_arn: arn:aws:sns:us-east-1:1234567:oxidized-test-backup_events
+```
+
+AWS SNS hook requires the following configuration keys:
+
+ * `region`: AWS Region name
+ * `topic_arn`: ASN Topic reference
+
+Your AWS credentials should be stored in `~/.aws/credentials`.
+
# Ruby API
The following objects exist in Oxidized.
@@ -718,7 +851,7 @@ The following objects exist in Oxidized.
## Input
* gets config from nodes
* must implement 'connect', 'get', 'cmd'
- * 'ssh' and 'telnet' implemented
+ * 'ssh', 'telnet, ftp, and tftp' implemented
## Output
* stores config
diff --git a/Rakefile b/Rakefile
index 11f5c96..de6ba82 100644
--- a/Rakefile
+++ b/Rakefile
@@ -19,10 +19,10 @@ task :test do
end
end
-desc 'Install gem'
-task :install => :build do
- system "sudo -Es sh -c \'umask 022; gem install gems/#{file}\'"
-end
+## desc 'Install gem'
+## task :install => :build do
+## system "sudo -Es sh -c \'umask 022; gem install gems/#{file}\'"
+## end
desc 'Remove gems'
task :clean do
diff --git a/extra/oxidized.apache2 b/extra/oxidized.apache2
new file mode 100644
index 0000000..0ab372b
--- /dev/null
+++ b/extra/oxidized.apache2
@@ -0,0 +1,14 @@
+<VirtualHost *:80>
+ # Place in sites-available
+
+ ServerAdmin admin@example.com
+ ServerName oxidized.example.com
+ ServerAlias oxidized
+
+ ProxyPass / http://127.0.0.1:8888/
+ ProxyPassReverse / http://127.0.0.1:8888/
+
+ ErrorLog /var/log/apache2/oxidized_error.log
+ CustomLog /var/log/apache2/oxidized_access.log combined
+
+</VirtualHost>
diff --git a/extra/oxidized.nginx b/extra/oxidized.nginx
new file mode 100644
index 0000000..06a4768
--- /dev/null
+++ b/extra/oxidized.nginx
@@ -0,0 +1,14 @@
+server {
+ listen 80;
+ listen [::]:80;
+
+ server_name oxidized.example.com;
+
+ location / {
+ proxy_pass http://127.0.0.1:8888/;
+ }
+
+ access_log /var/log/nginx/access_oxidized.log;
+ error_log /var/log/nginx/error_oxidized.log;
+}
+
diff --git a/extra/rest_client.rb b/extra/rest_client.rb
index a16bd42..35d93ae 100644
--- a/extra/rest_client.rb
+++ b/extra/rest_client.rb
@@ -2,8 +2,30 @@ module Oxidized
class RestClient
require 'net/http'
require 'json'
- HOST = 'localhost'
- PORT = 8888
+ require 'uri'
+ require 'asetus'
+
+ class Config
+ Root = Root = ENV['OXIDIZED_HOME'] || File.join(ENV['HOME'], '.config', 'oxidized')
+ end
+
+ CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
+ CFGS.default.rest = '127.0.0.1:8888'
+
+ begin
+ CFGS.load
+ rescue => error
+ raise InvalidConfig, "Error loading config: #{error.message}"
+ end
+
+ restcfg = CFGS.cfg.rest
+ unless restcfg.match(/^http:\/\//)
+ restcfg.insert(0, 'http://')
+ end
+
+ HOST = URI(restcfg).host
+ PORT = URI(restcfg).port
+ PATH = URI(restcfg).path
class << self
def next opt={}, host=HOST, port=PORT
@@ -18,7 +40,7 @@ module Oxidized
def next opt
data = JSON.dump opt
- @web.put '/node/next/' + opt[:name].to_s, data
+ @web.put PATH + '/node/next/' + opt[:name].to_s, data
end
end
diff --git a/lib/oxidized/hook/awssns.rb b/lib/oxidized/hook/awssns.rb
new file mode 100644
index 0000000..dbc2d47
--- /dev/null
+++ b/lib/oxidized/hook/awssns.rb
@@ -0,0 +1,27 @@
+require 'aws-sdk'
+
+class AwsSns < Oxidized::Hook
+ def validate_cfg!
+ raise KeyError, 'hook.region is required' unless cfg.has_key?('region')
+ raise KeyError, 'hook.topic_arn is required' unless cfg.has_key?('topic_arn')
+ end
+
+ def run_hook(ctx)
+ sns = Aws::SNS::Resource.new(region: cfg.region)
+ topic = sns.topic(cfg.topic_arn)
+ message = {
+ :event => ctx.event.to_s
+ }
+ if ctx.node
+ message.merge!(
+ :group => ctx.node.group.to_s,
+ :model => ctx.node.model.class.name.to_s.downcase,
+ :node => ctx.node.name.to_s
+ )
+ end
+ topic.publish({
+ message: message.to_json
+ })
+ end
+
+end
diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb
index cd12167..9a5c508 100644
--- a/lib/oxidized/input/ssh.rb
+++ b/lib/oxidized/input/ssh.rb
@@ -17,8 +17,9 @@ module Oxidized
class NoShell < OxidizedError; end
def connect node
- @node = node
- @output = ''
+ @node = node
+ @output = ''
+ @pty_options = { term: "vt100" }
@node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
secure = Oxidized.config.input.ssh.secure
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?
@@ -32,9 +33,10 @@ module Oxidized
:paranoid => secure,
:auth_methods => %w(none publickey password keyboard-interactive),
:number_of_password_prompts => 0,
- :proxy => proxy
+ :proxy => proxy,
}
- ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
+ ssh_opts[:keys] = vars(:ssh_keys).is_a?(Array) ? vars(:ssh_keys) : [vars(:ssh_keys)] if vars(:ssh_keys)
+ ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
Oxidized.logger.debug "lib/oxidized/input/ssh.rb: Connecting to #{@node.name}"
@@ -42,7 +44,7 @@ module Oxidized
unless @exec
shell_open @ssh
begin
- @username ? shell_login : expect(@node.prompt)
+ login
rescue Timeout::Error
raise PromptUndetect, [ @output, 'not matching configured prompt', @node.prompt ].join(' ')
end
@@ -71,6 +73,10 @@ module Oxidized
@output
end
+ def pty_options hash
+ @pty_options = @pty_options.merge hash
+ end
+
private
def disconnect
@@ -93,7 +99,7 @@ module Oxidized
@output << data
@output = @node.model.expects @output
end
- ch.request_pty (_opts={:term=>'vt100'}) do |_ch, success_pty|
+ ch.request_pty (@pty_options) do |_ch, success_pty|
raise NoShell, "Can't get PTY" unless success_pty
ch.send_channel_request 'shell' do |_ch, success_shell|
raise NoShell, "Can't get shell" unless success_shell
@@ -102,13 +108,18 @@ module Oxidized
end
end
- # Cisco WCS has extremely dubious SSH implementation, SSH auth is always
- # success, it always opens shell and then run auth in shell. I guess
- # they'll never support exec() :)
- def shell_login
- expect username
- cmd @node.auth[:username], password
- cmd @node.auth[:password]
+ # some models have SSH auth or terminal auth based on version of code
+ # if SSH is configured for terminal auth, we'll still try to detect prompt
+ def login
+ if @username
+ match = expect username, @node.prompt
+ if match == username
+ cmd @node.auth[:username], password
+ cmd @node.auth[:password]
+ end
+ else
+ expect @node.prompt
+ end
end
def exec state=nil
@@ -123,14 +134,18 @@ module Oxidized
@output
end
- def expect regexp
- Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexp.inspect} at #{node.name}"
+ def expect *regexps
+ regexps = [regexps].flatten
+ Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexps.inspect} at #{node.name}"
Timeout::timeout(Oxidized.config.timeout) do
@ssh.loop(0.1) do
sleep 0.1
- not @output.match regexp
+ match = regexps.find { |regexp| @output.match regexp }
+ return match if match
+ true
end
end
end
+
end
end
diff --git a/lib/oxidized/input/tftp.rb b/lib/oxidized/input/tftp.rb
new file mode 100644
index 0000000..78164d0
--- /dev/null
+++ b/lib/oxidized/input/tftp.rb
@@ -0,0 +1,41 @@
+module Oxidized
+ require 'stringio'
+ require_relative 'cli'
+
+ begin
+ require 'net/tftp'
+ rescue LoadError
+ raise OxidizedError, 'net/tftp not found: sudo gem install net-tftp'
+ end
+
+ class TFTP < Input
+
+ include Input::CLI
+
+ # TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data.
+ def connect node
+ @node = node
+
+ @node.model.cfg['tftp'].each { |cb| instance_exec(&cb) }
+ @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug?
+ @tftp = Net::TFTP.new @node.ip
+ end
+
+ def cmd file
+ Oxidized.logger.debug "TFTP: #{file} @ #{@node.name}"
+ config = StringIO.new
+ @tftp.getbinary file, config
+ config.rewind
+ config.read
+ end
+
+ private
+
+ def disconnect
+ # TFTP uses UDP, there is no connection to close
+ ensure
+ @log.close if Oxidized.config.input.debug?
+ end
+
+ end
+end
diff --git a/lib/oxidized/model/acos.rb b/lib/oxidized/model/acos.rb
index bb9846e..47649a2 100644
--- a/lib/oxidized/model/acos.rb
+++ b/lib/oxidized/model/acos.rb
@@ -6,6 +6,13 @@ class ACOS < Oxidized::Model
##ACOS prompt changes depending on the state of the device
prompt /^([-\w.\/:?\[\]\(\)]+[#>]\s?)$/
+ cmd :secret do |cfg|
+ cfg.gsub!(/community read encrypted (\S+)/, 'community read encrypted <hidden>') # snmp
+ cfg.gsub!(/secret encrypted (\S+)/, 'secret encrypted <hidden>') # tacacs-server
+ cfg.gsub!(/password encrypted (\S+)/, 'password encrypted <hidden>') # user
+ cfg
+ end
+
cmd 'show version' do |cfg|
cfg.gsub! /\s(Last configuration saved at).*/, ' \\1 <removed>'
cfg.gsub! /\s(Memory).*/, ' \\1 <removed>'
@@ -22,11 +29,20 @@ class ACOS < Oxidized::Model
comment cfg
end
+ cmd 'show partition-config all' do |cfg|
+ cfg.gsub! /(Current configuration).*/, '\\1 <removed>'
+ cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>'
+ cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>'
+ cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>'
+ cfg
+ end
+
cmd 'show running-config all-partitions' do |cfg|
cfg.gsub! /(Current configuration).*/, '\\1 <removed>'
cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>'
cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>'
cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>'
+ cfg
end
cmd 'show aflex all-partitions' do |cfg|
diff --git a/lib/oxidized/model/alvarion.rb b/lib/oxidized/model/alvarion.rb
new file mode 100644
index 0000000..3c762de
--- /dev/null
+++ b/lib/oxidized/model/alvarion.rb
@@ -0,0 +1,13 @@
+class Alvarion < Oxidized::Model
+
+ # Used in Alvarion wisp equipment
+
+ # Run this command as an instance of Model so we can access node
+ pre do
+ cmd "#{node.auth[:password]}.cfg"
+ end
+
+
+ cfg :tftp {}
+
+end
diff --git a/lib/oxidized/model/aosw.rb b/lib/oxidized/model/aosw.rb
index 394561f..11d8442 100644
--- a/lib/oxidized/model/aosw.rb
+++ b/lib/oxidized/model/aosw.rb
@@ -28,19 +28,21 @@ class AOSW < Oxidized::Model
cmd 'show version' do |cfg|
cfg = cfg.each_line.select { |line| not line.match /Switch uptime/i }
- comment cfg.join
+ rstrip_cfg comment cfg.join
end
cmd 'show inventory' do |cfg|
- clean cfg
+ rstrip_cfg clean cfg
end
cmd 'show slots' do |cfg|
- comment cfg
+ rstrip_cfg comment cfg
end
+
cmd 'show license' do |cfg|
- comment cfg
+ rstrip_cfg comment cfg
end
+
cmd 'show running-config' do |cfg|
out = []
cfg.each_line do |line|
@@ -60,8 +62,8 @@ class AOSW < Oxidized::Model
cfg :telnet, :ssh do
if vars :enable
post_login do
- send 'enable\n'
- send vars(:enable) + '\n'
+ send "enable\n"
+ cmd vars(:enable)
end
end
post_login 'no paging'
@@ -72,6 +74,15 @@ class AOSW < Oxidized::Model
pre_logout 'exit'
end
+ def rstrip_cfg cfg
+ out = []
+ cfg.each_line do |line|
+ out << line.rstrip
+ end
+ out = out.join "\n"
+ out << "\n"
+ end
+
def clean cfg
out = []
cfg.each_line do |line|
diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb
new file mode 100644
index 0000000..530d436
--- /dev/null
+++ b/lib/oxidized/model/apc_aos.rb
@@ -0,0 +1,11 @@
+class Apc_aos < Oxidized::Model
+
+ cmd 'config.ini' do |cfg|
+ cfg.gsub! /^; Configuration file\, generated on.*/, ''
+ end
+
+ cfg :ftp do
+ end
+
+end
+
diff --git a/lib/oxidized/model/asa.rb b/lib/oxidized/model/asa.rb
index a41348e..df30059 100644
--- a/lib/oxidized/model/asa.rb
+++ b/lib/oxidized/model/asa.rb
@@ -15,7 +15,7 @@ class ASA < Oxidized::Model
cfg.gsub! /username (\S+) password (\S+) (.*)/, 'username \1 password <secret hidden> \3'
cfg.gsub! /ikev2 pre-shared-key (\S+)/, 'ikev2 pre-shared-key <secret hidden>'
cfg.gsub! /ikev2 (remote|local)-authentication pre-shared-key (\S+)/, 'ikev2 \1-authentication pre-shared-key <secret hidden>'
- cfg.gsub! /^(aaa-server TACACS\+ \(\S+\) host.*\n\skey) \S+$/m, '\1 <secret hidden>'
+ cfg.gsub! /^(aaa-server TACACS\+? \(\S+\) host.*\n\skey) \S+$/mi, '\1 <secret hidden>'
cfg
end
diff --git a/lib/oxidized/model/casa.rb b/lib/oxidized/model/casa.rb
new file mode 100644
index 0000000..e85c904
--- /dev/null
+++ b/lib/oxidized/model/casa.rb
@@ -0,0 +1,46 @@
+class Casa < Oxidized::Model
+ # Casa Systems CMTS
+
+ prompt /^([\w.@()-]+[#>]\s?)$/
+ comment '! '
+
+ cmd :secret do |cfg|
+ cfg.gsub! /^(snmp community) \S+/, '\\1 <configuration removed>'
+ cfg.gsub! /^(snmp comm-tbl) \S+ \S+/, '\\1 <removed> <removed>'
+ cfg.gsub! /^(console-password encrypted) \S+/, '\\1 <secret hidden>'
+ cfg.gsub! /^(password encrypted) \S+/, '\\1 <secret hidden>'
+ cfg.gsub! /^(tacacs-server key) \S+/, '\\1 <secret hidden>'
+ cfg
+ end
+
+ cmd :all do |cfg|
+ cfg.each_line.to_a[1..-2].join
+ end
+
+ cmd 'show system' do |cfg|
+ comment cfg.each_line.reject { |line| line.match /^\s+System (Time|Uptime): / }.join
+ end
+
+ cmd 'show version' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show run'
+
+ cfg :telnet do
+ username /^Username:/
+ password /^Password:/
+ end
+
+ cfg :telnet, :ssh do
+ post_login 'page-off'
+ # preferred way to handle additional passwords
+ if vars :enable
+ post_login do
+ send "enable\n"
+ cmd vars(:enable)
+ end
+ end
+ pre_logout 'logout'
+ end
+end
diff --git a/lib/oxidized/model/catos.rb b/lib/oxidized/model/catos.rb
index 874ebbc..bac9eec 100644
--- a/lib/oxidized/model/catos.rb
+++ b/lib/oxidized/model/catos.rb
@@ -1,6 +1,6 @@
class Catos < Oxidized::Model
- prompt /^[\w.@-]+> \(enable\) $/
+ prompt /^[\w.@-]+>\s?(\(enable\) )?$/
comment '# '
cmd :all do |cfg|
@@ -28,8 +28,15 @@ class Catos < Oxidized::Model
password /^Password:/
end
- cfg :ssh, :telnet do
+ cfg :telnet, :ssh do
post_login 'set length 0'
+ # preferred way to handle additional passwords
+ if vars :enable
+ post_login do
+ send "enable\n"
+ cmd vars(:enable)
+ end
+ end
pre_logout 'exit'
end
diff --git a/lib/oxidized/model/ciscosmb.rb b/lib/oxidized/model/ciscosmb.rb
index 3ef9a85..e5501d5 100644
--- a/lib/oxidized/model/ciscosmb.rb
+++ b/lib/oxidized/model/ciscosmb.rb
@@ -33,14 +33,13 @@ class CiscoSMB < Oxidized::Model
cfg
end
- cfg :telnet do
- username /^User Name:/
- password /^\r?Password:$/
- end
-
cfg :telnet, :ssh do
+ username /^User ?[nN]ame:/
+ password /^\r?Password:$/
post_login 'terminal datadump' # Disable pager
post_login 'terminal width 0'
+ post_login 'terminal len 0'
+ pre_logout 'exit' #exit returns to previous priv level, no way to quit from exec(#)
pre_logout 'exit'
end
diff --git a/lib/oxidized/model/dlink.rb b/lib/oxidized/model/dlink.rb
new file mode 100644
index 0000000..5756bad
--- /dev/null
+++ b/lib/oxidized/model/dlink.rb
@@ -0,0 +1,36 @@
+class Dlink < Oxidized::Model
+ # D-LINK Switches
+
+ prompt /^(\r*[\w.@():-]+[#>]\s?)$/
+ comment '# '
+
+ cmd :secret do |cfg|
+ cfg.gsub! /^(create snmp community) \S+/, '\\1 <removed>'
+ cfg.gsub! /^(create snmp group) \S+/, '\\1 <removed>'
+ cfg
+ end
+
+ cmd :all do |cfg|
+ cfg.each_line.to_a[2..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n"
+ end
+
+ cmd 'show switch' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show vlan' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show config current'
+
+ cfg :telnet do
+ username /\r*username:/
+ password /\r*password:/
+ end
+
+ cfg :telnet, :ssh do
+ post_login 'disable clipaging'
+ pre_logout 'logout'
+ end
+end
diff --git a/lib/oxidized/model/dnos.rb b/lib/oxidized/model/dnos.rb
index 1c31aad..a44630e 100644
--- a/lib/oxidized/model/dnos.rb
+++ b/lib/oxidized/model/dnos.rb
@@ -33,15 +33,16 @@ class DNOS < Oxidized::Model
end
cfg :telnet, :ssh do
- post_login 'terminal length 0'
- post_login 'terminal width 0'
if vars :enable
post_login do
send "enable\n"
- send vars(:enable) + "\n"
+ cmd vars(:enable)
end
end
+ post_login 'terminal length 0'
+ post_login 'terminal width 0'
pre_logout 'exit'
+ pre_logout 'exit'
end
end
diff --git a/lib/oxidized/model/eos.rb b/lib/oxidized/model/eos.rb
index 75da0fa..a9f3ff3 100644
--- a/lib/oxidized/model/eos.rb
+++ b/lib/oxidized/model/eos.rb
@@ -22,7 +22,7 @@ class EOS < Oxidized::Model
comment cfg
end
- cmd 'show running-config | no-more' do |cfg|
+ cmd 'show running-config | no-more | exclude ! Time:' do |cfg|
cfg
end
diff --git a/lib/oxidized/model/fiberdriver.rb b/lib/oxidized/model/fiberdriver.rb
new file mode 100644
index 0000000..8f8eb07
--- /dev/null
+++ b/lib/oxidized/model/fiberdriver.rb
@@ -0,0 +1,21 @@
+class FiberDriver < Oxidized::Model
+ prompt /\w+#/
+ comment "! "
+
+ cmd :all do |cfg|
+ cfg.each_line.to_a[1..-2].join
+ end
+ cmd 'show inventory' do |cfg|
+ comment cfg
+ end
+
+ cmd "show running-config" do |cfg|
+ cfg.each_line.to_a[3..-1].join
+ end
+
+ cfg :ssh do
+ post_login 'terminal length 0'
+ post_login 'terminal width 512'
+ pre_logout 'exit'
+ end
+end
diff --git a/lib/oxidized/model/fujitsupy.rb b/lib/oxidized/model/fujitsupy.rb
new file mode 100644
index 0000000..20a78dd
--- /dev/null
+++ b/lib/oxidized/model/fujitsupy.rb
@@ -0,0 +1,42 @@
+class FujitsuPY < Oxidized::Model
+
+ prompt /^(\([\w.-]*\)\s#|^\S+\#\s)$/
+ comment '! '
+
+ cmd :all do |cfg|
+ cfg.each_line.to_a[1..-2].join
+ end
+
+# 1Gbe switch
+ cmd 'show version' do |cfg|
+ cfg.gsub! /^(<ERROR> : 2 : format error)$/, ''
+ comment cfg
+ end
+
+# 10Gbe switch
+ cmd 'show system information' do |cfg|
+ cfg.gsub! /^Current-time : [\w\s:]*$/, ''
+ cfg.gsub! /^(\s{33}\^)$/, ''
+ cfg.gsub! /^(\% Invalid input detected at '\^' marker.)$/, ''
+ comment cfg
+ end
+
+ cmd 'show running-config' do |cfg|
+ cfg
+ end
+
+ cfg :telnet do
+ username /^Username:/
+ password /^Password:/
+ end
+
+ cfg :telnet, :ssh do
+ post_login 'no pager'
+ post_login 'terminal pager disable'
+ pre_logout do
+ send "quit\n"
+ send "n\n"
+ end
+ end
+
+end
diff --git a/lib/oxidized/model/hatteras.rb b/lib/oxidized/model/hatteras.rb
new file mode 100644
index 0000000..4192cbc
--- /dev/null
+++ b/lib/oxidized/model/hatteras.rb
@@ -0,0 +1,52 @@
+class Hatteras < Oxidized::Model
+ # Hatteras Networks
+
+ prompt /^(\r?[\w.@()-]+[#>]\s?)$/
+ comment '# '
+
+ expect /WARNING: System configuration changes will be lost when the device restarts./ do |data, re|
+ send "y\r"
+ data.sub re, ''
+ end
+
+
+ cmd :secret do |cfg|
+ cfg.gsub! /^(community) \S+/, '\\1 "<configuration removed>"'
+ cfg.gsub! /^(communityString) "\S+"/, '\\1 "<configuration removed>"'
+ cfg.gsub! /^(key) "\S+"/, '\\1 "<secret hidden>"'
+ cfg
+ end
+
+ cmd :all do |cfg|
+ cfg.each_line.to_a[1..-2].join
+ end
+
+ cmd "show switch\r" do |cfg|
+ cfg = cfg.each_line.reject { |line| line.match /Switch uptime|Switch temperature|Last reset reason/ or
+ line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join
+ comment cfg
+ end
+
+ cmd "show card\r" do |cfg|
+ cfg = cfg.each_line.reject { |line| line.match /Card uptime|Card temperature|Last reset reason/ or
+ line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join
+ comment cfg
+ end
+
+ cmd "show sfp *\r" do |cfg|
+ comment cfg
+ end
+
+ cmd "show config run\r" do |cfg|
+ cfg
+ end
+
+ cfg :telnet do
+ username /^Login:/
+ password /^Password:/
+ end
+
+ cfg :telnet, :ssh do
+ pre_logout "logout\r"
+ end
+end
diff --git a/lib/oxidized/model/hpebladesystem.rb b/lib/oxidized/model/hpebladesystem.rb
new file mode 100644
index 0000000..5e34de8
--- /dev/null
+++ b/lib/oxidized/model/hpebladesystem.rb
@@ -0,0 +1,83 @@
+class HPEBladeSystem < Oxidized::Model
+ # HPE Onboard Administrator
+
+ prompt /.*> /
+ comment '# '
+
+ expect /^\s*--More--\s+.*$/ do |data, re|
+ send ' '
+ data.sub re, ''
+ end
+
+ cmd :all do |cfg|
+ cfg = cfg.delete("\r").each_line.to_a[0..-1].map{|line|line.rstrip}.join("\n") + "\n"
+ cfg.each_line.to_a[0..-2].join
+ end
+
+ cmd :secret do |cfg|
+ cfg.gsub! /^(SET SNMP COMMUNITY (READ|WRITE)).*/, '\\1 <configuration removed>'
+ cfg
+ end
+
+ cmd 'show oa info' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show oa network' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show oa certificate' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show sshfingerprint' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show fru' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show network' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show vlan' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show rack name' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show server list' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show server names' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show server port map all' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show server info all' do |cfg|
+ comment cfg
+ end
+
+ cmd 'show config' do |cfg|
+ cfg.gsub! /^#(Generated on:) .*$/, '\\1 <removed>'
+ cfg.gsub /^\s+/, ''
+ end
+
+ cfg :telnet do
+ username /\slogin:/
+ password /^Password: /
+ end
+
+ cfg :telnet, :ssh do
+ pre_logout "exit"
+ end
+end
diff --git a/lib/oxidized/model/ios.rb b/lib/oxidized/model/ios.rb
index 1f099c8..3cbe0f0 100644
--- a/lib/oxidized/model/ios.rb
+++ b/lib/oxidized/model/ios.rb
@@ -26,7 +26,9 @@ class IOS < Oxidized::Model
cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>'
cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>'
+ cfg.gsub! /^username \S+ secret \d \S+/, '<secret hidden>'
cfg.gsub! /^enable password \d \S+/, '<secret hidden>'
+ cfg.gsub! /^enable secret \d \S+/, '<secret hidden>'
cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>'
cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>'
cfg
diff --git a/lib/oxidized/model/ironware.rb b/lib/oxidized/model/ironware.rb
index 1e8c30e..db341d1 100644
--- a/lib/oxidized/model/ironware.rb
+++ b/lib/oxidized/model/ironware.rb
@@ -2,14 +2,14 @@ class IronWare < Oxidized::Model
prompt /^.*(telnet|ssh)\@.+[>#]\s?$/i
comment '! '
-
+
#to handle pager without enable
#expect /^((.*)--More--(.*))$/ do |data, re|
# send ' '
# data.sub re, ''
#end
-
+
#to remove backspace (if handle pager without enable)
#expect /^((.*)[\b](.*))$/ do |data, re|
# data.sub re, ''
@@ -44,14 +44,14 @@ class IronWare < Oxidized::Model
out << sc.rest
cfg = out
end
-
+
comment cfg
end
-
+
cmd 'show flash' do |cfg|
comment cfg
end
-
+
cmd 'show module' do |cfg|
cfg.gsub! /^((Invalid input)|(Type \?)).*$/, '' # some ironware devices are fixed config
comment cfg
@@ -74,7 +74,7 @@ class IronWare < Oxidized::Model
if vars :enable
post_login do
send "enable\r\n"
- send vars(:enable) + "\r\n"
+ cmd vars(:enable)
end
end
post_login ''
diff --git a/lib/oxidized/model/pfsense.rb b/lib/oxidized/model/pfsense.rb
index cd6885c..c02c0d0 100644
--- a/lib/oxidized/model/pfsense.rb
+++ b/lib/oxidized/model/pfsense.rb
@@ -1,20 +1,14 @@
class PfSense < Oxidized::Model
-
- comment '# '
-
- #add a comment in the final conf
- def add_comment comment
- "\n###### #{comment} ######\n"
- end
+ # use other use than 'admin' user, 'admin' user cannot get ssh/exec. See issue #535
+
cmd :all do |cfg|
cfg.each_line.to_a[1..-2].join
end
- #show the persistent configuration
- pre do
- cfg = add_comment 'Configuration'
- cfg += cmd 'cat /cf/conf/config.xml'
+ cmd 'cat /cf/conf/config.xml' do |cfg|
+ cfg.gsub! /\s<revision>\s*.*\s*<time>\d*<\/time>\s*.*\s*<\/revision>/, ''
+ cfg
end
cfg :ssh do
diff --git a/lib/oxidized/model/planet.rb b/lib/oxidized/model/planet.rb
new file mode 100644
index 0000000..05a369a
--- /dev/null
+++ b/lib/oxidized/model/planet.rb
@@ -0,0 +1,83 @@
+class Planet < Oxidized::Model
+
+ prompt /^\r?([\w.@()-]+[#>]\s?)$/
+ comment '! '
+
+ # example how to handle pager
+ #expect /^\s--More--\s+.*$/ do |data, re|
+ # send ' '
+ # data.sub re, ''
+ #end
+
+ # non-preferred way to handle additional PW prompt
+ #expect /^[\w.]+>$/ do |data|
+ # send "enable\n"
+ # send vars(:enable) + "\n"
+ # data
+ #end
+
+ cmd :all do |cfg|
+ #cfg.gsub! /\cH+\s{8}/, '' # example how to handle pager
+ #cfg.gsub! /\cH+/, '' # example how to handle pager
+ cfg.each_line.to_a[1..-2].join
+ end
+
+ cmd :secret do |cfg|
+ cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
+ cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>'
+ cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>'
+ cfg.gsub! /^enable password \d \S+/, '<secret hidden>'
+ cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>'
+ cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>'
+ cfg
+ end
+
+ cmd 'show version' do |cfg|
+ cfg.gsub! "\n\r", "\n"
+ @planetgs = true if cfg.match /^System Name\w*:\w*GS-.*$/
+ @planetsgs = true if cfg.match /SGS-(.*) Device, Compiled on .*$/
+
+ cfg = cfg.each_line.to_a[0...-2]
+
+ # Strip system time and system uptime from planet gs switches
+ cfg = cfg.reject { |line| line.match /System Time\s*:.*/ }
+ cfg = cfg.reject { |line| line.match /System Uptime\s*:.*/ }
+
+ comment cfg.join
+ end
+
+
+ cmd 'show running-config' do |cfg|
+ cfg.gsub! "\n\r", "\n"
+ cfg = cfg.each_line.to_a
+
+ cfg = cfg.reject { |line| line.match "Building configuration..." }
+
+ if @planetsgs
+ cfg << cmd('show transceiver detail | include transceiver detail information|found|Type|length|Nominal|wavelength|Base information') do |cfg|
+ comment cfg
+ end
+ end
+
+ cfg.join
+ end
+
+
+ cfg :telnet do
+ username /^Username:/
+ password /^Password:/
+ end
+
+ cfg :telnet, :ssh do
+ post_login 'terminal length 0'
+ # preferred way to handle additional passwords
+ if vars :enable
+ post_login do
+ send "enable\n"
+ cmd vars(:enable)
+ end
+ end
+ pre_logout 'exit'
+ end
+
+end
diff --git a/lib/oxidized/model/powerconnect.rb b/lib/oxidized/model/powerconnect.rb
index f0fa3df..ac36c26 100644
--- a/lib/oxidized/model/powerconnect.rb
+++ b/lib/oxidized/model/powerconnect.rb
@@ -39,7 +39,7 @@ class PowerConnect < Oxidized::Model
if vars :enable
post_login do
send "enable\n"
- send vars(:enable) + "\n"
+ cmd vars(:enable)
end
end
diff --git a/lib/oxidized/model/procurve.rb b/lib/oxidized/model/procurve.rb
index da792e6..c117df3 100644
--- a/lib/oxidized/model/procurve.rb
+++ b/lib/oxidized/model/procurve.rb
@@ -57,4 +57,8 @@ class Procurve < Oxidized::Model
pre_logout "logout\ny\nn"
end
+ cfg :ssh do
+ pty_options({ chars_wide: 1000 })
+ end
+
end
diff --git a/lib/oxidized/model/routeros.rb b/lib/oxidized/model/routeros.rb
index 4822500..a92ad5e 100644
--- a/lib/oxidized/model/routeros.rb
+++ b/lib/oxidized/model/routeros.rb
@@ -1,5 +1,5 @@
class RouterOS < Oxidized::Model
- prompt /\[\w+@\S+\]\s?>\s?$/
+ prompt /\[\w+@\S+(\s?\S+)*\]\s?>\s?$/
comment "# "
cmd '/system routerboard print' do |cfg|
@@ -8,6 +8,7 @@ class RouterOS < Oxidized::Model
cmd '/export' do |cfg|
cfg.gsub! /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/, '' # strip ANSI colours
+ cfg.gsub! /\\\r\n\s+/, '' # strip new line
cfg = cfg.split("\n").select { |line| not line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] }
cfg.join("\n") + "\n"
end
diff --git a/lib/oxidized/model/trango.rb b/lib/oxidized/model/trango.rb
new file mode 100644
index 0000000..b2aa1e7
--- /dev/null
+++ b/lib/oxidized/model/trango.rb
@@ -0,0 +1,62 @@
+class Trango < Oxidized::Model
+ # take a Trangolink sysinfo output and turn it into a configuration file
+
+ prompt /^#>\s?/
+ comment '# '
+
+ cmd 'sysinfo' do |cfg|
+ out = []
+ comments = []
+ cfg.each_line do |line|
+ if line.match /\[Opmode\] (off|on) \[Default Opmode\] (off|on)/
+ out << "opmode " + Regexp.last_match[1]
+ out << "defaultopmode " + Regexp.last_match[2]
+ end
+ if line.match /\[Tx Power\] ([\-\d]+) dBm/
+ out << "power " + Regexp.last_match[1]
+ end
+ if line.match /\[Active Channel\] (\d+) (v|h)/
+ out << "freq " + Regexp.last_match[1] + ' ' + Regexp.last_match[2]
+ end
+ if line.match /\[Peer ID\] ([A-F0-9]+)/
+ out << "peerid " + Regexp.last_match[1]
+ end
+ if line.match /\[Unit Type\] (\S+)/
+ out << "utype " + Regexp.last_match[1]
+ end
+ if line.match /\[(Hardware Version|Firmware Version|Model|S\/N)\] (\S+)/
+ comments << '# ' + Regexp.last_match[1] + ': ' + Regexp.last_match[2]
+ end
+ if line.match /\[Remarks\] (\S+)/
+ out << "remarks " + Regexp.last_match[1]
+ end
+ if line.match /\[RSSI LED\] (on|off)/
+ out << "rssiled " + Regexp.last_match[1]
+ end
+ if line.match /\[Speed\] (\d+) Mbps/
+ speed = Regexp.last_match[1]
+ end
+ if line.match /\[Tx MIR\] (\d+) Kbps/
+ out << "mir ".concat(Regexp.last_match[1])
+ end
+ if line.match /\[Auto Rate Shift\] (on|off)/
+ out << "autorateshift ".concat(Regexp.last_match[1])
+ if Regexp.last_match[1].eql? 'off'
+ out << "speed $speed"
+ end
+ end
+ if line.match /\[IP\] (\S+) \[Subnet Mask\] (\S+) \[Gateway\] (\S+)/
+ out << "ipconfig " + Regexp.last_match[1] + ' ' +
+ Regexp.last_match[2] + ' ' +
+ Regexp.last_match[3]
+ end
+ end
+ comments.push(*out).join "\n"
+ end
+
+ cfg :telnet do
+ password /Password:/
+ pre_logout 'exit'
+ end
+
+end
diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb
index f2b125a..b13ce0e 100644
--- a/lib/oxidized/node.rb
+++ b/lib/oxidized/node.rb
@@ -128,24 +128,15 @@ module Oxidized
end
def resolve_auth opt
- # Resolve configured username/password, give priority to group level configuration
- # TODO: refactor to use revised behaviour of Asetus
- cfg_username, cfg_password =
- if Oxidized.config.groups.has_key?(@group) and ['username', 'password'].all? {|e| Oxidized.config.groups[@group].has_key?(e)}
- [Oxidized.config.groups[@group].username, Oxidized.config.groups[@group].password]
- elsif ['username', 'password'].all? {|e| Oxidized.config.has_key?(e)}
- [Oxidized.config.username, Oxidized.config.password]
- else
- [nil, nil]
- end
- auth = {}
- auth[:username] = (opt[:username] or cfg_username)
- auth[:password] = (opt[:password] or cfg_password)
- auth
+ # Resolve configured username/password
+ {
+ username: resolve_key(:username, opt),
+ password: resolve_key(:password, opt),
+ }
end
def resolve_input opt
- inputs = (opt[:input] or Oxidized.config.input.default)
+ inputs = resolve_key :input, opt, Oxidized.config.input.default
inputs.split(/\s*,\s*/).map do |input|
if not Oxidized.mgr.input[input]
Oxidized.mgr.add_input input or raise MethodNotFound, "#{input} not found for node #{ip}"
@@ -155,7 +146,7 @@ module Oxidized
end
def resolve_output opt
- output = (opt[:output] or Oxidized.config.output.default)
+ output = resolve_key :output, opt, Oxidized.config.output.default
if not Oxidized.mgr.output[output]
Oxidized.mgr.add_output output or raise MethodNotFound, "#{output} not found for node #{ip}"
end
@@ -163,7 +154,7 @@ module Oxidized
end
def resolve_model opt
- model = (opt[:model] or Oxidized.config.model)
+ model = resolve_key :model, opt
if not Oxidized.mgr.model[model]
Oxidized.logger.debug "lib/oxidized/node.rb: Loading model #{model.inspect}"
Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}"
@@ -187,6 +178,33 @@ module Oxidized
end
end
+ def resolve_key key, opt, global=nil
+ # resolve key, first get global, then get group then get node config
+ key_sym = key.to_sym
+ key_str = key.to_s
+ value = global
+ Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'"
+
+ #global
+ if not value and Oxidized.config.has_key?(key_str)
+ value = Oxidized.config[key_str]
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global"
+ end
+
+ #group
+ if Oxidized.config.groups.has_key?(@group)
+ if Oxidized.config.groups[@group].has_key?(key_str)
+ value = Oxidized.config.groups[@group][key_str]
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group"
+ end
+ end
+
+ #node
+ value = opt[key_sym] || value
+ Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'"
+ value
+ end
+
def is_git? opt
(opt[:output] || Oxidized.config.output.default) == 'git'
end
diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb
index bb13827..45f72e1 100644
--- a/lib/oxidized/output/file.rb
+++ b/lib/oxidized/output/file.rb
@@ -17,7 +17,7 @@ class OxidizedFile < Output
end
def store node, outputs, opt={}
- file = @cfg.directory
+ file = File.expand_path @cfg.directory
if opt[:group]
file = File.join File.dirname(file), opt[:group]
end
@@ -28,18 +28,22 @@ class OxidizedFile < Output
end
def fetch node, group
- cfg_dir = @cfg.directory
+ cfg_dir = File.expand_path @cfg.directory
+ node_name = node.name
+
if group # group is explicitly defined by user
- IO.readlines File.join(cfg_dir, group, node)
+ cfg_dir = File.join File.dirname(cfg_dir), group
+ File.read File.join(cfg_dir, node_name)
else
- if File.exists? File.join(cfg_dir, node) # node configuration file is stored on base directory
- IO.readlines File.join(cfg_dir, node)
+ if File.exists? File.join(cfg_dir, node_name) # node configuration file is stored on base directory
+ File.read File.join(cfg_dir, node_name)
else
- path = Dir.glob File.join(cfg_dir, '**', node) # fetch node in all groups
- return nil if path[0].nil?
- open(path[0], 'r').readlines
+ path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups
+ File.read path
end
end
+ rescue Errno::ENOENT
+ return nil
end
def version node, group
diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb
index a0ce848..d498e0b 100644
--- a/lib/oxidized/source/csv.rb
+++ b/lib/oxidized/source/csv.rb
@@ -20,18 +20,20 @@ class CSV < Source
nodes = []
open(File.expand_path @cfg.file).each_line do |line|
next if line.match(/^\s*#/)
- data = line.chomp.split @cfg.delimiter
+ data = line.chomp.split(@cfg.delimiter, -1)
next if data.empty?
# map node parameters
keys = {}
@cfg.map.each do |key, position|
- keys[key.to_sym] = data[position]
+ keys[key.to_sym] = node_var_interpolate data[position]
end
keys[:model] = map_model keys[:model] if keys.key? :model
- # map node specific vars, empty value is considered as nil
+ # map node specific vars
vars = {}
- @cfg.vars_map.each { |key, position| vars[key.to_sym] = data[position].to_s.empty? ? nil : data[position] }
+ @cfg.vars_map.each do |key, position|
+ vars[key.to_sym] = node_var_interpolate data[position]
+ end
keys[:vars] = vars unless vars.empty?
nodes << keys
diff --git a/lib/oxidized/source/http.rb b/lib/oxidized/source/http.rb
index 93361a2..4fd388b 100644
--- a/lib/oxidized/source/http.rb
+++ b/lib/oxidized/source/http.rb
@@ -20,6 +20,7 @@ class HTTP < Source
uri = URI.parse(@cfg.url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @cfg.secure
# map headers
headers = {}
@@ -39,13 +40,15 @@ class HTTP < Source
# map node parameters
keys = {}
@cfg.map.each do |key, position|
- keys[key.to_sym] = line[position]
+ keys[key.to_sym] = node_var_interpolate line[position]
end
keys[:model] = map_model keys[:model] if keys.key? :model
- # map node specific vars, empty value is considered as nil
+ # map node specific vars
vars = {}
- @cfg.vars_map.each { |key, position| vars[key.to_sym] = line[position].to_s.empty? ? nil : line[position] }
+ @cfg.vars_map.each do |key, position|
+ vars[key.to_sym] = node_var_interpolate line[position]
+ end
keys[:vars] = vars unless vars.empty?
nodes << keys
diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb
index 7862dd1..9b8bc94 100644
--- a/lib/oxidized/source/source.rb
+++ b/lib/oxidized/source/source.rb
@@ -1,11 +1,23 @@
module Oxidized
class Source
class NoConfig < OxidizedError; end
+
def initialize
@map = (Oxidized.config.model_map or {})
end
+
def map_model model
@map.has_key?(model) ? @map[model] : model
end
+
+ def node_var_interpolate var
+ case var
+ when "nil" then nil
+ when "false" then false
+ when "true" then true
+ else var
+ end
+ end
+
end
end
diff --git a/lib/oxidized/source/sql.rb b/lib/oxidized/source/sql.rb
index fc1caa8..13fc39b 100644
--- a/lib/oxidized/source/sql.rb
+++ b/lib/oxidized/source/sql.rb
@@ -26,12 +26,14 @@ class SQL < Source
query.each do |node|
# map node parameters
keys = {}
- @cfg.map.each { |key, sql_column| keys[key.to_sym] = node[sql_column.to_sym] }
+ @cfg.map.each { |key, sql_column| keys[key.to_sym] = node_var_interpolate node[sql_column.to_sym] }
keys[:model] = map_model keys[:model] if keys.key? :model
# map node specific vars
vars = {}
- @cfg.vars_map.each { |key, sql_column| vars[key.to_sym] = node[sql_column.to_sym] }
+ @cfg.vars_map.each do |key, sql_column|
+ vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym]
+ end
keys[:vars] = vars unless vars.empty?
nodes << keys
diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb
index 54defae..073aae9 100644
--- a/lib/oxidized/version.rb
+++ b/lib/oxidized/version.rb
@@ -1,3 +1,3 @@
module Oxidized
- VERSION = '0.16.3'
+ VERSION = '0.19.0'
end
diff --git a/oxidized.gemspec b/oxidized.gemspec
index bb9366d..c12dcb5 100644
--- a/oxidized.gemspec
+++ b/oxidized.gemspec
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 2.0.0'
s.add_runtime_dependency 'asetus', '~> 0.1'
s.add_runtime_dependency 'slop', '~> 3.5'
- s.add_runtime_dependency 'net-ssh', '>= 3.0.0', '<3.1'
+ s.add_runtime_dependency 'net-ssh', '~> 3.0.2'
s.add_runtime_dependency 'rugged', '~> 0.21', '>= 0.21.4'
if defined?(RUBY_VERSION) && RUBY_VERSION > '2.3'