moob 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/moob CHANGED
@@ -5,45 +5,95 @@ require 'moob'
5
5
 
6
6
  options = {
7
7
  :action => :jnlp,
8
- :type => :idrac6,
9
- :machines => []
8
+ :type => :auto,
9
+ :machines => [],
10
+ :password => ENV['password']
10
11
  }
11
12
 
12
13
  OptionParser.new do |opts|
13
- opts.on '-l', '--list',
14
- 'List LOM types' do
15
- Moob::TYPES.each do |k, v|
16
- puts "#{k} (#{v.name})"
17
- end
18
- exit 0
19
- end
20
14
  opts.on '-t', '--type t',
21
- 'LOM type (idrac6 by default)' do |t|
15
+ 'LOM type, \'auto\' for autodetection, \'list\' to list',
16
+ 'Defaults to auto' do |t|
17
+ if t == 'list'
18
+ Moob::TYPES.each do |sym, klass|
19
+ puts "#{sym} (#{klass.name})"
20
+ end
21
+ exit 0
22
+ else
22
23
  options[:type] = t.to_sym
24
+ end
25
+ end
26
+
27
+ opts.on '-a', '--action t',
28
+ 'Action to perform, \'list\' to list',
29
+ 'Defaults to jnlp' do |a|
30
+ if a == 'list'
31
+ Moob::TYPES.each do |sym, klass|
32
+ if klass.actions
33
+ puts "#{sym}:"
34
+ klass.actions.each do |name, descr|
35
+ puts " #{name}: #{descr}"
36
+ end
37
+ else
38
+ puts "#{sym} doesn't have any actions"
39
+ end
40
+ end
41
+ exit 0
42
+ else
43
+ options[:action] = a.to_sym
44
+ end
23
45
  end
46
+
24
47
  opts.on '-u', '--username u',
25
- 'LOM username' do |u|
26
- options[:username] = u
48
+ 'LOM username',
49
+ 'Defaults to the model\'s default if known' do |u|
50
+ options[:username] = u
27
51
  end
52
+
28
53
  opts.on '-p', '--password p',
29
- 'LOM password' do |p|
30
- options[:password] = p
54
+ 'LOM password',
55
+ 'Defaults to the model\'s default if known',
56
+ 'Use the environment variable PASSWORD instead!' do |p|
57
+ options[:password] = p
31
58
  end
32
59
 
33
60
  opts.on '-m', '--machines a,b,c', Array,
34
- 'Comma-separated list of machines' do |m|
35
- options[:machines] = m
61
+ 'Comma-separated list of LOM hostnames' do |m|
62
+ options[:machines] = m
63
+ end
64
+
65
+ opts.on '-v', '--verbose' do
66
+ $VERBOSE = true
36
67
  end
37
68
  end.parse!
38
69
 
39
70
  if options[:machines].empty?
40
- $stderr.puts 'No machines selected.'
71
+ $stderr.puts 'No LOMs selected.'
41
72
  exit 1
42
73
  end
43
74
 
44
- options[:machines].each do |h|
45
- case options[:action]
46
- when :jnlp
47
- Moob.start_jnlp options[:type], h, options
75
+ begin
76
+ options[:machines].each do |h|
77
+ lom = Moob.lom options[:type], h, options
78
+
79
+ puts "Trying to authenticate to #{h}..." if $VERBOSE
80
+ lom.authenticate
81
+ puts "Authenticated on #{h}." if $VERBOSE
82
+
83
+ case options[:action]
84
+ when :jnlp
85
+ Moob.start_jnlp lom
86
+ else
87
+ res = lom.send options[:action]
88
+ puts res if res
89
+ end
48
90
  end
