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

112
README.md
View File

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

View File

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

View File

@ -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
@ -158,8 +155,15 @@ module VagrantPlugins
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsCreated do |env, b2|
if !env[:result]
# 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

View File

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

View File

@ -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
@ -53,6 +55,9 @@ module VagrantPlugins
@video_vram = config.video_vram
@keymap = config.keymap
# Boot order
@boot_order = config.boot_order
# Storage
@storage_pool_name = config.storage_pool_name
@disks = config.disks
@ -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 <path> element inside <target>
@ -87,19 +94,24 @@ module VagrantPlugins
disk[:absolute_path] = storage_prefix + disk[:path]
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 <path> 5g
begin
domain_volume_disk = env[:libvirt_compute].volumes.create(
:name => disk[:name],
:format_type => disk[:type],
:path => disk[:absolute_path],
:capacity => disk[:size],
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)
pool_name: @storage_pool_name)
rescue Fog::Errors::Error => e
raise Errors::FogDomainVolumeCreateError,
:error_message => e.message
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

View File

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

View File

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

View File

@ -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,26 +127,33 @@ 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])
@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
# 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
@interface_network[:network_address] = net_address
# 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
@ -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

View File

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

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] << "_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

View File

@ -14,14 +14,16 @@ module VagrantPlugins
end
def call(env)
@@lock.synchronize do
# 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.")

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,4 +1,7 @@
require 'nokogiri'
require 'socket'
require 'timeout'
module VagrantPlugins
module ProviderLibvirt
module Action
@ -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
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
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}
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]
# 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
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
nil
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

View File

@ -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) {
begin
domain.wait_for(2) do
addresses.each_pair do |type, ip|
ip_address = ip[0] if ip[0] != nil
# 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
}
raise Errors::NoIpAddressError if not ip_address
end
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,
@ -47,7 +58,7 @@ module VagrantPlugins
: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

View File

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

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

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

View File

@ -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
# <filesystem/> 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 }
folder_opts.merge!({ target: id,
accessmode: 'passthrough',
readonly: nil }) { |_k, ov, _nv| ov }
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 )
xml = to_xml('filesystem', folder_opts)
# puts "<<<<< XML:\n #{xml}\n >>>>>"
@conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0)
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,5 +6,10 @@
<target dev='vnet<%= @iface_number %>'/>
<alias name='net<%= @iface_number %>'/>
<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>

View File

@ -15,6 +15,13 @@
<% if @network_dhcp_enabled %>
<dhcp>
<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>
<% end %>
</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 ProviderLibvirt
VERSION = '0.0.25'
VERSION = '0.0.30'
end
end

View File

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

View File

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

View File

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

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