mbailey-ruby-xen 0.0.2 → 0.0.3
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.
- 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
|
-
|