91
+ puts 'There might be a delay before Java Web Start shows up...' if options[:action] == :jnlp
92
+
93
+ rescue Exception => e
94
+ $stderr.puts "\033[31mFailure (#{e.class}): #{e}\033[0m\n\n" \
95
+ "Backtrace to provide in any support request:\033[2;34m"
96
+ $stderr.puts e.backtrace
97
+ $stderr.puts "\033[0m"
98
+ exit 1
49
99
  end
@@ -1,9 +1,14 @@
1
- require 'socket'
2
- require 'cgi'
3
- require 'patron'
4
-
5
1
  module Moob
6
- VERSION = [0,1,1]
2
+ VERSION = [0,2,0]
3
+
4
+ class ResponseError < Exception
5
+ def initialize response
6
+ @response = response
7
+ end
8
+ def to_s
9
+ "#{@response.url} failed (status #{@response.status})"
10
+ end
11
+ end
7
12
 
8
13
  autoload :BaseLom, 'moob/baselom.rb'
9
14
  autoload :Idrac6, 'moob/idrac6.rb'
@@ -16,33 +21,36 @@ module Moob
16
21
  :sun => SunILom
17
22
  }
18
23
 
19
- class ResponseError < Exception
20
- def initialize response
21
- @response = response
22
- end
23
- def to_s
24
- "#{@response.url} failed (status #{@response.status})"
24
+ def self.lom type, hostname, options = {}
25
+ case type
26
+ when :auto
27
+ TYPES.find do |sym, klass|
28
+ puts "Trying type #{sym}..." if $VERBOSE
29
+ lom = klass.new hostname, options
30
+ if lom.detect
31
+ puts "Type #{sym} detected." if $VERBOSE
32
+ return lom
33
+ end
34
+ false
35
+ end
36
+ raise RuntimeError.new "Couldn't detect a known LOM type"
37
+ else
38
+ return TYPES[type].new hostname, options
25
39
  end
26
40
  end
27
41
 
28
- def self.start_jnlp type, hostname, options = {}
29
-
30
- lom = TYPES[type].new hostname, options
31
- jnlp = lom.authenticate.jnlp
42
+ def self.start_jnlp lom
43
+ jnlp = lom.jnlp
32
44
 
33
45
  unless jnlp[/<\/jnlp>/]
34
46
  raise RuntimeError.new "Invalid JNLP file (\"#{jnlp}\")"
35
47
  end
36
48
 
37
- filepath = "/tmp/#{hostname}.jnlp"
49
+ filepath = "/tmp/#{lom.hostname}.jnlp"
38
50
  File.open filepath, 'w' do |f|
39
51
  f.write jnlp
40
52
  end
41
53
 
42
- system "javaws #{filepath}"
43
- end
44
-
45
- def self.cmdline
46
- start_jnlp :megatrends, 'brenda.lom.sto.spotify.net'
54
+ raise Exception.new "javaws failed" unless system "javaws #{filepath} &"
47
55
  end
48
56
  end
@@ -1,5 +1,10 @@
1
+ require 'socket'
2
+ require 'cgi'
3
+ require 'patron'
4
+ require 'set'
5
+
1
6
  class Moob::BaseLom
2
- @@name = 'Unknown'
7
+ @name = "Unknown"
3
8
 
4
9
  def initialize hostname, options = {}
5
10
  @hostname = hostname
@@ -13,22 +18,24 @@ class Moob::BaseLom
13
18
  @session.connect_timeout = 10_000
14
19
  @session.timeout = 10_000
15
20
  @session.insecure = true
16
- @session.ignore_content_length = true
17
21
  end
18
22
 
19
- def jnlp
20
- raise new NoMethodError
23
+ attr_reader :hostname, :username
24
+
25
+ def detect
26
+ false
21
27
  end
22
28
 
23
29
  def self.name
24
- @@name
30
+ @name
31
+ end
32
+
33
+ def self.actions
34
+ @actions
25
35
  end
26
36
 
