corona 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +32 -0
- data/README.md +17 -0
- data/Rakefile +34 -0
- data/TODO.md +6 -0
- data/bin/corona +5 -0
- data/corona.gemspec +18 -0
- data/lib/corona/config/bootstrap.rb +13 -0
- data/lib/corona/config/core.rb +26 -0
- data/lib/corona/core.rb +179 -0
- data/lib/corona/db.rb +50 -0
- data/lib/corona/log.rb +13 -0
- data/lib/corona/snmp.rb +82 -0
- data/lib/corona.rb +3 -0
- metadata +112 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (1.0.9)
|
5
|
+
diff-lcs (1.2.3)
|
6
|
+
method_source (0.8.1)
|
7
|
+
pry (0.9.12)
|
8
|
+
coderay (~> 1.0.5)
|
9
|
+
method_source (~> 0.8)
|
10
|
+
slop (~> 3.4)
|
11
|
+
rspec (2.13.0)
|
12
|
+
rspec-core (~> 2.13.0)
|
13
|
+
rspec-expectations (~> 2.13.0)
|
14
|
+
rspec-mocks (~> 2.13.0)
|
15
|
+
rspec-core (2.13.1)
|
16
|
+
rspec-expectations (2.13.0)
|
17
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
+
rspec-mocks (2.13.1)
|
19
|
+
sequel (3.46.0)
|
20
|
+
slop (3.4.4)
|
21
|
+
snmp (1.1.1)
|
22
|
+
sqlite3 (1.3.7)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
pry
|
29
|
+
rspec
|
30
|
+
sequel
|
31
|
+
snmp
|
32
|
+
sqlite3
|
data/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# About
|
2
|
+
Corona sends SNMP queries to defined CIDR ranges and populates SQL database based on nodes found. Some particular problems it tries to deal with:
|
3
|
+
* Only discover one node once
|
4
|
+
* To that effect it has priority list of idDescr lo0.0, loopback0, vlan2 etc. Higher priority will always replace lower priority interface (say you have MGMT in loop0 but giga0/2.42 has valid MGMT address towards L2 metro)
|
5
|
+
* Tries to handle gracefully renumbering, renaming, etc
|
6
|
+
|
7
|
+
# Install
|
8
|
+
1. gem install corona
|
9
|
+
2. corona
|
10
|
+
3. ^C (break it)
|
11
|
+
4. edit ~/.config/corona/config
|
12
|
+
5. put corona in crontab as _corona|mail -E -s 'new nodes found' foo@example.com_
|
13
|
+
|
14
|
+
# Config
|
15
|
+
* You need to configure SNMP community
|
16
|
+
* You need to define CIDR to poll and CIDRs to ignore (subset of those you poll)
|
17
|
+
* CIDR in example config is list, but can be replaced with 'string' which points to file, where CIDRs are listed
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
Bundler.setup
|
5
|
+
rescue LoadError
|
6
|
+
warn 'missing dependencies'
|
7
|
+
exit 42
|
8
|
+
end
|
9
|
+
|
10
|
+
gemspec = eval(File.read(Dir['*.gemspec'].first))
|
11
|
+
|
12
|
+
desc 'Validate the gemspec'
|
13
|
+
task :gemspec do
|
14
|
+
gemspec.validate
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec::Core::RakeTask.new(:spec)
|
18
|
+
|
19
|
+
desc "Build gem locally"
|
20
|
+
task :build => [:spec, :gemspec] do
|
21
|
+
system "gem build #{gemspec.name}.gemspec"
|
22
|
+
FileUtils.mkdir_p "gems"
|
23
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "gems"
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Install gem locally"
|
27
|
+
task :install => :build do
|
28
|
+
system "sudo sh -c \'umask 022; gem install gems/#{gemspec.name}-#{gemspec.version}\'"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Clean automatically generated files"
|
32
|
+
task :clean do
|
33
|
+
FileUtils.rm_rf "gems"
|
34
|
+
end
|
data/TODO.md
ADDED
data/bin/corona
ADDED
data/corona.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'corona'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = [ 'Saku Ytti' ]
|
6
|
+
s.email = %w( saku@ytti.fi )
|
7
|
+
s.homepage = 'http://github.com/ytti/corona'
|
8
|
+
s.summary = 'device discovery via snmp polls'
|
9
|
+
s.description = 'Threaded SNMP poll based network discovery. Devices are stored in SQL'
|
10
|
+
s.rubyforge_project = s.name
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.executables = %w( corona )
|
13
|
+
s.require_path = 'lib'
|
14
|
+
|
15
|
+
s.add_dependency 'sequel'
|
16
|
+
s.add_dependency 'sqlite3'
|
17
|
+
s.add_dependency 'snmp'
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Corona
|
2
|
+
require 'fileutils'
|
3
|
+
FileUtils.mkdir_p Config::Root
|
4
|
+
CFG.community = 'public'
|
5
|
+
CFG.db = File.join Config::Root, 'corona.db'
|
6
|
+
CFG.poll = %w( 10.10.10.0/24 10.10.20.0/24 )
|
7
|
+
CFG.ignore = %w( 10.10.10.42/32 10.10.20.42/32 )
|
8
|
+
CFG.mgmt = %w( lo0.0 loopback0 vlan2 )
|
9
|
+
CFG.threads = 50
|
10
|
+
CFG.log = File.join Config::Root, 'log'
|
11
|
+
CFG.debug = false
|
12
|
+
CFG.save
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Corona
|
2
|
+
require 'ostruct'
|
3
|
+
require 'yaml'
|
4
|
+
class Config < OpenStruct
|
5
|
+
Root = File.join ENV['HOME'], '.config', 'corona'
|
6
|
+
Crash = File.join Root, 'crash'
|
7
|
+
def initialize file=File.join(Config::Root, 'config')
|
8
|
+
super()
|
9
|
+
@file = file.to_s
|
10
|
+
end
|
11
|
+
def load
|
12
|
+
if File.exists? @file
|
13
|
+
marshal_load YAML.load_file @file
|
14
|
+
else
|
15
|
+
require 'corona/config/bootstrap'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def save
|
19
|
+
File.write @file, YAML.dump(marshal_dump)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
CFG = Config.new
|
23
|
+
CFG.load
|
24
|
+
Log.file = CFG.log if CFG.log
|
25
|
+
Log.level = Logger::INFO unless CFG.debug
|
26
|
+
end
|
data/lib/corona/core.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'corona/log'
|
2
|
+
require 'corona/config/core'
|
3
|
+
require 'corona/snmp'
|
4
|
+
require 'corona/db'
|
5
|
+
require 'ipaddr'
|
6
|
+
require 'resolv'
|
7
|
+
|
8
|
+
module Corona
|
9
|
+
class << self
|
10
|
+
def new
|
11
|
+
Core.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Core
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
poll, ignore = resolve_networks
|
21
|
+
@mutex = Mutex.new
|
22
|
+
@db = DB.new
|
23
|
+
threads = []
|
24
|
+
Thread.abort_on_exception = true
|
25
|
+
poll.each do |net|
|
26
|
+
net.to_range.each do |ip|
|
27
|
+
next if ignore.any? { |ignore| ignore.include? ip }
|
28
|
+
while threads.size >= CFG.threads
|
29
|
+
threads.delete_if { |thread| not thread.alive? }
|
30
|
+
sleep 0.01
|
31
|
+
end
|
32
|
+
threads << Thread.new { poll ip }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
threads.each { |thread| thread.join }
|
36
|
+
end
|
37
|
+
|
38
|
+
def poll ip
|
39
|
+
snmp = SNMP.new ip.to_s
|
40
|
+
oids = snmp.dbget
|
41
|
+
if oids
|
42
|
+
if index = snmp.ip2index(ip.to_s)
|
43
|
+
if int = snmp.ifdescr(index)
|
44
|
+
@mutex.synchronize { process :oids=>oids, :int=>int.downcase, :ip=>ip }
|
45
|
+
else
|
46
|
+
Log.warn "no ifDescr for #{index} at #{ip}"
|
47
|
+
end
|
48
|
+
else
|
49
|
+
Log.warn "no ifIndex for #{ip}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def process opt
|
55
|
+
opt = normalize_opt opt
|
56
|
+
record = mkrecord opt
|
57
|
+
old_by_ip, old_by_sysname = @db.old record[:id], record[:oid_sysName]
|
58
|
+
|
59
|
+
# unique box having non-unique sysname
|
60
|
+
# old_by_sysname = false if record[:oid_sysDescr].match 'Application Control Engine'
|
61
|
+
|
62
|
+
if not old_by_sysname and not old_by_ip
|
63
|
+
# all new device
|
64
|
+
puts "ptr [%s] sysName [%s] ip [%s]" % [record[:ptr], record[:oid_sysName], record[:ip]]
|
65
|
+
Log.info "#{record[:ip]} added"
|
66
|
+
@db.add record
|
67
|
+
|
68
|
+
elsif not old_by_sysname and old_by_ip
|
69
|
+
# IP seen, name not, device got renamed?
|
70
|
+
Log.info "#{record[:ip]} got renamed"
|
71
|
+
@db.update record, [:ip, old_by_ip[:ip]]
|
72
|
+
|
73
|
+
elsif old_by_sysname and not old_by_ip
|
74
|
+
# name exists, but IP is new, figure out if we wan to use old or new IP
|
75
|
+
decide_old_new record, old_by_sysname
|
76
|
+
|
77
|
+
elsif old_by_sysname and old_by_ip
|
78
|
+
both_seen record, old_by_sysname, old_by_ip
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def both_seen record, old_by_sysname, old_by_ip
|
83
|
+
if old_by_sysname == old_by_ip
|
84
|
+
# no changes, updating same record
|
85
|
+
Log.debug "#{record[:ip]} refreshed, no channges"
|
86
|
+
@db.update record, [:oid_sysName, old_by_sysname[:oid_sysName]]
|
87
|
+
else
|
88
|
+
# same name seen and same IP seen, but records were not same (device got renumbered to existing node + existing node got delete?)
|
89
|
+
Log.warn "#{record[:ip]}, unique entries for IP and sysName in DB, updating by IP"
|
90
|
+
@db.update record, [:ip, old_by_ip[:ip]]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def decide_old_new record, old_by_sysname
|
95
|
+
new_int_pref = (CFG.mgmt.index(record[:oid_ifDescr]) or 100)
|
96
|
+
old_int_pref = (CFG.mgmt.index(old_by_sysname[:oid_ifDescr]) or 99)
|
97
|
+
|
98
|
+
if new_int_pref < old_int_pref
|
99
|
+
# new int is more preferable than old
|
100
|
+
Log.info "#{record[:ip]} is replacing inferior #{old_by_sysname[:ip]}"
|
101
|
+
@db.update record, [:oid_sysName, old_by_sysname[:oid_sysName]]
|
102
|
+
|
103
|
+
elsif new_int_pref == 100 and old_int_pref == 99
|
104
|
+
# neither old or new interface is known good MGMT interface
|
105
|
+
if SNMP.new(old_by_sysname[:ip]).sysdescr
|
106
|
+
# if old IP works, don't update
|
107
|
+
Log.debug "#{record[:ip]} not updating, previously seen as #{old_by_sysname[:ip]}"
|
108
|
+
else
|
109
|
+
Log.info "#{record[:ip]} updating, old #{old_by_sysname[:ip]} is dead"
|
110
|
+
@db.update record, [:oid_sysName, old_by_sysname[:oid_sysName]]
|
111
|
+
end
|
112
|
+
|
113
|
+
elsif new_int_pref >= old_int_pref
|
114
|
+
# nothing to do, we have better entry
|
115
|
+
Log.debug "#{record[:ip]} already seen as superior via #{old_by_sysname[:ip]}"
|
116
|
+
|
117
|
+
else
|
118
|
+
Log.error "not updating, new: #{record[:ip]}, old: #{old_by_sysname[:ip]}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def mkrecord opt
|
123
|
+
{
|
124
|
+
:id => opt[:ip].to_i,
|
125
|
+
:ip => opt[:ip].to_s,
|
126
|
+
:ptr => ip2name(opt[:ip].to_s),
|
127
|
+
:model => model(opt),
|
128
|
+
:oid_ifDescr => opt[:int],
|
129
|
+
:oid_sysName => opt[:oids][:sysName],
|
130
|
+
:oid_sysLocation => opt[:oids][:sysLocation],
|
131
|
+
:oid_sysDescr => opt[:oids][:sysDescr],
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def normalize_opt opt
|
136
|
+
opt[:oids][:sysName].sub! /-re[1-9]\./, '-re0.'
|
137
|
+
opt
|
138
|
+
end
|
139
|
+
|
140
|
+
def ip2name ip
|
141
|
+
Resolv.getname ip rescue ip
|
142
|
+
end
|
143
|
+
|
144
|
+
def model opt
|
145
|
+
case opt[:oids][:sysDescr]
|
146
|
+
when /Cisco Catalyst operating System/
|
147
|
+
'catos'
|
148
|
+
when /cisco/i, /Application Control Engine/i
|
149
|
+
'ios'
|
150
|
+
when /JUNOS/
|
151
|
+
'junos'
|
152
|
+
when /^NetScreen/, /^SSG-\d+/
|
153
|
+
'screenos'
|
154
|
+
when /IronWare/
|
155
|
+
'ironware'
|
156
|
+
when /^Summit/
|
157
|
+
'xos'
|
158
|
+
when /^\d+[A-Z]\sEthernet Switch$/
|
159
|
+
'powerconnect'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def resolve_networks
|
164
|
+
[CFG.poll, CFG.ignore].map do |nets|
|
165
|
+
if nets.respond_to? :each
|
166
|
+
nets.map { |net| IPAddr.new net }
|
167
|
+
else
|
168
|
+
out = []
|
169
|
+
File.read(nets).each_line do |net|
|
170
|
+
net = net.match /^([\d.\/]+)/
|
171
|
+
out.push IPAddr.new net[1] if net
|
172
|
+
end
|
173
|
+
out
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
end
|
data/lib/corona/db.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Corona
|
2
|
+
class DB
|
3
|
+
require 'sequel'
|
4
|
+
require 'sqlite3'
|
5
|
+
def initialize
|
6
|
+
@db = Sequel.sqlite(CFG.db, :max_connections => 1, :pool_timeout => 60)
|
7
|
+
|
8
|
+
@db.create_table :device do
|
9
|
+
primary_key :id
|
10
|
+
String :ip
|
11
|
+
String :ptr
|
12
|
+
String :model
|
13
|
+
String :oid_ifDescr
|
14
|
+
Boolean :active
|
15
|
+
Time :first_seen
|
16
|
+
Time :last_seen
|
17
|
+
String :oid_sysName
|
18
|
+
String :oid_sysLocation
|
19
|
+
String :oid_sysDescr
|
20
|
+
end unless @db.table_exists? :device
|
21
|
+
end
|
22
|
+
|
23
|
+
# http://sequel.rubyforge.org/rdoc/files/doc/cheat_sheet_rdoc.html
|
24
|
+
#
|
25
|
+
|
26
|
+
def add record
|
27
|
+
record[:first_seen] = record[:last_seen] = Time.now.utc
|
28
|
+
record[:active] = true
|
29
|
+
#Log.debug "adding: #{record}"
|
30
|
+
@db[:device].insert record
|
31
|
+
end
|
32
|
+
|
33
|
+
def update record, where
|
34
|
+
record[:last_seen] = Time.now.utc
|
35
|
+
record[:active] = true
|
36
|
+
#Log.debug "updating (where: #{where}): #{record}"
|
37
|
+
@db[:device].where('? == ?', where.first, where.last).update record
|
38
|
+
end
|
39
|
+
|
40
|
+
def [] primary_key
|
41
|
+
@db[:device].where('id == ?', primary_key).first
|
42
|
+
end
|
43
|
+
|
44
|
+
def old primary_key, oid_sysName
|
45
|
+
ip = self[primary_key]
|
46
|
+
sysName = @db[:device].where('oid_sysName == ?', oid_sysName).first
|
47
|
+
[ip, sysName]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/corona/log.rb
ADDED
data/lib/corona/snmp.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Corona
|
2
|
+
class SNMP
|
3
|
+
DB_OID = {
|
4
|
+
:sysDescr => '1.3.6.1.2.1.1.1.0',
|
5
|
+
:sysLocation => '1.3.6.1.2.1.1.6.0',
|
6
|
+
:sysName => '1.3.6.1.2.1.1.5.0',
|
7
|
+
}
|
8
|
+
OID = {
|
9
|
+
:ipCidrRouteIfIndex => '1.3.6.1.2.1.4.24.4.1.5', # addr.255.255.255.255.0.0.0.0.0
|
10
|
+
:ipAdEntIfIndex => '1.3.6.1.2.1.4.20.1.2', # addr
|
11
|
+
:ipAddressIfIndex => '1.3.6.1.2.1.4.34.1.3', # 1,2 (uni,any) . 4,16 (size) . addr
|
12
|
+
:ifDescr => '1.3.6.1.2.1.2.2.1.2',
|
13
|
+
}
|
14
|
+
UNICAST = 1
|
15
|
+
IPV4 = 4
|
16
|
+
|
17
|
+
BULK_MAX = 30
|
18
|
+
require 'snmp'
|
19
|
+
def initialize host, community=CFG.community
|
20
|
+
@snmp = ::SNMP::Manager.new :Host => host, :Community => community,
|
21
|
+
:Timeout => 1, :Retries => 3, :MibModules => false
|
22
|
+
end
|
23
|
+
def get *oid
|
24
|
+
oid = [oid].flatten.join('.')
|
25
|
+
begin
|
26
|
+
@snmp.get(oid).each_varbind { |vb| return vb }
|
27
|
+
rescue ::SNMP::RequestTimeout
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
def mget oids=DB_OID
|
32
|
+
result = {}
|
33
|
+
begin
|
34
|
+
@snmp.get(oids.map{|_,oid|oid}).each_varbind do |vb|
|
35
|
+
oids.each do |name,oid|
|
36
|
+
if vb.name.to_str == oid
|
37
|
+
result[name] = vb.value
|
38
|
+
next
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue ::SNMP::RequestTimeout
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
alias dbget mget
|
48
|
+
def bulkwalk root
|
49
|
+
last, oid, vbs = false, root, []
|
50
|
+
while not last
|
51
|
+
r = @snmp.get_bulk 0, BULK_MAX, oid
|
52
|
+
r.varbind_list.each do |vb|
|
53
|
+
oid = vb.name.to_str
|
54
|
+
(last = true; break) if not oid.match /^#{Regexp.quote root}/
|
55
|
+
vbs.push vb
|
56
|
+
end
|
57
|
+
end
|
58
|
+
vbs
|
59
|
+
end
|
60
|
+
|
61
|
+
def sysdescr
|
62
|
+
get DB_OID[:sysDescr]
|
63
|
+
end
|
64
|
+
|
65
|
+
def ip2index ip
|
66
|
+
oids = mget :route => [OID[:ipCidrRouteIfIndex], ip, '255.255.255.255.0.0.0.0.0'].join('.'),
|
67
|
+
:new => [OID[:ipAddressIfIndex], UNICAST, IPV4, ip].join('.'),
|
68
|
+
:old => [OID[:ipAdEntIfIndex], ip].join('.')
|
69
|
+
return false unless oids
|
70
|
+
index = oids[:route]
|
71
|
+
index = oids[:new] if not index.class == ::SNMP::Integer or index.to_s == '0'
|
72
|
+
index = oids[:old] if not index.class == ::SNMP::Integer or index.to_s == '0'
|
73
|
+
return false unless index.class == ::SNMP::Integer
|
74
|
+
index.to_s
|
75
|
+
end
|
76
|
+
def ifdescr index
|
77
|
+
descr = get OID[:ifDescr], index
|
78
|
+
return false unless descr and descr.value.class == ::SNMP::OctetString
|
79
|
+
descr.value.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/corona.rb
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: corona
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Saku Ytti
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sqlite3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: snmp
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Threaded SNMP poll based network discovery. Devices are stored in SQL
|
63
|
+
email:
|
64
|
+
- saku@ytti.fi
|
65
|
+
executables:
|
66
|
+
- corona
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .rspec
|
71
|
+
- Gemfile
|
72
|
+
- Gemfile.lock
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- TODO.md
|
76
|
+
- bin/corona
|
77
|
+
- corona.gemspec
|
78
|
+
- lib/corona.rb
|
79
|
+
- lib/corona/config/bootstrap.rb
|
80
|
+
- lib/corona/config/core.rb
|
81
|
+
- lib/corona/core.rb
|
82
|
+
- lib/corona/db.rb
|
83
|
+
- lib/corona/log.rb
|
84
|
+
- lib/corona/snmp.rb
|
85
|
+
homepage: http://github.com/ytti/corona
|
86
|
+
licenses: []
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
hash: 1454207244225852473
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project: corona
|
108
|
+
rubygems_version: 1.8.25
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: device discovery via snmp polls
|
112
|
+
test_files: []
|