vmit 0.0.3 → 0.0.3.99

Sign up to get free protection for your applications and to get access to all the features.
data/bin/vmit CHANGED
@@ -41,14 +41,14 @@ module Vmit
41
41
 
42
42
  def execute
43
43
  options = {}
44
- vm = Vmit::VirtualMachine.new(File.expand_path(Dir.pwd))
44
+ vm = Vmit::Workspace.new(File.expand_path(Dir.pwd))
45
45
 
46
46
  OPTION_KEYS.each do |k|
47
47
  val = self.send(k)
48
48
  options[k] = val if val
49
49
  end
50
50
 
51
- vm.options.merge!(options)
51
+ vm.config.configure(options)
52
52
  vm.save_config!
53
53
 
54
54
  vm.disk_image_init!(options)
@@ -57,7 +57,7 @@ module Vmit
57
57
 
58
58
  end
59
59
 
60
- class RunCommand < BaseVmCommand
60
+ class UpCommand < BaseVmCommand
61
61
 
62
62
  OPTION_KEYS = [:memory, :cdrom]
63
63
 
@@ -70,23 +70,89 @@ module Vmit
70
70
 
71
71
  def execute
72
72
  options = {}
73
- vm = Vmit::VirtualMachine.new(File.expand_path(Dir.pwd))
73
+ workspace = Vmit::Workspace.new(File.expand_path(Dir.pwd))
74
74
 
75
75
  OPTION_KEYS.each do |k|
76
76
  val = self.send(k)
77
77
  options[k] = val if val
78
78
  end
79
79
 
80
- vm.run(options)
80
+ vm = Vmit::LibvirtVM.new(workspace, options)
81
+ vm.up
81
82
  end
82
83
 
83
84
  end
84
85
 
86
+ class SpiceCommand < Clamp::Command
87
+ def execute
88
+ vm = LibvirtVM.from_pwd
89
+ vm.spice
90
+ end
91
+ end
92
+
93
+ class VncCommand < Clamp::Command
94
+ def execute
95
+ vm = LibvirtVM.from_pwd
96
+ vm.vnc
97
+ end
98
+ end
99
+
100
+ class SshCommand < Clamp::Command
101
+ def execute
102
+ vm = LibvirtVM.from_pwd
103
+ vm.assert_up
104
+
105
+ while not vm.ip_address
106
+ sleep(1)
107
+ Vmit.logger.info("Waiting for ip address...")
108
+ end
109
+ Vmit.logger.info("ip address is #{vm.ip_address}")
110
+ Vmit::Utils.wait_for_port(vm.ip_address, 22)
111
+ system("ssh root@#{vm.ip_address}")
112
+ end
113
+ end
114
+
115
+ class StatusCommand < Clamp::Command
116
+ def execute
117
+ vm = LibvirtVM.from_pwd
118
+ st, reason = vm.state
119
+
120
+ puts "VM is #{st}...(#{reason})"
121
+ if vm.ip_address
122
+ puts " #{vm.ip_address}"
123
+ end
124
+
125
+ end
126
+ end
127
+
128
+ class RebootCommand < Clamp::Command
129
+ def execute
130
+ vm = LibvirtVM.from_pwd
131
+ vm.reboot
132
+ end
133
+ end
134
+
135
+ class ShutdownCommand < Clamp::Command
136
+ def execute
137
+ vm = LibvirtVM.from_pwd
138
+ vm.shutdown
139
+ end
140
+ end
141
+
142
+ class DestroyCommand < Clamp::Command
143
+ def execute
144
+ vm = LibvirtVM.from_pwd
145
+ vm.destroy
146
+ end
147
+ end
148
+
85
149
  class DiskSnapshotCommand < Clamp::Command
86
150
 
87
151
  def execute
88
- vm = Vmit::VirtualMachine.new(File.expand_path(Dir.pwd))
89
- vm.disk_snapshot!
152
+ vm = LibvirtVM.from_pwd
153
+ vm.assert_down
154
+
155
+ vm.workspace.disk_snapshot!
90
156
  end
