[DRE-commits] [vagrant-libvirt] 01/104: Initial commit
Antonio Terceiro
terceiro at moszumanska.debian.org
Sun Apr 24 13:55:38 UTC 2016
This is an automated email from the git hooks/post-receive script.
terceiro pushed a commit to annotated tag 0.0.11
in repository vagrant-libvirt.
commit 810f0b31b973281bdf70e8b9810f02d2486e9eb8
Author: pradels <les.pradels at gmail.com>
Date: Wed Mar 27 00:55:30 2013 +0100
Initial commit
---
.gitignore | 19 +++
Gemfile | 12 ++
LICENSE | 22 +++
README.md | 178 +++++++++++++++++++++
Rakefile | 7 +
example_box/README.md | 23 +++
example_box/metadata.json | 5 +
lib/vagrant-libvirt.rb | 30 ++++
lib/vagrant-libvirt/action.rb | 94 +++++++++++
lib/vagrant-libvirt/action/connect_libvirt.rb | 72 +++++++++
lib/vagrant-libvirt/action/create_domain.rb | 62 +++++++
lib/vagrant-libvirt/action/create_domain_volume.rb | 58 +++++++
.../action/create_network_interfaces.rb | 85 ++++++++++
lib/vagrant-libvirt/action/destroy_domain.rb | 28 ++++
lib/vagrant-libvirt/action/handle_box_image.rb | 121 ++++++++++++++
lib/vagrant-libvirt/action/handle_storage_pool.rb | 49 ++++++
lib/vagrant-libvirt/action/is_created.rb | 18 +++
.../action/message_already_created.rb | 16 ++
lib/vagrant-libvirt/action/message_not_created.rb | 16 ++
lib/vagrant-libvirt/action/read_ssh_info.rb | 51 ++++++
lib/vagrant-libvirt/action/read_state.rb | 38 +++++
lib/vagrant-libvirt/action/set_name_of_domain.rb | 31 ++++
lib/vagrant-libvirt/action/start_domain.rb | 27 ++++
lib/vagrant-libvirt/action/sync_folders.rb | 58 +++++++
lib/vagrant-libvirt/action/timed_provision.rb | 21 +++
lib/vagrant-libvirt/action/wait_till_up.rb | 96 +++++++++++
lib/vagrant-libvirt/config.rb | 48 ++++++
lib/vagrant-libvirt/errors.rb | 90 +++++++++++
lib/vagrant-libvirt/plugin.rb | 77 +++++++++
lib/vagrant-libvirt/provider.rb | 76 +++++++++
.../templates/default_storage_pool.xml.erb | 13 ++
lib/vagrant-libvirt/templates/domain.xml.erb | 34 ++++
lib/vagrant-libvirt/templates/interface.xml.erb | 7 +
.../templates/volume_snapshot.xml.erb | 26 +++
lib/vagrant-libvirt/util.rb | 10 ++
lib/vagrant-libvirt/util/collection.rb | 22 +++
lib/vagrant-libvirt/util/erb_template.rb | 21 +++
lib/vagrant-libvirt/util/timer.rb | 17 ++
lib/vagrant-libvirt/version.rb | 5 +
locales/en.yml | 103 ++++++++++++
vagrant-libvirt.gemspec | 23 +++
41 files changed, 1809 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d036c0f
--- /dev/null
+++ b/.gitignore
@@ -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
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..ebd9fc4
--- /dev/null
+++ b/Gemfile
@@ -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
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..93920f0
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6e883d8
--- /dev/null
+++ b/README.md
@@ -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.
+
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..b41e6e1
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,7 @@
+#!/usr/bin/env rake
+
+require 'rubygems'
+require 'bundler/setup'
+#require 'bundler/gem_tasks'
+Bundler::GemHelper.install_tasks
+
diff --git a/example_box/README.md b/example_box/README.md
new file mode 100644
index 0000000..50b519e
--- /dev/null
+++ b/example_box/README.md
@@ -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.
+
diff --git a/example_box/metadata.json b/example_box/metadata.json
new file mode 100644
index 0000000..b755f46
--- /dev/null
+++ b/example_box/metadata.json
@@ -0,0 +1,5 @@
+{
+ "provider" : "libvirt"
+ "format" : "qcow2"
+ "virtual_size" : "40"
+}
diff --git a/lib/vagrant-libvirt.rb b/lib/vagrant-libvirt.rb
new file mode 100644
index 0000000..ad69975
--- /dev/null
+++ b/lib/vagrant-libvirt.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action.rb b/lib/vagrant-libvirt/action.rb
new file mode 100644
index 0000000..f1acc48
--- /dev/null
+++ b/lib/vagrant-libvirt/action.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/connect_libvirt.rb b/lib/vagrant-libvirt/action/connect_libvirt.rb
new file mode 100644
index 0000000..04a0f82
--- /dev/null
+++ b/lib/vagrant-libvirt/action/connect_libvirt.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/create_domain.rb b/lib/vagrant-libvirt/action/create_domain.rb
new file mode 100644
index 0000000..db58ec2
--- /dev/null
+++ b/lib/vagrant-libvirt/action/create_domain.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/create_domain_volume.rb b/lib/vagrant-libvirt/action/create_domain_volume.rb
new file mode 100644
index 0000000..021b47b
--- /dev/null
+++ b/lib/vagrant-libvirt/action/create_domain_volume.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/create_network_interfaces.rb b/lib/vagrant-libvirt/action/create_network_interfaces.rb
new file mode 100644
index 0000000..d8b7f24
--- /dev/null
+++ b/lib/vagrant-libvirt/action/create_network_interfaces.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/destroy_domain.rb b/lib/vagrant-libvirt/action/destroy_domain.rb
new file mode 100644
index 0000000..e722d85
--- /dev/null
+++ b/lib/vagrant-libvirt/action/destroy_domain.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/handle_box_image.rb b/lib/vagrant-libvirt/action/handle_box_image.rb
new file mode 100644
index 0000000..a329560
--- /dev/null
+++ b/lib/vagrant-libvirt/action/handle_box_image.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/handle_storage_pool.rb b/lib/vagrant-libvirt/action/handle_storage_pool.rb
new file mode 100644
index 0000000..b088d31
--- /dev/null
+++ b/lib/vagrant-libvirt/action/handle_storage_pool.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/is_created.rb b/lib/vagrant-libvirt/action/is_created.rb
new file mode 100644
index 0000000..39760a0
--- /dev/null
+++ b/lib/vagrant-libvirt/action/is_created.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/message_already_created.rb b/lib/vagrant-libvirt/action/message_already_created.rb
new file mode 100644
index 0000000..a803d61
--- /dev/null
+++ b/lib/vagrant-libvirt/action/message_already_created.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/message_not_created.rb b/lib/vagrant-libvirt/action/message_not_created.rb
new file mode 100644
index 0000000..14dfc0e
--- /dev/null
+++ b/lib/vagrant-libvirt/action/message_not_created.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/read_ssh_info.rb b/lib/vagrant-libvirt/action/read_ssh_info.rb
new file mode 100644
index 0000000..336ba96
--- /dev/null
+++ b/lib/vagrant-libvirt/action/read_ssh_info.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/read_state.rb b/lib/vagrant-libvirt/action/read_state.rb
new file mode 100644
index 0000000..f7543fa
--- /dev/null
+++ b/lib/vagrant-libvirt/action/read_state.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/set_name_of_domain.rb b/lib/vagrant-libvirt/action/set_name_of_domain.rb
new file mode 100644
index 0000000..fa9c0ef
--- /dev/null
+++ b/lib/vagrant-libvirt/action/set_name_of_domain.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/start_domain.rb b/lib/vagrant-libvirt/action/start_domain.rb
new file mode 100644
index 0000000..5ea9332
--- /dev/null
+++ b/lib/vagrant-libvirt/action/start_domain.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/sync_folders.rb b/lib/vagrant-libvirt/action/sync_folders.rb
new file mode 100644
index 0000000..f16fa02
--- /dev/null
+++ b/lib/vagrant-libvirt/action/sync_folders.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/action/timed_provision.rb b/lib/vagrant-libvirt/action/timed_provision.rb
new file mode 100644
index 0000000..b5cecd6
--- /dev/null
+++ b/lib/vagrant-libvirt/action/timed_provision.rb
@@ -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
diff --git a/lib/vagrant-libvirt/action/wait_till_up.rb b/lib/vagrant-libvirt/action/wait_till_up.rb
new file mode 100644
index 0000000..f42f901
--- /dev/null
+++ b/lib/vagrant-libvirt/action/wait_till_up.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/config.rb b/lib/vagrant-libvirt/config.rb
new file mode 100644
index 0000000..501effc
--- /dev/null
+++ b/lib/vagrant-libvirt/config.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/errors.rb b/lib/vagrant-libvirt/errors.rb
new file mode 100644
index 0000000..b158dec
--- /dev/null
+++ b/lib/vagrant-libvirt/errors.rb
@@ -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
diff --git a/lib/vagrant-libvirt/plugin.rb b/lib/vagrant-libvirt/plugin.rb
new file mode 100644
index 0000000..fbe162c
--- /dev/null
+++ b/lib/vagrant-libvirt/plugin.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/provider.rb b/lib/vagrant-libvirt/provider.rb
new file mode 100644
index 0000000..1b18aef
--- /dev/null
+++ b/lib/vagrant-libvirt/provider.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/templates/default_storage_pool.xml.erb b/lib/vagrant-libvirt/templates/default_storage_pool.xml.erb
new file mode 100644
index 0000000..b63eea7
--- /dev/null
+++ b/lib/vagrant-libvirt/templates/default_storage_pool.xml.erb
@@ -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>
diff --git a/lib/vagrant-libvirt/templates/domain.xml.erb b/lib/vagrant-libvirt/templates/domain.xml.erb
new file mode 100644
index 0000000..d1eb492
--- /dev/null
+++ b/lib/vagrant-libvirt/templates/domain.xml.erb
@@ -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>
diff --git a/lib/vagrant-libvirt/templates/interface.xml.erb b/lib/vagrant-libvirt/templates/interface.xml.erb
new file mode 100644
index 0000000..39edc0f
--- /dev/null
+++ b/lib/vagrant-libvirt/templates/interface.xml.erb
@@ -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>
+
diff --git a/lib/vagrant-libvirt/templates/volume_snapshot.xml.erb b/lib/vagrant-libvirt/templates/volume_snapshot.xml.erb
new file mode 100644
index 0000000..304d248
--- /dev/null
+++ b/lib/vagrant-libvirt/templates/volume_snapshot.xml.erb
@@ -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>
+
diff --git a/lib/vagrant-libvirt/util.rb b/lib/vagrant-libvirt/util.rb
new file mode 100644
index 0000000..870fea4
--- /dev/null
+++ b/lib/vagrant-libvirt/util.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/util/collection.rb b/lib/vagrant-libvirt/util/collection.rb
new file mode 100644
index 0000000..89d901e
--- /dev/null
+++ b/lib/vagrant-libvirt/util/collection.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/util/erb_template.rb b/lib/vagrant-libvirt/util/erb_template.rb
new file mode 100644
index 0000000..4f2b4be
--- /dev/null
+++ b/lib/vagrant-libvirt/util/erb_template.rb
@@ -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
+
diff --git a/lib/vagrant-libvirt/util/timer.rb b/lib/vagrant-libvirt/util/timer.rb
new file mode 100644
index 0000000..6349d49
--- /dev/null
+++ b/lib/vagrant-libvirt/util/timer.rb
@@ -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
diff --git a/lib/vagrant-libvirt/version.rb b/lib/vagrant-libvirt/version.rb
new file mode 100644
index 0000000..5e8b1a3
--- /dev/null
+++ b/lib/vagrant-libvirt/version.rb
@@ -0,0 +1,5 @@
+module VagrantPlugins
+ module Libvirt
+ VERSION = "0.0.1"
+ end
+end
diff --git a/locales/en.yml b/locales/en.yml
new file mode 100644
index 0000000..9e999cd
--- /dev/null
+++ b/locales/en.yml
@@ -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`.
diff --git a/vagrant-libvirt.gemspec b/vagrant-libvirt.gemspec
new file mode 100644
index 0000000..ae4bba7
--- /dev/null
+++ b/vagrant-libvirt.gemspec
@@ -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 at 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
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/vagrant-libvirt.git
More information about the Pkg-ruby-extras-commits
mailing list