mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06: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