merged in the upstream changes correctly (I hope)

This commit is contained in:
James Johnson 2015-08-05 10:47:58 -05:00
commit 1a8e457960
37 changed files with 786 additions and 234 deletions

20
.travis.yml Normal file
View File

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

10
Gemfile
View File

@ -7,10 +7,16 @@ group :development do
# We depend on Vagrant for development, but we don't add it as a # We depend on Vagrant for development, but we don't add it as a
# gem dependency because we expect to be installed within the # gem dependency because we expect to be installed within the
# Vagrant environment itself using `vagrant plugin`. # 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 end
group :plugins do group :plugins do
gem "vagrant-libvirt", :path => '.' gem 'vagrant-libvirt', :path => '.'
end end

112
README.md
View File

@ -10,7 +10,7 @@ welcome and can help a lot :-)
## Features ## Features
* Control local Libvirt hypervisors. * 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. * Upload box image (qcow2 format) to Libvirt storage pool.
* Create volume as COW diff image for domains. * Create volume as COW diff image for domains.
* Create private networks. * Create private networks.
@ -46,12 +46,19 @@ missing development libraries for libxslt, libxml2 and libvirt.
In Ubuntu, Debian, ... 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, ... 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 ## 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: want. This is just an example of Libvirt CentOS 6.4 box available:
``` ```
vagrant box add centos64 http://kwok.cz/centos64.box vagrant box add fedora21 http://citozin.com/fedora21.box
``` # or
or
```
vagrant box add centos64 http://citozin.com/centos64.box 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. 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 That's why there is `--provider=libvirt` option specified. Other way to tell
Vagrant to use Libvirt provider is to setup environment variable Vagrant to use Libvirt provider is to setup environment variable
`export VAGRANT_DEFAULT_PROVIDER=libvirt`. `export VAGRANT_DEFAULT_PROVIDER=libvirt`.
### How Project Is Created ### 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. * `connect_via_ssh` - If use ssh tunnel to connect to Libvirt.
* `username` - Username and password to access Libvirt. * `username` - Username and password to access Libvirt.
* `password` - 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) * `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. * `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`. * `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. * `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`. * `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_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_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. * `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 * `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. * `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` - 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 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 ## Networks
Networking features in the form of `config.vm.network` support private 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 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 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 http://www.libvirt.org/formatdomain.html#elementsNICSDirect
An examples of network interface definitions: An examples of network interface definitions:
```ruby ```ruby
# Private network # Private network using virtual network switching
config.vm.define :test_vm1 do |test_vm1| config.vm.define :test_vm1 do |test_vm1|
test_vm1.vm.network :private_network, :ip => "10.20.30.40" test_vm1.vm.network :private_network, :ip => "10.20.30.40"
end 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 # Public Network
config.vm.define :test_vm1 do |test_vm1| config.vm.define :test_vm1 do |test_vm1|
test_vm1.vm.network :public_network, 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. network 'default' is used.
* `:libvirt__netmask` - Used only together with `:ip` option. Default is * `:libvirt__netmask` - Used only together with `:ip` option. Default is
'255.255.255.0'. '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 * `:libvirt__dhcp_enabled` - If DHCP will offer addresses, or not. Used only
when creating new network. Default is true. 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__adapter` - Number specifiyng sequence number of interface.
* `:libvirt__forward_mode` - Specify one of `veryisolated`, `none`, `nat` or `route` options. * `: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 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 * `:libvirt__forward_device` - Name of interface/device, where network should
be forwarded (NATed or routed). Used only when creating new network. By be forwarded (NATed or routed). Used only when creating new network. By
default, all physical interfaces are used. 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. * `: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 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 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*. * `type` - Type of disk image to create. Defaults to *qcow2*.
* `bus` - Type of bus to connect device to. Defaults to *virtio*. * `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*. * `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. 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' 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 ## Customized Graphics
vagrant-libvirt supports customizing the display and video settings of the 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. * `Vagrantfile` that does default settings for the provider-specific configuration for this provider.
## Create Box ## 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``` 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:
Used Packer to create the qcow2 images, templates available at https://github.com/jakobadam/packer-qemu-templates
``` ~/packer-qemu-templates/ubuntu$ packer build ubuntu-14.04-server-amd64-vagrant.json```
## Development ## 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'`). 3. Commit your changes (`git commit -am 'Add some feature'`).
4. Push to the branch (`git push origin my-new-feature`). 4. Push to the branch (`git push origin my-new-feature`).
5. Create new Pull Request. 5. Create new Pull Request.

View File

@ -1,5 +1,4 @@
require 'pathname' require 'pathname'
require 'vagrant-libvirt/plugin'
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
@ -27,3 +26,6 @@ module VagrantPlugins
end end
end end
end end
# make sure base module class defined before loading plugin
require 'vagrant-libvirt/plugin'

View File

