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.
- data/lib/sambala.rb +250 -0
- data/lib/sambala_gardener.rb +111 -0
- 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
|
+
|