mbailey-ruby-xen 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +6 -4
- data/README.rdoc +1 -3
- data/lib/ruby-xen.rb +92 -18
- data/lib/templates/domu.cfg.erb +1 -1
- data/lib/templates/xen-tools.conf.erb +289 -0
- data/lib/xen/backup.rb +71 -2
- data/lib/xen/command.rb +94 -23
- data/lib/xen/config_file.rb +160 -0
- data/lib/xen/host.rb +23 -5
- data/lib/xen/instance.rb +53 -52
- data/lib/xen/lvm.rb +33 -0
- data/lib/xen/slice.rb +117 -43
- data/lib/xen/xen_tools_conf.rb +56 -0
- data/test/test_ruby-xen.rb +2 -2
- metadata +27 -8
- data/lib/xen/config.rb +0 -139
- data/lib/xen/image.rb +0 -12
data/lib/xen/lvm.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'open4'
|
2
|
+
|
3
|
+
module Xen
|
4
|
+
|
5
|
+
class VolumeGroup
|
6
|
+
|
7
|
+
attr_reader :name, :size
|
8
|
+
|
9
|
+
def initialize(name, size)
|
10
|
+
@name = name
|
11
|
+
@size = size
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find(name=nil)
|
15
|
+
name ||= nil
|
16
|
+
# XXX deal with not found error
|
17
|
+
cmd = "vgs --options=vg_name,vg_size --separator=' ' --noheadings --units=g --nosuffix #{name}"
|
18
|
+
output = Xen::Command.run cmd
|
19
|
+
result = output.collect { |line|
|
20
|
+
name, size = line.strip.split(' ')
|
21
|
+
new name, size
|
22
|
+
}
|
23
|
+
name ? result[0] : result
|
24
|
+
end
|
25
|
+
|
26
|
+
def free
|
27
|
+
cmd = "vgs --options=vg_free --separator=' ' --noheadings --units=g --nosuffix #{@name}"
|
28
|
+
Xen::Command.run cmd
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/xen/slice.rb
CHANGED
@@ -1,55 +1,129 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Xen
|
2
|
+
class Slice
|
3
|
+
attr_accessor :name, :image, :config_file, :backups
|
4
|
+
|
5
|
+
def self.find(*args)
|
6
|
+
options = args.extract_options!
|
7
|
+
case args.first
|
8
|
+
when :all then Xen::ConfigFile.find(:all, options).collect { |config_file| new :name => config_file.name }
|
9
|
+
when :running then Xen::Instance.find(:all, options).collect { |instance| new :name => instance.name }
|
10
|
+
# Retrieve a Slice by name
|
11
|
+
else Xen::ConfigFile.find_by_name(args.first) && new(:name => args.first)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all(options={})
|
16
|
+
self.find(:all, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
options = args.extract_options!
|
21
|
+
@name = options[:name]
|
22
|
+
@config_file = options[:config_file]
|
23
|
+
@instance = options[:instance]
|
24
|
+
@instance_cache_expires = Time.now
|
25
|
+
@backups = Array(options[:backups])
|
26
|
+
end
|
3
27
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
28
|
+
def create_image(*args)
|
29
|
+
options = args.extract_options!.stringify_keys
|
30
|
+
|
31
|
+
# Load default values for options that have not been set
|
32
|
+
options.reverse_merge! Xen::XenToolsConf.find.to_hash
|
33
|
+
|
34
|
+
# Set some derived options
|
35
|
+
options.reverse_merge! 'hostname' => name # Name host after this slice
|
36
|
+
options['dhcp'] = true unless options['ip']
|
37
|
+
options['swap'] ||= options['memory'].to_i * 2
|
38
|
+
if options['root_pass']
|
39
|
+
options['role'] = 'passwd'
|
40
|
+
options['role-args'] = options['root_pass']
|
41
|
+
end
|
42
|
+
unless [nil,''].include?(options['tarball'])
|
43
|
+
options['install-method'] = 'tar'
|
44
|
+
options['install-source'] = options['tarball']
|
45
|
+
options.delete('dist')
|
46
|
+
end
|
47
|
+
|
48
|
+
args = %w(hostname dist memory size
|
49
|
+
force boot
|
50
|
+
role role-args roledir
|
51
|
+
dir lvm mirror
|
52
|
+
ip mac netmask broadcast gateway dhcp
|
53
|
+
swap
|
54
|
+
accounts admins cache config fs image image-dev initrd
|
55
|
+
keep kernel modules output install hooks partitions
|
56
|
+
passwd tar-cmd extension swap-dev noswap ide arch
|
57
|
+
install-method install-source template evms)
|
58
|
+
# Remove options that are not in allowed argument list
|
59
|
+
options.keys.each { |key| options.delete(key) unless args.include?(key) }
|
60
|
+
|
61
|
+
Xen::Command.create_image(options)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Cache Xen instance info to reduce system calls to xm command.
|
65
|
+
# It still needs to be checked regularly as operations like shutdown
|
66
|
+
# and create can take a while.
|
67
|
+
def instance
|
68
|
+
if @instance_cache_expires > Time.now
|
69
|
+
@instance
|
70
|
+
else
|
71
|
+
@instance_cache_expires = Time.now + Xen::INSTANCE_OBJECT_LIFETIME
|
72
|
+
@instance = Xen::Instance.find(@name) if @name
|
73
|
+
end
|
74
|
+
end
|
10
75
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@instance = Xen::Instance.find(@name)
|
76
|
+
# XXX We're assuming other processes aren't going to edit config_files
|
77
|
+
# This is reasonable in simple cases.
|
78
|
+
def config_file
|
79
|
+
@config_file ||= Xen::ConfigFile.find(name) if @name
|
16
80
|
end
|
17
|
-
end
|
18
81
|
|
19
|
-
|
20
|
-
|
21
|
-
case args.first
|
22
|
-
when :all then Xen::Config.find(:all, options).collect { |config| config.slice }
|
23
|
-
when :running then Xen::Instance.find(:all, options).collect { |instance| instance.slice }
|
24
|
-
# Retrieve a Slice by name
|
25
|
-
else Xen::Config.find_by_name(args.first) && self.new(args.first)
|
82
|
+
def backups
|
83
|
+
Xen::Backup.find(name)
|
26
84
|
end
|
27
|
-
end
|
28
85
|
|
29
|
-
|
30
|
-
|
31
|
-
|
86
|
+
def create_backup(options = {})
|
87
|
+
Xen::Backup.create(name, :options)
|
88
|
+
end
|
32
89
|
|
33
|
-
|
34
|
-
|
35
|
-
|
90
|
+
def state
|
91
|
+
self.instance ? :running : :stopped
|
92
|
+
end
|
36
93
|
|
37
|
-
|
38
|
-
|
39
|
-
|
94
|
+
def running?
|
95
|
+
self.instance ? true : false
|
96
|
+
end
|
40
97
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
98
|
+
def start
|
99
|
+
Xen::Instance.create(@name)
|
100
|
+
@instance = Xen::Instance.find(@name)
|
101
|
+
end
|
45
102
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
103
|
+
def stop
|
104
|
+
Xen::Instance.shutdown(@name)
|
105
|
+
@instance = Xen::Instance.find(@name)
|
106
|
+
end
|
50
107
|
|
51
|
-
|
52
|
-
|
53
|
-
|
108
|
+
def config_file_newer_than_instance?
|
109
|
+
instance && config_file.updated_at > instance.start_time
|
110
|
+
end
|
111
|
+
|
112
|
+
def save
|
113
|
+
@config_file.save
|
114
|
+
end
|
115
|
+
|
116
|
+
def root_disk
|
117
|
+
config_file.vbds.detect { |vbd| vbd.name == "#{name}-disk" } unless config_file.nil?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Primary IP
|
121
|
+
def ip
|
122
|
+
config_file.vifs[0].ip
|
123
|
+
end
|
54
124
|
|
55
|
-
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Xen
|
2
|
+
# puts Xen::XenToolsConf.load.to_hash.inspect
|
3
|
+
|
4
|
+
class XenToolsConf
|
5
|
+
|
6
|
+
# XXX underscorize :install-method, install-source, copy_cmd, tar-cmd
|
7
|
+
attr_accessor :dir, :lvm, :install__method, :install__source, :copy_cmd,
|
8
|
+
:tar_cmd, :debootstrap__cmd, :size, :memory, :swap, :noswap,
|
9
|
+
:fs,
|
10
|
+
:dist, :image, :gateway,
|
11
|
+
:netmask, :broadcast, :dhcp, :cache, :passwd, :accounts,
|
12
|
+
:kernel, :initrd, :mirror, :ext3_options, :ext2_options,
|
13
|
+
:xfs_options, :reiser_options, :boot, :serial_device,
|
14
|
+
:disk_device, :output, :extension
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find(file=nil)
|
20
|
+
file ||= Xen::XEN_TOOLS_CONFIG_FILE
|
21
|
+
xtc = new # Create a new XenToolsConf object
|
22
|
+
xtc.load_from_config_file(File.readlines(file))
|
23
|
+
xtc
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_from_config_file(file_contents)
|
27
|
+
file_contents.reject! { |line| line.match /^\s*#/ } # Ignore commented out lines
|
28
|
+
file_contents.grep(/(.*) = (.*)/).each { |setting|
|
29
|
+
setting.scan(/\s*(.+?)\s*=\s*([^#]+)/).each { |match|
|
30
|
+
key, val = match
|
31
|
+
instance_variable_set("@#{key.strip.underscorize}", val.strip)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash
|
37
|
+
self.instance_variables.inject({}) { |m, variable_name|
|
38
|
+
m[variable_name.sub('@','').ununderscorize] = instance_variable_get(variable_name); m
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_file
|
43
|
+
template = ERB.new(IO.read(File.join(TEMPLATES_BASE, 'xen-tools.conf.erb')))
|
44
|
+
template.result(binding)
|
45
|
+
end
|
46
|
+
|
47
|
+
def save(filename=nil)
|
48
|
+
filename ||= Xen::XEN_TOOLS_CONFIG_FILE
|
49
|
+
File.open(filename, 'w') do |f|
|
50
|
+
f.write(to_file)
|
51
|
+
end
|
52
|
+
# XXX check for errors
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/test/test_ruby-xen.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
require '../lib/ruby-xen'
|
2
|
-
c = Xen::
|
3
|
-
c.save
|
2
|
+
c = Xen::ConfigFile.new
|
3
|
+
c.save
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mbailey-ruby-xen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Bailey
|
@@ -10,10 +10,27 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2008-09-
|
13
|
+
date: 2008-09-21 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
|
-
dependencies:
|
16
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activesupport
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: open4
|
27
|
+
version_requirement:
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: "0"
|
33
|
+
version:
|
17
34
|
description: ruby-xen allows you to manage Xen virtual servers via Ruby. It currently wraps the command line tools provided by Xen (xm) as well as Steve Kemps excellent Xen-tools (http://www.xen-tools.org/software/xen-tools/).
|
18
35
|
email: mike@bailey.net.au
|
19
36
|
executables: []
|
@@ -34,14 +51,16 @@ files:
|
|
34
51
|
- test/test_ruby-xen.rb
|
35
52
|
- lib/xen/backup.rb
|
36
53
|
- lib/xen/command.rb
|
37
|
-
- lib/xen/
|
38
|
-
- lib/xen/slice.rb
|
54
|
+
- lib/xen/config_file.rb
|
39
55
|
- lib/xen/host.rb
|
40
|
-
- lib/xen/image.rb
|
41
56
|
- lib/xen/instance.rb
|
57
|
+
- lib/xen/lvm.rb
|
58
|
+
- lib/xen/slice.rb
|
59
|
+
- lib/xen/xen_tools_conf.rb
|
42
60
|
- lib/templates/domu.cfg.erb
|
61
|
+
- lib/templates/xen-tools.conf.erb
|
43
62
|
has_rdoc: true
|
44
|
-
homepage: http://github.com/
|
63
|
+
homepage: http://github.com/mbailey/ruby-xen
|
45
64
|
post_install_message:
|
46
65
|
rdoc_options:
|
47
66
|
- --main
|
data/lib/xen/config.rb
DELETED
@@ -1,139 +0,0 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
# The Xen config files on disk
|
4
|
-
class Xen::Config
|
5
|
-
include Xen::Parentable
|
6
|
-
attr_accessor :name, :kernel, :ramdisk, :memory, :root, :vbds, :vifs, :on_poweroff, :on_reboot, :on_crash, :extra
|
7
|
-
|
8
|
-
def initialize(*args)
|
9
|
-
options = args.extract_options!
|
10
|
-
@name = args.first
|
11
|
-
@kernel = options[:kernel]
|
12
|
-
@ramdisk = options[:ramdisk]
|
13
|
-
@memory = options[:memory]
|
14
|
-
@root = options[:root]
|
15
|
-
@vbds = options[:vbds]
|
16
|
-
@vifs = options[:vifs]
|
17
|
-
@on_poweroff = options[:on_poweroff]
|
18
|
-
@on_reboot = options[:on_reboot]
|
19
|
-
@on_crash = options[:on_crash]
|
20
|
-
@extra = options[:extra]
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.find(*args)
|
24
|
-
options = args.extract_options!
|
25
|
-
case args.first
|
26
|
-
when :all then all
|
27
|
-
else find_by_name(args.first)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.all
|
32
|
-
config_files = Dir.glob("#{Xen::XEN_DOMU_CONFIG_DIR}/*#{Xen::CONFIG_FILE_EXTENSION}")
|
33
|
-
config_files.collect do |filename|
|
34
|
-
create_from_config_file(File.read(filename))
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.find_by_name(name)
|
39
|
-
return new('Domain-0') if name == 'Domain-0'
|
40
|
-
filename = "#{Xen::XEN_DOMU_CONFIG_DIR}/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
|
41
|
-
create_from_config_file(File.read(filename))
|
42
|
-
end
|
43
|
-
|
44
|
-
def config_file
|
45
|
-
"#{Xen::XEN_DOMU_CONFIG_DIR}/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
|
46
|
-
end
|
47
|
-
|
48
|
-
def auto_file
|
49
|
-
"#{Xen::XEN_DOMU_CONFIG_DIR}/auto/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
|
50
|
-
end
|
51
|
-
|
52
|
-
def updated_at
|
53
|
-
File.mtime(config_file)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Set to true|false to enable|disable autostart of slice
|
57
|
-
def auto=(value)
|
58
|
-
filename = File.basename(config_file)
|
59
|
-
if value == true
|
60
|
-
File.symlink("../#{filename}", auto_file)
|
61
|
-
else
|
62
|
-
File.unlink(auto_file)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Returns true|false depending on whether slice is set to start automatically
|
67
|
-
def auto
|
68
|
-
File.symlink?(auto_file) && File.expand_path(File.readlink(auto_file), File.dirname(auto_file)) == config_file
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.create_from_config_file(config)
|
72
|
-
name, kernel, ramdisk, memory, root, disk, vif, on_poweroff, on_reboot, on_crash, extra = nil
|
73
|
-
eval(config)
|
74
|
-
vifs = Array(vif).collect { |v| Xen::Vif.from_str(v) }
|
75
|
-
vbds = Array(disk).collect { |d| Xen::Vbd.from_str(d) }
|
76
|
-
new(name, :disk => disk, :kernel => kernel, :ramdisk => ramdisk, :memory => memory, :root => root, :vbds => vbds, :vifs => vifs, :on_poweroff => on_poweroff, :on_reboot => on_reboot, :on_crash => on_crash, :extra => extra)
|
77
|
-
end
|
78
|
-
|
79
|
-
def save
|
80
|
-
template = ERB.new IO.read(Xen::TEMPLATE_DIR + '/domu.cfg.erb')
|
81
|
-
File.open(config_file, 'w'){ |f| f.write template.result(binding) }
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
# Virtual Network Interface
|
88
|
-
#
|
89
|
-
# http://wiki.xensource.com/xenwiki/XenNetworking
|
90
|
-
#
|
91
|
-
class Xen::Vif
|
92
|
-
attr_accessor :ip, :mac, :bridge, :vifname
|
93
|
-
def initialize(*args)
|
94
|
-
options = args.extract_options!
|
95
|
-
@ip = options[:ip]
|
96
|
-
@mac = options[:mac]
|
97
|
-
@bridge = options[:bridge]
|
98
|
-
@vifname = options[:vifname]
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.from_str(value)
|
102
|
-
options = value.scan(/(\w+)=([^,]+)/).inject({}){ |m, (k, v)| m[k.to_sym] = v; m }
|
103
|
-
new(options)
|
104
|
-
end
|
105
|
-
|
106
|
-
def to_str
|
107
|
-
%w(ip mac bridge vifname).collect { |key|
|
108
|
-
"#{key}=#{instance_variable_get('@' + key)}" if !instance_variable_get('@'+key).nil?
|
109
|
-
}.compact.join(',')
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
# Virtual Network Interface
|
115
|
-
#
|
116
|
-
# http://wiki.xensource.com/xenwiki/XenStorage
|
117
|
-
#
|
118
|
-
# == Example
|
119
|
-
#
|
120
|
-
# disk = [ 'phy:xendisks/example-disk,sda1,w',
|
121
|
-
# 'phy:xendisks/example-swap,sda2,w',
|
122
|
-
# 'phy:assets/example-assets,sdb1,w' ]
|
123
|
-
class Xen::Vbd
|
124
|
-
attr_accessor :dom0, :domu, :mode
|
125
|
-
def initialize(dom0, domu, mode='w')
|
126
|
-
@dom0, @domu, @mode = dom0, domu, mode
|
127
|
-
end
|
128
|
-
|
129
|
-
def self.from_str(value)
|
130
|
-
dom0, domu, mode = value.split(',')
|
131
|
-
new(dom0, domu, mode)
|
132
|
-
end
|
133
|
-
|
134
|
-
def to_str
|
135
|
-
"#{dom0},#{domu},#{mode}"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
|