@ -8,6 +8,13 @@ module VagrantPlugins
include Vagrant::Action::Builtin include Vagrant::Action::Builtin
@logger = Log4r::Logger.new('vagrant_libvirt::action') @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. # This action is called to bring the box up from nothing.
def self.action_up def self.action_up
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|
@ -24,27 +31,18 @@ module VagrantPlugins
b2.use CreateDomain b2.use CreateDomain
b2.use Provision b2.use Provision
b2.use CreateNetworks
b2.use CreateNetworkInterfaces
b2.use PrepareNFSValidIds b2.use PrepareNFSValidIds
b2.use SyncedFolderCleanup b2.use SyncedFolderCleanup
b2.use SyncedFolders b2.use SyncedFolders
b2.use StartDomain
b2.use WaitTillUp
b2.use StartDomain
b2.use WaitTillUp
b2.use ForwardPorts
b2.use PrepareNFSSettings b2.use PrepareNFSSettings
b2.use ShareFolders b2.use ShareFolders
b2.use CreateNetworks
b2.use CreateNetworkInterfaces
b2.use StartDomain
b2.use WaitTillUp
b2.use ForwardPorts
b2.use SetHostname b2.use SetHostname
# b2.use SyncFolders # b2.use SyncFolders
else else
@ -83,7 +81,6 @@ module VagrantPlugins
b3.use SyncedFolderCleanup b3.use SyncedFolderCleanup
b3.use SyncedFolders b3.use SyncedFolders
# Start it.. # Start it..
b3.use StartDomain b3.use StartDomain
@ -91,11 +88,9 @@ module VagrantPlugins
# so wait for dhcp lease and store IP into machines data_dir. # so wait for dhcp lease and store IP into machines data_dir.
b3.use WaitTillUp b3.use WaitTillUp
b3.use ForwardPorts b3.use ForwardPorts
b3.use PrepareNFSSettings b3.use PrepareNFSSettings
b3.use ShareFolders b3.use ShareFolders
end end
end end
end end
@ -147,8 +142,10 @@ module VagrantPlugins
# not implemented and looks like not require # not implemented and looks like not require
def self.action_package def self.action_package
lambda do |env| Vagrant::Action::Builder.new.tap do |b|
raise Errors::PackageNotSupported b.use ConfigValidate
b.use ConnectLibvirt
b.use PackageDomain
end end
end end
@ -159,7 +156,14 @@ module VagrantPlugins
b.use ConfigValidate b.use ConfigValidate
b.use Call, IsCreated do |env, b2| b.use Call, IsCreated do |env, b2|
if !env[:result] 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 next
end end
@ -168,6 +172,7 @@ module VagrantPlugins
# b2.use PruneNFSExports # b2.use PruneNFSExports
b2.use DestroyDomain b2.use DestroyDomain
b2.use DestroyNetworks b2.use DestroyNetworks
b2.use ProvisionerCleanup
end end
end end
end end
@ -320,6 +325,7 @@ module VagrantPlugins
action_root = Pathname.new(File.expand_path('../action', __FILE__)) action_root = Pathname.new(File.expand_path('../action', __FILE__))
autoload :ConnectLibvirt, action_root.join('connect_libvirt') autoload :ConnectLibvirt, action_root.join('connect_libvirt')
autoload :PackageDomain, action_root.join('package_domain')
autoload :CreateDomain, action_root.join('create_domain') autoload :CreateDomain, action_root.join('create_domain')
autoload :CreateDomainVolume, action_root.join('create_domain_volume') autoload :CreateDomainVolume, action_root.join('create_domain_volume')
autoload :CreateNetworkInterfaces, action_root.join('create_network_interfaces') autoload :CreateNetworkInterfaces, action_root.join('create_network_interfaces')
@ -331,6 +337,7 @@ module VagrantPlugins
autoload :HaltDomain, action_root.join('halt_domain') autoload :HaltDomain, action_root.join('halt_domain')
autoload :HandleBoxImage, action_root.join('handle_box_image') autoload :HandleBoxImage, action_root.join('handle_box_image')
autoload :HandleStoragePool, action_root.join('handle_storage_pool') autoload :HandleStoragePool, action_root.join('handle_storage_pool')
autoload :RemoveLibvirtImage, action_root.join('remove_libvirt_image')
autoload :IsCreated, action_root.join('is_created') autoload :IsCreated, action_root.join('is_created')
autoload :IsRunning, action_root.join('is_running') autoload :IsRunning, action_root.join('is_running')
autoload :IsSuspended, action_root.join('is_suspended') autoload :IsSuspended, action_root.join('is_suspended')
@ -339,6 +346,8 @@ module VagrantPlugins
autoload :MessageNotRunning, action_root.join('message_not_running') autoload :MessageNotRunning, action_root.join('message_not_running')
autoload :MessageNotSuspended, action_root.join('message_not_suspended') 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 :PrepareNFSSettings, action_root.join('prepare_nfs_settings')
autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids') autoload :PrepareNFSValidIds, action_root.join('prepare_nfs_valid_ids')
autoload :PruneNFSExports, action_root.join('prune_nfs_exports') autoload :PruneNFSExports, action_root.join('prune_nfs_exports')
@ -362,6 +371,7 @@ module VagrantPlugins
autoload :HandleBox, 'vagrant/action/builtin/handle_box' autoload :HandleBox, 'vagrant/action/builtin/handle_box'
autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders' autoload :SyncedFolders, 'vagrant/action/builtin/synced_folders'
autoload :SyncedFolderCleanup, 'vagrant/action/builtin/synced_folder_cleanup' autoload :SyncedFolderCleanup, 'vagrant/action/builtin/synced_folder_cleanup'
autoload :ProvisionerCleanup, 'vagrant/action/builtin/provisioner_cleanup'
end end
end end
end end

View File

@ -1,4 +1,4 @@
require 'fog' require 'fog/libvirt'
require 'log4r' require 'log4r'
module VagrantPlugins module VagrantPlugins

View File