27
- protected
28
- # Get rid of security checks.
29
- # Might break some features,
30
- # please let me know if that's the case!
31
- def desecurize_jnlp jnlp
32
- jnlp.sub /<security>.*<\/security>/m, ''
37
+ def self.action sym, descr
38
+ @actions ||= {}
39
+ @actions[sym] = descr
33
40
  end
34
- end
41
+ end
@@ -1,10 +1,75 @@
1
- class Moob::Idrac6 < Moob::BaseLom
2
- @@name = 'Dell iDrac 6'
1
+ module Moob
2
+ class Idrac6 < BaseLom
3
+ @name = 'Dell iDrac 6'
4
+
5
+ INFO_FIELDS = %w{
6
+ sysDesc
7
+ biosVer
8
+ svcTag
9
+ expSvcCode
10
+ hostName
11
+ osName
12
+ osVersion
13
+ sysRev
14
+ LCCfwVersion
15
+ recoveryAction
16
+ initCountdown
17
+ presentCountdown
18
+ datetime
19
+ fwVersion
20
+ fwUpdated
21
+ hwVersion
22
+ macAddr
23
+ v4Enabled
24
+ v4IPAddr
25
+ v4Gateway
26
+ v4NetMask
27
+ v4DHCPEnabled
28
+ v4DHCPServers
29
+ v4DNS1
30
+ v4DNS2
31
+ v6Enabled
32
+ v6Addr
33
+ v6Gateway
34
+ v6DHCPEnabled
35
+ v6LinkLocal
36
+ v6Prefix
37
+ v6SiteLocal
38
+ v6SiteLocal3
39
+ v6SiteLocal4
40
+ v6SiteLocal5
41
+ v6SiteLocal6
42
+ v6SiteLocal7
43
+ v6SiteLocal8
44
+ v6SiteLocal9
45
+ v6SiteLocal10
46
+ v6SiteLocal11
47
+ v6SiteLocal12
48
+ v6SiteLocal13
49
+ v6SiteLocal14
50
+ v6SiteLocal15
51
+ racName
52
+ v6DHCPServers
53
+ v6DNS1
54
+ v6DNS2
55
+ NicEtherMac1
56
+ NicEtherMac2
57
+ NicEtherMac3
58
+ NicEtherMac4
59
+ NiciSCSIMac1
60
+ NiciSCSIMac2
61
+ NiciSCSIMac3
62
+ NiciSCSIMac4
63
+ NicEtherVMac1
64
+ NicEtherVMac2
65
+ NicEtherVMac3
66
+ NicEtherVMac4
67
+ }
3
68
 
4
69
  def initialize hostname, options = {}
5
70
  super hostname, options
6
- @username ||= 'root'
7
- @password ||= 'calvin'
71
+ @username ||= 'root'
72
+ @password ||= 'calvin'
8
73
  end
9
74
 
10
75
  def authenticate
@@ -16,34 +81,96 @@ class Moob::Idrac6 < Moob::BaseLom
16
81
  "user=#{@username}&password=#{@password}"
17
82
  raise ResponseError.new auth unless auth.status == 200
18
83
 
19
- result_match = auth.body.match /<authResult>([^<]+)<\/authResult>/
20
- raise Exception.new 'Cannot find auth result' unless result_match
21
- result_code = result_match[1]
22
- unless result_code == "0"
23
- raise Exception.new "Auth failed with: \"#{auth.body}\""
24
- end
84
+ auth.body =~ /<authResult>([^<]+)<\/authResult>/
85
+ raise Exception.new 'Cannot find auth result' unless $&
86
+ raise Exception.new "Auth failed with: \"#{auth.body}\"" unless $1 == "0"
25
87
  return self
26
88
  end
27
89
 
90
+ def detect
91
+ begin
92
+ home = @session.get 'login.html'
93
+ home.body =~ /Integrated Dell Remote Access Controller 6/
94
+ rescue
95
+ false
96
+ end
97
+ end
98
+
99
+ action :jnlp, 'Remote control'
28
100
  def jnlp
