consolr 1.0.2 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c01188d47bf7c259961f8f684016128fe3475d3b
4
- data.tar.gz: d632fe8cb4bc31f429939bd32952ed2c7b4bdd6b
3
+ metadata.gz: ec522cd9e46a3d7f619654ed781bd56c78f01186
4
+ data.tar.gz: 966abf1ef88812ddb044664fdf17b561a0b33b1d
5
5
  SHA512:
6
- metadata.gz: 955dac9d7942799846908523f60d2a30db3e481a819c6b244178b2dd9775f1688adb183657ebda15a48300191ee07bb7d8122c529e51341f228d69008fc2622b
7
- data.tar.gz: d734a3cad1e28620af367df53f93b5358812cb2e58ec6e708594d0d3114080f93a55634c1bdcdca8b9005d8636f8256a0c5c6935be00f223d79fa91ac6870836
6
+ metadata.gz: 4805b73bd42dbbf2ebfd574267fb7e5daf0878b91b6ac9bd06527fa8b2ee2659fcd8b05976622309a60a36e1dce384d3d4379489aff513a72af8797f0e493133
7
+ data.tar.gz: 2ff30dcf1993cfdfbe0ebdfed430be23fe279a00eb1e0eb06b570946ba94ba271c50f21da936185071987d4e48ceab7fcb97bf6e08e7df627d91aa266615b08e
data/README.md CHANGED
@@ -62,13 +62,17 @@ Configuration params are searched in these locations --
62
62
  An example consolr.yml file
63
63
 
64
64
  ```
65
- ipmitool: /usr/bin/ipmitool
65
+ runners:
66
+ ipmitool: /usr/bin/ipmitool
66
67
  dangerous_assets:
67
68
  - "007117"
68
69
  dangerous_status:
69
70
  - "Allocated"
70
71
  ```
71
72
 
73
+ Consolr will load the runners listed and pick the first runner that says it can
74
+ manage the node in question by running the `can_run?` method of the runner.
75
+
72
76
  ## Running the tool
73
77
 
74
78
  ```
@@ -81,6 +85,54 @@ For a full list of actions, look up the help page
81
85
  $ consolr --help
82
86
  ```
83
87
 
88
+ ## Runners
89
+ Consolr ships with a runner that uses ipmitool to manage servers but there are
90
+ cases where you might want to extend it. For instance to support your favorite
91
+ IaaS provider or a private cloud. In this case, you need to write a custom
92
+ runner and deploy it. The way we do it at Tumblr is to gather our custom runner
93
+ into a gem and install that gem along with consolr.
94
+
95
+ A runner is simply a class that extends the `Consolr::Runners::Runner` base
96
+ class and is located in the correct module. A very simple example could look
97
+ like:
98
+ ```
99
+ module Consolr
100
+ module Runners
101
+ class MyRunner < Runner
102
+ def initialize
103
+ // Set up the runner
104
+ end
105
+
106
+ def can_run? node
107
+ // Should return true if this runner can manage `node`
108
+ end
109
+
110
+ def verify node
111
+ // Verify that the runner can talk to the node. For instance by pinging
112
+ // the ipmi interface.
113
+ end
114
+
115
+ def on node
116
+ // issue command to power node on
117
+ end
118
+
119
+ end
120
+ end
121
+ ```
122
+ All the methods (`on`, `off`) etc. that can be implemented can be seen in the
123
+ `Consolr::Runners::Runner` base class.
124
+
125
+ In order to package it up as a gem the directory structure should look like
126
+
127
+ ```
128
+ .
129
+ └── lib
130
+ └── consolr
131
+ └── runners
132
+ └── myrunner.rb
133
+ ```
134
+ You'll also need a gemspec file at the root.
135
+
84
136
  ## Mailing list
85
137
  http://groups.google.com/group/collins-sm
86
138
 
data/bin/consolr CHANGED
@@ -18,9 +18,13 @@ opt_parser = OptionParser.new do |opt|
18
18
  opt.on('-l', '--log LOG', 'System Event Log (SEL) [list|clear]') { |log| options[:log] = log }
19
19
  opt.on('-o', '--on', 'turn on node') { options[:on] = true }
20
20
  opt.on('-r', '--reboot', 'restart node') { options[:reboot] = true }
21
+ opt.on('-R', '--soft-reboot', 'restart node gracefully') { options[:soft_reboot] = true }
21
22
  opt.on('-s', '--sdr', 'Sensor Data Repository (SDR)') { options[:sdr] = true }