@ -3,7 +3,6 @@ require 'log4r'
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
module Action module Action
class CreateDomain class CreateDomain
include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
@ -13,15 +12,17 @@ module VagrantPlugins
end end
def _disk_name(name, disk) def _disk_name(name, disk)
return "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name
end end
def _disks_print(disks) 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 end
def _cdroms_print(cdroms) def _cdroms_print(cdroms)
return cdroms.collect{ |x| x[:dev] }.join(', ') cdroms.collect { |x| x[:dev] }.join(', ')
end end
def call(env) def call(env)
@ -33,9 +34,10 @@ module VagrantPlugins
@cpus = config.cpus.to_i @cpus = config.cpus.to_i
@cpu_mode = config.cpu_mode @cpu_mode = config.cpu_mode
@machine_type = config.machine_type @machine_type = config.machine_type
@machine_arch = config.machine_arch
@disk_bus = config.disk_bus @disk_bus = config.disk_bus
@nested = config.nested @nested = config.nested
@memory_size = config.memory.to_i*1024 @memory_size = config.memory.to_i * 1024
@domain_volume_cache = config.volume_cache @domain_volume_cache = config.volume_cache
@kernel = config.kernel @kernel = config.kernel
@cmd_line = config.cmd_line @cmd_line = config.cmd_line
@ -52,6 +54,9 @@ module VagrantPlugins
@video_type = config.video_type @video_type = config.video_type
@video_vram = config.video_vram @video_vram = config.video_vram
@keymap = config.keymap @keymap = config.keymap
# Boot order
@boot_order = config.boot_order
# Storage # Storage
@storage_pool_name = config.storage_pool_name @storage_pool_name = config.storage_pool_name
@ -66,17 +71,19 @@ module VagrantPlugins
@os_type = 'hvm' @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( 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? raise Errors::DomainVolumeExists if domain_volume.nil?
@domain_volume_path = domain_volume.path @domain_volume_path = domain_volume.path
# the default storage prefix is typically: /var/lib/libvirt/images/ # 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| @disks.each do |disk|
disk[:path] ||= _disk_name(@name, disk) disk[:path] ||= _disk_name(@name, disk)
# On volume creation, the <path> element inside <target> # On volume creation, the <path> element inside <target>
@ -87,19 +94,24 @@ module VagrantPlugins
disk[:absolute_path] = storage_prefix + disk[:path] disk[:absolute_path] = storage_prefix + disk[:path]
# make the disk. equivalent to: if env[:libvirt_compute].volumes.select {
# qemu-img create -f qcow2 <path> 5g |x| x.name == disk[:name] and x.pool_name == @storage_pool_name}.empty?
begin # make the disk. equivalent to:
domain_volume_disk = env[:libvirt_compute].volumes.create( # qemu-img create -f qcow2 <path> 5g
:name => disk[:name], begin
:format_type => disk[:type], env[:libvirt_compute].volumes.create(
:path => disk[:absolute_path], name: disk[:name],
:capacity => disk[:size], format_type: disk[:type],
#:allocation => ?, path: disk[:absolute_path],
:pool_name => @storage_pool_name) capacity: disk[:size],
rescue Fog::Errors::Error => e #:allocation => ?,
raise Errors::FogDomainVolumeCreateError, pool_name: @storage_pool_name)
:error_message => e.message rescue Fog::Errors::Error => e
raise Errors::FogDomainVolumeCreateError,
error_message: e.message
end
else
disk[:preexisting] = true
end end
end end
@ -108,31 +120,40 @@ module VagrantPlugins
env[:ui].info(" -- Name: #{@name}") env[:ui].info(" -- Name: #{@name}")
env[:ui].info(" -- Domain type: #{@domain_type}") env[:ui].info(" -- Domain type: #{@domain_type}")
env[:ui].info(" -- Cpus: #{@cpus}") 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(" -- Base box: #{env[:machine].box.name}")
env[:ui].info(" -- Storage pool: #{@storage_pool_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(" -- Volume Cache: #{@domain_volume_cache}")
env[:ui].info(" -- Kernel: #{@kernel}") env[:ui].info(" -- Kernel: #{@kernel}")
env[:ui].info(" -- Initrd: #{@initrd}") env[:ui].info(" -- Initrd: #{@initrd}")
env[:ui].info(" -- Graphics Type: #{@graphics_type}") env[:ui].info(" -- Graphics Type: #{@graphics_type}")
env[:ui].info(" -- Graphics Port: #{@graphics_port}") env[:ui].info(" -- Graphics Port: #{@graphics_port}")
env[:ui].info(" -- Graphics IP: #{@graphics_ip}") 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 Type: #{@video_type}")
env[:ui].info(" -- Video VRAM: #{@video_vram}") env[:ui].info(" -- Video VRAM: #{@video_vram}")
env[:ui].info(" -- Keymap: #{@keymap}") env[:ui].info(" -- Keymap: #{@keymap}")
@boot_order.each do |device|
env[:ui].info(" -- Boot device: #{device}")
end
if @disks.length > 0 if @disks.length > 0
env[:ui].info(" -- Disks: #{_disks_print(@disks)}") env[:ui].info(" -- Disks: #{_disks_print(@disks)}")
end end
@disks.each do |disk| @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 end
if @cdroms.length > 0 if @cdroms.length > 0
env[:ui].info(" -- CDROMS: #{_cdroms_print(@cdroms)}") env[:ui].info(" -- CDROMS: #{_cdroms_print(@cdroms)}")
end end
@cdroms.each do |cdrom| @cdroms.each do |cdrom|
env[:ui].info(" -- CDROM(#{cdrom[:dev]}): #{cdrom[:path]}") env[:ui].info(" -- CDROM(#{cdrom[:dev]}): #{cdrom[:path]}")
end end
@ -146,10 +167,9 @@ module VagrantPlugins
# existing volume? Use domain creation from template.. # existing volume? Use domain creation from template..
begin begin
server = env[:libvirt_compute].servers.create( server = env[:libvirt_compute].servers.create(
:xml => to_xml('domain')) xml: to_xml('domain'))
rescue Fog::Errors::Error => e rescue Fog::Errors::Error => e
raise Errors::FogCreateServerError, raise Errors::FogCreateServerError, error_message: e.message
:error_message => e.message
end end
# Immediately save the ID since it is created at this point. # Immediately save the ID since it is created at this point.
@ -158,7 +178,6 @@ module VagrantPlugins
@app.call(env) @app.call(env)
end end
end end
end end
end end
end end

View File

@ -34,8 +34,8 @@ module VagrantPlugins
env[:libvirt_compute].volumes.all, env[:box_volume_name]) env[:libvirt_compute].volumes.all, env[:box_volume_name])
@backing_file = box_volume.path @backing_file = box_volume.path
# Virtual size of image. Same as box image size. # Virtual size of image. Take value worked out by HandleBoxImage
@capacity = env[:machine].box.metadata['virtual_size'] #G @capacity = env[:box_virtual_size] #G
# Create new volume from xml template. Fog currently doesn't support # Create new volume from xml template. Fog currently doesn't support
# volume snapshots directly. # volume snapshots directly.

View File

@ -19,6 +19,8 @@ module VagrantPlugins
@management_network_name = env[:machine].provider_config.management_network_name @management_network_name = env[:machine].provider_config.management_network_name
config = env[:machine].provider_config config = env[:machine].provider_config
@nic_model_type = config.nic_model_type @nic_model_type = config.nic_model_type
@nic_adapter_count = config.nic_adapter_count
@boot_order = config.boot_order
@app = app @app = app
end end
@ -36,7 +38,6 @@ module VagrantPlugins
adapters = [] adapters = []
# Vagrant gives you adapter 0 by default # Vagrant gives you adapter 0 by default
# Assign interfaces to slots. # Assign interfaces to slots.
configured_networks(env, @logger).each do |options| configured_networks(env, @logger).each do |options|
@ -70,7 +71,6 @@ module VagrantPlugins
@mac = iface_configuration.fetch(:mac, false) @mac = iface_configuration.fetch(:mac, false)
@model_type = iface_configuration.fetch(:model_type, @nic_model_type) @model_type = iface_configuration.fetch(:model_type, @nic_model_type)
template_name = 'interface' template_name = 'interface'
# Configuration for public interfaces which use the macvtap driver # Configuration for public interfaces which use the macvtap driver
if iface_configuration[:iface_type] == :public_network if iface_configuration[:iface_type] == :public_network
@device = iface_configuration.fetch(:dev, 'eth0') @device = iface_configuration.fetch(:dev, 'eth0')
@ -80,8 +80,18 @@ module VagrantPlugins
template_name = 'public_interface' template_name = 'public_interface'
@logger.info("Setting up public interface using device #{@device} in mode #{@mode}") @logger.info("Setting up public interface using device #{@device} in mode #{@mode}")
@ovs = iface_configuration.fetch(:ovs, false) @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 end
message = "Creating network interface eth#{@iface_number}" message = "Creating network interface eth#{@iface_number}"
message << " connected to network #{@network_name}." message << " connected to network #{@network_name}."
if @mac if @mac
@ -96,6 +106,21 @@ module VagrantPlugins
raise Errors::AttachDeviceError, raise Errors::AttachDeviceError,
:error_message => e.message :error_message => e.message
end 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 end
# Continue the middleware chain. # Continue the middleware chain.
@ -116,7 +141,7 @@ module VagrantPlugins
network = { network = {
:interface => slot_number, :interface => slot_number,
:use_dhcp_assigned_default_route => options[:use_dhcp_assigned_default_route], :use_dhcp_assigned_default_route => options[:use_dhcp_assigned_default_route],
#:mac => ..., :mac_address => options[:mac],
} }
if options[:ip] if options[:ip]
@ -129,6 +154,9 @@ module VagrantPlugins
network[:type] = :dhcp network[:type] = :dhcp
end end
# do not run configure_networks for tcp tunnel interfaces
next if options.fetch(:tcp_tunnel_type, nil)
networks_to_configure << network networks_to_configure << network
end end
@ -139,7 +167,7 @@ module VagrantPlugins
private private
def find_empty(array, start=0, stop=8) def find_empty(array, start=0, stop=@nic_adapter_count)
(start..stop).each do |i| (start..stop).each do |i|
return i if !array[i] return i if !array[i]
end end
@ -148,6 +176,9 @@ module VagrantPlugins
# Return network name according to interface options. # Return network name according to interface options.
def interface_network(libvirt_client, 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] if options[:network_name]
@logger.debug "Found network by name" @logger.debug "Found network by name"
return options[:network_name] return options[:network_name]