29
101
  idx = @session.get 'index.html'
30
102
  raise ResponseError.new idx unless idx.status == 200
31
103
 
32
- name_match = idx.body.match /var DnsName += +"([^"]+)"/
33
- raise Exception.new 'Couldn\'t find the DNS name' unless name_match
34
- dns_name = name_match[1]
104
+ idx.body =~ /var DnsName += +"([^"]+)"/
105
+ raise Exception.new "Couldn't find the DNS name" unless $&
106
+ dns_name = $1
35
107
 
36
- sys_match = idx.body.match /var sysNameStr += +"([^"]+)"/
37
- raise Exception.new 'Couldn\'t find the system name' unless name_match
38
- sys_name = sys_match[1] # eg PowerEdge R610
108
+ idx.body =~ /var sysNameStr += +"([^"]+)"/
109
+ raise Exception.new "Couldn't find the system name" unless $&
110
+ sys_name = $1 # eg PowerEdge R610
39
111
 
40
- # eg escaped "idrac-D4MHZ4J, PowerEdge R610, User:root"
112
+ # eg escaped "idrac-A1BCD2E, PowerEdge R610, User:root"
41
113
  title = CGI::escape "#{dns_name}, #{sys_name}, User:#{@username}"
42
- path = "viewer.jnlp(#{@hostname}@0@#{title}@#{Time.now.to_i * 1000})"
43
114
 
44
- viewer = @session.get path
115
+ viewer = @session.get "viewer.jnlp(#{@hostname}@0@#{title}@#{Time.now.to_i * 1000})"
45
116
  raise ResponseError.new viewer unless viewer.status == 200
46
117
 
47
- return desecurize_jnlp viewer.body
118
+ return viewer.body
119
+ end
120
+
121
+ def power_action action
122
+ req = @session.post "data?set=pwState:#{action}", {}
123
+ raise ResponseError.new req unless req.status == 200
124
+ raise Exception.new 'The answer looks wrong' unless req.body =~ /<status>ok<\/status>/
125
+ return nil
126
+ end
127
+
128
+ action :poff, 'Power Off System'
129
+ action :pon, 'Power On System'
130
+ action :pcycle, 'Power Cycle System (cold boot)'
131
+ action :reboot, 'Reset System (warm boot)'
132
+ action :nmi, 'NMI (Non-Masking Interrupt)'
133
+ action :shudown, 'Graceful Shutdown'
134
+
135
+ def poff; power_action 0; end
136
+ def pon; power_action 1; end
137
+ def pcycle; power_action 2; end
138
+ def reboot; power_action 3; end
139
+ def nmi; power_action 4; end
140
+ def shutdown; power_action 5; end
141
+
142
+ def get_infos keys
143
+ infos = @session.post "data?get=#{keys.join(',')}", {}
144
+
145
+ raise ResponseError.new infos unless infos.status == 200
146
+ raise Exception.new "The status isn't OK" unless infos.body =~ /<status>ok<\/status>/
147
+
148
+ return Hash[keys.collect do |k|
149
+ if infos.body =~ /<#{k}>(.*?)<\/#{k}>/
150
+ [k, $1]
151
+ else
152
+ [k, nil]
153
+ end
154
+ end]
155
+ end
156
+
157
+ action :pstatus, 'Power status'
158
+ def pstatus
159
+ case get_infos(['pwState'])['pwState']
160
+ when '0'
161
+ return :off
162
+ when '1'
163
+ return :on
164
+ else
165
+ return nil
166
+ end
167
+ end
168
+
169
+ action :infos, 'Get system information'
170
+ def infos
171
+ return get_infos(INFO_FIELDS).collect do |k,v|
172
+ "#{k}: #{v}"
173
+ end.join "\n"
48
174
  end
