ruby-iscsiadm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig ADDED
Binary file
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 0.0.1 / 2008-05-28
2
+
3
+ * Initial release
4
+
5
+ * Birthday!
6
+ * Test release
data/Manifest.txt ADDED
@@ -0,0 +1,17 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ README_SUDO.txt
5
+ Rakefile
6
+ Todo.txt
7
+ examples/example_session.rb
8
+ examples/show_nodes.rb
9
+ lib/iscsiadm.rb
10
+ lib/iscsiadm/common.rb
11
+ lib/iscsiadm/external.rb
12
+ lib/iscsiadm/iscsiadm.rb
13
+ lib/iscsiadm/node.rb
14
+ lib/iscsiadm/portal.rb
15
+ lib/iscsiadm/proc.rb
16
+ lib/iscsiadm/session.rb
17
+ test/test_iscsiadm.rb
data/README.txt ADDED
@@ -0,0 +1,51 @@
1
+ = ruby-iscsiadm
2
+
3
+ * http://ruby-iscsiadm.rubyforge.org
4
+ * mailto:matt@bravenet.com
5
+
6
+ == DESCRIPTION:
7
+
8
+ This is a wrapper for the open-iscsi administration utility, iscsiadm. This is
9
+ a simple binary wrapper and considered a temporary solution until a proper api
10
+ is finished as per http://thread.gmane.org/gmane.linux.iscsi.open-iscsi/422
11
+
12
+ == FEATURES/PROBLEMS:
13
+
14
+ * Not finished, is that a problem?
15
+
16
+ == SYNOPSIS:
17
+
18
+ FIX (code sample of usage)
19
+
20
+ == REQUIREMENTS:
21
+
22
+ * popen4
23
+
24
+ == INSTALL:
25
+
26
+ * sudo gem install ruby-iscsiadm
27
+
28
+ == LICENSE:
29
+
30
+ (The MIT License)
31
+
32
+ Copyright (c) 2008 Matthew Kent, Bravenet Web Services Inc.
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining
35
+ a copy of this software and associated documentation files (the
36
+ 'Software'), to deal in the Software without restriction, including
37
+ without limitation the rights to use, copy, modify, merge, publish,
38
+ distribute, sublicense, and/or sell copies of the Software, and to
39
+ permit persons to whom the Software is furnished to do so, subject to
40
+ the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be
43
+ included in all copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
46
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
48
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
49
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
50
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
51
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README_SUDO.txt ADDED
@@ -0,0 +1,10 @@
1
+ Want to run this without using root?
2
+
3
+ 1) Create an unprivileged user, preferably with the shell set to /sbin/nologin
4
+ 2) Add user to sudoers, eg:
5
+
6
+ iscsidev ALL = NOPASSWD: /sbin/iscsiadm
7
+
8
+ 3) Pass sudo in with the binary, eg:
9
+
10
+ Iscsiadm.new(:command => "/usr/bin/sudo /sbin/iscsiadm")
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $:.unshift(File.dirname(__FILE__) + "/lib")
6
+ require 'iscsiadm'
7
+
8
+ Hoe.new('ruby-iscsiadm', IscsiadmWrapper::VERSION) do |p|
9
+ p.developer('Matthew Kent', 'matt@bravenet.com')
10
+ p.extra_deps << ['open4', '>= 0.9.6']
11
+ p.remote_rdoc_dir = ''
12
+ end
13
+
14
+ # vim: syntax=Ruby
data/Todo.txt ADDED
@@ -0,0 +1,11 @@
1
+ = to do
2
+
3
+ == 0.1.0
4
+ * don't bother wrapping complex operations, just provide a standard
5
+ iscsiadm.raw('--login etc'), that traps errors to do work at this level for
6
+ now.
7
+ * proper handling of interfaces
8
+ * implement session --stats
9
+ * proper documentation
10
+ * basic test suite
11
+ * compatibility check and warning, pass flag to shutup
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'iscsiadm'
6
+
7
+ iscsi = IscsiadmWrapper::Iscsiadm.new(:command => "/usr/bin/sudo /sbin/iscsiadm", :debug => true)
8
+
9
+ iscsi.discover(:ip => "20.20.20.20").each do |n|
10
+ puts n
11
+ if n.target =~ /shared.1/
12
+ p iscsi.show_record(:node => n, :name => 'node.session.auth.authmethod')
13
+ p iscsi.update_record(:node => n, :name => 'node.session.auth.authmethod', :value => 'CHAP')
14
+ p iscsi.update_record(:node => n, :name => 'node.session.auth.username', :value => 'shareddata')
15
+ p iscsi.update_record(:node => n, :name => 'node.session.auth.password', :value => 'xxx')
16
+ p iscsi.show_record(:node => n, :name => 'node.session.auth.authmethod')
17
+ p iscsi.login(:node => n)
18
+ p iscsi.logout(:node => n)
19
+ p iscsi.show_config(:node => n)
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'iscsiadm'
6
+
7
+ iscsi = IscsiadmWrapper::Iscsiadm.new(:command => "/usr/bin/sudo /sbin/iscsiadm")
8
+
9
+ puts "nodes"
10
+ iscsi.list_nodes.each do |n|
11
+ puts "- #{n.target}"
12
+ end
13
+
14
+ puts "\nsessions"
15
+ iscsi.list_sessions.each do |n|
16
+ puts "- #{n.target}"
17
+ end
data/lib/iscsiadm.rb ADDED
@@ -0,0 +1,294 @@
1
+ # FIXME: proper rdoc summary here
2
+ #
3
+ # This is a wrapper for the open-iscsi administration utility, iscsiadm.
4
+ #
5
+ # Overview:
6
+ #
7
+ # The basic iscsiadm on disk is structure is
8
+ # - discovery (/var/lib/iscsi/send_targets on CentOS)
9
+ # portals which have been scanned
10
+ # - node (/var/lib/iscsi/send_targets on CentOS)
11
+ # settings records containing target,ip,and some params
12
+
13
+ require 'iscsiadm/common'
14
+ require 'iscsiadm/portal'
15
+ require 'iscsiadm/node'
16
+ require 'iscsiadm/session'
17
+
18
+ module IscsiadmWrapper
19
+ VERSION = '0.0.1'
20
+ COMPAT_VERSION = '2.0-865'
21
+
22
+ class Iscsiadm
23
+ DEFAULT_COMMAND = '/sbin/iscsiadm'
24
+
25
+ DEFAULT_TYPE = 'sendtargets'
26
+ DEFAULT_HOST = 'localhost'
27
+ DEFAULT_PORT = 3260
28
+ DEFAULT_INTERFACE = 1
29
+
30
+ COMMANDS = {
31
+ # misc
32
+ :version => { :cmd => "%s --version",
33
+ :filter => %r{^iscsiadm version (.*)$} },
34
+ # discovery mode
35
+ :discover => { :cmd => "%s --mode discovery --type %s --portal %s:%s",
36
+ :filter => %r{^.* (.*)$} },
37
+ :list_portals => { :cmd => "%s --mode discovery",
38
+ :filter => %r{^(.*) via (.*)$} },
39
+ # node mode
40
+ :login => { :cmd => "%s --mode node --target %s --portal %s --login",
41
+ :filter => %r{^iscsiadm version (.*)$} },
42
+ :logout => { :cmd => "%s --mode node --target %s --portal %s --logout",
43
+ :filter => %r{^iscsiadm version (.*)$} },
44
+ :list_nodes => { :cmd => "%s --mode node",
45
+ :filter => %r{^(.*) (.*)$} },
46
+ # session mode
47
+ :list_sessions => { :cmd => "%s --mode session",
48
+ :filter => %r{^tcp: \[(\d+)\] (.*) (.*)$} },
49
+ # config db
50
+ :show_config => { :cmd => "%s --mode node --target %s --portal %s --op show",
51
+ :filter => %r{^(.*) = (.*)$} },
52
+ :new_record => { :cmd => "%s --mode node --target %s --portal %s --op new --name %s --value %s",
53
+ :filter => %r{^(.*)$} },
54
+ :delete_record => { :cmd => "%s --mode node --target %s --portal %s --op delete --name %s",
55
+ :filter => %r{^(.*)$} },
56
+ :update_record => { :cmd => "%s --mode node --target %s --portal %s --op update --name %s --value %s",
57
+ :filter => %r{^(.*)$} },
58
+ # iscsiadm doesn't support this one really, see below
59
+ :show_record => { :cmd => "%s --mode node --target %s --portal %s --op show --name %s",
60
+ :filter => %r{^.* = (.*)$} },
61
+ }
62
+
63
+ def initialize(args = {})
64
+ @command = args[:command] || DEFAULT_COMMAND
65
+ @use_unsupported_version = args[:use_unsupported_version] || false
66
+ @debug = args[:debug] || false
67
+
68
+ puts "Running in debug mode, outputting commands to stdout." if @debug
69
+ end
70
+
71
+ def refresh_all
72
+ refresh_sessions
73
+ refresh_nodes
74
+ refresh_portals
75
+ end
76
+ def refresh_sessions; @sessions = list_sessions; end
77
+ def refresh_nodes; @nodes = list_nodes; end
78
+ def refresh_portals; @portals = list_portals; end
79
+ def sessions; refresh_sessions; @sessions; end
80
+ def nodes; refresh_nodes; @nodes; end
81
+ def portals; refresh_portals; @portals; end
82
+
83
+ def session(args = {})
84
+ target = args[:node].target
85
+ portal = args[:node].portal
86
+
87
+ refresh_sessions
88
+
89
+ @sessions.each do |s|
90
+ return s if s.target == target && s.portal == portal
91
+ end
92
+ end
93
+ def session_established?(args = {})
94
+ target = args[:node].target
95
+ portal = args[:node].portal
96
+
97
+ refresh_sessions
98
+
99
+ @sessions.each do |s|
100
+ return true if s.target == target && s.portal == portal
101
+ end
102
+ false
103
+ end
104
+
105
+ # Query iscsiadm version, returning resulting String
106
+ def version
107
+ Common::callout(
108
+ COMMANDS[:version][:cmd] % @command,
109
+ COMMANDS[:version][:filter]).to_s
110
+ end
111
+
112
+ # Discovery Mode
113
+ #
114
+
115
+ # Runs iscsi discovery against a target portal, returing resulting Array of
116
+ # Node objects.
117
+ def discover(args = {})
118
+ type = args[:type] || DEFAULT_TYPE
119
+ ip = args[:ip] || DEFAULT_HOST
120
+ port = args[:port] || DEFAULT_PORT
121
+ interface = args[:interface] || DEFAULT_INTERFACE
122
+
123
+ Common::callout(
124
+ COMMANDS[:discover][:cmd] % [@command, type, ip, port],
125
+ COMMANDS[:discover][:filter], @debug)
126
+
127
+ refresh_portals
128
+ refresh_nodes
129
+
130
+ list_nodes("#{ip}:#{port},#{interface}")
131
+ end
132
+
133
+ # Get list of portals, returning resulting Array of Portal objects.
134
+ def list_portals
135
+ ret = []
136
+
137
+ Common::callout(
138
+ COMMANDS[:list_portals][:cmd] % [@command],
139
+ COMMANDS[:list_portals][:filter], @debug) do |p,t|
140
+
141
+ ret << Portal.parse(p,t)
142
+ end
143
+
144
+ ret
145
+ end
146
+
147
+ # Node Mode
148
+ #
149
+
150
+ # Attempt to login to a given node and portal, returning resulting Session
151
+ # if successful.
152
+ def login(args = {})
153
+ target = args[:node].target
154
+ portal = args[:node].portal
155
+
156
+ Common::callout(
157
+ COMMANDS[:login][:cmd] % [@command, target, portal],
158
+ COMMANDS[:login][:filter], @debug)
159
+
160
+ session(args)
161
+ end
162
+
163
+ # Logout of a given node and portal, returning resulting Array of remaining
164
+ # Sessions.
165
+ def logout(args = {})
166
+ target = args[:node].target
167
+ portal = args[:node].portal
168
+
169
+ Common::callout(
170
+ COMMANDS[:logout][:cmd] % [@command, target, portal],
171
+ COMMANDS[:logout][:filter], @debug)
172
+
173
+ refresh_sessions
174
+ end
175
+
176
+ # Get list of nodes, returning resulting Array of Node objects.
177
+ def list_nodes(args = {})
178
+ portal = args[:portal]
179
+ ret = []
180
+
181
+ Common::callout(
182
+ COMMANDS[:list_nodes][:cmd] % [@command],
183
+ COMMANDS[:list_nodes][:filter], @debug) do |p,t|
184
+
185
+ next if portal.nil? == false and portal != p
186
+
187
+ ret << Node.new(:portal => p, :target => t)
188
+ end
189
+
190
+ ret
191
+ end
192
+
193
+ # Session Mode
194
+ #
195
+
196
+ # Get list of sessions, returning resulting Array of Session objects.
197
+ def list_sessions(args = {})
198
+ portal = args[:portal]
199
+ ret = []
200
+
201
+ Common::callout(
202
+ COMMANDS[:list_sessions][:cmd] % [@command],
203
+ COMMANDS[:list_sessions][:filter], @debug) do |r,p,t|
204
+
205
+ next if portal.nil? == false and portal != p
206
+
207
+ ret << Session.new(:portal => p, :target => t, :id => r)
208
+ end
209
+
210
+ ret
211
+ end
212
+
213
+ # Node Config Mode
214
+ #
215
+
216
+ # Get configuration for a given node and portal, returning resulting Hash of
217
+ # record values.
218
+ def show_config(args = {})
219
+ target = args[:node].target
220
+ portal = args[:node].portal
221
+
222
+ conf = {}
223
+
224
+ Common::callout(
225
+ COMMANDS[:show_config][:cmd] % [@command, target, portal],
226
+ COMMANDS[:show_config][:filter], @debug) do |k,v|
227
+
228
+ # easier to deal with nil
229
+ conf[k] = (v == '<empty>' ? nil : v)
230
+ end
231
+
232
+ conf
233
+ end
234
+
235
+ # Delete a named config entry for a given target
236
+ # Returns ?
237
+ #
238
+ # Should not be used on a running session so says the man page.
239
+ def delete_record(args = {})
240
+ target = args[:node].target
241
+ portal = args[:node].portal
242
+ name = args[:name]
243
+
244
+ Common::callout(
245
+ COMMANDS[:delete_record][:cmd] % [@command, target, portal, name],
246
+ COMMANDS[:delete_record][:filter], @debug)
247
+ end
248
+
249
+ # Create a new config entry for a given target
250
+ # Returns ?
251
+ #
252
+ # Why would you want to create new records again..?
253
+ def new_record(args = {})
254
+ target = args[:node].target
255
+ portal = args[:node].portal
256
+ name = args[:name]
257
+ value = args[:value]
258
+
259
+ Common::callout(
260
+ COMMANDS[:new_record][:cmd] % [@command, target, portal, name, value],
261
+ COMMANDS[:new_record][:filter], @debug)
262
+ end
263
+
264
+ # Update a named config entry for a given target
265
+ # Returns ?
266
+ #
267
+ def update_record(args = {})
268
+ target = args[:node].target
269
+ portal = args[:node].portal
270
+ name = args[:name]
271
+ value = args[:value]
272
+
273
+ Common::callout(
274
+ COMMANDS[:update_record][:cmd] % [@command, target, portal, name, value],
275
+ COMMANDS[:update_record][:filter], @debug)
276
+ end
277
+
278
+ # Takes a Node object and entry name as args.
279
+ #
280
+ # Returns String.
281
+ #
282
+ def show_record(args = {})
283
+ target = args[:node].target
284
+ portal = args[:node].portal
285
+ name = args[:name]
286
+
287
+ # iscsiadm doesn't support show by specific name, fake it
288
+ Common::callout(
289
+ COMMANDS[:show_record][:cmd] % [@command, target, portal, name],
290
+ %r{#{name} = (.*)$}, @debug)
291
+ end
292
+
293
+ end # class
294
+ end # module
@@ -0,0 +1,42 @@
1
+ require 'iscsiadm/external'
2
+
3
+ module IscsiadmWrapper
4
+ class Common
5
+
6
+ class<<self
7
+ # Convert match data to array, dropping the match all.
8
+ # Returns an array of matches.
9
+ #
10
+ def parse(line, filter) #:nodoc:
11
+ match = filter.match(line).to_a
12
+ match.shift
13
+ match
14
+ end
15
+
16
+ # Invoke an external program, passing each line of returned output through
17
+ # our filter.
18
+ #
19
+ # Yield each all matches per line in block or return an array of all matches on every line.
20
+ #
21
+ def callout(cmd, filter, debug=false) #:nodoc:
22
+ puts "Executing `#{cmd}`" if debug
23
+
24
+ ret = []
25
+ External::cmd(cmd) do |line|
26
+ match = parse(line, filter)
27
+ match.each do |t|
28
+ ret << t
29
+ end
30
+ if defined? yield
31
+ yield ret
32
+ ret.clear
33
+ end
34
+ end
35
+
36
+ ret
37
+ end
38
+
39
+ end # self
40
+
41
+ end # class
42
+ end # module
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'open4'
3
+
4
+ module IscsiadmWrapper
5
+ class External
6
+ class ExternalFailure < RuntimeError; end
7
+
8
+ class<<self
9
+ # Execute a command, returning the resulting String of standard output.
10
+ #
11
+ # The block is optional. If given, it will be invoked for each line of
12
+ # output.
13
+ def cmd(cmd)
14
+ output = []
15
+ error = nil
16
+ stat = Open4::popen4(cmd) do |pid, stdin, stdout, stderr|
17
+ while line = stdout.gets
18
+ output << line
19
+ end
20
+ error = stderr.read.strip
21
+ end
22
+ if stat.exited?
23
+ if stat.exitstatus > 0
24
+ raise ExternalFailure, "Fatal error, `#{cmd}` returned #{stat.exitstatus} with '#{error}'"
25
+ end
26
+ elsif stat.signaled?
27
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.termsig} and terminated"
28
+ elsif stat.stopped?
29
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.stopsig} and is stopped"
30
+ end
31
+
32
+ if block_given?
33
+ return output.each { |l| yield l }
34
+ else
35
+ return output.join
36
+ end
37
+ end
38
+ end
39
+
40
+ end # class
41
+ end # module
@@ -0,0 +1,309 @@
1
+ module IscsiadmWrapper
2
+ # This is a wrapper for the open-iscsi administration utility, iscsiadm.
3
+ #
4
+ # Overview:
5
+ #
6
+ # The basic iscsiadm on disk is structure is
7
+ # - discovery (/var/lib/iscsi/send_targets on CentOS)
8
+ # portals which have been scanned
9
+ # - node (/var/lib/iscsi/send_targets on CentOS)
10
+ # settings records containing target,ip,and some params
11
+
12
+ require 'iscsiadm/common'
13
+ require 'iscsiadm/portal'
14
+ require 'iscsiadm/node'
15
+ require 'iscsiadm/session'
16
+
17
+ class Iscsiadm
18
+ COMPAT_VERSION = '2.0-865'
19
+ DEFAULT_BINARY = '/sbin/iscsiadm'
20
+
21
+ DEFAULT_TYPE = 'sendtargets'
22
+ DEFAULT_HOST = 'localhost'
23
+ DEFAULT_PORT = 3260
24
+ DEFAULT_INTERFACE = 1
25
+
26
+ COMMANDS = {
27
+ # misc
28
+ :version => { :cmd => "%s --version",
29
+ :filter => %r{^iscsiadm version (.*)$} },
30
+ # discovery mode
31
+ :discover => { :cmd => "%s --mode discovery --type %s --portal %s:%s",
32
+ :filter => %r{^.* (.*)$} },
33
+ :list_portals => { :cmd => "%s --mode discovery",
34
+ :filter => %r{^(.*) via (.*)$} },
35
+ # node mode
36
+ :login => { :cmd => "%s --mode node --target %s --portal %s --login",
37
+ :filter => %r{^iscsiadm version (.*)$} },
38
+ :logout => { :cmd => "%s --mode node --target %s --portal %s --logout",
39
+ :filter => %r{^iscsiadm version (.*)$} },
40
+ :list_nodes => { :cmd => "%s --mode node",
41
+ :filter => %r{^(.*) (.*)$} },
42
+ # session mode
43
+ :list_sessions => { :cmd => "%s --mode session",
44
+ :filter => %r{^tcp: \[(\d+)\] (.*) (.*)$} },
45
+ # config db
46
+ :show_config => { :cmd => "%s --mode node --target %s --portal %s --op show",
47
+ :filter => %r{^(.*) = (.*)$} },
48
+ :new_record => { :cmd => "%s --mode node --target %s --portal %s --op new --name %s --value %s",
49
+ :filter => %r{^(.*)$} },
50
+ :delete_record => { :cmd => "%s --mode node --target %s --portal %s --op delete --name %s",
51
+ :filter => %r{^(.*)$} },
52
+ :update_record => { :cmd => "%s --mode node --target %s --portal %s --op update --name %s --value %s",
53
+ :filter => %r{^(.*)$} },
54
+ # iscsiadm doesn't support this one really, see below
55
+ :show_record => { :cmd => "%s --mode node --target %s --portal %s --op show --name %s",
56
+ :filter => %r{^.* = (.*)$} },
57
+ }
58
+
59
+ # Class Methods
60
+ #
61
+ class<<self
62
+ end
63
+
64
+ # Instance Methods
65
+ #
66
+ def initialize(args = {})
67
+ @iscsiadm_binary = args[:iscsiadm_binary] || DEFAULT_BINARY[:iscsiadm]
68
+ @use_unsupported_version = args[:use_unsupported_version] || false
69
+ @debug = args[:debug] || false
70
+
71
+ puts "Running in debug mode, outputting commands to stdout." if @debug
72
+ end
73
+
74
+ # Misc
75
+ #
76
+
77
+ def refresh_all
78
+ refresh_sessions
79
+ refresh_nodes
80
+ refresh_portals
81
+ end
82
+ def refresh_sessions; @sessions = list_sessions; end
83
+ def refresh_nodes; @nodes = list_nodes; end
84
+ def refresh_portals; @portals = list_portals; end
85
+ def sessions; refresh_sessions; @sessions; end
86
+ def nodes; refresh_nodes; @nodes; end
87
+ def portals; refresh_portals; @portals; end
88
+
89
+ def session(args = {})
90
+ target = args[:node].target
91
+ portal = args[:node].portal
92
+
93
+ refresh_sessions
94
+
95
+ @sessions.each do |s|
96
+ return s if s.target == target && s.portal == portal
97
+ end
98
+ end
99
+ def session_established?(args = {})
100
+ target = args[:node].target
101
+ portal = args[:node].portal
102
+
103
+ refresh_sessions
104
+
105
+ @sessions.each do |s|
106
+ return true if s.target == target && s.portal == portal
107
+ end
108
+ false
109
+ end
110
+
111
+ # Returns iscsiadm's version
112
+ #
113
+ def version
114
+ Common::callout(
115
+ COMMANDS[:version][:cmd] % @iscsiadm_binary,
116
+ COMMANDS[:version][:filter]).to_s
117
+ end
118
+
119
+ # Discovery Mode
120
+ #
121
+
122
+ # Runs iscsi discovery against a target portal
123
+ # Returns nodes.
124
+ #
125
+ def discover(args = {})
126
+ type = args[:type] || DEFAULT_TYPE
127
+ ip = args[:ip] || DEFAULT_HOST
128
+ port = args[:port] || DEFAULT_PORT
129
+ interface = args[:interface] || DEFAULT_INTERFACE
130
+
131
+ Common::callout(
132
+ COMMANDS[:discover][:cmd] % [@iscsiadm_binary, type, ip, port],
133
+ COMMANDS[:discover][:filter], @debug)
134
+
135
+ refresh_portals
136
+ refresh_nodes
137
+
138
+ list_nodes("#{ip}:#{port},#{interface}")
139
+ end
140
+
141
+ # Returns an array of portal objects.
142
+ #
143
+ def list_portals
144
+ ret = []
145
+
146
+ Common::callout(
147
+ COMMANDS[:list_portals][:cmd] % [@iscsiadm_binary],
148
+ COMMANDS[:list_portals][:filter], @debug) do |p,t|
149
+
150
+ ret << Portal.parse(p,t)
151
+ end
152
+
153
+ ret
154
+ end
155
+
156
+ # Node Mode
157
+ #
158
+
159
+ # Attempt to login to a given node.
160
+ # Returns session
161
+ #
162
+ def login(args = {})
163
+ target = args[:node].target
164
+ portal = args[:node].portal
165
+
166
+ Common::callout(
167
+ COMMANDS[:login][:cmd] % [@iscsiadm_binary, target, portal],
168
+ COMMANDS[:login][:filter], @debug)
169
+
170
+ session(args)
171
+ end
172
+
173
+ # Attempt to login to a given node.
174
+ # Returns ?
175
+ #
176
+ def logout(args = {})
177
+ target = args[:node].target
178
+ portal = args[:node].portal
179
+
180
+ Common::callout(
181
+ COMMANDS[:logout][:cmd] % [@iscsiadm_binary, target, portal],
182
+ COMMANDS[:logout][:filter], @debug)
183
+
184
+ refresh_sessions
185
+ end
186
+
187
+ # Returns an array of Node objects. Return all by
188
+ # default
189
+ #
190
+ def list_nodes(args = {})
191
+ portal = args[:portal]
192
+ ret = []
193
+
194
+ Common::callout(
195
+ COMMANDS[:list_nodes][:cmd] % [@iscsiadm_binary],
196
+ COMMANDS[:list_nodes][:filter], @debug) do |p,t|
197
+
198
+ next if portal.nil? == false and portal != p
199
+
200
+ ret << Node.new(:portal => p, :target => t)
201
+ end
202
+
203
+ ret
204
+ end
205
+
206
+ # Session Mode
207
+ #
208
+
209
+ # Returns an array of Session objects. Return all by
210
+ # default.
211
+ #
212
+ def list_sessions(args = {})
213
+ portal = args[:portal]
214
+ ret = []
215
+
216
+ Common::callout(
217
+ COMMANDS[:list_sessions][:cmd] % [@iscsiadm_binary],
218
+ COMMANDS[:list_sessions][:filter], @debug) do |r,p,t|
219
+
220
+ next if portal.nil? == false and portal != p
221
+
222
+ ret << Session.new(:portal => p, :target => t, :id => r)
223
+ end
224
+
225
+ ret
226
+ end
227
+
228
+ # Node Config Mode
229
+ #
230
+
231
+ # Returns a hash of all config key => value pairs for a given target
232
+ #
233
+ def show_config(args = {})
234
+ target = args[:node].target
235
+ portal = args[:node].portal
236
+
237
+ conf = {}
238
+
239
+ Common::callout(
240
+ COMMANDS[:show_config][:cmd] % [@iscsiadm_binary, target, portal],
241
+ COMMANDS[:show_config][:filter], @debug) do |k,v|
242
+
243
+ # easier to deal with nil
244
+ conf[k] = (v == '<empty>' ? nil : v)
245
+ end
246
+
247
+ conf
248
+ end
249
+
250
+ # Delete a named config entry for a given target
251
+ # Returns ?
252
+ #
253
+ # Should not be used on a running session so says the man page.
254
+ def delete_record(args = {})
255
+ target = args[:node].target
256
+ portal = args[:node].portal
257
+ name = args[:name]
258
+
259
+ Common::callout(
260
+ COMMANDS[:delete_record][:cmd] % [@iscsiadm_binary, target, portal, name],
261
+ COMMANDS[:delete_record][:filter], @debug)
262
+ end
263
+
264
+ # Create a new config entry for a given target
265
+ # Returns ?
266
+ #
267
+ # Why would you want to create new records again..?
268
+ def new_record(args = {})
269
+ target = args[:node].target
270
+ portal = args[:node].portal
271
+ name = args[:name]
272
+ value = args[:value]
273
+
274
+ Common::callout(
275
+ COMMANDS[:new_record][:cmd] % [@iscsiadm_binary, target, portal, name, value],
276
+ COMMANDS[:new_record][:filter], @debug)
277
+ end
278
+
279
+ # Update a named config entry for a given target
280
+ # Returns ?
281
+ #
282
+ def update_record(args = {})
283
+ target = args[:node].target
284
+ portal = args[:node].portal
285
+ name = args[:name]
286
+ value = args[:value]
287
+
288
+ Common::callout(
289
+ COMMANDS[:update_record][:cmd] % [@iscsiadm_binary, target, portal, name, value],
290
+ COMMANDS[:update_record][:filter], @debug)
291
+ end
292
+
293
+ # Takes a Node object and entry name as args.
294
+ #
295
+ # Returns String.
296
+ #
297
+ def show_record(args = {})
298
+ target = args[:node].target
299
+ portal = args[:node].portal
300
+ name = args[:name]
301
+
302
+ # iscsiadm doesn't support show by specific name, fake it
303
+ Common::callout(
304
+ COMMANDS[:show_record][:cmd] % [@iscsiadm_binary, target, portal, name],
305
+ %r{#{name} = (.*)$}, @debug)
306
+ end
307
+
308
+ end
309
+ end
@@ -0,0 +1,14 @@
1
+ require 'iscsiadm/common'
2
+ require 'iscsiadm/portal'
3
+
4
+ module IscsiadmWrapper
5
+ class Node
6
+ attr_reader :portal, :target
7
+
8
+ def initialize(args = {})
9
+ @portal = Portal.parse(args[:portal])
10
+ @target = args[:target]
11
+ end
12
+
13
+ end # class
14
+ end # module
@@ -0,0 +1,39 @@
1
+ require 'iscsiadm/common'
2
+
3
+ module IscsiadmWrapper
4
+ class Portal
5
+ attr_reader :ip, :port, :interface, :type
6
+
7
+ DEFAULT_TYPE = 'sendtargets'
8
+
9
+ # Class Methods
10
+ #
11
+ class<<self
12
+ # Takes the typical portal string from iscsiadm and the type and returns a
13
+ # new Portal object.
14
+ #
15
+ # eg Portal.parse('20.20.20.20:3260,1','sendtargets')
16
+ #
17
+ # XXX: whats the trailing ,1 again..?
18
+ def parse(portal,type=DEFAULT_TYPE)
19
+ if portal =~ %r{^(.*):(\d+),?(\d+)?$}
20
+ return Portal.new(:ip => $1, :port => $2, :interface => $3, :type => type)
21
+ end
22
+ end
23
+ end
24
+
25
+ # Instance Methods
26
+ #
27
+ def initialize(args = {})
28
+ @ip = args[:ip]
29
+ @port = args[:port]
30
+ @interface = args[:interface]
31
+ @type = args[:type]
32
+ end
33
+
34
+ def to_s
35
+ "#{@ip}:#{@port},#{@interface}"
36
+ end
37
+
38
+ end # class
39
+ end # module
@@ -0,0 +1,38 @@
1
+ module IscsiadmWrapper
2
+ class Proc
3
+
4
+ class ExternalFailure < RuntimeError; end
5
+
6
+ class<<self
7
+ # Return output of external command
8
+ # Yield each line or return entire output
9
+ #
10
+ def external(cmd) #:nodoc:
11
+ output = []
12
+ IO.popen(cmd, "r") do |p|
13
+ while line = p.gets
14
+ if defined? yield
15
+ yield line
16
+ else
17
+ output << line
18
+ end
19
+ end
20
+ end
21
+ stat = $?
22
+ if stat.exited?
23
+ if stat.exitstatus > 0
24
+ raise ExternalFailure, "Fatal error, `#{cmd}` returned #{stat.exitstatus}"
25
+ end
26
+ elsif stat.signaled?
27
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.termsig} and terminated"
28
+ elsif stat.stopped?
29
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.stopsig} and is stopped"
30
+ else
31
+ output.join
32
+ end
33
+ end
34
+
35
+ end # self
36
+
37
+ end # class
38
+ end # module
@@ -0,0 +1,14 @@
1
+ require 'iscsiadm/common'
2
+ require 'iscsiadm/node'
3
+
4
+ module IscsiadmWrapper
5
+ class Session < Node
6
+ attr_reader :id
7
+
8
+ def initialize(args = {})
9
+ super
10
+ @id = args[:id]
11
+ end
12
+
13
+ end # class
14
+ end # module
File without changes
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: ruby-iscsiadm
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2008-05-30 00:00:00 -07:00
8
+ summary: This is a wrapper for the open-iscsi administration utility, iscsiadm
9
+ require_paths:
10
+ - lib
11
+ email:
12
+ - matt@bravenet.com
13
+ homepage: http://ruby-iscsiadm.rubyforge.org
14
+ rubyforge_project: ruby-iscsiadm
15
+ description: This is a wrapper for the open-iscsi administration utility, iscsiadm. This is a simple binary wrapper and considered a temporary solution until a proper api is finished as per http://thread.gmane.org/gmane.linux.iscsi.open-iscsi/422
16
+ autorequire:
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ - - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ signing_key:
28
+ cert_chain:
29
+ - |
30
+ -----BEGIN CERTIFICATE-----
31
+ MIIDMDCCAhigAwIBAgIBADANBgkqhkiG9w0BAQUFADA+MQ0wCwYDVQQDDARtYXR0
32
+ MRgwFgYKCZImiZPyLGQBGRYIYnJhdmVuZXQxEzARBgoJkiaJk/IsZAEZFgNjb20w
33
+ HhcNMDgwNTI5MjM1MDUwWhcNMDkwNTI5MjM1MDUwWjA+MQ0wCwYDVQQDDARtYXR0
34
+ MRgwFgYKCZImiZPyLGQBGRYIYnJhdmVuZXQxEzARBgoJkiaJk/IsZAEZFgNjb20w
35
+ ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDGVrJnpk6/hCO5lg6T7As
36
+ unEksqdmCly6GkRpX0cVxusWG/wbjrZw4Ka5x9pSS2m3GfefEpu5CuWH9wnGB/q0
37
+ LwbWhrkpQb+uNVD6hEbQcngDEiQbiitaSWZKSvrIymbM5Tl92rIScmvx7Pd7l8tz
38
+ BLndL/DmrBHrHWQ47xLxOmBAA4NJy0gk7HdsdAFfJwrEoC14AAXqcI3X0qU3KGrr
39
+ 9Fs0jVeIKktx3fthwGVPrpYrMIw3xgiUqOqn7M+V4soD013dopaN7orewmCm9dQL
40
+ 4N06FMnGNjwFaS6+MV612nk8gkVpJwgcwTHE1GNt2NItR5zeK6j+AZ01DijYdjH9
41
+ AgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQ6jcdN
42
+ QqNNsx2XxigfTWE0Owy0XzANBgkqhkiG9w0BAQUFAAOCAQEAvWBkYaD7eKP+KHxJ
43
+ dtQuaebQTZvBX4vjk/Omjn7hj7u8X8aTvmCKyopnmywiApFH1zgDa8NRc1KTsvZt
44
+ zhSRWIuI7fFB+JFZUatE7Nius56id9UchYO98569t6bNU284XwBtcj4MN5Andfcp
45
+ LKdUNuuT0yui1InJG4fD0uKnOS1qcXEm+mN2s1uqPGVltEHorG7L/bPNAw8xV+uq
46
+ NPc7hhQTkbi7HB2Uwxr9uK9IGHEE5tDVnsPWPnJJ4jSOc7Bd1eHZuSMkEPfyREDf
47
+ h9ljoX3AGXW8qYohFzGjflamaWZ4xYaKZSNqS2i0Kc+M76Sy+r9FldDRNn4FGUBE
48
+ JqYgHg==
49
+ -----END CERTIFICATE-----
50
+
51
+ post_install_message:
52
+ authors:
53
+ - Matthew Kent
54
+ files:
55
+ - History.txt
56
+ - Manifest.txt
57
+ - README.txt
58
+ - README_SUDO.txt
59
+ - Rakefile
60
+ - Todo.txt
61
+ - examples/example_session.rb
62
+ - examples/show_nodes.rb
63
+ - lib/iscsiadm.rb
64
+ - lib/iscsiadm/common.rb
65
+ - lib/iscsiadm/external.rb
66
+ - lib/iscsiadm/iscsiadm.rb
67
+ - lib/iscsiadm/node.rb
68
+ - lib/iscsiadm/portal.rb
69
+ - lib/iscsiadm/proc.rb
70
+ - lib/iscsiadm/session.rb
71
+ - test/test_iscsiadm.rb
72
+ test_files:
73
+ - test/test_iscsiadm.rb
74
+ rdoc_options:
75
+ - --main
76
+ - README.txt
77
+ extra_rdoc_files:
78
+ - History.txt
79
+ - Manifest.txt
80
+ - README.txt
81
+ - README_SUDO.txt
82
+ - Todo.txt
83
+ executables: []
84
+
85
+ extensions: []
86
+
87
+ requirements: []
88
+
89
+ dependencies:
90
+ - !ruby/object:Gem::Dependency
91
+ name: open4
92
+ version_requirement:
93
+ version_requirements: !ruby/object:Gem::Version::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 0.9.6
98
+ version:
99
+ - !ruby/object:Gem::Dependency
100
+ name: hoe
101
+ version_requirement:
102
+ version_requirements: !ruby/object:Gem::Version::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 1.5.3
107
+ version:
metadata.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ ��RN����d�ʄ�Z+VBbY�~�P��ZH�z��W$]Ҏ�7R���E/p4�ў%����5by�zdp�F@3���4�^rd���_�f�v��"�5�
2
+ ��âA�*=Yɭ���ƌb�I��a�T��U���ϐ�)�����#>�Kg���X��(!���uպ�F�=���