91
157
 
92
158
  end
@@ -94,8 +160,8 @@ module Vmit
94
160
  class DiskImagesCommand < Clamp::Command
95
161
 
96
162
  def execute
97
- vm = Vmit::VirtualMachine.new(File.expand_path(Dir.pwd))
98
- puts vm.disk_images.last
163
+ vm = LibvirtVM.from_pwd
164
+ puts vm.workspace.disk_images.last
99
165
  end
100
166
 
101
167
  end
@@ -103,19 +169,30 @@ module Vmit
103
169
  class DiskRollbackCommand < Clamp::Command
104
170
 
105
171
  def execute
106
- vm = Vmit::VirtualMachine.new(File.expand_path(Dir.pwd))
107
- vm.disk_rollback!
172
+ vm = LibvirtVM.from_pwd
173
+ vm.assert_down
174
+
175
+ vm.workspace.disk_rollback!
108
176
  end
109
177
 
110
178
  end
111
179
 
180
+ Vmit.load_plugins!
181
+
112
182
  class MainCommand < Clamp::Command
113
183
 
114
184
  subcommand "init", "Initialize the vm", InitCommand
115
- subcommand "run", "Run the vm", RunCommand
185
+ subcommand "up", "Run the vm", UpCommand
186
+ subcommand "status", "Show VM status", StatusCommand
187
+ subcommand "shutdown", "Shutdown the VM", ShutdownCommand
188
+ subcommand "reboot", "Reboot the VM", RebootCommand
189
+ subcommand "destroy", "Destroy the VM", DestroyCommand
116
190
  subcommand "disk-images", "List disk images", DiskImagesCommand
117
191
  subcommand "disk-snapshot", "Creates a new disk-snapshot", DiskSnapshotCommand
118
192
  subcommand "disk-rollback", "Rollbacks to previous disk snapshot", DiskRollbackCommand
193
+ subcommand "spice", "SPICE into the machine", SpiceCommand
194
+ subcommand "ssh", "SSH into the machine", SshCommand
195
+ subcommand "vnc", "VNC into the machine", VncCommand
119
196
 
120
197
  # Add commands offered via plugins
121
198
  Vmit.plugins.each do |plugin|
@@ -127,7 +204,6 @@ module Vmit
127
204
  end
128
205
  end
129
206
 
130
-
131
207
  end
132
208
 
133
209
  begin
@@ -19,13 +19,18 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'vmit/version'
22
- require 'vmit/bootstrap'
22
+ require 'vmit/unattended_install'
23
23
  require 'vmit/logger'
24
- require 'vmit/refcounted_resource'
25
24
  require 'vmit/network'
26
25
  require 'vmit/vfs'
27
- require 'vmit/virtual_machine'
26
+ require 'vmit/workspace'
27
+ require 'vmit/libvirt_vm'
28
+ require 'vmit/autoyast'
29
+ require 'vmit/kickstart'
30
+ require 'vmit/debian_preseed'
31
+ require 'vmit/install_media'
28
32
  require 'vmit/ext'
33
+ require 'vmit/utils'
29
34
  require 'pidfile'
30
35
 
31
36
  module Vmit
@@ -47,21 +52,23 @@ module Vmit
47
52
  @plugins ||= []
48
53
  end
49
54
 
50
- end
55
+ def self.load_plugins!
56
+ # Scan plugins
57
+ plugin_glob = File.join(File.dirname(__FILE__), 'vmit', 'plugins', '*.rb')
58
+ Dir.glob(plugin_glob).each do |plugin|
59
+ Vmit.logger.debug("Loading file: #{plugin}")
60
+ #puts "Loading file: #{plugin}"
61
+ load plugin
62
+ end
51
63
 
