elecksee 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +2 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +18 -0
  4. data/README.md +48 -0
  5. data/bin/lxc-awesome-ephemeral +5 -0
  6. data/elecksee.gemspec +16 -0
  7. data/lib/elecksee/awesome.rb +14 -0
  8. data/lib/elecksee/lxc.rb +7 -0
  9. data/lib/elecksee/vendor/lxc/CHANGELOG.md +37 -0
  10. data/lib/elecksee/vendor/lxc/Gemfile +4 -0
  11. data/lib/elecksee/vendor/lxc/Gemfile.lock +41 -0
  12. data/lib/elecksee/vendor/lxc/README.md +112 -0
  13. data/lib/elecksee/vendor/lxc/attributes/default.rb +28 -0
  14. data/lib/elecksee/vendor/lxc/files/default/knife_lxc +228 -0
  15. data/lib/elecksee/vendor/lxc/files/default/lxc-awesome-ephemeral +495 -0
  16. data/lib/elecksee/vendor/lxc/libraries/lxc.rb +354 -0
  17. data/lib/elecksee/vendor/lxc/libraries/lxc_expanded_resources.rb +40 -0
  18. data/lib/elecksee/vendor/lxc/libraries/lxc_file_config.rb +84 -0
  19. data/lib/elecksee/vendor/lxc/libraries/monkey.rb +51 -0
  20. data/lib/elecksee/vendor/lxc/metadata.rb +12 -0
  21. data/lib/elecksee/vendor/lxc/providers/config.rb +75 -0
  22. data/lib/elecksee/vendor/lxc/providers/container.rb +318 -0
  23. data/lib/elecksee/vendor/lxc/providers/default.rb +57 -0
  24. data/lib/elecksee/vendor/lxc/providers/ephemeral.rb +40 -0
  25. data/lib/elecksee/vendor/lxc/providers/fstab.rb +30 -0
  26. data/lib/elecksee/vendor/lxc/providers/interface.rb +45 -0
  27. data/lib/elecksee/vendor/lxc/providers/service.rb +53 -0
  28. data/lib/elecksee/vendor/lxc/recipes/containers.rb +13 -0
  29. data/lib/elecksee/vendor/lxc/recipes/default.rb +58 -0
  30. data/lib/elecksee/vendor/lxc/recipes/install_dependencies.rb +15 -0
  31. data/lib/elecksee/vendor/lxc/recipes/knife.rb +37 -0
  32. data/lib/elecksee/vendor/lxc/resources/config.rb +19 -0
  33. data/lib/elecksee/vendor/lxc/resources/container.rb +54 -0
  34. data/lib/elecksee/vendor/lxc/resources/default.rb +12 -0
  35. data/lib/elecksee/vendor/lxc/resources/ephemeral.rb +13 -0
  36. data/lib/elecksee/vendor/lxc/resources/fstab.rb +12 -0
  37. data/lib/elecksee/vendor/lxc/resources/interface.rb +13 -0
  38. data/lib/elecksee/vendor/lxc/resources/service.rb +5 -0
  39. data/lib/elecksee/vendor/lxc/templates/default/client.rb.erb +13 -0
  40. data/lib/elecksee/vendor/lxc/templates/default/default-lxc.erb +3 -0
  41. data/lib/elecksee/vendor/lxc/templates/default/file_content.erb +2 -0
  42. data/lib/elecksee/vendor/lxc/templates/default/fstab.erb +5 -0
  43. data/lib/elecksee/vendor/lxc/templates/default/interface.erb +27 -0
  44. data/lib/elecksee/version.rb +6 -0
  45. data/lib/elecksee.rb +1 -0
  46. metadata +123 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ ## v0.1.0
