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.
@@ -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,10 @@
1
+ module Rubicante
2
+ class WebsiteError
3
+ attr_reader :code, :url
4
+
5
+ def initialize(url, code)
6
+ @url = url
7
+ @code = code
8
+ end
9
+ end
10
+ 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,3 @@
1
+ require "spec"
2
+
3
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)), *%w[.. lib rubicante])
@@ -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