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/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