sambala 0.8.2

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.
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
+