├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── lib ├── vagrant-mutate.rb └── vagrant-mutate │ ├── box │ ├── bhyve.rb │ ├── box.rb │ ├── kvm.rb │ ├── libvirt.rb │ └── virtualbox.rb │ ├── box_loader.rb │ ├── converter │ ├── bhyve.rb │ ├── converter.rb │ ├── kvm.rb │ └── libvirt.rb │ ├── errors.rb │ ├── mutate.rb │ ├── qemu.rb │ └── version.rb ├── locales └── en.yml ├── templates └── kvm │ └── box.xml.erb ├── test ├── expected_output │ ├── kvm │ │ └── libvirt │ │ │ ├── Vagrantfile │ │ │ ├── box.img │ │ │ └── metadata.json │ ├── libvirt │ │ └── kvm │ │ │ ├── Vagrantfile │ │ │ ├── box-disk1.img │ │ │ ├── box.xml │ │ │ └── metadata.json │ └── virtualbox │ │ ├── bhyve │ │ ├── Vagrantfile │ │ ├── disk.img │ │ └── metadata.json │ │ ├── kvm │ │ ├── Vagrantfile │ │ ├── box-disk1.img │ │ ├── box.xml │ │ └── metadata.json │ │ └── libvirt │ │ ├── Vagrantfile │ │ ├── box.img │ │ └── metadata.json ├── input │ ├── kvm │ │ └── mutate-test.box │ ├── libvirt │ │ └── mutate-test.box │ └── virtualbox │ │ └── mutate-test.box └── test.rb └── vagrant-mutate.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/actual_output 16 | test/tmp 17 | test/version_tmp 18 | tmp 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.2.0 (2016-08-15) 2 | * Add support for converting to bhyve (#86) 3 | 4 | # 1.1.1 (2016-02-24) 5 | * Add option to force use of virtio for disks (#82) 6 | 7 | # 1.0.4 (2016-01-01) 8 | * Support spaces in box names (#79) 9 | 10 | # 1.0.3 (2015-10-15) 11 | * Fix compatibility with qemu version in EL6 (#76) 12 | 13 | # 1.0.2 (2015-09-06) 14 | * Allow period in box name when loading from URL (#75) 15 | 16 | # 1.0.1 (2015-08-16) 17 | * Throw error during version search when missing box (#72) 18 | 19 | # 1.0.0 (2015-05-20) 20 | * Support versioned boxes (#51) 21 | * Support boxes with slash in name (#52) 22 | * Rename input provider option (#68) 23 | * Allow hyphen in box name when loading from url (#70) 24 | 25 | # 0.3.2 (2015-03-03) 26 | * Specify qcow compat instead of using default (#59) 27 | * Provide clearer error when qemu-img missing (#62) 28 | 29 | # 0.3.1 (2014-08-17) 30 | * Improve compatibility with newer qemu-img releases 31 | 32 | # 0.3.0 (2014-05-04) 33 | * Support Vagrant 1.5 with unversioned boxes (#47) 34 | * Drop support for Vagrant < 1.5 35 | * Move input provider specifier into own option 36 | * Warn if user provided identifier for Vagrant Cloud 37 | 38 | # 0.2.6 (2014-05-04) 39 | * Include CentOS 6.5/RHEL 6.5-friendly Qemu paths (#50) 40 | 41 | # 0.2.5 (2014-02-01) 42 | * Fix pci id for drives in kvm (#39) 43 | 44 | # 0.2.4 (2014-01-23) 45 | * Generate new vagrantfiles instead of copying them 46 | * Set disk bus when converting to vagrant-libvirt (#41) 47 | 48 | # 0.2.3 (2014-01-20) 49 | * Warn when qemu version cannot read vmdk3 files (#29) 50 | * Fix errors in how box name and provider were parsed (#35) 51 | * Load box from file based on existence not name (#36) 52 | * Warn when image is not the expected type for the provider (#38) 53 | 54 | # 0.2.2 (2014-01-05) 55 | * Determine virtualbox disk filename from ovf (#30) 56 | * Move Qemu checks to own class 57 | 58 | # 0.2.1 (2014-01-02) 59 | * Support kvm as input (#17) 60 | 61 | # 0.2.0 (2014-01-02) 62 | * Fix how box is loaded by name (#19) 63 | * Quit if input and output provider are the same (#27) 64 | * Support libvirt as input (#18) 65 | 66 | # 0.1.5 (2013-12-17) 67 | * Preserve dsik interface type when coverting to KVM (#21) 68 | * Remove dependency in minitar (#24) 69 | * Support downloading input box (#9) 70 | * Handle errors when reading ovf file 71 | 72 | # 0.1.4 (2013-12-08) 73 | * Rework box and converter implementation (#7) 74 | * Write disk images as sparse files (#13) 75 | * Switch vagrant-kvm disk format from raw to qcow2 (#16) 76 | * Prefer the binary named qemu-system-* over qemu-kvm or kvm (#20) 77 | 78 | # 0.1.3 (2013-12-03) 79 | 80 | * Add support for vagrant-kvm (#12) 81 | * Add acceptance tests 82 | 83 | # 0.1.2 (2013-11-20) 84 | 85 | * Rework provider and converter implementation (#7) 86 | 87 | # 0.1.1 (2013-11-12) 88 | 89 | * Fix handling of fractional virtual disk sizes (#11) 90 | 91 | # 0.1.0 (2013-11-02) 92 | 93 | * Initial release 94 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | # We depend on Vagrant for development, but we don't add it as a 7 | # gem dependency because we expect to be installed within the 8 | # Vagrant environment itself using `vagrant plugin`. 9 | gem "vagrant", git: "https://github.com/mitchellh/vagrant.git" 10 | end 11 | 12 | group :plugins do 13 | gem "vagrant-mutate", path: "." 14 | end 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Brian Pitts 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vagrant-Mutate 2 | 3 | Vagrant-mutate is a vagrant plugin to convert vagrant boxes to work with different providers. 4 | 5 | ## Maintenance Notice 6 | 7 | I am no longer actively maintaining vagrant-mutate. If you are interested in taking over, let me know. 8 | 9 | ## Supported Conversions 10 | 11 | * Virtualbox to kvm 12 | * Virtualbox to libvirt 13 | * Virtualbox to bhyve 14 | * Libvirt to kvm 15 | * Kvm to libvirt 16 | 17 | ## Compatibility 18 | 19 | Vagrant-mutate 0.3 and later requires Vagrant 1.5. If you are using an older vagrant, install vagrant-mutate version 0.2.6. 20 | 21 | ## Installation 22 | 23 | ### qemu-img and libvirt development 24 | 25 | First, you must install [qemu-img](http://wiki.qemu.org/Main_Page) and the libvirt development libraries. Information on supported versions is listed at [QEMU Version Compatibility](https://github.com/sciurus/vagrant-mutate/wiki/QEMU-Version-Compatibility). 26 | 27 | #### Debian and derivatives 28 | 29 | apt-get install qemu-utils libvirt-dev ruby-dev 30 | 31 | #### Red Hat and derivatives 32 | 33 | yum install qemu-img libvirt-devel rubygem-ruby-libvirt ruby-devel redhat-rpm-config 34 | 35 | #### OS X 36 | 37 | QEMU and libvirt are available from [homebrew](http://brew.sh/) 38 | 39 | #### Windows 40 | 41 | Download and install it from [Stefan Weil](http://qemu.weilnetz.de/) or compile it yourself. 42 | 43 | ### vagrant-mutate 44 | 45 | Now you're ready to install vagrant-mutate. To install the latest released version simply run 46 | 47 | vagrant plugin install vagrant-mutate 48 | 49 | To install from source, clone the repository and run `rake build`. That will produce a gem file in the _pkg_ directory which you can then install with `vagrant plugin install`. 50 | 51 | ## Usage 52 | 53 | The basic usage is 54 | 55 | vagrant mutate box-name-or-file-or-url output-provider 56 | 57 | For example, if you wanted to download a box created for virtualbox and add it to vagrant for libvirt 58 | 59 | vagrant mutate http://files.vagrantup.com/precise32.box libvirt 60 | 61 | Or if you had already downloaded it 62 | 63 | vagrant mutate precise32.box libvirt 64 | 65 | Or if you had already added the box to vagrant and now want to use it with libvirt 66 | 67 | vagrant mutate precise32 libvirt 68 | 69 | The latter syntax works for boxes you added from Vagrant Cloud or Atlas too. If you have installed multiple versions of these boxes, vagrant-mutate will always use the latest one. 70 | 71 | $ vagrant box list 72 | hashicorp/precise64 (virtualbox, 1.1.0) 73 | $ vagrant mutate hashicorp/precise32 libvirt 74 | 75 | If you have a box for multiple providers, you must specify the provider to use for input using the *--input-provider* option, e.g. 76 | 77 | $ vagrant box list 78 | precise32 (kvm) 79 | precise32 (virtualbox) 80 | $ vagrant mutate --input-provider=virtualbox precise32 libvirt 81 | 82 | To export a box you created with vagrant mutate, just repackage it, e.g. 83 | 84 | vagrant box repackage precise32 libvirt 85 | 86 | If you want to force the output box to use virtio for the disk interface, no matter what interface the input box used, use the *--force-virtio* option. 87 | 88 | 89 | ## Debugging 90 | 91 | vagrant and vagrant-mutate will output lots of information as they run if you set the VAGRANT_LOG environment variable to INFO. See [here](http://docs-v1.vagrantup.com/v1/docs/debugging.html) for information on how to do that on your operating system. 92 | 93 | If you experience any problems, please open an issue on [github](https://github.com/sciurus/vagrant-mutate/issues). 94 | 95 | ## Contributing 96 | 97 | Contributions are welcome! I'd especially like to see support for converting between more providers added. 98 | 99 | To contribute, follow the standard flow of 100 | 101 | 1. Fork it 102 | 1. Create your feature branch (`git checkout -b my-new-feature`) 103 | 1. Commit your changes (`git commit -am 'Add some feature'`) 104 | 1. Make sure the acceptance tests pass (`ruby test/test.rb`) 105 | 1. Push to the branch (`git push origin my-new-feature`) 106 | 1. Create new Pull Request 107 | 108 | Even if you can't contribute code, if you have an idea for an improvement please open an [issue](https://github.com/sciurus/vagrant-mutate/issues). 109 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/vagrant-mutate.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-mutate/version' 2 | require 'vagrant-mutate/errors' 3 | 4 | module VagrantMutate 5 | def self.source_root 6 | @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) 7 | end 8 | 9 | # http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby 10 | def self.find_bin(bin) 11 | exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] 12 | ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| 13 | exts.each do |ext| 14 | exe = File.join(path, "#{bin}#{ext}") 15 | if File.executable? exe 16 | return exe if File.executable?(exe) && !File.directory?(exe) 17 | end 18 | end 19 | end 20 | return nil 21 | end 22 | 23 | class Plugin < Vagrant.plugin('2') 24 | name 'vagrant-mutate' 25 | 26 | command 'mutate' do 27 | setup_i18n 28 | require 'vagrant-mutate/mutate' 29 | Mutate 30 | end 31 | 32 | def self.setup_i18n 33 | I18n.load_path << File.expand_path('locales/en.yml', VagrantMutate.source_root) 34 | I18n.reload! 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box/bhyve.rb: -------------------------------------------------------------------------------- 1 | require_relative 'box' 2 | 3 | module VagrantMutate 4 | module Box 5 | class Bhyve < Box 6 | def initialize(env, name, version, dir) 7 | super 8 | @provider_name = 'bhyve' 9 | @supported_input = true 10 | @supported_output = true 11 | @image_format = 'raw' 12 | @image_name = 'disk.img' 13 | end 14 | 15 | # TODO 16 | 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box/box.rb: -------------------------------------------------------------------------------- 1 | require 'shellwords' 2 | 3 | module VagrantMutate 4 | module Box 5 | class Box 6 | attr_reader :name, :dir, :version, :provider_name, :supported_input, :supported_output, :image_format, :image_name 7 | 8 | def initialize(env, name, version, dir) 9 | @env = env 10 | @name = name 11 | @dir = dir 12 | @version = version 13 | @logger = Log4r::Logger.new('vagrant::mutate') 14 | end 15 | 16 | def virtual_size 17 | extract_from_qemu_info(/(\d+) bytes/) 18 | end 19 | 20 | def verify_format 21 | format_found = extract_from_qemu_info(/file format: (\w+)/) 22 | unless format_found == @image_format 23 | @env.ui.warn "Expected input image format to be #{@image_format} but "\ 24 | "it is #{format_found}. Attempting conversion anyway." 25 | end 26 | end 27 | 28 | def extract_from_qemu_info(expression) 29 | input_file = File.join(@dir, image_name).shellescape 30 | info = `qemu-img info #{input_file}` 31 | @logger.debug "qemu-img info output\n#{info}" 32 | if info =~ expression 33 | return Regexp.last_match[1] 34 | else 35 | fail Errors::QemuInfoFailed 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box/kvm.rb: -------------------------------------------------------------------------------- 1 | require_relative 'box' 2 | require 'nokogiri' 3 | 4 | module VagrantMutate 5 | module Box 6 | class Kvm < Box 7 | def initialize(env, name, version, dir) 8 | super 9 | @provider_name = 'kvm' 10 | @supported_input = true 11 | @supported_output = true 12 | @image_format = 'qcow2' 13 | @image_name = 'box-disk1.img' 14 | end 15 | 16 | # TODO: implement these methods 17 | # architecture, mac_address, cpus, memory 18 | # to support converting to providers besides libvirt 19 | 20 | def disk_interface 21 | domain_file = File.join(@dir, 'box.xml') 22 | begin 23 | domain = File.open(domain_file) { |f| Nokogiri::XML(f) } 24 | domain.xpath("//*[local-name()='domain']/*[local-name()='devices']/*[local-name()='disk']/*[local-name()='target']").attribute('bus') 25 | rescue => e 26 | raise Errors::BoxAttributeError, error_message: e.message 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box/libvirt.rb: -------------------------------------------------------------------------------- 1 | require_relative 'box' 2 | 3 | module VagrantMutate 4 | module Box 5 | class Libvirt < Box 6 | def initialize(env, name, version, dir) 7 | super 8 | @provider_name = 'libvirt' 9 | @supported_input = true 10 | @supported_output = true 11 | @image_format = 'qcow2' 12 | @image_name = 'box.img' 13 | @mac = nil 14 | end 15 | 16 | # since none of below can be determined from the box 17 | # we just generate sane values 18 | 19 | def architecture 20 | 'x86_64' 21 | end 22 | 23 | # kvm prefix is 52:54:00 24 | def mac_address 25 | unless @mac 26 | octets = 3.times.map { rand(255).to_s(16) } 27 | @mac = "525400#{octets[0]}#{octets[1]}#{octets[2]}" 28 | end 29 | @mac 30 | end 31 | 32 | def cpus 33 | 1 34 | end 35 | 36 | def memory 37 | 536_870_912 38 | end 39 | 40 | def disk_interface 41 | 'virtio' 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box/virtualbox.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require_relative 'box' 3 | 4 | module VagrantMutate 5 | module Box 6 | class Virtualbox < Box 7 | def initialize(env, name, version, dir) 8 | super 9 | @provider_name = 'virtualbox' 10 | @supported_input = true 11 | @supported_output = false 12 | @image_format = 'vmdk' 13 | end 14 | 15 | # this is usually box-disk1.vmdk but some tools like packer customize it 16 | def image_name 17 | ovf.xpath("//*[local-name()='References']/*[local-name()='File']")[0].attribute("href").value 18 | end 19 | 20 | # the architecture is not defined in the ovf file, 21 | # we could try to guess from OSType 22 | # (https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Main/include/ovfreader.h) 23 | # but if that is not set correctly we risk a 64-bit box not booting 24 | # because we try to run in 32-bit vm. 25 | # in contrast, running 32-bit box in a 64-bit vm should work. 26 | def architecture 27 | 'x86_64' 28 | end 29 | 30 | # use mac from the first enabled nic 31 | def mac_address 32 | mac = nil 33 | 34 | ovf.xpath("//*[local-name()='Machine']/*[local-name()='Hardware']/*[local-name()='Network']/*[local-name()='Adapter']").each do |net| 35 | if net.attribute("enabled").value == "true" 36 | mac = net.attribute("MACAddress").value 37 | break 38 | end 39 | end 40 | 41 | if mac 42 | return mac 43 | else 44 | fail Errors::BoxAttributeError, error_message: 'Could not determine mac address' 45 | end 46 | end 47 | 48 | def cpus 49 | cpu_count = nil 50 | 51 | ovf.xpath("//*[local-name()='VirtualHardwareSection']/*[local-name()='Item']/*[local-name()='ResourceType']").each do |item| 52 | if item.text == "3" 53 | cpu_count = item.parent.xpath("*[local-name()='VirtualQuantity']").text 54 | end 55 | end 56 | 57 | if cpu_count 58 | return cpu_count 59 | else 60 | fail Errors::BoxAttributeError, error_message: 'Could not determine number of CPUs' 61 | end 62 | end 63 | 64 | def memory 65 | memory_in_bytes = nil 66 | 67 | ovf.xpath("//*[local-name()='VirtualHardwareSection']/*[local-name()='Item']/*[local-name()='ResourceType']").each do |item| 68 | if item.text == "4" 69 | memory_in_bytes = size_in_bytes(item.parent.xpath("*[local-name()='VirtualQuantity']").text, 70 | item.parent.xpath("*[local-name()='AllocationUnits']").text) 71 | end 72 | end 73 | 74 | if memory_in_bytes 75 | return memory_in_bytes 76 | else 77 | fail Errors::BoxAttributeError, error_message: 'Could not determine amount of memory' 78 | end 79 | end 80 | 81 | def disk_interface 82 | controller_type = {} 83 | controller_used_by_disk = nil 84 | ovf.xpath("//*[local-name()='VirtualHardwareSection']/*[local-name()='Item']/*[local-name()='ResourceType']").each do |device| 85 | # when we find a controller, store its ID and type 86 | # when we find a disk, store the ID of the controller it is connected to 87 | case device.text 88 | when '5' 89 | controller_type[device.parent.xpath("*[local-name()='InstanceID']").text] = 'ide' 90 | when '6' 91 | controller_type[device.parent.xpath("*[local-name()='InstanceID']").text] = 'scsi' 92 | when '20' 93 | controller_type[device.parent.xpath("*[local-name()='InstanceID']").text] = 'sata' 94 | when '17' 95 | controller_used_by_disk = device.parent.xpath("*[local-name()='Parent']").text 96 | end 97 | end 98 | if controller_used_by_disk and controller_type[controller_used_by_disk] 99 | return controller_type[controller_used_by_disk] 100 | else 101 | fail Errors::BoxAttributeError, error_message: 'Could not determine disk interface' 102 | end 103 | end 104 | 105 | private 106 | 107 | def ovf 108 | if @ovf 109 | return @ovf 110 | end 111 | 112 | ovf_file = File.join(@dir, 'box.ovf') 113 | begin 114 | @ovf = File.open(ovf_file) { |f| Nokogiri::XML(f) } 115 | rescue => e 116 | raise Errors::BoxAttributeError, error_message: e.message 117 | end 118 | end 119 | 120 | # Takes a quantity and a unit 121 | # returns quantity in bytes 122 | # mib = true to use mebibytes, etc 123 | # defaults to false because ovf MB != megabytes 124 | def size_in_bytes(qty, unit, mib = false) 125 | qty = qty.to_i 126 | unit = unit.downcase 127 | unless mib 128 | case unit 129 | when 'kb', 'kilobytes' 130 | unit = 'kib' 131 | when 'mb', 'megabytes' 132 | unit = 'mib' 133 | when 'gb', 'gigabytes' 134 | unit = 'gib' 135 | end 136 | end 137 | case unit 138 | when 'b', 'bytes' 139 | qty 140 | when 'kb', 'kilobytes' 141 | (qty * 1000) 142 | when 'kib', 'kibibytes' 143 | (qty * 1024) 144 | when 'mb', 'megabytes' 145 | (qty * 1_000_000) 146 | when 'm', 'mib', 'mebibytes' 147 | (qty * 1_048_576) 148 | when 'gb', 'gigabytes' 149 | (qty * 1_000_000_000) 150 | when 'g', 'gib', 'gibibytes' 151 | (qty * 1_073_741_824) 152 | else 153 | fail ArgumentError, "Unknown unit #{unit}" 154 | end 155 | end 156 | end 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/box_loader.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'json' 3 | require 'uri' 4 | require 'vagrant/util/subprocess' 5 | require 'vagrant/util/downloader' 6 | require 'vagrant/box' 7 | require 'vagrant/box_metadata' 8 | 9 | module VagrantMutate 10 | class BoxLoader 11 | def initialize(env) 12 | @env = env 13 | @logger = Log4r::Logger.new('vagrant::mutate') 14 | @tmp_files = [] 15 | end 16 | 17 | def prepare_for_output(name, provider_name, version) 18 | @logger.info "Preparing #{name} for output as #{provider_name} with version #{version}." 19 | safe_name = sanitize_name(name) 20 | dir = create_output_dir(safe_name, provider_name, version) 21 | box = create_box(provider_name, name, version, dir) 22 | 23 | if box.supported_output 24 | return box 25 | else 26 | fail Errors::ProviderNotSupported, provider: provider_name, direction: 'output' 27 | end 28 | end 29 | 30 | def load(box_arg, provider_name, input_version) 31 | if box_arg =~ /:\/\// 32 | box = load_from_url(box_arg) 33 | elsif File.file?(box_arg) 34 | box = load_from_file(box_arg) 35 | else 36 | box = load_from_boxes_path(box_arg, provider_name, input_version) 37 | end 38 | 39 | if box.supported_input 40 | return box 41 | else 42 | fail Errors::ProviderNotSupported, provider: box.provider_name, direction: 'input' 43 | end 44 | end 45 | 46 | def load_from_url(url) 47 | @logger.info "Loading box from url #{url}" 48 | 49 | # test that we have a valid url 50 | url = URI(url) 51 | unless url.scheme and url.host and url.path 52 | fail Errors::URLError, url: url 53 | end 54 | 55 | # extract the name of the box from the url 56 | # if it ends in .box remove that extension 57 | # if not just remove leading slash 58 | name = nil 59 | if url.path =~ /([-.\w]+).box$/ 60 | name = Regexp.last_match[1] 61 | else 62 | name = url.path.sub(/^\//, '') 63 | end 64 | if name.empty? 65 | fail Errors::URLError, url: url 66 | end 67 | 68 | # Extract the version of the box from the URL 69 | if url.path =~ /\/([\d.]+)\// 70 | version = Regexp.last_match[1] 71 | @logger.info "Pulled version from URL (#{version})" 72 | else 73 | version = '0' 74 | @logger.info "No version found in URL, assuming '0'" 75 | end 76 | 77 | # using same path as in vagrants box add action 78 | download_path = File.join(@env.tmp_path, 'box' + Digest::SHA1.hexdigest(url.to_s)) 79 | @tmp_files << download_path 80 | 81 | # if this fails it will raise an error and we'll quit 82 | @env.ui.info "Downloading box #{name} from #{url}" 83 | downloader = Vagrant::Util::Downloader.new(url, download_path, ui: @env.ui) 84 | downloader.download! 85 | 86 | dir = unpack(download_path) 87 | provider_name = determine_provider(dir) 88 | 89 | create_box(provider_name, name, version, dir) 90 | end 91 | 92 | def load_from_file(file) 93 | @logger.info "Loading box from file #{file}" 94 | name = File.basename(file, File.extname(file)) 95 | 96 | dir = unpack(file) 97 | provider_name = determine_provider(dir) 98 | version = determine_version(dir) 99 | 100 | create_box(provider_name, name, version, dir) 101 | end 102 | 103 | def load_from_boxes_path(name, provider_name, input_version) 104 | @logger.info "Loading box #{name} from vagrants box path using provider #{provider_name} and version #{input_version}." 105 | safe_name = sanitize_name(name) 106 | if provider_name 107 | @logger.info "Checking directory for provider #{provider_name}." 108 | if input_version 109 | @logger.info 'Input version provided, using it.' 110 | version = input_version 111 | else 112 | @logger.info 'No version provided, getting it.' 113 | version = get_version(safe_name) 114 | @logger.info "Version = #{version}" 115 | end 116 | dir = verify_input_dir(provider_name, safe_name, version) 117 | else 118 | @logger.info 'Working out provider, version and directory...' 119 | provider_name, version, dir = find_input_dir(safe_name) 120 | end 121 | @logger.info "Creating #{name} box using provider #{provider_name} with version #{version} in #{dir}." 122 | create_box(provider_name, name, version, dir) 123 | end 124 | 125 | def cleanup 126 | unless @tmp_files.empty? 127 | @env.ui.info 'Cleaning up temporary files.' 128 | @tmp_files.each do |f| 129 | @logger.info "Deleting #{f}" 130 | FileUtils.remove_entry_secure(f) 131 | end 132 | end 133 | end 134 | 135 | private 136 | 137 | def create_box(provider_name, name, version, dir) 138 | @logger.info "Creating box #{name} with provider #{provider_name} and version #{version} in #{dir}" 139 | case provider_name 140 | when 'bhyve' 141 | require_relative 'box/bhyve' 142 | Box::Bhyve.new(@env, name, version, dir) 143 | when 'kvm' 144 | require_relative 'box/kvm' 145 | Box::Kvm.new(@env, name, version, dir) 146 | when 'libvirt' 147 | require_relative 'box/libvirt' 148 | Box::Libvirt.new(@env, name, version, dir) 149 | when 'virtualbox' 150 | require_relative 'box/virtualbox' 151 | Box::Virtualbox.new(@env, name, version, dir) 152 | else 153 | fail Errors::ProviderNotSupported, provider: provider_name, direction: 'input or output' 154 | end 155 | end 156 | 157 | def create_output_dir(name, provider_name, version) 158 | # e.g. $HOME/.vagrant.d/boxes/fedora-19/0/libvirt 159 | @logger.info "Attempting to create output dir for #{name} with version #{version} and provider #{provider_name}." 160 | out_dir = File.join(@env.boxes_path, name, version, provider_name) 161 | @logger.info "Creating out_dir #{out_dir}." 162 | begin 163 | FileUtils.mkdir_p(out_dir) 164 | rescue => e 165 | raise Errors::CreateBoxDirFailed, error_message: e.message 166 | end 167 | @logger.info "Created output directory #{out_dir}" 168 | out_dir 169 | end 170 | 171 | def unpack(file) 172 | @env.ui.info 'Extracting box file to a temporary directory.' 173 | unless File.exist? file 174 | fail Errors::BoxNotFound, box: file 175 | end 176 | tmp_dir = Dir.mktmpdir(nil, @env.tmp_path) 177 | @tmp_files << tmp_dir 178 | result = Vagrant::Util::Subprocess.execute( 179 | 'bsdtar', '-v', '-x', '-m', '-C', tmp_dir.to_s, '-f', file) 180 | if result.exit_code != 0 181 | fail Errors::ExtractBoxFailed, error_message: result.stderr.to_s 182 | end 183 | @logger.info "Unpacked box to #{tmp_dir}" 184 | tmp_dir 185 | end 186 | 187 | def determine_provider(dir) 188 | metadata_file = File.join(dir, 'metadata.json') 189 | if File.exist? metadata_file 190 | begin 191 | metadata = JSON.load(File.new(metadata_file, 'r')) 192 | rescue => e 193 | raise Errors::LoadMetadataFailed, error_message: e.message 194 | end 195 | @logger.info "Determined input provider is #{metadata['provider']}" 196 | return metadata['provider'] 197 | else 198 | @logger.info 'No metadata found, so assuming input provider is virtualbox' 199 | return 'virtualbox' 200 | end 201 | end 202 | 203 | def determine_version(dir) 204 | metadata_file = File.join(dir, 'metadata.json') 205 | if File.exist? metadata_file 206 | begin 207 | metadata = JSON.load(File.new(metadata_file, 'r')) 208 | rescue => e 209 | raise Errors::LoadMetadataFailed, error_message: e.message 210 | end 211 | # Handle single or multiple versions 212 | if metadata['versions'].nil? 213 | @logger.info 'No versions provided by metadata, asuming version 0' 214 | version = '0' 215 | elsif metadata['versions'].length > 1 216 | metadata['versions'].each do |metadata_version| 217 | @logger.info 'Itterating available metadata versions for active version.' 218 | next unless metadata_version['status'] == 'active' 219 | version = metadata_version['version'] 220 | end 221 | else 222 | @logger.info 'Only one metadata version, grabbing version.' 223 | version = metadata['versions'][0]['version'] 224 | end 225 | @logger.info "Determined input version is #{version}" 226 | return version 227 | else 228 | @logger.info 'No metadata found, so assuming version is 0' 229 | return '0' 230 | end 231 | end 232 | 233 | def verify_input_dir(provider_name, name, version) 234 | input_dir = File.join(@env.boxes_path, name, version, provider_name) 235 | if File.directory?(input_dir) 236 | @logger.info "Found input directory #{input_dir}" 237 | return input_dir 238 | else 239 | fail Errors::BoxNotFound, box: input_dir 240 | end 241 | end 242 | 243 | def find_input_dir(name) 244 | @logger.info "Looking for input dir for box #{name}." 245 | version = get_version(name) 246 | box_parent_dir = File.join(@env.boxes_path, name, version) 247 | 248 | if Dir.exist?(box_parent_dir) 249 | providers = Dir.entries(box_parent_dir).reject { |entry| entry =~ /^\./ } 250 | @logger.info "Found potential providers #{providers}" 251 | else 252 | providers = [] 253 | end 254 | 255 | case 256 | when providers.length < 1 257 | fail Errors::BoxNotFound, box: name 258 | when providers.length > 1 259 | fail Errors::TooManyBoxesFound, box: name 260 | else 261 | provider_name = providers.first 262 | input_dir = File.join(box_parent_dir, provider_name) 263 | @logger.info "Found source for box #{name} from provider #{provider_name} with version #{version} at #{input_dir}" 264 | return provider_name, version, input_dir 265 | end 266 | end 267 | 268 | def get_version(name) 269 | # Get a list of directories for this box 270 | @logger.info "Getting versions for #{name}." 271 | 272 | box_dir = File.join(@env.boxes_path, name, '*') 273 | possible_versions = Dir.glob(box_dir).select { |f| File.directory? f }.map { |x| x.split('/').last } 274 | 275 | @logger.info "Possible_versions = #{possible_versions.inspect}" 276 | 277 | if possible_versions.length > 1 278 | @logger.info 'Got multiple possible versions, selecting max value' 279 | version = possible_versions.max 280 | elsif possible_versions.length == 1 281 | @logger.info 'Got a single version, so returning it' 282 | version = possible_versions.first 283 | else 284 | fail Errors::BoxNotFound, box: name 285 | end 286 | 287 | @logger.info "Found version #{version}" 288 | version 289 | end 290 | 291 | def sanitize_name(name) 292 | if name =~ /\// 293 | @logger.info 'Replacing / with -VAGRANTSLASH-.' 294 | name = name.dup 295 | name.gsub!('/', '-VAGRANTSLASH-') 296 | @logger.info "New name = #{name}." 297 | end 298 | name 299 | end 300 | end 301 | end 302 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/converter/bhyve.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | module VagrantMutate 4 | module Converter 5 | class Bhyve < Converter 6 | def generate_metadata 7 | 8 | output_name = File.join(@output_box.dir, @output_box.image_name).shellescape 9 | 10 | file_output = `file #{output_name}` 11 | 12 | if file_output.include? "GRUB" 13 | loader = "grub-bhyve" 14 | else 15 | loader = "bhyveload" 16 | end 17 | 18 | { 19 | "provider" => @output_box.provider_name, 20 | } 21 | end 22 | 23 | def generate_vagrantfile 24 | memory = @input_box.memory / 1024 / 1024 # convert bytes to mebibytes 25 | cpus = @input_box.cpus 26 | <<-EOF 27 | config.vm.provider :bhyve do |vm| 28 | vm.memory = "#{memory}M" 29 | vm.cpus = "#{cpus}" 30 | end 31 | EOF 32 | end 33 | 34 | def write_specific_files 35 | # nothing yet 36 | end 37 | 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/converter/converter.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'shellwords' 3 | 4 | module VagrantMutate 5 | module Converter 6 | class Converter 7 | def self.create(env, input_box, output_box, force_virtio='false') 8 | case output_box.provider_name 9 | when 'bhyve' 10 | require_relative 'bhyve' 11 | Bhyve.new(env, input_box, output_box) 12 | when 'kvm' 13 | require_relative 'kvm' 14 | Kvm.new(env, input_box, output_box) 15 | when 'libvirt' 16 | require_relative 'libvirt' 17 | Libvirt.new(env, input_box, output_box, force_virtio) 18 | else 19 | fail Errors::ProviderNotSupported, provider: output_box.provider_name, direction: 'output' 20 | end 21 | end 22 | 23 | def initialize(env, input_box, output_box, force_virtio='false') 24 | @env = env 25 | @input_box = input_box 26 | @output_box = output_box 27 | @force_virtio = force_virtio 28 | @logger = Log4r::Logger.new('vagrant::mutate') 29 | end 30 | 31 | def convert 32 | if @input_box.provider_name == @output_box.provider_name 33 | fail Errors::ProvidersMatch 34 | end 35 | 36 | @env.ui.info "Converting #{@input_box.name} from #{@input_box.provider_name} "\ 37 | "to #{@output_box.provider_name}." 38 | 39 | @input_box.verify_format 40 | write_disk 41 | write_metadata 42 | write_vagrantfile 43 | write_specific_files 44 | end 45 | 46 | private 47 | 48 | def write_metadata 49 | metadata = generate_metadata 50 | begin 51 | File.open(File.join(@output_box.dir, 'metadata.json'), 'w') do |f| 52 | f.write(JSON.generate(metadata)) 53 | end 54 | rescue => e 55 | raise Errors::WriteMetadataFailed, error_message: e.message 56 | end 57 | @logger.info 'Wrote metadata' 58 | end 59 | 60 | def write_vagrantfile 61 | body = generate_vagrantfile 62 | begin 63 | File.open(File.join(@output_box.dir, 'Vagrantfile'), 'w') do |f| 64 | f.puts('Vagrant.configure("2") do |config|') 65 | f.puts(body) 66 | f.puts('end') 67 | end 68 | rescue => e 69 | raise Errors::WriteVagrantfileFailed, error_message: e.message 70 | end 71 | @logger.info 'Wrote vagrantfile' 72 | end 73 | 74 | def write_disk 75 | if @input_box.image_format == @output_box.image_format 76 | copy_disk 77 | else 78 | convert_disk 79 | end 80 | end 81 | 82 | def copy_disk 83 | input = File.join(@input_box.dir, @input_box.image_name) 84 | output = File.join(@output_box.dir, @output_box.image_name) 85 | @logger.info "Copying #{input} to #{output}" 86 | begin 87 | FileUtils.copy_file(input, output) 88 | rescue => e 89 | raise Errors::WriteDiskFailed, error_message: e.message 90 | end 91 | end 92 | 93 | def convert_disk 94 | input_file = File.join(@input_box.dir, @input_box.image_name).shellescape 95 | output_file = File.join(@output_box.dir, @output_box.image_name).shellescape 96 | output_format = @output_box.image_format 97 | 98 | # p for progress bar 99 | # S for sparse file 100 | qemu_options = '-p -S 16k' 101 | qemu_version = Qemu.qemu_version() 102 | if qemu_version >= Gem::Version.new('1.1.0') 103 | if output_format == 'qcow2' 104 | qemu_options += ' -o compat=1.1' 105 | end 106 | end 107 | 108 | command = "qemu-img convert #{qemu_options} -O #{output_format} #{input_file} #{output_file}" 109 | @logger.info "Running #{command}" 110 | unless system(command) 111 | fail Errors::WriteDiskFailed, error_message: "qemu-img exited with status #{$CHILD_STATUS.exitstatus}" 112 | end 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/converter/kvm.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | module VagrantMutate 4 | module Converter 5 | class Kvm < Converter 6 | def generate_metadata 7 | { 'provider' => @output_box.provider_name } 8 | end 9 | 10 | def generate_vagrantfile 11 | " config.vm.base_mac = '#{@input_box.mac_address}'" 12 | end 13 | 14 | def write_specific_files 15 | template_path = VagrantMutate.source_root.join('templates', 'kvm', 'box.xml.erb') 16 | template = File.read(template_path) 17 | 18 | uuid = nil 19 | gui = true 20 | 21 | if @force_virtio == true 22 | disk_bus = 'virtio' 23 | else 24 | disk_bus = @input_box.disk_interface 25 | end 26 | 27 | image_type = @output_box.image_format 28 | disk = @output_box.image_name 29 | 30 | name = @input_box.name 31 | memory = @input_box.memory / 1024 # convert bytes to kib 32 | cpus = @input_box.cpus 33 | mac = format_mac(@input_box.mac_address) 34 | arch = @input_box.architecture 35 | 36 | qemu_bin = find_kvm 37 | 38 | File.open(File.join(@output_box.dir, 'box.xml'), 'w') do |f| 39 | f.write(ERB.new(template).result(binding)) 40 | end 41 | end 42 | 43 | private 44 | 45 | def find_kvm 46 | qemu_bin = nil 47 | 48 | qemu_bin_list = ['qemu-system-x86_64', 49 | 'qemu-system-i386', 50 | 'qemu-kvm', 51 | 'kvm'] 52 | logger = Log4r::Logger.new('vagrant::mutate') 53 | qemu_bin_list.each do |qemu| 54 | qemu_bin = VagrantMutate.find_bin(qemu) 55 | break 56 | end 57 | 58 | unless qemu_bin 59 | fail Errors::QemuNotFound 60 | end 61 | logger.info 'Found qemu_bin: ' + qemu_bin 62 | qemu_bin 63 | end 64 | 65 | # convert to format with colons 66 | def format_mac(mac) 67 | mac.scan(/(.{2})/).join(':') 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/converter/libvirt.rb: -------------------------------------------------------------------------------- 1 | module VagrantMutate 2 | module Converter 3 | class Libvirt < Converter 4 | 5 | def generate_metadata 6 | { 7 | 'provider' => @output_box.provider_name, 8 | 'format' => @output_box.image_format, 9 | 'virtual_size' => ( @input_box.virtual_size.to_f / (1024 * 1024 * 1024)).ceil 10 | } 11 | end 12 | 13 | def generate_vagrantfile 14 | 15 | if @force_virtio == true 16 | disk_bus = 'virtio' 17 | else 18 | disk_bus = @input_box.disk_interface 19 | end 20 | 21 | <<-EOF 22 | config.vm.provider :libvirt do |libvirt| 23 | libvirt.disk_bus = '#{disk_bus}' 24 | end 25 | EOF 26 | end 27 | 28 | def write_specific_files 29 | # nothing to do here 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/errors.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | module VagrantMutate 4 | module Errors 5 | class VagrantMutateError < Vagrant::Errors::VagrantError 6 | error_namespace('vagrant_mutate.errors') 7 | end 8 | 9 | class ProvidersMatch < VagrantMutateError 10 | error_key(:providers_match) 11 | end 12 | 13 | class ProviderNotSupported < VagrantMutateError 14 | error_key(:provider_not_supported) 15 | end 16 | 17 | class QemuNotFound < VagrantMutateError 18 | error_key(:qemu_not_found) 19 | end 20 | 21 | class QemuImgNotFound < VagrantMutateError 22 | error_key(:qemu_img_not_found) 23 | end 24 | 25 | class BoxNotFound < VagrantMutateError 26 | error_key(:box_not_found) 27 | end 28 | 29 | class TooManyBoxesFound < VagrantMutateError 30 | error_key(:too_many_boxes_found) 31 | end 32 | 33 | class ExtractBoxFailed < VagrantMutateError 34 | error_key(:extract_box_failed) 35 | end 36 | 37 | class ParseIdentifierFailed < VagrantMutateError 38 | error_key(:parse_identifier_failed) 39 | end 40 | 41 | class DetermineProviderFailed < VagrantMutateError 42 | error_key(:determine_provider_failed) 43 | end 44 | 45 | class LoadMetadataFailed < VagrantMutateError 46 | error_key(:load_metadata_failed) 47 | end 48 | 49 | class CreateBoxDirFailed < VagrantMutateError 50 | error_key(:create_box_dir_failed) 51 | end 52 | 53 | class WriteMetadataFailed < VagrantMutateError 54 | error_key(:write_metadata_failed) 55 | end 56 | 57 | class WriteVagrantfileFailed < VagrantMutateError 58 | error_key(:write_vagrantfile_failed) 59 | end 60 | 61 | class WriteDiskFailed < VagrantMutateError 62 | error_key(:write_disk_failed) 63 | end 64 | 65 | class ParseQemuVersionFailed < VagrantMutateError 66 | error_key(:parse_qemu_version_failed) 67 | end 68 | 69 | class QemuInfoFailed < VagrantMutateError 70 | error_key(:qemu_info_failed) 71 | end 72 | 73 | class BoxAttributeError < VagrantMutateError 74 | error_key(:box_attribute_error) 75 | end 76 | 77 | class URLError < VagrantMutateError 78 | error_key(:url_error) 79 | end 80 | 81 | class MetadataNotFound < VagrantMutateError 82 | error_key(:metadata_not_found) 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/mutate.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant-mutate/box_loader' 2 | require 'vagrant-mutate/qemu' 3 | require 'vagrant-mutate/converter/converter' 4 | 5 | module VagrantMutate 6 | class Mutate < Vagrant.plugin(2, :command) 7 | def execute 8 | options = {} 9 | options[:input_provider] = nil 10 | options[:version] = nil 11 | options[:force_virtio] = false 12 | 13 | opts = OptionParser.new do |o| 14 | o.banner = 'Usage: vagrant mutate ' 15 | o.on('--input-provider PROVIDER', 16 | 'Specify provider for input box') do |p| 17 | options[:input_provider] = p 18 | end 19 | o.on('--version VERSION', 20 | 'Specify version for input box') do |p| 21 | options[:version] = p 22 | end 23 | o.on('--force-virtio', 24 | 'Force virtio disk driver') do |p| 25 | options[:force_virtio] = true 26 | end 27 | end 28 | 29 | argv = parse_options(opts) 30 | return unless argv 31 | 32 | unless argv.length == 2 33 | @env.ui.info(opts.help) 34 | return 35 | end 36 | 37 | options[:box_arg] = argv[0] 38 | options[:output_provider] = argv[1] 39 | 40 | Qemu.verify_qemu_installed 41 | Qemu.verify_qemu_version(@env) 42 | 43 | input_loader = BoxLoader.new(@env) 44 | input_box = input_loader.load(options[:box_arg], options[:input_provider], options[:version]) 45 | 46 | output_loader = BoxLoader.new(@env) 47 | output_box = output_loader.prepare_for_output(input_box.name, options[:output_provider], input_box.version) 48 | 49 | converter = Converter::Converter.create(@env, input_box, output_box, options[:force_virtio]) 50 | converter.convert 51 | 52 | input_loader.cleanup 53 | 54 | @env.ui.info "The box #{output_box.name} (#{output_box.provider_name}) is now ready to use." 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/qemu.rb: -------------------------------------------------------------------------------- 1 | module VagrantMutate 2 | class Qemu 3 | def self.verify_qemu_installed 4 | qemu_img_bin = nil 5 | logger = Log4r::Logger.new('vagrant::mutate') 6 | qemu_img_bin = VagrantMutate.find_bin("qemu-img") 7 | unless qemu_img_bin 8 | fail Errors::QemuImgNotFound 9 | end 10 | logger.info 'Found qemu-img: ' + qemu_img_bin 11 | qemu_img_bin 12 | end 13 | 14 | def self.qemu_version() 15 | usage = `qemu-img --version` 16 | if usage =~ /(\d+\.\d+\.\d+)/ 17 | return Gem::Version.new(Regexp.last_match[1]) 18 | else 19 | fail Errors::ParseQemuVersionFailed 20 | end 21 | end 22 | 23 | def self.verify_qemu_version(env) 24 | installed_version = qemu_version() 25 | # less than 1.2 or equal to 1.6.x 26 | if installed_version < Gem::Version.new('1.2.0') or (installed_version >= Gem::Version.new('1.6.0') and installed_version < Gem::Version.new('1.7.0')) 27 | 28 | env.ui.warn "You have qemu #{installed_version} installed. "\ 29 | 'This version cannot read some virtualbox boxes. '\ 30 | 'If conversion fails, see below for recommendations. '\ 31 | 'https://github.com/sciurus/vagrant-mutate/wiki/QEMU-Version-Compatibility' 32 | 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/vagrant-mutate/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantMutate 2 | VERSION = '1.2.0' 3 | end 4 | -------------------------------------------------------------------------------- /locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | vagrant_mutate: 3 | errors: 4 | providers_match: |- 5 | Input and output provider are the same. Aborting. 6 | provider_not_supported: |- 7 | Vagrant-mutate does not support %{provider} for %{direction} 8 | qemu_not_found: |- 9 | QEMU was not found in your path 10 | qemu_img_not_found: |- 11 | qemu-img was not found in your path 12 | box_not_found: |- 13 | The box %{box} was not found 14 | too_many_boxes_found: |- 15 | More than one box named %{box} was found. Please specify the input provider as well. 16 | extract_box_failed: |- 17 | Extracting box failed with error: 18 | %{error_message} 19 | parse_identifier_failed: |- 20 | Expected name or provider/name but you gave %{identifier}. 21 | If you meant to specify a box by name fix this. If you meant to specify a box by path make sure the file exists. 22 | determine_provider_failed: |- 23 | Determining provider for box failed with error: 24 | %{error_message} 25 | load_metadata_failed: |- 26 | Loading metadata for box failed with error: 27 | %{error_message} 28 | create_box_dir_failed: |- 29 | Creating directory for box failed with error: 30 | %{error_message} 31 | write_metadata_failed: |- 32 | Writing metadata for box failed with error: 33 | %{error_message} 34 | write_vagrantfile_failed: |- 35 | Writing vagrantfile for box failed with error: 36 | %{error_message} 37 | write_disk_failed: |- 38 | Writing disk image for box failed with error: 39 | %{error_message} 40 | parse_qemu_version_failed: |- 41 | Determining the version of qemu-img installed failed 42 | qemu_info_failed: |- 43 | Getting information about the disk image via qemu-info failed 44 | box_attribute_error: |- 45 | Error determining information about the input box: 46 | %{error_message} 47 | url_error: |- 48 | The url %{url} is not valid 49 | metadata_not_found: |- 50 | Unable to find metadata file for %{box} 51 | -------------------------------------------------------------------------------- /templates/kvm/box.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= name %> 3 | <% if uuid %> 4 | <%= uuid %> 5 | <% end %> 6 | <%= memory %> 7 | <%= memory%> 8 | <%= cpus %> 9 | 10 | hvm 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | destroy 20 | restart 21 | restart 22 | 23 | <%= qemu_bin %> 24 | 25 | 26 | 27 | 28 | <% if disk_bus == 'virtio' %> 29 |
30 | <% else %> 31 |
32 | <% end %> 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | <% if gui %> 50 | <%= "" %> 51 | <% end %> 52 |