elecksee 1.0.8 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +18 -0
- data/README.md +9 -31
- data/lib/elecksee/clone.rb +115 -6
- data/lib/elecksee/ephemeral.rb +50 -178
- data/lib/elecksee/{helpers.rb → helpers/base.rb} +4 -4
- data/lib/elecksee/helpers/copies.rb +106 -0
- data/lib/elecksee/helpers/options.rb +58 -0
- data/lib/elecksee/lxc.rb +7 -2
- data/lib/elecksee/storage/overlay_directory.rb +16 -1
- data/lib/elecksee/storage/overlay_mount.rb +1 -1
- data/lib/elecksee/storage/virtual_device.rb +1 -1
- data/lib/elecksee/version.rb +1 -1
- metadata +6 -4
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
ADDED
data/README.md
CHANGED
@@ -1,47 +1,25 @@
|
|
1
1
|
# Elecksee
|
2
2
|
|
3
|
-
|
4
|
-
LXC tools being installed on the system to properly function.
|
5
|
-
It is extracted from the Chef LXC cookbook, so poke around there
|
3
|
+
An LXC library for Ruby
|
6
4
|
|
7
5
|
## Usage
|
8
6
|
|
9
7
|
```ruby
|
10
|
-
|
11
8
|
require 'elecksee/lxc'
|
12
9
|
|
13
|
-
lxc = Lxc.new('
|
14
|
-
|
15
|
-
```
|
16
|
-
|
17
|
-
## Awesome
|
18
|
-
|
19
|
-
Awesome ephemerals lets you create ephemeral nodes with different
|
20
|
-
overlays for the rootfs. Since tmpfs will place a size restriction
|
21
|
-
on ephemerals based on current memory available, this provides a
|
22
|
-
simple workaround. Currently supported is a raw temporary directory
|
23
|
-
on the host, or a VBD that defaults to 2GB.
|
24
|
-
|
25
|
-
### Using temporary directory
|
26
|
-
|
27
|
-
```
|
28
|
-
$ lxc-awesome-ephemeral -o ubuntu -d -z /tmp/lxc-roots
|
10
|
+
lxc = Lxc.new('container')
|
11
|
+
lxc.start unless lxc.running?
|
29
12
|
```
|
30
13
|
|
31
|
-
|
32
|
-
|
33
|
-
```
|
34
|
-
$ lxc-awesome-ephemeral -o ubuntu -d -D 2000
|
35
|
-
```
|
14
|
+
## Included
|
36
15
|
|
37
|
-
|
38
|
-
|
16
|
+
* Container inspect and interaction (`Lxc`)
|
17
|
+
* Container cloning (`Lxc::Clone`)
|
18
|
+
* Ephemeral containers (`Lxc::Ephemeral`)
|
39
19
|
|
40
|
-
|
41
|
-
----
|
20
|
+
### Notes
|
42
21
|
|
43
|
-
|
44
|
-
cleaned up when the container has reached a stop state.
|
22
|
+
This library is currently tested on ubuntu platforms >= 12.04
|
45
23
|
|
46
24
|
# Info
|
47
25
|
|
data/lib/elecksee/clone.rb
CHANGED
@@ -1,15 +1,124 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
%w(
|
2
|
+
helpers/base helpers/options helpers/copies lxc
|
3
|
+
storage/overlay_directory storage/overlay_mount
|
4
|
+
storage/virtual_device
|
5
|
+
).each do |path|
|
6
|
+
require "elecksee/#{path}"
|
7
|
+
end
|
3
8
|
|
4
9
|
class Lxc
|
5
10
|
class Clone
|
6
11
|
|
7
|
-
|
12
|
+
include Helpers
|
13
|
+
include Helpers::Options
|
14
|
+
include Helpers::Copies
|
15
|
+
|
16
|
+
option :original, '-o', :string, :required => true, :desc => 'Original container name', :aliases => 'orig'
|
17
|
+
option :new_name, '-n', :string, :required => true, :desc => 'New container name', :aliases => 'new'
|
18
|
+
option :snapshot, '-s', :boolean, :desc => 'Make new rootfs a snapshot of original'
|
19
|
+
option :fssize, '-L', :string, :desc => 'Size of new file system', :default => '2G'
|
20
|
+
option :vgname, '-v', :string, :desc => 'LVM volume group name', :default => 'lxc'
|
21
|
+
option :lvprefix, '-p', :string, :desc => 'LVM volume name prefix'
|
22
|
+
option :fstype, '-t', :string, :desc => 'New container file system', :default => 'ext4'
|
23
|
+
option :device, '-D', :integer, :desc => 'Make copy in VBD of {SIZE}M'
|
24
|
+
option :ipaddress, '-I', :string, :desc => 'Custom IP address'
|
25
|
+
option :gateway, '-G', :string, :desc => 'Custom gateway'
|
26
|
+
option :netmask, '-N', :string, :default => '255.255.255.0', :desc => 'Custom netmask'
|
27
|
+
|
28
|
+
# Hash containing original and new container instances
|
29
|
+
attr_reader :lxcs
|
30
|
+
|
8
31
|
def initialize(args={})
|
9
|
-
|
10
|
-
|
11
|
-
|
32
|
+
configure!(args)
|
33
|
+
@lxcs = {}
|
34
|
+
@lxcs[:original] = Lxc.new(original)
|
35
|
+
@lxcs[:new] = Lxc.new(new_name)
|
36
|
+
@created = []
|
37
|
+
validate!
|
38
|
+
end
|
39
|
+
|
40
|
+
def clone!
|
41
|
+
begin
|
42
|
+
copy_original
|
43
|
+
update_naming(:no_config)
|
44
|
+
apply_custom_addressing if ipaddress
|
45
|
+
lxcs[:new]
|
46
|
+
rescue Exception
|
47
|
+
@created.map(&:destroy)
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
alias_method :name, :new_name
|
55
|
+
|
56
|
+
# Returns new lxc instance
|
57
|
+
def lxc
|
58
|
+
lxcs[:new]
|
59
|
+
end
|
60
|
+
|
61
|
+
def created(thing)
|
62
|
+
@created << thing
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate!
|
66
|
+
unless(lxcs[:original].exists?)
|
67
|
+
raise "Requested `original` container does not exist (#{original})"
|
12
68
|
end
|
69
|
+
if(lxcs[:new].exists?)
|
70
|
+
raise "Requested `new` container already exists (#{new_name})"
|
71
|
+
end
|
72
|
+
if(lxcs[:original].running?)
|
73
|
+
raise "Requested `original` container is current running (#{original})"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def copy_original
|
78
|
+
copy_init
|
79
|
+
if(device)
|
80
|
+
rootfs_dir = copy_vbd
|
81
|
+
elsif(File.stat(lxcs[:original].path.to_s).blockdev?)
|
82
|
+
rootfs_dir = copy_lvm
|
83
|
+
elsif(command("btrfs subvolume list '#{lxcs[:original].path}'", :allow_failure => true))
|
84
|
+
rootfs_dir = copy_btrfs
|
85
|
+
else
|
86
|
+
rootfs_dir = copy_fs
|
87
|
+
end
|
88
|
+
update_rootfs(rootfs_dir)
|
89
|
+
end
|
90
|
+
|
91
|
+
def copy_init
|
92
|
+
directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
93
|
+
created(directory)
|
94
|
+
%w(config fstab).each do |file|
|
95
|
+
command("cp '#{lxcs[:original].path.join(file)}' '#{directory.target_path}'", :sudo => true)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def copy_fs
|
100
|
+
directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
101
|
+
created(directory)
|
102
|
+
command("rsync -ax '#{lxcs[:original].rootfs}/' '#{File.join(directory.target_path, 'rootfs')}/'", :sudo => true)
|
103
|
+
File.join(directory.target_path, 'rootfs')
|
104
|
+
end
|
105
|
+
|
106
|
+
def copy_vbd
|
107
|
+
storage = VirtualDevice.new(lxcs[:new].name, :tmp_dir => '/opt/lxc-vbd')
|
108
|
+
created(storage)
|
109
|
+
command("rsync -ax '#{lxcs[:original].rootfs}/' '#{storage.target_path}/'", :sudo => true)
|
110
|
+
storage.target_path
|
111
|
+
end
|
112
|
+
|
113
|
+
def copy_lvm
|
114
|
+
raise 'Not implemented'
|
115
|
+
end
|
116
|
+
|
117
|
+
def copy_btrfs
|
118
|
+
rootfs_path = lxcs[:new].path.join('rootfs')
|
119
|
+
command("btrfs subvolume snapshot '#{lxcs[:original].rootfs}' '#{rootfs_path}'", :sudo => true)
|
120
|
+
# TODO: Remove on failure
|
121
|
+
rootfs_path
|
13
122
|
end
|
14
123
|
end
|
15
124
|
end
|
data/lib/elecksee/ephemeral.rb
CHANGED
@@ -4,8 +4,9 @@ require 'tmpdir'
|
|
4
4
|
require 'etc'
|
5
5
|
|
6
6
|
%w(
|
7
|
-
helpers
|
8
|
-
storage/
|
7
|
+
helpers/base helpers/options helpers/copies lxc
|
8
|
+
storage/overlay_directory storage/overlay_mount
|
9
|
+
storage/virtual_device
|
9
10
|
).each do |path|
|
10
11
|
require "elecksee/#{path}"
|
11
12
|
end
|
@@ -15,26 +16,8 @@ class Lxc
|
|
15
16
|
class Ephemeral
|
16
17
|
|
17
18
|
include Helpers
|
18
|
-
|
19
|
-
|
20
|
-
HOSTNAME_FILES = %w(
|
21
|
-
rootfs/etc/hostname
|
22
|
-
rootfs/etc/hosts
|
23
|
-
rootfs/etc/sysconfig/network
|
24
|
-
rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
|
25
|
-
)
|
26
|
-
|
27
|
-
class << self
|
28
|
-
attr_reader :options
|
29
|
-
|
30
|
-
def option(name, short, type, args={})
|
31
|
-
@options ||= {}
|
32
|
-
@options[name] = args.merge(:short => short, :type => type)
|
33
|
-
instance_eval do
|
34
|
-
attr_accessor name.to_sym
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
19
|
+
include Helpers::Options
|
20
|
+
include Helpers::Copies
|
38
21
|
|
39
22
|
option :original, '-o', :string, :required => true, :desc => 'Original container name'
|
40
23
|
option :ipaddress, '-I', :string, :desc => 'Custom IP address'
|
@@ -49,6 +32,7 @@ class Lxc
|
|
49
32
|
option :ssh_key, '-S', :string, :default => '/opt/hw-lxc-config/id_rsa', :aliases => 'ssh-key', :desc => 'Deprecated: Provided for compatibility'
|
50
33
|
option :lxc_dir, '-L', :string, :default => '/var/lib/lxc', :aliases => 'lxc-dir', :desc => 'Directory of LXC store'
|
51
34
|
option :tmp_dir, '-T', :string, :default => '/tmp/lxc/ephemerals', :aliases => 'tmp-dir', :desc => 'Directory of ephemeral temp files'
|
35
|
+
option :ephemeral_command, '-C', :string, :aliases => 'command'
|
52
36
|
|
53
37
|
attr_reader :name
|
54
38
|
attr_reader :cli
|
@@ -63,6 +47,7 @@ class Lxc
|
|
63
47
|
configure!(args)
|
64
48
|
@cli = args[:cli]
|
65
49
|
@path = command("mktemp -d -p #{lxc_dir} #{original}-XXXXXXXXXXXX", :sudo => true).stdout.strip
|
50
|
+
command("chmod 0755 #{@path}", :sudo => true)
|
66
51
|
@name = File.basename(@path)
|
67
52
|
@hostname = @name.gsub(%r{[^A-Za-z0-9\-]}, '')
|
68
53
|
@ephemeral_binds = []
|
@@ -81,6 +66,21 @@ class Lxc
|
|
81
66
|
puts " - Connect using: sudo ssh -i #{ssh_key} root@#{lxc.container_ip(10)}"
|
82
67
|
end
|
83
68
|
end
|
69
|
+
|
70
|
+
def start_action
|
71
|
+
begin
|
72
|
+
lxc.start
|
73
|
+
if(ephemeral_command)
|
74
|
+
lxc.container_command(ephemeral_command)
|
75
|
+
else
|
76
|
+
cli_output
|
77
|
+
lxc.wait_for_state(:stopped)
|
78
|
+
end
|
79
|
+
ensure
|
80
|
+
cleanup
|
81
|
+
end
|
82
|
+
true
|
83
|
+
end
|
84
84
|
|
85
85
|
def start!(*args)
|
86
86
|
register_traps
|
@@ -88,23 +88,14 @@ class Lxc
|
|
88
88
|
if(daemon)
|
89
89
|
if(args.include?(:fork))
|
90
90
|
fork do
|
91
|
-
|
92
|
-
cli_output
|
93
|
-
lxc.wait_for_state(:stopped)
|
94
|
-
cleanup
|
91
|
+
start_action
|
95
92
|
end
|
96
93
|
else
|
97
94
|
Process.daemon
|
98
|
-
|
99
|
-
cli_output
|
100
|
-
lxc.wait_for_state(:stopped)
|
101
|
-
cleanup
|
95
|
+
start_action
|
102
96
|
end
|
103
97
|
else
|
104
|
-
|
105
|
-
cli_output
|
106
|
-
lxc.wait_for_state(:stopped)
|
107
|
-
cleanup
|
98
|
+
start_action
|
108
99
|
end
|
109
100
|
end
|
110
101
|
|
@@ -123,39 +114,7 @@ class Lxc
|
|
123
114
|
end
|
124
115
|
|
125
116
|
private
|
126
|
-
|
127
|
-
def configure!(args)
|
128
|
-
self.class.options.each do |name, opts|
|
129
|
-
argv = args.detect{|k,v| (Array(opts[:aliases]) + Array(opts[:short]) + [name]).include?(k.to_sym)}
|
130
|
-
argv = argv.last if argv
|
131
|
-
argv ||= opts[:default]
|
132
|
-
if(argv)
|
133
|
-
check_type!(name, argv, opts[:type])
|
134
|
-
self.send("#{name}=", argv)
|
135
|
-
else
|
136
|
-
if(opts[:required])
|
137
|
-
raise ArgumentError.new "Missing required argument: #{name}"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
if(ipaddress && gateway.nil?)
|
142
|
-
self.gateway = ipaddress.sub(%r{\d+$}, '1')
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def check_type!(arg_name, val, type)
|
147
|
-
valid = false
|
148
|
-
case type
|
149
|
-
when :string
|
150
|
-
valid = val.is_a?(String)
|
151
|
-
when :boolean
|
152
|
-
valid = val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
153
|
-
when :integer
|
154
|
-
valid = val.is_a?(Numeric)
|
155
|
-
end
|
156
|
-
raise ArgumentError.new "Invalid type provided for #{arg_name}. Expecting value type of: #{type.inspect} Got: #{val.class} - #{val}" unless valid
|
157
|
-
end
|
158
|
-
|
117
|
+
|
159
118
|
def setup
|
160
119
|
create
|
161
120
|
build_overlay
|
@@ -174,137 +133,50 @@ class Lxc
|
|
174
133
|
@ephemeral_overlay = OverlayMount.new(
|
175
134
|
:base => Lxc.new(original).rootfs.to_path,
|
176
135
|
:overlay => ephemeral_device.target_path,
|
177
|
-
:target => lxc.rootfs.to_path,
|
136
|
+
:target => lxc.path.join('rootfs').to_path,
|
178
137
|
:overlay_type => union
|
179
138
|
)
|
180
139
|
@ephemeral_overlay.mount
|
181
140
|
end
|
182
|
-
|
183
|
-
def writable_path!(path)
|
184
|
-
unless(File.directory?(File.dirname(path)))
|
185
|
-
command("mkdir -p #{File.dirname(path)}", :sudo => true)
|
186
|
-
end
|
187
|
-
unless(File.exists?(path))
|
188
|
-
command("touch #{path}", :sudo => true)
|
189
|
-
end
|
190
|
-
command("chown #{Etc.getlogin} #{path}", :sudo => true)
|
191
|
-
end
|
192
141
|
|
193
142
|
def create
|
194
143
|
Dir.glob(File.join(lxc_dir, original, '*')).each do |o_path|
|
195
144
|
next unless File.file?(o_path)
|
196
145
|
command("cp #{o_path} #{File.join(path, File.basename(o_path))}", :sudo => true)
|
197
146
|
end
|
198
|
-
command("chown -R #{Etc.getlogin} #{path}", :sudo => true)
|
199
147
|
@lxc = Lxc.new(name)
|
200
|
-
|
201
|
-
|
202
|
-
File.open(lxc.config, 'w') do |file|
|
203
|
-
contents.each do |line|
|
204
|
-
if(line.strip.start_with?('lxc.network.hwaddr'))
|
205
|
-
file.write "00:16:3e#{SecureRandom.hex(3).gsub(/(..)/, ':\1')}"
|
206
|
-
else
|
207
|
-
file.write line
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
148
|
+
command("mkdir -p '#{lxc.path.join('rootfs')}'", :sudo => true)
|
149
|
+
update_net_hwaddr
|
211
150
|
end
|
212
151
|
|
213
152
|
# TODO: Discovered binds for ephemeral are all tmpfs for now.
|
153
|
+
# TODO: We should default to overlay mount, make virt dev optional
|
214
154
|
def discover_binds
|
215
|
-
contents = File.readlines(lxc.path.join('fstab'))
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
if(union == 'overlayfs')
|
228
|
-
file.write "none #{container_target} overlayfs upperdir=#{device.mount_path},lowerdir=#{source} 0 0"
|
229
|
-
else
|
230
|
-
file.write "none #{container_target} aufs br=#{device.mount_path}=rw:#{source}=ro,noplink 0 0"
|
231
|
-
end
|
155
|
+
contents = File.readlines(lxc.path.join('fstab')).each do |line|
|
156
|
+
parts = line.split(' ')
|
157
|
+
if(parts[3] == 'bind')
|
158
|
+
source = parts.first
|
159
|
+
target = parts[1].sub(%r{^.+rootfs/}, '')
|
160
|
+
container_target = lxc.rootfs.join(target).to_path
|
161
|
+
device = VirtualDevice.new(target.gsub('/', '_'), :tmp_fs => true)
|
162
|
+
device.mount
|
163
|
+
FileUtils.mkdir_p(container_target)
|
164
|
+
ephemeral_binds << device
|
165
|
+
if(union == 'overlayfs')
|
166
|
+
"none #{container_target} overlayfs upperdir=#{device.mount_path},lowerdir=#{source} 0 0"
|
232
167
|
else
|
233
|
-
|
168
|
+
"none #{container_target} aufs br=#{device.mount_path}=rw:#{source}=ro,noplink 0 0"
|
234
169
|
end
|
235
|
-
|
236
|
-
|
237
|
-
if(bind)
|
238
|
-
command("mkdir -p #{lxc.rootfs.join(bind).to_path}", :sudo => true)
|
239
|
-
file.puts "#{bind} #{lxc.rootfs.join(bind)} none bind 0 0"
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def update_naming
|
245
|
-
NAME_FILES.each do |file|
|
246
|
-
next unless File.exists?(lxc.path.join(file))
|
247
|
-
writable_path!(lxc.path.join(file).to_path)
|
248
|
-
contents = File.read(lxc.path.join(file))
|
249
|
-
File.open(lxc.path.join(file), 'w') do |new_file|
|
250
|
-
new_file.write contents.gsub(original, name)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
HOSTNAME_FILES.each do |file|
|
254
|
-
next unless File.exists?(lxc.path.join(file))
|
255
|
-
writable_path!(lxc.path.join(file).to_path)
|
256
|
-
contents = File.read(lxc.path.join(file))
|
257
|
-
File.open(lxc.path.join(file), 'w') do |new_file|
|
258
|
-
new_file.write contents.gsub(original, hostname)
|
170
|
+
else
|
171
|
+
line
|
259
172
|
end
|
260
173
|
end
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
end
|
266
|
-
|
267
|
-
def apply_custom_networking
|
268
|
-
if(el_platform?)
|
269
|
-
writable_path!(path = lxc.rootfs.join('etc/sysconfig/network-scripts/ifcfg-eth0'))
|
270
|
-
File.open(path, 'w') do |file|
|
271
|
-
file.write <<-EOF
|
272
|
-
DEVICE=eth0
|
273
|
-
BOOTPROTO=static
|
274
|
-
NETMASK=#{netmask}
|
275
|
-
IPADDR=#{ipaddress}
|
276
|
-
ONBOOT=yes
|
277
|
-
TYPE=Ethernet
|
278
|
-
USERCTL=yes
|
279
|
-
PEERDNS=yes
|
280
|
-
IPV6INIT=no
|
281
|
-
GATEWAY=#{gateway}
|
282
|
-
EOF
|
283
|
-
end
|
284
|
-
writable_path!(path = lxc.rootfs.join('etc/sysconfig/network'))
|
285
|
-
File.open(path, 'w') do |file|
|
286
|
-
file.write <<-EOF
|
287
|
-
NETWORKING=yes
|
288
|
-
HOSTNAME=#{hostname}
|
289
|
-
EOF
|
290
|
-
end
|
291
|
-
File.open(@lxc.rootfs.join('etc/rc.local'), 'w') do |file|
|
292
|
-
file.puts "hostname #{hostname}"
|
293
|
-
end
|
294
|
-
else
|
295
|
-
writable_path!(path = lxc.rootfs.join('etc/network/interfaces'))
|
296
|
-
File.open(path, 'w') do |file|
|
297
|
-
file.write <<-EOF
|
298
|
-
auto lo
|
299
|
-
iface lo inet loopback
|
300
|
-
auto eth0
|
301
|
-
iface eth0 inet static
|
302
|
-
address #{ipaddress}
|
303
|
-
netmask #{netmask}
|
304
|
-
gateway #{gateway}
|
305
|
-
EOF
|
306
|
-
end
|
174
|
+
# If bind option used, bind in for rw
|
175
|
+
if(bind)
|
176
|
+
command("mkdir -p #{lxc.rootfs.join(bind).to_path}", :sudo => true)
|
177
|
+
contents << "#{bind} #{lxc.rootfs.join(bind)} none bind 0 0\n"
|
307
178
|
end
|
179
|
+
write_file(lxc.path.join('fstab'), contents.join)
|
308
180
|
end
|
309
181
|
end
|
310
182
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class Lxc
|
2
2
|
class CommandFailed < StandardError
|
3
3
|
end
|
4
|
-
|
5
|
-
module Helpers
|
6
4
|
|
5
|
+
module Helpers
|
6
|
+
|
7
7
|
def sudo
|
8
8
|
Lxc.sudo
|
9
9
|
end
|
@@ -14,7 +14,7 @@ class Lxc
|
|
14
14
|
cmd = [sudo, cmd].join(' ') if args[:sudo]
|
15
15
|
begin
|
16
16
|
shlout = Mixlib::ShellOut.new(cmd,
|
17
|
-
:logger => defined?(Chef) ? Chef::Log.logger : log,
|
17
|
+
:logger => defined?(Chef) && defined?(Chef::Log) ? Chef::Log.logger : log,
|
18
18
|
:live_stream => args[:livestream] ? STDOUT : nil,
|
19
19
|
:timeout => args[:timeout] || 1200,
|
20
20
|
:environment => {'HOME' => detect_home}
|
@@ -30,7 +30,7 @@ class Lxc
|
|
30
30
|
retries -= 1
|
31
31
|
retry
|
32
32
|
elsif(args[:allow_failure])
|
33
|
-
|
33
|
+
false
|
34
34
|
else
|
35
35
|
raise
|
36
36
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class Lxc
|
4
|
+
module Helpers
|
5
|
+
module Copies
|
6
|
+
|
7
|
+
NAME_FILES = %w(fstab config)
|
8
|
+
HOSTNAME_FILES = %w(
|
9
|
+
rootfs/etc/hostname
|
10
|
+
rootfs/etc/hosts
|
11
|
+
rootfs/etc/sysconfig/network
|
12
|
+
rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
|
13
|
+
)
|
14
|
+
|
15
|
+
def update_rootfs(rootfs_path)
|
16
|
+
contents = File.readlines(lxc.config.to_s).map do |line|
|
17
|
+
if(line.start_with?('lxc.rootfs'))
|
18
|
+
"lxc.rootfs = #{rootfs_path}\n"
|
19
|
+
else
|
20
|
+
line
|
21
|
+
end
|
22
|
+
end.join
|
23
|
+
write_file(lxc.config, contents)
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_net_hwaddr
|
27
|
+
contents = File.readlines(lxc.config).map do |line|
|
28
|
+
if(line.start_with?('lxc.network.hwaddr'))
|
29
|
+
parts = line.split('=')
|
30
|
+
"#{parts.first.strip} = 00:16:3e#{SecureRandom.hex(3).gsub(/(..)/, ':\1')}\n"
|
31
|
+
else
|
32
|
+
line
|
33
|
+
end
|
34
|
+
end.join
|
35
|
+
write_file(lxc.config, contents)
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_file(path, contents)
|
39
|
+
contents = contents.join if contents.is_a?(Array)
|
40
|
+
tmp = Tempfile.new('lxc-copy')
|
41
|
+
tmp.write(contents)
|
42
|
+
tmp.close
|
43
|
+
command("cp #{tmp.path} #{path}", :sudo => true)
|
44
|
+
tmp.unlink
|
45
|
+
command("chmod 0644 #{path}", :sudo => true)
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_naming(*args)
|
49
|
+
NAME_FILES.each do |file|
|
50
|
+
next unless File.exists?(lxc.path.join(file))
|
51
|
+
next if args.include?("no_#{file}".to_sym)
|
52
|
+
contents = File.read(lxc.path.join(file)).gsub(original, name)
|
53
|
+
write_file(lxc.path.join(file), contents)
|
54
|
+
end
|
55
|
+
HOSTNAME_FILES.each do |file|
|
56
|
+
next unless File.exists?(lxc.path.join(file))
|
57
|
+
next if args.include?("no_#{file}".to_sym)
|
58
|
+
contents = File.read(lxc.path.join(file)).gsub(original, name)
|
59
|
+
write_file(lxc.path.join(file), contents)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def el_platform?
|
64
|
+
lxc.rootfs.join('etc/redhat-release').exist?
|
65
|
+
end
|
66
|
+
|
67
|
+
def apply_custom_networking
|
68
|
+
if(el_platform?)
|
69
|
+
path = lxc.rootfs.join('etc/sysconfig/network-scripts/ifcfg-eth0')
|
70
|
+
content = <<-EOF
|
71
|
+
DEVICE=eth0
|
72
|
+
BOOTPROTO=static
|
73
|
+
NETMASK=#{netmask}
|
74
|
+
IPADDR=#{ipaddress}
|
75
|
+
ONBOOT=yes
|
76
|
+
TYPE=Ethernet
|
77
|
+
USERCTL=yes
|
78
|
+
PEERDNS=yes
|
79
|
+
IPV6INIT=no
|
80
|
+
GATEWAY=#{gateway}
|
81
|
+
EOF
|
82
|
+
write_file(path, content)
|
83
|
+
path = lxc.rootfs.join('etc/sysconfig/network')
|
84
|
+
content = <<-EOF
|
85
|
+
NETWORKING=yes
|
86
|
+
HOSTNAME=#{hostname}
|
87
|
+
EOF
|
88
|
+
write_file(path, content)
|
89
|
+
write_file(lxc.rootfs.join('etc/rc.local'), "hostname #{hostname}\n")
|
90
|
+
else
|
91
|
+
path = lxc.rootfs.join('etc/network/interfaces')
|
92
|
+
content = <<-EOF
|
93
|
+
auto lo
|
94
|
+
iface lo inet loopback
|
95
|
+
auto eth0
|
96
|
+
iface eth0 inet static
|
97
|
+
address #{ipaddress}
|
98
|
+
netmask #{netmask}
|
99
|
+
gateway #{gateway}
|
100
|
+
EOF
|
101
|
+
write_file(path, content)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Lxc
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
module Options
|
5
|
+
class << self
|
6
|
+
def included(klass)
|
7
|
+
klass.class_eval do
|
8
|
+
class << self
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
def option(name, short, type, args={})
|
12
|
+
@options ||= {}
|
13
|
+
@options[name] = args.merge(:short => short, :type => type)
|
14
|
+
instance_eval do
|
15
|
+
attr_accessor name.to_sym
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def configure!(args)
|
26
|
+
self.class.options.each do |name, opts|
|
27
|
+
argv = args.detect{|k,v| (Array(opts[:aliases]) + Array(opts[:short]) + [name]).include?(k.to_sym)}
|
28
|
+
argv = argv.last if argv
|
29
|
+
argv ||= opts[:default]
|
30
|
+
if(argv)
|
31
|
+
check_type!(name, argv, opts[:type])
|
32
|
+
self.send("#{name}=", argv)
|
33
|
+
else
|
34
|
+
if(opts[:required])
|
35
|
+
raise ArgumentError.new "Missing required argument: #{name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
if(ipaddress && gateway.nil?)
|
40
|
+
self.gateway = ipaddress.sub(%r{\d+$}, '1')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_type!(arg_name, val, type)
|
45
|
+
valid = false
|
46
|
+
case type
|
47
|
+
when :string
|
48
|
+
valid = val.is_a?(String)
|
49
|
+
when :boolean
|
50
|
+
valid = val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
51
|
+
when :integer
|
52
|
+
valid = val.is_a?(Numeric)
|
53
|
+
end
|
54
|
+
raise ArgumentError.new "Invalid type provided for #{arg_name}. Expecting value type of: #{type.inspect} Got: #{val.class} - #{val}" unless valid
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/elecksee/lxc.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'elecksee/helpers'
|
1
|
+
require 'elecksee/helpers/base'
|
2
2
|
require 'mixlib/shellout'
|
3
3
|
require 'pathname'
|
4
4
|
require 'tmpdir'
|
@@ -245,7 +245,12 @@ class Lxc
|
|
245
245
|
alias_method :config, :container_config
|
246
246
|
|
247
247
|
def container_rootfs
|
248
|
-
|
248
|
+
if(File.exists?(config))
|
249
|
+
r_path = File.readlines(config).detect do |line|
|
250
|
+
line.start_with?('lxc.rootfs')
|
251
|
+
end.to_s.split('=').last.to_s.strip
|
252
|
+
end
|
253
|
+
r_path.to_s.empty? ? container_path.join('rootfs') : Pathname.new(r_path)
|
249
254
|
end
|
250
255
|
alias_method :rootfs, :container_rootfs
|
251
256
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'elecksee/helpers'
|
1
|
+
require 'elecksee/helpers/base'
|
2
2
|
|
3
3
|
class Lxc
|
4
4
|
class OverlayDirectory
|
@@ -32,4 +32,19 @@ class Lxc
|
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
35
|
+
|
36
|
+
# Clone directory does the same as the overlay, just in
|
37
|
+
# a persistent place
|
38
|
+
class CloneDirectory < OverlayDirectory
|
39
|
+
def initialize(name, args={})
|
40
|
+
args[:tmp_dir] = args[:dir] if args[:dir]
|
41
|
+
args[:tmp_dir] || '/var/lib/lxc'
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def overlay_path
|
46
|
+
File.join(tmp_dir, name)
|
47
|
+
end
|
48
|
+
alias_method :target_path, :overlay_path
|
49
|
+
end
|
35
50
|
end
|
data/lib/elecksee/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elecksee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mixlib-shellout
|
@@ -54,7 +54,9 @@ files:
|
|
54
54
|
- lib/elecksee/clone.rb
|
55
55
|
- lib/elecksee/version.rb
|
56
56
|
- lib/elecksee/lxc_file_config.rb
|
57
|
-
- lib/elecksee/helpers.rb
|
57
|
+
- lib/elecksee/helpers/copies.rb
|
58
|
+
- lib/elecksee/helpers/options.rb
|
59
|
+
- lib/elecksee/helpers/base.rb
|
58
60
|
- lib/elecksee/storage/overlay_mount.rb
|
59
61
|
- lib/elecksee/storage/virtual_device.rb
|
60
62
|
- lib/elecksee/storage/overlay_directory.rb
|
@@ -65,8 +67,8 @@ files:
|
|
65
67
|
- README.md
|
66
68
|
- LICENSE
|
67
69
|
- bin/lxc-awesome-ephemeral
|
68
|
-
- elecksee-1.0.8.gem
|
69
70
|
- CHANGELOG.md
|
71
|
+
- Gemfile.lock
|
70
72
|
homepage: http://github.com/chrisroberts/elecksee
|
71
73
|
licenses: []
|
72
74
|
post_install_message:
|