elecksee 1.0.2 → 1.0.4
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/CHANGELOG.md +4 -0
- data/bin/lxc-awesome-ephemeral +57 -2
- data/lib/elecksee/clone.rb +15 -0
- data/lib/elecksee/ephemeral.rb +310 -0
- data/lib/elecksee/helpers.rb +70 -0
- data/lib/elecksee/lxc.rb +409 -5
- data/lib/elecksee/lxc_file_config.rb +86 -0
- data/lib/elecksee/storage/overlay_directory.rb +31 -0
- data/lib/elecksee/storage/overlay_mount.rb +60 -0
- data/lib/elecksee/storage/virtual_device.rb +81 -0
- data/lib/elecksee/version.rb +1 -1
- metadata +9 -39
- data/Gemfile.lock +0 -18
- data/lib/elecksee/awesome.rb +0 -14
- data/lib/elecksee/vendor/lxc/CHANGELOG.md +0 -37
- data/lib/elecksee/vendor/lxc/Gemfile +0 -4
- data/lib/elecksee/vendor/lxc/Gemfile.lock +0 -41
- data/lib/elecksee/vendor/lxc/README.md +0 -112
- data/lib/elecksee/vendor/lxc/attributes/default.rb +0 -28
- data/lib/elecksee/vendor/lxc/files/default/knife_lxc +0 -228
- data/lib/elecksee/vendor/lxc/files/default/lxc-awesome-ephemeral +0 -495
- data/lib/elecksee/vendor/lxc/libraries/lxc.rb +0 -366
- data/lib/elecksee/vendor/lxc/libraries/lxc_expanded_resources.rb +0 -40
- data/lib/elecksee/vendor/lxc/libraries/lxc_file_config.rb +0 -84
- data/lib/elecksee/vendor/lxc/libraries/monkey.rb +0 -51
- data/lib/elecksee/vendor/lxc/metadata.rb +0 -12
- data/lib/elecksee/vendor/lxc/providers/config.rb +0 -75
- data/lib/elecksee/vendor/lxc/providers/container.rb +0 -318
- data/lib/elecksee/vendor/lxc/providers/default.rb +0 -57
- data/lib/elecksee/vendor/lxc/providers/ephemeral.rb +0 -40
- data/lib/elecksee/vendor/lxc/providers/fstab.rb +0 -30
- data/lib/elecksee/vendor/lxc/providers/interface.rb +0 -45
- data/lib/elecksee/vendor/lxc/providers/service.rb +0 -53
- data/lib/elecksee/vendor/lxc/recipes/containers.rb +0 -13
- data/lib/elecksee/vendor/lxc/recipes/default.rb +0 -58
- data/lib/elecksee/vendor/lxc/recipes/install_dependencies.rb +0 -15
- data/lib/elecksee/vendor/lxc/recipes/knife.rb +0 -37
- data/lib/elecksee/vendor/lxc/resources/config.rb +0 -19
- data/lib/elecksee/vendor/lxc/resources/container.rb +0 -54
- data/lib/elecksee/vendor/lxc/resources/default.rb +0 -12
- data/lib/elecksee/vendor/lxc/resources/ephemeral.rb +0 -13
- data/lib/elecksee/vendor/lxc/resources/fstab.rb +0 -12
- data/lib/elecksee/vendor/lxc/resources/interface.rb +0 -13
- data/lib/elecksee/vendor/lxc/resources/service.rb +0 -5
- data/lib/elecksee/vendor/lxc/templates/default/client.rb.erb +0 -13
- data/lib/elecksee/vendor/lxc/templates/default/default-lxc.erb +0 -3
- data/lib/elecksee/vendor/lxc/templates/default/file_content.erb +0 -2
- data/lib/elecksee/vendor/lxc/templates/default/fstab.erb +0 -5
- data/lib/elecksee/vendor/lxc/templates/default/interface.erb +0 -27
data/lib/elecksee/lxc.rb
CHANGED
@@ -1,7 +1,411 @@
|
|
1
|
+
require 'elecksee/helpers'
|
1
2
|
require 'mixlib/shellout'
|
3
|
+
require 'pathname'
|
4
|
+
require 'tmpdir'
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
class Lxc
|
7
|
+
|
8
|
+
# Pathname#join does not act like File#join when joining paths that
|
9
|
+
# begin with '/', and that's dumb. So we'll make our own Pathname,
|
10
|
+
# with a #join that uses File
|
11
|
+
class Pathname < ::Pathname
|
12
|
+
def join(*args)
|
13
|
+
self.class.new(::File.join(self.to_path, *args))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
include Helpers
|
18
|
+
|
19
|
+
attr_reader :name, :base_path, :lease_file, :preferred_device
|
20
|
+
|
21
|
+
class << self
|
22
|
+
|
23
|
+
include Helpers
|
24
|
+
|
25
|
+
attr_accessor :use_sudo
|
26
|
+
attr_accessor :base_path
|
27
|
+
|
28
|
+
def sudo
|
29
|
+
case use_sudo
|
30
|
+
when TrueClass
|
31
|
+
'sudo '
|
32
|
+
when String
|
33
|
+
"#{use_sudo} "
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_path
|
38
|
+
@base_path || '/var/lib/lxc'
|
39
|
+
end
|
40
|
+
|
41
|
+
# List running containers
|
42
|
+
def running
|
43
|
+
full_list[:running]
|
44
|
+
end
|
45
|
+
|
46
|
+
# List stopped containers
|
47
|
+
def stopped
|
48
|
+
full_list[:stopped]
|
49
|
+
end
|
50
|
+
|
51
|
+
# List frozen containers
|
52
|
+
def frozen
|
53
|
+
full_list[:frozen]
|
54
|
+
end
|
55
|
+
|
56
|
+
# name:: name of container
|
57
|
+
# Returns if container exists
|
58
|
+
def exists?(name)
|
59
|
+
list.include?(name)
|
60
|
+
end
|
61
|
+
|
62
|
+
# List of containers
|
63
|
+
def list
|
64
|
+
Dir.glob(File.join(base_path, '*')).map do |item|
|
65
|
+
if(File.directory?(item) && File.exists?(File.join(item, 'config')))
|
66
|
+
File.basename(item)
|
67
|
+
end
|
68
|
+
end.compact
|
69
|
+
end
|
70
|
+
|
71
|
+
# name:: Name of container
|
72
|
+
# Returns information about given container
|
73
|
+
def info(name)
|
74
|
+
res = {:state => nil, :pid => nil}
|
75
|
+
info = run_command("#{sudo}lxc-info -n #{name}").stdout.split("\n")
|
76
|
+
if(info.first)
|
77
|
+
parts = info.first.split(' ')
|
78
|
+
res[:state] = parts.last.downcase.to_sym
|
79
|
+
parts = info.last.split(' ')
|
80
|
+
res[:pid] = parts.last.to_i
|
81
|
+
res
|
82
|
+
else
|
83
|
+
res[:state] = :unknown
|
84
|
+
res[:pid] = -1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return full container information list
|
89
|
+
def full_list
|
90
|
+
res = {}
|
91
|
+
list.each do |item|
|
92
|
+
item_info = info(item)
|
93
|
+
res[item_info[:state]] ||= []
|
94
|
+
res[item_info[:state]] << item
|
95
|
+
end
|
96
|
+
res
|
97
|
+
end
|
98
|
+
|
99
|
+
# ip:: IP address
|
100
|
+
# Returns if IP address is alive
|
101
|
+
def connection_alive?(ip)
|
102
|
+
%x{ping -c 1 -W 1 #{ip}}
|
103
|
+
$?.exitstatus == 0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# name:: name of container
|
108
|
+
# args:: Argument hash
|
109
|
+
# - :base_path -> path to container directory
|
110
|
+
# - :dnsmasq_lease_file -> path to lease file
|
111
|
+
# - :net_device -> network device to use within container for ssh connection
|
112
|
+
def initialize(name, args={})
|
113
|
+
@name = name
|
114
|
+
@base_path = args[:base_path] || self.class.base_path
|
115
|
+
@lease_file = args[:dnsmasq_lease_file] || '/var/lib/misc/dnsmasq.leases'
|
116
|
+
@preferred_device = args[:net_device]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns if container exists
|
120
|
+
def exists?
|
121
|
+
self.class.exists?(name)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns if container is running
|
125
|
+
def running?
|
126
|
+
self.class.info(name)[:state] == :running
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns if container is stopped
|
130
|
+
def stopped?
|
131
|
+
self.class.info(name)[:state] == :stopped
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns if container is frozen
|
135
|
+
def frozen?
|
136
|
+
self.class.info(name)[:state] == :frozen
|
137
|
+
end
|
138
|
+
|
139
|
+
# retries:: Number of discovery attempt (3 second sleep intervals)
|
140
|
+
# Returns container IP
|
141
|
+
def container_ip(retries=0, raise_on_fail=false)
|
142
|
+
(retries.to_i + 1).times do
|
143
|
+
ip = proc_detected_address || hw_detected_address || leased_address || lxc_stored_address
|
144
|
+
if(ip.is_a?(Array))
|
145
|
+
# Filter any found loopbacks
|
146
|
+
ip.delete_if{|info| info[:device].start_with?('lo') }
|
147
|
+
ip = ip.detect do |info|
|
148
|
+
if(@preferred_device)
|
149
|
+
info[:device] == @preferred_device
|
150
|
+
else
|
151
|
+
true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
ip = ip[:address] if ip
|
155
|
+
end
|
156
|
+
return ip if ip && self.class.connection_alive?(ip)
|
157
|
+
log.warn "LXC IP discovery: Failed to detect live IP"
|
158
|
+
sleep(3) if retries > 0
|
159
|
+
end
|
160
|
+
raise "Failed to detect live IP address for container: #{name}" if raise_on_fail
|
161
|
+
end
|
162
|
+
|
163
|
+
# Container address via lxc config file
|
164
|
+
def lxc_stored_address
|
165
|
+
if(File.exists?(container_config))
|
166
|
+
ip = File.readlines(container_config).detect{|line|
|
167
|
+
line.include?('ipv4')
|
168
|
+
}.to_s.split('=').last.to_s.strip
|
169
|
+
if(ip.to_s.empty?)
|
170
|
+
nil
|
171
|
+
else
|
172
|
+
log.info "LXC Discovery: Found container address via storage: #{ip}"
|
173
|
+
ip
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Container address via dnsmasq lease
|
179
|
+
def leased_address
|
180
|
+
ip = nil
|
181
|
+
if(File.exists?(@lease_file))
|
182
|
+
leases = File.readlines(@lease_file).map{|line| line.split(' ')}
|
183
|
+
leases.each do |lease|
|
184
|
+
if(lease.include?(name))
|
185
|
+
ip = lease[2]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
if(ip.to_s.empty?)
|
190
|
+
nil
|
191
|
+
else
|
192
|
+
log.info "LXC Discovery: Found container address via DHCP lease: #{ip}"
|
193
|
+
ip
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def hw_detected_address
|
198
|
+
if(container_config.readable?)
|
199
|
+
hw = File.readlines(container_config).detect{|line|
|
200
|
+
line.include?('hwaddr')
|
201
|
+
}.to_s.split('=').last.to_s.downcase
|
202
|
+
if(File.exists?(container_config) && !hw.empty?)
|
203
|
+
running? # need to do a list!
|
204
|
+
ip = File.readlines('/proc/net/arp').detect{|line|
|
205
|
+
line.downcase.include?(hw)
|
206
|
+
}.to_s.split(' ').first.to_s.strip
|
207
|
+
if(ip.to_s.empty?)
|
208
|
+
nil
|
209
|
+
else
|
210
|
+
log.info "LXC Discovery: Found container address via HW addr: #{ip}"
|
211
|
+
ip
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def proc_detected_address(base='/run/netns')
|
218
|
+
if(pid != -1)
|
219
|
+
Dir.mktmpdir do |t_dir|
|
220
|
+
name = File.basename(t_dir)
|
221
|
+
path = File.join(base, name)
|
222
|
+
system("#{sudo}mkdir -p #{base}")
|
223
|
+
system("#{sudo}ln -s /proc/#{pid}/ns/net #{path}")
|
224
|
+
res = %x{#{sudo}ip netns exec #{name} ip -4 addr show scope global | grep inet}
|
225
|
+
system("#{sudo}rm -f #{path}")
|
226
|
+
ips = res.split("\n").map do |line|
|
227
|
+
parts = line.split(' ')
|
228
|
+
{:address => parts[1].to_s.sub(%r{/.+$}, ''), :device => parts.last}
|
229
|
+
end
|
230
|
+
ips.empty? ? nil : ips
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Full path to container
|
236
|
+
def container_path
|
237
|
+
Pathname.new(@base_path).join(name)
|
238
|
+
end
|
239
|
+
alias_method :path, :container_path
|
240
|
+
|
241
|
+
# Full path to container configuration file
|
242
|
+
def container_config
|
243
|
+
container_path.join('config')
|
244
|
+
end
|
245
|
+
alias_method :config, :container_config
|
246
|
+
|
247
|
+
def container_rootfs
|
248
|
+
container_path.join('rootfs')
|
249
|
+
end
|
250
|
+
alias_method :rootfs, :container_rootfs
|
251
|
+
|
252
|
+
def expand_path(path)
|
253
|
+
container_rootfs.join(path)
|
254
|
+
end
|
255
|
+
|
256
|
+
def state
|
257
|
+
self.class.info(name)[:state]
|
258
|
+
end
|
259
|
+
|
260
|
+
def pid
|
261
|
+
self.class.info(name)[:pid]
|
262
|
+
end
|
263
|
+
|
264
|
+
# Start the container
|
265
|
+
def start(*args)
|
266
|
+
if(args.include?(:no_daemon))
|
267
|
+
run_command("#{sudo}lxc-start -n #{name}")
|
268
|
+
else
|
269
|
+
run_command("#{sudo}lxc-start -n #{name} -d")
|
270
|
+
wait_for_state(:running)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Stop the container
|
275
|
+
def stop
|
276
|
+
run_command("#{sudo}lxc-stop -n #{name}", :allow_failure_retry => 3)
|
277
|
+
wait_for_state(:stopped)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Freeze the container
|
281
|
+
def freeze
|
282
|
+
run_command("#{sudo}lxc-freeze -n #{name}")
|
283
|
+
wait_for_state(:frozen)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Unfreeze the container
|
287
|
+
def unfreeze
|
288
|
+
run_command("#{sudo}lxc-unfreeze -n #{name}")
|
289
|
+
wait_for_state(:running)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Shutdown the container
|
293
|
+
def shutdown
|
294
|
+
run_command("#{sudo}lxc-shutdown -n #{name}")
|
295
|
+
wait_for_state(:stopped, :timeout => 120)
|
296
|
+
# This block is for fedora/centos/anyone else that does not like lxc-shutdown
|
297
|
+
if(running?)
|
298
|
+
container_command('shutdown -h now')
|
299
|
+
wait_for_state(:stopped, :timeout => 120)
|
300
|
+
# If still running here, something is wrong
|
301
|
+
if(running?)
|
302
|
+
raise "Failed to shutdown container: #{name}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def direct_container_command(command, args={})
|
308
|
+
com = "#{sudo}ssh root@#{args[:ip] || container_ip} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no '#{command}'"
|
309
|
+
begin
|
310
|
+
cmd = Mixlib::ShellOut.new(com,
|
311
|
+
:live_stream => args[:live_stream],
|
312
|
+
:timeout => args[:timeout] || 1200
|
313
|
+
)
|
314
|
+
cmd.run_command
|
315
|
+
cmd.error!
|
316
|
+
true
|
317
|
+
rescue
|
318
|
+
raise if args[:raise_on_failure]
|
319
|
+
false
|
320
|
+
end
|
321
|
+
end
|
322
|
+
alias_method :knife_container, :direct_container_command
|
323
|
+
|
324
|
+
# Simple helper to shell out
|
325
|
+
def run_command(cmd, args={})
|
326
|
+
retries = args[:allow_failure_retry].to_i
|
327
|
+
begin
|
328
|
+
shlout = Mixlib::ShellOut.new(cmd,
|
329
|
+
:logger => defined?(Chef) ? Chef::Log.logger : log,
|
330
|
+
:live_stream => args[:livestream] ? nil : STDOUT,
|
331
|
+
:timeout => args[:timeout] || 1200,
|
332
|
+
:environment => {'HOME' => detect_home}
|
333
|
+
)
|
334
|
+
shlout.run_command
|
335
|
+
shlout.error!
|
336
|
+
shlout
|
337
|
+
rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout
|
338
|
+
if(retries > 0)
|
339
|
+
log.warn "LXC run command failed: #{cmd}"
|
340
|
+
log.warn "Retrying command. #{args[:allow_failure_retry].to_i - retries} of #{args[:allow_failure_retry].to_i} retries remain"
|
341
|
+
sleep(0.3)
|
342
|
+
retries -= 1
|
343
|
+
retry
|
344
|
+
elsif(args[:allow_failure])
|
345
|
+
true
|
346
|
+
else
|
347
|
+
raise
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def wait_for_state(desired_state, args={})
|
353
|
+
args[:sleep_interval] ||= 1.0
|
354
|
+
wait_total = 0.0
|
355
|
+
until(state == desired_state.to_sym || (args[:timeout].to_i > 0 && wait_total.to_i > args[:timeout].to_i))
|
356
|
+
sleep(args[:sleep_interval])
|
357
|
+
wait_total += args[:sleep_interval]
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Detect HOME environment variable. If not an acceptable
|
362
|
+
# value, set to /root or /tmp
|
363
|
+
def detect_home(set_if_missing=false)
|
364
|
+
if(ENV['HOME'] && Pathname.new(ENV['HOME']).absolute?)
|
365
|
+
ENV['HOME']
|
366
|
+
else
|
367
|
+
home = File.directory?('/root') && File.writable?('/root') ? '/root' : '/tmp'
|
368
|
+
if(set_if_missing)
|
369
|
+
ENV['HOME'] = home
|
370
|
+
end
|
371
|
+
home
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# cmd:: Shell command string
|
376
|
+
# retries:: Number of retry attempts (1 second sleep interval)
|
377
|
+
# Runs command in container via ssh
|
378
|
+
def container_command(cmd, retries=1)
|
379
|
+
begin
|
380
|
+
detect_home(true)
|
381
|
+
direct_container_command(cmd,
|
382
|
+
:ip => container_ip(5),
|
383
|
+
:live_stream => STDOUT,
|
384
|
+
:raise_on_failure => true
|
385
|
+
)
|
386
|
+
rescue => e
|
387
|
+
if(retries.to_i > 0)
|
388
|
+
log.info "Encountered error running container command (#{cmd}): #{e}"
|
389
|
+
log.info "Retrying command..."
|
390
|
+
retries = retries.to_i - 1
|
391
|
+
sleep(1)
|
392
|
+
retry
|
393
|
+
else
|
394
|
+
raise e
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def log
|
400
|
+
if(defined?(Chef))
|
401
|
+
Chef::Log
|
402
|
+
else
|
403
|
+
unless(@logger)
|
404
|
+
require 'logger'
|
405
|
+
@logger = Logger.new('/dev/null')
|
406
|
+
end
|
407
|
+
@logger
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Lxc
|
2
|
+
class FileConfig
|
3
|
+
|
4
|
+
attr_reader :network
|
5
|
+
attr_reader :base
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def generate_config(resource)
|
9
|
+
config = []
|
10
|
+
config << "lxc.utsname = #{resource.utsname}"
|
11
|
+
if(resource.aa_profile)
|
12
|
+
config << "lxc.aa_profile = #{resource.aa_profile}"
|
13
|
+
end
|
14
|
+
[resource.network].flatten.each do |net_hash|
|
15
|
+
nhsh = Mash.new(net_hash)
|
16
|
+
flags = nhsh.delete(:flags)
|
17
|
+
%w(type link).each do |k|
|
18
|
+
config << "lxc.network.#{k} = #{nhsh.delete(k)}" if nhsh[k]
|
19
|
+
end
|
20
|
+
nhsh.each_pair do |k,v|
|
21
|
+
config << "lxc.network.#{k} = #{v}"
|
22
|
+
end
|
23
|
+
if(flags)
|
24
|
+
config << "lxc.network.flags = #{flags}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
if(resource.cap_drop)
|
28
|
+
config << "lxc.cap.drop = #{Array(resource.cap_drop).join(' ')}"
|
29
|
+
end
|
30
|
+
%w(pts tty arch devttydir mount mount_entry rootfs rootfs_mount pivotdir).each do |k|
|
31
|
+
config << "lxc.#{k.sub('_', '.')} = #{resource.send(k)}" if resource.send(k)
|
32
|
+
end
|
33
|
+
prefix = 'lxc.cgroup'
|
34
|
+
resource.cgroup.each_pair do |key, value|
|
35
|
+
if(value.is_a?(Array))
|
36
|
+
value.each do |val|
|
37
|
+
config << "#{prefix}.#{key} = #{val}"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
config << "#{prefix}.#{key} = #{value}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
config.join("\n") + "\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(path)
|
49
|
+
raise 'LXC config file not found' unless File.exists?(path)
|
50
|
+
@path = path
|
51
|
+
@network = []
|
52
|
+
@base = Mash.new
|
53
|
+
parse!
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def parse!
|
59
|
+
cur_net = nil
|
60
|
+
File.readlines(@path).each do |line|
|
61
|
+
if(line.start_with?('lxc.network'))
|
62
|
+
parts = line.split('=')
|
63
|
+
name = parts.first.split('.').last.strip
|
64
|
+
if(name.to_sym == :type)
|
65
|
+
@network << cur_net if cur_net
|
66
|
+
cur_net = Mash.new
|
67
|
+
end
|
68
|
+
if(cur_net)
|
69
|
+
cur_net[name] = parts.last.strip
|
70
|
+
else
|
71
|
+
raise "Expecting 'lxc.network.type' to start network config block. Found: 'lxc.network.#{name}'"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
parts = line.split('=')
|
75
|
+
name = parts.first.sub('lxc.', '').strip
|
76
|
+
if(@base[name])
|
77
|
+
@base[name] = [@base[name], parts.last.strip].flatten
|
78
|
+
else
|
79
|
+
@base[name] = parts.last
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
@network << cur_net if cur_net
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'elecksee/helpers'
|
2
|
+
|
3
|
+
class Lxc
|
4
|
+
class OverlayDirectory
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
attr_reader :tmp_dir
|
8
|
+
|
9
|
+
def initialize(name, args={})
|
10
|
+
@name = name
|
11
|
+
@tmp_dir = args[:tmp_dir] || '/tmp/lxc/ephemerals'
|
12
|
+
create
|
13
|
+
end
|
14
|
+
|
15
|
+
def overlay_path
|
16
|
+
File.join(tmp_dir, 'virt-overlays', name)
|
17
|
+
end
|
18
|
+
alias_method :target_path, :overlay_path
|
19
|
+
|
20
|
+
def create
|
21
|
+
unless(File.directory?(overlay_path))
|
22
|
+
FileUtils.mkdir_p(overlay_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy
|
27
|
+
FileUtils.rm_rf(overlay_path) if File.directory?(overlay_path)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'elecksee/helpers'
|
2
|
+
|
3
|
+
class Lxc
|
4
|
+
class OverlayMount
|
5
|
+
|
6
|
+
include Helpers
|
7
|
+
|
8
|
+
attr_reader :base
|
9
|
+
attr_reader :overlay
|
10
|
+
attr_reader :target
|
11
|
+
attr_reader :overlay_type
|
12
|
+
|
13
|
+
def initialize(args={})
|
14
|
+
validate!(args)
|
15
|
+
@base = args[:base]
|
16
|
+
@overlay = args[:overlay]
|
17
|
+
@target = args[:target]
|
18
|
+
@overlay_type = args[:overlay_type] || 'overlayfs'
|
19
|
+
end
|
20
|
+
|
21
|
+
def mount
|
22
|
+
unless(mounted?)
|
23
|
+
case overlay_type
|
24
|
+
when 'aufs'
|
25
|
+
cmd = "mount -t aufs -o br=#{overlay}=rw:#{base}=ro,noplink none #{target}"
|
26
|
+
when 'overlayfs'
|
27
|
+
cmd = "mount -t overlayfs -oupperdir=#{overlay},lowerdir=#{base} none #{target}"
|
28
|
+
else
|
29
|
+
raise "Invalid overlay type provided: #{overlay_type}"
|
30
|
+
end
|
31
|
+
command(cmd, :sudo => true)
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def mounted?
|
37
|
+
command("mount").stdout.include?(target)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unmount
|
41
|
+
if(mounted?)
|
42
|
+
command("umount #{target}", :sudo => true, :allow_failure => true)
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def validate!(args)
|
50
|
+
[:base, :overlay, :target].each do |required|
|
51
|
+
unless(args[required])
|
52
|
+
raise ArgumentError.new "Missing required argument: #{required}"
|
53
|
+
end
|
54
|
+
unless(File.directory?(args[required]))
|
55
|
+
raise TypeError.new "Provided argument is not a valid directory for #{required}: #{args[required]}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'elecksee/helpers'
|
2
|
+
|
3
|
+
class Lxc
|
4
|
+
|
5
|
+
class VirtualDevice
|
6
|
+
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :tmp_dir
|
11
|
+
attr_reader :size
|
12
|
+
attr_reader :tmp_fs
|
13
|
+
attr_reader :fs_type
|
14
|
+
|
15
|
+
def initialize(name, args={})
|
16
|
+
@name = name
|
17
|
+
@tmp_dir = args[:tmp_dir] || '/tmp/lxc/ephemerals'
|
18
|
+
@size = args[:size] || 2000
|
19
|
+
@fs_type = args[:fs_type] || 'ext4'
|
20
|
+
@tmp_fs = !!args[:tmp_fs]
|
21
|
+
@fs_type = 'tmpfs' if @tmp_fs
|
22
|
+
create
|
23
|
+
end
|
24
|
+
|
25
|
+
def device_path
|
26
|
+
tmp_fs ? 'none' : File.join(tmp_dir, 'virt-imgs', name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def mount_path
|
30
|
+
File.join(tmp_dir, 'virt-mnts', name)
|
31
|
+
end
|
32
|
+
alias_method :target_path, :mount_path
|
33
|
+
|
34
|
+
def create
|
35
|
+
make_directories!
|
36
|
+
unless(tmp_fs)
|
37
|
+
command("dd if=/dev/zero of=#{@device_path} bs=1k seek=#{sive}k count=1 > /dev/null")
|
38
|
+
command("echo \"y\" | mkfs -t #{fs_type} #{size} > /dev/null")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def mounted?
|
43
|
+
command("mount").stdout.include?(mount_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def mount
|
47
|
+
unless(mounted?)
|
48
|
+
command("mount -t #{fs_type}#{mount_options} #{device_path} #{mount_path}", :sudo => true)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def unmount
|
54
|
+
if(mounted?)
|
55
|
+
command("umount #{mount_path}", :sudo => true)
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy
|
61
|
+
unmount
|
62
|
+
File.delete(device_path) if File.file?(device_path)
|
63
|
+
FileUtils.rm_rf(device_path) if File.directory?(device_path)
|
64
|
+
FileUtils.rmdir(mount_path) if File.directory?(mount_path)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def mount_options
|
70
|
+
' -o loop' unless tmp_fs
|
71
|
+
end
|
72
|
+
|
73
|
+
def make_directories!
|
74
|
+
[device_path, mount_path].each do |path|
|
75
|
+
unless(File.directory?(path))
|
76
|
+
FileUtils.mkdir_p(path)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/elecksee/version.rb
CHANGED