22
23
  opt.on('-t', '--tag ASSET', 'asset tag') { |tag| options[:tag] = tag }
23
24
  opt.on('-x', '--off', 'turn off node') { options[:off] = true }
25
+ opt.on('-X', '--soft-off', 'turn off gracefully (via ACPI)') { options[:soft_off] = true }
26
+ opt.on('-S', '--status', 'print asset power status') { options[:status] = true }
27
+ opt.on('--runner RUNNER', 'specify the runner to use') { |runner| options[:runner] = runner }
24
28
 
25
29
  opt.on_tail('-h', '--help', 'help') { puts opt; exit 0 }
26
30
  opt.on_tail('-v', '--version', 'version') { puts Consolr::VERSION; exit 0 }
@@ -0,0 +1,82 @@
1
+ require 'consolr/runners/runner'
2
+
3
+ module Consolr
4
+ module Runners
5
+ class Ipmitool < Runner
6
+ def initialize config
7
+ @ipmitool = if config.empty?
8
+ '/usr/bin/ipmitool'
9
+ else
10
+ config
11
+ end
12
+ end
13
+
14
+ def can_run? node
15
+ begin
16
+ not (node.ipmi.address.empty? or node.ipmi.username.empty? or node.ipmi.password.empty?)
17
+ rescue
18
+ false
19
+ end
20
+ end
21
+
22
+ def verify node
23
+ Net::Ping::External.new(node.ipmi.address).ping?
24
+ end
25
+
26
+ def console node
27
+ cmd 'sol activate', node
28
+ end
29
+
30
+ def kick node
31
+ cmd 'sol deactivate', node
32
+ end
33
+
34
+ def identify node
35
+ cmd 'chassis identify', node
36
+ end
37
+
38
+ def sdr node
39
+ cmd 'sdr elist all', node
40
+ end
41
+
42
+ def log_list node
43
+ cmd 'sel list', node
44
+ end
45
+
46
+ def log_clear node
47
+ cmd 'sel clear', node
48
+ end
49
+
50
+ def on node
51
+ cmd 'power on', node
52
+ end
53
+
54
+ def off node
55
+ cmd 'power off', node
56
+ end
57
+
58
+ def soft_off node
59
+ cmd 'power soft', node
60
+ end
61
+
62
+ def reboot node
63
+ cmd 'power cycle', node
64
+ end
65
+
66
+ def soft_reboot node
67
+ cmd 'power reset', node
68
+ end
69
+
70
+ def status node
71
+ cmd 'power status', node
72
+ end
73
+
74
+ private
75
+ def cmd action, node
76
+ system("#{@ipmitool} -I lanplus -H #{node.ipmi.address} -U #{node.ipmi.username} -P #{node.ipmi.password} #{action}")
77
+ return $?.exitstatus == 0 ? "SUCCESS" : "FAILED"
78
+ end
79
+ end
80
+ end
81
+ end
82
+
@@ -0,0 +1,64 @@
1
+ require 'net/ping'
2
+
3
+ module Consolr
4
+ module Runners
5
+ class Runner
6
+ def can_run? node
7
+ raise 'can_run? is not implemented for this runner'
8
+ end
9
+
10
+ def verify node
11
+ raise 'verify is not implemented for this runner'
12
+ end
13
+
14
+ def console node
15
+ raise 'console is not implemented for this runner'
16
+ end
17
+
18
+ def kick node
19
+ raise 'kick is not implemented for this runner'
20
+ end
21
+
22
+ def identify node
23
+ raise 'identify is not implemented for this runner'
24
+ end
25
+
26
+ def sdr node
27
+ raise 'sdr is not implemented for this runner'
28
+ end
29
+
30
+ def log_list node
31
+ raise 'log list is not implemented for this runner'
32
+ end
33
+
34
+ def log_clear node
35
+ raise 'log clear is not implemented for this runner'
36
+ end
37
+
38
+ def on node
39
+ raise 'power on is not implemented for this runner'
40
+ end
41
+
42
+ def off node
43
+ raise 'power off is not implemented for this runner'
44
+ end
45
+
46
+ def soft_off node
47
+ raise 'soft_off is not implemented for this runner'
48
+ end
49
+
50
+ def reboot node
51
+ raise 'reboot is not implemented for this runner'
52
+ end
53
+
54
+ def soft_reboot node
55
+ raise 'reboot_soft is not implemented for this runner'
56
+ end
57
+
58
+ def status node
59
+ raise 'status is not implemented for this runner'
60
+ end
61
+ end
62
+ end
63
+ end
64
+
@@ -1,3 +1,3 @@
1
1
  module Consolr
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/consolr.rb CHANGED
@@ -2,10 +2,10 @@ $LOAD_PATH.unshift('./lib/')
2
2
 
