rubicante 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +7 -0
- data/Manifest.txt +26 -0
- data/README.rdoc +118 -0
- data/README.txt +118 -0
- data/Rakefile +14 -0
- data/bin/rubicante +34 -0
- data/lib/rubicante.rb +6 -0
- data/lib/rubicante/cli.rb +89 -0
- data/lib/rubicante/environment.rb +78 -0
- data/lib/rubicante/host.rb +140 -0
- data/lib/rubicante/host_error.rb +21 -0
- data/lib/rubicante/host_group.rb +18 -0
- data/lib/rubicante/type.rb +20 -0
- data/lib/rubicante/type_group.rb +18 -0
- data/lib/rubicante/website.rb +54 -0
- data/lib/rubicante/website_error.rb +10 -0
- data/spec/environment_spec.rb +84 -0
- data/spec/helper.rb +3 -0
- data/spec/host_error_spec.rb +61 -0
- data/spec/host_group_spec.rb +33 -0
- data/spec/host_spec.rb +159 -0
- data/spec/type_group_spec.rb +35 -0
- data/spec/type_spec.rb +42 -0
- data/spec/website_error_spec.rb +35 -0
- data/spec/website_spec.rb +53 -0
- data/test/test_rubicante.rb +9 -0
- metadata +111 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rubicante/website'
|
2
|
+
require 'rubicante/host_error'
|
3
|
+
|
4
|
+
require 'logging'
|
5
|
+
require 'ping'
|
6
|
+
require 'socket'
|
7
|
+
|
8
|
+
module Rubicante
|
9
|
+
class Host
|
10
|
+
attr_reader :name
|
11
|
+
attr_accessor :ports, :services, :types, :websites
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
@ports = []
|
16
|
+
@services = []
|
17
|
+
@types = []
|
18
|
+
@websites = []
|
19
|
+
|
20
|
+
@log = Logging::Logger[self]
|
21
|
+
|
22
|
+
# Prepare Website logger
|
23
|
+
@appender = Logging::Appender['rubicante']
|
24
|
+
Logging::Logger['Rubicante::Website'].add_appenders(Logging::Appender['rubicante']) if not @appender.nil?
|
25
|
+
Logging::Logger['Rubicante::Website'].level = @log.level
|
26
|
+
end
|
27
|
+
|
28
|
+
def ping
|
29
|
+
@log.debug "Performing TCP echo ping on host '#{@name}'"
|
30
|
+
Ping.pingecho @name
|
31
|
+
end
|
32
|
+
|
33
|
+
def port(port_number)
|
34
|
+
@ports << port_number
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def service(service_name)
|
39
|
+
@services << service_name
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def type(type_name)
|
44
|
+
@types << type_name
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def website(website_url)
|
49
|
+
@websites << Website.new(website_url)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Check if the specified port is active by connecting to it
|
54
|
+
def check_port(port)
|
55
|
+
port_output = "#{@name}:#{port}"
|
56
|
+
|
57
|
+
@log.debug "Checking port #{port_output}..."
|
58
|
+
|
59
|
+
begin
|
60
|
+
test = TCPSocket.open(@name, port)
|
61
|
+
@log.debug "Port #{port_output} looks good"
|
62
|
+
return true # if we get here, the socket opened
|
63
|
+
rescue
|
64
|
+
@log.debug "Port #{port_output} raised an exception when opening"
|
65
|
+
return false # if we get here, there are problems with the port
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Iterates through all the ports in the Host and runs check_port(port)
|
70
|
+
# against them.
|
71
|
+
#
|
72
|
+
# == Yields: port, is_alive
|
73
|
+
#
|
74
|
+
# Each registered port is yielded along with the boolean result from
|
75
|
+
# check_port(port).
|
76
|
+
#
|
77
|
+
# == Example:
|
78
|
+
#
|
79
|
+
# host = Rubicante::Host.new("test-host")
|
80
|
+
# host.port(80)
|
81
|
+
# host.port(443)
|
82
|
+
#
|
83
|
+
# host.check_ports do |port, response|
|
84
|
+
# puts "Port #{port} is UP" if response
|
85
|
+
# puts "Port #{port} is DOWN" if not response
|
86
|
+
# end
|
87
|
+
def check_ports
|
88
|
+
@ports.each do |port|
|
89
|
+
yield port, check_port(port)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Iterates through all the websites in the Host and runs
|
94
|
+
# wrong? against them looking for problems. If a problem is
|
95
|
+
# found, a Hash of the URL and the HTTP Status code is yielded.
|
96
|
+
#
|
97
|
+
# == Yields: a Hash
|
98
|
+
#
|
99
|
+
# {
|
100
|
+
# :url => String, # the URL of the current website in the block
|
101
|
+
# :code => String # the HTTP Status code of the check (i.e., 404, 500, etc.)
|
102
|
+
# }
|
103
|
+
#
|
104
|
+
# == Example
|
105
|
+
#
|
106
|
+
# host = Rubicante::Host.new("test-host")
|
107
|
+
# host.website('www.exmaple.com')
|
108
|
+
# host.website('www.rubicante.com')
|
109
|
+
# host.website('www.openbsd.org')
|
110
|
+
#
|
111
|
+
# host.check_websites do |result|
|
112
|
+
# puts "Website #{result[:url]} failed with code #{result[:code]}!"
|
113
|
+
# end
|
114
|
+
def check_websites
|
115
|
+
@log.debug "Checking websites registered to host '#{@name}'"
|
116
|
+
@websites.each do |website|
|
117
|
+
yield website.wrong?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def wrong?
|
122
|
+
@log.debug "Determing what is wrong with host '#{@name}'"
|
123
|
+
result = HostError.new(@name)
|
124
|
+
result.ping = self.ping
|
125
|
+
|
126
|
+
# If the host is alive, continue testing
|
127
|
+
if result.ping
|
128
|
+
check_ports do |port, response|
|
129
|
+
result.bad_ports << port if not response
|
130
|
+
end
|
131
|
+
|
132
|
+
check_websites do |website_error|
|
133
|
+
result.add(website_error)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return result
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubicante/website_error'
|
2
|
+
|
3
|
+
module Rubicante
|
4
|
+
class HostError
|
5
|
+
attr_reader :hostname, :website_errors
|
6
|
+
attr_accessor :bad_ports, :ping
|
7
|
+
|
8
|
+
def initialize(hostname)
|
9
|
+
@hostname = hostname
|
10
|
+
@ping = false
|
11
|
+
@bad_ports = []
|
12
|
+
@website_errors = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(error)
|
16
|
+
if error.kind_of? WebsiteError
|
17
|
+
@website_errors << error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'rubicante/host'
|
3
|
+
|
4
|
+
module Rubicante
|
5
|
+
class HostGroup
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_reader :hosts
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@hosts = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](hostname)
|
15
|
+
@hosts[hostname] ||= Host.new(hostname)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rubicante
|
2
|
+
class Type
|
3
|
+
attr_reader :name
|
4
|
+
attr_accessor :ports, :services
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@ports = []
|
9
|
+
@services = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def port(new_port)
|
13
|
+
@ports << new_port
|
14
|
+
end
|
15
|
+
|
16
|
+
def service(new_service)
|
17
|
+
@services << new_service
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'rubicante/type'
|
3
|
+
|
4
|
+
module Rubicante
|
5
|
+
class TypeGroup
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_accessor :types
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@types = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](type_name)
|
15
|
+
@types[type_name] ||= Type.new(type_name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubicante/website_error'
|
2
|
+
|
3
|
+
require 'logging'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Rubicante
|
7
|
+
class Website
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
def initialize(url)
|
11
|
+
@url = url
|
12
|
+
|
13
|
+
@log = Logging::Logger[self]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Determines whether or not the site OK based on the HTTP Status code
|
17
|
+
#
|
18
|
+
# == DETERMINING IF A SITE IS OK
|
19
|
+
#
|
20
|
+
# When performing an HTTP GET on the root, if a 2xx (HTTP OK) or
|
21
|
+
# 3xx (HTTP Redirect) response is received, the method considers the
|
22
|
+
# site to be workinging nominally and returns *true*
|
23
|
+
#
|
24
|
+
# However, if a 4xx (HTTP Client Error) or 5xx (HTTP Server Error)
|
25
|
+
# response is received, the method considers the site to be working
|
26
|
+
# abnormally and returns *false*
|
27
|
+
def is_ok?
|
28
|
+
result = false
|
29
|
+
|
30
|
+
result = true if response_code.match(/^(2|3)/)
|
31
|
+
|
32
|
+
return result
|
33
|
+
end
|
34
|
+
|
35
|
+
# Performs and HTTP GET on the URL's root ('/') and returns the
|
36
|
+
# HTTP Status code
|
37
|
+
def response_code
|
38
|
+
@log.debug "Retreiving HTTP Code for website '#{@url}'"
|
39
|
+
result = Net::HTTP.get_response(@url, '/').code
|
40
|
+
@log.debug "Received HTTP Code #{result} for website '#{@url}'"
|
41
|
+
|
42
|
+
return result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Checks to see if the site is OK. If it is not, it returns
|
46
|
+
# a Rubicante::WebsiteError
|
47
|
+
def wrong?
|
48
|
+
@log.debug "Checking website '#{@url}' for problems"
|
49
|
+
if not self.is_ok?
|
50
|
+
WebsiteError.new(@url, self.response_code)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)),"helper")
|
2
|
+
require "#{LIB_DIR}/environment"
|
3
|
+
|
4
|
+
describe "A Rubicante environment" do
|
5
|
+
before :all do
|
6
|
+
Net::HTTP.stub!(:get_response).and_return(Net::HTTPServerError.new('1', '500', 'Internal Server Error'))
|
7
|
+
end
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@env = Rubicante::Environment.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should exist" do
|
14
|
+
@env.should_not be_nil
|
15
|
+
@env.should be_an_instance_of(Rubicante::Environment)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have a HostGroup" do
|
19
|
+
@env.host.should be_an_instance_of(Rubicante::HostGroup)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have an empty HostGroup by default" do
|
23
|
+
@env.host.hosts.should == {}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have a wrong? method" do
|
27
|
+
@env.respond_to?('wrong?').should == true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return an array of HostErrors" do
|
31
|
+
@env.wrong? do |result|
|
32
|
+
result.should be_an_instance_of(Array)
|
33
|
+
result[0].should be_an_instance_of(Rubicante::HostError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have an eval_host method" do
|
38
|
+
@env.respond_to?('eval_host').should == true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should handle 'Host webservice provides website ...'" do
|
42
|
+
hostname = 'webservice'
|
43
|
+
url = 'www.rubicante-example.com'
|
44
|
+
cmd = "Host #{hostname} provides website #{url}"
|
45
|
+
@env.eval_host(cmd)
|
46
|
+
@env.host[hostname].websites[0].url.should == url
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should handle specifying multiple websites" do
|
50
|
+
hostname = 'www2'
|
51
|
+
url0 = 'test1'
|
52
|
+
url1 = 'test2'
|
53
|
+
cmd = "Host #{hostname} provides website #{url0}, provides website #{url1}"
|
54
|
+
@env.eval_host(cmd)
|
55
|
+
@env.host[hostname].websites[0].url.should == url0
|
56
|
+
@env.host[hostname].websites[1].url.should == url1
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should have an eval_command method" do
|
60
|
+
@env.respond_to?('eval_command').should == true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should raise a NotImplementedError for unknown commands" do
|
64
|
+
lambda { @env.eval_command('whumpus frumpus') }.should raise_error(NotImplementedError)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should handle 'host' commands" do
|
68
|
+
lambda { @env.eval_command('host frank') }.should_not raise_error(NotImplementedError)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should have an eval_what method" do
|
72
|
+
@env.respond_to?('eval_what').should == true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should handle 'what' commands" do
|
76
|
+
lambda { @env.eval_command('What is wrong') }.should_not raise_error(NotImplementedError)
|
77
|
+
end
|
78
|
+
|
79
|
+
after :each do
|
80
|
+
# Clean up the HostGroup instance's hash so that other specs will
|
81
|
+
# run properly
|
82
|
+
@env.host.hosts.clear
|
83
|
+
end
|
84
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)),"helper")
|
2
|
+
require "#{LIB_DIR}/host_error"
|
3
|
+
|
4
|
+
describe "An error with a host" do
|
5
|
+
before :all do
|
6
|
+
@url = "http://www.rubicante-example.com/"
|
7
|
+
@code = "500"
|
8
|
+
@website_error = Rubicante::WebsiteError.new(@url, @code)
|
9
|
+
end
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
@host_error = Rubicante::HostError.new("test-host")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should exist" do
|
16
|
+
@host_error.should_not be_nil
|
17
|
+
@host_error.should be_an_instance_of(Rubicante::HostError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should require an argument at initialization" do
|
21
|
+
lambda { Rubicante::HostError.new }.should raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should now allow changing of hostname after initialization" do
|
25
|
+
lambda { @host_error.hostname = "new-host-name" }.should raise_error(NoMethodError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have an empty array of website errors" do
|
29
|
+
@host_error.website_errors.should == []
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should allow adding of new website errors with add" do
|
33
|
+
@host_error.add(@website_error)
|
34
|
+
@host_error.website_errors.include?(@website_error).should == true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have an ping value" do
|
38
|
+
@host_error.respond_to?('ping').should == true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "ping should be false by default" do
|
42
|
+
@host_error.ping.should == false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow changin the ping value" do
|
46
|
+
@new_ping = true
|
47
|
+
@host_error.ping.should_not == @new_ping
|
48
|
+
@host_error.ping = @new_ping
|
49
|
+
@host_error.ping.should == @new_ping
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should have an empty array of bad_ports by default" do
|
53
|
+
@host_error.bad_ports.should == []
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should allow appending to bad_ports" do
|
57
|
+
@new_bad_port = 80
|
58
|
+
@host_error.bad_ports << 80
|
59
|
+
@host_error.bad_ports.include?(@new_bad_port).should == true
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)),"helper")
|
2
|
+
require "#{LIB_DIR}/host_group"
|
3
|
+
|
4
|
+
describe "A group of hosts" do
|
5
|
+
before :each do
|
6
|
+
@host_group = Rubicante::HostGroup.instance
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should exist" do
|
10
|
+
@host_group.should_not be_nil
|
11
|
+
@host_group.should be_an_instance_of(Rubicante::HostGroup)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have an empty hash of hosts by default" do
|
15
|
+
@host_group.hosts.should == {}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should create a new host in hosts if it doesn't exist" do
|
19
|
+
hostname = "new-host"
|
20
|
+
@host_group[hostname].name.should == hostname
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should allow for modifying previously defined hosts" do
|
24
|
+
hostname = "new-host"
|
25
|
+
new_port = 80
|
26
|
+
new_service = "new-service"
|
27
|
+
|
28
|
+
@host_group[hostname].port(new_port).service(new_service)
|
29
|
+
|
30
|
+
@host_group[hostname].ports.include?(new_port).should == true
|
31
|
+
@host_group[hostname].services.include?(new_service).should == true
|
32
|
+
end
|
33
|
+
end
|