sambala 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/sambala.rb +250 -0
  2. data/lib/sambala_gardener.rb +111 -0
  3. metadata +63 -0
data/lib/sambala.rb ADDED
@@ -0,0 +1,250 @@
1
+ # This class acts as a Ruby wrapper around the smbclient command line utility,
2
+ # allowing access to Samba (SMB, CIFS) shares from Ruby.
3
+ # What's special about Sambala's implementation, is that it allows for both both queue
4
+ # and interactive commands operations. While interactive mode is invoked in a blocking manner,
5
+ # the queue mode behaves as a non-blocking, multi-threaded, background process.
6
+ #
7
+ # Sambala works on Unix derivatives operating systems (Linux, BSD, OSX, else),
8
+ # as long as the operating system has the smbclient utility installed somewhere in the environment's PATH.
9
+ #
10
+ # Sambala supports most, but not all, smbclient features. I didn't to this point implement
11
+ # commands relying on posix server support, because I wanted Sambala to be server agnostic.
12
+ # If some commands or interfaces you would like to use is not supported by Sambala,
13
+ # email me and I may answer with a quick feature update when feasible.
14
+ #
15
+ # I tried to make Sambala as simple as possible, retaining most of the original smbclient command names,
16
+ # as instance method names for the Sambala client object. It behaves as you would expect from an OOP lib:
17
+ # You instantiate a new Sambala object and are then allowed to send smbclient commands a instance method to this object.
18
+ # The only big difference, is the queue mode, which is activated on a per command/method invocation, with a 'true' flag,
19
+ # you add as the last parameter to the method invocation. When some commands are queued, you can harvest them at a later point
20
+ # with the +queue_results+ method, returning an array containing your commands and their respective results.
21
+ #
22
+ # When in doubt about what each command does, please refer to smbclient man page for help.
23
+ #
24
+ # Author:: lp (mailto:lp@spiralix.org)
25
+ # Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
26
+ #
27
+ # :title:Sambala
28
+
29
+ class Sambala
30
+ require 'pty'
31
+ require 'expect'
32
+ require 'rubygems'
33
+ require 'abundance'
34
+
35
+ require 'sambala_gardener'
36
+ include Gardener
37
+
38
+ # The +new+ class method initializes Sambala.
39
+ # === Parameters
40
+ # * :domain = the smb server domain, optionnal in most cases
41
+ # * :host = the hostname of the smb server, may be IP or fully qualified domain name
42
+ # * :user = the user name to log into the server
43
+ # * :password = the password to log into the server
44
+ # * :threads = how many parallel operations you want initiated, !!! higher than 4 at you own risk !!!
45
+ # === Example
46
+ # sam = Sambala.new( :domain => 'NTDOMAIN',
47
+ # :host => 'sambaserver',
48
+ # :user => 'walrus',
49
+ # :password => 'eggman',
50
+ # :threads => 2 )
51
+ def initialize(options={:domain => '', :host => '', :user => '', :password => '', :threads => 1})
52
+ options[:threads] = 4 if options[:threads] > 4
53
+ @options = options; init_gardener
54
+ end
55
+ # The +cd+ instance method takes only one argument, the path to which you wish to change directory
56
+ # Its one of the only implemented command where queue mode is not available, for the simple reason that
57
+ # when queued operations are executed in parallel, one does not control which command will get executed first,
58
+ # making a queued +cd+ operation very dangerous.
59
+ # === Parameters
60
+ # * :to = the path to change directory to
61
+ # === Interactive Returns
62
+ # * _boolean_ = confirms if +cd+ operation completed successfully
63
+ # === Example
64
+ # sam.cd(:to => 'aFolder/anOtherFolder/') # => true
65
+ def cd(opts={:to => ''})
66
+ execute('cd', opts[:to], false)[0]
67
+ end
68
+
69
+ # The +du+ instance does exactly what _du_ usually does: estimates file space usage.
70
+ # === Parameters
71
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
72
+ # === Interactive Returns
73
+ # * _string_ = +du+ command results
74
+ # === Example
75
+ # puts sam.du # => 34923 blocks of size 2097152. 27407 blocks available
76
+ # Total number of bytes: 59439077
77
+ def du(opts={:queue=>false})
78
+ execute('du', '', opts[:queue])[1]
79
+ end
80
+
81
+ # The +del+ instance method delete files on smb shares
82
+ # === Parameters
83
+ # * :mask = the mask matching the file to be deleted inside the current working directory.
84
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
85
+ # === Interactive Returns
86
+ # * _boolean_ = confirms if +del+ operation completed successfully
87
+ # === Example
88
+ # sam.del(:mask => 'aFile') # => true
89
+ def del(opts={:mask => nil, :queue=>false})
90
+ execute('del', opts[:mask], opts[:queue])[0]
91
+ end
92
+
93
+ # The +get+ instance method copy files from smb shares.
94
+ # As with the smbclient get command, the destination path is optional.
95
+ # === Parameters
96
+ # * :from = the source path, relative path to the current working directory in the smb server
97
+ # * :to = the destination path, absolute or relative path to the current working directory in the local OS
98
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
99
+ # === Interactive Returns
100
+ # _array_ = [ _booleanSuccess_, _getResultMessage_ ]
101
+ # === Example
102
+ # sam.get(:from => 'aFile.txt') # => [true, "getting file \\aFile.txt.rb of size 3877 as test.rb (99.6 kb/s) (average 89.9 kb/s)\r\n"]
103
+ def get(opts={:from => nil, :to => nil, :queue => false})
104
+ opts[:to].nil? ? strng = opts[:from] : strng = opts[:from] + ' ' + opts[:to]
105
+ execute('get', strng, opts[:queue])
106
+ end
107
+
108
+ # The +lcd+ instance method changes the current working directory on the local machine to the directory specified.
109
+ # Its one of the only implemented command where queue mode is not available, for the simple reason that
110
+ # when queued operations are executed in parallel, one does not control which command will get executed first,
111
+ # making a queued +lcd+ operation very dangerous.
112
+ # === Parameters
113
+ # * :to = the path to change directory to
114
+ # === Interactive Returns
115
+ # * _boolean_ = confirms if +cd+ operation completed successfully
116
+ # === Example
117
+ # sam.lcd(:to => 'aLocalFolder/anOtherFolder/') # => true
118
+ def lcd(opts={:to => ''})
119
+ execute('lcd', opts[:to], false)[0]
120
+ end
121
+
122
+ # The +lowercase+ method toggles lowercasing of filenames for the get command.
123
+ # Can be usefull when copying files from DOS servers.
124
+ # This method has no queue processing option
125
+ # === Interactive Returns
126
+ # * _boolean_ = confirms if +lowercase+ operation completed successfully
127
+ # === Example
128
+ # sam.lowercase # => true # toggle from files all UPPERCASE to all lowercase
129
+ def lowercase
130
+ execute_all('lowercase' ,'')
131
+ end
132
+
133
+ # The method +ls+ or its alias _dir_, list the files and directories matching :mask in the current working directory on the smb server.
134
+ # === Parameters
135
+ # * :mask = the mask matching the file to be listed inside the current working directory.
136
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
137
+ # === Interactive Returns
138
+ # * _string_ = containing +ls+ command results
139
+ # === Example
140
+ # sam.ls # => genpi.rb A 81 Mon Nov 17 22:12:40 2008
141
+ # 34923 blocks of size 2097152. 27407 blocks available
142
+ def ls(opts={:mask => nil, :queue=>false})
143
+ execute('ls' ,opts[:mask], opts[:queue])[1]
144
+ end
145
+ alias dir ls
146
+
147
+ # The +mask+ method sets a mask to be used during recursive operation of the +mget+ and +mput+ commands.
148
+ # See man page for smbclient to get more on the details of operation
149
+ # This method has no queue processing option
150
+ # === Parameters
151
+ # * :mask = the matching filter
152
+ # === Example
153
+ # sam.mask(:mask => 'filter*') # => true
154
+ def mask(opts={:mask => nil})
155
+ execute_all('mask' ,opts[:mask])
156
+ end
157
+
158
+ # The +mget+ method copy all files matching :mask from the server to the client machine
159
+ # See man page for smbclient to get more on the details of operation
160
+ # === Parameters
161
+ # * :mask = the file matching filter
162
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
163
+ # === Interactive Returns
164
+ # _array_ = [ _booleanSuccess_, _mgetResultMessage_ ]
165
+ # === Example
166
+ # sam.mget(:mask => 'file*') # => [true, "getting file \\file_new.txt of size 3877 as file_new.txt (99.6 kb/s) (average 89.9 kb/s)\r\n"]
167
+ def mget(opts={:mask => nil, :queue => false})
168
+ execute('mget' ,opts[:mask], opts[:queue])
169
+ end
170
+
171
+ # The method +mkdir+ or its alias _md_, creates a new directory on the server.
172
+ # === Parameters
173
+ # * :path = the directory to create
174
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
175
+ # === Interactive Returns
176
+ # * _boolean_ = confirms if +mkdir+ operation completed successfully
177
+ # === Example
178
+ # sam.mkdir(:path => 'aFolder/aNewFolder') # => true
179
+ def mkdir(opts={:path => '', :queue => false})
180
+ execute('mkdir' ,opts[:path], opts[:queue])[0]
181
+ end
182
+ alias md mkdir
183
+
184
+ # The +mput+ method copy all files matching :mask in the current working directory on the local machine to the server.
185
+ # See man page for smbclient to get more on the details of operation
186
+ # === Parameters
187
+ # * :mask = the file matching filter
188
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
189
+ # === Interactive Returns
190
+ # _array_ = [ _booleanSuccess_, _mputResultMessage_ ]
191
+ # === Example
192
+ # sam.mput(:mask => 'file*') # => [true, "putting file \\file_new.txt of size 1004 as file_new.txt (65.4 kb/s) (average 65.4 kb/s)\r\n"]
193
+ def mput(opts={:mask => nil, :queue => false})
194
+ execute('mput' ,opts[:mask], opts[:queue])
195
+ end
196
+
197
+ # The +put+ instance method copy files to smb shares.
198
+ # As with the smbclient put command, the destination path is optional.
199
+ # === Parameters
200
+ # * :from = the source path, absolute or relative path to the current working directory in the local OS
201
+ # * :to = the destination path, relative path to the current working directory in the smb server OS
202
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
203
+ # === Interactive Returns
204
+ # _array_ = [ _booleanSuccess_, _putResultMessage_ ]
205
+ # === Example
206
+ # sam.put(:from => 'aLocalFile.txt') # => [false, "aLocalFile.txt does not exist\r\n"]
207
+
208
+ def put(opts={:from => nil, :to => nil, :queue => false})
209
+ opts[:to].nil? ? strng = opts[:from] : strng = opts[:from] + ' ' + opts[:to]
210
+ execute('put' ,strng, opts[:queue])
211
+ end
212
+
213
+ # The +recurse+ method toggles directory recursion
214
+ # This method has no queue processing option
215
+ # === Interactive Returns
216
+ # * _boolean_ = confirms if +mkdir+ operation completed successfully
217
+ # === Example
218
+ # sam.recurse # => true
219
+ def recurse
220
+ execute_all('recurse' ,'')
221
+ end
222
+
223
+ # The +volume+ method returns remote volume information.
224
+ # === Parameters
225
+ # * :queue = sets queue processing mode. Defaults to interactive mode when no option given.
226
+ # === Interactive Returns
227
+ # * _string_ = containing +volume+ command results
228
+ # === Example
229
+ # sam.volume # => "Volume: |geminishare| serial number 0x6d723053"
230
+ def volume(opts={:queue=>false})
231
+ execute('volume' ,'', opts[:queue])[1]
232
+ end
233
+
234
+ # The +queue_results+ methods collect a done queue items results
235
+ # === Example
236
+ # result = sam.queue_results
237
+ def queue_results
238
+ crop = @gardener.harvest(:full_crop)
239
+ crop.map! { |result| [ result[:success], result[:seed], result[:message] ] }
240
+ end
241
+
242
+ # The +close+ method safely end the smbclient session
243
+ # === Example
244
+ # sam.close
245
+ def close
246
+ result = @gardener.close
247
+ result.values.map { |queue| queue.empty? }.uniq.size == 1 ? true : false
248
+ end
249
+
250
+ end
@@ -0,0 +1,111 @@
1
+ class Sambala
2
+ # The Sambala::Gardener module bings Abundance into Sambala.
3
+ # A bunch of mixins used inside Sambala to access Abundance Non-Blocking Threading.
4
+ # These methods are a higher level abstraction of Abundance methods,
5
+ # serving as reusable general purpose agents inside Sambala.
6
+ #
7
+ # Author:: lp (mailto:lp@spiralix.org)
8
+ # Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
9
+ #
10
+ # :title:Sambala::Gardener
11
+ module Gardener
12
+
13
+ # The +execute+ method splits the execution according to the operation mode: queue or interactive.
14
+ # === Parameters
15
+ # * _command_ = the command as a string
16
+ # * _data_ = the command argument as a string
17
+ # * _queue_ = the command operation mode
18
+ # === Example
19
+ # result = execute('cd','dir/otherDir*',false) # => true
20
+ def execute(command,data,queue)
21
+ (queue.is_a? TrueClass) ? exec_queue(command,data) : exec_interactive(command,data)
22
+ end
23
+
24
+ # The +execute_all+ method does a special command invocation where all smbclient workers running in parallel
25
+ # are all sent the same command. The method returns one boolean value for all workers success.
26
+ # === Parameters
27
+ # * _command_ = the command as a string
28
+ # * _data_ = the command argument as a string
29
+ # === Example
30
+ # result = execute_all('mask','match*') # => true
31
+ def execute_all(command,data)
32
+ sleep 1
33
+ result = @gardener.seed_all("#{command} #{data}")
34
+ bools = result.map { |row| row[:success] }
35
+ bools.uniq.size == 1 ? true : false
36
+ end
37
+
38
+ # The +exec_interactive+ method follows +execute+ when queue=false.
39
+ # === Parameters
40
+ # * _command_ = the command as a string
41
+ # * _data_ = the command argument as a string
42
+ # === Example
43
+ # result = exec_interactive('put','aFile.txt') # => [false, "aFile.txt does not exist\r\n"]
44
+ def exec_interactive(command,data)
45
+ id = @gardener.seed("#{command} #{data}")
46
+ result = @gardener.harvest(id)
47
+ return result[:success], result[:message]
48
+ end
49
+
50
+ # The +exec_queue+ method follows +execute+ when queue=true
51
+ # === Parameters
52
+ # * _command_ = the command as a string
53
+ # * _data_ = the command argument as a string
54
+ # === Example
55
+ # result = exec_queue('get','aFile.txt') # => [true,true]
56
+ def exec_queue(command,data)
57
+ @gardener.seed("#{command} #{data}").integer? ? [true,true] : [false,false]
58
+ end
59
+
60
+ # The +init_gardener+ method initialize a gardener class object
61
+ def init_gardener
62
+ # To Implement:
63
+ # workgroup -W WORKGROUP
64
+ @gardener = Abundance.gardener(:seed_size => 8192, :rows => @options[:threads], :init_timeout => @options[:threads] + 1) do
65
+ PTY.spawn("smbclient //#{@options[:host]}/#{@options[:share]} #{@options[:password]} -U #{@options[:user]}") do |r,w,pid|
66
+ w.sync = true
67
+ $expect_verbose = false
68
+
69
+ catch :init do
70
+ loop do
71
+ r.expect(/.*\xD\xAsmb:[ \x5C]*\x3E.*/) do |text|
72
+ if text != nil
73
+ Abundance.init_status(true,"#{text.inspect}")
74
+ throw :init
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ Abundance.grow do |seed|
81
+ w.print "#{seed.sprout}\r"
82
+ catch :result do
83
+ loop do
84
+ r.expect(/.*\xD\xAsmb: \w*[\x5C]*\x3E.*/) do |text|
85
+ if text != nil
86
+ msg = text[0]
87
+
88
+ msg.gsub!(/smb: \w*\x5C\x3E\s*$/, '')
89
+ msg.gsub!(/^\s*#{seed.sprout}/, '')
90
+ msg.lstrip!
91
+
92
+ success = case seed.sprout
93
+ when /^put/
94
+ msg['putting'].nil? ? false : true
95
+ else
96
+ msg['NT_STATUS'].nil? ? true : false
97
+ end
98
+
99
+ seed.crop(success, msg)
100
+ throw :result
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sambala
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.2
5
+ platform: ruby
6
+ authors:
7
+ - Louis-Philippe Perron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-15 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: abundance
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.9
24
+ version:
25
+ description:
26
+ email: lp@spiralix.org
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/sambala.rb
35
+ - lib/sambala_gardener.rb
36
+ has_rdoc: true
37
+ homepage: http://sambala.rubyforge.org/
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project: Sambala
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: ruby samba client, interactive smbclient commands session and multi-threaded smb transfer queued mode
62
+ test_files: []
63
+