3
3
  require 'consolr/version'
4
4
  require 'collins_auth'
5
- require 'net/ping'
6
5
  require 'optparse'
7
6
  require 'yaml'
8
7
 
8
+
9
9
  module Consolr
10
10
  class Console
11
11
 
@@ -19,7 +19,7 @@ module Consolr
19
19
  File.readable?(File.expand_path(conf, __FILE__)) and File.size(File.expand_path(conf, __FILE__)) > 0
20
20
  end
21
21
 
22
- config_params = begin
22
+ @config_params = begin
23
23
  YAML.load(File.open(File.expand_path(config_file, __FILE__)))
24
24
  rescue TypeError => e
25
25
  puts "-------"
@@ -36,33 +36,21 @@ module Consolr
36
36
  puts "------"
37
37
  exit 1
38
38
  end
39
-
40
- begin
41
- @ipmitool_exec = config_params['ipmitool'] # ipmitool absolute path
42
- rescue Exception => e
43
- puts e
44
- puts "-------"
45
- puts "Ensure that the ipmitool's path (#{@ipmitool_exec}) is given in the consolr.yml file and is correct"
46
- puts "-------"
47
- exit 1
48
- end
49
-
39
+ #
50
40
  # Will be ignored for dangerous actions, no matter what, even with --force
51
41
  begin
52
- @dangerous_assets = config_params['dangerous_assets']
42
+ @dangerous_assets = @config_params['dangerous_assets']
53
43
  rescue Exception => e
54
44
  puts e
55
45
  puts "-------"
56
46
  puts "Dangerous Assets -- #{dangrous}"
57
47
  puts "Please make sure dangerous_assets exists and is valid."
58
48
  puts "Supply them in a comma separated list."
59
- puts "-------"
60
- exit 1
61
49
  end
62
50
 
63
51
  # Dangerous actions wont be run in these status, override with --force
64
52
  begin
65
- @dangerous_status = config_params['dangerous_status']
53
+ @dangerous_status = @config_params['dangerous_status']
66
54
  rescue Exception => e
67
55
  puts e
68
56
  puts "-------"
@@ -78,8 +66,6 @@ module Consolr
78
66
  def start options
79
67
  abort("Please pass either the asset tag or hostname") if options[:tag].nil? and options[:hostname].nil?
80
68
 
81
- abort("Cannot find #{@ipmitool_exec}") unless File.exist?(@ipmitool_exec)
82
-
83
69
  dangerous_body = "Dangerous actions: #{dangerous_actions.join(', ')}\n"\
84
70
  "Dangerous status: #{dangerous_status.join(', ')} (override with --force)\n"\
85
71
  "Dangerous assets: #{dangerous_assets.join(', ')} (ignored no matter what, even with --force)"
@@ -103,11 +89,36 @@ module Consolr
103
89
  abort("Please pass either the hostname OR the tag but not both.")
104
90
  end
105
91
 
92
+ if options[:runner].nil?
93
+ runners = load_runners(@config_params.fetch('runners', []))
94
+ # Default to the ipmitool runner for backwards compatibility
95
+ if runners.empty?
96
+ require 'consolr/runners/ipmitool'
97
+ runners = [Consolr::Runners::Ipmitool.new(@config_params.fetch('ipmitool', {}))]
98
+ end
99
+ else
100
+ runners = load_runners([options[:runner]])
101
+ if runners.empty?
102
+ abort('Specified runner could not be loaded. Aborting.')
103
+ end
104
+ end
105
+
106
106
  # match assets like vm-67f5eh, zt-*, etc.
107
107
  nodes = options[:tag] ? (collins.find :tag => options[:tag]) : (collins.find :hostname => options[:hostname])
108
108
  @node = nodes.length == 1 ? nodes.first : abort("Found #{nodes.length} assets, aborting.")
109
109
 
110
- abort("Cannot ping IP #{@node.ipmi.address} (#{@node.tag})") unless Net::Ping::External.new(@node.ipmi.address).ping?
110
+ # select the first runner that support the node
111
+ runner = runners.select {|runner|
112
+ runner.can_run? @node
113
+ }.first
114
+
115
+ if runner.nil?
116
+ abort("No runners available for node #{@node.hostname} (#{@node.tag})")
117
+ end
118
+
119
+ if not runner.verify @node
120
+ abort("Cannot verify asset #{@node.hostname} (#{@node.tag})")
121
+ end
111
122
 