View File

@ -35,7 +35,8 @@ module VagrantPlugins
# available, create it if possible. Otherwise raise an error. # available, create it if possible. Otherwise raise an error.
configured_networks(env, @logger).each do |options| configured_networks(env, @logger).each do |options|
# Only need to create private networks # 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}" @logger.debug "Searching for network with options #{options}"
# should fix other methods so this doesn't have to be instance var # 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 # Get a list of all (active and inactive) libvirt networks. This
# list is used throughout this class and should be easier to # list is used throughout this class and should be easier to
# process than libvirt API calls. # 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. # Prepare a hash describing network for this specific interface.
@interface_network = { @interface_network = {
@ -56,19 +58,16 @@ module VagrantPlugins
created: false, created: false,
active: false, active: false,
autostart: false, autostart: false,
libvirt_network: nil, libvirt_network: nil
} }
if @options[:ip] if @options[:ip]
handle_ip_option(env) 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] elsif @options[:network_name]
handle_network_name_option handle_network_name_option(env)
end end
autostart_network if !@interface_network[:autostart] autostart_network if @interface_network[:autostart]
activate_network if !@interface_network[:active] activate_network if !@interface_network[:active]
end end
end end
@ -78,11 +77,25 @@ module VagrantPlugins
private 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. # Return hash of network for specified name, or nil if not found.
def lookup_network_by_name(network_name) def lookup_network_by_name(network_name)
@logger.debug "looking up network named #{network_name}" @logger.debug "looking up network named #{network_name}"
@available_networks.each do |network| @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 end
nil nil
end end
@ -114,31 +127,38 @@ module VagrantPlugins
# @available_networks should be filled before calling this function. # @available_networks should be filled before calling this function.
def handle_ip_option(env) def handle_ip_option(env)
return if !@options[:ip] 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 @interface_network[:network_address] = net_address
# Set IP address of network (actually bridge). It will be used as # if network is veryisolated, search by name
# gateway address for machines connected to this network. if @options[:libvirt__forward_mode] == "veryisolated"
net = IPAddr.new(net_address) network = lookup_network_by_name(@options[:network_name])
@interface_network[:ip_address] = net.to_range.begin.succ elsif net_address
# otherwise, search by ip (if set)
# Is there an available network matching to configured ip network = lookup_network_by_ip(net_address)
# address? else
@available_networks.each do |available_network| # leaving this here to mimic prior behavior. If we get
if available_network[:network_address] == \ # here, something's probably broken.
@interface_network[:network_address] network = lookup_network_by_name(@options[:network_name])
@interface_network = available_network
@logger.debug "found existing network by ip, values are"
@logger.debug @interface_network
break
end
end end
@interface_network = network if network
if @interface_network[:created] if @interface_network[:created]
verify_dhcp verify_dhcp
end end
if @options[:network_name] if @options[:network_name]
@logger.debug "Checking that network name does not clash with ip" @logger.debug "Checking that network name does not clash with ip"
if @interface_network[:created] if @interface_network[:created]
@ -157,13 +177,13 @@ module VagrantPlugins
ip_address: @options[:ip], ip_address: @options[:ip],
network_name: @options[:network_name] network_name: @options[:network_name]
end end
# Network with 'name' doesn't exist. Set it as name for new # Network with 'name' doesn't exist. Set it as name for new
# network. # network.
@interface_network[:name] = @options[:network_name] @interface_network[:name] = @options[:network_name]
end end
end end
# Do we need to create new network? # Do we need to create new network?
if !@interface_network[:created] if !@interface_network[:created]
@ -206,15 +226,39 @@ module VagrantPlugins
# Handle network_name option, if ip was not specified. Variables # Handle network_name option, if ip was not specified. Variables
# @options and @available_networks should be filled before calling this # @options and @available_networks should be filled before calling this
# function. # function.
def handle_network_name_option def handle_network_name_option(env)
return if @options[:ip] || !@options[:network_name] return if @options[:ip] || \
!@options[:network_name] || \
!@options[:libvirt__forward_mode] == "veryisolated"
@interface_network = lookup_network_by_name(@options[:network_name]) network = lookup_network_by_name(@options[:network_name])
if !@interface_network @interface_network = network if network
# if this interface has a network address, something's wrong.
if @interface_network[:network_address]
raise Errors::NetworkNotAvailableError, raise Errors::NetworkNotAvailableError,
network_name: @options[:network_name] network_name: @options[:network_name]
else end
verify_dhcp
# 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
end end
@ -233,18 +277,19 @@ module VagrantPlugins
# Find out DHCP addresses pool range. # Find out DHCP addresses pool range.
network_address = "#{@interface_network[:network_address]}/" network_address = "#{@interface_network[:network_address]}/"
network_address << "#{@interface_network[:netmask]}" 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. # First is address of network, second is gateway (by default).
# Start the range two # So start the range two addresses after network address by default.
# addresses after network address.
# TODO: Detect if this IP is not set on the interface. # 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. # Default to last possible address. (Stop address must not be broadcast address.)
stop_address = net.to_range.end & IPAddr.new('255.255.255.254') stop_address = @options[:dhcp_stop] || (net.to_range.end & IPAddr.new('255.255.255.254'))
@network_dhcp_enabled = true @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_start = start_address
@network_range_stop = stop_address @network_range_stop = stop_address
else else

