dev-lxc 2.6.1 → 2.6.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.
- checksums.yaml +4 -4
- data/.gitignore +17 -17
- data/CHANGELOG.md +317 -313
- data/Gemfile +4 -4
- data/LICENSE +201 -201
- data/README.md +287 -279
- data/Rakefile +1 -1
- data/bin/dev-lxc +9 -9
- data/dev-lxc.gemspec +26 -26
- data/docs/adhoc_clusters.md +22 -22
- data/docs/base_containers.md +44 -44
- data/docs/configuration.md +203 -203
- data/docs/dev-lxc_version_2.md +10 -10
- data/docs/manage_multiple_clusters.md +30 -30
- data/docs/usage.md +187 -187
- data/lib/dev-lxc/cli.rb +434 -434
- data/lib/dev-lxc/cluster.rb +1211 -1211
- data/lib/dev-lxc/container.rb +123 -123
- data/lib/dev-lxc/server.rb +197 -197
- data/lib/dev-lxc/version.rb +3 -3
- data/lib/dev-lxc.rb +118 -118
- metadata +2 -2
data/lib/dev-lxc/container.rb
CHANGED
@@ -1,123 +1,123 @@
|
|
1
|
-
module DevLXC
|
2
|
-
class Container < LXC::Container
|
3
|
-
def status
|
4
|
-
if self.defined?
|
5
|
-
state = self.state
|
6
|
-
ip_addresses = self.ip_addresses.join(" ") if self.state == :running
|
7
|
-
else
|
8
|
-
state = "not_created"
|
9
|
-
end
|
10
|
-
{ 'name' => self.name, 'state' => state, 'ip_addresses' => ip_addresses }
|
11
|
-
end
|
12
|
-
|
13
|
-
def start
|
14
|
-
unless self.defined?
|
15
|
-
puts "ERROR: Container '#{self.name}' does not exist."
|
16
|
-
exit 1
|
17
|
-
end
|
18
|
-
puts "Starting container '#{self.name}'"
|
19
|
-
super
|
20
|
-
wait(:running, 3)
|
21
|
-
puts "Waiting for '#{self.name}' container's network"
|
22
|
-
ips = nil
|
23
|
-
30.times do
|
24
|
-
ips = self.ip_addresses
|
25
|
-
break unless ips.empty?
|
26
|
-
sleep 1
|
27
|
-
end
|
28
|
-
if ips.empty?
|
29
|
-
puts "ERROR: Container '#{self.name}' network is not available."
|
30
|
-
exit 1
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def shutdown
|
35
|
-
puts "Shutting down container '#{self.name}'"
|
36
|
-
super
|
37
|
-
wait(:stopped, 3)
|
38
|
-
end
|
39
|
-
|
40
|
-
def destroy
|
41
|
-
stop if running?
|
42
|
-
puts "Destroying container '#{self.name}'"
|
43
|
-
super if self.defined?
|
44
|
-
end
|
45
|
-
|
46
|
-
def sync_mounts(mounts)
|
47
|
-
existing_mounts = self.config_item("lxc.mount.entry")
|
48
|
-
unless existing_mounts.nil?
|
49
|
-
preserved_mounts = existing_mounts.delete_if { |m| m.end_with?("## dev-lxc ##") }
|
50
|
-
self.clear_config_item('lxc.mount.entry')
|
51
|
-
self.set_config_item("lxc.mount.entry", preserved_mounts)
|
52
|
-
end
|
53
|
-
unless mounts.nil?
|
54
|
-
mounts.each do |mount|
|
55
|
-
if ! preserved_mounts.nil? && preserved_mounts.any? { |m| m.start_with?("#{mount} ") }
|
56
|
-
puts "Skipping mount entry #{mount}, it already exists"
|
57
|
-
next
|
58
|
-
else
|
59
|
-
puts "Adding mount entry #{mount}"
|
60
|
-
self.set_config_item("lxc.mount.entry", "#{mount} none bind,optional,create=dir 0 0 ## dev-lxc ##")
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
self.save_config
|
65
|
-
end
|
66
|
-
|
67
|
-
def sync_ssh_keys(ssh_keys)
|
68
|
-
dot_ssh_path = "/home/dev-lxc/.ssh"
|
69
|
-
unless File.exist?("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys")
|
70
|
-
run_command("sudo -u dev-lxc mkdir -p #{dot_ssh_path}")
|
71
|
-
run_command("sudo -u dev-lxc chmod 700 #{dot_ssh_path}")
|
72
|
-
run_command("sudo -u dev-lxc touch #{dot_ssh_path}/authorized_keys")
|
73
|
-
run_command("sudo -u dev-lxc chmod 600 #{dot_ssh_path}/authorized_keys")
|
74
|
-
end
|
75
|
-
authorized_keys = IO.read("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys").split("\n")
|
76
|
-
authorized_keys.delete_if { |m| m.end_with?("## dev-lxc ##") }
|
77
|
-
unless ssh_keys.nil?
|
78
|
-
ssh_keys.each do |ssh_key|
|
79
|
-
puts "Adding SSH key #{ssh_key} to #{dot_ssh_path}/authorized_keys"
|
80
|
-
authorized_keys << IO.read(ssh_key).chomp + " ## dev-lxc ##"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
authorized_keys_content = String.new
|
84
|
-
authorized_keys_content = authorized_keys.join("\n") + "\n" unless authorized_keys.empty?
|
85
|
-
IO.write("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys", authorized_keys_content)
|
86
|
-
end
|
87
|
-
|
88
|
-
def run_command(command, output_file=nil)
|
89
|
-
unless running?
|
90
|
-
puts "ERROR: Container '#{self.name}' must be running first"
|
91
|
-
exit 1
|
92
|
-
end
|
93
|
-
attach_opts = { wait: true, env_policy: LXC::LXC_ATTACH_CLEAR_ENV, extra_env_vars: ['HOME=/root'] }
|
94
|
-
if output_file
|
95
|
-
file = File.open(output_file, 'w+')
|
96
|
-
attach_opts[:stdout] = file
|
97
|
-
end
|
98
|
-
begin
|
99
|
-
attach(attach_opts) do
|
100
|
-
LXC.run_command(command)
|
101
|
-
end
|
102
|
-
ensure
|
103
|
-
file.close if file
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def install_package(package_path)
|
108
|
-
unless run_command("test -e #{package_path}") == 0
|
109
|
-
puts "ERROR: File #{package_path} does not exist in container '#{self.name}'"
|
110
|
-
exit 1
|
111
|
-
end
|
112
|
-
puts "Installing #{package_path} in container '#{self.name}'"
|
113
|
-
case File.extname(package_path)
|
114
|
-
when ".deb"
|
115
|
-
install_command = "dpkg -i --skip-same-version #{package_path}"
|
116
|
-
when ".rpm"
|
117
|
-
install_command = "rpm -Uvh #{package_path}"
|
118
|
-
end
|
119
|
-
run_command(install_command)
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
1
|
+
module DevLXC
|
2
|
+
class Container < LXC::Container
|
3
|
+
def status
|
4
|
+
if self.defined?
|
5
|
+
state = self.state
|
6
|
+
ip_addresses = self.ip_addresses.join(" ") if self.state == :running
|
7
|
+
else
|
8
|
+
state = "not_created"
|
9
|
+
end
|
10
|
+
{ 'name' => self.name, 'state' => state, 'ip_addresses' => ip_addresses }
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
unless self.defined?
|
15
|
+
puts "ERROR: Container '#{self.name}' does not exist."
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
puts "Starting container '#{self.name}'"
|
19
|
+
super
|
20
|
+
wait(:running, 3)
|
21
|
+
puts "Waiting for '#{self.name}' container's network"
|
22
|
+
ips = nil
|
23
|
+
30.times do
|
24
|
+
ips = self.ip_addresses
|
25
|
+
break unless ips.empty?
|
26
|
+
sleep 1
|
27
|
+
end
|
28
|
+
if ips.empty?
|
29
|
+
puts "ERROR: Container '#{self.name}' network is not available."
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def shutdown
|
35
|
+
puts "Shutting down container '#{self.name}'"
|
36
|
+
super
|
37
|
+
wait(:stopped, 3)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
stop if running?
|
42
|
+
puts "Destroying container '#{self.name}'"
|
43
|
+
super if self.defined?
|
44
|
+
end
|
45
|
+
|
46
|
+
def sync_mounts(mounts)
|
47
|
+
existing_mounts = self.config_item("lxc.mount.entry")
|
48
|
+
unless existing_mounts.nil?
|
49
|
+
preserved_mounts = existing_mounts.delete_if { |m| m.end_with?("## dev-lxc ##") }
|
50
|
+
self.clear_config_item('lxc.mount.entry')
|
51
|
+
self.set_config_item("lxc.mount.entry", preserved_mounts)
|
52
|
+
end
|
53
|
+
unless mounts.nil?
|
54
|
+
mounts.each do |mount|
|
55
|
+
if ! preserved_mounts.nil? && preserved_mounts.any? { |m| m.start_with?("#{mount} ") }
|
56
|
+
puts "Skipping mount entry #{mount}, it already exists"
|
57
|
+
next
|
58
|
+
else
|
59
|
+
puts "Adding mount entry #{mount}"
|
60
|
+
self.set_config_item("lxc.mount.entry", "#{mount} none bind,optional,create=dir 0 0 ## dev-lxc ##")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
self.save_config
|
65
|
+
end
|
66
|
+
|
67
|
+
def sync_ssh_keys(ssh_keys)
|
68
|
+
dot_ssh_path = "/home/dev-lxc/.ssh"
|
69
|
+
unless File.exist?("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys")
|
70
|
+
run_command("sudo -u dev-lxc mkdir -p #{dot_ssh_path}")
|
71
|
+
run_command("sudo -u dev-lxc chmod 700 #{dot_ssh_path}")
|
72
|
+
run_command("sudo -u dev-lxc touch #{dot_ssh_path}/authorized_keys")
|
73
|
+
run_command("sudo -u dev-lxc chmod 600 #{dot_ssh_path}/authorized_keys")
|
74
|
+
end
|
75
|
+
authorized_keys = IO.read("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys").split("\n")
|
76
|
+
authorized_keys.delete_if { |m| m.end_with?("## dev-lxc ##") }
|
77
|
+
unless ssh_keys.nil?
|
78
|
+
ssh_keys.each do |ssh_key|
|
79
|
+
puts "Adding SSH key #{ssh_key} to #{dot_ssh_path}/authorized_keys"
|
80
|
+
authorized_keys << IO.read(ssh_key).chomp + " ## dev-lxc ##"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
authorized_keys_content = String.new
|
84
|
+
authorized_keys_content = authorized_keys.join("\n") + "\n" unless authorized_keys.empty?
|
85
|
+
IO.write("#{config_item('lxc.rootfs')}#{dot_ssh_path}/authorized_keys", authorized_keys_content)
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_command(command, output_file=nil)
|
89
|
+
unless running?
|
90
|
+
puts "ERROR: Container '#{self.name}' must be running first"
|
91
|
+
exit 1
|
92
|
+
end
|
93
|
+
attach_opts = { wait: true, env_policy: LXC::LXC_ATTACH_CLEAR_ENV, extra_env_vars: ['HOME=/root'] }
|
94
|
+
if output_file
|
95
|
+
file = File.open(output_file, 'w+')
|
96
|
+
attach_opts[:stdout] = file
|
97
|
+
end
|
98
|
+
begin
|
99
|
+
attach(attach_opts) do
|
100
|
+
LXC.run_command(command)
|
101
|
+
end
|
102
|
+
ensure
|
103
|
+
file.close if file
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def install_package(package_path)
|
108
|
+
unless run_command("test -e #{package_path}") == 0
|
109
|
+
puts "ERROR: File #{package_path} does not exist in container '#{self.name}'"
|
110
|
+
exit 1
|
111
|
+
end
|
112
|
+
puts "Installing #{package_path} in container '#{self.name}'"
|
113
|
+
case File.extname(package_path)
|
114
|
+
when ".deb"
|
115
|
+
install_command = "dpkg -i --skip-same-version #{package_path}"
|
116
|
+
when ".rpm"
|
117
|
+
install_command = "rpm -Uvh #{package_path}"
|
118
|
+
end
|
119
|
+
run_command(install_command)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
data/lib/dev-lxc/server.rb
CHANGED
@@ -1,197 +1,197 @@
|
|
1
|
-
require "json"
|
2
|
-
require "dev-lxc/container"
|
3
|
-
|
4
|
-
module DevLXC
|
5
|
-
class Server
|
6
|
-
attr_reader :container
|
7
|
-
|
8
|
-
def initialize(name, ipaddress, additional_fqdn, memory_per_server, mounts, ssh_keys)
|
9
|
-
@container = DevLXC::Container.new(name)
|
10
|
-
@ipaddress = ipaddress
|
11
|
-
@additional_fqdn = additional_fqdn
|
12
|
-
@memory_per_server = memory_per_server
|
13
|
-
@mounts = mounts
|
14
|
-
@ssh_keys = ssh_keys
|
15
|
-
end
|
16
|
-
|
17
|
-
def name
|
18
|
-
@container.name
|
19
|
-
end
|
20
|
-
|
21
|
-
def status
|
22
|
-
@container.status
|
23
|
-
end
|
24
|
-
|
25
|
-
def run_command(command, output_file=nil)
|
26
|
-
if @container.running?
|
27
|
-
puts "Running '#{command}' in '#{@container.name}'"
|
28
|
-
puts "Saving output to #{output_file}" if output_file
|
29
|
-
@container.run_command(command, output_file)
|
30
|
-
else
|
31
|
-
puts "'#{@container.name}' is not running"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def install_package(package_path)
|
36
|
-
@container.install_package(package_path)
|
37
|
-
end
|
38
|
-
|
39
|
-
def start
|
40
|
-
return if @container.running?
|
41
|
-
hwaddr = @container.config_item("lxc.network.0.hwaddr")
|
42
|
-
release_lingering_dhcp_ip_addresses(hwaddr)
|
43
|
-
assign_static_ip_address(hwaddr) if @ipaddress
|
44
|
-
@container.sync_mounts(@mounts)
|
45
|
-
@container.start
|
46
|
-
@container.sync_ssh_keys(@ssh_keys)
|
47
|
-
@container.set_cgroup_item('memory.limit_in_bytes', @memory_per_server) if @memory_per_server
|
48
|
-
puts
|
49
|
-
end
|
50
|
-
|
51
|
-
def shutdown
|
52
|
-
@container.shutdown if @container.running?
|
53
|
-
remove_static_ip_address(@container.config_item("lxc.network.0.hwaddr")) if @container.defined?
|
54
|
-
end
|
55
|
-
|
56
|
-
def snapshot(comment=nil)
|
57
|
-
unless @container.defined?
|
58
|
-
puts "WARNING: Skipping snapshot of '#{@container.name}' because it does not exist"
|
59
|
-
return
|
60
|
-
end
|
61
|
-
if @container.running?
|
62
|
-
puts "WARNING: Skipping snapshot of '#{@container.name}' because it is running"
|
63
|
-
return
|
64
|
-
end
|
65
|
-
puts "Creating snapshot of container '#{@container.name}'"
|
66
|
-
snapname = @container.snapshot
|
67
|
-
unless comment.nil?
|
68
|
-
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
69
|
-
snapshot_comment_file = snapshot.flatten[1]
|
70
|
-
IO.write(snapshot_comment_file, comment) unless snapshot_comment_file.nil?
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def snapshot_destroy(snapname=nil)
|
75
|
-
unless @container.defined?
|
76
|
-
puts "Skipping container '#{@container.name}' because it does not exist"
|
77
|
-
return
|
78
|
-
end
|
79
|
-
if snapname == "ALL"
|
80
|
-
if @container.snapshot_list.empty?
|
81
|
-
puts "Container '#{@container.name}' does not have any snapshots"
|
82
|
-
else
|
83
|
-
@container.snapshot_list.each do |snapshot|
|
84
|
-
puts "Destroying snapshot '#{snapshot.first}' of container '#{@container.name}'"
|
85
|
-
@container.snapshot_destroy(snapshot.first)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
elsif snapname == "LAST"
|
89
|
-
if @container.snapshot_list.empty?
|
90
|
-
puts "Container '#{@container.name}' does not have any snapshots"
|
91
|
-
else
|
92
|
-
snapname = @container.snapshot_list.last.first
|
93
|
-
puts "Destroying snapshot '#{snapname}' of container '#{@container.name}'"
|
94
|
-
@container.snapshot_destroy(snapname)
|
95
|
-
end
|
96
|
-
else
|
97
|
-
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
98
|
-
if snapshot.flatten.empty?
|
99
|
-
puts "Container '#{@container.name}' does not have a '#{snapname}' snapshot"
|
100
|
-
else
|
101
|
-
puts "Destroying snapshot '#{snapname}' of container '#{@container.name}'"
|
102
|
-
@container.snapshot_destroy(snapname)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def snapshot_list
|
108
|
-
snapshots = Array.new
|
109
|
-
return snapshots unless @container.defined?
|
110
|
-
@container.snapshot_list.each do |snapshot|
|
111
|
-
(snapname, snap_comment_file, snaptime) = snapshot
|
112
|
-
snap_comment = IO.read(snap_comment_file).chomp if File.exist?(snap_comment_file)
|
113
|
-
snapshots << [snapname, snaptime, snap_comment]
|
114
|
-
end
|
115
|
-
snapshots
|
116
|
-
end
|
117
|
-
|
118
|
-
def snapshot_restore(snapname=nil)
|
119
|
-
unless @container.defined?
|
120
|
-
puts "WARNING: Skipping container '#{@container.name}' because it does not exist"
|
121
|
-
return
|
122
|
-
end
|
123
|
-
if @container.running?
|
124
|
-
puts "WARNING: Skipping container '#{@container.name}' because it is running"
|
125
|
-
return
|
126
|
-
end
|
127
|
-
if snapname == "LAST"
|
128
|
-
if @container.snapshot_list.empty?
|
129
|
-
puts "WARNING: Skipping container '#{@container.name}' because it does not have any snapshots"
|
130
|
-
else
|
131
|
-
snapname = @container.snapshot_list.last.first
|
132
|
-
puts "Restoring snapshot '#{snapname}' of container '#{@container.name}'"
|
133
|
-
@container.snapshot_restore(snapname)
|
134
|
-
end
|
135
|
-
else
|
136
|
-
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
137
|
-
if snapshot.flatten.empty?
|
138
|
-
puts "WARNING: Skipping container '#{@container.name}' because it does not have a '#{snapname}' snapshot"
|
139
|
-
else
|
140
|
-
puts "Restoring snapshot '#{snapname}' of container '#{@container.name}'"
|
141
|
-
@container.snapshot_restore(snapname)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def destroy
|
147
|
-
return unless @container.defined?
|
148
|
-
@container.snapshot_list.each { |snapshot| @container.snapshot_destroy(snapshot.first) }
|
149
|
-
hwaddr = @container.config_item("lxc.network.0.hwaddr")
|
150
|
-
@container.destroy
|
151
|
-
remove_static_ip_address(hwaddr)
|
152
|
-
end
|
153
|
-
|
154
|
-
def release_lingering_dhcp_ip_addresses(hwaddr)
|
155
|
-
dhcp_leases = IO.readlines('/var/lib/misc/dnsmasq.lxcbr0.leases')
|
156
|
-
leases_to_release = dhcp_leases.map do |dhcp_lease|
|
157
|
-
if m = dhcp_lease.match(/ #{hwaddr} (\d+\.\d+\.\d+\.\d+) /)
|
158
|
-
mac_addr = hwaddr
|
159
|
-
ip_addr = m[1]
|
160
|
-
elsif m = dhcp_lease.match(/ (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) #{@ipaddress} /)
|
161
|
-
mac_addr = m[1]
|
162
|
-
ip_addr = @ipaddress
|
163
|
-
elsif m = dhcp_lease.match(/ (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) (\d+\.\d+\.\d+\.\d+) #{@container.name.sub(/\.lxc$/, '')} /)
|
164
|
-
mac_addr = m[1]
|
165
|
-
ip_addr = m[2]
|
166
|
-
end
|
167
|
-
if mac_addr && ip_addr
|
168
|
-
{ dhcp_lease: dhcp_lease, mac_addr: mac_addr, ip_addr: ip_addr }
|
169
|
-
end
|
170
|
-
end
|
171
|
-
leases_to_release.compact!
|
172
|
-
unless leases_to_release.empty?
|
173
|
-
system("systemctl stop lxc-net.service")
|
174
|
-
leases_to_release.each do |l|
|
175
|
-
puts "Releasing lingering DHCP lease: #{l[:dhcp_lease]}"
|
176
|
-
DevLXC.search_file_delete_line("/var/lib/misc/dnsmasq.lxcbr0.leases", /( #{l[:mac_addr]} #{l[:ip_addr]} )/)
|
177
|
-
end
|
178
|
-
system("systemctl start lxc-net.service")
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def assign_static_ip_address(hwaddr)
|
183
|
-
puts "Assigning IP address #{@ipaddress} to '#{@container.name}' container's lxc.network.hwaddr #{hwaddr}"
|
184
|
-
DevLXC.search_file_delete_line("/etc/lxc/dhcp-hosts.conf", /(^#{hwaddr}|,#{@ipaddress}$)/)
|
185
|
-
DevLXC.append_line_to_file("/etc/lxc/dhcp-hosts.conf", "#{hwaddr},#{@ipaddress}\n")
|
186
|
-
DevLXC.reload_dnsmasq
|
187
|
-
end
|
188
|
-
|
189
|
-
def remove_static_ip_address(hwaddr=nil)
|
190
|
-
if hwaddr
|
191
|
-
DevLXC.search_file_delete_line("/etc/lxc/dhcp-hosts.conf", /^#{hwaddr}/)
|
192
|
-
DevLXC.reload_dnsmasq
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
end
|
1
|
+
require "json"
|
2
|
+
require "dev-lxc/container"
|
3
|
+
|
4
|
+
module DevLXC
|
5
|
+
class Server
|
6
|
+
attr_reader :container
|
7
|
+
|
8
|
+
def initialize(name, ipaddress, additional_fqdn, memory_per_server, mounts, ssh_keys)
|
9
|
+
@container = DevLXC::Container.new(name)
|
10
|
+
@ipaddress = ipaddress
|
11
|
+
@additional_fqdn = additional_fqdn
|
12
|
+
@memory_per_server = memory_per_server
|
13
|
+
@mounts = mounts
|
14
|
+
@ssh_keys = ssh_keys
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@container.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def status
|
22
|
+
@container.status
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_command(command, output_file=nil)
|
26
|
+
if @container.running?
|
27
|
+
puts "Running '#{command}' in '#{@container.name}'"
|
28
|
+
puts "Saving output to #{output_file}" if output_file
|
29
|
+
@container.run_command(command, output_file)
|
30
|
+
else
|
31
|
+
puts "'#{@container.name}' is not running"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def install_package(package_path)
|
36
|
+
@container.install_package(package_path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
return if @container.running?
|
41
|
+
hwaddr = @container.config_item("lxc.network.0.hwaddr")
|
42
|
+
release_lingering_dhcp_ip_addresses(hwaddr)
|
43
|
+
assign_static_ip_address(hwaddr) if @ipaddress
|
44
|
+
@container.sync_mounts(@mounts)
|
45
|
+
@container.start
|
46
|
+
@container.sync_ssh_keys(@ssh_keys)
|
47
|
+
@container.set_cgroup_item('memory.limit_in_bytes', @memory_per_server) if @memory_per_server
|
48
|
+
puts
|
49
|
+
end
|
50
|
+
|
51
|
+
def shutdown
|
52
|
+
@container.shutdown if @container.running?
|
53
|
+
remove_static_ip_address(@container.config_item("lxc.network.0.hwaddr")) if @container.defined?
|
54
|
+
end
|
55
|
+
|
56
|
+
def snapshot(comment=nil)
|
57
|
+
unless @container.defined?
|
58
|
+
puts "WARNING: Skipping snapshot of '#{@container.name}' because it does not exist"
|
59
|
+
return
|
60
|
+
end
|
61
|
+
if @container.running?
|
62
|
+
puts "WARNING: Skipping snapshot of '#{@container.name}' because it is running"
|
63
|
+
return
|
64
|
+
end
|
65
|
+
puts "Creating snapshot of container '#{@container.name}'"
|
66
|
+
snapname = @container.snapshot
|
67
|
+
unless comment.nil?
|
68
|
+
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
69
|
+
snapshot_comment_file = snapshot.flatten[1]
|
70
|
+
IO.write(snapshot_comment_file, comment) unless snapshot_comment_file.nil?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def snapshot_destroy(snapname=nil)
|
75
|
+
unless @container.defined?
|
76
|
+
puts "Skipping container '#{@container.name}' because it does not exist"
|
77
|
+
return
|
78
|
+
end
|
79
|
+
if snapname == "ALL"
|
80
|
+
if @container.snapshot_list.empty?
|
81
|
+
puts "Container '#{@container.name}' does not have any snapshots"
|
82
|
+
else
|
83
|
+
@container.snapshot_list.each do |snapshot|
|
84
|
+
puts "Destroying snapshot '#{snapshot.first}' of container '#{@container.name}'"
|
85
|
+
@container.snapshot_destroy(snapshot.first)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
elsif snapname == "LAST"
|
89
|
+
if @container.snapshot_list.empty?
|
90
|
+
puts "Container '#{@container.name}' does not have any snapshots"
|
91
|
+
else
|
92
|
+
snapname = @container.snapshot_list.last.first
|
93
|
+
puts "Destroying snapshot '#{snapname}' of container '#{@container.name}'"
|
94
|
+
@container.snapshot_destroy(snapname)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
98
|
+
if snapshot.flatten.empty?
|
99
|
+
puts "Container '#{@container.name}' does not have a '#{snapname}' snapshot"
|
100
|
+
else
|
101
|
+
puts "Destroying snapshot '#{snapname}' of container '#{@container.name}'"
|
102
|
+
@container.snapshot_destroy(snapname)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def snapshot_list
|
108
|
+
snapshots = Array.new
|
109
|
+
return snapshots unless @container.defined?
|
110
|
+
@container.snapshot_list.each do |snapshot|
|
111
|
+
(snapname, snap_comment_file, snaptime) = snapshot
|
112
|
+
snap_comment = IO.read(snap_comment_file).chomp if File.exist?(snap_comment_file)
|
113
|
+
snapshots << [snapname, snaptime, snap_comment]
|
114
|
+
end
|
115
|
+
snapshots
|
116
|
+
end
|
117
|
+
|
118
|
+
def snapshot_restore(snapname=nil)
|
119
|
+
unless @container.defined?
|
120
|
+
puts "WARNING: Skipping container '#{@container.name}' because it does not exist"
|
121
|
+
return
|
122
|
+
end
|
123
|
+
if @container.running?
|
124
|
+
puts "WARNING: Skipping container '#{@container.name}' because it is running"
|
125
|
+
return
|
126
|
+
end
|
127
|
+
if snapname == "LAST"
|
128
|
+
if @container.snapshot_list.empty?
|
129
|
+
puts "WARNING: Skipping container '#{@container.name}' because it does not have any snapshots"
|
130
|
+
else
|
131
|
+
snapname = @container.snapshot_list.last.first
|
132
|
+
puts "Restoring snapshot '#{snapname}' of container '#{@container.name}'"
|
133
|
+
@container.snapshot_restore(snapname)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
snapshot = @container.snapshot_list.select { |sn| sn.first == snapname }
|
137
|
+
if snapshot.flatten.empty?
|
138
|
+
puts "WARNING: Skipping container '#{@container.name}' because it does not have a '#{snapname}' snapshot"
|
139
|
+
else
|
140
|
+
puts "Restoring snapshot '#{snapname}' of container '#{@container.name}'"
|
141
|
+
@container.snapshot_restore(snapname)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def destroy
|
147
|
+
return unless @container.defined?
|
148
|
+
@container.snapshot_list.each { |snapshot| @container.snapshot_destroy(snapshot.first) }
|
149
|
+
hwaddr = @container.config_item("lxc.network.0.hwaddr")
|
150
|
+
@container.destroy
|
151
|
+
remove_static_ip_address(hwaddr)
|
152
|
+
end
|
153
|
+
|
154
|
+
def release_lingering_dhcp_ip_addresses(hwaddr)
|
155
|
+
dhcp_leases = IO.readlines('/var/lib/misc/dnsmasq.lxcbr0.leases')
|
156
|
+
leases_to_release = dhcp_leases.map do |dhcp_lease|
|
157
|
+
if m = dhcp_lease.match(/ #{hwaddr} (\d+\.\d+\.\d+\.\d+) /)
|
158
|
+
mac_addr = hwaddr
|
159
|
+
ip_addr = m[1]
|
160
|
+
elsif m = dhcp_lease.match(/ (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) #{@ipaddress} /)
|
161
|
+
mac_addr = m[1]
|
162
|
+
ip_addr = @ipaddress
|
163
|
+
elsif m = dhcp_lease.match(/ (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) (\d+\.\d+\.\d+\.\d+) #{@container.name.sub(/\.lxc$/, '')} /)
|
164
|
+
mac_addr = m[1]
|
165
|
+
ip_addr = m[2]
|
166
|
+
end
|
167
|
+
if mac_addr && ip_addr
|
168
|
+
{ dhcp_lease: dhcp_lease, mac_addr: mac_addr, ip_addr: ip_addr }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
leases_to_release.compact!
|
172
|
+
unless leases_to_release.empty?
|
173
|
+
system("systemctl stop lxc-net.service")
|
174
|
+
leases_to_release.each do |l|
|
175
|
+
puts "Releasing lingering DHCP lease: #{l[:dhcp_lease]}"
|
176
|
+
DevLXC.search_file_delete_line("/var/lib/misc/dnsmasq.lxcbr0.leases", /( #{l[:mac_addr]} #{l[:ip_addr]} )/)
|
177
|
+
end
|
178
|
+
system("systemctl start lxc-net.service")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def assign_static_ip_address(hwaddr)
|
183
|
+
puts "Assigning IP address #{@ipaddress} to '#{@container.name}' container's lxc.network.hwaddr #{hwaddr}"
|
184
|
+
DevLXC.search_file_delete_line("/etc/lxc/dhcp-hosts.conf", /(^#{hwaddr}|,#{@ipaddress}$)/)
|
185
|
+
DevLXC.append_line_to_file("/etc/lxc/dhcp-hosts.conf", "#{hwaddr},#{@ipaddress}\n")
|
186
|
+
DevLXC.reload_dnsmasq
|
187
|
+
end
|
188
|
+
|
189
|
+
def remove_static_ip_address(hwaddr=nil)
|
190
|
+
if hwaddr
|
191
|
+
DevLXC.search_file_delete_line("/etc/lxc/dhcp-hosts.conf", /^#{hwaddr}/)
|
192
|
+
DevLXC.reload_dnsmasq
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
data/lib/dev-lxc/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module DevLXC
|
2
|
-
VERSION = "2.6.
|
3
|
-
end
|
1
|
+
module DevLXC
|
2
|
+
VERSION = "2.6.2"
|
3
|
+
end
|