112
123
  selected_dangerous_actions = dangerous_actions.select { |o| options[o] }
113
124
  if dangerous_assets.include?(@node.tag) and selected_dangerous_actions.any?
@@ -121,40 +132,47 @@ module Consolr
121
132
  case
122
133
  when options[:console]
123
134
  puts '--> Opening SOL session (type ~~. to quit)'
124
- puts ipmitool_cmd('sol activate')
135
+ puts runner.console @node
125
136
  when options[:kick]
126
- puts ipmitool_cmd('sol deactivate')
137
+ puts runner.kick @node
127
138
  when options[:identify]
128
- puts ipmitool_cmd('chassis identify')
139
+ puts runner.identify @node
129
140
  when options[:sdr]
130
- puts ipmitool_cmd('sdr elist all')
141
+ puts runner.sdr @node
131
142
  when options[:log] == 'list'
132
- puts ipmitool_cmd('sel list')
143
+ puts runner.log_list @node
133
144
  when options[:log] == 'clear'
134
- puts ipmitool_cmd('sel clear')
145
+ puts runner.log_clear @node
135
146
  when options[:on]
136
- puts ipmitool_cmd('power on')
147
+ puts runner.on @node
137
148
  when options[:off]
138
- puts ipmitool_cmd('power off')
149
+ puts runner.off @node
150
+ when options[:soft_off]
151
+ puts runner.soft_off @node
139
152
  when options[:reboot]
140
- puts ipmitool_cmd('power cycle')
153
+ puts runner.reboot @node
154
+ when options[:soft_reboot]
155
+ puts runner.soft_reboot @node
156
+ when options[:status]
157
+ puts runner.status @node
141
158
  else
142
159
  begin
143
- raise OptionParser::MissingArgument, "specify an action"
144
- rescue OptionParser::MissingArgument => e
145
- puts e
160
+ puts "specify an action"
146
161
  exit 1
147
162
  end
148
163
  end
149
164
  end
150
165
 
151
166
  private
152
-
153
- def ipmitool_cmd action
154
- system("#{@ipmitool_exec} -I lanplus -H #{@node.ipmi.address} -U #{@node.ipmi.username} -P #{@node.ipmi.password} #{action}")
155
- return $?.exitstatus == 0 ? "SUCCESS" : "FAILED"
167
+ def load_runners runners
168
+ runners.map {|runner|
169
+ begin
170
+ require "consolr/runners/#{runner}"
171
+ Consolr::Runners.const_get(runner.capitalize).new @config_params.fetch(runner, {})
172
+ rescue NameError, LoadError => e
173
+ puts "Could not load runner #{runner.capitalize}, skipping."
174
+ end
175
+ }.compact
156
176
  end
157
-
158
177
  end
159
-
160
178
  end
@@ -0,0 +1,9 @@
1
+ # this is a dummy config for the unit tests
2
+ runners:
3
+ - failrunner
4
+ - testrunner
5
+ dangerous_assets:
6
+ - "dangerous-allocated-tag"
7
+ - "dangerous-maintenance-tag"
8
+ dangerous_status:
9
+ - "Allocated"
data/spec/consolr_spec.rb CHANGED
@@ -26,6 +26,40 @@ describe Consolr::Console do
26
26
  expect { @console.start({:hostname => 'hostname-with-multiple-assets.dc.net'}) }.to raise_error(SystemExit, /Found \d+ assets/)
27
27
  end
28
28
 