52
- # Scan plugins
53
- plugin_glob = File.join(File.dirname(__FILE__), 'vmit', 'plugins', '*.rb')
54
- Dir.glob(plugin_glob).each do |plugin|
55
- Vmit.logger.debug("Loading file: #{plugin}")
56
- #puts "Loading file: #{plugin}"
57
- load plugin
58
- end
64
+ # instantiate plugins
65
+ ::Vmit::Plugins.constants.each do |cnt|
66
+ pl_class = ::Vmit::Plugins.const_get(cnt)
67
+ #pl_instance = pl_class.new
68
+ Vmit.add_plugin(pl_class)
69
+ Vmit.logger.debug("Loaded: #{pl_class}")
70
+ #puts "Loaded: #{pl_class}"
71
+ end
72
+ end
59
73
 
60
- # instantiate plugins
61
- ::Vmit::Plugins.constants.each do |cnt|
62
- pl_class = ::Vmit::Plugins.const_get(cnt)
63
- #pl_instance = pl_class.new
64
- Vmit.add_plugin(pl_class)
65
- Vmit.logger.debug("Loaded: #{pl_class}")
66
- #puts "Loaded: #{pl_class}"
67
74
  end
@@ -19,38 +19,92 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'nokogiri'
22
+ require 'vmit/unattended_install'
23
+ require 'vmit/vfs'
22
24
 
23
25
  module Vmit
24
26
 
25
- class AutoYaST
27
+ class AutoYaST < UnattendedInstall
26
28
 
27
- attr_accessor :patterns
28
- attr_accessor :packages
29
-
30
- def initialize
31
- @net_udev = {}
32
- @patterns = []
33
- @packages = []
29
+ def initialize(location)
30
+ super(location)
31
+ base!
34
32
  end
35
33
 
36
- def minimal_opensuse!
37
- @patterns << 'base'
34
+ def base!
35
+ config.add_patterns! 'base'
36
+ config.add_packages! 'zypper'
37
+ config.add_packages! 'openssh'
38
38
  end
39
39
 
40
- def minimal_sle!
41
- @patterns << 'Minimal'
40
+ # minimal installation
41
+ # does not work with openSUSE
42
+ def minimal!
43
+ config.add_patterns! 'Minimal'
44
+ config.add_packages! 'zypper'
45
+ config.add_packages! 'openssh'
42
46
  end
43
47
 
44
- # Map a network device name and make
45
- # it persistent
46
- #
47
- # @param [String] MAC mac address
48
- # @param [String] device name
49
- def name_network_device(mac, name)
50
- if @net_udev.has_key?(mac) or @net_udev.has_value?(mac)
51
- raise "Device with MAC #{mac} is already assigned to #{@net_udev[name]}"
48
+ # @param [Hash] args Arguments for 1st stage
49
+ def execute_autoinstall(vm, args)
50
+ vm.config.push!
51
+ begin
52
+ vm.config.configure(args)
53
+ media = Vmit::VFS.from(location)
54
+ kernel_append_arg = case media
55
+ when Vmit::VFS::URI then "install=#{location}"
56
+ when Vmit::VFS::ISO then 'install=cdrom'
57
+ else raise ArgumentError.new("Unsupported autoinstallation: #{location}")
58
+ end
59
+ vm.config.add_kernel_cmdline!(kernel_append_arg)
60
+
61
+ if media.is_a?(Vmit::VFS::ISO)
62
+ vm.config.cdrom = location.to_s
63
+ end
64
+
65
+ Dir.mktmpdir do |floppy_dir|
66
+ FileUtils.chmod_R 0775, floppy_dir
67
+ vm.config.floppy = floppy_dir
68
+ #vm.config.add_kernel_cmdline!('autoyast=device://fd0/autoinst.xml')
69
+ vm.config.add_kernel_cmdline!('autoyast=floppy')
70
+ vm.config.reboot = false
71
+
72
+ # WTF SLE and openSUSE have different
73
+ # base pattern names
74
+ #media.open('/content') do |content_file|
75
+ # content_file.each_line do |line|
76
+ # case line
77
+ # when /^DISTRIBUTION (.+)$/
78
+ # case $1
79
+ # when /SUSE_SLE/ then autoyast.minimal_sle!
80
+ # when /openSUSE/ then autoyast.minimal_opensuse!
81
+ # end
82
+ # end
83
+ # end
84
+ #end
85
+
86
+ File.write(File.join(floppy_dir, 'autoinst.xml'), to_xml)
87
+ Vmit.logger.info "AutoYaST: 1st stage."
88
+ puts vm.config.inspect
89
+ vm.up
90
+ vm.wait_until_shutdown! do
91
+ vm.vnc
92
+ end
93
+ vm.config.pop!
94
+
95
+ Vmit.logger.info "AutoYaST: 2st stage."
96
+ # 2nd stage
97
+ vm.config.push!
98
+ vm.config.configure(:reboot => false)
99
+ vm.up
100
+ vm.wait_until_shutdown! do
101
+ vm.vnc
102
+ end
103
+
104
+ end
105
+ ensure
106
+ vm.config.pop!
52
107
  end
