diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f967fdf --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +--- +language: ruby +before_install: + - sudo apt-get update -qq + - sudo apt-get install -y libvirt-dev + - gem install bundler --version $BUNDLER_VERSION +install: bundle _${BUNDLER_VERSION}_ install +script: bundle _${BUNDLER_VERSION}_ exec rspec --color --format documentation +notifications: + email: false +rvm: + - 2.0.0 +env: + global: + - NOKOGIRI_USE_SYSTEM_LIBRARIES=true + matrix: + - VAGRANT_VERSION=v1.5.4 BUNDLER_VERSION=1.5.3 + - VAGRANT_VERSION=v1.6.5 BUNDLER_VERSION=1.6.9 + - VAGRANT_VERSION=v1.7.0 BUNDLER_VERSION=1.7.9 + - VAGRANT_VERSION= BUNDLER_VERSION=1.7.9 diff --git a/Gemfile b/Gemfile index 1c69fb4..4e4fd9d 100644 --- a/Gemfile +++ b/Gemfile @@ -7,10 +7,16 @@ group :development do # We depend on Vagrant for development, but we don't add it as a # gem dependency because we expect to be installed within the # Vagrant environment itself using `vagrant plugin`. - gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git" + if ENV['VAGRANT_VERSION'] + gem 'vagrant', :git => 'https://github.com/mitchellh/vagrant.git', + tag: ENV['VAGRANT_VERSION'] + else + gem 'vagrant', :git => 'https://github.com/mitchellh/vagrant.git' + end + gem 'pry' end group :plugins do - gem "vagrant-libvirt", :path => '.' + gem 'vagrant-libvirt', :path => '.' end diff --git a/README.md b/README.md index 74efc64..ce65e4b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ welcome and can help a lot :-) ## Features * Control local Libvirt hypervisors. -* Vagrant `up`, `destroy`, `suspend`, `resume`, `halt`, `ssh`, `reload` and `provision` commands. +* Vagrant `up`, `destroy`, `suspend`, `resume`, `halt`, `ssh`, `reload`, `package` and `provision` commands. * Upload box image (qcow2 format) to Libvirt storage pool. * Create volume as COW diff image for domains. * Create private networks. @@ -46,12 +46,19 @@ missing development libraries for libxslt, libxml2 and libvirt. In Ubuntu, Debian, ... ``` -$ sudo apt-get install libxslt-dev libxml2-dev libvirt-dev +$ sudo apt-get install libxslt-dev libxml2-dev libvirt-dev zlib1g-dev ``` In RedHat, Centos, Fedora, ... ``` -# yum install libxslt-devel libxml2-devel libvirt-devel +# yum install libxslt-devel libxml2-devel libvirt-devel libguestfs-tools-c +``` + +If have problem with installation - check your linker. It should be ld.gold: +``` +sudo alternatives --set ld /usr/bin/ld.gold +# OR +sudo ln -fs /usr/bin/ld.gold /usr/bin/ld ``` ## Vagrant Project Preparation @@ -64,10 +71,8 @@ a `config.vm.provider` block. So first, add Libvirt box using any name you want. This is just an example of Libvirt CentOS 6.4 box available: ``` -vagrant box add centos64 http://kwok.cz/centos64.box -``` -or -``` +vagrant box add fedora21 http://citozin.com/fedora21.box +# or vagrant box add centos64 http://citozin.com/centos64.box ``` @@ -96,6 +101,7 @@ $ vagrant up --provider=libvirt Vagrant needs to know that we want to use Libvirt and not default VirtualBox. That's why there is `--provider=libvirt` option specified. Other way to tell Vagrant to use Libvirt provider is to setup environment variable + `export VAGRANT_DEFAULT_PROVIDER=libvirt`. ### How Project Is Created @@ -124,7 +130,7 @@ Although it should work without any configuration for most people, this provider * `connect_via_ssh` - If use ssh tunnel to connect to Libvirt. * `username` - Username and password to access Libvirt. * `password` - Password to access Libvirt. -* `id_ssh_key_file` - The id ssh key file name to access Libvirt (eg: id_dsa or id_rsa or ... in the user .ssh directory) +* `id_ssh_key_file` - If not nil, uses this ssh private key to access Libvirt. Default is $HOME/.ssh/id_rsa. Prepends $HOME/.ssh/ if no directory. * `socket` - Path to the libvirt unix socket (eg: /var/run/libvirt/libvirt-sock) * `uri` - For advanced usage. Directly specifies what libvirt connection URI vagrant-libvirt should use. Overrides all other connection configuration options. @@ -155,7 +161,7 @@ end * `initrd` - To specify the initramfs/initrd to use for the guest. Equivalent to qemu `-initrd`. * `random_hostname` - To create a domain name with extra information on the end to prevent hostname conflicts. * `cmd_line` - Arguments passed on to the guest kernel initramfs or initrd to use. Equivalent to qemu `-append`. -* `graphics_type` - Sets the protocol used to expose the guest display. Defaults to `vnc`. Possible values are "sdl", "curses", "none", "gtk", or "vnc". +* `graphics_type` - Sets the protocol used to expose the guest display. Defaults to `vnc`. Possible values are "sdl", "curses", "none", "gtk", "vnc" or "spice". * `graphics_port` - Sets the port for the display protocol to bind to. Defaults to 5900. * `graphics_ip` - Sets the IP for the display protocol to bind to. Defaults to "127.0.0.0.1". * `graphics_passwd` - Sets the password for the display protocol. Working for vnc and spice. by default working without passsword. @@ -163,6 +169,10 @@ end * `keymap` - Set keymap for vm. default: en-us * `video_vram` - Used by some graphics card types to vary the amount of RAM dedicated to video. Defaults to 9216. * `machine` - Sets machine type. Equivalent to qemu `-machine`. Use `qemu-system-x86_64 -machine help` to get a list of supported machines. +* `machine_arch` - Sets machine architecture. This helps libvirt to determine the correct emulator type. Possible values depend on your version of qemu. For possible values, see which emulator executable `qemu-system-*` your system provides. Common examples are `aarch64`, `alpha`, `arm`, `cris`, `i386`, `lm32`, `m68k`, `microblaze`, `microblazeel`, `mips`, `mips64`, `mips64el`, `mipsel`, `moxie`, `or32`, `ppc`, `ppc64`, `ppcemb`, `s390x`, `sh4`, `sh4eb`, `sparc`, `sparc64`, `tricore`, `unicore32`, `x86_64`, `xtensa`, `xtensaeb`. +* `machine_virtual_size` - Sets the disk size in GB for the machine overriding the default specified in the box. Allows boxes to defined with a minimal size disk by default and to be grown to a larger size at creation time. Will ignore sizes smaller than the size specified by the box metadata. Note that currently there is no support for automatically resizing the filesystem to take advantage of the larger disk. +* `boot` - Change the boot order and enables the boot menu. Possible options are "hd" or "network". Defaults to "hd" with boot menu disabled. When "network" is set first, *all* NICs will be tried before the first disk is tried. +* `nic_adapter_count` - Defaults to '8'. Only use case for increasing this count is for VMs that virtualize switches such as Cumulus Linux. Max value for Cumulus Linux VMs is 33. Specific domain settings can be set for each domain separately in multi-VM @@ -184,10 +194,31 @@ Vagrant.configure("2") do |config| # ... ``` +The following example shows part of a Vagrantfile that enables the VM to +boot from a network interface first and a hard disk second. This could be +used to run VMs that are meant to be a PXE booted machines. + +```ruby +Vagrant.configure("2") do |config| + config.vm.define :pxeclient do |pxeclient| + pxeclient.vm.box = "centos64" + pxeclient.vm.provider :libvirt do |domain| + domain.boot 'network' + domain.boot 'hd' + end + end + + # ... +``` + ## Networks Networking features in the form of `config.vm.network` support private networks -concept. +concept. It supports both the virtual network switch routing types and the point to +point Guest OS to Guest OS setting using TCP tunnel interfaces. + +http://wiki.libvirt.org/page/VirtualNetworking +https://libvirt.org/formatdomain.html#elementsNICSTCP Public Network interfaces are currently implemented using the macvtap driver. The macvtap driver is only available with the Linux Kernel version >= 2.6.24. See the following libvirt @@ -195,14 +226,33 @@ documentation for the details of the macvtap usage. http://www.libvirt.org/formatdomain.html#elementsNICSDirect + An examples of network interface definitions: ```ruby - # Private network + # Private network using virtual network switching config.vm.define :test_vm1 do |test_vm1| test_vm1.vm.network :private_network, :ip => "10.20.30.40" end + # Private network. Point to Point between 2 Guest OS using a TCP tunnel + # Guest 1 + config.vm.define :test_vm1 do |test_vm1| + test_vm1.vm.network :private_network, + :libvirt__tcp_tunnel_type => 'server', + # default is 127.0.0.1 if omitted + # :libvirt__tcp_tunnel_ip => '127.0.0.1', + :libvirt__tcp_tunnel_port => '11111' + + # Guest 2 + config.vm.define :test_vm2 do |test_vm2| + test_vm2.vm.network :private_network, + :libvirt__tcp_tunnel_type => 'client', + # default is 127.0.0.1 if omitted + # :libvirt__tcp_tunnel_ip => '127.0.0.1', + :libvirt__tcp_tunnel_port => '11111' + + # Public Network config.vm.define :test_vm1 do |test_vm1| test_vm1.vm.network :public_network, @@ -241,8 +291,18 @@ starts with 'libvirt__' string. Here is a list of those options: network 'default' is used. * `:libvirt__netmask` - Used only together with `:ip` option. Default is '255.255.255.0'. +* `:libvirt__host_ip` - Adress to use for the host (not guest). + Default is first possible address (after network address). * `:libvirt__dhcp_enabled` - If DHCP will offer addresses, or not. Used only when creating new network. Default is true. +* `:libvirt__dhcp_start` - First address given out via DHCP. + Default is third address in range (after network name and gateway). +* `:libvirt__dhcp_stop` - Last address given out via DHCP. + Default is last possible address in range (before broadcast address). +* `:libvirt__dhcp_bootp_file` - The file to be used for the boot image. + Used only when dhcp is enabled. +* `:libvirt__dhcp_bootp_server` - The server that runs the DHCP server. + Used only when dhcp is enabled.By default is the same host that runs the DHCP server. * `:libvirt__adapter` - Number specifiyng sequence number of interface. * `:libvirt__forward_mode` - Specify one of `veryisolated`, `none`, `nat` or `route` options. This option is used only when creating new network. Mode `none` will create @@ -254,8 +314,18 @@ starts with 'libvirt__' string. Here is a list of those options: * `:libvirt__forward_device` - Name of interface/device, where network should be forwarded (NATed or routed). Used only when creating new network. By default, all physical interfaces are used. +* `:libvirt_tcp_tunnel_type` - Set it to "server" or "client" to enable TCP + tunnel interface configuration. This configuration type uses TCP tunnels to + generate point to point connections between Guests. Useful for Switch VMs like + Cumulus Linux. No virtual switch setting like "libvirt__network_name" applies with TCP + tunnel interfaces and will be ignored if configured. +* `:libvirt_tcp_tunnel_ip` - Sets the source IP of the TCP Tunnel interface. By + default this is `127.0.0.1` +* `:libvirt_tcp_tunnel_port` - Sets the TCP Tunnel interface port that either + the client will connect to, or the server will listen on. * `:mac` - MAC address for the interface. -* `model_type` - parameter specifies the model of the network adapter when you create a domain value by default virtio KVM believe possible values, see the documentation for libvirt +* `:model_type` - parameter specifies the model of the network adapter when you create a domain value by default virtio KVM believe possible values, see the documentation for libvirt + When the option `:libvirt__dhcp_enabled` is to to 'false' it shouldn't matter whether the virtual network contains a DHCP server or not and vagrant-libvirt @@ -301,6 +371,7 @@ You can create and attach additional disks to a VM via `libvirt.storage :file`. * `type` - Type of disk image to create. Defaults to *qcow2*. * `bus` - Type of bus to connect device to. Defaults to *virtio*. * `cache` - Cache mode to use, e.g. `none`, `writeback`, `writethrough` (see the [libvirt documentation for possible values](http://libvirt.org/formatdomain.html#elementsDisks) or [here](https://www.suse.com/documentation/sles11/book_kvm/data/sect1_chapter_book_kvm.html) for a fuller explanation). Defaults to *default*. +* `allow_existing` - Set to true if you want to allow the VM to use a pre-existing disk. This is useful for sharing disks between VMs, e.g. in order to simulate shared SAN storage. Shared disks removed only manualy.If not exists - will created. If exists - using existed. The following example creates two additional disks. @@ -379,6 +450,13 @@ it an setting the type, e.g. config.vm.synced_folder './', '/vagrant', type: 'rsync' + or + + config.vm.synced_folder './', '/vagrant', type: '9p', disabled: false, accessmode: "squash", owner: "vagrant" + +**SECURITY NOTE:** for remote libvirt, nfs synced folders requires a bridged public network interface and you must connect to libvirt via ssh. + + ## Customized Graphics vagrant-libvirt supports customizing the display and video settings of the @@ -408,12 +486,13 @@ The box is a tarball containing: * `Vagrantfile` that does default settings for the provider-specific configuration for this provider. ## Create Box -This script creates a vagrant-libvirt box from a qcow2 file: +To create a vagrant-libvirt box from a qcow2 image, run `create_box.sh` (located in the tools directory): -Usage: +```$ create_box.sh ubuntu14.qcow2``` -```create_box.sh ubuntu14.qcow2``` -Used Packer to create the qcow2 images, templates available at https://github.com/jakobadam/packer-qemu-templates +You can also create a box by using [Packer](https://packer.io). Packer templates for use with vagrant-libvirt are available at https://github.com/jakobadam/packer-qemu-templates. After cloning that project you can build a vagrant-libvirt box by running: + +``` ~/packer-qemu-templates/ubuntu$ packer build ubuntu-14.04-server-amd64-vagrant.json``` ## Development @@ -457,4 +536,3 @@ IMPORTANT NOTE: bundle is crucial. You need to use bundled vagrant. 3. Commit your changes (`git commit -am 'Add some feature'`). 4. Push to the branch (`git push origin my-new-feature`). 5. Create new Pull Request. - diff --git a/lib/vagrant-libvirt.rb b/lib/vagrant-libvirt.rb index 825e5fa..24322c0 100644 --- a/lib/vagrant-libvirt.rb +++ b/lib/vagrant-libvirt.rb @@ -1,5 +1,4 @@ require 'pathname' -require 'vagrant-libvirt/plugin' module VagrantPlugins module ProviderLibvirt @@ -27,3 +26,6 @@ module VagrantPlugins end end end + +# make sure base module class defined before loading plugin +require 'vagrant-libvirt/plugin' diff --git a/lib/vagrant-libvirt/action.rb b/lib/vagrant-libvirt/action.rb index 98479a7..4fba536 100644 --- a/lib/vagrant-libvirt/action.rb +++ b/lib/vagrant-libvirt/action.rb @@ -8,6 +8,13 @@ module VagrantPlugins include Vagrant::Action::Builtin @logger = Log4r::Logger.new('vagrant_libvirt::action') + # remove image from libvirt storage pool + def self.remove_libvirt_image + Vagrant::Action::Builder.new.tap do |b| + b.use RemoveLibvirtImage + end + end + # This action is called to bring the box up from nothing. def self.action_up Vagrant::Action::Builder.new.tap do |b| @@ -24,27 +31,18 @@ module VagrantPlugins b2.use CreateDomain b2.use Provision - b2.use CreateNetworks - b2.use CreateNetworkInterfaces - - b2.use PrepareNFSValidIds b2.use SyncedFolderCleanup b2.use SyncedFolders - - b2.use StartDomain - b2.use WaitTillUp - - b2.use StartDomain - b2.use WaitTillUp - - - - - b2.use ForwardPorts - b2.use PrepareNFSSettings b2.use ShareFolders + b2.use CreateNetworks + b2.use CreateNetworkInterfaces + + b2.use StartDomain + b2.use WaitTillUp + + b2.use ForwardPorts b2.use SetHostname # b2.use SyncFolders else @@ -83,7 +81,6 @@ module VagrantPlugins b3.use SyncedFolderCleanup b3.use SyncedFolders - # Start it.. b3.use StartDomain @@ -91,11 +88,9 @@ module VagrantPlugins # so wait for dhcp lease and store IP into machines data_dir. b3.use WaitTillUp - b3.use ForwardPorts b3.use PrepareNFSSettings b3.use ShareFolders - end end end @@ -147,8 +142,10 @@ module VagrantPlugins # not implemented and looks like not require def self.action_package - lambda do |env| - raise Errors::PackageNotSupported + Vagrant::Action::Builder.new.tap do |b| + b.use ConfigValidate + b.use ConnectLibvirt + b.use PackageDomain end end @@ -159,7 +156,14 @@ module VagrantPlugins b.use ConfigValidate b.use Call, IsCreated do |env, b2| if !env[:result] - b2.use MessageNotCreated + # Try to remove stale volumes anyway + b2.use ConnectLibvirt + b2.use SetNameOfDomain + b2.use RemoveStaleVolume + if !env[:result] + b2.use MessageNotCreated + end + next end @@ -168,6 +172,7 @@ module VagrantPlugins # b2.use PruneNFSExports b2.use DestroyDomain b2.use DestroyNetworks + b2.use ProvisionerCleanup end end end @@ -320,6 +325,7 @@ module VagrantPlugins action_root = Pathname.new(File.expand_path('../action', __FILE__)) autoload :ConnectLibvirt, action_root.join('connect_libvirt') + autoload :PackageDomain, action_root.join('package_domain') autoload :CreateDomain, action_root.join('create_domain') autoload :CreateDomainVolume, action_root.join('create_domain_volume') autoload :CreateNetworkInterfaces, action_root.join('create_network_interfaces') @@ -331,6 +337,7 @@ module VagrantPlugins autoload :HaltDomain, action_root.join('halt_domain') autoload :HandleBoxImage, action_root.join('handle_box_image') autoload :HandleStoragePool, action_root.join('handle_storage_pool') + autoload :RemoveLibvirtImage, action_root.join('remove_libvirt_image') autoload :IsCreated, action_root.join('is_created') autoload :IsRunning, action_root.join('is_running') autoload :IsSuspended, action_root.join('is_suspended') @@ -339,6 +346,8 @@ module VagrantPlugins autoload :MessageNotRunning, action_root.join('message_not_running') autoload :MessageNotSuspended, action_root.join('message_not_suspended') + autoload :RemoveStaleVolume, action_root.join('remove_stale_volume') + autoload :PrepareNFSSettings, action_root.join('prepare_nfs_settings') autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids') autoload :PruneNFSExports, action_root.join('prune_nfs_exports') @@ -362,6 +371,7 @@ module VagrantPlugins autoload :HandleBox, 'vagrant/action/builtin/handle_box' autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders' autoload :SyncedFolderCleanup, 'vagrant/action/builtin/synced_folder_cleanup' + autoload :ProvisionerCleanup, 'vagrant/action/builtin/provisioner_cleanup' end end end diff --git a/lib/vagrant-libvirt/action/connect_libvirt.rb b/lib/vagrant-libvirt/action/connect_libvirt.rb index fd5d9d8..d34f6c5 100644 --- a/lib/vagrant-libvirt/action/connect_libvirt.rb +++ b/lib/vagrant-libvirt/action/connect_libvirt.rb @@ -1,4 +1,4 @@ -require 'fog' +require 'fog/libvirt' require 'log4r' module VagrantPlugins diff --git a/lib/vagrant-libvirt/action/create_domain.rb b/lib/vagrant-libvirt/action/create_domain.rb index d4ad3b9..80fbbba 100644 --- a/lib/vagrant-libvirt/action/create_domain.rb +++ b/lib/vagrant-libvirt/action/create_domain.rb @@ -3,7 +3,6 @@ require 'log4r' module VagrantPlugins module ProviderLibvirt module Action - class CreateDomain include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate @@ -13,15 +12,17 @@ module VagrantPlugins end def _disk_name(name, disk) - return "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name + "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name end def _disks_print(disks) - return disks.collect{ |x| x[:device]+'('+x[:type]+','+x[:size]+')' }.join(', ') + disks.collect do |x| + "#{x[:device]}(#{x[:type]},#{x[:size]})" + end.join(', ') end def _cdroms_print(cdroms) - return cdroms.collect{ |x| x[:dev] }.join(', ') + cdroms.collect { |x| x[:dev] }.join(', ') end def call(env) @@ -33,9 +34,10 @@ module VagrantPlugins @cpus = config.cpus.to_i @cpu_mode = config.cpu_mode @machine_type = config.machine_type + @machine_arch = config.machine_arch @disk_bus = config.disk_bus @nested = config.nested - @memory_size = config.memory.to_i*1024 + @memory_size = config.memory.to_i * 1024 @domain_volume_cache = config.volume_cache @kernel = config.kernel @cmd_line = config.cmd_line @@ -52,6 +54,9 @@ module VagrantPlugins @video_type = config.video_type @video_vram = config.video_vram @keymap = config.keymap + + # Boot order + @boot_order = config.boot_order # Storage @storage_pool_name = config.storage_pool_name @@ -66,17 +71,19 @@ module VagrantPlugins @os_type = 'hvm' - # Get path to domain image. + # Get path to domain image from the storage pool selected. + actual_volumes = env[:libvirt_compute].volumes.all.select do |x| + x.pool_name == @storage_pool_name + end domain_volume = ProviderLibvirt::Util::Collection.find_matching( - env[:libvirt_compute].volumes.all, "#{@name}.img") + actual_volumes,"#{@name}.img") raise Errors::DomainVolumeExists if domain_volume.nil? @domain_volume_path = domain_volume.path # the default storage prefix is typically: /var/lib/libvirt/images/ - storage_prefix = File.dirname(@domain_volume_path)+'/' # steal + storage_prefix = File.dirname(@domain_volume_path) + '/' # steal @disks.each do |disk| - disk[:path] ||= _disk_name(@name, disk) # On volume creation, the element inside @@ -87,19 +94,24 @@ module VagrantPlugins disk[:absolute_path] = storage_prefix + disk[:path] - # make the disk. equivalent to: - # qemu-img create -f qcow2 5g - begin - domain_volume_disk = env[:libvirt_compute].volumes.create( - :name => disk[:name], - :format_type => disk[:type], - :path => disk[:absolute_path], - :capacity => disk[:size], - #:allocation => ?, - :pool_name => @storage_pool_name) - rescue Fog::Errors::Error => e - raise Errors::FogDomainVolumeCreateError, - :error_message => e.message + if env[:libvirt_compute].volumes.select { + |x| x.name == disk[:name] and x.pool_name == @storage_pool_name}.empty? + # make the disk. equivalent to: + # qemu-img create -f qcow2 5g + begin + env[:libvirt_compute].volumes.create( + name: disk[:name], + format_type: disk[:type], + path: disk[:absolute_path], + capacity: disk[:size], + #:allocation => ?, + pool_name: @storage_pool_name) + rescue Fog::Errors::Error => e + raise Errors::FogDomainVolumeCreateError, + error_message: e.message + end + else + disk[:preexisting] = true end end @@ -108,31 +120,40 @@ module VagrantPlugins env[:ui].info(" -- Name: #{@name}") env[:ui].info(" -- Domain type: #{@domain_type}") env[:ui].info(" -- Cpus: #{@cpus}") - env[:ui].info(" -- Memory: #{@memory_size/1024}M") + env[:ui].info(" -- Memory: #{@memory_size / 1024}M") env[:ui].info(" -- Base box: #{env[:machine].box.name}") env[:ui].info(" -- Storage pool: #{@storage_pool_name}") - env[:ui].info(" -- Image: #{@domain_volume_path}") + env[:ui].info(" -- Image: #{@domain_volume_path} (#{env[:box_virtual_size]}G)") env[:ui].info(" -- Volume Cache: #{@domain_volume_cache}") env[:ui].info(" -- Kernel: #{@kernel}") env[:ui].info(" -- Initrd: #{@initrd}") env[:ui].info(" -- Graphics Type: #{@graphics_type}") env[:ui].info(" -- Graphics Port: #{@graphics_port}") env[:ui].info(" -- Graphics IP: #{@graphics_ip}") - env[:ui].info(" -- Graphics Password: #{@graphics_passwd.empty? ? 'Not defined': 'Defined'}") + env[:ui].info(" -- Graphics Password: #{@graphics_passwd.empty? ? 'Not defined' : 'Defined'}") env[:ui].info(" -- Video Type: #{@video_type}") env[:ui].info(" -- Video VRAM: #{@video_vram}") env[:ui].info(" -- Keymap: #{@keymap}") + + @boot_order.each do |device| + env[:ui].info(" -- Boot device: #{device}") + end if @disks.length > 0 env[:ui].info(" -- Disks: #{_disks_print(@disks)}") end + @disks.each do |disk| - env[:ui].info(" -- Disk(#{disk[:device]}): #{disk[:absolute_path]}") + msg = " -- Disk(#{disk[:device]}): #{disk[:absolute_path]}" + msg += " (shared. Remove only manualy)" if disk[:allow_existing] + msg += " Not created - using existed." if disk[:preexisting] + env[:ui].info(msg) end if @cdroms.length > 0 env[:ui].info(" -- CDROMS: #{_cdroms_print(@cdroms)}") end + @cdroms.each do |cdrom| env[:ui].info(" -- CDROM(#{cdrom[:dev]}): #{cdrom[:path]}") end @@ -146,10 +167,9 @@ module VagrantPlugins # existing volume? Use domain creation from template.. begin server = env[:libvirt_compute].servers.create( - :xml => to_xml('domain')) + xml: to_xml('domain')) rescue Fog::Errors::Error => e - raise Errors::FogCreateServerError, - :error_message => e.message + raise Errors::FogCreateServerError, error_message: e.message end # Immediately save the ID since it is created at this point. @@ -158,7 +178,6 @@ module VagrantPlugins @app.call(env) end end - end end end diff --git a/lib/vagrant-libvirt/action/create_domain_volume.rb b/lib/vagrant-libvirt/action/create_domain_volume.rb index 2d536b6..04ff9c8 100644 --- a/lib/vagrant-libvirt/action/create_domain_volume.rb +++ b/lib/vagrant-libvirt/action/create_domain_volume.rb @@ -34,8 +34,8 @@ module VagrantPlugins env[:libvirt_compute].volumes.all, env[:box_volume_name]) @backing_file = box_volume.path - # Virtual size of image. Same as box image size. - @capacity = env[:machine].box.metadata['virtual_size'] #G + # Virtual size of image. Take value worked out by HandleBoxImage + @capacity = env[:box_virtual_size] #G # Create new volume from xml template. Fog currently doesn't support # volume snapshots directly. diff --git a/lib/vagrant-libvirt/action/create_network_interfaces.rb b/lib/vagrant-libvirt/action/create_network_interfaces.rb index 35886ad..88ab821 100644 --- a/lib/vagrant-libvirt/action/create_network_interfaces.rb +++ b/lib/vagrant-libvirt/action/create_network_interfaces.rb @@ -19,6 +19,8 @@ module VagrantPlugins @management_network_name = env[:machine].provider_config.management_network_name config = env[:machine].provider_config @nic_model_type = config.nic_model_type + @nic_adapter_count = config.nic_adapter_count + @boot_order = config.boot_order @app = app end @@ -36,7 +38,6 @@ module VagrantPlugins adapters = [] # Vagrant gives you adapter 0 by default - # Assign interfaces to slots. configured_networks(env, @logger).each do |options| @@ -70,7 +71,6 @@ module VagrantPlugins @mac = iface_configuration.fetch(:mac, false) @model_type = iface_configuration.fetch(:model_type, @nic_model_type) template_name = 'interface' - # Configuration for public interfaces which use the macvtap driver if iface_configuration[:iface_type] == :public_network @device = iface_configuration.fetch(:dev, 'eth0') @@ -80,8 +80,18 @@ module VagrantPlugins template_name = 'public_interface' @logger.info("Setting up public interface using device #{@device} in mode #{@mode}") @ovs = iface_configuration.fetch(:ovs, false) + # configuration for tcp tunnel interfaces (p2p conn btwn guest OSes) + elsif iface_configuration.fetch(:tcp_tunnel_type, nil) + @tcp_tunnel_port = iface_configuration.fetch(:tcp_tunnel_port, nil) + raise Errors::TcpTunnelPortNotDefined if @tcp_tunnel_port.nil? + @tcp_tunnel_ip = iface_configuration.fetch(:tcp_tunnel_address, '127.0.0.1') + @type = iface_configuration.fetch(:tcp_tunnel_type) + @model_type = iface_configuration.fetch(:model_type, @nic_model_type) + template_name = 'tcp_tunnel_interface' + @logger.info("Setting up #{@type} tunnel interface using #{@tcp_tunnel_ip} port #{@tcp_tunnel_port}") end + message = "Creating network interface eth#{@iface_number}" message << " connected to network #{@network_name}." if @mac @@ -96,6 +106,21 @@ module VagrantPlugins raise Errors::AttachDeviceError, :error_message => e.message end + + # Re-read the network configuration and grab the MAC address + if !@mac + xml = Nokogiri::XML(domain.xml_desc) + if iface_configuration[:iface_type] == :public_network + if @type == 'direct' + @mac = xml.xpath("/domain/devices/interface[source[@dev='#{@device}']]/mac/@address") + else + @mac = xml.xpath("/domain/devices/interface[source[@bridge='#{@device}']]/mac/@address") + end + else + @mac = xml.xpath("/domain/devices/interface[source[@network='#{@network_name}']]/mac/@address") + end + iface_configuration[:mac] = @mac.to_s + end end # Continue the middleware chain. @@ -116,7 +141,7 @@ module VagrantPlugins network = { :interface => slot_number, :use_dhcp_assigned_default_route => options[:use_dhcp_assigned_default_route], - #:mac => ..., + :mac_address => options[:mac], } if options[:ip] @@ -129,6 +154,9 @@ module VagrantPlugins network[:type] = :dhcp end + # do not run configure_networks for tcp tunnel interfaces + next if options.fetch(:tcp_tunnel_type, nil) + networks_to_configure << network end @@ -139,7 +167,7 @@ module VagrantPlugins private - def find_empty(array, start=0, stop=8) + def find_empty(array, start=0, stop=@nic_adapter_count) (start..stop).each do |i| return i if !array[i] end @@ -148,6 +176,9 @@ module VagrantPlugins # Return network name according to interface options. def interface_network(libvirt_client, options) + # no need to get interface network for tcp tunnel config + return 'tcp_tunnel' if options.fetch(:tcp_tunnel_type, nil) + if options[:network_name] @logger.debug "Found network by name" return options[:network_name] diff --git a/lib/vagrant-libvirt/action/create_networks.rb b/lib/vagrant-libvirt/action/create_networks.rb index 7ef4643..b8cb08d 100644 --- a/lib/vagrant-libvirt/action/create_networks.rb +++ b/lib/vagrant-libvirt/action/create_networks.rb @@ -35,7 +35,8 @@ module VagrantPlugins # available, create it if possible. Otherwise raise an error. configured_networks(env, @logger).each do |options| # Only need to create private networks - next if options[:iface_type] != :private_network + next if options[:iface_type] != :private_network or + options.fetch(:tcp_tunnel_type, nil) @logger.debug "Searching for network with options #{options}" # should fix other methods so this doesn't have to be instance var @@ -44,7 +45,8 @@ module VagrantPlugins # Get a list of all (active and inactive) libvirt networks. This # list is used throughout this class and should be easier to # process than libvirt API calls. - @available_networks = libvirt_networks(env[:libvirt_compute].client) + @available_networks = libvirt_networks( + env[:libvirt_compute].client) # Prepare a hash describing network for this specific interface. @interface_network = { @@ -56,19 +58,16 @@ module VagrantPlugins created: false, active: false, autostart: false, - libvirt_network: nil, + libvirt_network: nil } if @options[:ip] handle_ip_option(env) - # in vagrant 1.2.3 and later it is not possible to take this branch - # because cannot have name without ip - # https://github.com/mitchellh/vagrant/commit/cf2f6da4dbcb4f57c9cdb3b94dcd0bba62c5f5fd elsif @options[:network_name] - handle_network_name_option + handle_network_name_option(env) end - autostart_network if !@interface_network[:autostart] + autostart_network if @interface_network[:autostart] activate_network if !@interface_network[:active] end end @@ -78,11 +77,25 @@ module VagrantPlugins private + def lookup_network_by_ip(ip) + @logger.debug "looking up network with ip == #{ip}" + @available_networks.each do |network| + if network[:network_address] == ip + @logger.debug "found existing network by ip: #{network}" + return network + end + end + nil + end + # Return hash of network for specified name, or nil if not found. def lookup_network_by_name(network_name) @logger.debug "looking up network named #{network_name}" @available_networks.each do |network| - return network if network[:name] == network_name + if network[:name] == network_name + @logger.debug "found existing network by name: #{network}" + return network + end end nil end @@ -114,31 +127,38 @@ module VagrantPlugins # @available_networks should be filled before calling this function. def handle_ip_option(env) return if !@options[:ip] + net_address = nil + if @options[:forward_mode] != 'veryisolated' + net_address = network_address(@options[:ip], @options[:netmask]) + # Set IP address of network (actually bridge). It will be used as + # gateway address for machines connected to this network. + net = IPAddr.new(net_address) + + # Default to first address (after network name) + @interface_network[:ip_address] = @options[:host_ip].nil? ? \ + net.to_range.begin.succ : \ + IPAddr.new(@options[:host_ip]) + end - net_address = network_address(@options[:ip], @options[:netmask]) @interface_network[:network_address] = net_address - # Set IP address of network (actually bridge). It will be used as - # gateway address for machines connected to this network. - net = IPAddr.new(net_address) - @interface_network[:ip_address] = net.to_range.begin.succ - - # Is there an available network matching to configured ip - # address? - @available_networks.each do |available_network| - if available_network[:network_address] == \ - @interface_network[:network_address] - @interface_network = available_network - @logger.debug "found existing network by ip, values are" - @logger.debug @interface_network - break - end + # if network is veryisolated, search by name + if @options[:libvirt__forward_mode] == "veryisolated" + network = lookup_network_by_name(@options[:network_name]) + elsif net_address + # otherwise, search by ip (if set) + network = lookup_network_by_ip(net_address) + else + # leaving this here to mimic prior behavior. If we get + # here, something's probably broken. + network = lookup_network_by_name(@options[:network_name]) end + @interface_network = network if network if @interface_network[:created] verify_dhcp end - + if @options[:network_name] @logger.debug "Checking that network name does not clash with ip" if @interface_network[:created] @@ -157,13 +177,13 @@ module VagrantPlugins ip_address: @options[:ip], network_name: @options[:network_name] end - + # Network with 'name' doesn't exist. Set it as name for new # network. @interface_network[:name] = @options[:network_name] end end - + # Do we need to create new network? if !@interface_network[:created] @@ -206,15 +226,39 @@ module VagrantPlugins # Handle network_name option, if ip was not specified. Variables # @options and @available_networks should be filled before calling this # function. - def handle_network_name_option - return if @options[:ip] || !@options[:network_name] + def handle_network_name_option(env) + return if @options[:ip] || \ + !@options[:network_name] || \ + !@options[:libvirt__forward_mode] == "veryisolated" - @interface_network = lookup_network_by_name(@options[:network_name]) - if !@interface_network + network = lookup_network_by_name(@options[:network_name]) + @interface_network = network if network + + # if this interface has a network address, something's wrong. + if @interface_network[:network_address] raise Errors::NetworkNotAvailableError, network_name: @options[:network_name] - else - verify_dhcp + end + + # Do we need to create new network? + if !@interface_network[:created] + @interface_network[:name] = @options[:network_name] + + # Generate a unique name for network bridge. + count = 0 + while @interface_network[:bridge_name].nil? + @logger.debug "generating name for bridge" + bridge_name = 'virbr' + bridge_name << count.to_s + count += 1 + + next if lookup_bridge_by_name(bridge_name) + + @interface_network[:bridge_name] = bridge_name + end + + # Create a private network. + create_private_network(env) end end @@ -233,18 +277,19 @@ module VagrantPlugins # Find out DHCP addresses pool range. network_address = "#{@interface_network[:network_address]}/" network_address << "#{@interface_network[:netmask]}" - net = IPAddr.new(network_address) + net = @interface_network[:network_address] ? IPAddr.new(network_address) : nil - # First is address of network, second is gateway. - # Start the range two - # addresses after network address. + # First is address of network, second is gateway (by default). + # So start the range two addresses after network address by default. # TODO: Detect if this IP is not set on the interface. - start_address = net.to_range.begin.succ.succ + start_address = @options[:dhcp_start] || net.to_range.begin.succ - # Stop address must not be broadcast. - stop_address = net.to_range.end & IPAddr.new('255.255.255.254') + # Default to last possible address. (Stop address must not be broadcast address.) + stop_address = @options[:dhcp_stop] || (net.to_range.end & IPAddr.new('255.255.255.254')) @network_dhcp_enabled = true + @network_dhcp_bootp_file = @options[:dhcp_bootp_file] + @network_dhcp_bootp_server = @options[:dhcp_bootp_server] @network_range_start = start_address @network_range_stop = stop_address else diff --git a/lib/vagrant-libvirt/action/destroy_domain.rb b/lib/vagrant-libvirt/action/destroy_domain.rb index c964056..e4610be 100644 --- a/lib/vagrant-libvirt/action/destroy_domain.rb +++ b/lib/vagrant-libvirt/action/destroy_domain.rb @@ -4,7 +4,7 @@ module VagrantPlugins module ProviderLibvirt module Action class DestroyDomain - def initialize(app, env) + def initialize(app, _env) @logger = Log4r::Logger.new('vagrant_libvirt::action::destroy_domain') @app = app end @@ -17,7 +17,8 @@ module VagrantPlugins # Fog libvirt currently doesn't support snapshots. Use # ruby-libvirt client directly. Note this is racy, see # http://www.libvirt.org/html/libvirt-libvirt.html#virDomainSnapshotListNames - libvirt_domain = env[:libvirt_compute].client.lookup_domain_by_uuid(env[:machine].id) + libvirt_domain = env[:libvirt_compute].client.lookup_domain_by_uuid( + env[:machine].id) libvirt_domain.list_snapshots.each do |name| @logger.info("Deleting snapshot '#{name}'") begin @@ -28,7 +29,39 @@ module VagrantPlugins end domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s) - domain.destroy(destroy_volumes: true) + + if env[:machine].provider_config.disks.empty? + # if using default configuration of disks + domain.destroy(destroy_volumes: true) + else + domain.destroy(destroy_volumes: false) + + env[:machine].provider_config.disks.each do |disk| + # shared disks remove only manualy or ??? + next if disk[:allow_existing] + diskname = libvirt_domain.name + '-' + disk[:device] + '.' + disk[:type].to_s + # diskname is uniq + libvirt_disk = domain.volumes.select do |x| + x.name == diskname + end.first + if libvirt_disk + libvirt_disk.destroy + elsif disk[:path] + poolname = env[:machine].provider_config.storage_pool_name + libvirt_disk = domain.volumes.select do |x| + # FIXME can remove pool/target.img and pool/123/target.img + x.path =~ /\/#{disk[:path]}$/ && x.pool_name == poolname + end.first + libvirt_disk.destroy if libvirt_disk + end + end + + # remove root storage + root_disk = domain.volumes.select do |x| + x.name == libvirt_domain.name + '.img' + end.first + root_disk.destroy if root_disk + end @app.call(env) end diff --git a/lib/vagrant-libvirt/action/handle_box_image.rb b/lib/vagrant-libvirt/action/handle_box_image.rb index d1dc03c..2cf87ac 100644 --- a/lib/vagrant-libvirt/action/handle_box_image.rb +++ b/lib/vagrant-libvirt/action/handle_box_image.rb @@ -37,9 +37,29 @@ module VagrantPlugins env[:box_volume_name] = env[:machine].box.name.to_s.dup.gsub("/", "-VAGRANTSLASH-") env[:box_volume_name] << "_vagrant_box_image_#{env[:machine].box.version.to_s rescue ''}.img" + # Override box_virtual_size + if config.machine_virtual_size + if config.machine_virtual_size < box_virtual_size + # Warn that a virtual size less than the box metadata size + # is not supported and will be ignored + env[:ui].warn I18n.t( + 'vagrant_libvirt.warnings.ignoring_virtual_size_too_small', + requested: config.machine_virtual_size, minimum: box_virtual_size + ) + else + env[:ui].info I18n.t('vagrant_libvirt.manual_resize_required') + box_virtual_size = config.machine_virtual_size + end + end + # save for use by later actions + env[:box_virtual_size] = box_virtual_size + + # while inside the synchronize block take care not to call the next + # action in the chain, as must exit this block first to prevent + # locking all subsequent actions as well. @@lock.synchronize do # Don't continue if image already exists in storage pool. - return @app.call(env) if ProviderLibvirt::Util::Collection.find_matching( + break if ProviderLibvirt::Util::Collection.find_matching( env[:libvirt_compute].volumes.all, env[:box_volume_name]) # Box is not available as a storage pool volume. Create and upload @@ -123,11 +143,7 @@ module VagrantPlugins :error_message => e.message end - if progress == image_size - return true - else - return false - end + return progress == image_size end end diff --git a/lib/vagrant-libvirt/action/handle_storage_pool.rb b/lib/vagrant-libvirt/action/handle_storage_pool.rb index e405638..f567687 100644 --- a/lib/vagrant-libvirt/action/handle_storage_pool.rb +++ b/lib/vagrant-libvirt/action/handle_storage_pool.rb @@ -14,14 +14,16 @@ module VagrantPlugins end def call(env) - @@lock.synchronize do - # Get config options. - config = env[:machine].provider_config + # Get config options. + config = env[:machine].provider_config + # while inside the synchronize block take care not to call the next + # action in the chain, as must exit this block first to prevent + # locking all subsequent actions as well. + @@lock.synchronize do # Check for storage pool, where box image should be created - fog_pool = ProviderLibvirt::Util::Collection.find_matching( + break if ProviderLibvirt::Util::Collection.find_matching( env[:libvirt_compute].pools.all, config.storage_pool_name) - return @app.call(env) if fog_pool @logger.info("No storage pool '#{config.storage_pool_name}' is available.") diff --git a/lib/vagrant-libvirt/action/package_domain.rb b/lib/vagrant-libvirt/action/package_domain.rb new file mode 100644 index 0000000..8c6c8b0 --- /dev/null +++ b/lib/vagrant-libvirt/action/package_domain.rb @@ -0,0 +1,83 @@ +require 'log4r' + +module VagrantPlugins + module ProviderLibvirt + module Action + # Action for create new box for libvirt provider + class PackageDomain + def initialize(app, env) + @logger = Log4r::Logger.new('vagrant_libvirt::action::package_domain') + @app = app + env['package.files'] ||= {} + env['package.output'] ||= 'package.box' + end + + def call(env) + env[:ui].info(I18n.t('vagrant_libvirt.package_domain')) + libvirt_domain = env[:libvirt_compute].client.lookup_domain_by_uuid( + env[:machine].id) + domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s) + root_disk = domain.volumes.select do |x| + x.name == libvirt_domain.name + '.img' + end.first + boxname = env['package.output'] + raise "#{boxname}: Already exists" if File.exists?(boxname) + @tmp_dir = Dir.pwd + '/_tmp_package' + @tmp_img = @tmp_dir + '/box.img' + Dir.mkdir(@tmp_dir) + if File.readable?(root_disk.path) + backing = `qemu-img info "#{root_disk.path}" | grep 'backing file:' | cut -d ':' -f2`.chomp + else + env[:ui].error("Require set read access to #{root_disk.path}. sudo chmod a+r #{root_disk.path}") + FileUtils.rm_rf(@tmp_dir) + raise 'Have no access' + end + env[:ui].info('Image has backing image, copying image and rebasing ...') + FileUtils.cp(root_disk.path, @tmp_img) + `qemu-img rebase -p -b "" #{@tmp_img}` + # remove hw association with interface + # working for centos with lvs default disks + `virt-sysprep --no-logfile --operations defaults,-ssh-userdir -a #{@tmp_img} ` + Dir.chdir(@tmp_dir) + img_size = `qemu-img info #{@tmp_img} | grep 'virtual size' | awk '{print $3;}' | tr -d 'G'`.chomp + File.write(@tmp_dir + '/metadata.json', metadata_content(img_size)) + File.write(@tmp_dir + '/Vagrantfile',vagrantfile_content) + assebmle_box(boxname) + FileUtils.mv(@tmp_dir + '/' + boxname, '../' + boxname) + FileUtils.rm_rf(@tmp_dir) + env[:ui].info('Box created') + env[:ui].info('You can now add the box:') + env[:ui].info("vagrant box add #{boxname} --name any_comfortable_name") + @app.call(env) + end + + def assebmle_box(boxname) + `tar cvzf "#{boxname}" --totals ./metadata.json ./Vagrantfile ./box.img` + end + + def vagrantfile_content + <<-EOF + Vagrant.configure("2") do |config| + config.vm.provider :libvirt do |libvirt| + libvirt.driver = "kvm" + libvirt.host = "" + libvirt.connect_via_ssh = false + libvirt.storage_pool_name = "default" + end + end + EOF + end + + def metadata_content(filesize) + <<-EOF + { + "provider": "libvirt", + "format": "qcow2", + "virtual_size": #{filesize} + } + EOF + end + end + end + end +end diff --git a/lib/vagrant-libvirt/action/prepare_nfs_settings.rb b/lib/vagrant-libvirt/action/prepare_nfs_settings.rb index ebb9c54..9bc24a1 100644 --- a/lib/vagrant-libvirt/action/prepare_nfs_settings.rb +++ b/lib/vagrant-libvirt/action/prepare_nfs_settings.rb @@ -1,10 +1,13 @@ require 'nokogiri' +require 'socket' +require 'timeout' + module VagrantPlugins module ProviderLibvirt module Action class PrepareNFSSettings include Vagrant::Action::Builtin::MixinSyncedFolders - + def initialize(app,env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") @@ -16,8 +19,8 @@ module VagrantPlugins if using_nfs? @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") - env[:nfs_host_ip] = read_host_ip(env[:machine],env) - env[:nfs_machine_ip] = env[:machine].ssh_info[:host] + env[:nfs_machine_ip] = read_machine_ip(env[:machine]) + env[:nfs_host_ip] = read_host_ip(env[:nfs_machine_ip]) @logger.info("host IP: #{env[:nfs_host_ip]} machine IP: #{env[:nfs_machine_ip]}") @@ -32,37 +35,59 @@ module VagrantPlugins !!synced_folders(@machine)[:nfs] end - # Returns the IP address of the first host only network adapter + # Returns the IP address of the host # # @param [Machine] machine # @return [String] - def read_host_ip(machine,env) - nets = env[:libvirt_compute].list_networks - if nets.size == 1 - net = nets.first - else - domain = env[:libvirt_compute].servers.get(machine.id.to_s) - xml=Nokogiri::XML(domain.to_xml) - networkname = xml.xpath('/domain/devices/interface/source').first.attributes['network'].value.to_s - @logger.info("Using network named #{networkname}") - net = env[:libvirt_compute].list_networks.find {|netw| netw[:name] == networkname} + def read_host_ip(ip) + UDPSocket.open do |s| + @logger.info("!!!! ALL IPs #{ip} #{ip.kind_of?(Array)}" ) + if ip.kind_of?(Array) + s.connect(ip[0], 1) + else + s.connect(ip, 1) + end + s.addr.last end - # FIXME better implement by libvirt xml parsing - `ip addr show | grep -A 2 #{net[:bridge_name]} | grep -i 'inet ' | tr -s ' ' | cut -d' ' -f3 | cut -d'/' -f 1`.chomp end - # Returns the IP address of the guest by looking at the first - # enabled host only network. + # Returns the IP address of the guest # + # @param [Machine] machine # @return [String] def read_machine_ip(machine) - machine.config.vm.networks.each do |type, options| - if type == :private_network && options[:ip].is_a?(String) - return options[:ip] - end + # check host only ip + ssh_host = machine.ssh_info[:host] + return ssh_host if ping(ssh_host) + + # check other ips + command = "ip addr show | grep -i 'inet ' | grep -v '127.0.0.1' | tr -s ' ' | cut -d' ' -f3 | cut -d'/' -f 1" + result = "" + machine.communicate.execute(command) do |type, data| + result << data if type == :stdout end - nil + ips = result.chomp.split("\n") + @logger.info("guest IPs: #{ips.join(', ')}") + ips.each do |ip| + next if ip == ssh_host + return ip if ping(ip) + end + end + + private + + # Check if we can open a connection to the host + def ping(host, timeout = 3) + timeout(timeout) do + s = TCPSocket.new(host, 'echo') + s.close + end + true + rescue Errno::ECONNREFUSED + true + rescue Timeout::Error, StandardError + false end end end diff --git a/lib/vagrant-libvirt/action/read_ssh_info.rb b/lib/vagrant-libvirt/action/read_ssh_info.rb index 01e32a7..3d7ea9d 100644 --- a/lib/vagrant-libvirt/action/read_ssh_info.rb +++ b/lib/vagrant-libvirt/action/read_ssh_info.rb @@ -20,6 +20,7 @@ module VagrantPlugins def read_ssh_info(libvirt, machine) return nil if machine.id.nil? + return nil if machine.state.id != :running # Find the machine domain = libvirt.servers.get(machine.id) @@ -32,13 +33,23 @@ module VagrantPlugins # Get IP address from dnsmasq lease file. ip_address = nil - domain.wait_for(2) { - addresses.each_pair do |type, ip| - ip_address = ip[0] if ip[0] != nil + begin + domain.wait_for(2) do + addresses.each_pair do |type, ip| + # Multiple leases are separated with a newline, return only + # the most recent address + ip_address = ip[0].split("\n").first if ip[0] != nil + end + ip_address != nil end - ip_address != nil - } - raise Errors::NoIpAddressError if not ip_address + rescue Fog::Errors::TimeoutError + @logger.info("Timeout at waiting for an ip address for machine %s" % machine.name) + end + + if not ip_address + @logger.info("No lease found for machine %s" % machine.name) + return nil + end ssh_info = { :host => ip_address, @@ -46,8 +57,8 @@ module VagrantPlugins :forward_agent => machine.config.ssh.forward_agent, :forward_x11 => machine.config.ssh.forward_x11, } - - ssh_info[:proxy_command] = "ssh '#{machine.provider_config.host}' -l '#{machine.provider_config.username}' nc %h %p" if machine.provider_config.connect_via_ssh + + ssh_info[:proxy_command] = "ssh '#{machine.provider_config.host}' -l '#{machine.provider_config.username}' -i '#{machine.provider_config.id_ssh_key_file}' nc %h %p" if machine.provider_config.connect_via_ssh ssh_info end diff --git a/lib/vagrant-libvirt/action/read_state.rb b/lib/vagrant-libvirt/action/read_state.rb index ad552a1..1848de4 100644 --- a/lib/vagrant-libvirt/action/read_state.rb +++ b/lib/vagrant-libvirt/action/read_state.rb @@ -27,10 +27,17 @@ module VagrantPlugins end # Find the machine begin + # Wait for libvirt to shutdown the domain + while libvirt.servers.get(machine.id).state.to_sym == :'shutting-down' do + @logger.info('Waiting on the machine to shut down...') + sleep 1 + end + server = libvirt.servers.get(machine.id) - if server.nil? || [:'shutting-down', :terminated].include?(server.state.to_sym) + + if server.nil? || server.state.to_sym == :terminated # The machine can't be found - @logger.info('Machine shutting down or terminated, assuming it got destroyed.') + @logger.info('Machine terminated, assuming it got destroyed.') machine.id = nil return :not_created end diff --git a/lib/vagrant-libvirt/action/remove_libvirt_image.rb b/lib/vagrant-libvirt/action/remove_libvirt_image.rb new file mode 100644 index 0000000..554f0a7 --- /dev/null +++ b/lib/vagrant-libvirt/action/remove_libvirt_image.rb @@ -0,0 +1,20 @@ +require 'log4r' + +module VagrantPlugins + module ProviderLibvirt + module Action + class RemoveLibvirtImage + def initialize(app, env) + @logger = Log4r::Logger.new("vagrant_libvirt::action::remove_libvirt_image") + @app = app + end + + def call(env) + env[:ui].info("Vagrant-libvirt plugin removed box only from you LOCAL ~/.vagrant/boxes directory") + env[:ui].info("From libvirt storage pool you have to delete image manualy(virsh, virt-manager or by any other tool)") + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant-libvirt/action/remove_stale_volume.rb b/lib/vagrant-libvirt/action/remove_stale_volume.rb new file mode 100644 index 0000000..5b3c876 --- /dev/null +++ b/lib/vagrant-libvirt/action/remove_stale_volume.rb @@ -0,0 +1,49 @@ +require 'log4r' +#require 'log4r/yamlconfigurator' + +module VagrantPlugins + module ProviderLibvirt + module Action + class RemoveStaleVolume + def initialize(app, _env) + +# log4r_config= YAML.load_file(File.join(File.dirname(__FILE__),"log4r.yaml")) +# log_cfg = Log4r::YamlConfigurator +# log_cfg.decode_yaml( log4r_config['log4r_config'] ) + + @logger = Log4r::Logger.new('vagrant_libvirt::action::remove_stale_volume') + @app = app + end + + def call(env) + # Remove stale server volume + env[:ui].info(I18n.t('vagrant_libvirt.remove_stale_volume')) + + config = env[:machine].provider_config + # Check for storage pool, where box image should be created + fog_pool = ProviderLibvirt::Util::Collection.find_matching( + env[:libvirt_compute].pools.all, config.storage_pool_name) + @logger.debug("**** Pool #{fog_pool.name}") + + # This is name of newly created image for vm. + name = "#{env[:domain_name]}.img" + @logger.debug("**** Volume name #{name}") + + # remove root storage + box_volume = ProviderLibvirt::Util::Collection.find_matching( + env[:libvirt_compute].volumes.all, name) + if box_volume && box_volume.pool_name == fog_pool.name + @logger.info("Deleting volume #{box_volume.key}") + box_volume.destroy + env[:result] = box_volume + else + env[:result] = nil + end + + # Continue the middleware chain. + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant-libvirt/action/set_name_of_domain.rb b/lib/vagrant-libvirt/action/set_name_of_domain.rb index 9d91ea7..99bc330 100644 --- a/lib/vagrant-libvirt/action/set_name_of_domain.rb +++ b/lib/vagrant-libvirt/action/set_name_of_domain.rb @@ -43,7 +43,7 @@ module VagrantPlugins # @return [String] libvirt domain name def build_domain_name(env) config = env[:machine].provider_config - domain_name = + domain_name = if config.default_prefix.nil? env[:root_path].basename.to_s.dup else diff --git a/lib/vagrant-libvirt/cap/mount_p9.rb b/lib/vagrant-libvirt/cap/mount_p9.rb index 274edb1..b788809 100644 --- a/lib/vagrant-libvirt/cap/mount_p9.rb +++ b/lib/vagrant-libvirt/cap/mount_p9.rb @@ -1,3 +1,4 @@ +require "digest/md5" require "vagrant/util/retryable" module VagrantPlugins @@ -16,7 +17,7 @@ module VagrantPlugins machine.communicate.sudo("mkdir -p #{expanded_guest_path}") # Mount - mount_tag = name.dup + mount_tag = Digest::MD5.new.update(opts[:hostpath]).to_s[0,31] mount_opts="-o trans=virtio" mount_opts += ",access=#{opts[:owner]}" if opts[:owner] diff --git a/lib/vagrant-libvirt/cap/synced_folder.rb b/lib/vagrant-libvirt/cap/synced_folder.rb index 90e7133..9d3ca8d 100644 --- a/lib/vagrant-libvirt/cap/synced_folder.rb +++ b/lib/vagrant-libvirt/cap/synced_folder.rb @@ -1,74 +1,71 @@ -require "log4r" +require 'log4r' require 'ostruct' require 'nokogiri' +require "digest/md5" - -require "vagrant/util/subprocess" -require "vagrant/errors" -require "vagrant-libvirt/errors" +require 'vagrant/util/subprocess' +require 'vagrant/errors' +require 'vagrant-libvirt/errors' # require_relative "helper" module VagrantPlugins module SyncedFolder9p - class SyncedFolder < Vagrant.plugin("2", :synced_folder) + class SyncedFolder < Vagrant.plugin('2', :synced_folder) include Vagrant::Util include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate def initialize(*args) super - - @logger = Log4r::Logger.new("vagrant_libvirt::synced_folders::9p") + @logger = Log4r::Logger.new('vagrant_libvirt::synced_folders::9p') end - def usable?(machine, raise_error=false) + def usable?(machine, raise_error = false) # bail now if not using libvirt since checking version would throw error return false unless machine.provider_name == :libvirt # support in device attach/detach introduced in 1.2.2 # version number format is major * 1,000,000 + minor * 1,000 + release libvirt_version = ProviderLibvirt.libvirt_connection.client.libversion - if libvirt_version >= 1002002 - return true - else - return false - end + libvirt_version >= 1_002_002 end - def prepare(machine, folders, opts) - - raise Vagrant::Errors::Error("No libvirt connection") if ProviderLibvirt.libvirt_connection.nil? - + def prepare(machine, folders, _opts) + raise Vagrant::Errors::Error('No libvirt connection') if ProviderLibvirt.libvirt_connection.nil? @conn = ProviderLibvirt.libvirt_connection.client begin # loop through folders folders.each do |id, folder_opts| - folder_opts.merge!({ :accessmode => "passthrough", - :readonly => nil }) { |_k, ov, _nv| ov } - machine.ui.info "================\nMachine id: #{machine.id}\nShould be mounting folders\n #{id}, opts: #{folder_opts}" + folder_opts.merge!({ target: id, + accessmode: 'passthrough', + readonly: nil }) { |_k, ov, _nv| ov } - xml = to_xml('filesystem', folder_opts ) + mount_tag = Digest::MD5.new.update(folder_opts[:hostpath]).to_s[0,31] + folder_opts[:mount_tag] = mount_tag + + machine.ui.info "================\nMachine id: #{machine.id}\nShould be mounting folders\n #{id}, opts: #{folder_opts}" + + xml = to_xml('filesystem', folder_opts) # puts "<<<<< XML:\n #{xml}\n >>>>>" @conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0) - - end + end rescue => e machine.ui.error("could not attach device because: #{e}") - raise VagrantPlugins::ProviderLibvirt::Errors::AttachDeviceError,:error_message => e.message + raise VagrantPlugins::ProviderLibvirt::Errors::AttachDeviceError, + error_message: e.message end end - # TODO once up, mount folders def enable(machine, folders, _opts) # Go through each folder and mount - machine.ui.info("mounting p9 share in guest") + machine.ui.info('mounting p9 share in guest') # Only mount folders that have a guest path specified. mount_folders = {} folders.each do |id, opts| mount_folders[id] = opts.dup if opts[:guestpath] # merge common options if not given - mount_folders[id].merge!(:version => '9p2000.L') { |_k, ov, _nv| ov } + mount_folders[id].merge!(version: '9p2000.L') { |_k, ov, _nv| ov } end # Mount the actual folder machine.guest.capability( @@ -76,27 +73,25 @@ module VagrantPlugins end def cleanup(machine, _opts) - - raise Vagrant::Errors::Error("No libvirt connection") if ProviderLibvirt.libvirt_connection.nil? - + if ProviderLibvirt.libvirt_connection.nil? + raise Vagrant::Errors::Error('No libvirt connection') + end @conn = ProviderLibvirt.libvirt_connection.client - begin - if machine.id && machine.id != "" + if machine.id && machine.id != '' dom = @conn.lookup_domain_by_uuid(machine.id) - Nokogiri::XML(dom.xml_desc).xpath('/domain/devices/filesystem').each do |xml| + Nokogiri::XML(dom.xml_desc).xpath( + '/domain/devices/filesystem').each do |xml| dom.detach_device(xml.to_s) - - machine.ui.info "Cleaned up shared folders" + machine.ui.info 'Cleaned up shared folders' end end rescue => e machine.ui.error("could not detach device because: #{e}") - raise VagrantPlugins::ProviderLibvirt::Errors::DetachDeviceError,:error_message => e.message + raise VagrantPlugins::ProviderLibvirt::Errors::DetachDeviceError, + error_message: e.message end - end - end end end diff --git a/lib/vagrant-libvirt/config.rb b/lib/vagrant-libvirt/config.rb index ebcf60e..9811ac6 100644 --- a/lib/vagrant-libvirt/config.rb +++ b/lib/vagrant-libvirt/config.rb @@ -55,7 +55,10 @@ module VagrantPlugins attr_accessor :memory attr_accessor :cpus attr_accessor :cpu_mode + attr_accessor :boot_order attr_accessor :machine_type + attr_accessor :machine_arch + attr_accessor :machine_virtual_size attr_accessor :disk_bus attr_accessor :nic_model_type attr_accessor :nested @@ -72,6 +75,11 @@ module VagrantPlugins attr_accessor :video_vram attr_accessor :keymap + # Sets the max number of NICs that can be created + # Default set to 8. Don't change the default unless you know + # what are doing + attr_accessor :nic_adapter_count + # Storage attr_accessor :disks attr_accessor :cdroms @@ -98,6 +106,8 @@ module VagrantPlugins @cpus = UNSET_VALUE @cpu_mode = UNSET_VALUE @machine_type = UNSET_VALUE + @machine_arch = UNSET_VALUE + @machine_virtual_size = UNSET_VALUE @disk_bus = UNSET_VALUE @nic_model_type = UNSET_VALUE @nested = UNSET_VALUE @@ -114,6 +124,10 @@ module VagrantPlugins @video_vram = UNSET_VALUE @keymap = UNSET_VALUE + @nic_adapter_count = UNSET_VALUE + + # Boot order + @boot_order = [] # Storage @disks = [] @cdroms = [] @@ -122,6 +136,10 @@ module VagrantPlugins @inputs = UNSET_VALUE end + def boot(device) + @boot_order << device # append + end + def _get_device(disks) # skip existing devices and also the first one (vda) exist = disks.collect {|x| x[:device]}+[1.vdev.to_s] @@ -221,6 +239,7 @@ module VagrantPlugins :path => options[:path], :bus => options[:bus], :cache => options[:cache] || 'default', + :allow_existing => options[:allow_existing], } @disks << disk # append @@ -266,8 +285,10 @@ module VagrantPlugins if @id_ssh_key_file # set ssh key for access to libvirt host - home_dir = `echo ${HOME}`.chomp - uri << "\&keyfile=#{home_dir}/.ssh/"+@id_ssh_key_file + uri << "\&keyfile=" + # if no slash, prepend $HOME/.ssh/ + @id_ssh_key_file.prepend("#{`echo ${HOME}`.chomp}/.ssh/") if @id_ssh_key_file !~ /\A\// + uri << @id_ssh_key_file end # set path to libvirt socket uri << "\&socket="+@socket if @socket @@ -295,6 +316,8 @@ module VagrantPlugins @cpus = 1 if @cpus == UNSET_VALUE @cpu_mode = 'host-model' if @cpu_mode == UNSET_VALUE @machine_type = nil if @machine_type == UNSET_VALUE + @machine_arch = nil if @machine_arch == UNSET_VALUE + @machine_virtual_size = nil if @machine_virtual_size == UNSET_VALUE @disk_bus = 'virtio' if @disk_bus == UNSET_VALUE @nic_model_type = 'virtio' if @nic_model_type == UNSET_VALUE @nested = false if @nested == UNSET_VALUE @@ -305,7 +328,7 @@ module VagrantPlugins @graphics_type = 'vnc' if @graphics_type == UNSET_VALUE @graphics_autoport = 'yes' if @graphics_port == UNSET_VALUE @graphics_autoport = 'no' if @graphics_port != UNSET_VALUE - if (@graphics_type != 'vnc' && @graphics_port != 'spice') || + if (@graphics_type != 'vnc' && @graphics_type != 'spice') || @graphics_passwd == UNSET_VALUE @graphics_passwd = nil end @@ -314,6 +337,10 @@ module VagrantPlugins @video_type = 'cirrus' if @video_type == UNSET_VALUE @video_vram = 9216 if @video_vram == UNSET_VALUE @keymap = 'en-us' if @keymap == UNSET_VALUE + @nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE + + # Boot order + @boot_order = [] if @boot_order == UNSET_VALUE # Storage @disks = [] if @disks == UNSET_VALUE diff --git a/lib/vagrant-libvirt/errors.rb b/lib/vagrant-libvirt/errors.rb index 870c647..863a4f0 100644 --- a/lib/vagrant-libvirt/errors.rb +++ b/lib/vagrant-libvirt/errors.rb @@ -106,6 +106,10 @@ module VagrantPlugins error_key(:activate_network_error) end + class TcpTunnelPortNotDefined < VagrantLibvirtError + error_key(:tcp_tunnel_port_not_defined) + end + # Other exceptions class InterfaceSlotNotAvailable < VagrantLibvirtError error_key(:interface_slot_not_available) diff --git a/lib/vagrant-libvirt/plugin.rb b/lib/vagrant-libvirt/plugin.rb index 18e20ad..0b7cae3 100644 --- a/lib/vagrant-libvirt/plugin.rb +++ b/lib/vagrant-libvirt/plugin.rb @@ -24,14 +24,15 @@ module VagrantPlugins end provider('libvirt', parallel: true) do - # Setup logging and i18n - setup_logging - setup_i18n - require_relative 'provider' Provider end + action_hook(:remove_libvirt_image) do |hook| + hook.after Vagrant::Action::Builtin::BoxRemove, Action.remove_libvirt_image + end + + guest_capability('linux', 'mount_p9_shared_folder') do require_relative 'cap/mount_p9' Cap::MountP9 @@ -85,7 +86,12 @@ module VagrantPlugins end end + # Setup logging and i18n before any autoloading loads other classes + # with logging configured as this prevents inheritance of the log level + # from the parent logger. + setup_logging + setup_i18n + end end end - diff --git a/lib/vagrant-libvirt/provider.rb b/lib/vagrant-libvirt/provider.rb index 9420dc2..e1053fa 100644 --- a/lib/vagrant-libvirt/provider.rb +++ b/lib/vagrant-libvirt/provider.rb @@ -45,7 +45,7 @@ module VagrantPlugins # :username => "mitchellh", # :private_key_path => "/path/to/my/key" #} - env = @machine.action('read_ssh_info') + env = @machine.action('read_ssh_info', :lock => false) env[:machine_ssh_info] end @@ -67,7 +67,7 @@ module VagrantPlugins # Run a custom action we define called "read_state" which does # what it says. It puts the state in the `:machine_state_id` # key in the environment. - env = @machine.action('read_state') + env = @machine.action('read_state', :lock => false) state_id = env[:machine_state_id] diff --git a/lib/vagrant-libvirt/templates/domain.xml.erb b/lib/vagrant-libvirt/templates/domain.xml.erb index 89520ab..dff3458 100644 --- a/lib/vagrant-libvirt/templates/domain.xml.erb +++ b/lib/vagrant-libvirt/templates/domain.xml.erb @@ -14,12 +14,22 @@ <% end %> - <% if @machine_type %> - hvm - <% else %> - hvm - <% end %> - + <% if @machine_type %> + <% if @machine_arch %> + hvm + <% else %> + hvm + <% end %> + <% else %> + <% if @machine_arch %> + hvm + <% else %> + hvm + <% end %> + <% end %> + <% if @boot_order.count >= 1 %> + + <% end %> <%= @kernel %> <%= @initrd %> <%= @cmd_line %> @@ -36,6 +46,11 @@ <%# we need to ensure a unique target dev -%> + <% if @boot_order[0] == 'hd' %> + + <% elsif @boot_order.count >= 1 %> + + <% end %> <%# additional disks -%> <% @disks.each do |d| -%> diff --git a/lib/vagrant-libvirt/templates/filesystem.xml.erb b/lib/vagrant-libvirt/templates/filesystem.xml.erb index 469cae6..a9824e4 100644 --- a/lib/vagrant-libvirt/templates/filesystem.xml.erb +++ b/lib/vagrant-libvirt/templates/filesystem.xml.erb @@ -1,8 +1,8 @@ - + <% unless readonly.nil? %> <% end %> - \ No newline at end of file + diff --git a/lib/vagrant-libvirt/templates/interface.xml.erb b/lib/vagrant-libvirt/templates/interface.xml.erb index 34ceccd..7086a7f 100644 --- a/lib/vagrant-libvirt/templates/interface.xml.erb +++ b/lib/vagrant-libvirt/templates/interface.xml.erb @@ -6,5 +6,10 @@ + <% if @boot_order[0] == 'network' %> + + <% elsif @boot_order.include?('network') %> + + <% end %> diff --git a/lib/vagrant-libvirt/templates/private_network.xml.erb b/lib/vagrant-libvirt/templates/private_network.xml.erb index e52bbe3..bfd3573 100644 --- a/lib/vagrant-libvirt/templates/private_network.xml.erb +++ b/lib/vagrant-libvirt/templates/private_network.xml.erb @@ -15,6 +15,13 @@ <% if @network_dhcp_enabled %> + <% if @network_dhcp_bootp_file %> + <% if @network_dhcp_bootp_server %> + + <% else %> + + <% end %> + <% end %> <% end %> diff --git a/lib/vagrant-libvirt/templates/tcp_tunnel_interface.xml.erb b/lib/vagrant-libvirt/templates/tcp_tunnel_interface.xml.erb new file mode 100644 index 0000000..379b740 --- /dev/null +++ b/lib/vagrant-libvirt/templates/tcp_tunnel_interface.xml.erb @@ -0,0 +1,7 @@ + + <% if @mac %> + + <% end %> + + + diff --git a/lib/vagrant-libvirt/version.rb b/lib/vagrant-libvirt/version.rb index b4a637d..e29adef 100644 --- a/lib/vagrant-libvirt/version.rb +++ b/lib/vagrant-libvirt/version.rb @@ -1,5 +1,5 @@ module VagrantPlugins module ProviderLibvirt - VERSION = '0.0.25' + VERSION = '0.0.30' end end diff --git a/locales/en.yml b/locales/en.yml index 38da9bf..1ab8e78 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -12,6 +12,9 @@ en: Checking if volume is available. creating_domain: |- Creating domain with the following settings... + manual_resize_required: |- + Created volume larger than box defaults, will require manual resizing of + filesystems to utilize. uploading_volume: |- Uploading base box image as volume into libvirt storage... creating_domain_volume: |- @@ -44,6 +47,13 @@ en: Rsyncing folder: %{hostpath} => %{guestpath} ready: |- Machine is booted and ready for use! + remove_stale_volume: |- + Remove stale volume... + + warnings: + ignoring_virtual_size_too_small: |- + Ignoring requested virtual disk size of '%{requested}' as it is below + the minimum box image size of '%{box_virtual_size}'. errors: package_not_supported: Not support package for libvirt. Create box manualy. @@ -125,6 +135,8 @@ en: Error while removing network %{network_name}. %{error_message}. delete_snapshot_error: |- Error while deleting snapshot: %{error_message}. + tcp_tunnel_port_not_defined: |- + TCP tunnel port not defined. states: short_paused: |- diff --git a/spec/support/environment_helper.rb b/spec/support/environment_helper.rb index f49db85..ddfa43b 100644 --- a/spec/support/environment_helper.rb +++ b/spec/support/environment_helper.rb @@ -11,6 +11,20 @@ class EnvironmentHelper self.send(value.to_sym) end + def cpus + 4 + end + + def memory + 1024 + end + + %w(cpus cpu_mode boot_order machine_type disk_bus nested volume_cache kernel cmd_line initrd graphics_type graphics_autoport graphics_port graphics_ip graphics_passwd video_type video_vram keymap storage_pool_name disks cdroms driver).each do |name| + define_method(name.to_sym) do + nil + end + end + def machine self end diff --git a/spec/vagrant-libvirt/action/set_name_of_domain_spec.rb b/spec/vagrant-libvirt/action/set_name_of_domain_spec.rb index 69d3319..838910a 100644 --- a/spec/vagrant-libvirt/action/set_name_of_domain_spec.rb +++ b/spec/vagrant-libvirt/action/set_name_of_domain_spec.rb @@ -10,12 +10,12 @@ describe VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain do dmn = VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain.new(Object.new, @env) first = dmn.build_domain_name(@env) second = dmn.build_domain_name(@env) - first.should_not eq(second) + first.should_not eq(second) end it "builds simple domain name" do @env.default_prefix= 'pre' dmn = VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain.new(Object.new, @env) - dmn.build_domain_name(@env).should eq('pre_') + dmn.build_domain_name(@env).should eq('pre_') end end diff --git a/tools/create_box.sh b/tools/create_box.sh index 3a2f631..9f35409 100755 --- a/tools/create_box.sh +++ b/tools/create_box.sh @@ -3,7 +3,7 @@ error() { local msg="${1}" - echo "==> ${msg}" + echo "==> ERROR: ${msg}" exit 1 } @@ -60,7 +60,7 @@ TMP_IMG="$TMP_DIR/box.img" mkdir -p "$TMP_DIR" -[[ ! -w "$IMG" ]] && error "'$IMG': Permission denied" +[[ ! -r "$IMG" ]] && error "'$IMG': Permission denied" # We move / copy (when the image has master) the image to the tempdir # ensure that it's moved back / removed again @@ -70,6 +70,10 @@ if [[ -n $(backing "$IMG") ]]; then cp "$IMG" "$TMP_IMG" rebase "$TMP_IMG" else + if fuser -s "$IMG"; then + error "Image '$IMG_BASENAME' is used by another process" + fi + # move the image to get a speed-up and use less space on disk trap 'mv "$TMP_IMG" "$IMG"; rm -rf "$TMP_DIR"' EXIT mv "$IMG" "$TMP_IMG" diff --git a/vagrant-libvirt.gemspec b/vagrant-libvirt.gemspec index 26a4adb..5b18192 100644 --- a/vagrant-libvirt.gemspec +++ b/vagrant-libvirt.gemspec @@ -20,10 +20,8 @@ Gem::Specification.new do |gem| gem.add_development_dependency "rspec-expectations", "~> 2.12.1" gem.add_development_dependency "rspec-mocks", "~> 2.12.1" - gem.add_runtime_dependency 'fog', '~> 1.15' - gem.add_runtime_dependency 'ruby-libvirt', '~> 0.4' + gem.add_runtime_dependency 'fog-libvirt', '~> 0.0.1' gem.add_runtime_dependency 'nokogiri', '~> 1.6.0' gem.add_development_dependency 'rake' end -