lxc-ruby 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/README.md +143 -0
- data/Rakefile +10 -0
- data/lib/lxc/configuration.rb +77 -0
- data/lib/lxc/configuration_options.rb +27 -0
- data/lib/lxc/container.rb +219 -0
- data/lib/lxc/errors.rb +5 -0
- data/lib/lxc/shell.rb +89 -0
- data/lib/lxc/version.rb +3 -0
- data/lib/lxc.rb +58 -0
- data/lxc.gemspec +20 -0
- data/spec/configuration_spec.rb +22 -0
- data/spec/container_spec.rb +86 -0
- data/spec/fixtures/configuration.txt +33 -0
- data/spec/fixtures/lxc-checkconfig.txt +29 -0
- data/spec/fixtures/lxc-info-running.txt +2 -0
- data/spec/fixtures/lxc-info-stopped.txt +2 -0
- data/spec/fixtures/lxc-ps-aux.txt +9 -0
- data/spec/fixtures/lxc-version.txt +1 -0
- data/spec/lxc_spec.rb +118 -0
- data/spec/spec_helper.rb +17 -0
- metadata +113 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.swp
|
4
|
+
*.tmproj
|
5
|
+
*~
|
6
|
+
.DS_Store
|
7
|
+
.\#*
|
8
|
+
.bundle
|
9
|
+
.config
|
10
|
+
.yardoc
|
11
|
+
Gemfile.lock
|
12
|
+
InstalledFiles
|
13
|
+
\#*
|
14
|
+
_yardoc
|
15
|
+
coverage
|
16
|
+
doc/
|
17
|
+
lib/bundler/man
|
18
|
+
pkg
|
19
|
+
rdoc
|
20
|
+
spec/reports
|
21
|
+
test/tmp
|
22
|
+
test/version_tmp
|
23
|
+
tmp
|
24
|
+
tmtags
|
25
|
+
lib/test.rb
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# LXC Ruby Wrapper
|
2
|
+
|
3
|
+
Ruby wrapper to [LXC](http://lxc.sourceforge.net/) cli tools.
|
4
|
+
|
5
|
+
Provides a simple ruby dsl and json API to manage containers.
|
6
|
+
|
7
|
+
## Build status
|
8
|
+
|
9
|
+
This library uses Travis-CI service for automated tests.
|
10
|
+
|
11
|
+
[![Build Status](https://secure.travis-ci.org/sosedoff/lxc-ruby.png?branch=master)](http://travis-ci.org/sosedoff/lxc-ruby)
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
|
15
|
+
Supported LXC versions:
|
16
|
+
|
17
|
+
- 0.7.5
|
18
|
+
- 0.8.0-rc1
|
19
|
+
- 0.8.0-rc2 - in works
|
20
|
+
|
21
|
+
For testing purposes you can use [Vagrant](http://vagrantup.com/) or [VirtualBox](https://www.virtualbox.org/). Most of functionality
|
22
|
+
was tested on Ubuntu 11.04 / 11.10. Additional boxes could be found [here](http://www.vagrantbox.es/)
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
As for now this gem is not released yet, so use gem install task:
|
27
|
+
|
28
|
+
```
|
29
|
+
rake build
|
30
|
+
rake install
|
31
|
+
```
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
You should have LXC already installed on your system before using the library.
|
36
|
+
|
37
|
+
Example:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'lxc'
|
41
|
+
|
42
|
+
# Check if all lxc binaries are installed
|
43
|
+
LXC.installed?
|
44
|
+
|
45
|
+
# Get LXC version
|
46
|
+
LXC.version
|
47
|
+
|
48
|
+
# Get current LXC configuration
|
49
|
+
LXC.config
|
50
|
+
|
51
|
+
# Get a list of all containers
|
52
|
+
LXC.containers
|
53
|
+
|
54
|
+
# Get a single container by name
|
55
|
+
LXC.container('name')
|
56
|
+
```
|
57
|
+
|
58
|
+
Container instance is a simple abstaction for lxc's container tools:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
c = LXC.container('foo')
|
62
|
+
|
63
|
+
# Get current status of container
|
64
|
+
c.status # => {:state => 'RUNNING', :pid => 1234}
|
65
|
+
|
66
|
+
# Check if container exists?
|
67
|
+
# this is needed since lxc does not raise any errors if container is
|
68
|
+
# not present in the system, and returns the same result as if container
|
69
|
+
# is actually stopped
|
70
|
+
c.exists? # => true
|
71
|
+
|
72
|
+
# Status helpers
|
73
|
+
c.running? # => true
|
74
|
+
c.frozen? # => false
|
75
|
+
|
76
|
+
# Start and stop containers
|
77
|
+
c.start # => {:state => 'RUNNING', :pid => 1234}
|
78
|
+
c.stop # => {:state => 'STOPPED', :pid => -1}
|
79
|
+
|
80
|
+
# Free and unfreeze (also returns current status)
|
81
|
+
c.freeze
|
82
|
+
c.unfreeze
|
83
|
+
|
84
|
+
# Get container memory usage (in bytes)
|
85
|
+
c.memory_usage
|
86
|
+
c.memory_limit
|
87
|
+
|
88
|
+
# Get running processes
|
89
|
+
c.processes
|
90
|
+
# =>
|
91
|
+
#[{"pid"=>"27404",
|
92
|
+
# "user"=>"root",
|
93
|
+
# "cpu"=>"0.0",
|
94
|
+
# "memory"=>"0.1",
|
95
|
+
# "command"=>"/sbin/init",
|
96
|
+
# "args"=>""}]
|
97
|
+
|
98
|
+
# Destroy container
|
99
|
+
c.destroy # => true
|
100
|
+
```
|
101
|
+
|
102
|
+
To create a new container:
|
103
|
+
|
104
|
+
``` ruby
|
105
|
+
c = LXC::Container.new('foo')
|
106
|
+
c.create(path_to_lxc_config)
|
107
|
+
```
|
108
|
+
|
109
|
+
This method invokes ```lxc-create -n NAME -f CONFIG``` command. It *DOES NOT* create
|
110
|
+
any rootfs images or configures anything.
|
111
|
+
|
112
|
+
### Running with sudo
|
113
|
+
|
114
|
+
By default LXC does not allow to run its command under unprivileged user. There are
|
115
|
+
two ways to make it work:
|
116
|
+
|
117
|
+
**Using sudo**
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
LXC.use_sudo = true
|
121
|
+
```
|
122
|
+
|
123
|
+
**Using lxc-setcap**
|
124
|
+
|
125
|
+
If you want to make container usable by non-root users, run lxc-setcap as root, and some capabilities will be set so that normal users will be able to use the container utils. This is not done by default, though, and you have to explicitly allow it.
|
126
|
+
|
127
|
+
## Testing
|
128
|
+
|
129
|
+
To run the test suite execute:
|
130
|
+
|
131
|
+
```
|
132
|
+
rake test
|
133
|
+
```
|
134
|
+
|
135
|
+
## License
|
136
|
+
|
137
|
+
Copyright (c) 2012 Dan Sosedoff.
|
138
|
+
|
139
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
140
|
+
|
141
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
142
|
+
|
143
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module LXC
|
2
|
+
class Configuration
|
3
|
+
include LXC::ConfigurationOptions
|
4
|
+
|
5
|
+
attr_reader :content
|
6
|
+
|
7
|
+
# Initialize a new LXC::Configuration instance
|
8
|
+
# @param [data] string or hash data (optional)
|
9
|
+
def initialize(data=nil)
|
10
|
+
if data.kind_of?(String)
|
11
|
+
@content = parse(data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Load an existing LXC container configuration from file
|
16
|
+
# @param [path] path to configuration
|
17
|
+
# @return [LXC::Configuration]
|
18
|
+
def self.load_file(path)
|
19
|
+
fullpath = File.expand_path(path)
|
20
|
+
if !File.exists?(fullpath)
|
21
|
+
raise ArgumentError, "File '#{path}' does not exist."
|
22
|
+
end
|
23
|
+
LXC::Configuration.new(File.read(fullpath))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get all configuration attributes
|
27
|
+
# @return [Array]
|
28
|
+
def attributes
|
29
|
+
@content.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get attribute value
|
33
|
+
# @param [key] attribute name
|
34
|
+
# @return [String] configuration attribute value
|
35
|
+
def [](key)
|
36
|
+
@content[key.to_s]
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(key)
|
40
|
+
@content[key.to_s]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Save configuration into file
|
44
|
+
# @param [path] path to output file
|
45
|
+
def save_to_file(path)
|
46
|
+
fullpath = File.expand_path(path)
|
47
|
+
lines = []
|
48
|
+
@content.each_pair do |key,value|
|
49
|
+
k = "lxc.#{key.gsub('_', '.')}"
|
50
|
+
if value.kind_of?(Array)
|
51
|
+
lines << value.map { |v| "#{k} = #{v}" }
|
52
|
+
else
|
53
|
+
lines << "#{k} = #{value}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
File.open(path, 'w') { |f| f.write(lines.flatten.join("\n")) }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def parse(data)
|
62
|
+
hash = {}
|
63
|
+
lines = data.split("\n").map(&:strip).select { |l| !l.empty? && l[0,1] != '#' }
|
64
|
+
lines.each do |l|
|
65
|
+
key,value = l.split('=').map(&:strip)
|
66
|
+
if !valid_option?(key)
|
67
|
+
raise ConfigurationError, "Invalid config attribute: #{key}."
|
68
|
+
end
|
69
|
+
key.gsub!(/^lxc\./, '').gsub!('.', '_')
|
70
|
+
hash[key] = [] if !hash.key?(key)
|
71
|
+
hash[key] << value
|
72
|
+
end
|
73
|
+
hash.each_pair { |k,v| hash[k] = v.first if v.size == 1 }
|
74
|
+
hash
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module LXC
|
2
|
+
module ConfigurationOptions
|
3
|
+
VALID_OPTIONS = [
|
4
|
+
'lxc.utsname',
|
5
|
+
'lxc.network.type',
|
6
|
+
'lxc.network.flags',
|
7
|
+
'lxc.network.link',
|
8
|
+
'lxc.network.name',
|
9
|
+
'lxc.network.hwaddr',
|
10
|
+
'lxc.network.ipv4',
|
11
|
+
'lxc.network.ipv6',
|
12
|
+
'lxc.pts',
|
13
|
+
'lxc.tty',
|
14
|
+
'lxc.mount',
|
15
|
+
'lxc.mount.entry',
|
16
|
+
'lxc.rootfs',
|
17
|
+
'lxc.cgroup',
|
18
|
+
'lxc.cap.drop'
|
19
|
+
]
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def valid_option?(name)
|
24
|
+
VALID_OPTIONS.include?(name) || name =~ /^lxc.cgroup/
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module LXC
|
2
|
+
class Container
|
3
|
+
attr_accessor :name
|
4
|
+
attr_reader :state
|
5
|
+
attr_reader :pid
|
6
|
+
|
7
|
+
# Initialize a new LXC::Container instance
|
8
|
+
# @param [String] name container name
|
9
|
+
# @return [LXC::Container] container instance
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get container attributes hash
|
15
|
+
# @return [Hash]
|
16
|
+
def to_hash
|
17
|
+
status
|
18
|
+
{'name' => name, 'state' => state, 'pid' => pid}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get current status of container
|
22
|
+
# @return [Hash] hash with :state and :pid attributes
|
23
|
+
def status
|
24
|
+
str = LXC.run('info', '-n', name)
|
25
|
+
@state = str.scan(/^state:\s+([\w]+)$/).flatten.first
|
26
|
+
@pid = str.scan(/^pid:\s+(-?[\d]+)$/).flatten.first
|
27
|
+
{:state => @state, :pid => @pid}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if container exists
|
31
|
+
# @return [Boolean]
|
32
|
+
def exists?
|
33
|
+
LXC.run('ls').split("\n").uniq.include?(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if container is running
|
37
|
+
# @return [Boolean]
|
38
|
+
def running?
|
39
|
+
status[:state] == 'RUNNING'
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check if container is frozen
|
43
|
+
# @return [Boolean]
|
44
|
+
def frozen?
|
45
|
+
status[:state] == 'FROZEN'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Start container
|
49
|
+
# @return [Hash] container status hash
|
50
|
+
def start
|
51
|
+
LXC.run('start', '-d', '-n', name)
|
52
|
+
status
|
53
|
+
end
|
54
|
+
|
55
|
+
# Stop container
|
56
|
+
# @return [Hash] container status hash
|
57
|
+
def stop
|
58
|
+
LXC.run('stop', '-n', name)
|
59
|
+
status
|
60
|
+
end
|
61
|
+
|
62
|
+
# Restart container
|
63
|
+
# @return [Hash] container status hash
|
64
|
+
def restart
|
65
|
+
stop
|
66
|
+
start
|
67
|
+
end
|
68
|
+
|
69
|
+
# Freeze container
|
70
|
+
# @return [Hash] container status hash
|
71
|
+
def freeze
|
72
|
+
LXC.run('freeze', '-n', name)
|
73
|
+
status
|
74
|
+
end
|
75
|
+
|
76
|
+
# Unfreeze container
|
77
|
+
# @return [Hash] container status hash
|
78
|
+
def unfreeze
|
79
|
+
LXC.run('unfreeze', '-n', name)
|
80
|
+
status
|
81
|
+
end
|
82
|
+
|
83
|
+
# Wait for container to change status
|
84
|
+
# @param [String] state state name
|
85
|
+
def wait(state)
|
86
|
+
if !LXC::Shell.valid_state?(state)
|
87
|
+
raise ArgumentError, "Invalid container state: #{state}"
|
88
|
+
end
|
89
|
+
LXC.run('wait', '-n', name, '-s', state)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get container memory usage in bytes
|
93
|
+
# @return [Integer]
|
94
|
+
def memory_usage
|
95
|
+
LXC.run('cgroup', '-n', name, 'memory.usage_in_bytes').strip.to_i
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get container memory limit in bytes
|
99
|
+
# @return [Integer]
|
100
|
+
def memory_limit
|
101
|
+
LXC.run('cgroup', '-n', name, 'memory.limit_in_bytes').strip.to_i
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get container processes
|
105
|
+
# @return [Array] list of all processes
|
106
|
+
def processes
|
107
|
+
raise ContainerError, "Container is not running" if !running?
|
108
|
+
str = LXC.run('ps', '-n', name, '--', '-eo pid,user,%cpu,%mem,args').strip
|
109
|
+
lines = str.split("\n") ; lines.delete_at(0)
|
110
|
+
lines.map { |l| parse_process_line(l) }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create a new container
|
114
|
+
# @param [String] path path to container config file or [Hash] options
|
115
|
+
# @return [Boolean]
|
116
|
+
def create(path)
|
117
|
+
raise ContainerError, "Container already exists." if exists?
|
118
|
+
if path.is_a?(Hash)
|
119
|
+
args = "-n #{name}"
|
120
|
+
|
121
|
+
if !!path[:config_file]
|
122
|
+
unless File.exists?(path[:config_file])
|
123
|
+
raise ArgumentError, "File #{path[:config_file]} does not exist."
|
124
|
+
end
|
125
|
+
args += " -f #{path[:config_file]}"
|
126
|
+
end
|
127
|
+
|
128
|
+
if !!path[:template]
|
129
|
+
template_path = "/usr/lib/lxc/templates/lxc-#{path[:template]}"
|
130
|
+
unless File.exists?(template_path)
|
131
|
+
raise ArgumentError, "Template #{path[:template]} does not exist."
|
132
|
+
end
|
133
|
+
args += " -t #{path[:template]}"
|
134
|
+
end
|
135
|
+
|
136
|
+
args += " -B #{path[:backingstore]}" if !!path[:backingstore]
|
137
|
+
args += " -- #{path[:template_options].join(' ')}".strip if !!path[:template_options]
|
138
|
+
|
139
|
+
LXC.run('create', args)
|
140
|
+
exists?
|
141
|
+
else
|
142
|
+
raise ArgumentError, "File #{path} does not exist." unless File.exists?(path)
|
143
|
+
LXC.run('create', '-n', name, '-f', path)
|
144
|
+
exists?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Clone to a new container from self
|
149
|
+
# @param [String] target name of new container
|
150
|
+
# @return [LXC::Container] new container instance
|
151
|
+
def clone_to(target)
|
152
|
+
raise ContainerError, "Container does not exist." unless exists?
|
153
|
+
if self.class.new(target).exists?
|
154
|
+
raise ContainerError, "New container already exists."
|
155
|
+
end
|
156
|
+
|
157
|
+
LXC.run('clone', '-o', name, '-n', target)
|
158
|
+
self.class.new target
|
159
|
+
end
|
160
|
+
|
161
|
+
# Create a new container from an existing container
|
162
|
+
# @param [String] source name of existing container
|
163
|
+
# @return [Boolean]
|
164
|
+
def clone_from(source)
|
165
|
+
raise ContainerError, "Container already exists." if exists?
|
166
|
+
unless self.class.new(source).exists?
|
167
|
+
raise ContainerError, "Source container does not exist."
|
168
|
+
end
|
169
|
+
|
170
|
+
LXC.run('clone', '-o', source, '-n', name)
|
171
|
+
exists?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Destroy the container
|
175
|
+
# @param [Boolean] force force destruction
|
176
|
+
# @return [Boolean] true if container was destroyed
|
177
|
+
#
|
178
|
+
# If container is running and `force` parameter is true
|
179
|
+
# it will be stopped first. Otherwise it will raise exception.
|
180
|
+
#
|
181
|
+
def destroy(force=false)
|
182
|
+
raise ContainerError, "Container does not exist." unless exists?
|
183
|
+
if running?
|
184
|
+
if force
|
185
|
+
# This will force stop and destroy container automatically
|
186
|
+
LXC.run('destroy', '-n', '-f', name)
|
187
|
+
else
|
188
|
+
raise ContainerError, "Container is running. Stop it first or use force=true"
|
189
|
+
end
|
190
|
+
else
|
191
|
+
LXC.run('destroy', '-n', name)
|
192
|
+
end
|
193
|
+
!exists?
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def parse_process_line(line)
|
199
|
+
chunks = line.split(' ')
|
200
|
+
chunks.delete_at(0)
|
201
|
+
|
202
|
+
pid = chunks.shift
|
203
|
+
user = chunks.shift
|
204
|
+
cpu = chunks.shift
|
205
|
+
mem = chunks.shift
|
206
|
+
command = chunks.shift
|
207
|
+
args = chunks.join(' ')
|
208
|
+
|
209
|
+
{
|
210
|
+
'pid' => pid,
|
211
|
+
'user' => user,
|
212
|
+
'cpu' => cpu,
|
213
|
+
'memory' => mem,
|
214
|
+
'command' => command,
|
215
|
+
'args' => args
|
216
|
+
}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/lib/lxc/errors.rb
ADDED
data/lib/lxc/shell.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module LXC
|
2
|
+
module Shell
|
3
|
+
extend self
|
4
|
+
|
5
|
+
BIN_PREFIX = '/usr/bin'
|
6
|
+
|
7
|
+
BIN_FILES = [
|
8
|
+
'lxc-attach',
|
9
|
+
'lxc-cgroup',
|
10
|
+
'lxc-checkconfig',
|
11
|
+
'lxc-checkpoint',
|
12
|
+
'lxc-clone',
|
13
|
+
'lxc-console',
|
14
|
+
'lxc-create',
|
15
|
+
'lxc-destroy',
|
16
|
+
'lxc-execute',
|
17
|
+
'lxc-freeze',
|
18
|
+
'lxc-info',
|
19
|
+
'lxc-kill',
|
20
|
+
'lxc-ls',
|
21
|
+
'lxc-monitor',
|
22
|
+
'lxc-netstat',
|
23
|
+
'lxc-ps',
|
24
|
+
'lxc-restart',
|
25
|
+
'lxc-setcap',
|
26
|
+
'lxc-setuid',
|
27
|
+
'lxc-start',
|
28
|
+
'lxc-start-ephemeral',
|
29
|
+
'lxc-stop',
|
30
|
+
'lxc-unfreeze',
|
31
|
+
'lxc-unshare',
|
32
|
+
'lxc-version',
|
33
|
+
'lxc-wait'
|
34
|
+
]
|
35
|
+
|
36
|
+
CONTAINER_STATES = [
|
37
|
+
'STOPPED',
|
38
|
+
'STARTING',
|
39
|
+
'RUNNING',
|
40
|
+
'STOPPING',
|
41
|
+
'ABORTING',
|
42
|
+
'FREEZING',
|
43
|
+
'FROZEN'
|
44
|
+
]
|
45
|
+
|
46
|
+
@@use_sudo = false
|
47
|
+
|
48
|
+
# Check if LXC is using sudo to run commands
|
49
|
+
# @return [Boolean] current sudo flag value
|
50
|
+
def use_sudo
|
51
|
+
@@use_sudo
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set LXC to execute commands with sudo
|
55
|
+
# @param val [Boolean] true for sudo usage
|
56
|
+
# @return [Boolean] new sudo flag value
|
57
|
+
def use_sudo=(val)
|
58
|
+
@@use_sudo = val
|
59
|
+
val
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if container state is valid
|
63
|
+
# @param name [String] container name
|
64
|
+
# @return [Boolean]
|
65
|
+
def valid_state?(name)
|
66
|
+
CONTAINER_STATES.include?(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Execute a LXC command
|
70
|
+
# @param [String] name command name
|
71
|
+
# @param [Array] args command arguments
|
72
|
+
# @return [String] execution result
|
73
|
+
#
|
74
|
+
# If you would like to use pipe command you'll need to
|
75
|
+
# provide a block that returns string
|
76
|
+
def run(command, *args)
|
77
|
+
command_name = "lxc-#{command}"
|
78
|
+
unless BIN_FILES.include?(command_name)
|
79
|
+
raise ArgumentError, "Invalid command: #{command_name}."
|
80
|
+
end
|
81
|
+
|
82
|
+
cmd = ""
|
83
|
+
cmd += "sudo " if use_sudo == true
|
84
|
+
cmd += "#{command_name} #{args.join(' ')}".strip
|
85
|
+
cmd += " | #{yield}" if block_given?
|
86
|
+
`#{cmd.strip}`
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/lxc/version.rb
ADDED
data/lib/lxc.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'lxc/version'
|
2
|
+
require 'lxc/errors'
|
3
|
+
require 'lxc/shell'
|
4
|
+
require 'lxc/configuration_options'
|
5
|
+
require 'lxc/configuration'
|
6
|
+
require 'lxc/container'
|
7
|
+
|
8
|
+
module LXC
|
9
|
+
class << self
|
10
|
+
include LXC::Shell
|
11
|
+
|
12
|
+
# Check if binary file is installed
|
13
|
+
# @param [String] name binary filename
|
14
|
+
# @return [Boolean] true if installed
|
15
|
+
def binary_installed?(name)
|
16
|
+
path = File.join(LXC::Shell::BIN_PREFIX, name)
|
17
|
+
File.exists?(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Check if all binaries are present in the system
|
21
|
+
# @return [Boolean] true if binary files are found
|
22
|
+
def installed?
|
23
|
+
!BIN_FILES.map { |f| binary_installed?(f) }.uniq.include?(false)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get LXC configuration info
|
27
|
+
# @return [Hash] hash containing config groups
|
28
|
+
def config
|
29
|
+
str = LXC.run('checkconfig') { 'sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"' }
|
30
|
+
data = str.scan(/^([\w\s]+): (enabled|disabled)$/).map { |r|
|
31
|
+
[r.first.downcase.gsub(' ', '_'), r.last == 'enabled']
|
32
|
+
}
|
33
|
+
Hash[data]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get container information record
|
37
|
+
# @param [name] name container name
|
38
|
+
# @return [LXC::Container] container instance
|
39
|
+
def container(name)
|
40
|
+
LXC::Container.new(name)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a list of all available containers
|
44
|
+
# @param [String] filter select containers that match string
|
45
|
+
# @return [Array] array of LXC::Containers
|
46
|
+
def containers(filter=nil)
|
47
|
+
names = LXC.run('ls').split("\n").uniq
|
48
|
+
names.delete_if { |v| !v.include?(filter) } if filter.kind_of?(String)
|
49
|
+
names.map { |name| Container.new(name) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get current LXC version
|
53
|
+
# @return [String] current LXC version
|
54
|
+
def version
|
55
|
+
LXC.run('version').strip.split(' ').last
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lxc.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../lib/lxc/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "lxc-ruby"
|
5
|
+
s.version = LXC::VERSION
|
6
|
+
s.summary = "Ruby wrapper to LXC"
|
7
|
+
s.description = "Ruby wrapper to manage LXC (Linux Containers)."
|
8
|
+
s.homepage = "http://github.com/sosedoff/lxc"
|
9
|
+
s.authors = ["Dan Sosedoff"]
|
10
|
+
s.email = ["dan.sosedoff@gmail.com"]
|
11
|
+
|
12
|
+
s.add_development_dependency 'rake'
|
13
|
+
s.add_development_dependency 'rspec', '~> 2.6'
|
14
|
+
s.add_development_dependency 'simplecov', '~> 0.4'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LXC::Configuration do
|
4
|
+
it 'parses config data' do
|
5
|
+
conf = LXC::Configuration.new(fixture('configuration.txt'))
|
6
|
+
conf.content.should be_a Hash
|
7
|
+
conf.attributes.should be_an Array
|
8
|
+
conf.attributes.should_not be_empty
|
9
|
+
conf.utsname.should_not be_nil
|
10
|
+
conf['utsname'].should_not be_nil
|
11
|
+
conf[:utsname].should_not be_nil
|
12
|
+
conf.network_ipv4.should_not be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'saves config data into file' do
|
16
|
+
conf = LXC::Configuration.new(fixture('configuration.txt'))
|
17
|
+
conf.save_to_file('/tmp/lxc.txt')
|
18
|
+
c1 = LXC::Configuration.new(fixture('configuration.txt'))
|
19
|
+
c2 = LXC::Configuration.new(File.read('/tmp/lxc.txt'))
|
20
|
+
c1.content.should eq(c2.content)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LXC::Container do
|
4
|
+
subject { LXC::Container.new('app') }
|
5
|
+
|
6
|
+
it { should respond_to(:name) }
|
7
|
+
it { should respond_to(:state) }
|
8
|
+
it { should respond_to(:pid) }
|
9
|
+
|
10
|
+
it 'has proper default attributes' do
|
11
|
+
subject.name.should eq('app')
|
12
|
+
subject.state.should be_nil
|
13
|
+
subject.pid.should be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should exist' do
|
17
|
+
stub_lxc('ls') { "app\napp2" }
|
18
|
+
subject.exists?.should be_true
|
19
|
+
|
20
|
+
stub_lxc('ls') { "app2\napp3" }
|
21
|
+
subject.exists?.should be_false
|
22
|
+
end
|
23
|
+
|
24
|
+
context '.status' do
|
25
|
+
it 'returns STOPPED' do
|
26
|
+
stub_lxc('info', '-n', 'app') { fixture('lxc-info-stopped.txt') }
|
27
|
+
subject.status.should eq({:state => 'STOPPED', :pid => '-1'})
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns RUNNING' do
|
31
|
+
stub_lxc('info', '-n', 'app') { fixture('lxc-info-running.txt') }
|
32
|
+
subject.status.should eq({:state => 'RUNNING', :pid => '2125'})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '.destroy' do
|
37
|
+
it 'raises error if container does not exist' do
|
38
|
+
stub_lxc('ls') { "app2" }
|
39
|
+
proc { subject.destroy }.
|
40
|
+
should raise_error LXC::ContainerError, "Container does not exist."
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises error if container is running' do
|
44
|
+
stub_lxc('ls') { "app" }
|
45
|
+
stub_lxc('info', '-n', 'app') { fixture('lxc-info-running.txt') }
|
46
|
+
proc { subject.destroy }.
|
47
|
+
should raise_error LXC::ContainerError, "Container is running. Stop it first or use force=true"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns the amount of used memory' do
|
52
|
+
stub_lxc('cgroup', '-n', 'app', 'memory.usage_in_bytes') { "3280896\n" }
|
53
|
+
subject.memory_usage.should eq(3280896)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns the memory limit' do
|
57
|
+
stub_lxc('cgroup', '-n', 'app', 'memory.limit_in_bytes') { "268435456\n" }
|
58
|
+
subject.memory_limit.should eq(268435456)
|
59
|
+
end
|
60
|
+
|
61
|
+
context '.processes' do
|
62
|
+
it 'raises error if container is not running' do
|
63
|
+
stub_lxc('info', '-n', 'app') { fixture('lxc-info-stopped.txt') }
|
64
|
+
|
65
|
+
proc { subject.processes }.
|
66
|
+
should raise_error LXC::ContainerError, "Container is not running"
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns list of all processes' do
|
70
|
+
stub_lxc('info', '-n', 'app') { fixture('lxc-info-running.txt') }
|
71
|
+
stub_lxc('ps', '-n', 'app', '--', '-eo pid,user,%cpu,%mem,args') { fixture('lxc-ps-aux.txt') }
|
72
|
+
|
73
|
+
list = subject.processes
|
74
|
+
list.should be_an Array
|
75
|
+
|
76
|
+
p = list.first
|
77
|
+
p.should be_a Hash
|
78
|
+
p.should have_key('pid')
|
79
|
+
p.should have_key('user')
|
80
|
+
p.should have_key('cpu')
|
81
|
+
p.should have_key('memory')
|
82
|
+
p.should have_key('command')
|
83
|
+
p.should have_key('args')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
lxc.utsname = NAME
|
2
|
+
|
3
|
+
lxc.tty = 4
|
4
|
+
lxc.pts = 1024
|
5
|
+
lxc.rootfs = ROOTFS
|
6
|
+
lxc.mount = ROOTFS/fstab
|
7
|
+
|
8
|
+
lxc.network.type = veth
|
9
|
+
lxc.network.flags = up
|
10
|
+
lxc.network.name = eth0
|
11
|
+
lxc.network.link = br0
|
12
|
+
lxc.network.ipv4 = 0.0.0.0
|
13
|
+
|
14
|
+
lxc.cgroup.devices.deny = a
|
15
|
+
|
16
|
+
# /dev/null and zero
|
17
|
+
lxc.cgroup.devices.allow = c 1:3 rwm
|
18
|
+
lxc.cgroup.devices.allow = c 1:5 rwm
|
19
|
+
|
20
|
+
# consoles
|
21
|
+
lxc.cgroup.devices.allow = c 5:1 rwm
|
22
|
+
lxc.cgroup.devices.allow = c 5:0 rwm
|
23
|
+
lxc.cgroup.devices.allow = c 4:0 rwm
|
24
|
+
lxc.cgroup.devices.allow = c 4:1 rwm
|
25
|
+
|
26
|
+
# /dev/{,u}random
|
27
|
+
lxc.cgroup.devices.allow = c 1:9 rwm
|
28
|
+
lxc.cgroup.devices.allow = c 1:8 rwm
|
29
|
+
lxc.cgroup.devices.allow = c 136:* rwm
|
30
|
+
lxc.cgroup.devices.allow = c 5:2 rwm
|
31
|
+
|
32
|
+
# rtc
|
33
|
+
lxc.cgroup.devices.allow = c 254:0 rwm
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Kernel config /proc/config.gz not found, looking in other places...
|
2
|
+
Found kernel config file /boot/config-3.0.0-12-server
|
3
|
+
--- Namespaces ---
|
4
|
+
Namespaces: enabled
|
5
|
+
Utsname namespace: enabled
|
6
|
+
Ipc namespace: enabled
|
7
|
+
Pid namespace: enabled
|
8
|
+
User namespace: enabled
|
9
|
+
Network namespace: enabled
|
10
|
+
Multiple /dev/pts instances: enabled
|
11
|
+
|
12
|
+
--- Control groups ---
|
13
|
+
Cgroup: enabled
|
14
|
+
Cgroup clone_children flag: enabled
|
15
|
+
Cgroup device: enabled
|
16
|
+
Cgroup sched: enabled
|
17
|
+
Cgroup cpu account: enabled
|
18
|
+
Cgroup memory controller: enabled
|
19
|
+
Cgroup cpuset: enabled
|
20
|
+
|
21
|
+
--- Misc ---
|
22
|
+
Veth pair device: enabled
|
23
|
+
Macvlan: enabled
|
24
|
+
Vlan: enabled
|
25
|
+
File capabilities: enabled
|
26
|
+
|
27
|
+
Note : Before booting a new kernel, you can check its configuration
|
28
|
+
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig
|
29
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
CONTAINER PID USER %CPU %MEM COMMAND
|
2
|
+
app 27404 root 0.0 0.1 /sbin/init
|
3
|
+
app 27476 root 0.0 0.0 upstart-udev-bridge --daemon
|
4
|
+
app 27479 root 0.0 0.1 udevd --daemon
|
5
|
+
app 27590 root 0.0 0.0 udevd --daemon
|
6
|
+
app 27591 root 0.0 0.0 udevd --daemon
|
7
|
+
app 27692 root 0.0 0.0 upstart-socket-bridge --daemon
|
8
|
+
app 27704 root 0.0 0.2 /usr/sbin/sshd -D
|
9
|
+
app 27784 root 0.0 0.0 /sbin/getty -8 38400 console
|
@@ -0,0 +1 @@
|
|
1
|
+
lxc version: 0.7.5
|
data/spec/lxc_spec.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe LXC do
|
5
|
+
describe '.check_binaries' do
|
6
|
+
LXC::Shell::BIN_PREFIX = '/tmp/lxc'
|
7
|
+
|
8
|
+
before do
|
9
|
+
FileUtils.mkdir_p('/tmp/lxc')
|
10
|
+
LXC::Shell::BIN_FILES.each do |f|
|
11
|
+
FileUtils.touch("/tmp/lxc/#{f}")
|
12
|
+
FileUtils.chmod(0555, "/tmp/lxc/#{f}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
FileUtils.rm_rf('/tmp/lxc')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns true if all files are found' do
|
21
|
+
LXC.installed?.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns false on missing files' do
|
25
|
+
FileUtils.rm("/tmp/lxc/lxc-version")
|
26
|
+
LXC.installed?.should be_false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns installed version' do
|
31
|
+
stub_lxc('version') { fixture('lxc-version.txt') }
|
32
|
+
LXC.version.should eq('0.7.5')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns config hash with attributes' do
|
36
|
+
stub_lxc('checkconfig') { fixture('lxc-checkconfig.txt') }
|
37
|
+
|
38
|
+
info = LXC.config
|
39
|
+
info.should be_a Hash
|
40
|
+
|
41
|
+
info['namespaces'].should be_true
|
42
|
+
info['utsname_namespace'].should be_true
|
43
|
+
info['ipc_namespace'].should be_true
|
44
|
+
info['pid_namespace'].should be_true
|
45
|
+
info['user_namespace'].should be_true
|
46
|
+
info['network_namespace'].should be_true
|
47
|
+
info['cgroup'].should be_true
|
48
|
+
info['cgroup_clone_children_flag'].should be_true
|
49
|
+
info['cgroup_device'].should be_true
|
50
|
+
info['cgroup_sched'].should be_true
|
51
|
+
info['cgroup_cpu_account'].should be_true
|
52
|
+
info['cgroup_memory_controller'].should be_true
|
53
|
+
info['cgroup_cpuset'].should be_true
|
54
|
+
info['veth_pair_device'].should be_true
|
55
|
+
info['macvlan'].should be_true
|
56
|
+
info['vlan'].should be_true
|
57
|
+
info['file_capabilities'].should be_true
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns a single container' do
|
61
|
+
c = LXC.container('foo')
|
62
|
+
c.should be_a LXC::Container
|
63
|
+
c.name.should eq('foo')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns all available containers' do
|
67
|
+
stub_lxc('ls') { "vm0\nvm1\nvm0" }
|
68
|
+
list = LXC.containers
|
69
|
+
list.should be_an Array
|
70
|
+
list.size.should eq(2)
|
71
|
+
list.first.should be_a LXC::Container
|
72
|
+
list.first.name.should eq('vm0')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns filtered list of containers' do
|
76
|
+
stub_lxc('ls') { "vm0\nvm1\nfoo\n"}
|
77
|
+
list = LXC.containers("vm")
|
78
|
+
list.size.should eq(2)
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'sudo' do
|
82
|
+
class Foo
|
83
|
+
include LXC::Shell
|
84
|
+
end
|
85
|
+
|
86
|
+
before do
|
87
|
+
LXC.use_sudo = true
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'executes command using sudo' do
|
91
|
+
LXC.use_sudo.should be_true
|
92
|
+
|
93
|
+
bar = Foo.new
|
94
|
+
bar.should_receive(:'`').with('sudo lxc-version').and_return(fixture('lxc-version.txt'))
|
95
|
+
bar.run('version').should_not be_empty
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context '.use_sudo' do
|
100
|
+
class Bar
|
101
|
+
include LXC::Shell
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should be true' do
|
105
|
+
foo = Bar.new
|
106
|
+
foo.use_sudo.should eq(true)
|
107
|
+
LXC.use_sudo.should eq(true)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should be false' do
|
111
|
+
LXC.use_sudo = false
|
112
|
+
foo = Bar.new
|
113
|
+
|
114
|
+
LXC.use_sudo.should eq(false)
|
115
|
+
foo.use_sudo.should eq(false)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift File.expand_path("../..", __FILE__)
|
2
|
+
|
3
|
+
require 'lib/lxc'
|
4
|
+
|
5
|
+
def fixture_path(filename=nil)
|
6
|
+
path = File.expand_path("../fixtures", __FILE__)
|
7
|
+
filename.nil? ? path : File.join(path, filename)
|
8
|
+
end
|
9
|
+
|
10
|
+
def fixture(file)
|
11
|
+
File.read(File.join(fixture_path, file))
|
12
|
+
end
|
13
|
+
|
14
|
+
def stub_lxc(command, *args)
|
15
|
+
output = yield
|
16
|
+
LXC.should_receive(:run).with(command, *args).and_return(output)
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lxc-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Sosedoff
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-28 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &2152032520 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2152032520
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2152032020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.6'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2152032020
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &2152031520 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.4'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2152031520
|
47
|
+
description: Ruby wrapper to manage LXC (Linux Containers).
|
48
|
+
email:
|
49
|
+
- dan.sosedoff@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- .rspec
|
56
|
+
- .travis.yml
|
57
|
+
- Gemfile
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- lib/lxc.rb
|
61
|
+
- lib/lxc/configuration.rb
|
62
|
+
- lib/lxc/configuration_options.rb
|
63
|
+
- lib/lxc/container.rb
|
64
|
+
- lib/lxc/errors.rb
|
65
|
+
- lib/lxc/shell.rb
|
66
|
+
- lib/lxc/version.rb
|
67
|
+
- lxc.gemspec
|
68
|
+
- spec/configuration_spec.rb
|
69
|
+
- spec/container_spec.rb
|
70
|
+
- spec/fixtures/configuration.txt
|
71
|
+
- spec/fixtures/lxc-checkconfig.txt
|
72
|
+
- spec/fixtures/lxc-info-running.txt
|
73
|
+
- spec/fixtures/lxc-info-stopped.txt
|
74
|
+
- spec/fixtures/lxc-ps-aux.txt
|
75
|
+
- spec/fixtures/lxc-version.txt
|
76
|
+
- spec/lxc_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
homepage: http://github.com/sosedoff/lxc
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.8.15
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Ruby wrapper to LXC
|
102
|
+
test_files:
|
103
|
+
- spec/configuration_spec.rb
|
104
|
+
- spec/container_spec.rb
|
105
|
+
- spec/fixtures/configuration.txt
|
106
|
+
- spec/fixtures/lxc-checkconfig.txt
|
107
|
+
- spec/fixtures/lxc-info-running.txt
|
108
|
+
- spec/fixtures/lxc-info-stopped.txt
|
109
|
+
- spec/fixtures/lxc-ps-aux.txt
|
110
|
+
- spec/fixtures/lxc-version.txt
|
111
|
+
- spec/lxc_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
has_rdoc:
|