53
- @net_udev[mac] = name
54
108
  end
55
109
 
56
110
  def to_xml
@@ -87,10 +141,15 @@ module Vmit
87
141
  }
88
142
  xml.software {
89
143
  xml.patterns('config:type' => 'list') {
90
- @patterns.each do |pat|
144
+ config.patterns.each do |pat|
91
145
  xml.pattern pat
92
146
  end
93
147
  }
148
+ xml.packages('config:type' => 'list') {
149
+ config.packages.each do |pkg|
150
+ xml.package pkg
151
+ end
152
+ }
94
153
  }
95
154
  # SLE 11 can do without this basic partitioning but
96
155
  # SLE 10 is not that smart.
@@ -19,23 +19,54 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  require 'erb'
22
+ require 'vmit/unattended_install'
22
23
 
23
24
  module Vmit
24
25
 
25
26
  # Some good references here:
26
27
  # http://www.hps.com/~tpg/notebook/autoinstall.php
27
- class DebianPreseed
28
+ class DebianPreseed < UnattendedInstall
29
+
30
+ def execute_autoinstall(vm, args)
31
+ vm.config.push!
32
+ begin
33
+ vm.config.configure(args)
34
+ Dir.mktmpdir do |floppy_dir|
35
+ FileUtils.chmod_R 0755, floppy_dir
36
+ vm.config.floppy = floppy_dir
37
+ vm.config.add_kernel_cmdline!('preseed/file=/floppy/preseed.cfg')
38
+ vm.config.add_kernel_cmdline!('auto=true')
39
+ vm.config.add_kernel_cmdline!('priority=critical')
40
+ vm.config.reboot = false
41
+
42
+ File.write(File.join(floppy_dir, 'preseed.cfg'), to_txt)
43
+ Vmit.logger.info "Preseed: 1st stage."
44
+ vm.up
45
+ vm.wait_until_shutdown! do
46
+ vm.vnc
47
+ end
48
+ end
49
+ ensure
50
+ vm.config.pop!
51
+ end
52
+ end
28
53
 
29
54
  def to_txt
30
55
  template = ERB.new <<-EOF
56
+ d-i debconf/priority select critical
57
+ d-i auto-install/enabled boolean true
31
58
  d-i debian-installer/locale string en_US
32
59
  d-i console-tools/archs select at
33
60
  d-i console-keymaps-at/keymap select American English
61
+
34
62
  d-i debian-installer/keymap string us
63
+
64
+ d-i netcfg/choose_interface select auto
35
65
  d-i netcfg/get_hostname string unassigned-hostname
36
66
  d-i netcfg/get_hostname seen true
37
67
  d-i netcfg/get_domain string unassigned-domain
38
68
  d-i netcfg/get_domain seen true
69
+
39
70
  d-i mirror/protocol string ftp
40
71
  d-i mirror/ftp/hostname string ftp.de.debian.org
