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/lib/xen/backup.rb CHANGED
@@ -1,3 +1,72 @@
1
- class Xen::Backup
2
- include Xen::Parentable
1
+ module Xen
2
+ class Backup
3
+ include Xen::Parentable
4
+
5
+ attr_accessor :name, :version
6
+
7
+ def self.create(*args)
8
+ options = args.extract_options!
9
+ name = args.first
10
+
11
+ options = {}
12
+ name = 'foo' # XXX replace with real value
13
+ version = options[:version] || Time.now.strftime('%Y%m%d')
14
+ backup_dir = options[:backup_dir] || Xen::BACKUP_DIR
15
+ backup_file_ext = options[:backup_file_ext] || Xen::BACKUP_FILE_EXT
16
+ archive_name="#{name}-#{version}.#{backup_file_ext}"
17
+
18
+ slice = Xen::Slice.find(name) # XXX test for failure
19
+ slice.stop
20
+ sleep 10
21
+
22
+ temp_mount = `mktemp -d -p /mnt #{name}-XXXXX`.chomp # XXX test for failure
23
+ `mount #{slice.root_disk.path} #{temp_mount}` # XXX test for failure
24
+
25
+ # Creating archive at $ARCHIVE_DIR/$ARCHIVE_NAME ...
26
+ `tar --create --exclude=/proc --exclude=/etc/udev/rules.d/70-persistent-net.rules --directory #{temp_mount} --file #{backup_dir}/#{archive_name} .`
27
+ # XXX test for failure
28
+
29
+ # Unmounting image
30
+ `umount #{temp_mount}`
31
+ Dir.delete(temp_mount)
32
+
33
+ # Creating symlink from new backup to filename without version number
34
+ last_backup = "#{backup_dir}/#{name}.#{backup_file_ext}"
35
+ File.delete(last_backup) if File.symlink(last_backup)
36
+ `ln -sf #{backup_dir}/#{archive_name} #{last_backup}`
37
+
38
+ slice.start
39
+
40
+ new(:name => name, :version => version)
41
+ end
42
+
43
+ def initialize(*args)
44
+ options = args.extract_options!
45
+ @name = options[:name]
46
+ @version = options[:version]
47
+ end
48
+
49
+ def self.find(*args)
50
+ # return all
51
+ slice = args.first
52
+ Dir.glob("#{Xen::BACKUP_DIR}/*-*#{Xen::BACKUP_FILE_EXT}").collect { |file|
53
+ if match = File.basename(file, Xen::BACKUP_FILE_EXT).match(/(#{ slice || '.*' })-(.*)/)
54
+ new(:name => match[1], :version => match[2])
55
+ end
56
+ }.compact
57
+ end
58
+
59
+ def filename
60
+ "#{@name}-#{@version}#{Xen::BACKUP_FILE_EXT}"
61
+ end
62
+
63
+ def fullpath
64
+ File.join(Xen::BACKUP_DIR, filename)
65
+ end
66
+
67
+ def size
68
+ File.size(fullpath)
69
+ end
70
+
71
+ end
3
72
  end
data/lib/xen/command.rb CHANGED
@@ -1,30 +1,101 @@
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
1
+ module Xen
2
+ class Command
3
+
4
+ class ExternalFailure < RuntimeError; end
5
+
6
+ # def self.xm_list
7
+ # raw = `xm list`.scan(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/)
8
+ # headers = raw.delete_at(0)
9
+ # raw.map do |row|
10
+ # headers.enum_with_index.inject({}) { |m, (head, i)| m[head] = row[i]; m }
11
+ # end
12
+ # end
13
+
14
+ def self.run(cmd)
15
+ output = []
16
+ error = nil
17
+ stat = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
18
+ while line = stdout.gets
19
+ output << line.strip
20
+ end
21
+ error = stderr.read.strip
22
+ end
23
+ # if stat.exited? # Is this needed?
24
+ if stat.exitstatus > 0
25
+ raise ExternalFailure, "Fatal error, `#{cmd}` returned #{stat.exitstatus} with '#{error}'"
26
+ end
27
+ # end
28
+ return output
29
+ end
9
30
 
10
- def self.detailed_instance_list(name='')
11
- raw_entries = `xm list --long #{name}`.split(/\n\(domain/)
12
- raw_entries.collect do |entry|
13
- attributes = entry.scan(/\((name|domid|memory|vcpus|state|cpu_time|start_time) (.*)\)/)
14
- attributes.inject({}) { |m, (key, val)| m[key.to_sym] = val; m }
31
+ # Return the size of a logical volume in gigabytes
32
+ def self.lv_size(vg_name, lv_name)
33
+ cmd = "lvs --noheadings --nosuffix --options lv_size --units g #{vg_name}/#{lv_name}"
34
+ `#{cmd}`.strip
15
35
  end
16
- end
17
36
 
18
- def self.create(config_file)
19
- `xm create #{config_file}`
20
- end
37
+ # Return list of logical volumes
38
+ def self.lv_list(vg_name)
39
+ cmd = "lvs --noheadings --nosuffix --options vg_name,lv_name,lv_size --units g #{vg_name}"
40
+ raw = `#{cmd}`
41
+ raw.scan(/(\S+)\s+(\S+)\s+(\S+)/).collect{ |vg_name, lv_name, size|
42
+ {
43
+ :vg => vg_name,
44
+ :name => lv_name,
45
+ :size => size
46
+ }
47
+ }
48
+ end
49
+
50
+ def self.vg_list
51
+ cmd = "vgs --noheadings --units g --nosuffix --options vg_name,vg_size,vg_free,lv_count,max_lv"
52
+ raw = `#{cmd}`
53
+ raw.scan(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/).collect{ |vg_name, vg_size, vg_free, lv_count, max_lv|
54
+ {
55
+ :name => vg_name,
56
+ :size => vg_size,
57
+ :free => vg_free,
58
+ :lv_count => lv_count,
59
+ :max_lv => max_lv
60
+ }
61
+ }
62
+ end
21
63
 
22
- def self.shutdown(name, blocking=false)
23
- `xm shutdown #{'-w' if blocking} #{name}`
24
- end
64
+ def self.detailed_instance_list(name='')
65
+ cmd = "xm list --long #{name}"
66
+ raw_entries = `#{cmd}`.split(/\n\(domain/)
67
+ raw_entries.collect do |entry|
68
+ attributes = entry.scan(/\((name|domid|memory|vcpus|state|cpu_time|start_time) (.*)\)/)
69
+ attributes.inject({}) { |m, (key, val)| m[key.to_sym] = val; m }
70
+ end
71
+ end
72
+
73
+ def self.start_instance(config_file)
74
+ `xm create #{config_file}`
75
+ end
76
+
77
+ def self.shutdown_instance(name, blocking=false)
78
+ `xm shutdown #{'-w' if blocking} #{name}`
79
+ end
80
+
81
+ # Xen::Command.create_image('memory=512', :size => '10Gb')
82
+ # => "xm-create-image memory=512 size=10Gb"
83
+ #
84
+ # XXX call with a hash by default
85
+ #
86
+ def self.create_image(*args)
87
+ options = args.extract_options!
88
+ cmd = "xen-create-image #{args.concat(options.to_args).join(' ')}"
89
+ puts
90
+ puts "Running the command:"
91
+ puts cmd
92
+ puts
93
+ system(cmd)
94
+ end
25
95
 
26
- def self.xm_info
27
- result = `xm info`
28
- result.scan(/(\S+)\s*:\s*([^\n]+)/).inject({}){ |m, (i,j)| m[i.to_sym] = j; m }
96
+ def self.xm_info
97
+ result = `/usr/sbin/xm info`
98
+ result.scan(/(\S+)\s*:\s*([^\n]+)/).inject({}){ |m, (i,j)| m[i.to_sym] = j; m }
99
+ end
29
100
  end
30
101
  end
@@ -0,0 +1,160 @@
1
+ require 'erb'
2
+
3
+ module Xen
4
+ class ConfigFile
5
+ # The config files for each Xen domU
6
+ include Xen::Parentable
7
+ attr_accessor :name, :kernel, :ramdisk, :memory, :root, :vbds, :vifs, :on_poweroff, :on_reboot, :on_crash, :extra
8
+
9
+ def initialize(*args)
10
+ options = args.extract_options!
11
+ @name = options[:name]
12
+ @kernel = options[:kernel]
13
+ @ramdisk = options[:ramdisk]
14
+ @memory = options[:memory].to_i
15
+ @root = options[:root]
16
+ @vbds = options[:vbds]
17
+ @vifs = options[:vifs]
18
+ @on_poweroff = options[:on_poweroff]
19
+ @on_reboot = options[:on_reboot]
20
+ @on_crash = options[:on_crash]
21
+ @extra = options[:extra]
22
+ end
23
+
24
+ def self.find(*args)
25
+ options = args.extract_options!
26
+ case args.first
27
+ when :all then all
28
+ else find_by_name(args.first)
29
+ end
30
+ end
31
+
32
+ def self.all
33
+ config_files = Dir.glob("#{Xen::XEN_DOMU_CONFIG_DIR}/*#{Xen::CONFIG_FILE_EXTENSION}")
34
+ config_files.collect do |filename|
35
+ create_from_config_file(File.read(filename))
36
+ end
37
+ end
38
+
39
+ def self.find_by_name(name)
40
+ return new(:name => 'Domain-0') if name == 'Domain-0'
41
+ filename = "#{Xen::XEN_DOMU_CONFIG_DIR}/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
42
+ create_from_config_file(File.read(filename)) if File.exists?(filename)
43
+ end
44
+
45
+ def filename
46
+ "#{Xen::XEN_DOMU_CONFIG_DIR}/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
47
+ end
48
+
49
+ def auto_file
50
+ "#{Xen::XEN_DOMU_CONFIG_DIR}/auto/#{name}#{Xen::CONFIG_FILE_EXTENSION}"
51
+ end
52
+
53
+ def updated_at
54
+ File.mtime(filename)
55
+ end
56
+
57
+ # Set to true|false to enable|disable autostart of slice
58
+ def set_auto(value)
59
+ if value == true
60
+ File.symlink("../#{File.basename(filename)}", auto_file) unless auto
61
+ else
62
+ File.unlink(auto_file) if auto
63
+ end
64
+ auto == value # return true if final state is as requested
65
+ end
66
+
67
+ # Returns true|false depending on whether slice is set to start automatically
68
+ def auto
69
+ File.symlink?(auto_file) && File.expand_path(File.readlink(auto_file), File.dirname(auto_file)) == filename
70
+ end
71
+
72
+ alias auto? auto
73
+
74
+ def self.create_from_config_file(config)
75
+ name, kernel, ramdisk, memory, root, disk, vif, on_poweroff, on_reboot, on_crash, extra = nil
76
+ eval(config)
77
+ vifs = Array(vif).collect { |v| Xen::Vif.from_str(v) }
78
+ vbds = Array(disk).collect { |d| Xen::Vbd.from_str(d) }
79
+ new(:name => 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)
80
+ end
81
+
82
+ def save
83
+ template = ERB.new IO.read(Xen::TEMPLATE_DIR + '/domu.cfg.erb')
84
+ File.open(filename, 'w'){ |f| f.write template.result(binding) }
85
+ end
86
+
87
+ end
88
+
89
+
90
+ # Virtual Network Interface
91
+ #
92
+ # http://wiki.xensource.com/xenwiki/XenNetworking
93
+ #
94
+ class Xen::Vif
95
+ attr_accessor :ip, :mac, :bridge, :vifname
96
+
97
+ def initialize(*args)
98
+ options = args.extract_options!
99
+
100
+ # Validations - move them out into a validate function
101
+ raise(ValidationFailed, 'message') if options[:vifname] == 'foos'
102
+
103
+ @ip = options[:ip].to_s.gsub /['"]/, ''
104
+ @mac = options[:mac].to_s.gsub /['"]/, ''
105
+ @bridge = options[:bridge].to_s.gsub /['"]/, ''
106
+ @vifname = options[:vifname].to_s.gsub /['"]/, ''
107
+ end
108
+
109
+ def self.from_str(value)
110
+ options = value.scan(/(\w+)=([^,]+)/).inject({}){ |m, (k, v)| m[k.to_sym] = v; m }
111
+ new(options)
112
+ end
113
+
114
+ def to_str
115
+ %w(ip mac bridge vifname).collect { |key|
116
+ "#{key}=#{instance_variable_get('@' + key)}" if !instance_variable_get('@'+key).nil?
117
+ }.compact.join(',')
118
+ end
119
+ end
120
+
121
+
122
+ # Virtual Block Device
123
+ #
124
+ # We're only supporting Logical Volumes. No loopback devices.
125
+ #
126
+ # http://wiki.xensource.com/xenwiki/XenStorage
127
+ #
128
+ # == Example
129
+ #
130
+ # disk = [ 'phy:xendisks/example-disk,sda1,w',
131
+ # 'phy:xendisks/example-swap,sda2,w',
132
+ # 'phy:assets/example-assets,sdb1,w' ]
133
+ class Xen::Vbd
134
+ attr_accessor :name, :volume_group, :domu, :mode
135
+ def initialize(name, volume_group, domu, mode='w')
136
+ @name, @volume_group, @domu, @mode = name, volume_group, domu, mode
137
+ end
138
+
139
+ def self.from_str(value)
140
+ dom0, domu, mode = value.split(',')
141
+ volume_group, name = dom0.split(/[\/:]/).slice(-2, 2)
142
+ new(name, volume_group, domu, mode)
143
+ end
144
+
145
+ def size
146
+ Xen::Command.lv_size(@volume_group, @name)
147
+ end
148
+
149
+ # XXX Not needed?
150
+ # def path
151
+ # "/dev/#{volume_group}/#{name}"
152
+ # end
153
+
154
+ def to_str
155
+ "phy:#{volume_group}/#{name},#{domu},#{mode}"
156
+ end
157
+ end
158
+
159
+ end
160
+
data/lib/xen/host.rb CHANGED
@@ -1,9 +1,27 @@
1
- class Xen::Host
2
- attr_reader :host, :machine, :total_memory, :free_memory
1
+ module Xen
2
+ class Host
3
+ attr_reader :host, :machine, :total_memory, :nr_cpus
3
4
 
4
- def initialize
5
- Xen::Command.xm_info.each do |i,j|
6
- instance_variable_set("@#{i}", j)
5
+ def initialize(detail_hash={})
6
+ detail_hash.each { |i,j| instance_variable_set("@#{i}", j) }
7
7
  end
8
+
9
+ def self.find
10
+ new Xen::Command.xm_info
11
+ end
12
+
13
+ def free_memory
14
+ if f = `free -m`
15
+ if (m = f.match /buffers\/cache.*\s+(\w+)\n/)
16
+ m[1].to_i
17
+ end
18
+ end
19
+ end
20
+
21
+ def domu_memory
22
+ Xen::Slice.find(:running).inject(0){|m, slice| m += slice.instance.memory.to_i; m}
23
+ end
24
+
8
25
  end
26
+
9
27
  end
data/lib/xen/instance.rb CHANGED
@@ -1,67 +1,68 @@
1
- class Xen::Instance
2
- include Xen::Parentable
3
- attr_reader :name, :domid, :memory, :cpu_time, :vcpus, :state, :start_time, :object_expires
1
+ module Xen
2
+ class Instance
3
+ include Xen::Parentable
4
+ attr_reader :name, :domid, :memory, :cpu_time, :vcpus, :state, :start_time
4
5
 
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
- @object_expires = Time.now + Xen::INSTANCE_OBJECT_LIFETIME
14
- end
6
+ def initialize(options={})
7
+ @name = options[:name]
8
+ @domid = options[:domid]
9
+ @memory = options[:memory]
10
+ @cpu_time = options[:cpu_time]
11
+ @vcpus = options[:vcpus]
12
+ @state = options[:state]
13
+ @start_time = Time.at(options[:start_time].to_f) if options[:start_time]
14
+ end
15
15
 
16
- def self.find(*args)
17
- options = args.extract_options!
18
- case args.first
19
- when :all then all
20
- else find_by_name(args.first)
16
+ def self.find(*args)
17
+ options = args.extract_options!
18
+ case args.first
19
+ when :all then all
20
+ else find_by_name(args.first)
21
+ end
21
22
  end
22
- end
23
23
 
24
- def self.all
25
- Xen::Command.detailed_instance_list.collect do |instance|
26
- new(name, instance)
24
+ def self.all
25
+ Xen::Command.detailed_instance_list.collect do |instance|
26
+ new(instance)
27
+ end
27
28
  end
28
- end
29
29
 
30
- def self.find_by_name(name)
31
- Xen::Command.detailed_instance_list(name).each do |instance|
32
- return new(name, instance)
30
+ def self.find_by_name(name)
31
+ Xen::Command.detailed_instance_list(name).each do |instance|
32
+ return new(instance)
33
+ end
34
+ return false
33
35
  end
34
- return false
35
- end
36
36
 
37
- def self.create(name)
38
- output = Xen::Command.create(name.to_s + Xen::CONFIG_FILE_EXTENSION)
39
- $? == 0 ? true : false
40
- end
37
+ def self.create(name)
38
+ output = Xen::Command.start_instance(name.to_s + Xen::CONFIG_FILE_EXTENSION)
39
+ $? == 0 ? true : false
40
+ end
41
41
 
42
- def self.shutdown(name)
43
- output = Xen::Command.shutdown(name)
44
- $? == 0 ? true : false
45
- end
42
+ def self.shutdown(name)
43
+ output = Xen::Command.shutdown_instance(name)
44
+ $? == 0 ? true : false
45
+ end
46
46
 
47
- # A convenience wrapper for <tt>find(:dom0)</tt>.</tt>.
48
- def self.dom0(*args)
49
- find_by_name(:dom0)
50
- end
47
+ # A convenience wrapper for <tt>find(:dom0)</tt>.</tt>.
48
+ def self.dom0(*args)
49
+ find_by_name(:dom0)
50
+ end
51
51
 
52
- def uptime
53
- start_time ? Time.now - start_time : nil
54
- end
52
+ def uptime
53
+ start_time ? Time.now - start_time : nil
54
+ end
55
55
 
56
- def reboot
57
- `xm reboot #{name}`
58
- $? == 0 ? true : false
59
- end
56
+ def reboot
57
+ `xm reboot #{name}`
58
+ $? == 0 ? true : false
59
+ end
60
60
 
61
- def destroy
62
- end
61
+ def destroy
62
+ end
63
63
 
64
- def pause
65
- end
64
+ def pause
65
+ end
66
66
 
67
- end
67
+ end
68
+ end