consolr 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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