ruby-xen 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/Manifest.txt +9 -1
- data/README.rdoc +16 -4
- data/Rakefile +8 -8
- data/lib/ruby-xen.rb +79 -3
- data/lib/templates/domu.cfg.erb +40 -0
- data/lib/xen/backup.rb +42 -0
- data/lib/xen/command.rb +73 -0
- data/lib/xen/config.rb +146 -0
- data/lib/xen/host.rb +10 -0
- data/lib/xen/image.rb +12 -0
- data/lib/xen/instance.rb +66 -0
- data/lib/xen/slice.rb +80 -0
- data/test/test_ruby-xen.rb +3 -0
- metadata +12 -5
- data/lib/ruby-xen/domain.rb +0 -229
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
=== 0.0.3 / 2008-09-21
|
2
|
+
|
3
|
+
* fleshed out :backup class
|
4
|
+
* Xen::Commands are now easily called by backgroundjob (bj)
|
5
|
+
|
6
|
+
=== 0.0.2 / 2008-09-11
|
7
|
+
|
8
|
+
* Broke out classes into separate files
|
9
|
+
* Added rspec outline
|
10
|
+
|
1
11
|
=== 0.0.1 / 2008-09-08
|
2
12
|
|
3
13
|
* Initial import
|
data/Manifest.txt
CHANGED
@@ -4,5 +4,13 @@ README.rdoc
|
|
4
4
|
Rakefile
|
5
5
|
bin/ruby-xen
|
6
6
|
lib/ruby-xen.rb
|
7
|
-
test/
|
7
|
+
test/test_ruby-xen.rb
|
8
8
|
lib/ruby-xen/domain.rb
|
9
|
+
lib/xen/backup.rb
|
10
|
+
lib/xen/command.rb
|
11
|
+
lib/xen/config.rb
|
12
|
+
lib/xen/slice.rb
|
13
|
+
lib/xen/host.rb
|
14
|
+
lib/xen/image.rb
|
15
|
+
lib/xen/instance.rb
|
16
|
+
lib/templates/domu.cfg.erb
|
data/README.rdoc
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
= ruby-xen
|
2
2
|
|
3
|
-
|
3
|
+
# Warning! Not ready yet - project started Sep 2008
|
4
|
+
|
5
|
+
http://github.com/mbailey/ruby-xen
|
6
|
+
|
4
7
|
|
5
8
|
== DESCRIPTION:
|
6
9
|
|
@@ -19,11 +22,20 @@ ruby-xen can also be used by ruby code or from irb.
|
|
19
22
|
|
20
23
|
== SYNOPSIS:
|
21
24
|
|
22
|
-
|
25
|
+
require 'rubygems'
|
26
|
+
require 'ruby-xen'
|
27
|
+
|
28
|
+
slice = Xen::Slice.find(:example)
|
29
|
+
slice.running? # true
|
30
|
+
slice.stop
|
31
|
+
slice.running? # false
|
32
|
+
slice.start
|
33
|
+
slice.running? # true
|
23
34
|
|
24
35
|
== REQUIREMENTS:
|
25
36
|
|
26
|
-
xen
|
37
|
+
ruby-xen must be run as root as it uses Xen's 'xm' command.
|
38
|
+
xen-tools must be installed (http://www.xen-tools.org/software/xen-tools/)
|
27
39
|
|
28
40
|
== INSTALL:
|
29
41
|
|
@@ -36,7 +48,7 @@ or open source applications. More details found here:
|
|
36
48
|
http://www.gnu.org/licenses/gpl.html
|
37
49
|
|
38
50
|
ruby-xen
|
39
|
-
Copyright (C) 2008 Mike Bailey and Nick
|
51
|
+
Copyright (C) 2008 Mike Bailey and Nick Marfleet
|
40
52
|
|
41
53
|
This program is free software; you can redistribute it and/or
|
42
54
|
modify it under the terms of the GNU General Public License
|
data/Rakefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
|
-
require 'hoe'
|
5
|
-
require './lib/
|
6
|
-
|
7
|
-
Hoe.new('ruby-xen',
|
8
|
-
|
9
|
-
|
10
|
-
end
|
3
|
+
# require 'rubygems'
|
4
|
+
# require 'hoe'
|
5
|
+
# require './lib/ruby-xen.rb'
|
6
|
+
#
|
7
|
+
# Hoe.new('ruby-xen', Xen::VERSION) do |p|
|
8
|
+
# # p.rubyforge_name = 'ruby-xenx' # if different than lowercase project name
|
9
|
+
# p.developer('Mike Bailey', 'mike@bailey.net.au')
|
10
|
+
# end
|
11
11
|
|
12
12
|
# vim: syntax=Ruby
|
data/lib/ruby-xen.rb
CHANGED
@@ -1,4 +1,80 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Xen
|
2
|
+
# General configuration for ruby-xen
|
3
|
+
|
4
|
+
# Location of Xen config files
|
5
|
+
XEN_DOMU_CONFIG_DIR = '/etc/xen'
|
6
|
+
# XEN_DOMU_CONFIG_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '/spec/fixtures/xen_domu_configs'))
|
7
|
+
|
8
|
+
# We don't want out library to hit Xen too often (premature optimization perhaps?)
|
9
|
+
# so we keep information about Xen instances in an object. Specify how long before
|
10
|
+
# the object expires.
|
11
|
+
INSTANCE_OBJECT_LIFETIME = 5 # seconds
|
12
|
+
|
13
|
+
# General location for config file templates
|
14
|
+
TEMPLATE_DIR = File.expand_path(File.dirname(__FILE__) + '/../lib/templates')
|
15
|
+
|
16
|
+
# Extension for Xen domU config files
|
17
|
+
CONFIG_FILE_EXTENSION = '.cfg'
|
18
|
+
|
19
|
+
# Directory for backups of system images
|
20
|
+
BACKUP_DIR='/var/xen_images'
|
21
|
+
|
22
|
+
# FIle extension for backups
|
23
|
+
BACKUP_FILE_EXT = '.tar'
|
3
24
|
end
|
4
|
-
|
25
|
+
|
26
|
+
class Array #:nodoc:
|
27
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
28
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
29
|
+
#
|
30
|
+
# def options(*args)
|
31
|
+
# args.extract_options!
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# options(1, 2) # => {}
|
35
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
36
|
+
def extract_options!
|
37
|
+
last.is_a?(::Hash) ? pop : {}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Hash #:nodoc:
|
42
|
+
# Converts a Hash into an array of key=val formatted strings
|
43
|
+
#
|
44
|
+
# puts { :nics => 2, :vcpus => 1, :memory => 64 }.to_args
|
45
|
+
#
|
46
|
+
# produces:
|
47
|
+
#
|
48
|
+
# ["memory=64", "nics=2", "vcpus=1"]
|
49
|
+
def to_args
|
50
|
+
collect{|k,v| "#{k}=#{v}"}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module Xen
|
55
|
+
# DRY up some classes (children of Slice) with some module funkiness.
|
56
|
+
module Parentable
|
57
|
+
# Returns the parent Slice object (d) for a sub-object.
|
58
|
+
# We ensure d.instance.object_id == self.object_id
|
59
|
+
#
|
60
|
+
# ==== Example
|
61
|
+
# i = Xen::Instance.all[2]
|
62
|
+
# s = i.slice
|
63
|
+
# i.object_id == s.instance.object_id # true
|
64
|
+
#
|
65
|
+
def slice
|
66
|
+
d = Xen::Slice.new(name)
|
67
|
+
# Insert the current object into the newly created Slice's attributes
|
68
|
+
d.instance_variable_set("@#{self.class.to_s.sub('Xen::','').downcase}", self)
|
69
|
+
d
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
require "#{File.dirname(__FILE__)}/xen/backup"
|
75
|
+
require "#{File.dirname(__FILE__)}/xen/command"
|
76
|
+
require "#{File.dirname(__FILE__)}/xen/config"
|
77
|
+
require "#{File.dirname(__FILE__)}/xen/slice"
|
78
|
+
require "#{File.dirname(__FILE__)}/xen/host"
|
79
|
+
require "#{File.dirname(__FILE__)}/xen/image"
|
80
|
+
require "#{File.dirname(__FILE__)}/xen/instance"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Configuration file for the Xen instance <%= name %>, created
|
3
|
+
# by ruby-xen on <%= Time.now %>.
|
4
|
+
#
|
5
|
+
|
6
|
+
#
|
7
|
+
# Kernel + memory size
|
8
|
+
#
|
9
|
+
kernel = '<%= kernel %>'
|
10
|
+
ramdisk = '<%= ramdisk %>'
|
11
|
+
memory = '<%= memory %>'
|
12
|
+
|
13
|
+
#
|
14
|
+
# Disk device(s).
|
15
|
+
#
|
16
|
+
root = '<%= root %>'
|
17
|
+
disk = [
|
18
|
+
'<%= Array(vbds).collect{|d| d.to_str}.join("',\n\t'") %>'
|
19
|
+
]
|
20
|
+
|
21
|
+
#
|
22
|
+
# Hostname
|
23
|
+
#
|
24
|
+
name = '<%= name %>'
|
25
|
+
|
26
|
+
#
|
27
|
+
# Networking
|
28
|
+
#
|
29
|
+
vif = [
|
30
|
+
'<%= Array(vifs).collect{|v| v.to_str}.join("',\n\t'") %>'
|
31
|
+
]
|
32
|
+
|
33
|
+
#
|
34
|
+
# Behaviour
|
35
|
+
#
|
36
|
+
on_poweroff = '<%= on_poweroff %>'
|
37
|
+
on_reboot = '<%= on_reboot %>'
|
38
|
+
on_crash = '<%= on_crash %>'
|
39
|
+
|
40
|
+
extra = '<%= extra %>'
|
data/lib/xen/backup.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Xen::Backup
|
2
|
+
include Xen::Parentable
|
3
|
+
|
4
|
+
attr_accessor :name, :version
|
5
|
+
|
6
|
+
def self.create(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
name = args.first
|
9
|
+
version = options[:version] || generate_version
|
10
|
+
Xen::Command.backup_slice(name, version)
|
11
|
+
new(name, version)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(*args)
|
15
|
+
options = args.extract_options!
|
16
|
+
@name = args.first
|
17
|
+
@version = options[:version]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.find(*args)
|
21
|
+
# return all
|
22
|
+
slice = args.first
|
23
|
+
Dir.glob("#{Xen::BACKUP_DIR}/*-*#{Xen::BACKUP_FILE_EXT}").collect { |file|
|
24
|
+
if match = File.basename(file, Xen::BACKUP_FILE_EXT).match(/(#{ slice || '.*' })-(.*)/)
|
25
|
+
new(match[1], :version => match[2])
|
26
|
+
end
|
27
|
+
}.compact
|
28
|
+
end
|
29
|
+
|
30
|
+
def filename
|
31
|
+
"#{@name}-#{@version}#{Xen::BACKUP_FILE_EXT}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def fullpath
|
35
|
+
File.join(Xen::BACKUP_DIR, filename)
|
36
|
+
end
|
37
|
+
|
38
|
+
def size
|
39
|
+
File.size(fullpath)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/xen/command.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
class Xen::Command
|
2
|
+
# def self.xm_list
|
3
|
+
# raw = `xm list`.scan(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/)
|
4
|
+
# headers = raw.delete_at(0)
|
5
|
+
# raw.map do |row|
|
6
|
+
# headers.enum_with_index.inject({}) { |m, (head, i)| m[head] = row[i]; m }
|
7
|
+
# end
|
8
|
+
# end
|
9
|
+
|
10
|
+
# Return the size of a logical volume in gigabytes
|
11
|
+
def self.lv_size(vg_name, lv_name)
|
12
|
+
cmd = "lvs --noheadings --nosuffix --options lv_size --units g #{vg_name}/#{lv_name}"
|
13
|
+
`#{cmd}`.strip
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return list of logical volumes
|
17
|
+
def self.lv_list(vg_name)
|
18
|
+
cmd = "lvs --noheadings --nosuffix --options vg_name,lv_name,lv_size --units g #{vg_name}"
|
19
|
+
raw = `#{cmd}`
|
20
|
+
raw.scan(/(\S+)\s+(\S+)\s+(\S+)/).collect{ |vg_name, lv_name, size|
|
21
|
+
{
|
22
|
+
:vg => vg_name,
|
23
|
+
:name => lv_name,
|
24
|
+
:size => size
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.vg_list
|
30
|
+
cmd = "vgs --noheadings --units g --nosuffix --options vg_name,vg_size,vg_free,lv_count,max_lv"
|
31
|
+
raw = `#{cmd}`
|
32
|
+
raw.scan(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/).collect{ |vg_name, vg_size, vg_free, lv_count, max_lv|
|
33
|
+
{
|
34
|
+
:name => vg_name,
|
35
|
+
:size => vg_size,
|
36
|
+
:free => vg_free,
|
37
|
+
:lv_count => lv_count,
|
38
|
+
:max_lv => max_lv
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.detailed_instance_list(name='')
|
44
|
+
cmd = "xm list --long #{name}"
|
45
|
+
raw_entries = `#{cmd}`.split(/\n\(domain/)
|
46
|
+
raw_entries.collect do |entry|
|
47
|
+
attributes = entry.scan(/\((name|domid|memory|vcpus|state|cpu_time|start_time) (.*)\)/)
|
48
|
+
attributes.inject({}) { |m, (key, val)| m[key.to_sym] = val; m }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.start_instance(config_file)
|
53
|
+
`xm create #{config_file}`
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.shutdown_instance(name, blocking=false)
|
57
|
+
`xm shutdown #{'-w' if blocking} #{name}`
|
58
|
+
end
|
59
|
+
|
60
|
+
# Xen::Command.create_image('memory=512', :size => '10Gb')
|
61
|
+
# => "xm-create-image memory=512 size=10Gb"
|
62
|
+
#
|
63
|
+
def self.create_image(*args)
|
64
|
+
options = args.extract_options!
|
65
|
+
cmd = "xm-create-image #{args.concat(options.to_args).join(' ')}"
|
66
|
+
`cmd`
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.xm_info
|
70
|
+
result = `xm info`
|
71
|
+
result.scan(/(\S+)\s*:\s*([^\n]+)/).inject({}){ |m, (i,j)| m[i.to_sym] = j; m }
|
72
|
+
end
|
73
|
+
end
|
data/lib/xen/config.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
class Xen::Config
|
4
|
+
# The config files for each Xen domU
|
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)) if File.exists?(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 Block Device
|
115
|
+
#
|
116
|
+
# We're only supporting Logical Volumes. No loopback devices.
|
117
|
+
#
|
118
|
+
# http://wiki.xensource.com/xenwiki/XenStorage
|
119
|
+
#
|
120
|
+
# == Example
|
121
|
+
#
|
122
|
+
# disk = [ 'phy:xendisks/example-disk,sda1,w',
|
123
|
+
# 'phy:xendisks/example-swap,sda2,w',
|
124
|
+
# 'phy:assets/example-assets,sdb1,w' ]
|
125
|
+
class Xen::Vbd
|
126
|
+
attr_accessor :name, :vg, :domu, :mode
|
127
|
+
def initialize(name, vg, domu, mode='w')
|
128
|
+
@name, @vg, @domu, @mode = name, vg, domu, mode
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.from_str(value)
|
132
|
+
dom0, domu, mode = value.split(',')
|
133
|
+
vg, name = dom0.split(/[\/:]/).slice(-2, 2)
|
134
|
+
new(name, vg, domu, mode)
|
135
|
+
end
|
136
|
+
|
137
|
+
def size
|
138
|
+
Xen::Command.lv_size(@vg, @name)
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_str
|
142
|
+
"phy:#{vg}/#{lv},#{domu},#{mode}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
data/lib/xen/host.rb
ADDED
data/lib/xen/image.rb
ADDED
data/lib/xen/instance.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
class Xen::Instance
|
2
|
+
include Xen::Parentable
|
3
|
+
attr_reader :name, :domid, :memory, :cpu_time, :vcpus, :state, :start_time
|
4
|
+
|
5
|
+
def initialize(name, options={})
|
6
|
+
@name = name
|
7
|
+
@domid = options[:domid]
|
8
|
+
@memory = options[:memory]
|
9
|
+
@cpu_time = options[:cpu_time]
|
10
|
+
@vcpus = options[:vcpus]
|
11
|
+
@state = options[:state]
|
12
|
+
@start_time = Time.at(options[:start_time].to_f) if options[:start_time]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find(*args)
|
16
|
+
options = args.extract_options!
|
17
|
+
case args.first
|
18
|
+
when :all then all
|
19
|
+
else find_by_name(args.first)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
Xen::Command.detailed_instance_list.collect do |instance|
|
25
|
+
new(name, instance)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find_by_name(name)
|
30
|
+
Xen::Command.detailed_instance_list(name).each do |instance|
|
31
|
+
return new(name, instance)
|
32
|
+
end
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.create(name)
|
37
|
+
output = Xen::Command.start_instance(name.to_s + Xen::CONFIG_FILE_EXTENSION)
|
38
|
+
$? == 0 ? true : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.shutdown(name)
|
42
|
+
output = Xen::Command.shutdown_instance(name)
|
43
|
+
$? == 0 ? true : false
|
44
|
+
end
|
45
|
+
|
46
|
+
# A convenience wrapper for <tt>find(:dom0)</tt>.</tt>.
|
47
|
+
def self.dom0(*args)
|
48
|
+
find_by_name(:dom0)
|
49
|
+
end
|
50
|
+
|
51
|
+
def uptime
|
52
|
+
start_time ? Time.now - start_time : nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def reboot
|
56
|
+
`xm reboot #{name}`
|
57
|
+
$? == 0 ? true : false
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy
|
61
|
+
end
|
62
|
+
|
63
|
+
def pause
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/xen/slice.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
class Xen::Slice
|
2
|
+
attr_accessor :name, :image, :config, :backups
|
3
|
+
|
4
|
+
def self.find(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
case args.first
|
7
|
+
when :all then Xen::Config.find(:all, options).collect { |config| config.slice }
|
8
|
+
when :running then Xen::Instance.find(:all, options).collect { |instance| instance.slice }
|
9
|
+
# Retrieve a Slice by name
|
10
|
+
else Xen::Config.find_by_name(args.first) && self.new(args.first)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.all(options={})
|
15
|
+
self.find(:all, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(*args)
|
19
|
+
options = args.extract_options! # remove trailing hash (not used)
|
20
|
+
@name = args.first
|
21
|
+
@config = options[:config]
|
22
|
+
@instance = options[:instance]
|
23
|
+
@instance_cache_expires = Time.now
|
24
|
+
@backups = Array(options[:backups])
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_image(args)
|
28
|
+
args = hash.collect{|k,v| "#{k}=#{v}"}
|
29
|
+
Xen::Command.create_image
|
30
|
+
end
|
31
|
+
|
32
|
+
# Cache Xen instance info to reduce system calls to xm command.
|
33
|
+
# It still needs to be checked regularly as operations like shutdown
|
34
|
+
# and create can take a while.
|
35
|
+
def instance
|
36
|
+
if @instance_cache_expires > Time.now
|
37
|
+
@instance
|
38
|
+
else
|
39
|
+
@instance_cache_expires = Time.now + Xen::INSTANCE_OBJECT_LIFETIME
|
40
|
+
@instance = Xen::Instance.find(@name) if @name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# XXX We're assuming other processes aren't going to edit configs
|
45
|
+
# This is reasonable in simple cases.
|
46
|
+
def config
|
47
|
+
@config ||= Xen::Config.find(name) if @name
|
48
|
+
end
|
49
|
+
|
50
|
+
def backups
|
51
|
+
Xen::Backup.find(name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def state
|
55
|
+
self.instance ? :running : :stopped
|
56
|
+
end
|
57
|
+
|
58
|
+
def running?
|
59
|
+
self.instance ? true : false
|
60
|
+
end
|
61
|
+
|
62
|
+
def start
|
63
|
+
Xen::Instance.create(@name)
|
64
|
+
@instance = Xen::Instance.find(@name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def stop
|
68
|
+
Xen::Instance.shutdown(@name)
|
69
|
+
@instance = Xen::Instance.find(@name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def config_newer_than_instance?
|
73
|
+
instance && config.updated_at > instance.start_time
|
74
|
+
end
|
75
|
+
|
76
|
+
def save
|
77
|
+
@config.save
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
data/test/test_ruby-xen.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: 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,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2008-09-
|
13
|
+
date: 2008-09-21 00:00:00 +10:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -32,13 +32,20 @@ files:
|
|
32
32
|
- bin/ruby-xen
|
33
33
|
- lib/ruby-xen.rb
|
34
34
|
- test/test_ruby-xen.rb
|
35
|
-
- lib/
|
35
|
+
- lib/xen/backup.rb
|
36
|
+
- lib/xen/command.rb
|
37
|
+
- lib/xen/config.rb
|
38
|
+
- lib/xen/host.rb
|
39
|
+
- lib/xen/image.rb
|
40
|
+
- lib/xen/instance.rb
|
41
|
+
- lib/xen/slice.rb
|
42
|
+
- lib/templates/domu.cfg.erb
|
36
43
|
has_rdoc: true
|
37
|
-
homepage: http://github.com/
|
44
|
+
homepage: http://github.com/mbailey/ruby-xen
|
38
45
|
post_install_message:
|
39
46
|
rdoc_options:
|
40
47
|
- --main
|
41
|
-
- README.
|
48
|
+
- README.rdoc
|
42
49
|
require_paths:
|
43
50
|
- lib
|
44
51
|
required_ruby_version: !ruby/object:Gem::Requirement
|
data/lib/ruby-xen/domain.rb
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
class Array #:nodoc:
|
2
|
-
# Extracts options from a set of arguments. Removes and returns the last
|
3
|
-
# element in the array if it's a hash, otherwise returns a blank hash.
|
4
|
-
#
|
5
|
-
# def options(*args)
|
6
|
-
# args.extract_options!
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
# options(1, 2) # => {}
|
10
|
-
# options(1, 2, :a => :b) # => {:a=>:b}
|
11
|
-
def extract_options!
|
12
|
-
last.is_a?(::Hash) ? pop : {}
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module Xen
|
17
|
-
class Host
|
18
|
-
|
19
|
-
attr_reader :host, :machine, :total_memory, :free_memory
|
20
|
-
def initialize
|
21
|
-
result = `xm info`
|
22
|
-
result.scan(/(\S+)\s*:\s*([^\n]+)/).each do |i,j|
|
23
|
-
instance_variable_set("@#{i}", j)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
class Domain
|
31
|
-
|
32
|
-
attr_accessor :name, :image, :config, :instance
|
33
|
-
|
34
|
-
def initialize(name)
|
35
|
-
@name = name
|
36
|
-
@config = Xen::Config.find(name)
|
37
|
-
@instance = Xen::Instance.find(name)
|
38
|
-
@image = Xen::Image.find(name)
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.find(*args)
|
42
|
-
options = args.extract_options!
|
43
|
-
case args.first
|
44
|
-
when :all then Xen::Config.find(:all, options).collect { |config| Xen::Domain.new(config.name) }
|
45
|
-
when :running then Xen::Instance.find(:all, options).collect { |instance| Xen::Domain.new(instance.name) }
|
46
|
-
# Retrieve a Domain by name
|
47
|
-
else Xen::Config.find_by_name(args.first) && self.new(args.first)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def running?
|
52
|
-
@instance
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
class Config
|
59
|
-
|
60
|
-
attr_reader :name, :memory, :ip
|
61
|
-
|
62
|
-
def initialize(*args)
|
63
|
-
options = args.extract_options!
|
64
|
-
@name = args.first
|
65
|
-
@memory = options[:memory] || nil
|
66
|
-
@ip = options[:ip] || nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.find(*args)
|
70
|
-
options = args.extract_options!
|
71
|
-
case args.first
|
72
|
-
when :all then all
|
73
|
-
else find_by_name(args.first)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.all
|
78
|
-
result = `xen-list-images`
|
79
|
-
configs = result.scan(/Name: (\w+)\nMemory: (\w+)\nIP: (\S+)/)
|
80
|
-
configs.collect do |config|
|
81
|
-
name, memory, ip = config
|
82
|
-
new(name, :memory => memory, :ip => ip)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def self.find_by_name(name)
|
87
|
-
return new('Domain-0') if name == 'Domain-0'
|
88
|
-
all.detect {|config| puts config; config.name == name.to_s}
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
class Image
|
95
|
-
|
96
|
-
attr_accessor :name
|
97
|
-
|
98
|
-
def initialize(name)
|
99
|
-
@name = name
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.find(name)
|
103
|
-
new name
|
104
|
-
end
|
105
|
-
|
106
|
-
def find_one(name, options)
|
107
|
-
if result = find_every(options).first
|
108
|
-
result
|
109
|
-
else
|
110
|
-
raise RecordNotFound, "Couldn't find domain with name=#{name}"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
|
116
|
-
class Instance
|
117
|
-
|
118
|
-
attr_reader :name, :domid, :memory, :cpu_time, :vcpus, :state, :start_time
|
119
|
-
|
120
|
-
def initialize(name, options={})
|
121
|
-
@name = name
|
122
|
-
@domid = options[:domid] || nil
|
123
|
-
@memory = options[:memory] || nil
|
124
|
-
@cpu_time = options[:cpu_time] || nil
|
125
|
-
@vcpus = options[:vcpus] || nil
|
126
|
-
@state = options[:state] || nil
|
127
|
-
@start_time = options[:start_time] || nil
|
128
|
-
end
|
129
|
-
|
130
|
-
def self.find(*args)
|
131
|
-
options = args.extract_options!
|
132
|
-
case args.first
|
133
|
-
when :all then all
|
134
|
-
else find_by_name(args.first)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.all
|
139
|
-
result = `xm list`
|
140
|
-
# XXX check for failed command
|
141
|
-
result_array = result.split("\n")
|
142
|
-
result_array.shift
|
143
|
-
result_array.collect do |domain|
|
144
|
-
name, domid, memory, vcpus, state, cpu_time = domain.scan(/[^ ,]+/)
|
145
|
-
new(name, :domid => domid, :memory => memory, :cpu_time => cpu_time)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def self.find_by_name(name)
|
150
|
-
all.detect{|domain| domain.name == name.to_s }
|
151
|
-
end
|
152
|
-
|
153
|
-
# XXX Rails version - we need some error checking!
|
154
|
-
#
|
155
|
-
# def self.find_by_name(name, options)
|
156
|
-
# if result = find_every(options)
|
157
|
-
# result.detect{ |domain| domain.name == name }
|
158
|
-
# else
|
159
|
-
# raise RecordNotFound, "Couldn't find domain with name=#{name}"
|
160
|
-
# end
|
161
|
-
# end
|
162
|
-
|
163
|
-
# A convenience wrapper for <tt>find(:dom0)</tt>.</tt>.
|
164
|
-
def dom0(*args)
|
165
|
-
find_by_name(:dom0)
|
166
|
-
end
|
167
|
-
|
168
|
-
# This is an alias for find(:all). You can pass in all the same arguments to this method as you can
|
169
|
-
# to find(:all)
|
170
|
-
def all(*args)
|
171
|
-
find(:all, *args)
|
172
|
-
end
|
173
|
-
|
174
|
-
def uptime
|
175
|
-
start_time ? Time.now - start_time : nil
|
176
|
-
end
|
177
|
-
|
178
|
-
def running?
|
179
|
-
output = `xm list #{name}`
|
180
|
-
$? == 0 ? true : false
|
181
|
-
end
|
182
|
-
|
183
|
-
def start
|
184
|
-
output = `xm create #{name}.cfg`
|
185
|
-
$? == 0 ? true : false
|
186
|
-
end
|
187
|
-
|
188
|
-
def shutdown
|
189
|
-
output = `xm shutdown #{name}`
|
190
|
-
$? == 0 ? true : false
|
191
|
-
end
|
192
|
-
|
193
|
-
def reboot
|
194
|
-
`xm reboot #{name}`
|
195
|
-
$? == 0 ? true : false
|
196
|
-
end
|
197
|
-
|
198
|
-
def destroy
|
199
|
-
end
|
200
|
-
|
201
|
-
def pause
|
202
|
-
end
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
class Backup
|
207
|
-
end
|
208
|
-
|
209
|
-
# class XenTools
|
210
|
-
#
|
211
|
-
# def self.xen_list_images
|
212
|
-
# puts "returning list of images"
|
213
|
-
# end
|
214
|
-
#
|
215
|
-
# def xen_create_image
|
216
|
-
# puts "creating image"
|
217
|
-
# end
|
218
|
-
#
|
219
|
-
# def xen_delete_image
|
220
|
-
# puts "creating image"
|
221
|
-
# end
|
222
|
-
#
|
223
|
-
# def xen_archive_image
|
224
|
-
# puts "archiving image"
|
225
|
-
# end
|
226
|
-
#
|
227
|
-
# end
|
228
|
-
|
229
|
-
end
|