mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2024-07-04 11:22:56 -05:00
Initial commit
This commit is contained in:
commit
810f0b31b9
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
doc/
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
||||
Vagrantfile
|
||||
.vagrant
|
12
Gemfile
Normal file
12
Gemfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in vagrant-libvirt.gemspec
|
||||
gemspec
|
||||
|
||||
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"
|
||||
end
|
||||
|
22
LICENSE
Normal file
22
LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2013 Lukas Stanek
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
178
README.md
Normal file
178
README.md
Normal file
|
@ -0,0 +1,178 @@
|
|||
# Vagrant Libvirt Provider
|
||||
|
||||
This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that adds an
|
||||
[Libvirt](http://libvirt.org) provider to Vagrant, allowing Vagrant to
|
||||
control and provision machines via Libvirt toolkit.
|
||||
|
||||
This plugin is inspired by existing [vagrant-aws](https://github.com/mitchellh/vagrant-aws) provider.
|
||||
|
||||
**Note:** This plugin requires Vagrant 1.1+.
|
||||
|
||||
## Features
|
||||
|
||||
* Upload box image (qcow2 format) to Libvirt storage pool.
|
||||
* Create volume as COW diff image for domains.
|
||||
* Create and boot Libvirt domains.
|
||||
* SSH into domains.
|
||||
* Provision domains with any built-in Vagrant provisioner.
|
||||
* Minimal synced folder support via `rsync`.
|
||||
|
||||
## Usage
|
||||
|
||||
Install using standard Vagrant 1.1+ plugin installation methods. After
|
||||
installing, `vagrant up` and specify the `libvirt` provider. An example is
|
||||
shown below.
|
||||
|
||||
```
|
||||
$ vagrant plugin install vagrant-libvirt
|
||||
...
|
||||
$ vagrant up --provider=libvirt
|
||||
...
|
||||
```
|
||||
|
||||
Of course prior to doing this, you'll need to obtain an Libvirt-compatible
|
||||
box file for Vagrant.
|
||||
|
||||
### Problems with plugin installation
|
||||
|
||||
In case of problems with building nokogiri gem, install missing development
|
||||
libraries libxslt and libxml2.
|
||||
|
||||
In Ubuntu, Debian, ...
|
||||
```
|
||||
$ sudo apt-get install libxslt-dev libxml2-dev
|
||||
```
|
||||
|
||||
In RedHat, Centos, Fedora, ...
|
||||
```
|
||||
# yum install libxslt-devel libxml2-devel
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
After installing the plugin (instructions above), the quickest way to get
|
||||
started is to add Libvirt box and specify all the details manually within
|
||||
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
|
||||
...
|
||||
```
|
||||
|
||||
And then make a Vagrantfile that looks like the following, filling in
|
||||
your information where necessary.
|
||||
|
||||
```
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define :test_vm do |test_vm|
|
||||
test_vm.vm.box = "centos64"
|
||||
end
|
||||
|
||||
config.vm.provider :libvirt do |libvirt|
|
||||
libvirt.driver = "qemu"
|
||||
libvirt.host = "example.com"
|
||||
libvirt.connect_via_ssh = true
|
||||
libvirt.username = "root"
|
||||
#libvirt.password = "secret"
|
||||
libvirt.storage_pool_name = "default"
|
||||
end
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
And then run `vagrant up --provider=libvirt`.
|
||||
|
||||
This will first upload box image to remote Libvirt storage pool as new volume.
|
||||
Then create and start a CentOS 6.4 domain on example.com Libvirt host. In this
|
||||
example configuration, connection to Libvirt is tunneled via SSH.
|
||||
|
||||
## Box Format
|
||||
|
||||
Every provider in Vagrant must introduce a custom box format. This
|
||||
provider introduces `Libvirt` boxes. You can view an example box in
|
||||
the [example_box/directory](https://github.com/pradels/vagrant-libvirt/tree/master/example_box). That directory also contains instructions on how to build a box.
|
||||
|
||||
The box format is qcow2 image file `box.img`, the required `metadata.json` file
|
||||
along with a `Vagrantfile` that does default settings for the
|
||||
provider-specific configuration for this provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
This provider exposes quite a few provider-specific configuration options:
|
||||
|
||||
* `driver` - A hypervisor name to access. For now only qemu is supported.
|
||||
* `host` - The name of the server, where libvirtd is running.
|
||||
* `connect_via_ssh` - If use ssh tunnel to connect to Libvirt.
|
||||
* `username` - Username and password to access Libvirt.
|
||||
* `password` - Password to access Libvirt.
|
||||
* `storage_pool_name` - Libvirt storage pool name, where box image and
|
||||
instance snapshots will be stored.
|
||||
|
||||
## Networks
|
||||
|
||||
Networking features in the form of `config.vm.network` are supported only
|
||||
in bridged format, no hostonly network is supported in current version of
|
||||
provider.
|
||||
|
||||
Example of network interface definition:
|
||||
|
||||
```
|
||||
config.vm.define :test_vm do |test_vm|
|
||||
test_vm.vm.network :bridged, :bridge => "default", :adapter => 1
|
||||
end
|
||||
```
|
||||
|
||||
Bridged network adapter connected to network `default` is defined.
|
||||
|
||||
## Getting IP address
|
||||
|
||||
There is a little problem to find out which IP address was assigned to remote
|
||||
domain. Fog library uses SSH connection to remote libvirt host and by default
|
||||
checks arpwatch entries there. Libvirt provider uses just arp table. There is no
|
||||
need to install and setup arpwatch, but information about MAC->IP address
|
||||
mapping is lost after short time. That is why there are no ssh or provision
|
||||
actions available yet.
|
||||
|
||||
## Synced Folders
|
||||
|
||||
There is minimal support for synced folders. Upon `vagrant up`, the Libvirt
|
||||
provider will use `rsync` (if available) to uni-directionally sync the folder
|
||||
to the remote machine over SSH.
|
||||
|
||||
This is good enough for all built-in Vagrant provisioners (shell,
|
||||
chef, and puppet) to work!
|
||||
|
||||
## Development
|
||||
|
||||
To work on the `vagrant-libvirt` plugin, clone this repository out, and use
|
||||
[Bundler](http://gembundler.com) to get the dependencies:
|
||||
|
||||
```
|
||||
$ bundle
|
||||
```
|
||||
|
||||
Once you have the dependencies, verify the unit tests pass with `rake`:
|
||||
|
||||
```
|
||||
$ bundle exec rake
|
||||
```
|
||||
|
||||
If those pass, you're ready to start developing the plugin. You can test
|
||||
the plugin without installing it into your Vagrant environment by just
|
||||
creating a `Vagrantfile` in the top level of this directory (it is gitignored)
|
||||
that uses it, and uses bundler to execute Vagrant:
|
||||
|
||||
```
|
||||
$ bundle exec vagrant up --provider=libvirt
|
||||
```
|
||||
|
||||
## Future work
|
||||
|
||||
* Read cpu and memory settings from config.
|
||||
* Hostonly networks.
|
||||
* Use Libvirt shared folder, not rsync if machine is local.
|
||||
* Test if arpwatch is available for getting MAC->IP mapping.
|
||||
* Provision, ssh, reload, halt, resume actions.
|
||||
* Support other domain types than KVM.
|
||||
|
7
Rakefile
Normal file
7
Rakefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env rake
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
#require 'bundler/gem_tasks'
|
||||
Bundler::GemHelper.install_tasks
|
||||
|
23
example_box/README.md
Normal file
23
example_box/README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Vagrant Libvirt Example Box
|
||||
|
||||
Vagrant providers each require a custom provider-specific box format.
|
||||
This folder shows the example contents of a box for the `libvirt` provider.
|
||||
To turn this into a box create a vagrant image according documentation (don't
|
||||
forget to install rsync command) and create box with following command:
|
||||
|
||||
```
|
||||
$ tar cvzf custom_box.box ./metadata.json ./Vagrantfile ./box.img
|
||||
```
|
||||
|
||||
This box works by using Vagrant's built-in Vagrantfile merging to setup
|
||||
defaults for Libvirt. These defaults can easily be overwritten by higher-level
|
||||
Vagrantfiles (such as project root Vagrantfiles).
|
||||
|
||||
## Box Metadata
|
||||
|
||||
Libvirt box should define at least three data fields in `metadata.json` file.
|
||||
|
||||
* provider - Provider name is libvirt.
|
||||
* format - Currently supported format is qcow2.
|
||||
* virtual_size - Virtual size of image in GBytes.
|
||||
|
5
example_box/metadata.json
Normal file
5
example_box/metadata.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"provider" : "libvirt"
|
||||
"format" : "qcow2"
|
||||
"virtual_size" : "40"
|
||||
}
|
30
lib/vagrant-libvirt.rb
Normal file
30
lib/vagrant-libvirt.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'pathname'
|
||||
require 'vagrant-libvirt/plugin'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
lib_path = Pathname.new(File.expand_path("../vagrant-libvirt", __FILE__))
|
||||
autoload :Action, lib_path.join("action")
|
||||
autoload :Errors, lib_path.join("errors")
|
||||
autoload :Util, lib_path.join("util")
|
||||
|
||||
# Hold connection handler so there is no need to connect more times than
|
||||
# one. This can be annoying when there are more machines to create, or when
|
||||
# doing state action first and then some other.
|
||||
#
|
||||
# TODO Don't sure if this is the best solution
|
||||
@@libvirt_connection = nil
|
||||
def self.libvirt_connection
|
||||
@@libvirt_connection
|
||||
end
|
||||
|
||||
def self.libvirt_connection=(conn)
|
||||
@@libvirt_connection = conn
|
||||
end
|
||||
|
||||
def self.source_root
|
||||
@source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
94
lib/vagrant-libvirt/action.rb
Normal file
94
lib/vagrant-libvirt/action.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
require 'vagrant/action/builder'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# Include the built-in modules so we can use them as top-level things.
|
||||
include Vagrant::Action::Builtin
|
||||
|
||||
# This action is called to bring the box up from nothing.
|
||||
def self.action_up
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use ConfigValidate
|
||||
b.use ConnectLibvirt
|
||||
b.use Call, IsCreated do |env, b2|
|
||||
if env[:result]
|
||||
b2.use MessageAlreadyCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use SetNameOfDomain
|
||||
b2.use HandleStoragePool
|
||||
b2.use HandleBoxImage
|
||||
b2.use CreateDomainVolume
|
||||
b2.use CreateDomain
|
||||
b2.use CreateNetworkInterfaces
|
||||
end
|
||||
|
||||
b.use TimedProvision
|
||||
b.use StartDomain
|
||||
b.use WaitTillUp
|
||||
b.use SyncFolders
|
||||
end
|
||||
end
|
||||
|
||||
# This is the action that is primarily responsible for completely
|
||||
# freeing the resources of the underlying virtual machine.
|
||||
def self.action_destroy
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use ConfigValidate
|
||||
b.use Call, IsCreated do |env, b2|
|
||||
if !env[:result]
|
||||
b2.use MessageNotCreated
|
||||
next
|
||||
end
|
||||
|
||||
b2.use ConnectLibvirt
|
||||
b2.use DestroyDomain
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This action is called to read the state of the machine. The resulting
|
||||
# state is expected to be put into the `:machine_state_id` key.
|
||||
def self.action_read_state
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use ConfigValidate
|
||||
b.use ConnectLibvirt
|
||||
b.use ReadState
|
||||
end
|
||||
end
|
||||
|
||||
# This action is called to read the SSH info of the machine. The
|
||||
# resulting state is expected to be put into the `:machine_ssh_info`
|
||||
# key.
|
||||
def self.action_read_ssh_info
|
||||
Vagrant::Action::Builder.new.tap do |b|
|
||||
b.use ConfigValidate
|
||||
b.use ConnectLibvirt
|
||||
b.use ReadSSHInfo
|
||||
end
|
||||
end
|
||||
|
||||
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
||||
autoload :ConnectLibvirt, action_root.join("connect_libvirt")
|
||||
autoload :IsCreated, action_root.join("is_created")
|
||||
autoload :MessageAlreadyCreated, action_root.join("message_already_created")
|
||||
autoload :MessageNotCreated, action_root.join("message_not_created")
|
||||
autoload :HandleStoragePool, action_root.join("handle_storage_pool")
|
||||
autoload :HandleBoxImage, action_root.join("handle_box_image")
|
||||
autoload :SetNameOfDomain, action_root.join("set_name_of_domain")
|
||||
autoload :CreateDomainVolume, action_root.join("create_domain_volume")
|
||||
autoload :CreateDomain, action_root.join("create_domain")
|
||||
autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
|
||||
autoload :DestroyDomain, action_root.join("destroy_domain")
|
||||
autoload :StartDomain, action_root.join("start_domain")
|
||||
autoload :ReadState, action_root.join("read_state")
|
||||
autoload :ReadSSHInfo, action_root.join("read_ssh_info")
|
||||
autoload :TimedProvision, action_root.join("timed_provision")
|
||||
autoload :WaitTillUp, action_root.join("wait_till_up")
|
||||
autoload :SyncFolders, action_root.join("sync_folders")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
72
lib/vagrant-libvirt/action/connect_libvirt.rb
Normal file
72
lib/vagrant-libvirt/action/connect_libvirt.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
require 'fog'
|
||||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class ConnectLibvirt
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::connect_libvirt")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
||||
# If already connected to libvirt, just use it and don't connect
|
||||
# again.
|
||||
if Libvirt.libvirt_connection
|
||||
env[:libvirt_compute] = Libvirt.libvirt_connection
|
||||
return @app.call(env)
|
||||
end
|
||||
|
||||
# Get config options for libvirt provider.
|
||||
config = env[:machine].provider_config
|
||||
|
||||
# Setup connection uri.
|
||||
uri = config.driver
|
||||
if config.connect_via_ssh
|
||||
uri << '+ssh://'
|
||||
if config.username
|
||||
uri << config.username + '@'
|
||||
end
|
||||
|
||||
if config.host
|
||||
uri << config.host
|
||||
else
|
||||
uri << 'localhost'
|
||||
end
|
||||
else
|
||||
uri << '://'
|
||||
uri << config.host if config.host
|
||||
end
|
||||
uri << '/system?no_verify=1'
|
||||
|
||||
conn_attr = {}
|
||||
conn_attr[:provider] = 'libvirt'
|
||||
conn_attr[:libvirt_uri] = uri
|
||||
conn_attr[:libvirt_username] = config.username if config.username
|
||||
conn_attr[:libvirt_password] = config.password if config.password
|
||||
|
||||
# Setup command for retrieving IP address for newly created machine
|
||||
# with some MAC address. Get it via arp table. This solution doesn't
|
||||
# require arpwatch to be installed.
|
||||
conn_attr[:libvirt_ip_command] = "arp -an | grep $mac | sed '"
|
||||
conn_attr[:libvirt_ip_command] << 's/.*(\([0-9\.]*\)).*/\1/'
|
||||
conn_attr[:libvirt_ip_command] << "'"
|
||||
|
||||
@logger.info("Connecting to Libvirt (#{uri}) ...")
|
||||
begin
|
||||
env[:libvirt_compute] = Fog::Compute.new(conn_attr)
|
||||
rescue Fog::Errors::Error => e
|
||||
raise Errors::FogLibvirtConnectionError,
|
||||
:error_message => e.message
|
||||
end
|
||||
Libvirt.libvirt_connection = env[:libvirt_compute]
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
62
lib/vagrant-libvirt/action/create_domain.rb
Normal file
62
lib/vagrant-libvirt/action/create_domain.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
class CreateDomain
|
||||
include VagrantPlugins::Libvirt::Util::ErbTemplate
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_domain")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Gather some info about domain
|
||||
# TODO from Vagrantfile
|
||||
@name = env[:domain_name]
|
||||
@cpus = 1
|
||||
@memory_size = 512*1024
|
||||
|
||||
# TODO get type from driver config option
|
||||
@domain_type = 'kvm'
|
||||
|
||||
@os_type = 'hvm'
|
||||
|
||||
# Get path to domain image.
|
||||
domain_volume = Libvirt::Util::Collection.find_matching(
|
||||
env[:libvirt_compute].volumes.all, "#{@name}.img")
|
||||
raise Errors::DomainVolumeExists if domain_volume == nil
|
||||
@domain_volume_path = domain_volume.path
|
||||
|
||||
# Output the settings we're going to use to the user
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.creating_domain"))
|
||||
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(" -- Base box: #{env[:machine].box.name}")
|
||||
env[:ui].info(" -- Image: #{@domain_volume_path}")
|
||||
|
||||
# Create libvirt domain.
|
||||
# Is there a way to tell fog to create new domain with already
|
||||
# existing volume? Use domain creation from template..
|
||||
begin
|
||||
server = env[:libvirt_compute].servers.create(
|
||||
:xml => to_xml('domain'))
|
||||
rescue Fog::Errors::Error => e
|
||||
raise Errors::FogCreateServerError,
|
||||
:error_message => e.message
|
||||
end
|
||||
|
||||
# Immediately save the ID since it is created at this point.
|
||||
env[:machine].id = server.id
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
58
lib/vagrant-libvirt/action/create_domain_volume.rb
Normal file
58
lib/vagrant-libvirt/action/create_domain_volume.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
# Create a snapshot of base box image. This new snapshot is just new
|
||||
# cow image with backing storage pointing to base box image. Use this
|
||||
# image as new domain volume.
|
||||
class CreateDomainVolume
|
||||
include VagrantPlugins::Libvirt::Util::ErbTemplate
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_domain_volume")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.creating_domain_volume"))
|
||||
|
||||
# Get config options.
|
||||
config = env[:machine].provider_config
|
||||
|
||||
# This is name of newly created image for vm.
|
||||
@name = "#{env[:domain_name]}.img"
|
||||
|
||||
# Verify the volume doesn't exist already.
|
||||
domain_volume = Libvirt::Util::Collection.find_matching(
|
||||
env[:libvirt_compute].volumes.all, @name)
|
||||
raise Errors::DomainVolumeExists if domain_volume
|
||||
|
||||
# Get path to backing image - box volume.
|
||||
box_volume = Libvirt::Util::Collection.find_matching(
|
||||
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
|
||||
|
||||
# Create new volume from xml template. Fog currently doesn't support
|
||||
# volume snapshots directly.
|
||||
begin
|
||||
domain_volume = env[:libvirt_compute].volumes.create(
|
||||
:xml => to_xml('volume_snapshot'),
|
||||
:pool_name => config.storage_pool_name)
|
||||
rescue Fog::Errors::Error => e
|
||||
raise Errors::FogDomainVolumeCreateError,
|
||||
:error_message => e.message
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
85
lib/vagrant-libvirt/action/create_network_interfaces.rb
Normal file
85
lib/vagrant-libvirt/action/create_network_interfaces.rb
Normal file
|
@ -0,0 +1,85 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
# Create network interfaces for domain, before domain is running.
|
||||
class CreateNetworkInterfaces
|
||||
include VagrantPlugins::Libvirt::Util::ErbTemplate
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_network_interfaces")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Get domain first.
|
||||
begin
|
||||
domain = env[:libvirt_compute].client.lookup_domain_by_uuid(
|
||||
env[:machine].id.to_s)
|
||||
rescue => e
|
||||
raise Errors::NoDomainError,
|
||||
:error_message => e.message
|
||||
end
|
||||
|
||||
# Setup list of interfaces before creating them
|
||||
adapters = []
|
||||
|
||||
# Assign main interface for provisioning to first slot.
|
||||
# Use network 'default' as network for ssh connecting and
|
||||
# machine provisioning. This should be maybe configurable in
|
||||
# Vagrantfile in future.
|
||||
adapters[0] = 'default'
|
||||
|
||||
env[:machine].config.vm.networks.each do |type, options|
|
||||
# Other types than bridged are not supported for now.
|
||||
next if type != :bridged
|
||||
|
||||
network_name = 'default'
|
||||
network_name = options[:bridge] if options[:bridge]
|
||||
|
||||
if options[:adapter]
|
||||
if adapters[options[:adapter]]
|
||||
raise Errors::InterfaceSlotNotAvailable
|
||||
end
|
||||
|
||||
adapters[options[:adapter].to_i] = network_name
|
||||
else
|
||||
empty_slot = find_empty(adapters, start=1)
|
||||
raise Errors::InterfaceSlotNotAvailable if empty_slot == nil
|
||||
|
||||
adapters[empty_slot] = network_name
|
||||
end
|
||||
end
|
||||
|
||||
# Create each interface as new domain device
|
||||
adapters.each_with_index do |network_name, slot_number|
|
||||
@iface_number = slot_number
|
||||
@network_name = network_name
|
||||
@logger.info("Creating network interface eth#{@iface_number}")
|
||||
begin
|
||||
domain.attach_device(to_xml('interface'))
|
||||
rescue => e
|
||||
raise Errors::AttachDeviceError,
|
||||
:error_message => e.message
|
||||
end
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_empty(array, start=0, stop=8)
|
||||
for i in start..stop
|
||||
return i if !array[i]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
28
lib/vagrant-libvirt/action/destroy_domain.rb
Normal file
28
lib/vagrant-libvirt/action/destroy_domain.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class DestroyDomain
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_domain")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
||||
# Destroy the server and remove the tracking ID
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.destroy_domain"))
|
||||
|
||||
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
|
||||
domain.destroy(:destroy_volumes => true)
|
||||
env[:machine].id = nil
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
121
lib/vagrant-libvirt/action/handle_box_image.rb
Normal file
121
lib/vagrant-libvirt/action/handle_box_image.rb
Normal file
|
@ -0,0 +1,121 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class HandleBoxImage
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::handle_box_image")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
||||
# Verify box metadata for mandatory values.
|
||||
#
|
||||
# Virtual size has to be set for allocating space in storage pool.
|
||||
box_virtual_size = env[:machine].box.metadata['virtual_size']
|
||||
if box_virtual_size == nil
|
||||
raise Errors::NoBoxVirtualSizeSet
|
||||
end
|
||||
|
||||
# Support qcow2 format only for now, but other formats with backing
|
||||
# store capability should be usable.
|
||||
box_format = env[:machine].box.metadata['format']
|
||||
if box_format == nil
|
||||
raise Errors::NoBoxFormatSet
|
||||
elsif box_format != 'qcow2'
|
||||
raise Errors::WrongBoxFormatSet
|
||||
end
|
||||
|
||||
# Get config options
|
||||
config = env[:machine].provider_config
|
||||
box_image_file = env[:machine].box.directory.join("box.img").to_s
|
||||
env[:box_volume_name] = env[:machine].box.name.to_s.dup
|
||||
env[:box_volume_name] << '_vagrant_box_image.img'
|
||||
|
||||
# Don't continue if image already exists in storage pool.
|
||||
return @app.call(env) if Libvirt::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
|
||||
# it as a copy of local box image.
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.uploading_volume"))
|
||||
|
||||
# Create new volume in storage pool
|
||||
box_image_size = File.size(box_image_file) # B
|
||||
message = "Creating volume #{env[:box_volume_name]}"
|
||||
message << " in storage pool #{config.storage_pool_name}."
|
||||
@logger.info(message)
|
||||
begin
|
||||
fog_volume = env[:libvirt_compute].volumes.create(
|
||||
:name => env[:box_volume_name],
|
||||
:allocation => "#{box_image_size/1024/1024}M",
|
||||
:capacity => "#{box_virtual_size}G",
|
||||
:format_type => box_format,
|
||||
:pool_name => config.storage_pool_name)
|
||||
rescue Fog::Errors::Error => e
|
||||
raise Errors::FogCreateVolumeError,
|
||||
:error_message => e.message
|
||||
end
|
||||
|
||||
# Upload box image to storage pool
|
||||
ret = upload_image(box_image_file, config.storage_pool_name,
|
||||
env[:box_volume_name], env) do |progress|
|
||||
env[:ui].clear_line
|
||||
env[:ui].report_progress(progress, box_image_size, false)
|
||||
end
|
||||
|
||||
# Clear the line one last time since the progress meter doesn't
|
||||
# disappear immediately.
|
||||
env[:ui].clear_line
|
||||
|
||||
# If upload failed or was interrupted, remove created volume from
|
||||
# storage pool.
|
||||
if env[:interrupted] or !ret
|
||||
begin
|
||||
fog_volume.destroy
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Fog libvirt currently doesn't support uploading images to storage
|
||||
# pool volumes. Use ruby-libvirt client instead.
|
||||
def upload_image(image_file, pool_name, volume_name, env)
|
||||
image_size = File.size(image_file) # B
|
||||
|
||||
begin
|
||||
pool = env[:libvirt_compute].client.lookup_storage_pool_by_name(
|
||||
pool_name)
|
||||
volume = pool.lookup_volume_by_name(volume_name)
|
||||
stream = env[:libvirt_compute].client.stream
|
||||
volume.upload(stream, offset=0, length=image_size)
|
||||
buf_size = 1024*1024 # 1M
|
||||
progress = 0
|
||||
open(image_file, 'rb') do |io|
|
||||
while (buff = io.read(buf_size)) do
|
||||
sent = stream.send buff
|
||||
progress += sent
|
||||
yield progress
|
||||
end
|
||||
end
|
||||
rescue => e
|
||||
raise Errors::ImageUploadError,
|
||||
:error_message => e.message
|
||||
end
|
||||
|
||||
return true if progress == image_size
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
49
lib/vagrant-libvirt/action/handle_storage_pool.rb
Normal file
49
lib/vagrant-libvirt/action/handle_storage_pool.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class HandleStoragePool
|
||||
include VagrantPlugins::Libvirt::Util::ErbTemplate
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::handle_storage_pool")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Get config options.
|
||||
config = env[:machine].provider_config
|
||||
|
||||
# Check for storage pool, where box image should be created
|
||||
fog_pool = Libvirt::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.")
|
||||
|
||||
# If user specified other pool than default, don't create default
|
||||
# storage pool, just write error message.
|
||||
raise Errors::NoStoragePool if config.storage_pool_name != 'default'
|
||||
|
||||
@logger.info("Creating storage pool 'default'")
|
||||
|
||||
# Fog libvirt currently doesn't support creating pools. Use
|
||||
# ruby-libvirt client directly.
|
||||
begin
|
||||
libvirt_pool = env[:libvirt_compute].client.create_storage_pool_xml(
|
||||
to_xml('default_storage_pool'))
|
||||
rescue => e
|
||||
raise Errors::CreatingStoragePoolError,
|
||||
:error_message => e.message
|
||||
end
|
||||
raise Errors::NoStoragePool if !libvirt_pool
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
18
lib/vagrant-libvirt/action/is_created.rb
Normal file
18
lib/vagrant-libvirt/action/is_created.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# This can be used with "Call" built-in to check if the machine
|
||||
# is created and branch in the middleware.
|
||||
class IsCreated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:result] = env[:machine].state.id != :not_created
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
16
lib/vagrant-libvirt/action/message_already_created.rb
Normal file
16
lib/vagrant-libvirt/action/message_already_created.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class MessageAlreadyCreated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.already_created"))
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
16
lib/vagrant-libvirt/action/message_not_created.rb
Normal file
16
lib/vagrant-libvirt/action/message_not_created.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
class MessageNotCreated
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.not_created"))
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
51
lib/vagrant-libvirt/action/read_ssh_info.rb
Normal file
51
lib/vagrant-libvirt/action/read_ssh_info.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
require "log4r"
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# This action reads the SSH info for the machine and puts it into the
|
||||
# `:machine_ssh_info` key in the environment.
|
||||
class ReadSSHInfo
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::read_ssh_info")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:machine_ssh_info] = read_ssh_info(
|
||||
env[:libvirt_compute], env[:machine])
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
|
||||
def read_ssh_info(libvirt, machine)
|
||||
return nil if machine.id.nil?
|
||||
|
||||
# Find the machine
|
||||
server = libvirt.servers.get(machine.id)
|
||||
if server.nil?
|
||||
# The machine can't be found
|
||||
@logger.info("Machine couldn't be found, assuming it got destroyed.")
|
||||
machine.id = nil
|
||||
return nil
|
||||
end
|
||||
|
||||
# Get ip address of machine
|
||||
ip_address = server.public_ip_address
|
||||
ip_address = server.private_ip_address if ip_address == nil
|
||||
return nil if ip_address == nil
|
||||
|
||||
# Return the info
|
||||
# TODO: Some info should be configurable in Vagrantfile
|
||||
return {
|
||||
:host => ip_address,
|
||||
:port => 22,
|
||||
:username => 'root',
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
38
lib/vagrant-libvirt/action/read_state.rb
Normal file
38
lib/vagrant-libvirt/action/read_state.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
require "log4r"
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# This action reads the state of the machine and puts it in the
|
||||
# `:machine_state_id` key in the environment.
|
||||
class ReadState
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::read_state")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:machine_state_id] = read_state(env[:libvirt_compute], env[:machine])
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def read_state(libvirt, machine)
|
||||
return :not_created if machine.id.nil?
|
||||
|
||||
# Find the machine
|
||||
server = libvirt.servers.get(machine.id)
|
||||
if server.nil? || [:"shutting-down", :terminated].include?(server.state.to_sym)
|
||||
# The machine can't be found
|
||||
@logger.info("Machine not found or terminated, assuming it got destroyed.")
|
||||
machine.id = nil
|
||||
return :not_created
|
||||
end
|
||||
|
||||
# Return the state
|
||||
return server.state.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
lib/vagrant-libvirt/action/set_name_of_domain.rb
Normal file
31
lib/vagrant-libvirt/action/set_name_of_domain.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
# Setup name for domain and domain volumes.
|
||||
class SetNameOfDomain
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:domain_name] = env[:root_path].basename.to_s.dup
|
||||
env[:domain_name].gsub!(/[^-a-z0-9_]/i, "")
|
||||
env[:domain_name] << "_#{Time.now.to_i}"
|
||||
|
||||
# Check if the domain name is not already taken
|
||||
domain = Libvirt::Util::Collection.find_matching(
|
||||
env[:libvirt_compute].servers.all, env[:domain_name])
|
||||
if domain != nil
|
||||
raise Vagrant::Errors::DomainNameExists,
|
||||
:domain_name => env[:domain_name]
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
27
lib/vagrant-libvirt/action/start_domain.rb
Normal file
27
lib/vagrant-libvirt/action/start_domain.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'log4r'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
# Just start the domain.
|
||||
class StartDomain
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::start_domain")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.starting_domain"))
|
||||
|
||||
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
|
||||
raise Errors::NoDomainError if domain == nil
|
||||
domain.start
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
58
lib/vagrant-libvirt/action/sync_folders.rb
Normal file
58
lib/vagrant-libvirt/action/sync_folders.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
require "log4r"
|
||||
require "vagrant/util/subprocess"
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# This middleware uses `rsync` to sync the folders over to the
|
||||
# libvirt domain.
|
||||
class SyncFolders
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::sync_folders")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@app.call(env)
|
||||
|
||||
ssh_info = env[:machine].ssh_info
|
||||
|
||||
env[:machine].config.vm.synced_folders.each do |id, data|
|
||||
hostpath = File.expand_path(data[:hostpath], env[:root_path])
|
||||
guestpath = data[:guestpath]
|
||||
|
||||
# Make sure there is a trailing slash on the host path to
|
||||
# avoid creating an additional directory with rsync
|
||||
hostpath = "#{hostpath}/" if hostpath !~ /\/$/
|
||||
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.rsync_folder",
|
||||
:hostpath => hostpath,
|
||||
:guestpath => guestpath))
|
||||
|
||||
# Create the guest path
|
||||
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
|
||||
env[:machine].communicate.sudo(
|
||||
"chown #{ssh_info[:username]} '#{guestpath}'")
|
||||
|
||||
# Rsync over to the guest path using the SSH info
|
||||
command = [
|
||||
"rsync", "--verbose", "--archive", "-z",
|
||||
"--exclude", ".vagrant/",
|
||||
"-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no -i '#{ssh_info[:private_key_path]}'",
|
||||
hostpath,
|
||||
"#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
|
||||
|
||||
r = Vagrant::Util::Subprocess.execute(*command)
|
||||
if r.exit_code != 0
|
||||
raise Errors::RsyncError,
|
||||
:guestpath => guestpath,
|
||||
:hostpath => hostpath,
|
||||
:stderr => r.stderr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
21
lib/vagrant-libvirt/action/timed_provision.rb
Normal file
21
lib/vagrant-libvirt/action/timed_provision.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "vagrant-libvirt/util/timer"
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
# This is the same as the builtin provision except it times the
|
||||
# provisioner runs.
|
||||
class TimedProvision < Vagrant::Action::Builtin::Provision
|
||||
def run_provisioner(env, p)
|
||||
timer = Util::Timer.time do
|
||||
super
|
||||
end
|
||||
|
||||
env[:metrics] ||= {}
|
||||
env[:metrics]["provisioner_times"] ||= []
|
||||
env[:metrics]["provisioner_times"] << [p.class.to_s, timer]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
96
lib/vagrant-libvirt/action/wait_till_up.rb
Normal file
96
lib/vagrant-libvirt/action/wait_till_up.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
require 'log4r'
|
||||
require 'vagrant-libvirt/util/timer'
|
||||
require 'vagrant/util/retryable'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Action
|
||||
|
||||
# Wait till domain is started, till it obtains an IP address and is
|
||||
# accessible via ssh.
|
||||
class WaitTillUp
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant_libvirt::action::wait_till_up")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# Initialize metrics if they haven't been
|
||||
env[:metrics] ||= {}
|
||||
|
||||
# Get domain object
|
||||
domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
|
||||
raise NoDomainError if domain == nil
|
||||
|
||||
# Wait for domain to obtain an ip address. Ip address is searched
|
||||
# from arp table, either localy or remotely via ssh, if libvirt
|
||||
# connection was done via ssh.
|
||||
env[:ip_address] = nil
|
||||
env[:metrics]["instance_ip_time"] = Util::Timer.time do
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.waiting_for_ip"))
|
||||
retryable(:on => Fog::Errors::TimeoutError, :tries => 300) do
|
||||
# If we're interrupted don't worry about waiting
|
||||
next if env[:interrupted]
|
||||
|
||||
# Wait for domain to obtain an ip address
|
||||
domain.wait_for(2) {
|
||||
addresses.each_pair do |type, ip|
|
||||
env[:ip_address] = ip[0]
|
||||
end
|
||||
env[:ip_address] != nil
|
||||
}
|
||||
end
|
||||
end
|
||||
terminate(env) if env[:interrupted]
|
||||
@logger.info("Got IP address #{env[:ip_address]}")
|
||||
@logger.info("Time for getting IP: #{env[:metrics]["instance_ip_time"]}")
|
||||
|
||||
# Machine has ip address assigned, now wait till we are able to
|
||||
# connect via ssh.
|
||||
env[:metrics]["instance_ssh_time"] = Util::Timer.time do
|
||||
env[:ui].info(I18n.t("vagrant_libvirt.waiting_for_ssh"))
|
||||
retryable(:on => Fog::Errors::TimeoutError, :tries => 60) do
|
||||
# If we're interrupted don't worry about waiting
|
||||
next if env[:interrupted]
|
||||
|
||||
# Wait till we are able to connect via ssh.
|
||||
while true
|
||||
# If we're interrupted then just back out
|
||||
break if env[:interrupted]
|
||||
break if env[:machine].communicate.ready?
|
||||
sleep 2
|
||||
end
|
||||
end
|
||||
end
|
||||
terminate(env) if env[:interrupted]
|
||||
@logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
|
||||
|
||||
# Booted and ready for use.
|
||||
#env[:ui].info(I18n.t("vagrant_libvirt.ready"))
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def recover(env)
|
||||
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
|
||||
|
||||
if env[:machine].provider.state.id != :not_created
|
||||
# Undo the import
|
||||
terminate(env)
|
||||
end
|
||||
end
|
||||
|
||||
def terminate(env)
|
||||
destroy_env = env.dup
|
||||
destroy_env.delete(:interrupted)
|
||||
destroy_env[:config_validate] = false
|
||||
destroy_env[:force_confirm_destroy] = true
|
||||
env[:action_runner].run(Action.action_destroy, destroy_env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
48
lib/vagrant-libvirt/config.rb
Normal file
48
lib/vagrant-libvirt/config.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
require 'vagrant'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
class Config < Vagrant.plugin('2', :config)
|
||||
# A hypervisor name to access via Libvirt.
|
||||
attr_accessor :driver
|
||||
|
||||
# The name of the server, where libvirtd is running.
|
||||
attr_accessor :host
|
||||
|
||||
# If use ssh tunnel to connect to Libvirt.
|
||||
attr_accessor :connect_via_ssh
|
||||
|
||||
# The username to access Libvirt.
|
||||
attr_accessor :username
|
||||
|
||||
# Password for Libvirt connection.
|
||||
attr_accessor :password
|
||||
|
||||
# Libvirt storage pool name, where box image and instance snapshots will
|
||||
# be stored.
|
||||
attr_accessor :storage_pool_name
|
||||
|
||||
def initialize
|
||||
@driver = UNSET_VALUE
|
||||
@host = UNSET_VALUE
|
||||
@connect_via_ssh = UNSET_VALUE
|
||||
@username = UNSET_VALUE
|
||||
@password = UNSET_VALUE
|
||||
@storage_pool_name = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@driver = 'qemu' if @driver == UNSET_VALUE
|
||||
@host = nil if @host == UNSET_VALUE
|
||||
@connect_via_ssh = false if @connect_via_ssh == UNSET_VALUE
|
||||
@username = nil if @username == UNSET_VALUE
|
||||
@password = nil if @password == UNSET_VALUE
|
||||
@storage_pool_name = 'default' if @storage_pool_name == UNSET_VALUE
|
||||
end
|
||||
|
||||
def validate(machine)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
90
lib/vagrant-libvirt/errors.rb
Normal file
90
lib/vagrant-libvirt/errors.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
require 'vagrant'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Errors
|
||||
class VagrantLibvirtError < Vagrant::Errors::VagrantError
|
||||
error_namespace("vagrant_libvirt.errors")
|
||||
end
|
||||
|
||||
# Storage pools and volumes exceptions
|
||||
class NoStoragePool < VagrantLibvirtError
|
||||
error_key(:no_storage_pool)
|
||||
end
|
||||
|
||||
class DomainVolumeExists < VagrantLibvirtError
|
||||
error_key(:domain_volume_exists)
|
||||
end
|
||||
|
||||
class NoDomainVolume < VagrantLibvirtError
|
||||
error_key(:no_domain_volume)
|
||||
end
|
||||
|
||||
class CreatingStoragePoolError < VagrantLibvirtError
|
||||
error_key(:creating_storage_pool_error)
|
||||
end
|
||||
|
||||
class ImageUploadError < VagrantLibvirtError
|
||||
error_key(:image_upload_error_error)
|
||||
end
|
||||
|
||||
|
||||
# Box exceptions
|
||||
class NoBoxVolume < VagrantLibvirtError
|
||||
error_key(:no_box_volume)
|
||||
end
|
||||
|
||||
class NoBoxVirtualSizeSet < VagrantLibvirtError
|
||||
error_key(:no_box_virtual_size_error)
|
||||
end
|
||||
|
||||
class NoBoxFormatSet < VagrantLibvirtError
|
||||
error_key(:no_box_format_error)
|
||||
end
|
||||
|
||||
class WrongBoxFormatSet < VagrantLibvirtError
|
||||
error_key(:wrong_box_format_error)
|
||||
end
|
||||
|
||||
|
||||
# Fog libvirt exceptions
|
||||
class FogLibvirtConnectionError < VagrantLibvirtError
|
||||
error_key(:fog_libvirt_connection_error)
|
||||
end
|
||||
|
||||
class FogCreateVolumeError < VagrantLibvirtError
|
||||
error_key(:fog_create_volume_error)
|
||||
end
|
||||
|
||||
class FogCreateDomainVolumeError < VagrantLibvirtError
|
||||
error_key(:fog_create_domain_volume_error)
|
||||
end
|
||||
|
||||
class FogCreateServerError < VagrantLibvirtError
|
||||
error_key(:fog_create_server_error)
|
||||
end
|
||||
|
||||
|
||||
# Other exceptions
|
||||
class InterfaceSlotNotAvailable < VagrantLibvirtError
|
||||
error_key(:interface_slot_not_available)
|
||||
end
|
||||
|
||||
class RsyncError < VagrantLibvirtError
|
||||
error_key(:rsync_error)
|
||||
end
|
||||
|
||||
class DomainNameExists < VagrantLibvirtError
|
||||
error_key(:domain_name_exists_error)
|
||||
end
|
||||
|
||||
class NoDomainError < VagrantLibvirtError
|
||||
error_key(:no_domain_error)
|
||||
end
|
||||
|
||||
class AttachDeviceError < VagrantLibvirtError
|
||||
error_key(:attach_device_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
77
lib/vagrant-libvirt/plugin.rb
Normal file
77
lib/vagrant-libvirt/plugin.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
begin
|
||||
require 'vagrant'
|
||||
rescue LoadError
|
||||
raise "The Vagrant Libvirt plugin must be run within Vagrant."
|
||||
end
|
||||
|
||||
|
||||
# This is a sanity check to make sure no one is attempting to install
|
||||
# this into an early Vagrant version.
|
||||
if Vagrant::VERSION < '1.1.0'
|
||||
raise "The Vagrant Libvirt plugin is only compatible with Vagrant 1.1+"
|
||||
end
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
class Plugin < Vagrant.plugin('2')
|
||||
name "libvirt"
|
||||
description <<-DESC
|
||||
Vagrant plugin to manage VMs in libvirt.
|
||||
DESC
|
||||
|
||||
|
||||
config("libvirt", :provider) do
|
||||
require_relative "config"
|
||||
Config
|
||||
end
|
||||
|
||||
provider "libvirt" do
|
||||
# Setup logging and i18n
|
||||
setup_logging
|
||||
setup_i18n
|
||||
|
||||
require_relative "provider"
|
||||
Provider
|
||||
end
|
||||
|
||||
|
||||
# This initializes the internationalization strings.
|
||||
def self.setup_i18n
|
||||
I18n.load_path << File.expand_path("locales/en.yml", Libvirt.source_root)
|
||||
I18n.reload!
|
||||
end
|
||||
|
||||
|
||||
# This sets up our log level to be whatever VAGRANT_LOG is.
|
||||
def self.setup_logging
|
||||
require "log4r"
|
||||
|
||||
level = nil
|
||||
begin
|
||||
level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
|
||||
rescue NameError
|
||||
# This means that the logging constant wasn't found,
|
||||
# which is fine. We just keep `level` as `nil`. But
|
||||
# we tell the user.
|
||||
level = nil
|
||||
end
|
||||
|
||||
# Some constants, such as "true" resolve to booleans, so the
|
||||
# above error checking doesn't catch it. This will check to make
|
||||
# sure that the log level is an integer, as Log4r requires.
|
||||
level = nil if !level.is_a?(Integer)
|
||||
|
||||
# Set the logging level on all "vagrant" namespaced
|
||||
# logs as long as we have a valid level.
|
||||
if level
|
||||
logger = Log4r::Logger.new("vagrant_libvirt")
|
||||
logger.outputters = Log4r::Outputter.stderr
|
||||
logger.level = level
|
||||
logger = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
76
lib/vagrant-libvirt/provider.rb
Normal file
76
lib/vagrant-libvirt/provider.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
require 'vagrant'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
|
||||
# This is the base class for a provider for the V2 API. A provider
|
||||
# is responsible for creating compute resources to match the
|
||||
# needs of a Vagrant-configured system.
|
||||
class Provider < Vagrant.plugin('2', :provider)
|
||||
def initialize(machine)
|
||||
@machine = machine
|
||||
end
|
||||
|
||||
# This should return an action callable for the given name.
|
||||
def action(name)
|
||||
# Attempt to get the action method from the Action class if it
|
||||
# exists, otherwise return nil to show that we don't support the
|
||||
# given action.
|
||||
action_method = "action_#{name}"
|
||||
return Action.send(action_method) if Action.respond_to?(action_method)
|
||||
nil
|
||||
end
|
||||
|
||||
# This method is called if the underying machine ID changes. Providers
|
||||
# can use this method to load in new data for the actual backing
|
||||
# machine or to realize that the machine is now gone (the ID can
|
||||
# become `nil`).
|
||||
def machine_id_changed
|
||||
end
|
||||
|
||||
# This should return a hash of information that explains how to
|
||||
# SSH into the machine. If the machine is not at a point where
|
||||
# SSH is even possible, then `nil` should be returned.
|
||||
def ssh_info
|
||||
# Run a custom action called "read_ssh_info" which does what it says
|
||||
# and puts the resulting SSH info into the `:machine_ssh_info` key in
|
||||
# the environment.
|
||||
#
|
||||
# Ssh info has following format..
|
||||
#
|
||||
#{
|
||||
# :host => "1.2.3.4",
|
||||
# :port => "22",
|
||||
# :username => "mitchellh",
|
||||
# :private_key_path => "/path/to/my/key"
|
||||
#}
|
||||
env = @machine.action("read_ssh_info")
|
||||
env[:machine_ssh_info]
|
||||
end
|
||||
|
||||
# This should return the state of the machine within this provider.
|
||||
# The state must be an instance of {MachineState}.
|
||||
def state
|
||||
# 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")
|
||||
|
||||
state_id = env[:machine_state_id]
|
||||
|
||||
# Get the short and long description
|
||||
short = I18n.t("vagrant_libvirt.states.short_#{state_id}")
|
||||
long = I18n.t("vagrant_libvirt.states.long_#{state_id}")
|
||||
|
||||
# Return the MachineState object
|
||||
Vagrant::MachineState.new(state_id, short, long)
|
||||
end
|
||||
|
||||
def to_s
|
||||
id = @machine.id.nil? ? "new" : @machine.id
|
||||
"Libvirt (#{id})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
13
lib/vagrant-libvirt/templates/default_storage_pool.xml.erb
Normal file
13
lib/vagrant-libvirt/templates/default_storage_pool.xml.erb
Normal file
|
@ -0,0 +1,13 @@
|
|||
<pool type='dir'>
|
||||
<name>default</name>
|
||||
<source>
|
||||
</source>
|
||||
<target>
|
||||
<path>/var/lib/libvirt/images</path>
|
||||
<permissions>
|
||||
<mode>0755</mode>
|
||||
<owner>-1</owner>
|
||||
<group>-1</group>
|
||||
</permissions>
|
||||
</target>
|
||||
</pool>
|
34
lib/vagrant-libvirt/templates/domain.xml.erb
Normal file
34
lib/vagrant-libvirt/templates/domain.xml.erb
Normal file
|
@ -0,0 +1,34 @@
|
|||
<domain type='<%= @domain_type %>'>
|
||||
<name><%= @name %></name>
|
||||
<memory><%= @memory_size %></memory>
|
||||
<vcpu><%= @cpus %></vcpu>
|
||||
<os>
|
||||
<type arch='x86_64'>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<pae/>
|
||||
</features>
|
||||
<clock offset='utc'/>
|
||||
<devices>
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source file='<%= @domain_volume_path %>'/>
|
||||
<%# we need to ensure a unique target dev -%>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
</disk>
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
<input type='mouse' bus='ps2'/>
|
||||
<graphics type='vnc' port='5900' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
|
||||
<video>
|
||||
<model type='cirrus' vram='9216' heads='1'/>
|
||||
</video>
|
||||
</devices>
|
||||
</domain>
|
7
lib/vagrant-libvirt/templates/interface.xml.erb
Normal file
7
lib/vagrant-libvirt/templates/interface.xml.erb
Normal file
|
@ -0,0 +1,7 @@
|
|||
<interface type='network'>
|
||||
<source network='<%= @network_name %>'/>
|
||||
<target dev='vnet<%= @iface_number %>'/>
|
||||
<alias name='net<%= @iface_number %>'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
|
26
lib/vagrant-libvirt/templates/volume_snapshot.xml.erb
Normal file
26
lib/vagrant-libvirt/templates/volume_snapshot.xml.erb
Normal file
|
@ -0,0 +1,26 @@
|
|||
<volume>
|
||||
<name><%= @name %></name>
|
||||
<capacity unit="G"><%= @capacity %></capacity>
|
||||
|
||||
<target>
|
||||
<format type='qcow2'/>
|
||||
<permissions>
|
||||
<owner>0</owner>
|
||||
<group>0</group>
|
||||
<mode>0600</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
</target>
|
||||
|
||||
<backingStore>
|
||||
<path><%= @backing_file %></path>
|
||||
<format type='qcow2'/>
|
||||
<permissions>
|
||||
<owner>0</owner>
|
||||
<group>0</group>
|
||||
<mode>0600</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
</backingStore>
|
||||
</volume>
|
||||
|
10
lib/vagrant-libvirt/util.rb
Normal file
10
lib/vagrant-libvirt/util.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Util
|
||||
autoload :ErbTemplate, 'vagrant-libvirt/util/erb_template'
|
||||
autoload :Collection, 'vagrant-libvirt/util/collection'
|
||||
autoload :Timer, 'vagrant-libvirt/util/timer'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
22
lib/vagrant-libvirt/util/collection.rb
Normal file
22
lib/vagrant-libvirt/util/collection.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Util
|
||||
module Collection
|
||||
|
||||
# This method finds a matching _thing_ in a collection of
|
||||
# _things_. This works matching if the ID or NAME equals to
|
||||
# `name`. Or, if `name` is a regexp, a partial match is chosen
|
||||
# as well.
|
||||
def self.find_matching(collection, name)
|
||||
collection.each do |single|
|
||||
return single if single.name == name
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
21
lib/vagrant-libvirt/util/erb_template.rb
Normal file
21
lib/vagrant-libvirt/util/erb_template.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require 'erb'
|
||||
|
||||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Util
|
||||
module ErbTemplate
|
||||
|
||||
# Taken from fog source.
|
||||
def to_xml template_name = nil
|
||||
erb = template_name || self.class.to_s.split("::").last.downcase
|
||||
path = File.join(File.dirname(__FILE__), "..", "templates",
|
||||
"#{erb}.xml.erb")
|
||||
template = File.read(path)
|
||||
ERB.new(template, nil, '-').result(binding)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
17
lib/vagrant-libvirt/util/timer.rb
Normal file
17
lib/vagrant-libvirt/util/timer.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
module Util
|
||||
class Timer
|
||||
# A basic utility method that times the execution of the given
|
||||
# block and returns it.
|
||||
def self.time
|
||||
start_time = Time.now.to_f
|
||||
yield
|
||||
end_time = Time.now.to_f
|
||||
|
||||
end_time - start_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
5
lib/vagrant-libvirt/version.rb
Normal file
5
lib/vagrant-libvirt/version.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
module VagrantPlugins
|
||||
module Libvirt
|
||||
VERSION = "0.0.1"
|
||||
end
|
||||
end
|
103
locales/en.yml
Normal file
103
locales/en.yml
Normal file
|
@ -0,0 +1,103 @@
|
|||
en:
|
||||
vagrant_libvirt:
|
||||
already_created: |-
|
||||
The machine is already created.
|
||||
not_created: |-
|
||||
Machine is not created. Please run `vagrant up` first.
|
||||
finding_volume: |-
|
||||
Checking if volume is available.
|
||||
creating_domain: |-
|
||||
Creating machine with the following settings...
|
||||
uploading_volume: |-
|
||||
Uploading base box image as volume into libvirt storage...
|
||||
creating_domain_volume: |-
|
||||
Creating image (snapshot of base box volume).
|
||||
removing_domain_volume: |-
|
||||
Removing image (snapshot of base box volume).
|
||||
starting_domain: |-
|
||||
Starting machine.
|
||||
terminating: |-
|
||||
Removing machine...
|
||||
poweroff_domain: |-
|
||||
Poweroff machine.
|
||||
destroy_domain: |-
|
||||
Removing machine...
|
||||
waiting_for_ready: |-
|
||||
Waiting for machine to become "ready"...
|
||||
waiting_for_ip: |-
|
||||
Waiting for machine to get an IP address...
|
||||
waiting_for_ssh: |-
|
||||
Waiting for SSH to become available...
|
||||
booted: |-
|
||||
Machine is booted.
|
||||
rsync_folder: |-
|
||||
Rsyncing folder: %{hostpath} => %{guestpath}
|
||||
ready: |-
|
||||
Machine is booted and ready for use!
|
||||
|
||||
errors:
|
||||
fog_error: |-
|
||||
There was an error talking to Libvirt. The error message is shown
|
||||
below:
|
||||
|
||||
%{message}
|
||||
no_matching_volume: |-
|
||||
No matching volume was found! Please check your volume setting
|
||||
to make sure you have a valid volume chosen.
|
||||
no_storage_pool: |-
|
||||
No usable storage pool found! Please check if storage pool is
|
||||
created and available.
|
||||
no_box_volume: |-
|
||||
Volume for box image is missing in storage pools. Try to run vagrant
|
||||
again, or check if storage volume is accessible.
|
||||
domain_volume_exists: |-
|
||||
Volume for domain is already created. Please run 'vagrant destroy' first.
|
||||
no_domain_volume: |-
|
||||
Volume for domain is missing. Try to run 'vagrant up' again.
|
||||
interface_slot_not_available: |-
|
||||
Interface adapter number is already in use. Please specify other adapter
|
||||
number.
|
||||
rsync_error: |-
|
||||
There was an error when attemping to rsync a share folder.
|
||||
Please inspect the error message below for more info.
|
||||
|
||||
Host path: %{hostpath}
|
||||
Guest path: %{guestpath}
|
||||
Error: %{stderr}
|
||||
no_box_virtual_size: |-
|
||||
No image virtual size specified for box.
|
||||
no_box_format: |-
|
||||
No image format specified for box.
|
||||
wrong_box_format: |-
|
||||
Wrong image format specified for box.
|
||||
fog_libvirt_connection_error: |-
|
||||
Error while connecting to libvirt: %{error_message}
|
||||
fog_create_volume_error: |-
|
||||
Error while creating a storage pool volume: %{error_message}
|
||||
fog_create_domain_volume_error: |-
|
||||
Error while creating volume for domain: %{error_message}
|
||||
fog_create_server_error: |-
|
||||
Error while creating domain: %{error_message}
|
||||
domain_name_exists: |-
|
||||
Name of domain about to create is already taken. Please try to run
|
||||
`vagrant up` command again.
|
||||
creating_storage_pool_error: |-
|
||||
There was error while creating libvirt storage pool: %{error_message}
|
||||
image_upload_error: |-
|
||||
Error while uploading image to storage pool: %{error_message}
|
||||
no_domain_error: |-
|
||||
No domain found. %{error_message}
|
||||
attach_device_error: |-
|
||||
Error while attaching new device to domain. %{error_message}
|
||||
|
||||
states:
|
||||
short_not_created: |-
|
||||
not created
|
||||
long_not_created: |-
|
||||
The Libvirt domain is not created. Run `vagrant up` to create it.
|
||||
|
||||
short_running: |-
|
||||
running
|
||||
long_running: |-
|
||||
The Libvirt domain is running. To stop this machine, you can run
|
||||
`vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
|
23
vagrant-libvirt.gemspec
Normal file
23
vagrant-libvirt.gemspec
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
require File.expand_path('../lib/vagrant-libvirt/version', __FILE__)
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
gem.authors = ["Lukas Stanek"]
|
||||
gem.email = ["ls@elostech.cz"]
|
||||
gem.description = %q{Vagrant provider for libvirt.}
|
||||
gem.summary = %q{Vagrant provider for libvirt.}
|
||||
gem.homepage = "http://www.vagrantup.com"
|
||||
|
||||
gem.files = `git ls-files`.split($\)
|
||||
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
||||
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
||||
gem.name = "vagrant-libvirt"
|
||||
gem.require_paths = ["lib"]
|
||||
gem.version = VagrantPlugins::Libvirt::VERSION
|
||||
|
||||
gem.add_runtime_dependency "fog", "~> 1.10.0"
|
||||
gem.add_runtime_dependency "ruby-libvirt", "~> 0.4.0"
|
||||
|
||||
gem.add_development_dependency "rake"
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user