2
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ elecksee (1.0.0)
5
+ mixlib-shellout
6
+ net-ssh
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ mixlib-shellout (1.1.0)
12
+ net-ssh (2.6.7)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ elecksee!
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Elecksee
2
+
3
+ This is a simple library for interacting with LXC. It relies on
4
+ LXC tools being installed on the system to properly function.
5
+ It is extracted from the Chef LXC cookbook, so poke around there
6
+
7
+ ## Usage
8
+
9
+ ```ruby
10
+
11
+ require 'elecksee/lxc'
12
+
13
+ lxc = Lxc.new('my-container')
14
+ p lxc.info
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
29
+ ```
30
+
31
+ ### Using VBD
32
+
33
+ ```
34
+ $ lxc-awesome-ephemeral -o ubuntu -d -D 2000
35
+ ```
36
+
37
+ This will create a 2GB virtual block device for the container
38
+ to use for the overlay.
39
+
40
+ Note
41
+ ----
42
+
43
+ Overlays are not persistent (thus ephemeral) and will be automatically
44
+ cleaned up when the container has reached a stop state.
45
+
46
+ # Info
47
+
48
+ * Repository: https://github.com/chrisroberts/elecksee.git
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'elecksee/awesome'
4
+
5
+ Elecksee::Awesome.run!
data/elecksee.gemspec ADDED
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'elecksee/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'elecksee'
5
+ s.version = Elecksee::VERSION.version
6
+ s.summary = 'LXC helpers'
7
+ s.author = 'Chris Roberts'
8
+ s.email = 'chrisroberts.code@gmail.com'
9
+ s.homepage = 'http://github.com/chrisroberts/elecksee'
10
+ s.description = 'LXC helpers'
11
+ s.require_path = 'lib'
12
+ s.executables = %w(lxc-awesome-ephemeral)
13
+ s.add_dependency 'mixlib-shellout'
14
+ s.add_dependency 'net-ssh'
15
+ s.files = Dir['**/*']
16
+ end
@@ -0,0 +1,14 @@
1
+ module Elecksee
2
+ class Awesome
3
+ class << self
4
+ def run!
5
+ path = File.expand_path(
6
+ File.join(
7
+ File.dirname(__FILE__), 'vendor/lxc/files/default/lxc-awesome-ephemeral'
8
+ )
9
+ )
10
+ exec("/bin/bash #{path} #{ARGV.join(' ')}")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'mixlib/shellout'
2
+
3
+ require File.expand_path(
4
+ File.join(
5
+ File.dirname(__FILE__), 'vendor/lxc/libraries/lxc.rb'
6
+ )
7
+ )
@@ -0,0 +1,37 @@
1
+ ## v1.0.0
2
+ * Extract container actions out to new LWRP `lxc`
3
+ * Update `container` to use `lxc` resource for container actions
4
+ * Update `container` to allow nested subresources for `interface` and `fstab_mount`
5
+ * Fix `interface` LWRP to allow IPv6 based values
6
+ * Remove static_ip config set as it was introducing bogus route
7
+ * Provide assumed environment when not available (like when running via runit)
8
+ * Make chef enabled containers properly idempotent
9
+ * Clean up the `container` provider implementation to be more resource based
10
+ * Add `ephemeral` LWRP
11
+ * Add custom ephemeral script to allow host directory overlay or virtual block device
12
+ * Patches execute resource to provide streaming output
13
+ * Updates Lxc library to be more reusable
14
+ * Add proc based network detection for more robust address discovery
15
+ * Shell out directly to ssh for container commands instead of using knife ssh
16
+
17
+ ## v0.1.0
18
+ * Abstracted out packages for cross-platform support later.
19
+ * Added the 'containers' recipe to create containers for the members of the node['lxc']['containers'] hash
20
+ * Add support for use of the apt::cacher-client settings if a proxy is in use.
21
+ * chef_enabled defaults to false on lxc_containers
22
+ * Better idempotency checks when building new containers
23
+ * Refactoring of lxc_service
24
+ * Container based commands run via knife::ssh providing proper logging feedback
25
+ * New networking related attributes added to lxc_container for easy basic network setups
26
+
27
+ ## v0.0.3
28
+ * Remove resource for deprecated template
29
+
30
+ ## v0.0.2
31
+ * Cleanup current config and container LWRPs
32
+ * Add new LWRPs (fstab and interface)
33
+ * Add better configuration build to prevent false updates
34
+ * Thanks to Sean Porter (https://github.com/portertech) for help debugging LWRP updates
35
+
36
+ ## v0.0.1
37
+ * Initial release
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'test-kitchen', git: 'git://github.com/opscode/test-kitchen.git', branch: '1.0'
4
+
@@ -0,0 +1,41 @@
1
+ GIT
2
+ remote: git://github.com/opscode/test-kitchen.git
3
+ revision: ab1b878561e896c0619a780d168e71addff2ad91
4
+ branch: 1.0
5
+ specs:
6
+ test-kitchen (1.0.0.dev)
7
+ celluloid
8
+ mixlib-shellout
9
+ net-scp
10
+ net-ssh
11
+ pry
12
+ safe_yaml
13
+ thor
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ celluloid (0.12.4)
19
+ facter (>= 1.6.12)
20
+ timers (>= 1.0.0)
21
+ coderay (1.0.9)
22
+ facter (1.6.17)
23
+ method_source (0.8.1)
24
+ mixlib-shellout (1.1.0)
25
+ net-scp (1.1.0)
26
+ net-ssh (>= 2.6.5)
27
+ net-ssh (2.6.6)
28
+ pry (0.9.12)
29
+ coderay (~> 1.0.5)
30
+ method_source (~> 0.8)
31
+ slop (~> 3.4)
32
+ safe_yaml (0.8.5)
33
+ slop (3.4.4)
34
+ thor (0.17.0)
35
+ timers (1.1.0)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ test-kitchen!
@@ -0,0 +1,112 @@
1
+ # LXC
2
+
3
+ Manage linux containers with Chef.
4
+
5
+ ## Recipes
6
+
7
+ ### default
8
+
9
+ Installs the packages and configuration files needed for lxc on the server. If
10
+ the node uses apt-cacher-ng as a client, the server will be reused when building
11
+ containers.
12
+
13
+ ### install_dependencies
14
+
15
+ Installs the packages needed to support lxc's containers.
16
+
17
+ ### containers
18
+
19
+ This recipe creates all of the containers defined in the `['lxc']['containers']`
20
+ hash. Here is an example of an `example` container:
21
+
22
+ ```ruby
23
+ node['lxc']['containers']['example'] = {
24
+ 'template' => 'ubuntu',
25
+ 'initialize_commands' => ['apt-get update']
26
+ }
27
+ ```
28
+
29
+ ### knife
30
+
31
+ Install and manage containers via the knife-remotelxc plugin.
32
+
33
+ ## LWRPs
34
+
35
+ ### lxc
36
+
37
+ Allows for creation, deletion, and cloning of containers
38
+
39
+ ### lxc_config
40
+
41
+ Allows configuration of the LXC configuration file
42
+
43
+ ### lxc_fstab
44
+
45
+ Allows defining mounts to be used within the container
46
+
47
+ ### lxc_interface
48
+
49
+ Allows configurations of network interfaces within a container
50
+
51
+ ### lxc_ephemeral
52
+
53
+ Run a command within an ephemeral container
54
+
55
+ ### lxc_container
56
+
57
+ Creates a container using the `lxc` LWRP and configures the container
58
+ as requested. This resource also allows nesting `lxc_fstab` and
59
+ `lxc_interface` within the container resource.
60
+
61
+ ## Example
62
+
63
+ ```ruby
64
+ include_recipe 'lxc'
65
+
66
+ lxc_container 'my_container' do
67
+ action :create
68
+ validation_client 'my-validator'
69
+ server_uri 'https://api.opscode.com/organizations/myorg'
70
+ validator_pem content_from_encrypted_dbag
71
+ run_list ['role[base]']
72
+ chef_enabled true
73
+ fstab_mount "Persist" do
74
+ file_system '/opt/file_store'
75
+ mount_point '/opt/file_store'
76
+ type 'none'
77
+ options 'bind,rw'
78
+ end
79
+ end
80
+
81
+ lxc_container 'my_container_clone' do
82
+ action :create
83
+ clone 'my_container'
84
+ chef_enabled true
85
+ end
86
+
87
+ lxc_service 'my_container_clone' do
88
+ action :start
89
+ end
90
+ ```
91
+
92
+ Containers do not have to be Chef enabled but it does make them
93
+ extremely easy to configure. If you want the Omnibus installer
94
+ cached, you can set the attribute
95
+
96
+ ```ruby
97
+ node['omnibus_updater']['cache_omnibus_installer'] = true
98
+ ```
99
+
100
+ in a role or environment (default is false). The `lxc_container`
101
+ resource also provides `initialize_commands` which an array of
102
+ commands can be provided that will be run after the container is
103
+ created.
104
+
105
+ ### Repository:
106
+
107
+ * https://github.com/hw-cookbooks/lxc
108
+
109
+ ### Contributors
110
+
111
+ * Sean Porter (https://github.com/portertech)
112
+ * Matt Ray (https://github.com/mattray)
@@ -0,0 +1,28 @@
1
+ default[:lxc][:start_ipaddress] = nil
2
+ default[:lxc][:validator_pem] = nil
3
+ default[:lxc][:auto_start] = true
4
+ default[:lxc][:bridge] = 'lxcbr0'
5
+ default[:lxc][:use_bridge] = true
6
+ default[:lxc][:addr] = '10.0.3.1'
7
+ default[:lxc][:netmask] = '255.255.255.0'
8
+ default[:lxc][:network] = '10.0.3.0/24'
9
+ default[:lxc][:dhcp_range] = '10.0.3.2,10.0.3.254'
10
+ default[:lxc][:dhcp_max] = '253'
11
+ default[:lxc][:shutdown_timeout] = 120
12
+ default[:lxc][:allowed_types] = %w(debian ubuntu fedora)
13
+ default[:lxc][:container_directory] = '/var/lib/lxc'
14
+ default[:lxc][:dnsmasq_lease_file] = '/var/lib/misc/dnsmasq.leases'
15
+
16
+ default[:lxc][:knife] = {}
17
+ default[:lxc][:knife][:static_range] = ''
18
+ default[:lxc][:knife][:static_ips] = []
19
+
20
+ default[:lxc][:user_pass][:debian] = {:username => 'root', :password => 'root'}
21
+ default[:lxc][:user_pass][:ubuntu] = {:username => 'ubuntu', :password => 'ubuntu'}
22
+ default[:lxc][:user_pass][:fedora] = {:username => 'root', :password => 'root'}
23
+
24
+ default[:lxc][:packages] = %w(lxc)
25
+ default[:lxc][:mirror] = 'http://archive.ubuntu.com/ubuntu'
26
+ default[:lxc][:containers] = {}
27
+
28
+ default[:lxc][:awesome_ephemerals] = true
@@ -0,0 +1,228 @@
1
+ #!/opt/chef/embedded/bin/ruby
2
+
3
+ require 'json'
4
+ require 'getoptlong'
5
+
6
+ LXC_HOME = '/var/lib/lxc'
7
+
8
+ opts = GetoptLong.new(
9
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
10
+ ['--version', '-v', GetoptLong::NO_ARGUMENT],
11
+ ['--template', '-t', GetoptLong::REQUIRED_ARGUMENT]
12
+ )
13
+
14
+ # Default
15
+ template = 'ubuntu'
16
+
17
+ opts.each do |opt, arg|
18
+ case opt
19
+ when '--help'
20
+ show_usage
21
+ exit 0
22
+ when '--version'
23
+ show_version
24
+ exit 0
25
+ when '--template'
26
+ template = arg
27
+ raise 'Unsupported template provided' unless %w(debian fedora ubuntu).include?(template)
28
+ end
29
+ end
30
+
31
+ def conf
32
+ @c ||= JSON.load(File.read('/etc/knife-lxc/config.json'))
33
+ end
34
+
35
+ def lxc_exists?(lxc_name)
36
+ current_names = Dir.glob(File.join(LXC_HOME, '*')).map{|c| File.basename(c)}
37
+ current_names.include?(lxc_name)
38
+ end
39
+
40
+ def ensure_name_availability!(name)
41
+ raise 'Name already in use' if lxc_exists?(name)
42
+ end
43
+
44
+ def available_ips
45
+ # TODO: Add range calculation
46
+ range = conf['address']['range']
47
+ if(range.to_s.empty?)
48
+ range = (range.split('-').first.split('.').last..range.split('-').last).map{|oct|
49
+ "#{range.split('-').first.split('.').slice(0,3).join('.')}.#{oct}"
50
+ }
51
+ else
52
+ range = []
53
+ end
54
+ (conf['addresses']['static'] + range).compact
55
+ end
56
+
57
+ def used_ips
58
+ Dir.glob(File.join(LXC_HOME, '*')).map{ |ctn|
59
+ File.readlines(File.join(ctn, 'rootfs', 'etc', 'network', 'interfaces')).detect{ |line|
60
+ line.include?('address')
61
+ }.to_s.split(' ').last.to_s.strip
62
+ }.reject{ |addr|
63
+ addr.empty?
64
+ }
65
+ end
66
+
67
+ def update_container_ip(name)
68
+ new_ip = (available_ips - used_ips).pop
69
+ raise 'no ips available' unless new_ip
70
+ update_network_interfaces(name, new_ip)
71
+ new_ip
72
+ end
73
+
74
+ def update_network_interfaces(name, address)
75
+ default_nm = '255.255.255.0'
76
+ (parts = address.split('.')).last.replace('1')
77
+ default_gw = parts.join('.')
78
+ File.open(File.join(LXC_HOME, name, 'rootfs', 'etc', 'network', 'interfaces'), 'w') do |file|
79
+ file.puts "auto lo eth0"
80
+ file.puts "iface lo inet loopback"
81
+ file.puts "iface eth0 inet static"
82
+ file.puts " address #{address}"
83
+ file.puts " gateway #{conf['gateway'] || default_gw}"
84
+ file.puts " netmask #{conf['netmask'] || default_nm}"
85
+ end
86
+ end
87
+
88
+ def sudoable_ubuntu(name)
89
+ path = File.join(LXC_HOME, name, 'rootfs', 'etc', 'sudoers')
90
+ content = File.readlines(path).map do |line|
91
+ if(line.start_with?('%sudo'))
92
+ '%sudo ALL=(ALL) NOPASSWD:ALL'
93
+ else
94
+ line
95
+ end
96
+ end
97
+ File.open(path, 'w') do |file|
98
+ file.write(content.join("\n"))
99
+ end
100
+ end
101
+
102
+ def clone_container(name, template)
103
+ %x{lxc-clone -o #{template}_base -n #{name}}
104
+ end
105
+
106
+ def start_container(name)
107
+ %x{lxc-start -n #{name} -d}
108
+ %x{lxc-wait -n #{name} -s RUNNING}
109
+ end
110
+
111
+ def stop_container(name)
112
+ %x{lxc-shutdown -n #{name} -d}
113
+ %x{lxc-wait -n #{name} -s STOPPED}
114
+ end
115
+
116
+ def create_lxc(lxc_name, template)
117
+ ensure_name_availability!(lxc_name)
118
+ clone_container(lxc_name, template)
119
+ address = update_container_ip(lxc_name)
120
+ # TODO: Update debian and fedora to use sudo user and remove root login
121
+ if(lxc_name == 'ubuntu')
122
+ sudoable_ubuntu(lxc_name)
123
+ end
124
+ start_container(lxc_name)
125
+ puts "LXC Node #{lxc_name} available at: #{address}"
126
+ end
127
+
128
+ def lxc_address(name)
129
+ if(File.exists?(int = File.join(LXC_HOME, name, 'rootfs', 'etc', 'network', 'interfaces')))
130
+ addr = File.readlines(int).detect{|l|l.include?('address')}.to_s.split(' ').last.to_s.strip
131
+ addr.empty? ? 'dynamic' : addr
132
+ else
133
+ 'dynamic'
134
+ end
135
+ end
136
+
137
+ def lxc_type(name)
138
+ base = File.join(LXC_HOME, name, 'rootfs', 'etc')
139
+ if(File.exists?(lsb = File.join(base, 'lsb-release')))
140
+ File.readlines(lsb).last.split('=').last.strip.gsub('"', '')
141
+ elsif(File.exists?(sys_rel = File.join(base, 'system-release')))
142
+ File.readlines(sys_rel).first.strip
143
+ elsif(File.exists?(deb_ver = File.join(base, 'debian_version')))
144
+ "Debain #{File.read(deb_ver).strip}"
145
+ else
146
+ 'UNKNOWN'
147
+ end
148
+ end
149
+
150
+ def list_lxcs
151
+ info = Hash[
152
+ *Dir.glob(File.join(LXC_HOME, '*')).map{|dir|
153
+ key = File.basename(dir)
154
+ [key, {:address => lxc_address(key), :type => lxc_type(key)}]
155
+ }.sort{|a,b|
156
+ a.first <=> b.first
157
+ }.flatten
158
+ ]
159
+ info.each do |name, info|
160
+ puts "#{name}"
161
+ puts " Type: #{info[:type]}"
162
+ puts " Address: #{info[:address]}"
163
+ end
164
+ end
165
+
166
+ def info_lxc(lxc_name)
167
+ puts "#{lxc_name}"
168
+ puts " Type: #{lxc_type(lxc_name)}"
169
+ puts " Address: #{lxc_address(lxc_name)}"
170
+ end
171
+
172
+ def delete_lxc(lxc_name)
173
+ %x{lxc-stop -n #{lxc_name}}
174
+ %x{lxc-wait -n #{lxc_name} -s STOPPED}
175
+ %x{lxc-destroy -n #{lxc_name}}
176
+ end
177
+
178
+ action = ARGV.first.to_s
179
+
180
+ case action
181
+ when 'create'
182
+ lxc_name = ARGV[1]
183
+ create_lxc(lxc_name, template)
184
+ when 'list'
185
+ list_lxcs
186
+ when 'info'
187
+ lxc_name = ARGV[1]
188
+ if(lxc_exists?(lxc_name))
189
+ info_lxc(lxc_name)
190
+ else
191
+ $stderr.puts "Requested container does not exist: #{lxc_name}"
192
+ exit 2
193
+ end
194
+ when 'start'
195
+ lxc_name = ARGV[1]
196
+ if(lxc_exists?(lxc_name))
197
+ print "Starting container #{lxc_name}... "
198
+ start_lxc(lxc_name)
199
+ puts 'started'
200
+ else
201
+ $stderr.puts "Requested container does not exist: #{lxc_name}"
202
+ exit 2
203
+ end
204
+ when 'stop'
205
+ lxc_name = ARGV[1]
206
+ if(lxc_exists?(lxc_name))
207
+ print "Stopping container #{lxc_name}... "
208
+ stop_lxc(lxc_name)
209
+ puts 'stopped'
210
+ else
211
+ $stderr.puts "Requested container does not exist: #{lxc_name}"
212
+ exit 2
213
+ end
214
+ when 'delete'
215
+ lxc_name = ARGV[1]
216
+ if(lxc_exists?(lxc_name))
217
+ print "Deleting LXC #{lxc_name}... "
218
+ destroy_lxc(lxc_name)
219
+ puts 'done'
220
+ else
221
+ $stderr.puts "Requested container does not exist: #{lxc_name}"
222
+ exit 2
223
+ end
224
+ else
225
+ $stderr.puts "ERROR: Unknown action: #{action}"
226
+ exit 1
227
+ end
228
+