49
- end
175
+ end
176
+ end
@@ -1,12 +1,13 @@
1
- class Moob::Megatrends < Moob::BaseLom
2
- @@name = 'American Megatrends'
1
+ module Moob
2
+ class Megatrends < BaseLom
3
+ @name = 'American Megatrends'
3
4
 
4
5
  def initialize hostname, options = {}
5
6
  super hostname, options
6
7
  @username ||= 'root'
7
8
  @password ||= 'superuser'
8
9
  begin
9
- @ip = Socket.getaddrinfo(hostname, nil)[0][2]
10
+ @ip = Socket.getaddrinfo(hostname, nil)[0][3]
10
11
  rescue
11
12
  raise Exception.new "Couldn't resolve \"#{hostname}\""
12
13
  end
@@ -19,19 +20,65 @@ class Moob::Megatrends < Moob::BaseLom
19
20
 
20
21
  raise ResponseError.new auth unless auth.status == 200
21
22
 
22
- cookie_match = auth.body.match /'SESSION_COOKIE' *: *'([^']+)'/
23
- unless cookie_match
24
- raise Exception.new "Couldn't find auth cookie in \"#{auth.body}\""
25
- end
23
+ auth.body =~ /'SESSION_COOKIE' *: *'([^']+)'/
24
+ raise Exception.new "Couldn't find auth cookie in \"#{auth.body}\"" unless $&
26
25
 
27
- @cookie = "test=1; path=/; SessionCookie=#{cookie_match[1]}"
26
+ @cookie = "test=1; path=/; SessionCookie=#{$1}"
28
27
  return self
29
28
  end
30
29
 
30
+ def detect
31
+ begin
32
+ home = @session.get 'page/login.html'
33
+ home.body =~ /\.\.\/res\/banner_right.png/
34
+ rescue
35
+ false
36
+ end
37
+ end
38
+
39
+ action :jnlp, 'Remote control'
31
40
  def jnlp
41
+ @session.ignore_content_length = true
32
42
  viewer = @session.get 'Java/jviewer.jnlp', { 'Cookie' => @cookie }
33
43
  raise ResponseError.new viewer unless viewer.status == 200
34
44
 
35
- return desecurize_jnlp viewer.body
45
+ return viewer.body
46
+ end
47
+
48
+ def power_action action
49
+ req = @session.post 'rpc/hostctl.asp',
50
+ { 'WEBVAR_POWER_CMD' => action },
51
+ { 'Cookie' => @cookie }
52
+ raise ResponseError.new req unless req.status == 200
53
+ unless req.body =~ /WEBVAR_STRUCTNAME_HL_POWERSTATUS/
54
+ raise Exception.new 'The answer looks wrong'
55
+ end
56
+ return nil
36
57
  end
58
+
59
+ action :poff, 'Power Off'
60
+ action :pon, 'Power On'
61
+ action :pcycle, 'Power Cycle'
62
+ action :preset, 'Power Reset'
63
+ action :soft_poff, 'Soft Power Off'
64
+ def poff; power_action 0; end
65
+ def pon; power_action 1; end
66
+ def pcycle; power_action 2; end
67
+ def preset; power_action 3; end
68
+ def soft_poff; power_action 5; end
69
+
70
+ action :pstatus, 'Power status'
71
+ def pstatus
72
+ status = @session.get 'rpc/hoststatus.asp',
73
+ { 'Cookie' => @cookie }
74
+ raise ResponseError.new status unless status.status == 200
75
+ raise Exception.new 'Couldn\'t read the state' unless status.body =~ /'JF_STATE' : (.),/
76
+ case $1
77
+ when '0'
78
+ return :off
79
+ when '1'
80
+ return :on
81
+ end
82
+ end
83
+ end
37
84
  end
@@ -1,5 +1,6 @@
1
- class Moob::SunILom < Moob::BaseLom
2
- @@name = 'Sun Integrated Lights Out Manager'
1
+ module Moob
2
+ class SunILom < BaseLom
3
+ @name = 'Sun Integrated Lights Out Manager'
3
4
 