View File

@ -4,7 +4,7 @@ module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
module Action module Action
class DestroyDomain class DestroyDomain
def initialize(app, env) def initialize(app, _env)
@logger = Log4r::Logger.new('vagrant_libvirt::action::destroy_domain') @logger = Log4r::Logger.new('vagrant_libvirt::action::destroy_domain')
@app = app @app = app
end end
@ -17,7 +17,8 @@ module VagrantPlugins
# Fog libvirt currently doesn't support snapshots. Use # Fog libvirt currently doesn't support snapshots. Use
# ruby-libvirt client directly. Note this is racy, see # ruby-libvirt client directly. Note this is racy, see
# http://www.libvirt.org/html/libvirt-libvirt.html#virDomainSnapshotListNames # 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| libvirt_domain.list_snapshots.each do |name|
@logger.info("Deleting snapshot '#{name}'") @logger.info("Deleting snapshot '#{name}'")
begin begin
@ -28,7 +29,39 @@ module VagrantPlugins
end end
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s) 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) @app.call(env)
end end

View File

@ -37,9 +37,29 @@ module VagrantPlugins
env[:box_volume_name] = env[:machine].box.name.to_s.dup.gsub("/", "-VAGRANTSLASH-") 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" 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 @@lock.synchronize do
# Don't continue if image already exists in storage pool. # 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]) env[:libvirt_compute].volumes.all, env[:box_volume_name])
# Box is not available as a storage pool volume. Create and upload # Box is not available as a storage pool volume. Create and upload
@ -123,11 +143,7 @@ module VagrantPlugins
:error_message => e.message :error_message => e.message
end end
if progress == image_size return progress == image_size
return true
else
return false
end
end end
end end

View File

@ -14,14 +14,16 @@ module VagrantPlugins
end end
def call(env) def call(env)
@@lock.synchronize do # Get config options.
# Get config options. config = env[:machine].provider_config
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 # 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) 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.") @logger.info("No storage pool '#{config.storage_pool_name}' is available.")

View File

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

View File

@ -1,10 +1,13 @@
require 'nokogiri' require 'nokogiri'
require 'socket'
require 'timeout'
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
module Action module Action
class PrepareNFSSettings class PrepareNFSSettings
include Vagrant::Action::Builtin::MixinSyncedFolders include Vagrant::Action::Builtin::MixinSyncedFolders
def initialize(app,env) def initialize(app,env)
@app = app @app = app
@logger = Log4r::Logger.new("vagrant::action::vm::nfs") @logger = Log4r::Logger.new("vagrant::action::vm::nfs")
@ -16,8 +19,8 @@ module VagrantPlugins
if using_nfs? if using_nfs?
@logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") @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] = read_machine_ip(env[:machine])
env[:nfs_machine_ip] = env[:machine].ssh_info[:host] 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]}") @logger.info("host IP: #{env[:nfs_host_ip]} machine IP: #{env[:nfs_machine_ip]}")
@ -32,37 +35,59 @@ module VagrantPlugins
!!synced_folders(@machine)[:nfs] !!synced_folders(@machine)[:nfs]
end end
# Returns the IP address of the first host only network adapter # Returns the IP address of the host
# #
# @param [Machine] machine # @param [Machine] machine
# @return [String] # @return [String]
def read_host_ip(machine,env) def read_host_ip(ip)
nets = env[:libvirt_compute].list_networks UDPSocket.open do |s|
if nets.size == 1 @logger.info("!!!! ALL IPs #{ip} #{ip.kind_of?(Array)}" )
net = nets.first if ip.kind_of?(Array)
else s.connect(ip[0], 1)
domain = env[:libvirt_compute].servers.get(machine.id.to_s) else
xml=Nokogiri::XML(domain.to_xml) s.connect(ip, 1)
networkname = xml.xpath('/domain/devices/interface/source').first.attributes['network'].value.to_s end
@logger.info("Using network named #{networkname}") s.addr.last
net = env[:libvirt_compute].list_networks.find {|netw| netw[:name] == networkname}
end 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 end
# Returns the IP address of the guest by looking at the first # Returns the IP address of the guest
# enabled host only network.
# #
# @param [Machine] machine
# @return [String] # @return [String]
def read_machine_ip(machine) def read_machine_ip(machine)
machine.config.vm.networks.each do |type, options| # check host only ip
if type == :private_network && options[:ip].is_a?(String) ssh_host = machine.ssh_info[:host]
return options[:ip] return ssh_host if ping(ssh_host)
end
# 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 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 end
end end

View File