41
72
  d-i mirror/ftp/directory string /debian/
@@ -51,7 +82,10 @@ d-i clock-setup/utc boolean true
51
82
  d-i time/zone string US/Eastern
52
83
  d-i clock-setup/ntp boolean true
53
84
  popularity-contest popularity-contest/participate boolean false
85
+
54
86
  d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop
87
+
88
+ d-i passwd/root-login boolean true
55
89
  d-i passwd/root-password password linux
56
90
  d-i passwd/root-password-again password linux
57
91
  d-i passwd/make-user boolean false
@@ -0,0 +1,224 @@
1
+ #
2
+ # Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ #
21
+ require 'vmit/utils'
22
+ require 'vmit/vfs'
23
+ require 'confstruct'
24
+
25
+ module Vmit
26
+
27
+ class InstallMedia
28
+ abstract_method :unattended_install_class
29
+ abstract_method :kernel_path
30
+ abstract_method :initrd_path
31
+
32
+ attr_reader :location
33
+
34
+ def initialize(location)
35
+ @location = location
36
+ end
37
+
38
+ def to_s
39
+ "#{super.to_s}:#{location}"
40
+ end
41
+
42
+ def unattended_install
43
+ unless @unattended_install
44
+ @unattended_install = unattended_install_class.new(location)
45
+ end
46
+ @unattended_install
47
+ end
48
+
49
+ def self.class_for_media_type(type)
50
+ case type.to_s.downcase
51
+ when /fedora|redhat|centos/ then FedoraInstallMedia
52
+ when /suse/ then SUSEInstallMedia
53
+ when /debian/ then DebianInstallMedia
54
+ else
55
+ raise "Don't know how to bootstrap media #{location}"
56
+ end
57
+ end
58
+
59
+ # @return [InstallMedia] an install media for an arbitrary
60
+ # string like 'sles 11 sp2' or 'SLED-11_SP3'
61
+ #
62
+ # @raise [ArgumentError] if can't figure out an install media
63
+ # from the string
64
+ def self.alias_to_install_media(key)
65
+ case key.to_s.downcase.gsub(/[\s_\-]+/, '')
66
+ when /^opensuse(\d+\.\d+)$/
67
+ SUSEInstallMedia.new(
68
+ 'http://download.opensuse.org/distribution/$version/repo/oss/'
69
+ .gsub('$version', $1))
70
+ when /^(opensuse)?factory$/
71
+ SUSEInstallMedia.new(
72
+ 'http://download.opensuse.org/factory/repo/oss/')
73
+ when /^debian(.+)$/
74
+ DebianInstallMedia.new(
75
+ 'http://cdn.debian.net/debian/dists/$version'
76
+ .gsub('$version', $1))
77
+ when /^ubuntu(.+)$/
78
+ UbuntuInstallMedia.new(
79
+ 'http://archive.ubuntu.com/ubuntu/dists/$version'
80
+ .gsub('$version', $1))
81
+ when /^fedora(\d+)/
82
+ FedoraInstallMedia.new(
83
+ 'http://mirrors.n-ix.net/fedora/linux/releases/$release/Fedora/$arch/os/'
84
+ .gsub('$arch', Vmit::Utils.arch)
85
+ .gsub('$release', $1))
86
+ when /^sle(s|d)?(\d+)(sp(\d+))?$/
87
+ edition = case $1
88
+ when 's' then 'sle-server'
89
+ when 'd' then 'sle-desktop'
90
+ else
91
+ Vmit.logger.warn "SLE given. Assuming server."
92
+ 'sle-server'
93
+ end
94
+ release = $2
95
+ sp = $4 || '0'
96
+ klass = if release.to_i > 9
97
+ SUSEInstallMedia
98
+ else
99
+ SUSE9InstallMedia
100
+ end
101
+ suffix = if (release.to_i > 9)
102
+ '/DVD1'
103
+ else
104
+ if (sp.to_i > 0)
105
+ '/CD1'
106
+ else
107
+ ''
108
+ end
109
+ end
110
+ klass.new(
111
+ "http://schnell.suse.de/BY_PRODUCT/$edition-$release-sp$sp-$arch$topdir"
112
+ .gsub('$edition', edition)
113
+ .gsub('$arch', Vmit::Utils.arch)
114
+ .gsub('$release', release)
115
+ .gsub('$sp', sp)
116
+ .gsub('$topdir', suffix))
117
+ else raise ArgumentError.new("Unknown install media '#{key}'")
118
+ end
119
+ end
120
+
121
+ # @return [InstallMedia] an install media for a url.
122
+ # it accesses the network in order to detect the
123
+ # url type.
124
+ #
125
+ # @raise [ArgumentError] if can't figure out an install media
126
+ # from the string
127
+ def self.url_to_install_media(url)
128
+ begin
129
+ media = Vmit::VFS.from(url)
130
+ media.open('/content')
131
+ return SUSEInstallMedia.new(url)
132
+ rescue
133
+ raise ArgumentError.new("Don't know the install media '#{url}'")
134
+ end
135
+ end
136
+
137
+ # @return [InstallMedia] scans the install media
138
+ # and returns a specific type (suse, debian, etc...)
139
+ def self.scan(key)
140
+ case key
141
+ when /^http:\/|ftp:\// then url_to_install_media(key)
142
+ else alias_to_install_media(key)
143
+ end
144
+ end
145
+
146
+ def autoinstall(vm)
147
+ Vmit.logger.debug("Autoinstall from #{location}")
148
+ media = Vmit::VFS.from(location)
149
+ kernel = media.open(kernel_path)
150
+ initrd = media.open(initrd_path)
151
+ opts = {:kernel => kernel.path, :initrd => initrd.path}
152
+ unattended_install.execute_autoinstall(vm, opts)
153
+ end
154
+
155
+ end
156
+
157
+ class SUSEInstallMedia < InstallMedia
158
+
159
+ def unattended_install_class
160
+ Vmit::AutoYaST
161
+ end
162
+
163
+ def initrd_path
164
+ "/boot/#{Vmit::Utils.arch}/loader/initrd"
165
+ end
166
+
167
+ def kernel_path
168
+ "/boot/#{Vmit::Utils.arch}/loader/linux"
169
+ end
170
+ end
171
+
172
+ class SUSE9InstallMedia < SUSEInstallMedia
173
+ def initrd_path
174
+ "/boot/loader/initrd"
175
+ end
176
+
177
+ def kernel_path
178
+ "/boot/loader/linux"
179
+ end
180
+ end
181
+
182
+ class FedoraInstallMedia < InstallMedia
183
+
184
+ def unattended_install_class
185
+ Vmit::Kickstart
186
+ end
187
+
188
+ def initrd_path
189
+ '/images/pxeboot/initrd.img'
190
+ end
191
+
192
+ def kernel_path
193
+ '/images/pxeboot/vmlinuz'
194
+ end
195
+ end
196
+
197
+ class DebianInstallMedia < InstallMedia
198
+
199
+ def name
200
+ 'debian'
201
+ end
202
+
203
+ def unattended_install_class
204
+ Vmit::DebianPreseed
205
+ end
206
+
207
+ def initrd_path
208
+ arch = Vmit::Utils.arch.gsub(/x86_64/, 'amd64')
209
+ "/main/installer-#{arch}/current/images/netboot/#{name}-installer/#{arch}/initrd.gz"
210
+ end
211
+
212
+ def kernel_path
213
+ arch = Vmit::Utils.arch.gsub(/x86_64/, 'amd64')
214
+ "/main/installer-#{arch}/current/images/netboot/#{name}-installer/#{arch}/linux"
215
+ end
216
+ end
217
+
218
+ class UbuntuInstallMedia < DebianInstallMedia
219
+ def name
220
+ 'ubuntu'
221
+ end
222
+ end
223
+
224
+ end