4
5
  def initialize hostname, options = {}
5
6
  super hostname, options
@@ -15,19 +16,38 @@ class Moob::SunILom < Moob::BaseLom
15
16
 
16
17
  raise ResponseError.new auth unless auth.status == 200
17
18
 
18
- cookie_match = auth.body.match /SetWebSessionString\("([^"]+)","([^"]+)"\);/
19
- unless cookie_match
20
- raise Exception.new "Couldn't find session cookie in \"#{auth.body}\""
19
+ if auth.body =~ /\/iPages\/i_login.asp\?msg=([^"])+"/
20
+ error = "code #{$1}"
21
+ error_page = @session.get "iPages/i_login.asp?msg=#{$1}"
22
+ if error_page.body =~ /<div class="AlrtErrTxt">(.*?)<\/div>/
23
+ error = "\"#{$1.gsub /<[^>]+>/, ''}\""
24
+ end
25
+
26
+ raise Exception.new "Auth failed (#{error})"
21
27
  end
22
28
 
23
- @cookie = "#{cookie_match[1]}=#{cookie_match[2]}; langsetting=EN"
29
+ auth.body =~ /SetWebSessionString\("([^"]+)","([^"]+)"\);/
30
+ raise Exception.new "Couldn't find session cookie in \"#{auth.body}\"" unless $&
31
+
32
+ @cookie = "#{$1}=#{$2}; langsetting=EN"
24
33
  return self
25
34
  end
26
35
 
36
+ action :jnlp, 'Remote control'
27
37
  def jnlp
28
38
  viewer = @session.get 'cgi-bin/jnlpgenerator-8', { 'Cookie' => @cookie }
29
39
  raise ResponseError.new viewer unless viewer.status == 200
30
40
 
31
41
  return viewer.body
32
42
  end
43
+
44
+ def detect
45
+ begin
46
+ home = @session.get 'iPages/i_login.asp'
47
+ home.body =~ /Sun\(TM\) Sun Integrated Lights Out Manager/
48
+ rescue
49
+ false
50
+ end
51
+ end
52
+ end
33
53
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moob
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 23
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 1
8
- - 1
9
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Pierre Carrier
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2011-07-25 00:00:00 +02:00
18
+ date: 2011-07-30 00:00:00 +02:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,11 +26,12 @@ dependencies:
25
26
  requirements:
26
27
  - - ~>
27
28
  - !ruby/object:Gem::Version
29
+ hash: 19
28
30
  segments:
29
31
  - 0
30
32
  - 4
31
- - 13
32
- version: 0.4.13
33
+ - 14
34
+ version: 0.4.14
33
35
  type: :runtime
34
36
  version_requirements: *id001
35
37
  description: Control systems using Web-based out-of-band managers without a browser
@@ -51,8 +53,8 @@ files:
51
53
  - COPYING
52
54
  has_rdoc: true
53
55
  homepage: http://github.com/pcarrier/moob
54
- licenses: []
55
-
56
+ licenses:
57
+ - Public-domain-like
56
58
  post_install_message:
57
59
  rdoc_options: []
58
60
 
@@ -63,6 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
65
  requirements:
64
66
  - - ">="
65
67
  - !ruby/object:Gem::Version
68
+ hash: 3
66
69
  segments:
67
70
  - 0
68
71
  version: "0"
@@ -71,6 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
74
  requirements:
72
75
  - - ">="
73
76
  - !ruby/object:Gem::Version
77
+ hash: 31
74
78
  segments:
75
79
  - 1
76
80
  - 2
@@ -79,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
83
  requirements: []
80
84
 
81
85
  rubyforge_project: moob
82
- rubygems_version: 1.3.7
86
+ rubygems_version: 1.6.2
83
87
  signing_key:
84
88
  specification_version: 3
85
89
  summary: Manage Out-Of-Band!