rsynconrails 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/README.license +92 -0
- data/README.md +10 -0
- data/config/system_config.yml +15 -0
- data/lib/cli_funcs.rb +54 -0
- data/lib/cli_funcs_cpio.rb +38 -0
- data/lib/cli_funcs_rpm.rb +268 -0
- data/lib/cli_funcs_rpm2cpio.rb +28 -0
- data/lib/cli_funcs_rsync.rb +351 -0
- data/lib/cli_utils.rb +70 -0
- data/lib/host.rb +194 -0
- data/lib/package.rb +66 -0
- data/lib/ssh_funcs.rb +29 -0
- data/lib/system_config.rb +8 -0
- data/spec/cli_funcs_cpio_spec.rb +20 -0
- data/spec/cli_funcs_rpm2cpio_spec.rb +15 -0
- data/spec/cli_funcs_rpm_spec.rb +89 -0
- data/spec/cli_funcs_rsync_spec.rb +91 -0
- data/spec/cli_funcs_spec.rb +34 -0
- data/spec/cli_utils_spec.rb +13 -0
- data/spec/host_spec.rb +91 -0
- data/spec/package_spec.rb +39 -0
- data/spec/spec_helper.rb +127 -0
- data/spec/ssh_funcs_spec.rb +14 -0
- data/testfiles/libcom_err-1.41.12-14.el6_4.2.x86_64.rpm +0 -0
- data/testfiles/postfix-2.6.6-2.2.el6_1.x86_64.rpm +0 -0
- metadata +111 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
require 'system_config'
|
|
2
|
+
require 'cli_funcs'
|
|
3
|
+
|
|
4
|
+
class Rsync < CliFuncs
|
|
5
|
+
attr_accessor :flags_all, :cmd_run, :source, :destination
|
|
6
|
+
attr_reader :uptodate, :deleted, :modified, :created, :excluded, :ignored, :duplicates, :base_dir, :data_dir, :output, :transfer_stats
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
super
|
|
10
|
+
@utility = CliUtils.new("rsync").utility_path
|
|
11
|
+
@uptodate = Array.new
|
|
12
|
+
@deleted = Array.new
|
|
13
|
+
@modified = Array.new
|
|
14
|
+
@created = Array.new
|
|
15
|
+
@excluded = Hash.new
|
|
16
|
+
@ignored = Array.new
|
|
17
|
+
@duplicates = Array.new
|
|
18
|
+
@transfer_stats = Hash.new
|
|
19
|
+
@source = Array.new
|
|
20
|
+
@destination = String
|
|
21
|
+
@output_filter_junk = Regexp
|
|
22
|
+
@output_filter_excluded = Regexp
|
|
23
|
+
@output_filter_warn_err = Regexp
|
|
24
|
+
@output_filter_stats = Regexp
|
|
25
|
+
@output_filter_duplicates = Regexp
|
|
26
|
+
@output_filter_created = Regexp
|
|
27
|
+
set_flags_base
|
|
28
|
+
set_output_filters
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def rsync
|
|
32
|
+
run_and_capture(cmd_run)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_output_filters
|
|
36
|
+
set_output_filter_junk
|
|
37
|
+
set_output_filter_excluded
|
|
38
|
+
set_output_filter_warn_err
|
|
39
|
+
set_output_filter_stats
|
|
40
|
+
set_output_filter_duplicates
|
|
41
|
+
set_output_filter_created
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def output_process
|
|
45
|
+
@output.each do |line|
|
|
46
|
+
if line.match @output_filter_junk
|
|
47
|
+
next
|
|
48
|
+
elsif line.match @output_filter_duplicates
|
|
49
|
+
puts "#{line.chomp} DUPLICATE!" if DEBUG
|
|
50
|
+
@duplicates.push(line)
|
|
51
|
+
next
|
|
52
|
+
elsif line.match @output_filter_excluded
|
|
53
|
+
puts "#{line.chomp} EXCLUDED!" if DEBUG
|
|
54
|
+
@excluded[$3] = $4
|
|
55
|
+
next
|
|
56
|
+
elsif line.match @output_filter_warn_err
|
|
57
|
+
# Capture warnings / errors here
|
|
58
|
+
next
|
|
59
|
+
elsif line.match @output_filter_stats
|
|
60
|
+
# Set hash of stats
|
|
61
|
+
@transfer_stats[$1] = $2
|
|
62
|
+
@transfer_stats[$3] = $4
|
|
63
|
+
@transfer_stats[$5] = $6
|
|
64
|
+
@transfer_stats[$7] = $8
|
|
65
|
+
@transfer_stats[$9] = $10
|
|
66
|
+
@transfer_stats[$11] = $12
|
|
67
|
+
@transfer_stats[$13] = $14
|
|
68
|
+
@transfer_stats[$15] = $16
|
|
69
|
+
@transfer_stats[$17] = $18
|
|
70
|
+
@transfer_stats[$19] = $20
|
|
71
|
+
@transfer_stats[$21] = $22
|
|
72
|
+
next
|
|
73
|
+
elsif line.match @output_filter_created
|
|
74
|
+
#@created.push(line)
|
|
75
|
+
next
|
|
76
|
+
else
|
|
77
|
+
# catch all, this is the main content
|
|
78
|
+
process_itemized(line)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
puts @transfer_stats.inspect if DEBUG
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def process_itemized(line)
|
|
85
|
+
# Break apart the line by spaces (e.g. ".f 9/file9")
|
|
86
|
+
attrs,item = line.split(/\s+/, 2)
|
|
87
|
+
# Break apart itemized attrs on each character 0 = . 1 = f 2 = nil, 3 = nil ...
|
|
88
|
+
attrs_p = attrs.split("")
|
|
89
|
+
# Begin check's for file/directory disposition according to rsync
|
|
90
|
+
# If element 0 contains a '.', it means no update has occurred, but may have attribute changes
|
|
91
|
+
if(attrs_p[0] == ".")
|
|
92
|
+
# This first check ignores directories which have had their timestamp changed
|
|
93
|
+
if(
|
|
94
|
+
attrs_p[1] == "d" and
|
|
95
|
+
attrs_p[2] == "." and
|
|
96
|
+
attrs_p[3] == "." and
|
|
97
|
+
attrs_p[4] == "t" and
|
|
98
|
+
attrs_p[5] == "." and
|
|
99
|
+
attrs_p[6] == "." and
|
|
100
|
+
attrs_p[7] == "." and
|
|
101
|
+
attrs_p[8] == "." and
|
|
102
|
+
attrs_p[9] == "." and
|
|
103
|
+
attrs_p[10] == "."
|
|
104
|
+
)
|
|
105
|
+
puts "#{line.chomp} IGNORED!" if DEBUG
|
|
106
|
+
@ignored.push(line)
|
|
107
|
+
#return
|
|
108
|
+
# checks if nothing has changed with this item
|
|
109
|
+
elsif(
|
|
110
|
+
attrs_p[1] =~ /f|d|L|D|S/ and
|
|
111
|
+
attrs_p[2] == nil and
|
|
112
|
+
attrs_p[3] == nil and
|
|
113
|
+
attrs_p[4] == nil and
|
|
114
|
+
attrs_p[5] == nil and
|
|
115
|
+
attrs_p[6] == nil and
|
|
116
|
+
attrs_p[7] == nil and
|
|
117
|
+
attrs_p[8] == nil and
|
|
118
|
+
attrs_p[9] == nil and
|
|
119
|
+
attrs_p[10] == nil
|
|
120
|
+
)
|
|
121
|
+
puts "#{line.chomp} UPTODATE!" if DEBUG
|
|
122
|
+
@uptodate.push(line)
|
|
123
|
+
# something must have changed, like an attribute (e.g. ownership or mode)
|
|
124
|
+
else
|
|
125
|
+
puts "#{line.chomp} MODIFIED OWNERSHIP OR MODE!" if DEBUG
|
|
126
|
+
@modified.push(line)
|
|
127
|
+
end
|
|
128
|
+
elsif(attrs_p[0] =~ /\*|<|>|c|h/)
|
|
129
|
+
# checks if item is being deleted
|
|
130
|
+
if(
|
|
131
|
+
attrs_p[1] == "d" and
|
|
132
|
+
attrs_p[2] == "e" and
|
|
133
|
+
attrs_p[3] == "l" and
|
|
134
|
+
attrs_p[4] == "e" and
|
|
135
|
+
attrs_p[5] == "t" and
|
|
136
|
+
attrs_p[6] == "i" and
|
|
137
|
+
attrs_p[7] == "n" and
|
|
138
|
+
attrs_p[8] == "g" and
|
|
139
|
+
attrs_p[9] == nil and
|
|
140
|
+
attrs_p[10] == nil
|
|
141
|
+
)
|
|
142
|
+
puts "#{line.chomp} DELETED!" if DEBUG
|
|
143
|
+
@deleted.push(line)
|
|
144
|
+
|
|
145
|
+
# checks if item is being created (i.e. new file/dir)
|
|
146
|
+
elsif(
|
|
147
|
+
attrs_p[1] =~ /f|d/ and
|
|
148
|
+
attrs_p[2] == "+" and
|
|
149
|
+
attrs_p[3] == "+" and
|
|
150
|
+
attrs_p[4] == "+" and
|
|
151
|
+
attrs_p[5] == "+" and
|
|
152
|
+
attrs_p[6] == "+" and
|
|
153
|
+
attrs_p[7] == "+" and
|
|
154
|
+
attrs_p[8] == "+" and
|
|
155
|
+
attrs_p[9] == "+" and
|
|
156
|
+
attrs_p[10] == "+"
|
|
157
|
+
)
|
|
158
|
+
puts "#{line.chomp} CREATED!" if DEBUG
|
|
159
|
+
@created.push(line)
|
|
160
|
+
|
|
161
|
+
# everthing else is considered a modification
|
|
162
|
+
else
|
|
163
|
+
puts "#{line.chomp} MODIFIED CATCH ALL 1!" if DEBUG
|
|
164
|
+
@modified.push(line)
|
|
165
|
+
end
|
|
166
|
+
else
|
|
167
|
+
puts "#{line.chomp} MODIFIED CATCH ALL 2!" if DEBUG
|
|
168
|
+
@modified.push(line)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def set_output_filter_warn_err
|
|
173
|
+
filter = Array.new
|
|
174
|
+
|
|
175
|
+
# These are warnings and errors kicked out by rsync during it's run
|
|
176
|
+
filter.push("WARNING: .* failed verification -- update discarded \(will try again\)\.")
|
|
177
|
+
filter.push("IO error encountered -- skipping file deletion")
|
|
178
|
+
filter.push("file has vanished: .*")
|
|
179
|
+
filter.push("rsync (error|warning): .*")
|
|
180
|
+
filter.push("cannot delete non-empty directory: .*")
|
|
181
|
+
|
|
182
|
+
@output_filter_warn_err = /#{filter.join("|")}/
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def set_output_filter_created
|
|
186
|
+
filter = Array.new
|
|
187
|
+
|
|
188
|
+
# These should be rare, for some reason it doesn't show up in the itemized list
|
|
189
|
+
# and rsync doesn't count it as a 'created file', it seems like the only time
|
|
190
|
+
# this happens is if the destination root directory doesn't already exist.
|
|
191
|
+
filter.push("^created directory .*")
|
|
192
|
+
|
|
193
|
+
@output_filter_created = /#{filter[0]}/
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def set_output_filter_stats
|
|
197
|
+
filter = Array.new
|
|
198
|
+
|
|
199
|
+
# These are for rsync stats which are output after the transfer is complete
|
|
200
|
+
filter.push("(Number of files): (\\d+)")
|
|
201
|
+
filter.push("(Number of files transferred): (\\d+)")
|
|
202
|
+
filter.push("(Total file size): (\\d+) bytes")
|
|
203
|
+
filter.push("(Total transferred file size): (\\d+) bytes")
|
|
204
|
+
filter.push("(Literal data): (\\d+) bytes")
|
|
205
|
+
filter.push("(Matched data): (\\d+) bytes")
|
|
206
|
+
filter.push("(File list size): (\\d+)")
|
|
207
|
+
filter.push("(File list generation time): (.*) seconds")
|
|
208
|
+
filter.push("(File list transfer time): (.*) seconds")
|
|
209
|
+
filter.push("(Total bytes sent): (\\d+)")
|
|
210
|
+
filter.push("(Total bytes received): (\\d+)")
|
|
211
|
+
|
|
212
|
+
@output_filter_stats = /#{filter.join("|")}/
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def set_output_filter_excluded
|
|
216
|
+
# These are files/directories which have been excluded by a pattern we passed to rsync
|
|
217
|
+
@output_filter_excluded = /^\[generator\] (excluding|protecting) (file|directory) (.*) because of pattern (.*)$/
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def set_output_filter_duplicates
|
|
221
|
+
filter = Array.new
|
|
222
|
+
|
|
223
|
+
# These are file/directories which are the same in the sources list
|
|
224
|
+
filter.push("^removing duplicate name .* from file list .*")
|
|
225
|
+
|
|
226
|
+
@output_filter_duplicates = /#{filter[0]}/
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def set_output_filter_junk
|
|
230
|
+
filter = Array.new
|
|
231
|
+
|
|
232
|
+
# blank line
|
|
233
|
+
filter.push("^$")
|
|
234
|
+
# ignore line
|
|
235
|
+
filter.push("^sending incremental file list")
|
|
236
|
+
# ignore line
|
|
237
|
+
filter.push("^building file list ...")
|
|
238
|
+
# ignore line
|
|
239
|
+
filter.push("^expand file_list\s\w+")
|
|
240
|
+
# ignore line
|
|
241
|
+
filter.push("^rsync: expand\s\w+")
|
|
242
|
+
# ignore line
|
|
243
|
+
filter.push("^opening connection\s\w+")
|
|
244
|
+
# ignore line - should probably capture this info
|
|
245
|
+
filter.push("^total")
|
|
246
|
+
# ignore line - should probably capture this info
|
|
247
|
+
filter.push("^wrote")
|
|
248
|
+
# ignore line - should probably capture this info
|
|
249
|
+
filter.push("^sent")
|
|
250
|
+
# ignore line
|
|
251
|
+
filter.push("^done")
|
|
252
|
+
# ignore line
|
|
253
|
+
filter.push("^excluding")
|
|
254
|
+
# ignore line
|
|
255
|
+
filter.push("^hiding")
|
|
256
|
+
# ignore line
|
|
257
|
+
filter.push("^delta( |-)transmission (dis|en)abled")
|
|
258
|
+
# ignore line
|
|
259
|
+
filter.push("^deleting in \./")
|
|
260
|
+
# ignore line
|
|
261
|
+
filter.push("^opening connection using: ssh")
|
|
262
|
+
# ignore line
|
|
263
|
+
filter.push("^rsync\\[\\d+\\] \\(sender\\) heap statistics:")
|
|
264
|
+
# ignore line
|
|
265
|
+
filter.push("^rsync\\[\\d+\\] \\(server receiver\\) heap statistics:")
|
|
266
|
+
# ignore line
|
|
267
|
+
filter.push("^rsync\\[\\d+\\] \\(server generator\\) heap statistics:")
|
|
268
|
+
# ignore line
|
|
269
|
+
filter.push("^ arena:")
|
|
270
|
+
# ignore line
|
|
271
|
+
filter.push("^ ordblks:")
|
|
272
|
+
# ignore line
|
|
273
|
+
filter.push("^ smblks:")
|
|
274
|
+
# ignore line
|
|
275
|
+
filter.push("^ hblks:")
|
|
276
|
+
# ignore line
|
|
277
|
+
filter.push("^ hblkhd:")
|
|
278
|
+
# ignore line
|
|
279
|
+
filter.push("^ allmem:")
|
|
280
|
+
# ignore line
|
|
281
|
+
filter.push("^ usmblks:")
|
|
282
|
+
# ignore line
|
|
283
|
+
filter.push("^ fsmblks:")
|
|
284
|
+
# ignore line
|
|
285
|
+
filter.push("^ uordblks:")
|
|
286
|
+
# ignore line
|
|
287
|
+
filter.push("^ fordblks:")
|
|
288
|
+
# ignore line
|
|
289
|
+
filter.push("^ keepcost:")
|
|
290
|
+
|
|
291
|
+
@output_filter_junk = /#{filter.join("|")}/
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def cmd_run
|
|
295
|
+
u = CliUtils.new(@utility)
|
|
296
|
+
puts [u.utility_path, flags_run, @source, @destination].flatten.inspect if DEBUG
|
|
297
|
+
[u.utility_path, flags_run, @source, @destination].flatten
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def flag_delete
|
|
301
|
+
flag_add("--delete")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def flag_compress
|
|
305
|
+
flag_add("-z")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def flag_dryrun
|
|
309
|
+
flag_add("-n")
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def flag_verbose
|
|
313
|
+
flag_add("-vv")
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def flag_archive
|
|
317
|
+
flag_add("-a")
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def flag_itemized
|
|
321
|
+
flag_add("-i")
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def flag_checksum
|
|
325
|
+
flag_add("-c")
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def flag_stats
|
|
329
|
+
flag_add("--stats")
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def flag_bwlimit(kbps)
|
|
333
|
+
flag_add("--bwlimit=#{kbps}")
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def flag_rsync_path(path)
|
|
337
|
+
flag_add("--rsync-path=#{path}")
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def flag_exclude(pattern)
|
|
341
|
+
flag_add("--exclude=#{pattern[1..-1]}")
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def set_flags_base
|
|
345
|
+
flag_archive
|
|
346
|
+
flag_verbose
|
|
347
|
+
flag_itemized
|
|
348
|
+
flag_delete
|
|
349
|
+
flag_stats
|
|
350
|
+
end
|
|
351
|
+
end
|
data/lib/cli_utils.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
|
|
3
|
+
class CliUtils
|
|
4
|
+
attr_accessor :utility, :utility_path
|
|
5
|
+
|
|
6
|
+
def initialize(utility)
|
|
7
|
+
@utility = utility
|
|
8
|
+
@utility_path
|
|
9
|
+
@version
|
|
10
|
+
valid?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def valid?
|
|
14
|
+
find_utility
|
|
15
|
+
get_version
|
|
16
|
+
supp_util_versions
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def find_utility
|
|
20
|
+
cmd = "which #{@utility}"
|
|
21
|
+
ph = IO.popen(cmd)
|
|
22
|
+
output = ph.readlines()
|
|
23
|
+
ph.close
|
|
24
|
+
if($? != 0)
|
|
25
|
+
puts "Cannot find required utility #{@utility} in path."
|
|
26
|
+
false
|
|
27
|
+
else
|
|
28
|
+
@utility_path = output[0].chomp
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get_version
|
|
33
|
+
version = Array.new
|
|
34
|
+
if(@utility_path == nil)
|
|
35
|
+
find_utility
|
|
36
|
+
end
|
|
37
|
+
cmd = "#{@utility_path} --version"
|
|
38
|
+
Open3.popen2e(cmd) { |i,oe,t|
|
|
39
|
+
oe.each do |line|
|
|
40
|
+
version.push(line)
|
|
41
|
+
end
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@version = version.to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def supp_util_versions
|
|
48
|
+
case @utility
|
|
49
|
+
when "rsync"
|
|
50
|
+
supp_version = 3
|
|
51
|
+
version_pattern.match(@version)
|
|
52
|
+
if($1.to_s < supp_version.to_s)
|
|
53
|
+
puts "Unsupported version of 'rsync', use version #{supp_version} or higher."
|
|
54
|
+
false
|
|
55
|
+
else
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def version_pattern
|
|
62
|
+
case @utility
|
|
63
|
+
when "rsync"
|
|
64
|
+
Regexp.new(/rsync version ([\d\.]+) protocol version (\d+)/)
|
|
65
|
+
else
|
|
66
|
+
puts "Not a supported utility: #{utility}"
|
|
67
|
+
exit 1
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/host.rb
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
require 'system_config'
|
|
2
|
+
require 'find'
|
|
3
|
+
|
|
4
|
+
class Host
|
|
5
|
+
attr_accessor :host, :domain, :host_yml, :autocreate
|
|
6
|
+
attr_reader :hosts_dir, :host_dir, :found_host, :host_yml_values
|
|
7
|
+
|
|
8
|
+
def initialize(hostname="")
|
|
9
|
+
@autocreate = false
|
|
10
|
+
@hostname = hostname
|
|
11
|
+
@host = ""
|
|
12
|
+
@domain = ""
|
|
13
|
+
@host_dir = ""
|
|
14
|
+
@host_yml = ""
|
|
15
|
+
@found_host = ""
|
|
16
|
+
@host_yml_values = Hash.new
|
|
17
|
+
|
|
18
|
+
@hosts_dir = SYSTEM_CONFIG['hosts_dir']
|
|
19
|
+
prepare_hosts_dir
|
|
20
|
+
set_hostname_domain
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def set_hostname_domain
|
|
24
|
+
if @hostname.include? "."
|
|
25
|
+
@host = @hostname.split(".").shift
|
|
26
|
+
@domain = @hostname.split(".")[1..-1].join(".")
|
|
27
|
+
else
|
|
28
|
+
@host = @hostname
|
|
29
|
+
@domain = ""
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_host
|
|
34
|
+
begin
|
|
35
|
+
Find.find(@hosts_dir) do |path|
|
|
36
|
+
# These are the 3 things which define a 'host' in the system, 2 if there's no domain
|
|
37
|
+
if path =~ /#{@domain}/ and path =~ /#{@host}$/ and File.directory? "#{path}/overrides"
|
|
38
|
+
@host_dir = path
|
|
39
|
+
@host_yml = "#{@host_dir}/host.yml"
|
|
40
|
+
# Since this is a path with more than just the domain and hostname in it,
|
|
41
|
+
# we need to test if this is a hostname without a domain in front. If so
|
|
42
|
+
# we need to set @found_host differently. We test to see if the full path
|
|
43
|
+
# minus the hostname is equal to the '@hosts_dir' because someone might
|
|
44
|
+
# name create a host without it having a domain component.
|
|
45
|
+
if "/#{path.split('/')[1..-2].join('/')}" == @hosts_dir
|
|
46
|
+
# If we get here, it means this path did not have a domain component
|
|
47
|
+
@found_host = "#{path.split('/')[-1]}"
|
|
48
|
+
else
|
|
49
|
+
# This path did have a domain component
|
|
50
|
+
@found_host = "#{path.split('/')[-2]}/#{path.split('/')[-1]}"
|
|
51
|
+
end
|
|
52
|
+
# This should set @host_yml_values
|
|
53
|
+
unless host_valid?
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
prepare_host
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
rescue Exception => e
|
|
61
|
+
puts "Tried to find host: #{@hostname} in #{@hosts_dir} during Host.find_host, received exception #{e}"
|
|
62
|
+
exit 1
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def prepare_host
|
|
67
|
+
if @autocreate
|
|
68
|
+
@host_dir = "#{@hosts_dir}/#{@domain}/#{@host}"
|
|
69
|
+
@host_yml = "#{@host_dir}/host.yml"
|
|
70
|
+
# This should set @host_yml_values
|
|
71
|
+
unless host_valid?
|
|
72
|
+
return false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def host_valid?
|
|
78
|
+
if host_dir?
|
|
79
|
+
if host_yml?
|
|
80
|
+
return true
|
|
81
|
+
else
|
|
82
|
+
puts "Error: @host_yml: #{@host_yml} isn't valid!"
|
|
83
|
+
return false
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
puts "Error: @host_dir: #{@host_dir} does not exist!"
|
|
87
|
+
return false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def host_yml?
|
|
92
|
+
unless File.exists? @host_yml then
|
|
93
|
+
prepare_host_yml
|
|
94
|
+
yml_valid?
|
|
95
|
+
else
|
|
96
|
+
yml_valid?
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def prepare_host_yml
|
|
101
|
+
begin
|
|
102
|
+
f = open(@host_yml, 'w+')
|
|
103
|
+
f.puts "config:"
|
|
104
|
+
f.puts " package_base: test_dist"
|
|
105
|
+
f.puts " release_tag: current"
|
|
106
|
+
f.puts " rsync_path:"
|
|
107
|
+
f.puts " ssh_port:"
|
|
108
|
+
f.puts " session_mode:"
|
|
109
|
+
f.puts "include:"
|
|
110
|
+
f.puts " # - section/packagename"
|
|
111
|
+
f.puts "exclude:"
|
|
112
|
+
f.puts " # - /somefile"
|
|
113
|
+
f.puts "exclude_backup:"
|
|
114
|
+
f.puts " # - /somefile"
|
|
115
|
+
f.puts "execute:"
|
|
116
|
+
f.puts " # - some arbitrary command"
|
|
117
|
+
f.close
|
|
118
|
+
rescue Exception => e
|
|
119
|
+
puts "Tried to open #{@host_yml} during 'Host.prepare_host_yml', received exception: #{e}"
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def yml_valid?
|
|
125
|
+
begin
|
|
126
|
+
@host_yml_values = YAML.load_file(@host_yml)
|
|
127
|
+
|
|
128
|
+
valid_keys = ['config','include','exclude','exclude_backup','execute']
|
|
129
|
+
config_valid_subkeys = ['package_base','release_tag','rsync_path','ssh_port','session_mode']
|
|
130
|
+
|
|
131
|
+
# Begin check for basic elements
|
|
132
|
+
@host_yml_values.each_pair do |key,val|
|
|
133
|
+
if key == 'config' and val.class == Hash
|
|
134
|
+
val.each_pair do |skey,sval|
|
|
135
|
+
unless config_valid_subkeys.include? skey
|
|
136
|
+
puts "Invalid sub-option for 'config:' => #{skey} detected in #{@host_yml}. Exiting..."
|
|
137
|
+
return false
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
unless valid_keys.include? key
|
|
142
|
+
puts "Invalid option => #{key} detected in #{@host_yml}. Exiting..."
|
|
143
|
+
return false
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
rescue Exception => e
|
|
148
|
+
puts "Tried to load #{@host_yml} during 'Host.yml_valid?', received exception: #{e}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def host_dir?
|
|
153
|
+
if File.directory? @host_dir
|
|
154
|
+
true
|
|
155
|
+
else
|
|
156
|
+
prepare_host_dir
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def hosts_dir?
|
|
161
|
+
if File.directory? @hosts_dir
|
|
162
|
+
true
|
|
163
|
+
else
|
|
164
|
+
prepare_hosts_dir
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def prompt_host_dir
|
|
169
|
+
# This isn't used right now
|
|
170
|
+
print "Configuration directory doesn't exist for #{@host}, create? (N/y): "
|
|
171
|
+
input = gets.chomp
|
|
172
|
+
if input =~ /[yY]/ then true else exit end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def prepare_host_dir
|
|
176
|
+
begin
|
|
177
|
+
FileUtils.mkdir_p "#{@host_dir}/overrides"
|
|
178
|
+
true
|
|
179
|
+
rescue Exception => e
|
|
180
|
+
puts "Tried to create #{@host_dir} during 'Host.prepare_host_dir', received exception: #{e}"
|
|
181
|
+
false
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def prepare_hosts_dir
|
|
186
|
+
begin
|
|
187
|
+
FileUtils.mkdir_p @hosts_dir
|
|
188
|
+
true
|
|
189
|
+
rescue Exception => e
|
|
190
|
+
puts "Tried to create #{@hosts_dir} during 'Host.prepare_hosts_dir', received exception: #{e}"
|
|
191
|
+
false
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
data/lib/package.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'system_config'
|
|
2
|
+
require 'find'
|
|
3
|
+
|
|
4
|
+
class Package
|
|
5
|
+
attr_accessor :package, :package_yml, :package_base, :package_release_tag, :package_dir
|
|
6
|
+
attr_reader :packages_dir
|
|
7
|
+
|
|
8
|
+
def initialize(package="")
|
|
9
|
+
@package = package
|
|
10
|
+
@package_base = ""
|
|
11
|
+
@package_release_tag = ""
|
|
12
|
+
@package_yml = ""
|
|
13
|
+
@package_dir = ""
|
|
14
|
+
|
|
15
|
+
@packages_dir = SYSTEM_CONFIG['packages_dir']
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set_properties(obj)
|
|
19
|
+
@package_base = obj.host_yml_values['config']['package_base']
|
|
20
|
+
@package_release_tag = obj.host_yml_values['config']['release_tag']
|
|
21
|
+
@package_dir = "#{@packages_dir}/#{@package_base}/#{package}/#{@package_release_tag}"
|
|
22
|
+
@package_yml = "#{@package_dir}/package.yml"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def package_valid?
|
|
26
|
+
if package_dir?
|
|
27
|
+
if package_yml?
|
|
28
|
+
return true
|
|
29
|
+
else
|
|
30
|
+
puts "Error: @package_yml: #{@package_yml} isn't valid!"
|
|
31
|
+
return false
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
puts "Error: @package_dir: #{@package_dir} does not exist!"
|
|
35
|
+
return false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def package_yml?
|
|
40
|
+
File.exists? @package_yml
|
|
41
|
+
yml_valid?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def yml_valid?
|
|
45
|
+
begin
|
|
46
|
+
package_yml = YAML.load_file(@package_yml)
|
|
47
|
+
|
|
48
|
+
valid_keys = ['rank','include','exclude','exclude_backup','execute']
|
|
49
|
+
|
|
50
|
+
# Begin check for basic elements
|
|
51
|
+
package_yml.each_pair do |key,val|
|
|
52
|
+
unless valid_keys.include? key
|
|
53
|
+
puts "Invalid option => #{key} detected in #{@package_yml}. Exiting..."
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
rescue Exception => e
|
|
58
|
+
puts "Tried to load #{@package_yml} during 'Package.yml_valid?', received exception: #{e}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def package_dir?
|
|
63
|
+
# Package isn't valid unless it has a 'files' directory under it
|
|
64
|
+
File.directory? "#{@package_dir}/files"
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/ssh_funcs.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'system_config'
|
|
2
|
+
require 'net/ssh'
|
|
3
|
+
|
|
4
|
+
class SSHFuncs
|
|
5
|
+
attr_reader :output
|
|
6
|
+
attr_accessor :hostname, :remote_cmd
|
|
7
|
+
|
|
8
|
+
def initialize(hostname)
|
|
9
|
+
@hostname = hostname
|
|
10
|
+
@output
|
|
11
|
+
@run_cmd
|
|
12
|
+
@ssh
|
|
13
|
+
ssh_channel
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ssh_channel
|
|
17
|
+
begin
|
|
18
|
+
@ssh = Net::SSH.start(@hostname, 'root')
|
|
19
|
+
rescue Exception => e
|
|
20
|
+
puts "Couldn't establish SSH session with #{@hostname}, received exception: #{e}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def run_cmd(cmd)
|
|
25
|
+
# capture all stderr and stdout output from a remote process
|
|
26
|
+
@output = @ssh.exec!(cmd)
|
|
27
|
+
puts "SSH CMD OUTPUT: #{@output}" if DEBUG
|
|
28
|
+
end
|
|
29
|
+
end
|