@ -20,6 +20,7 @@ module VagrantPlugins
def read_ssh_info(libvirt, machine) def read_ssh_info(libvirt, machine)
return nil if machine.id.nil? return nil if machine.id.nil?
return nil if machine.state.id != :running
# Find the machine # Find the machine
domain = libvirt.servers.get(machine.id) domain = libvirt.servers.get(machine.id)
@ -32,13 +33,23 @@ module VagrantPlugins
# Get IP address from dnsmasq lease file. # Get IP address from dnsmasq lease file.
ip_address = nil ip_address = nil
domain.wait_for(2) { begin
addresses.each_pair do |type, ip| domain.wait_for(2) do
ip_address = ip[0] if ip[0] != nil 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 end
ip_address != nil rescue Fog::Errors::TimeoutError
} @logger.info("Timeout at waiting for an ip address for machine %s" % machine.name)
raise Errors::NoIpAddressError if not ip_address end
if not ip_address
@logger.info("No lease found for machine %s" % machine.name)
return nil
end
ssh_info = { ssh_info = {
:host => ip_address, :host => ip_address,
@ -46,8 +57,8 @@ module VagrantPlugins
:forward_agent => machine.config.ssh.forward_agent, :forward_agent => machine.config.ssh.forward_agent,
:forward_x11 => machine.config.ssh.forward_x11, :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 ssh_info
end end

View File

@ -27,10 +27,17 @@ module VagrantPlugins
end end
# Find the machine # Find the machine
begin 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) 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 # 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 machine.id = nil
return :not_created return :not_created
end end

View File

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

View File

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

View File

@ -43,7 +43,7 @@ module VagrantPlugins
# @return [String] libvirt domain name # @return [String] libvirt domain name
def build_domain_name(env) def build_domain_name(env)
config = env[:machine].provider_config config = env[:machine].provider_config
domain_name = domain_name =
if config.default_prefix.nil? if config.default_prefix.nil?
env[:root_path].basename.to_s.dup env[:root_path].basename.to_s.dup
else else

View File

@ -1,3 +1,4 @@
require "digest/md5"
require "vagrant/util/retryable" require "vagrant/util/retryable"
module VagrantPlugins module VagrantPlugins
@ -16,7 +17,7 @@ module VagrantPlugins
machine.communicate.sudo("mkdir -p #{expanded_guest_path}") machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
# Mount # Mount
mount_tag = name.dup mount_tag = Digest::MD5.new.update(opts[:hostpath]).to_s[0,31]
mount_opts="-o trans=virtio" mount_opts="-o trans=virtio"
mount_opts += ",access=#{opts[:owner]}" if opts[:owner] mount_opts += ",access=#{opts[:owner]}" if opts[:owner]

View File

@ -1,74 +1,71 @@
require "log4r" require 'log4r'
require 'ostruct' require 'ostruct'
require 'nokogiri' require 'nokogiri'
require "digest/md5"
require 'vagrant/util/subprocess'
require "vagrant/util/subprocess" require 'vagrant/errors'
require "vagrant/errors" require 'vagrant-libvirt/errors'
require "vagrant-libvirt/errors"
# require_relative "helper" # require_relative "helper"
module VagrantPlugins module VagrantPlugins
module SyncedFolder9p module SyncedFolder9p
class SyncedFolder < Vagrant.plugin("2", :synced_folder) class SyncedFolder < Vagrant.plugin('2', :synced_folder)
include Vagrant::Util include Vagrant::Util
include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
def initialize(*args) def initialize(*args)
super super
@logger = Log4r::Logger.new('vagrant_libvirt::synced_folders::9p')
@logger = Log4r::Logger.new("vagrant_libvirt::synced_folders::9p")
end 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 # bail now if not using libvirt since checking version would throw error
return false unless machine.provider_name == :libvirt return false unless machine.provider_name == :libvirt
# <filesystem/> support in device attach/detach introduced in 1.2.2 # <filesystem/> support in device attach/detach introduced in 1.2.2
# version number format is major * 1,000,000 + minor * 1,000 + release # version number format is major * 1,000,000 + minor * 1,000 + release
libvirt_version = ProviderLibvirt.libvirt_connection.client.libversion libvirt_version = ProviderLibvirt.libvirt_connection.client.libversion
if libvirt_version >= 1002002 libvirt_version >= 1_002_002
return true
else
return false
end
end end
def prepare(machine, folders, opts) def prepare(machine, folders, _opts)
raise Vagrant::Errors::Error('No libvirt connection') if ProviderLibvirt.libvirt_connection.nil?
raise Vagrant::Errors::Error("No libvirt connection") if ProviderLibvirt.libvirt_connection.nil?
@conn = ProviderLibvirt.libvirt_connection.client @conn = ProviderLibvirt.libvirt_connection.client
begin begin
# loop through folders # loop through folders
folders.each do |id, folder_opts| folders.each do |id, folder_opts|
folder_opts.merge!({ :accessmode => "passthrough", folder_opts.merge!({ target: id,
:readonly => nil }) { |_k, ov, _nv| ov } accessmode: 'passthrough',
machine.ui.info "================\nMachine id: #{machine.id}\nShould be mounting folders\n #{id}, opts: #{folder_opts}" 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 >>>>>" # puts "<<<<< XML:\n #{xml}\n >>>>>"
@conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0) @conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0)
end
end
rescue => e rescue => e
machine.ui.error("could not attach device because: #{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
end end
# TODO once up, mount folders # TODO once up, mount folders
def enable(machine, folders, _opts) def enable(machine, folders, _opts)
# Go through each folder and mount # 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. # Only mount folders that have a guest path specified.
mount_folders = {} mount_folders = {}
folders.each do |id, opts| folders.each do |id, opts|
mount_folders[id] = opts.dup if opts[:guestpath] mount_folders[id] = opts.dup if opts[:guestpath]
# merge common options if not given # 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 end
# Mount the actual folder # Mount the actual folder
machine.guest.capability( machine.guest.capability(
@ -76,27 +73,25 @@ module VagrantPlugins
end end
def cleanup(machine, _opts) def cleanup(machine, _opts)
if ProviderLibvirt.libvirt_connection.nil?
raise Vagrant::Errors::Error("No libvirt connection") if ProviderLibvirt.libvirt_connection.nil? raise Vagrant::Errors::Error('No libvirt connection')
end
@conn = ProviderLibvirt.libvirt_connection.client @conn = ProviderLibvirt.libvirt_connection.client
begin begin
if machine.id && machine.id != "" if machine.id && machine.id != ''
dom = @conn.lookup_domain_by_uuid(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) dom.detach_device(xml.to_s)
machine.ui.info 'Cleaned up shared folders'
machine.ui.info "Cleaned up shared folders"
end end
end end
rescue => e rescue => e
machine.ui.error("could not detach device because: #{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 end
end end
end end

View File

@ -55,7 +55,10 @@ module VagrantPlugins
attr_accessor :memory attr_accessor :memory
attr_accessor :cpus attr_accessor :cpus
attr_accessor :cpu_mode attr_accessor :cpu_mode
attr_accessor :boot_order
attr_accessor :machine_type attr_accessor :machine_type
attr_accessor :machine_arch
attr_accessor :machine_virtual_size
attr_accessor :disk_bus attr_accessor :disk_bus
attr_accessor :nic_model_type attr_accessor :nic_model_type
attr_accessor :nested attr_accessor :nested
@ -72,6 +75,11 @@ module VagrantPlugins
attr_accessor :video_vram attr_accessor :video_vram
attr_accessor :keymap 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 # Storage
attr_accessor :disks attr_accessor :disks
attr_accessor :cdroms attr_accessor :cdroms
@ -98,6 +106,8 @@ module VagrantPlugins
@cpus = UNSET_VALUE @cpus = UNSET_VALUE
@cpu_mode = UNSET_VALUE @cpu_mode = UNSET_VALUE
@machine_type = UNSET_VALUE @machine_type = UNSET_VALUE
@machine_arch = UNSET_VALUE
@machine_virtual_size = UNSET_VALUE
@disk_bus = UNSET_VALUE @disk_bus = UNSET_VALUE
@nic_model_type = UNSET_VALUE @nic_model_type = UNSET_VALUE
@nested = UNSET_VALUE @nested = UNSET_VALUE
@ -114,6 +124,10 @@ module VagrantPlugins
@video_vram = UNSET_VALUE @video_vram = UNSET_VALUE
@keymap = UNSET_VALUE @keymap = UNSET_VALUE
@nic_adapter_count = UNSET_VALUE
# Boot order
@boot_order = []
# Storage # Storage
@disks = [] @disks = []
@cdroms = [] @cdroms = []
@ -122,6 +136,10 @@ module VagrantPlugins
@inputs = UNSET_VALUE @inputs = UNSET_VALUE
end end
def boot(device)
@boot_order << device # append
end
def _get_device(disks) def _get_device(disks)
# skip existing devices and also the first one (vda) # skip existing devices and also the first one (vda)
exist = disks.collect {|x| x[:device]}+[1.vdev.to_s] exist = disks.collect {|x| x[:device]}+[1.vdev.to_s]
@ -221,6 +239,7 @@ module VagrantPlugins
:path => options[:path], :path => options[:path],
:bus => options[:bus], :bus => options[:bus],
:cache => options[:cache] || 'default', :cache => options[:cache] || 'default',
:allow_existing => options[:allow_existing],
} }
@disks << disk # append @disks << disk # append
@ -266,8 +285,10 @@ module VagrantPlugins
if @id_ssh_key_file if @id_ssh_key_file
# set ssh key for access to libvirt host # set ssh key for access to libvirt host
home_dir = `echo ${HOME}`.chomp uri << "\&keyfile="
uri << "\&keyfile=#{home_dir}/.ssh/"+@id_ssh_key_file # 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 end
# set path to libvirt socket # set path to libvirt socket
uri << "\&socket="+@socket if @socket uri << "\&socket="+@socket if @socket
@ -295,6 +316,8 @@ module VagrantPlugins
@cpus = 1 if @cpus == UNSET_VALUE @cpus = 1 if @cpus == UNSET_VALUE
@cpu_mode = 'host-model' if @cpu_mode == UNSET_VALUE @cpu_mode = 'host-model' if @cpu_mode == UNSET_VALUE
@machine_type = nil if @machine_type == 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 @disk_bus = 'virtio' if @disk_bus == UNSET_VALUE
@nic_model_type = 'virtio' if @nic_model_type == UNSET_VALUE @nic_model_type = 'virtio' if @nic_model_type == UNSET_VALUE
@nested = false if @nested == UNSET_VALUE @nested = false if @nested == UNSET_VALUE
@ -305,7 +328,7 @@ module VagrantPlugins
@graphics_type = 'vnc' if @graphics_type == UNSET_VALUE @graphics_type = 'vnc' if @graphics_type == UNSET_VALUE
@graphics_autoport = 'yes' if @graphics_port == UNSET_VALUE @graphics_autoport = 'yes' if @graphics_port == UNSET_VALUE
@graphics_autoport = 'no' 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 == UNSET_VALUE
@graphics_passwd = nil @graphics_passwd = nil
end end
@ -314,6 +337,10 @@ module VagrantPlugins
@video_type = 'cirrus' if @video_type == UNSET_VALUE @video_type = 'cirrus' if @video_type == UNSET_VALUE
@video_vram = 9216 if @video_vram == UNSET_VALUE @video_vram = 9216 if @video_vram == UNSET_VALUE
@keymap = 'en-us' if @keymap == 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 # Storage
@disks = [] if @disks == UNSET_VALUE @disks = [] if @disks == UNSET_VALUE

View File

@ -106,6 +106,10 @@ module VagrantPlugins
error_key(:activate_network_error) error_key(:activate_network_error)
end end
class TcpTunnelPortNotDefined < VagrantLibvirtError
error_key(:tcp_tunnel_port_not_defined)
end
# Other exceptions # Other exceptions
class InterfaceSlotNotAvailable < VagrantLibvirtError class InterfaceSlotNotAvailable < VagrantLibvirtError
error_key(:interface_slot_not_available) error_key(:interface_slot_not_available)

View File

@ -24,14 +24,15 @@ module VagrantPlugins
end end
provider('libvirt', parallel: true) do provider('libvirt', parallel: true) do
# Setup logging and i18n
setup_logging
setup_i18n
require_relative 'provider' require_relative 'provider'
Provider Provider
end 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 guest_capability('linux', 'mount_p9_shared_folder') do
require_relative 'cap/mount_p9' require_relative 'cap/mount_p9'
Cap::MountP9 Cap::MountP9
@ -85,7 +86,12 @@ module VagrantPlugins
end end
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 end
end end

View File

@ -45,7 +45,7 @@ module VagrantPlugins
# :username => "mitchellh", # :username => "mitchellh",
# :private_key_path => "/path/to/my/key" # :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] env[:machine_ssh_info]
end end
@ -67,7 +67,7 @@ module VagrantPlugins
# Run a custom action we define called "read_state" which does # Run a custom action we define called "read_state" which does
# what it says. It puts the state in the `:machine_state_id` # what it says. It puts the state in the `:machine_state_id`
# key in the environment. # key in the environment.
env = @machine.action('read_state') env = @machine.action('read_state', :lock => false)
state_id = env[:machine_state_id] state_id = env[:machine_state_id]

View File

@ -14,12 +14,22 @@
<% end %> <% end %>
<os> <os>
<% if @machine_type %> <% if @machine_type %>
<type machine='<%= @machine_type %>'>hvm</type> <% if @machine_arch %>
<% else %> <type arch='<%= @machine_arch %>' machine='<%= @machine_type %>'>hvm</type>
<type>hvm</type> <% else %>
<% end %> <type machine='<%= @machine_type %>'>hvm</type>
<boot dev='hd'/> <% end %>
<% else %>
<% if @machine_arch %>
<type arch='<%= @machine_arch %>'>hvm</type>
<% else %>
<type>hvm</type>
<% end %>
<% end %>
<% if @boot_order.count >= 1 %>
<bootmenu enable='yes'/>
<% end %>
<kernel><%= @kernel %></kernel> <kernel><%= @kernel %></kernel>
<initrd><%= @initrd %></initrd> <initrd><%= @initrd %></initrd>
<cmdline><%= @cmd_line %></cmdline> <cmdline><%= @cmd_line %></cmdline>
@ -36,6 +46,11 @@
<source file='<%= @domain_volume_path %>'/> <source file='<%= @domain_volume_path %>'/>
<%# we need to ensure a unique target dev -%> <%# we need to ensure a unique target dev -%>
<target dev='vda' bus='<%= @disk_bus %>'/> <target dev='vda' bus='<%= @disk_bus %>'/>
<% if @boot_order[0] == 'hd' %>
<boot order='1'/>
<% elsif @boot_order.count >= 1 %>
<boot order='9'/>
<% end %>
</disk> </disk>
<%# additional disks -%> <%# additional disks -%>
<% @disks.each do |d| -%> <% @disks.each do |d| -%>

View File

@ -1,8 +1,8 @@
<filesystem type='mount' accessmode='<%= accessmode %>'> <filesystem type='mount' accessmode='<%= accessmode %>'>
<driver type='path' wrpolicy='immediate'/> <driver type='path' wrpolicy='immediate'/>
<source dir='<%= hostpath %>'/> <source dir='<%= hostpath %>'/>
<target dir='<%= guestpath %>'/> <target dir='<%= mount_tag %>'/>
<% unless readonly.nil? %> <% unless readonly.nil? %>
<readonly /> <readonly />
<% end %> <% end %>
</filesystem> </filesystem>

View File

@ -6,5 +6,10 @@
<target dev='vnet<%= @iface_number %>'/> <target dev='vnet<%= @iface_number %>'/>
<alias name='net<%= @iface_number %>'/> <alias name='net<%= @iface_number %>'/>
<model type='<%=@model_type%>'/> <model type='<%=@model_type%>'/>
<% if @boot_order[0] == 'network' %>
<boot order='<%= @iface_number+1 %>'/>
<% elsif @boot_order.include?('network') %>
<boot order='<%= @iface_number+2 %>'/>
<% end %>
</interface> </interface>

View File

@ -15,6 +15,13 @@
<% if @network_dhcp_enabled %> <% if @network_dhcp_enabled %>
<dhcp> <dhcp>
<range start="<%= @network_range_start %>" end="<%= @network_range_stop %>" /> <range start="<%= @network_range_start %>" end="<%= @network_range_stop %>" />
<% if @network_dhcp_bootp_file %>
<% if @network_dhcp_bootp_server %>
<bootp file="<%= @network_dhcp_bootp_file %>" server="<%= @network_dhcp_bootp_server %>" />
<% else %>
<bootp file="<%= @network_dhcp_bootp_file %>" />
<% end %>
<% end %>
</dhcp> </dhcp>
<% end %> <% end %>
</ip> </ip>

View File

@ -0,0 +1,7 @@
<interface type='<%= @type %>'>
<% if @mac %>
<mac address='<%= @mac %>'/>
<% end %>
<source address='<%=@tcp_tunnel_ip%>' port='<%= @tcp_tunnel_port %>'/>
<model type='<%=@model_type%>'/>
</interface>

View File

@ -1,5 +1,5 @@
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
VERSION = '0.0.25' VERSION = '0.0.30'
end end
end end

View File

@ -12,6 +12,9 @@ en:
Checking if volume is available. Checking if volume is available.
creating_domain: |- creating_domain: |-
Creating domain with the following settings... 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_volume: |-
Uploading base box image as volume into libvirt storage... Uploading base box image as volume into libvirt storage...
creating_domain_volume: |- creating_domain_volume: |-
@ -44,6 +47,13 @@ en:
Rsyncing folder: %{hostpath} => %{guestpath} Rsyncing folder: %{hostpath} => %{guestpath}
ready: |- ready: |-
Machine is booted and ready for use! 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: errors:
package_not_supported: Not support package for libvirt. Create box manualy. package_not_supported: Not support package for libvirt. Create box manualy.
@ -125,6 +135,8 @@ en:
Error while removing network %{network_name}. %{error_message}. Error while removing network %{network_name}. %{error_message}.
delete_snapshot_error: |- delete_snapshot_error: |-
Error while deleting snapshot: %{error_message}. Error while deleting snapshot: %{error_message}.
tcp_tunnel_port_not_defined: |-
TCP tunnel port not defined.
states: states:
short_paused: |- short_paused: |-

View File

@ -11,6 +11,20 @@ class EnvironmentHelper
self.send(value.to_sym) self.send(value.to_sym)
end 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 def machine
self self
end end

View File

@ -10,12 +10,12 @@ describe VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain do
dmn = VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain.new(Object.new, @env) dmn = VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain.new(Object.new, @env)
first = dmn.build_domain_name(@env) first = dmn.build_domain_name(@env)
second = dmn.build_domain_name(@env) second = dmn.build_domain_name(@env)
first.should_not eq(second) first.should_not eq(second)
end end
it "builds simple domain name" do it "builds simple domain name" do
@env.default_prefix= 'pre' @env.default_prefix= 'pre'
dmn = VagrantPlugins::ProviderLibvirt::Action::SetNameOfDomain.new(Object.new, @env) 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
end end

View File

@ -3,7 +3,7 @@
error() { error() {
local msg="${1}" local msg="${1}"
echo "==> ${msg}" echo "==> ERROR: ${msg}"
exit 1 exit 1
} }
@ -60,7 +60,7 @@ TMP_IMG="$TMP_DIR/box.img"
mkdir -p "$TMP_DIR" 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 # We move / copy (when the image has master) the image to the tempdir
# ensure that it's moved back / removed again # ensure that it's moved back / removed again
@ -70,6 +70,10 @@ if [[ -n $(backing "$IMG") ]]; then
cp "$IMG" "$TMP_IMG" cp "$IMG" "$TMP_IMG"
rebase "$TMP_IMG" rebase "$TMP_IMG"
else 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 # move the image to get a speed-up and use less space on disk
trap 'mv "$TMP_IMG" "$IMG"; rm -rf "$TMP_DIR"' EXIT trap 'mv "$TMP_IMG" "$IMG"; rm -rf "$TMP_DIR"' EXIT
mv "$IMG" "$TMP_IMG" mv "$IMG" "$TMP_IMG"

View File

@ -20,10 +20,8 @@ Gem::Specification.new do |gem|
gem.add_development_dependency "rspec-expectations", "~> 2.12.1" gem.add_development_dependency "rspec-expectations", "~> 2.12.1"
gem.add_development_dependency "rspec-mocks", "~> 2.12.1" gem.add_development_dependency "rspec-mocks", "~> 2.12.1"
gem.add_runtime_dependency 'fog', '~> 1.15' gem.add_runtime_dependency 'fog-libvirt', '~> 0.0.1'
gem.add_runtime_dependency 'ruby-libvirt', '~> 0.4'
gem.add_runtime_dependency 'nokogiri', '~> 1.6.0' gem.add_runtime_dependency 'nokogiri', '~> 1.6.0'
gem.add_development_dependency 'rake' gem.add_development_dependency 'rake'
end end