ruby-iscsiadm 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.
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�=���