29
+ it 'loads ipmitool as the default runner' do
30
+ expect_any_instance_of(Consolr::Runners::Ipmitool).to receive(:initialize)
31
+ @console.start({:tag => 'safe-allocated-tag', :on => true})
32
+ end
33
+
34
+ it 'prints an error when passed an unknown runner' do
35
+ expect {
36
+ @console.start({:tag => 'safe-allocated-tag', :on => true, :runner => 'bogusrunner'})
37
+ }.to raise_error SystemExit
38
+ end
39
+
40
+ it 'loads a custom runner when specified' do
41
+ $LOAD_PATH << 'spec/mocks'
42
+ require 'consolr/runners/testrunner'
43
+ expect_any_instance_of(Consolr::Runners::Testrunner).to receive(:initialize)
44
+ @console.start({:tag => 'safe-allocated-tag', :on => true, :runner => 'testrunner'})
45
+ end
46
+
47
+ it 'selects a runner that supports the node' do
48
+ $LOAD_PATH << 'spec/mocks'
49
+ require 'consolr/runners/testrunner'
50
+ require 'consolr/runners/failrunner'
51
+ old_config = ENV['CONSOLR_CONFIG']
52
+ ENV['CONSOLR_CONFIG'] = '../../spec/configs/consolr_runners_rspec.yml'
53
+
54
+ console = Consolr::Console.new
55
+ expect_any_instance_of(Consolr::Runners::Failrunner).to receive(:can_run?).
56
+ and_return(false)
57
+ expect_any_instance_of(Consolr::Runners::Testrunner).to receive(:on)
58
+ console.start({:tag => 'safe-allocated-tag', :on => true})
59
+
60
+ ENV['CONSOLR_CONFIG'] = old_config
61
+ end
62
+
29
63
  safe_boolean_actions = {:console => "--> Opening SOL session (type ~~. to quit)\nsol activate", :kick => 'sol deactivate', :identify => 'chassis identify', :sdr => 'sdr elist all', :on => 'power on'}
30
64
  dangerous_boolean_actions = {:off => 'power off', :reboot => 'power cycle'}
31
65
 
@@ -0,0 +1,13 @@
1
+ module Consolr
2
+ module Runners
3
+ class Failrunner < Runner
4
+ def initialize config
5
+ true
6
+ end
7
+
8
+ def can_run? node
9
+ false
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Consolr
2
+ module Runners
3
+ class Testrunner < Runner
4
+ def initialize config
5
+ true
6
+ end
7
+
8
+ def can_run? node
9
+ true
10
+ end
11
+
12
+ def verify node
13
+ true
14
+ end
15
+
16
+ def on node
17
+ 'SUCCESS'
18
+ end
19
+
20
+ def power node
21
+ 'SUCCESS'
22
+ end
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consolr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Richard
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-17 00:00:00.000000000 Z
12
+ date: 2016-03-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: collins_auth
@@ -118,9 +118,12 @@ files:
118
118
  - bin/consolr
119
119
  - consolr.gemspec
120
120
  - lib/consolr.rb
121
+ - lib/consolr/runners/ipmitool.rb
122
+ - lib/consolr/runners/runner.rb
121
123
  - lib/consolr/version.rb
122
124
  - spec/configs/collins_rspec.yml
123
125
  - spec/configs/consolr_rspec.yml
126
+ - spec/configs/consolr_runners_rspec.yml
124
127
  - spec/consolr_spec.rb
125
128
  - spec/fixtures/assets/dangerous-allocated-tag.yml
126
129
  - spec/fixtures/assets/dangerous-maintenance-tag.yml
@@ -128,6 +131,8 @@ files:
128
131
  - spec/fixtures/assets/safe-maintenance-tag.yml
129
132
  - spec/mocks/collins.rb
130
133
  - spec/mocks/collins_auth.rb
134
+ - spec/mocks/consolr/runners/failrunner.rb
135
+ - spec/mocks/consolr/runners/testrunner.rb
131
136
  - spec/mocks/ping.rb
132
137
  - spec/rspec_ipmi
133
138
  - spec/spec_helper.rb
@@ -151,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
156
  version: '0'
152
157
  requirements: []
153
158
  rubyforge_project:
154
- rubygems_version: 2.4.5
159
+ rubygems_version: 2.4.6
155
160
  signing_key:
156
161
  specification_version: 4
157
162
  summary: consolr is a pure ruby wrapper over IPMI to allow Out of Band communiation
@@ -159,6 +164,7 @@ summary: consolr is a pure ruby wrapper over IPMI to allow Out of Band communiat
159
164
  test_files:
160
165
  - spec/configs/collins_rspec.yml
161
166
  - spec/configs/consolr_rspec.yml
167
+ - spec/configs/consolr_runners_rspec.yml
162
168
  - spec/consolr_spec.rb
163
169
  - spec/fixtures/assets/dangerous-allocated-tag.yml
164
170
  - spec/fixtures/assets/dangerous-maintenance-tag.yml
@@ -166,6 +172,8 @@ test_files:
166
172
  - spec/fixtures/assets/safe-maintenance-tag.yml
167
173
  - spec/mocks/collins.rb
168
174
  - spec/mocks/collins_auth.rb
175
+ - spec/mocks/consolr/runners/failrunner.rb
176
+ - spec/mocks/consolr/runners/testrunner.rb
169
177
  - spec/mocks/ping.rb
170
178
  - spec/rspec_ipmi
171
179
  - spec/spec_helper.rb