vagrant-vbguest-update 0.10.1.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +209 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE +21 -0
  6. data/Rakefile +11 -0
  7. data/Readme.md +267 -0
  8. data/lib/vagrant-vbguest.rb +57 -0
  9. data/lib/vagrant-vbguest/command.rb +95 -0
  10. data/lib/vagrant-vbguest/config.rb +51 -0
  11. data/lib/vagrant-vbguest/core_ext/string/interpolate.rb +110 -0
  12. data/lib/vagrant-vbguest/download.rb +28 -0
  13. data/lib/vagrant-vbguest/errors.rb +18 -0
  14. data/lib/vagrant-vbguest/hosts/base.rb +118 -0
  15. data/lib/vagrant-vbguest/hosts/virtualbox.rb +103 -0
  16. data/lib/vagrant-vbguest/installer.rb +121 -0
  17. data/lib/vagrant-vbguest/installers/base.rb +201 -0
  18. data/lib/vagrant-vbguest/installers/debian.rb +40 -0
  19. data/lib/vagrant-vbguest/installers/linux.rb +158 -0
  20. data/lib/vagrant-vbguest/installers/redhat.rb +29 -0
  21. data/lib/vagrant-vbguest/installers/ubuntu.rb +44 -0
  22. data/lib/vagrant-vbguest/machine.rb +105 -0
  23. data/lib/vagrant-vbguest/middleware.rb +43 -0
  24. data/lib/vagrant-vbguest/rebootable.rb +39 -0
  25. data/lib/vagrant-vbguest/vagrant_compat.rb +21 -0
  26. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_0/command.rb +17 -0
  27. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_0/download.rb +51 -0
  28. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_0/rebootable.rb +23 -0
  29. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_0/vm_compatible.rb +31 -0
  30. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_1/command.rb +18 -0
  31. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_1/download.rb +1 -0
  32. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_1/rebootable.rb +33 -0
  33. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_1/vm_compatible.rb +31 -0
  34. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_2/command.rb +1 -0
  35. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_2/download.rb +18 -0
  36. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_2/rebootable.rb +1 -0
  37. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_2/vm_compatible.rb +15 -0
  38. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_3/command.rb +1 -0
  39. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_3/download.rb +1 -0
  40. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_3/rebootable.rb +1 -0
  41. data/lib/vagrant-vbguest/vagrant_compat/vagrant_1_3/vm_compatible.rb +1 -0
  42. data/lib/vagrant-vbguest/version.rb +3 -0
  43. data/lib/vagrant_init.rb +38 -0
  44. data/locales/en.yml +63 -0
  45. data/vagrant-vbguest.gemspec +30 -0
  46. metadata +161 -0
@@ -0,0 +1,103 @@
1
+ module VagrantVbguest
2
+ module Hosts
3
+ class VirtualBox < Base
4
+
5
+ protected
6
+
7
+ # Default web URI, where GuestAdditions iso file can be downloaded.
8
+ #
9
+ # @return [String] A URI template containing the versions placeholder.
10
+ def web_path
11
+ "http://download.virtualbox.org/virtualbox/%{version}/VBoxGuestAdditions_%{version}.iso"
12
+ end
13
+
14
+
15
+ # Finds GuestAdditions iso file on the host system.
16
+ # Returns +nil+ if none found.
17
+ #
18
+ # @return [String] Absolute path to the local GuestAdditions iso file, or +nil+ if not found.
19
+ def local_path
20
+ media_manager_iso || guess_local_iso
21
+ end
22
+
23
+ # Kicks off +VagrantVbguest::Download+ to download the additions file
24
+ # into a temp file.
25
+ #
26
+ # To remove the created tempfile call +cleanup+
27
+ #
28
+ # @param [String] The path or URI to download
29
+ #
30
+ # @return [String] The path to the downloaded file
31
+ def download(path)
32
+ temp_path = File.join(@env.tmp_path, "VBoxGuestAdditions_#{version}.iso")
33
+ @download = VagrantVbguest::Download.new(path, temp_path, :ui => @env.ui)
34
+ @download.download!
35
+ @download.destination
36
+ end
37
+
38
+ private
39
+
40
+ # Helper method which queries the VirtualBox media manager
41
+ # for the first existing path that looks like a
42
+ # +VBoxGuestAdditions.iso+ file.
43
+ #
44
+ # @return [String] Absolute path to the local GuestAdditions iso file, or +nil+ if not found.
45
+ def media_manager_iso
46
+ driver.execute('list', 'dvds').scan(/^.+:\s+(.*VBoxGuestAdditions(?:_#{version})?\.iso)$/i).map { |path, _|
47
+ path if File.exist?(path)
48
+ }.compact.first
49
+ end
50
+
51
+ # Find the first GuestAdditions iso file which exists on the host system
52
+ #
53
+ # @return [String] Absolute path to the local GuestAdditions iso file, or +nil+ if not found.
54
+ def guess_local_iso
55
+ Array(platform_path).find do |path|
56
+ path && File.exists?(path)
57
+ end
58
+ end
59
+
60
+ # Makes an educated guess where the GuestAdditions iso file
61
+ # could be found on the host system depending on the OS.
62
+ # Returns +nil+ if no the file is not in it's place.
63
+ def platform_path
64
+ [:linux, :darwin, :cygwin, :windows].each do |sys|
65
+ return self.send("#{sys}_path") if Vagrant::Util::Platform.respond_to?("#{sys}?") && Vagrant::Util::Platform.send("#{sys}?")
66
+ end
67
+ nil
68
+ end
69
+
70
+ # Makes an educated guess where the GuestAdditions iso file
71
+ # on linux based systems
72
+ def linux_path
73
+ paths = ["/usr/share/virtualbox/VBoxGuestAdditions.iso"]
74
+ paths.unshift(File.join(ENV['HOME'], '.VirtualBox', "VBoxGuestAdditions_#{version}.iso")) if ENV['HOME']
75
+ paths
76
+ end
77
+
78
+ # Makes an educated guess where the GuestAdditions iso file
79
+ # on Macs
80
+ def darwin_path
81
+ "/Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso"
82
+ end
83
+
84
+ # Makes an educated guess where the GuestAdditions iso file
85
+ # on windows systems
86
+ def windows_path
87
+ if (p = ENV["VBOX_INSTALL_PATH"]) && !p.empty?
88
+ File.join(p, "VBoxGuestAdditions.iso")
89
+ elsif (p = ENV["PROGRAM_FILES"] || ENV["ProgramW6432"] || ENV["PROGRAMFILES"]) && !p.empty?
90
+ File.join(p, "/Oracle/VirtualBox/VBoxGuestAdditions.iso")
91
+ end
92
+ end
93
+ alias_method :cygwin_path, :windows_path
94
+
95
+ # overwrite the default version string to allow lagacy
96
+ # '$VBOX_VERSION' as a placerholder
97
+ def versionize(path)
98
+ super(path.gsub('$VBOX_VERSION', version))
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,121 @@
1
+ module VagrantVbguest
2
+
3
+ ##
4
+ # Dispatches the installation process to a rigistered
5
+ # Installer implementation.
6
+ class Installer
7
+
8
+ class NoInstallerFoundError < Vagrant::Errors::VagrantError
9
+ error_namespace "vagrant_vbguest.errors.installer"
10
+ error_key "no_installer_for_platform"
11
+ end
12
+
13
+ class << self
14
+
15
+ ##
16
+ # Register an Installer implementation.
17
+ # All Installer classes which wish to get picked automaticly
18
+ # using their `#match?` method have to register.
19
+ # Ad-hoc or small custom Installer meight not need to get
20
+ # registered, but need to get passed as an config option (`installer`)
21
+ #
22
+ # Registration takes a priority which defines how specific
23
+ # the Installer matches a system. Low level installers, like
24
+ # "linux" or "bsd" use a small priority (2), while distribution
25
+ # installers use higher priority (5). Installers matching a
26
+ # specific version of a distribution should use heigher
27
+ # priority numbers.
28
+ #
29
+ # @param [Class] installer_class A reference to the Installer class.
30
+ # @param [Fixnum] prio Priority describing how specific the Installer matches. (default: `5`)
31
+ def register(installer_class, prio = 5)
32
+ @installers ||= {}
33
+ @installers[prio] ||= []
34
+ @installers[prio] << installer_class
35
+ end
36
+
37
+ ##
38
+ # Returns the class of the registrated Installer class which
39
+ # matches first (according to it's priority) or `nil` if none matches.
40
+ def detect(vm, options)
41
+ @installers.keys.sort.reverse.each do |prio|
42
+ klass = @installers[prio].detect { |k| k.match?(vm) }
43
+ return klass if klass
44
+ end
45
+ return nil
46
+ end
47
+ end
48
+
49
+ def initialize(vm, options = {})
50
+ @vm = vm
51
+ @env = vm.env
52
+ @options = options
53
+ @iso_path = nil
54
+ end
55
+
56
+ def install
57
+ installer = guest_installer
58
+ raise NoInstallerFoundError, :method => 'install' if !installer
59
+
60
+ installer.install do |type, data|
61
+ @env.ui.info(data, :prefix => false, :new_line => false)
62
+ end
63
+ ensure
64
+ cleanup
65
+ end
66
+
67
+ def rebuild
68
+ installer = guest_installer
69
+ raise NoInstallerFoundError, :method => 'rebuild' if !installer
70
+
71
+ installer.rebuild do |type, data|
72
+ @env.ui.info(data, :prefix => false, :new_line => false)
73
+ end
74
+ end
75
+
76
+ def start
77
+ installer = guest_installer
78
+ raise NoInstallerFoundError, :method => 'manual start' if !installer
79
+
80
+ installer.start do |type, data|
81
+ @env.ui.info(data, :prefix => false, :new_line => false)
82
+ end
83
+ end
84
+
85
+ def guest_version(reload=false)
86
+ installer = guest_installer
87
+ raise NoInstallerFoundError, :method => 'check guest version of' if !installer
88
+ installer.guest_version(reload)
89
+ end
90
+
91
+ def host_version
92
+ installer = guest_installer
93
+ raise NoInstallerFoundError, :method => 'check host version of' if !installer
94
+ installer.host.version
95
+ end
96
+
97
+ def running?
98
+ installer = guest_installer
99
+ raise NoInstallerFoundError, :method => 'check current state of' if !installer
100
+ installer.running?
101
+ end
102
+
103
+ # Returns an installer instance for the current vm
104
+ # This is either the one configured via `installer` option or
105
+ # detected from all registered installers (see {Installer.detect})
106
+ #
107
+ # @return [Installers::Base]
108
+ def guest_installer
109
+ @guest_installer ||= if @options[:installer].is_a? Class
110
+ @options[:installer].new(@vm, @options)
111
+ else
112
+ installer_klass = Installer.detect(@vm, @options) and installer_klass.new(@vm, @options)
113
+ end
114
+ end
115
+
116
+ def cleanup
117
+ @guest_installer.cleanup if @guest_installer
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,201 @@
1
+ module VagrantVbguest
2
+ module Installers
3
+ class Error < Vagrant::Errors::VagrantError
4
+ error_namespace "vagrant_vbguest.errors.installer"
5
+ end
6
+
7
+ # This is the base class all installers must inherit from
8
+ # It defines the basic structure of an Installer and should
9
+ # never be used directly
10
+ class Base
11
+ include VagrantVbguest::Helpers::VmCompatible
12
+
13
+ # Tests whether this installer class is applicable to the
14
+ # current environment. Usually, testing for a specific OS.
15
+ # Subclasses must override this method and return `true` if
16
+ # they wish to handle.
17
+ #
18
+ # This method will be called only when an Installer detection
19
+ # is run. It is ignored, when passing an Installer class
20
+ # directly as an config (`installer`) option.
21
+ #
22
+ # @param [Vagrant::VM]
23
+ # @return [Boolean]
24
+ def self.match?(vm)
25
+ false
26
+ end
27
+
28
+ attr_reader :env, :vm, :options, :host
29
+
30
+ def initialize(vm, options=nil)
31
+ @vm = vm
32
+ @env = vm.env
33
+ @options = options
34
+
35
+ @host = VagrantVbguest::Hosts::VirtualBox.new(vm, options)
36
+ end
37
+
38
+ # The absolute file path of the GuestAdditions iso file should
39
+ # be uploaded into the guest.
40
+ # Subclasses must override this method!
41
+ #
42
+ # @return [String]
43
+ def tmp_path
44
+ end
45
+
46
+ # The mountpoint path
47
+ # Subclasses shall override this method, if they need to mount the uploaded file!
48
+ #
49
+ # @return [String]
50
+ def mount_point
51
+ end
52
+
53
+ # Handles the installation process.
54
+ # All necessary steps for an installation must be defined here.
55
+ # This includes uploading the iso into the box, mounting,
56
+ # installing and cleaning up.
57
+ # The path to the local iso file should be obtained by calling +iso_file+
58
+ # Subclasses must override this method!
59
+ #
60
+ # @param [Hash] opts Optional options Hash wich meight get passed to {Vagrant::Communication::SSH#execute} and firends
61
+ # @yield [type, data] Takes a Block like {Vagrant::Communication::Base#execute} for realtime output of the command being executed
62
+ # @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc.
63
+ # @yieldparam [String] data Data for the given output.
64
+ def install(opts=nil, &block)
65
+ end
66
+
67
+ # Handels the rebuild of allready running GuestAdditions
68
+ # It may happen, that the guest has the correct GuestAdditions
69
+ # version running, but not the kernel module is not running.
70
+ # This method should perform a rebuild or try to reload the
71
+ # kernel module _without_ the GuestAdditions iso file.
72
+ # If there is no way of rebuidling or reloading the
73
+ # GuestAdditions on a specific system, this method should left
74
+ # empty.
75
+ # Subclasses should override this method.
76
+ #
77
+ # @param [Hash] opts Optional options Hash wich meight get passed to {Vagrant::Communication::SSH#execute} and firends
78
+ # @yield [type, data] Takes a Block like {Vagrant::Communication::Base#execute} for realtime output of the command being executed
79
+ # @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc.
80
+ # @yieldparam [String] data Data for the given output.
81
+ def rebuild(opts=nil, &block)
82
+ end
83
+
84
+ # Restarts the allready installed GuestAdditions
85
+ # It may happen, that the guest has the correct GuestAdditions
86
+ # version installed, but for some reason are not (yet) runnig.
87
+ # This method should execute the GuestAdditions system specific
88
+ # init script in order to start it manually.
89
+ # If there is no way of doing this on a specific system,
90
+ # this method should left empty.
91
+ # Subclasses should override this method.
92
+ #
93
+ # @param [Hash] opts Optional options Hash wich meight get passed to {Vagrant::Communication::SSH#execute} and firends
94
+ # @yield [type, data] Takes a Block like {Vagrant::Communication::Base#execute} for realtime output of the command being executed
95
+ # @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc.
96
+ # @yieldparam [String] data Data for the given output.
97
+ def start(opts=nil, &block)
98
+ end
99
+
100
+ # Determinates if the GuestAdditions kernel module is loaded.
101
+ # This method tests if there is a working GuestAdditions
102
+ # kernel module. If there is none, {#rebuild} is beeing called.
103
+ # If there is no way of telling if there is a working
104
+ # GuestAddition for a specific system, this method should
105
+ # return `true`.
106
+ # Subclasses should override this method.
107
+ #
108
+ # @return [Boolean] `true` if the kernel module is loaded (and thus seems to work), `false` otherwise.
109
+ def running?(opts=nil, &block)
110
+ true
111
+ end
112
+
113
+ # Determinates the GuestAdditions version installed on the
114
+ # guest system.
115
+ #
116
+ # @param [Boolean] reload Whether to read the value again or use
117
+ # the cached value form an erlier call.
118
+ # @return [String] The version code of the VirtualBox Guest Additions
119
+ # available on the guest, or `nil` if none installed.
120
+ def guest_version(reload=false)
121
+ return @guest_version if @guest_version && !reload
122
+
123
+ guest_version = driver.read_guest_additions_version
124
+ guest_version = !guest_version ? nil : guest_version.gsub(/[-_]ose/i, '')
125
+
126
+ @guest_version = guest_version
127
+ end
128
+
129
+
130
+ # Determinates the version of the GuestAdditions installer in use
131
+ #
132
+ # @return [String] The version code of the GuestAdditions installer
133
+ def installer_version(path_to_installer)
134
+ version = nil
135
+ communicate.sudo("#{path_to_installer} --info", :error_check => false) do |type, data|
136
+ if (v = data.to_s.match(/\AIdentification.*\s(\d+\.\d+.\d+)/i))
137
+ version = v[1]
138
+ end
139
+ end
140
+ version
141
+ end
142
+
143
+ # Helper to yield a warning message to the user, that the installation
144
+ # will start _now_.
145
+ # The message includes the host and installer version strings.
146
+ def yield_installation_waring(path_to_installer)
147
+ @env.ui.warn I18n.t("vagrant_vbguest.installing#{@options[:force] ? '_forced' : ''}",
148
+ :guest_version => guest_version,
149
+ :installer_version => installer_version(path_to_installer) || I18n.t("vagrant_vbguest.unknown"))
150
+ end
151
+
152
+ # Helper to yield a warning message to the user, that the installation
153
+ # will be rebuild using the installed GuestAdditions.
154
+ # The message includes the host and installer version strings.
155
+ def yield_rebuild_warning
156
+ @env.ui.warn I18n.t("vagrant_vbguest.rebuild#{@options[:force] ? '_forced' : ''}",
157
+ :guest_version => guest_version(true),
158
+ :host_version => @host.version)
159
+ end
160
+
161
+ # Helper to yield a warning message to the user in the event that the
162
+ # installer returned a non-zero exit status. Because lack of a window
163
+ # system will cause this result in VirtualBox 4.2.8+, we don't want to
164
+ # kill the entire boot process, but we do want to make sure the user
165
+ # knows there could be a problem. The message includles the installer
166
+ # version.
167
+ def yield_installation_error_warning(path_to_installer)
168
+ @env.ui.warn I18n.t("vagrant_vbguest.install_error",
169
+ :installer_version => installer_version(path_to_installer) || I18n.t("vagrant_vbguest.unknown"))
170
+ end
171
+
172
+ def iso_file
173
+ @host.additions_file
174
+ end
175
+ alias_method :additions_file, :iso_file
176
+
177
+ # A helper method to handle the GuestAdditions iso file upload
178
+ # into the guest box.
179
+ # The file will uploaded to the location given by the +temp_path+ method.
180
+ #
181
+ # @example Default upload
182
+ # upload(file)
183
+ #
184
+ # @param [String] Path of the file to upload to the +tmp_path*
185
+ def upload(file)
186
+ env.ui.info(I18n.t("vagrant_vbguest.start_copy_iso", :from => file, :to => tmp_path))
187
+ communicate.upload(file, tmp_path)
188
+ end
189
+
190
+ # A helper method to delete the uploaded GuestAdditions iso file
191
+ # from the guest box
192
+ def cleanup
193
+ @host.cleanup
194
+ communicate.execute("test -f #{tmp_path} && rm #{tmp_path}", :error_check => false) do |type, data|
195
+ env.ui.error(data.chomp, :prefix => false)
196
+ end
197
+ end
198
+
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,40 @@
1
+ module VagrantVbguest
2
+ module Installers
3
+ class Debian < Linux
4
+
5
+ def self.match?(vm)
6
+ :debian == self.distro(vm)
7
+ end
8
+
9
+ # installes the correct linux-headers package
10
+ # installes `dkms` package for dynamic kernel module loading
11
+ # @param [Hash] opts Optional options Hash wich meight get passed to {Vagrant::Communication::SSH#execute} and firends
12
+ # @yield [type, data] Takes a Block like {Vagrant::Communication::Base#execute} for realtime output of the command being executed
13
+ # @yieldparam [String] type Type of the output, `:stdout`, `:stderr`, etc.
14
+ # @yieldparam [String] data Data for the given output.
15
+ def install(opts=nil, &block)
16
+ begin
17
+ communicate.sudo(install_dependencies_cmd, opts, &block)
18
+ rescue
19
+ communicate.sudo('apt-get update', opts, &block)
20
+ communicate.sudo(install_dependencies_cmd, opts, &block)
21
+ end
22
+ super
23
+ end
24
+
25
+ protected
26
+ def install_dependencies_cmd
27
+ "apt-get install -y #{dependencies}"
28
+ end
29
+
30
+ def dependencies
31
+ packages = ['linux-headers-`uname -r`']
32
+ # some Debian system (lenny) dont come with a dkms packe so we neet to skip that.
33
+ # apt-cache search will exit with 0 even if nothing was found, so we need to grep.
34
+ packages << 'dkms' if communicate.test('apt-cache search --names-only \'^dkms$\' | grep dkms')
35
+ packages.join ' '
36
+ end
37
+ end
38
+ end
39
+ end
40
+ VagrantVbguest::Installer.register(VagrantVbguest::Installers::Debian, 5)