├── .gitignore ├── .rspec ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── example.box ├── example_box ├── README.md ├── initrd.gz ├── metadata.json └── vmlinuz ├── lib ├── vagrant-xhyve.rb └── vagrant-xhyve │ ├── action.rb │ ├── action │ ├── boot.rb │ ├── import.rb │ ├── is_created.rb │ ├── is_stopped.rb │ ├── message_already_created.rb │ ├── message_not_created.rb │ ├── message_will_not_destroy.rb │ ├── read_ssh_info.rb │ ├── read_state.rb │ ├── stop_instance.rb │ ├── terminate_instance.rb │ ├── timed_provision.rb │ └── wait_for_state.rb │ ├── config.rb │ ├── errors.rb │ ├── plugin.rb │ ├── provider.rb │ ├── util │ ├── timer.rb │ └── vagrant-xhyve.rb │ └── version.rb ├── locales └── en.yml ├── spec ├── spec_helper.rb └── vagrant-xhyve │ └── config_spec.rb ├── templates ├── metadata.json.erb └── vagrant-aws_package_Vagrantfile.erb ├── vagrant-xhyve.gemspec └── vendor └── xhyve-ruby ├── .gitignore ├── .travis.yml ├── Gemfile ├── README.md ├── Rakefile ├── example └── test.rb ├── lib ├── rubygems_plugin.rb ├── xhyve.rb └── xhyve │ ├── dhcp.rb │ ├── guest.rb │ ├── vendor │ └── xhyve │ └── version.rb ├── spec ├── fixtures │ ├── dhcpd_leases.txt │ └── guest │ │ ├── README.md │ │ ├── initrd │ │ └── vmlinuz ├── lib │ ├── dhcp_spec.rb │ └── guest_spec.rb └── spec_helper.rb └── xhyve-ruby.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | # OS-specific 2 | .DS_Store 3 | 4 | # editors 5 | *.swp 6 | 7 | # Bundler/Rubygems 8 | *.gem 9 | .bundle 10 | pkg/* 11 | tags 12 | Gemfile.lock 13 | 14 | # Vagrant 15 | .vagrant 16 | Vagrantfile 17 | example_box 18 | example.box 19 | 20 | # RVM files for gemset/ruby setting 21 | .ruby-* 22 | .rvmrc 23 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format doc --order random --color --fail-fast 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.4.1 (December 10th, 2016) 2 | 3 | * Put the embedded version of xhyve-ruby first in the LOAD_PATH (Guy Pascarella) 4 | 5 | # 0.4.0 (December 10th, 2016) 6 | 7 | * Added qcow support to use with hyperkit 8 | * Fixed intermittent failures with certain MAC addresses 9 | 10 | # 0.3.0 (December 5th, 2016) 11 | 12 | * Added `kernel_command` option to set custom kernel parameters (Nuno Passaro) 13 | 14 | # 0.2.0 (July 23, 2016) 15 | 16 | * Added ability to set custom binary (to work with docker beta etc) 17 | 18 | # 0.1.1 (April 5, 2016) 19 | 20 | * Initial release. 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | group :plugins do 4 | gemspec 5 | gem "xhyve-ruby", :path => "vendor/xhyve-ruby" 6 | end 7 | 8 | group :development do 9 | # We depend on Vagrant for development, but we don't add it as a 10 | # gem dependency because we expect to be installed within the 11 | # Vagrant environment itself using `vagrant plugin`. 12 | gem "vagrant", :git => "https://github.com/mitchellh/vagrant.git" 13 | end 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Patrick Armstrong 3 | Copyright (c) 2014 Mitchell Hashimoto 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vagrant xhyve Provider 2 | 3 | [![Gem Version](https://badge.fury.io/rb/vagrant-xhyve.svg)](https://badge.fury.io/rb/vagrant-xhyve) 4 | 5 | This is a [Vagrant](http://www.vagrantup.com) plugin that adds an [xhyve](http://xhyve.org) 6 | provider to Vagrant. 7 | 8 | ## Features 9 | 10 | * Basic features work 11 | * Can work with Hyperkit fork of Xhyve 12 | * qcow image support 13 | 14 | ## Limitations 15 | 16 | You need to use sudo for most vagrant actions with the xhyve driver, 17 | due to the entitlements needed for xhyve to run without sudo. More details 18 | in the [xhyve github issue](https://github.com/mist64/xhyve/issues/60). 19 | 20 | ## Usage 21 | 22 | Install using standard Vagrant plugin installation methods. After 23 | installing, `vagrant up` and specify the `xhyve` provider. An example is 24 | shown below. 25 | 26 | ``` 27 | $ vagrant plugin install vagrant-xhyve 28 | ... 29 | $ sudo vagrant up --provider=xhyve 30 | ... 31 | ``` 32 | 33 | Of course prior to doing this, you'll need to obtain an xhyve-compatible 34 | box file for Vagrant. 35 | 36 | ## Quick Start 37 | 38 | After installing the plugin (instructions above), you can try an xhyve ubuntu 39 | linux example. This is similar to the example from the xhyve intro blog post. 40 | 41 | ``` 42 | $ mkdir xhyve-vagrant 43 | $ cd xhyve-vagrant 44 | $ vagrant init oldpatricka/ubuntu-14.04 45 | $ sudo vagrant up --provider xhyve 46 | ... 47 | ``` 48 | 49 | This will start an Ubuntu Linux instance. you can log in with: 50 | 51 | ``` 52 | $ sudo vagrant ssh 53 | ``` 54 | 55 | ## Box Format 56 | 57 | The vagrant-xhyve box format is pretty straightforward. See 58 | the [example_box/ directory](https://github.com/oldpatricka/vagrant-xhyve/tree/master/example_box). 59 | That directory also contains instructions on how to build a box. 60 | 61 | ## Configuration 62 | 63 | This provider exposes quite a few provider-specific configuration options: 64 | 65 | * `memory` - The amount of memory to give the VM. This can just be a simple 66 | integer for memory in MB or you can use the suffixed style, eg. 2G for two 67 | Gigabytes 68 | * `cpus` - The number of CPUs to give the VM 69 | * `xhyve_binary` - use a custom xhyve version 70 | * kernel_command - send a custom kernel boot command 71 | 72 | These can be set like typical provider-specific configuration: 73 | 74 | ```ruby 75 | Vagrant.configure("2") do |config| 76 | # ... other stuff 77 | 78 | config.vm.provider :xhyve do |xhyve| 79 | xhyve.cpus = 2 80 | xhyve.memory = "1G" 81 | xhyve.xhyve_binary = "/Applications/Docker.app/Contents/MacOS/com.docker.hyperkit" 82 | xhyve.kernel_command = "root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap acpi=off console=ttyS0 LANG=en_GB.UTF-8" # example for a CentOS installed in a LVM filesystem 83 | end 84 | end 85 | ``` 86 | ## Synced Folders 87 | 88 | There is minimal support for synced folders. Upon `vagrant up`, 89 | `vagrant reload`, and `vagrant provision`, the XHYVE provider will use 90 | `rsync` (if available) to uni-directionally sync the folder to 91 | the remote machine over SSH. 92 | 93 | ## Using Docker's HyperKit Fork of Xhyve 94 | 95 | Docker has a very nice port of Xhyve called [HyperKit](https://github.com/docker/hyperkit). It has some interesting features like better stability and qcow support (which this provider can't use yet). 96 | 97 | If you want to try it out, either install hyperkit directly, or you can use the version bundled with [Docker for Mac](https://docs.docker.com/engine/installation/mac/). The path to the binary is `/Applications/Docker.app/Contents/MacOS/com.docker.hyperkit`. See the configuration section above for how to use this with the `xhyve_binary` option. 98 | 99 | ## Questions 100 | 101 | Q. Should I use this for my work? 102 | 103 | A. Do you want to keep your job? I'm not even sure you should use this for toy 104 | projects. 105 | 106 | Q. Why? 107 | 108 | A. This project is powered by ignorance and good intentions. 109 | 110 | Q. Will I ever not have to use sudo or setuid root? 111 | 112 | A. There's a theory in that issue linked above that wrapping xhyve in an 113 | app store app would help. If that were the case, you could probably use the 114 | embedded binary with vagrant-xhyve. Another option is to use setuid root. 115 | 116 | Q. This sucks. 117 | 118 | A. That's not a question, but why don't you try out [another implementation](https://github.com/sirn/vagrant-xhyve). Looks pretty nice. 119 | 120 | ## Acknowledgements 121 | 122 | This plugin was heavilly cribbed from the vagrant-aws and vagrant-virtualbox 123 | providers. So thanks for those. 124 | 125 | This also uses the nice [xhyve-ruby](https://github.com/dalehamel/xhyve-ruby) 126 | gem, by Dale Hamel. 127 | 128 | ## Development 129 | 130 | To work on the `vagrant-xhyve` plugin, clone this repository out, and use 131 | [Bundler](http://gembundler.com) to get the dependencies: 132 | 133 | ``` 134 | $ bundle 135 | ``` 136 | 137 | Once you have the dependencies, verify the unit tests pass with `rake`: 138 | 139 | ``` 140 | $ bundle exec rake 141 | ``` 142 | 143 | If those pass, you're ready to start developing the plugin. You can test 144 | the plugin without installing it into your Vagrant environment by just 145 | creating a `Vagrantfile` in the top level of this directory (it is gitignored) 146 | and add the following line to your `Vagrantfile` 147 | ```ruby 148 | Vagrant.require_plugin "vagrant-xhyve" 149 | ``` 150 | Use bundler to execute Vagrant: 151 | ``` 152 | $ bundle exec vagrant up --provider=xhyve 153 | ``` 154 | 155 | ## Contributors 156 | 157 | * Patrick Armstrong 158 | * Nuno Passaro 159 | * Guy Pascarella 160 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'rspec/core/rake_task' 4 | 5 | # Immediately sync all stdout so that tools like buildbot can 6 | # immediately load in the output. 7 | $stdout.sync = true 8 | $stderr.sync = true 9 | 10 | # Change to the directory of this file. 11 | Dir.chdir(File.expand_path("../", __FILE__)) 12 | 13 | # This installs the tasks that help with gem creation and 14 | # publishing. 15 | Bundler::GemHelper.install_tasks 16 | 17 | # Install the `spec` task so that we can run tests. 18 | RSpec::Core::RakeTask.new(:spec) do |t| 19 | t.rspec_opts = "--order defined" 20 | end 21 | # Default task is to run the unit tests 22 | task :default => :spec 23 | -------------------------------------------------------------------------------- /example.box: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/example.box -------------------------------------------------------------------------------- /example_box/README.md: -------------------------------------------------------------------------------- 1 | # vagrant-xhyve Box Format 2 | 3 | A vagrant-xhyve box, like all vagrant boxes is an archive of a directory of 4 | files with a metadata file, a kernel, ramdisk, and optionally some raw disk 5 | images. 6 | 7 | Here's an example: 8 | 9 | ``` 10 | . 11 | |-- block0.img 12 | |-- initrd.gz 13 | |-- metadata.json 14 | `-- vmlinuz 15 | ``` 16 | 17 | The metadata.json just contains the defaults from the vagrant box documentation, 18 | that is it looks like: 19 | 20 | ``` 21 | { 22 | "provider": "xhyve" 23 | } 24 | ``` 25 | 26 | initrd.gz and vmlinuz are extracted from a raw disk image using the technique 27 | described in Michael Steil's nice blog post 28 | [introducing xhyve](http://www.pagetable.com/?p=831). 29 | 30 | You can have up to ten block devices to attach to your vm, named from block0.img 31 | upto block9.img. You can also have zero. That's ok. 32 | 33 | If you would like to use a qcow image, simply name the image block0.qcow to 34 | block9.qcow. Note this will only work with the hyperkit fork of xhyve. 35 | -------------------------------------------------------------------------------- /example_box/initrd.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/example_box/initrd.gz -------------------------------------------------------------------------------- /example_box/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "xhyve" 3 | } 4 | -------------------------------------------------------------------------------- /example_box/vmlinuz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/example_box/vmlinuz -------------------------------------------------------------------------------- /lib/vagrant-xhyve.rb: -------------------------------------------------------------------------------- 1 | require "pathname" 2 | 3 | require "vagrant-xhyve/plugin" 4 | 5 | module VagrantPlugins 6 | module XHYVE 7 | lib_path = Pathname.new(File.expand_path("../vagrant-xhyve", __FILE__)) 8 | autoload :Action, lib_path.join("action") 9 | autoload :Errors, lib_path.join("errors") 10 | 11 | # Put vagrant-xhyve-x.y.z/vendor/xhyve-ruby/lib on the LOAD_PATH before 12 | # the auto-loaded xhyve-ruby-a.b.c 13 | $LOAD_PATH.unshift(Pathname.new(File.expand_path("../../vendor/xhyve-ruby/lib", __FILE__))) 14 | 15 | # This returns the path to the source of this plugin. 16 | # 17 | # @return [Pathname] 18 | def self.source_root 19 | @source_root ||= Pathname.new(File.expand_path("../../", __FILE__)) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action.rb: -------------------------------------------------------------------------------- 1 | require "pathname" 2 | 3 | require "vagrant/action/builder" 4 | 5 | module VagrantPlugins 6 | module XHYVE 7 | module Action 8 | # Include the built-in modules so we can use them as top-level things. 9 | include Vagrant::Action::Builtin 10 | 11 | def self.action_package 12 | Vagrant::Action::Builder.new.tap do |b| 13 | b.use Call, IsCreated do |env, b2| 14 | if !env[:result] 15 | b2.use MessageNotCreated 16 | next 17 | end 18 | 19 | # Connect to AWS and then Create a package from the server instance 20 | #b2.use ConnectAWS 21 | b2.use PackageInstance 22 | end 23 | end 24 | end 25 | 26 | # This action is called to halt the remote machine. 27 | def self.action_halt 28 | Vagrant::Action::Builder.new.tap do |b| 29 | b.use ConfigValidate 30 | b.use ReadState 31 | b.use Call, IsCreated do |env, b2| 32 | if env[:result] 33 | b2.use Call, GracefulHalt, :stopped, :running do |env2, b3| 34 | if !env2[:result] 35 | b3.use StopInstance 36 | end 37 | end 38 | else 39 | b2.use MessageNotCreated 40 | end 41 | end 42 | end 43 | end 44 | 45 | # This action is called to terminate the remote machine. 46 | def self.action_destroy 47 | Vagrant::Action::Builder.new.tap do |b| 48 | b.use Call, DestroyConfirm do |env, b2| 49 | if env[:result] 50 | b2.use ConfigValidate 51 | b2.use Call, IsCreated do |env2, b3| 52 | if !env2[:result] 53 | b3.use MessageNotCreated 54 | next 55 | end 56 | b3.use TerminateInstance 57 | #b3.use ProvisionerCleanup if defined?(ProvisionerCleanup) 58 | end 59 | else 60 | b2.use MessageWillNotDestroy 61 | end 62 | end 63 | end 64 | end 65 | 66 | # This action is called when `vagrant provision` is called. 67 | def self.action_provision 68 | Vagrant::Action::Builder.new.tap do |b| 69 | b.use ConfigValidate 70 | b.use Call, IsCreated do |env, b2| 71 | if !env[:result] 72 | b2.use MessageNotCreated 73 | next 74 | end 75 | 76 | b2.use Provision 77 | end 78 | end 79 | end 80 | 81 | # This action is called to read the SSH info of the machine. The 82 | # resulting state is expected to be put into the `:machine_ssh_info` 83 | # key. 84 | def self.action_read_ssh_info 85 | Vagrant::Action::Builder.new.tap do |b| 86 | b.use ConfigValidate 87 | b.use ReadState 88 | b.use ReadSSHInfo 89 | end 90 | end 91 | 92 | # This action is called to read the state of the machine. The 93 | # resulting state is expected to be put into the `:machine_state_id` 94 | # key. 95 | def self.action_read_state 96 | Vagrant::Action::Builder.new.tap do |b| 97 | b.use ConfigValidate 98 | b.use ReadState 99 | end 100 | end 101 | 102 | # This action is called to SSH into the machine. 103 | def self.action_ssh 104 | Vagrant::Action::Builder.new.tap do |b| 105 | b.use ConfigValidate 106 | b.use Call, IsCreated do |env, b2| 107 | if !env[:result] 108 | b2.use MessageNotCreated 109 | next 110 | end 111 | 112 | b2.use SSHExec 113 | end 114 | end 115 | end 116 | 117 | def self.action_ssh_run 118 | Vagrant::Action::Builder.new.tap do |b| 119 | b.use ConfigValidate 120 | b.use Call, IsCreated do |env, b2| 121 | if !env[:result] 122 | b2.use MessageNotCreated 123 | next 124 | end 125 | 126 | b2.use SSHRun 127 | end 128 | end 129 | end 130 | 131 | def self.action_prepare_boot 132 | Vagrant::Action::Builder.new.tap do |b| 133 | b.use Import 134 | b.use Provision 135 | b.use SyncedFolders 136 | end 137 | end 138 | 139 | # This action is called to bring the box up from nothing. 140 | def self.action_up 141 | Vagrant::Action::Builder.new.tap do |b| 142 | b.use HandleBox 143 | b.use ConfigValidate 144 | b.use BoxCheckOutdated 145 | b.use Call, IsCreated do |env1, b1| 146 | if not env1[:result] 147 | b1.use action_prepare_boot 148 | end 149 | 150 | b1.use Boot # launch a new instance 151 | end 152 | end 153 | end 154 | 155 | def self.action_reload 156 | Vagrant::Action::Builder.new.tap do |b| 157 | b.use ConfigValidate 158 | b.use Call, IsCreated do |env, b2| 159 | if !env[:result] 160 | b2.use MessageNotCreated 161 | next 162 | end 163 | 164 | b2.use action_halt 165 | b2.use Call, WaitForState, :stopped, 120 do |env2, b3| 166 | if env2[:result] 167 | b3.use action_up 168 | else 169 | # TODO we couldn't reach :stopped, what now? 170 | end 171 | end 172 | end 173 | end 174 | end 175 | 176 | # The autoload farm 177 | action_root = Pathname.new(File.expand_path("../action", __FILE__)) 178 | autoload :IsCreated, action_root.join("is_created") 179 | autoload :IsStopped, action_root.join("is_stopped") 180 | autoload :MessageAlreadyCreated, action_root.join("message_already_created") 181 | autoload :MessageNotCreated, action_root.join("message_not_created") 182 | autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy") 183 | autoload :PackageInstance, action_root.join("package_instance") 184 | autoload :ReadSSHInfo, action_root.join("read_ssh_info") 185 | autoload :ReadState, action_root.join("read_state") 186 | autoload :Import, action_root.join("import") 187 | autoload :Boot, action_root.join("boot") 188 | autoload :StartInstance, action_root.join("start_instance") 189 | autoload :StopInstance, action_root.join("stop_instance") 190 | autoload :TerminateInstance, action_root.join("terminate_instance") 191 | autoload :TimedProvision, action_root.join("timed_provision") # some plugins now expect this action to exist 192 | autoload :WaitForState, action_root.join("wait_for_state") 193 | end 194 | end 195 | end 196 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/boot.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require 'json' 3 | require 'securerandom' 4 | 5 | require 'vagrant/util/retryable' 6 | 7 | require 'vagrant-xhyve/util/timer' 8 | require 'vagrant-xhyve/util/vagrant-xhyve' 9 | 10 | module VagrantPlugins 11 | module XHYVE 12 | module Action 13 | # This runs the configured instance. 14 | class Boot 15 | include Vagrant::Util::Retryable 16 | 17 | def initialize(app, env) 18 | @app = app 19 | end 20 | 21 | def call(env) 22 | env[:ui].info(" About to launch vm...") 23 | # Initialize metrics if they haven't been 24 | env[:metrics] ||= {} 25 | 26 | machine_info_path = File.join(env[:machine].data_dir, "xhyve.json") 27 | if File.exist?(machine_info_path) 28 | machine_json = File.read(machine_info_path) 29 | machine_options = JSON.parse(machine_json, :symbolize_names => true) 30 | log.debug "Machine Options: #{JSON.pretty_generate(machine_options)}" 31 | machine_uuid = machine_options[:uuid] 32 | pid = machine_options[:pid] 33 | mac = machine_options[:mac] 34 | else 35 | machine_uuid = SecureRandom.uuid 36 | end 37 | 38 | guest_config = { 39 | pid: pid, 40 | mac: mac, 41 | uuid: machine_uuid, 42 | cmdline: kernel_command(env), 43 | memory: memory(env), 44 | processors: cpus(env), 45 | binary: xhyve_binary(env), 46 | } 47 | 48 | xhyve_guest = start_guest(env, guest_config) 49 | # Immediately save the ID since it is created at this point. 50 | env[:machine].id = xhyve_guest.uuid 51 | 52 | save_guest_status(env, xhyve_guest) 53 | # Terminate the instance if we were interrupted 54 | terminate(env) if env[:interrupted] 55 | 56 | @app.call(env) 57 | end 58 | 59 | def recover(env) 60 | return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) 61 | 62 | if env[:machine].provider.state.id != :not_created 63 | # Undo the import 64 | terminate(env) 65 | end 66 | end 67 | 68 | def terminate(env) 69 | destroy_env = env.dup 70 | destroy_env.delete(:interrupted) 71 | destroy_env[:config_validate] = false 72 | destroy_env[:force_confirm_destroy] = true 73 | env[:action_runner].run(Action.action_destroy, destroy_env) 74 | end 75 | 76 | private 77 | 78 | def block_device_paths(base_path) 79 | block_paths = Dir.glob File.join(base_path, "block*.{raw,img,qcow,qcow2}") 80 | block_paths.sort 81 | end 82 | 83 | def kernel_file_path(base_path) 84 | File.join(base_path, "vmlinuz") 85 | end 86 | 87 | def initrd_file_path(base_path) 88 | File.join(base_path, "initrd.gz") 89 | end 90 | 91 | def memory(env) 92 | provider_config(env).memory 93 | end 94 | 95 | def cpus(env) 96 | provider_config(env).cpus 97 | end 98 | 99 | def xhyve_binary(env) 100 | provider_config(env).xhyve_binary 101 | end 102 | 103 | def kernel_command(env) 104 | provider_config(env).kernel_command 105 | end 106 | 107 | def provider_config(env) 108 | @provider_config ||= env[:machine].provider_config 109 | end 110 | 111 | def start_guest(env, config = {}) 112 | image_dir = File.join(env[:machine].data_dir, "image") 113 | default_config = { 114 | kernel: kernel_file_path(image_dir), 115 | initrd: initrd_file_path(image_dir), 116 | blockdevs: block_device_paths(image_dir), 117 | serial: 'com1', 118 | networking: true, 119 | acpi: true 120 | } 121 | config = default_config.merge(config) 122 | log.debug "xhyve configuration: #{JSON.pretty_generate(config)}" 123 | xhyve_guest = Util::XhyveGuest.new config 124 | xhyve_guest.start 125 | xhyve_guest 126 | end 127 | 128 | def save_guest_status(env, guest) 129 | wait_for_guest_ip(env, guest) 130 | machine_info_path = File.join(env[:machine].data_dir, "xhyve.json") 131 | log.debug "xhyve configuration: #{JSON.pretty_generate(guest.options)}" 132 | File.write(machine_info_path, guest.options().to_json) 133 | log.info(" Launched xhyve VM with PID #{guest.pid}, MAC: #{guest.mac}, and IP #{guest.ip}") 134 | end 135 | 136 | def wait_for_guest_ip(env, guest) 137 | network_ready_retries = 0 138 | network_ready_retries_max = 3 139 | update_xhyve_status(env, guest.options) 140 | while guest.ip.nil? 141 | break if env[:interrupted] 142 | 143 | if network_ready_retries < network_ready_retries_max then 144 | network_ready_retries += 1 145 | env[:ui].info("Waiting for IP to be ready. Try #{network_ready_retries}/#{network_ready_retries_max}...") 146 | else 147 | raise 'Waited too long for IP to be ready. Your VM probably did not boot.' 148 | end 149 | sleep 0.5 150 | end 151 | update_xhyve_status(env, guest.options) 152 | end 153 | 154 | def update_xhyve_status(env, status) 155 | env[:xhyve_status] = status 156 | end 157 | 158 | def log 159 | @logger ||= Log4r::Logger.new("vagrant_xhyve::action::boot") 160 | end 161 | end 162 | end 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/import.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require "json" 3 | require "fileutils" 4 | 5 | module VagrantPlugins 6 | module XHYVE 7 | module Action 8 | # This terminates the running instance. 9 | class Import 10 | def initialize(app, env) 11 | @app = app 12 | end 13 | 14 | def call(env) 15 | 16 | #TODO: Progress bar 17 | env[:ui].info("Importing box...") 18 | 19 | image_dir = File.join(env[:machine].data_dir, "image") 20 | box_dir = env[:machine].box.directory 21 | 22 | log.debug("Importing box from: #{box_dir}") 23 | 24 | create_image_directory(image_dir) 25 | copy_kernel_to(box_dir, image_dir) 26 | copy_initrd_to(box_dir, image_dir) 27 | copy_block_files_to(box_dir, image_dir) 28 | env[:ui].info("Done importing box.") 29 | 30 | @app.call(env) 31 | end 32 | 33 | private 34 | 35 | def copy_kernel_to(from, to) 36 | copy_file(from, to, "vmlinuz") 37 | end 38 | 39 | def copy_initrd_to(from, to) 40 | copy_file(from, to, "initrd.gz") 41 | end 42 | 43 | def copy_block_files_to(from, to) 44 | block_glob = Dir.glob(File.join(from, "block*.{img,raw,qcow,qcow2}")) 45 | log.debug("Copying #{block_glob} to #{to} ") 46 | FileUtils.cp_r block_glob, to 47 | end 48 | 49 | def copy_file(from, to, filename) 50 | from_box_file_path = File.join(from, filename) 51 | to_image_file_path = File.join(to, filename) 52 | 53 | unless File.exist? to_image_file_path 54 | log.debug("Copying #{from_box_file_path} to #{to} ") 55 | FileUtils.cp(from_box_file_path, to) 56 | end 57 | end 58 | 59 | def create_image_directory(path) 60 | FileUtils.mkdir_p(path) 61 | end 62 | 63 | def log 64 | @logger ||= Log4r::Logger.new("vagrant_xhyve::action::import") 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/is_created.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Action 4 | # This can be used with "Call" built-in to check if the machine 5 | # is created and branch in the middleware. 6 | class IsCreated 7 | def initialize(app, env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | env[:result] = env[:machine].id != nil 13 | @app.call(env) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/is_stopped.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Action 4 | # This can be used with "Call" built-in to check if the machine 5 | # is stopped and branch in the middleware. 6 | class IsStopped 7 | def initialize(app, env) 8 | @app = app 9 | end 10 | 11 | def call(env) 12 | env[:result] = env[:machine].state.id == :stopped 13 | @app.call(env) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/message_already_created.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Action 4 | class MessageAlreadyCreated 5 | def initialize(app, env) 6 | @app = app 7 | end 8 | 9 | def call(env) 10 | env[:ui].info(I18n.t("vagrant_xhyve.already_status", :status => "created")) 11 | @app.call(env) 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/message_not_created.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Action 4 | class MessageNotCreated 5 | def initialize(app, env) 6 | @app = app 7 | end 8 | 9 | def call(env) 10 | env[:ui].info(I18n.t("vagrant_xhyve.not_created")) 11 | @app.call(env) 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/message_will_not_destroy.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Action 4 | class MessageWillNotDestroy 5 | def initialize(app, env) 6 | @app = app 7 | end 8 | 9 | def call(env) 10 | env[:ui].info(I18n.t("vagrant_xhyve.will_not_destroy", name: env[:machine].name)) 11 | @app.call(env) 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/read_ssh_info.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require 'vagrant-xhyve/util/vagrant-xhyve' 3 | 4 | module VagrantPlugins 5 | module XHYVE 6 | module Action 7 | # This action reads the SSH info for the machine and puts it into the 8 | # `:machine_ssh_info` key in the environment. 9 | class ReadSSHInfo 10 | def initialize(app, env) 11 | @app = app 12 | @logger = Log4r::Logger.new("vagrant_xhyve::action::read_ssh_info") 13 | end 14 | 15 | def call(env) 16 | env[:machine_ssh_info] = read_ssh_info(env) 17 | @app.call(env) 18 | end 19 | 20 | def read_ssh_info(env) 21 | xhyve_status = env[:xhyve_status] 22 | return { :host => xhyve_status[:ip], :port => 22 } 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/read_state.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | 3 | module VagrantPlugins 4 | module XHYVE 5 | module Action 6 | # This action reads the state of the machine and puts it in the 7 | # `:machine_state_id` key in the environment. 8 | class ReadState 9 | def initialize(app, env) 10 | @app = app 11 | @env = env 12 | @logger = Log4r::Logger.new("vagrant_xhyve::action::read_state") 13 | end 14 | 15 | def call(env) 16 | 17 | env[:machine_state_id] = read_state(env) 18 | @app.call(env) 19 | end 20 | 21 | private 22 | 23 | def read_state(env) 24 | xhyve_status = read_xhyve_status_file(env) 25 | return :not_created if env[:machine].id.nil? 26 | env[:xhyve_status] = xhyve_status 27 | if process_alive(xhyve_status[:pid]) 28 | return :running 29 | else 30 | return :stopped 31 | end 32 | end 33 | 34 | def read_xhyve_status_file(env) 35 | xhyve_status_file_path = File.join(env[:machine].data_dir, "xhyve.json") 36 | return {} unless File.exist?(xhyve_status_file_path) 37 | machine_json = File.read(xhyve_status_file_path) 38 | JSON.parse(machine_json, :symbolize_names => true) 39 | end 40 | 41 | def process_alive(pid) 42 | return false if pid.nil? 43 | begin 44 | Process.getpgid(pid.to_i) 45 | true 46 | rescue Errno::ESRCH 47 | false 48 | end 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/stop_instance.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | 3 | module VagrantPlugins 4 | module XHYVE 5 | module Action 6 | # This stops the running instance. 7 | class StopInstance 8 | def initialize(app, env) 9 | @app = app 10 | @logger = Log4r::Logger.new("vagrant_xhyve::action::stop_instance") 11 | end 12 | 13 | def call(env) 14 | if is_process_alive? pid(env) 15 | env[:ui].info(I18n.t("vagrant_xhyve.stopping")) 16 | kill_xhyve_process(env) 17 | else 18 | env[:ui].info(I18n.t("vagrant_xhyve.already_status", status: env[:machine].state.id)) 19 | end 20 | destroy_xhyve_status_file(env) 21 | @app.call(env) 22 | end 23 | 24 | def is_process_alive?(pid) 25 | return false if pid == 0 26 | begin 27 | Process.getpgid(pid) 28 | true 29 | rescue Errno::ESRCH 30 | false 31 | end 32 | end 33 | 34 | def kill_xhyve_process(env) 35 | Process.kill(3, pid(env)) 36 | end 37 | 38 | def destroy_xhyve_status_file(env) 39 | xhyve_status_file_path = File.join(env[:machine].data_dir, "xhyve.json") 40 | FileUtils.remove_file(xhyve_status_file_path, force: true) 41 | end 42 | 43 | def pid(env) 44 | @pid ||= env[:xhyve_status][:pid].to_i 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/terminate_instance.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require "json" 3 | require "fileutils" 4 | 5 | module VagrantPlugins 6 | module XHYVE 7 | module Action 8 | # This terminates the running instance. 9 | class TerminateInstance 10 | def initialize(app, env) 11 | @app = app 12 | @logger = Log4r::Logger.new("vagrant_xhyve::action::terminate_instance") 13 | end 14 | 15 | def call(env) 16 | stop_instance(env) 17 | # Remove the tracking ID 18 | env[:ui].info(I18n.t("vagrant_xhyve.terminating")) 19 | FileUtils.rm_rf(env[:machine].data_dir) 20 | 21 | @app.call(env) 22 | end 23 | 24 | def stop_instance(env) 25 | halt_env = env.dup 26 | env[:action_runner].run(Action.action_halt, halt_env) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/timed_provision.rb: -------------------------------------------------------------------------------- 1 | require "vagrant-xhyve/util/timer" 2 | 3 | module VagrantPlugins 4 | module XHYVE 5 | module Action 6 | # This is the same as the builtin provision except it times the 7 | # provisioner runs. 8 | class TimedProvision < Vagrant::Action::Builtin::Provision 9 | def run_provisioner(env, name, p) 10 | timer = Util::Timer.time do 11 | super 12 | end 13 | 14 | env[:metrics] ||= {} 15 | env[:metrics]["provisioner_times"] ||= [] 16 | env[:metrics]["provisioner_times"] << [name, timer] 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/action/wait_for_state.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require "timeout" 3 | 4 | module VagrantPlugins 5 | module XHYVE 6 | module Action 7 | # This action will wait for a machine to reach a specific state or quit by timeout 8 | class WaitForState 9 | # env[:result] will be false in case of timeout. 10 | # @param [Symbol] state Target machine state. 11 | # @param [Number] timeout Timeout in seconds. 12 | def initialize(app, env, state, timeout) 13 | @app = app 14 | @logger = Log4r::Logger.new("vagrant_xhyve::action::wait_for_state") 15 | @state = state 16 | @timeout = timeout 17 | end 18 | 19 | def call(env) 20 | env[:result] = true 21 | if env[:machine].state.id == @state 22 | @logger.info(I18n.t("vagrant_xhyve.already_status", :status => @state)) 23 | else 24 | @logger.info("Waiting for machine to reach state #{@state}") 25 | begin 26 | Timeout.timeout(@timeout) do 27 | until env[:machine].state.id == @state 28 | sleep 2 29 | end 30 | end 31 | rescue Timeout::Error 32 | env[:result] = false # couldn't reach state in time 33 | end 34 | end 35 | 36 | @app.call(env) 37 | end 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/config.rb: -------------------------------------------------------------------------------- 1 | require "vagrant" 2 | require "iniparse" 3 | 4 | module VagrantPlugins 5 | module XHYVE 6 | class Config < Vagrant.plugin("2", :config) 7 | 8 | 9 | # The number of CPUs to give the VM 10 | # 11 | # @return [Fixnum] 12 | attr_accessor :cpus 13 | 14 | # The amount of memory to give the VM 15 | # 16 | # This can just be a simple integer for memory in MB 17 | # or you can use the suffixed style, eg. 2G for two 18 | # Gigabytes 19 | # 20 | # @return [String] 21 | attr_accessor :memory 22 | 23 | 24 | # The path to the xhyve binary if you don't want to 25 | # use the one bundled with xhyve-ruby 26 | # 27 | # @return [String] 28 | attr_accessor :xhyve_binary 29 | 30 | # The mac address of the VM 31 | # 32 | # @return [String] 33 | attr_accessor :mac 34 | 35 | # The uuid of the VM 36 | # 37 | # @return [String] 38 | attr_accessor :uuid 39 | 40 | # The kernel command to initialize the machine 41 | # The default command assumes that the instalation is done directly on the 42 | # disk and not using a lvm. For example, by default, Centos intallation uses 43 | # LVM. Moreover, it might be interesting to add new kernel options. 44 | # @return [String] 45 | attr_accessor :kernel_command 46 | 47 | def initialize(region_specific=false) 48 | @cpus = UNSET_VALUE 49 | @memory = UNSET_VALUE 50 | @xhyve_binary = UNSET_VALUE 51 | @mac = UNSET_VALUE 52 | @uuid = UNSET_VALUE 53 | @kernel_command = UNSET_VALUE 54 | 55 | # Internal state (prefix with __ so they aren't automatically 56 | # merged) 57 | @__compiled_region_configs = {} 58 | @__finalized = false 59 | @__region_config = {} 60 | @__region_specific = region_specific 61 | end 62 | 63 | #------------------------------------------------------------------- 64 | # Internal methods. 65 | #------------------------------------------------------------------- 66 | 67 | def finalize! 68 | 69 | @cpus = 1 if @cpus == UNSET_VALUE 70 | @memory = 1024 if @memory == UNSET_VALUE 71 | @xhyve_binary = nil if @xhyve_binary == UNSET_VALUE 72 | @kernel_command = %Q{"earlyprintk=serial console=ttyS0 root=/dev/vda1 ro"} if @kernel_command == UNSET_VALUE 73 | # Mark that we finalized 74 | @__finalized = true 75 | end 76 | 77 | def validate(machine) 78 | errors = _detected_errors 79 | { "XHYVE Provider" => errors } 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/errors.rb: -------------------------------------------------------------------------------- 1 | require "vagrant" 2 | 3 | module VagrantPlugins 4 | module XHYVE 5 | module Errors 6 | class VagrantXHYVEError < Vagrant::Errors::VagrantError 7 | error_namespace("vagrant_xhyve.errors") 8 | end 9 | 10 | class RsyncError < VagrantXYHVEError 11 | error_key(:rsync_error) 12 | end 13 | 14 | class MkdirError < VagrantXHYVEError 15 | error_key(:mkdir_error) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/plugin.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require "vagrant" 3 | rescue LoadError 4 | raise "The Vagrant XHYVE plugin must be run within Vagrant." 5 | end 6 | 7 | # This is a sanity check to make sure no one is attempting to install 8 | # this into an early Vagrant version. 9 | if Vagrant::VERSION < "1.2.0" 10 | raise "The Vagrant XHYVE plugin is only compatible with Vagrant 1.2+" 11 | end 12 | 13 | module VagrantPlugins 14 | module XHYVE 15 | class Plugin < Vagrant.plugin("2") 16 | name "XHYVE" 17 | description <<-DESC 18 | This plugin installs a provider that allows Vagrant to manage 19 | machines in XHYVE. 20 | DESC 21 | 22 | config(:xhyve, :provider) do 23 | require_relative "config" 24 | Config 25 | end 26 | 27 | provider(:xhyve) do 28 | # Setup logging and i18n 29 | setup_logging 30 | setup_i18n 31 | 32 | # Return the provider 33 | require_relative "provider" 34 | Provider 35 | end 36 | 37 | # This initializes the internationalization strings. 38 | def self.setup_i18n 39 | I18n.load_path << File.expand_path("locales/en.yml", XHYVE.source_root) 40 | I18n.reload! 41 | end 42 | 43 | # This sets up our log level to be whatever VAGRANT_LOG is. 44 | def self.setup_logging 45 | require "log4r" 46 | 47 | level = nil 48 | begin 49 | level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase) 50 | rescue NameError 51 | # This means that the logging constant wasn't found, 52 | # which is fine. We just keep `level` as `nil`. But 53 | # we tell the user. 54 | level = nil 55 | end 56 | 57 | # Some constants, such as "true" resolve to booleans, so the 58 | # above error checking doesn't catch it. This will check to make 59 | # sure that the log level is an integer, as Log4r requires. 60 | level = nil if !level.is_a?(Integer) 61 | 62 | # Set the logging level on all "vagrant" namespaced 63 | # logs as long as we have a valid level. 64 | if level 65 | logger = Log4r::Logger.new("vagrant_xhyve") 66 | logger.outputters = Log4r::Outputter.stderr 67 | logger.level = level 68 | logger = nil 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/provider.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | require "vagrant" 3 | 4 | module VagrantPlugins 5 | module XHYVE 6 | class Provider < Vagrant.plugin("2", :provider) 7 | def initialize(machine) 8 | @machine = machine 9 | end 10 | 11 | def action(name) 12 | # Attempt to get the action method from the Action class if it 13 | # exists, otherwise return nil to show that we don't support the 14 | # given action. 15 | action_method = "action_#{name}" 16 | return Action.send(action_method) if Action.respond_to?(action_method) 17 | nil 18 | end 19 | 20 | def ssh_info 21 | # Run a custom action called "read_ssh_info" which does what it 22 | # says and puts the resulting SSH info into the `:machine_ssh_info` 23 | # key in the environment. 24 | env = @machine.action("read_ssh_info") 25 | env[:machine_ssh_info] 26 | end 27 | 28 | def state 29 | # Run a custom action we define called "read_state" which does 30 | # what it says. It puts the state in the `:machine_state_id` 31 | # key in the environment. 32 | env = @machine.action("read_state") 33 | 34 | state_id = env[:machine_state_id] 35 | 36 | # Get the short and long description 37 | short = I18n.t("vagrant_xhyve.states.short_#{state_id}") 38 | long = I18n.t("vagrant_xhyve.states.long_#{state_id}") 39 | 40 | # Return the MachineState object 41 | Vagrant::MachineState.new(state_id, short, long) 42 | end 43 | 44 | def to_s 45 | id = @machine.id.nil? ? "new" : @machine.id 46 | "XHYVE (#{id})" 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/util/timer.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | module Util 4 | class Timer 5 | # A basic utility method that times the execution of the given 6 | # block and returns it. 7 | def self.time 8 | start_time = Time.now.to_f 9 | yield 10 | end_time = Time.now.to_f 11 | 12 | end_time - start_time 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/util/vagrant-xhyve.rb: -------------------------------------------------------------------------------- 1 | require 'xhyve' 2 | 3 | module VagrantPlugins 4 | module XHYVE 5 | module Util 6 | # TODO: send all this upstream 7 | class XhyveGuest < Xhyve::Guest 8 | 9 | def initialize(**opts) 10 | super.tap do |s| 11 | @pid = opts.fetch(:pid, nil) 12 | @mac = opts[:mac] unless opts[:mac].nil? 13 | end 14 | end 15 | 16 | def start 17 | return @pid if running? 18 | super 19 | end 20 | 21 | def options 22 | { 23 | :pid => @pid, 24 | :kernel => @kernel, 25 | :initrd => @initrd, 26 | :cmdline => @cmdline, 27 | :blockdevs => @blockdevs, 28 | :memory => @memory, 29 | :processors => @processors, 30 | :uuid => @uuid, 31 | :serial => @serial, 32 | :acpi => @acpi, 33 | :networking => @networking, 34 | :foreground => @foreground, 35 | :command => @command, 36 | :mac => @mac, 37 | :ip => ip, 38 | :binary => @binary 39 | } 40 | end 41 | 42 | def build_command 43 | [ 44 | "#{@binary}", 45 | "#{'-A' if @acpi}", 46 | '-U', @uuid, 47 | '-m', @memory, 48 | '-c', @processors, 49 | '-s', '0:0,hostbridge', 50 | "#{"-s #{PCI_BASE - 1}:0,virtio-net" if @networking }" , 51 | "#{build_block_device_parameter}", 52 | '-s', '31,lpc', 53 | '-l', "#{@serial},stdio", 54 | '-f', "kexec,#{@kernel},#{@initrd},'#{@cmdline}'" 55 | ].join(' ') 56 | end 57 | 58 | def build_block_device_parameter 59 | block_device_parameter = "" 60 | @blockdevs.each_with_index.map do |p, i| 61 | if p.include? "qcow" 62 | block_device_parameter << "-s #{PCI_BASE + i},virtio-blk,file://#{p},format\=qcow " 63 | else 64 | block_device_parameter << "-s #{PCI_BASE + i},virtio-blk,#{p} " 65 | end 66 | end 67 | block_device_parameter 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/vagrant-xhyve/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module XHYVE 3 | VERSION = '0.4.1' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | vagrant_xhyve: 3 | already_status: |- 4 | The machine is already %{status}. 5 | launching_instance: |- 6 | Launching an instance with the following settings... 7 | not_created: |- 8 | Instance is not created. Please run `vagrant up` first. 9 | ready: |- 10 | Machine is booted and ready for use! 11 | rsync_not_found_warning: |- 12 | Warning! Folder sync disabled because the rsync binary is missing in the %{side}. 13 | Make sure rsync is installed and the binary can be found in the PATH. 14 | rsync_folder: |- 15 | Rsyncing folder: %{hostpath} => %{guestpath} 16 | source_dest_checks_no_vpc: |- 17 | Warning! Ignoring source_dest_checks flag as it can only be configured on 18 | a VPC instance. 19 | starting: |- 20 | Starting the instance... 21 | stopping: |- 22 | Stopping the instance... 23 | terminating: |- 24 | Terminating the instance... 25 | waiting_for_ready: |- 26 | Waiting for instance to become "ready"... 27 | waiting_for_ssh: |- 28 | Waiting for SSH to become available... 29 | warn_ssh_access: |- 30 | Warning! Vagrant might not be able to SSH into the instance. 31 | Please check your security groups settings. 32 | will_not_destroy: |- 33 | The instance '%{name}' will not be destroyed, since the confirmation 34 | was declined. 35 | 36 | errors: 37 | rsync_error: |- 38 | There was an error when attempting to rsync a shared folder. 39 | Please inspect the error message below for more info. 40 | 41 | Host path: %{hostpath} 42 | Guest path: %{guestpath} 43 | Error: %{stderr} 44 | mkdir_error: |- 45 | There was an error when attempting to create a shared host folder. 46 | Please inspect the error message below for more info. 47 | 48 | Host path: %{hostpath} 49 | Error: %{err} 50 | 51 | states: 52 | short_not_created: |- 53 | not created 54 | long_not_created: |- 55 | The xhyve VM is not created. Run `vagrant up` to create it. 56 | 57 | short_stopped: |- 58 | stopped 59 | long_stopped: |- 60 | The xhyve VM is stopped. Run `vagrant up` to start it. 61 | 62 | short_stopping: |- 63 | stopping 64 | long_stopping: |- 65 | The xhyve VM is stopping. Wait until is completely stopped to 66 | run `vagrant up` and start it. 67 | 68 | short_pending: |- 69 | pending 70 | long_pending: |- 71 | The xhyve VM is pending a start (i.e. this is a transition state). 72 | 73 | short_running: |- 74 | running 75 | long_running: |- 76 | The xhyve VM is running. To stop this machine, you can run 77 | `vagrant halt`. To destroy the machine, you can run `vagrant destroy`. 78 | 79 | short_pending: |- 80 | pending 81 | long_pending: |- 82 | The xhyve VM is still being initialized. To destroy this machine, 83 | you can run `vagrant destroy`. 84 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/vagrant-xhyve/config_spec.rb: -------------------------------------------------------------------------------- 1 | require "vagrant-xhyve/config" 2 | require 'rspec/its' 3 | 4 | # remove deprecation warnings 5 | # (until someone decides to update the whole spec file to rspec 3.4) 6 | RSpec.configure do |config| 7 | # ... 8 | config.mock_with :rspec do |c| 9 | c.syntax = [:should, :expect] 10 | end 11 | config.expect_with :rspec do |c| 12 | c.syntax = [:should, :expect] 13 | end 14 | end 15 | 16 | describe VagrantPlugins::XHYVE::Config do 17 | let(:instance) { described_class.new } 18 | 19 | before :each do 20 | ENV.stub(:[] => nil) 21 | end 22 | 23 | describe "defaults" do 24 | subject do 25 | instance.tap do |o| 26 | o.finalize! 27 | end 28 | end 29 | 30 | its("memory") { should.to_s == "1024" } 31 | its("cpus") { should == 1 } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /templates/metadata.json.erb: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "aws" 3 | } 4 | -------------------------------------------------------------------------------- /templates/vagrant-aws_package_Vagrantfile.erb: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.provider "aws" do |aws| 3 | aws.region_config "<%= region %>", ami: "<%= ami %>" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /vagrant-xhyve.gemspec: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../lib", __FILE__) 2 | require "vagrant-xhyve/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "vagrant-xhyve" 6 | s.version = VagrantPlugins::XHYVE::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.license = "MIT" 9 | s.authors = "Patrick Armstrong" 10 | s.email = "pat@oldpatricka.com" 11 | s.homepage = "http://github.com/oldpatricka/vagrant-xhyve" 12 | s.summary = "Enables Vagrant to manage machines in xhyve." 13 | s.description = "Enables Vagrant to manage machines in xhyve." 14 | 15 | s.required_rubygems_version = ">= 1.3.6" 16 | s.rubyforge_project = "vagrant-xhyve" 17 | 18 | s.add_runtime_dependency "xhyve-ruby", ">= 0.0.6" 19 | s.add_runtime_dependency "iniparse", "~> 1.4", ">= 1.4.2" 20 | 21 | s.add_development_dependency "rake" 22 | # rspec 3.4 to mock File 23 | s.add_development_dependency "rspec", "~> 3.4" 24 | s.add_development_dependency "rspec-its" 25 | 26 | # The following block of code determines the files that should be included 27 | # in the gem. It does this by reading all the files in the directory where 28 | # this gemspec is, and parsing out the ignored files from the gitignore. 29 | # Note that the entire gitignore(5) syntax is not supported, specifically 30 | # the "!" syntax, but it should mostly work correctly. 31 | root_path = File.dirname(__FILE__) 32 | all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") } 33 | all_files.reject! { |file| [".", ".."].include?(File.basename(file)) } 34 | gitignore_path = File.join(root_path, ".gitignore") 35 | gitignore = File.readlines(gitignore_path) 36 | gitignore.map! { |line| line.chomp.strip } 37 | gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ } 38 | 39 | unignored_files = all_files.reject do |file| 40 | # Ignore any directories, the gemspec only cares about files 41 | next true if File.directory?(file) 42 | 43 | # Ignore any paths that match anything in the gitignore. We do 44 | # two tests here: 45 | # 46 | # - First, test to see if the entire path matches the gitignore. 47 | # - Second, match if the basename does, this makes it so that things 48 | # like '.DS_Store' will match sub-directories too (same behavior 49 | # as git). 50 | # 51 | gitignore.any? do |ignore| 52 | File.fnmatch(ignore, file, File::FNM_PATHNAME) || 53 | File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME) 54 | end 55 | end 56 | 57 | s.files = unignored_files 58 | s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact 59 | s.require_path = 'lib' 60 | end 61 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | mkmf.log 3 | Makefile 4 | coverage 5 | lib/xhyve/vmnet/vmnet.bundle 6 | *.gem 7 | *.swo 8 | *.swp 9 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode7.1 3 | rvm: 4 | - 2.1.5 5 | install: 6 | - sudo bundle install 7 | - sudo bundle exec rake install 8 | script: sudo bundle exec rake 9 | env: 10 | - TRAVIS=true 11 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/README.md: -------------------------------------------------------------------------------- 1 | ![](https://travis-ci.org/dalehamel/xhyve-ruby.svg) 2 | 3 | # Ruby Xhyve 4 | 5 | This is a simple ruby-wrapper around [xhyve](https://github.com/mist64/xhyve), allowing you to start hypervisor Guests on OS-X 6 | 7 | # Usage 8 | 9 | You can run a guest fairly easily: 10 | 11 | ``` 12 | require 'xhyve' 13 | 14 | guest = Xhyve::Guest.new( 15 | kernel: 'guest/vmlinuz', # path to vmlinuz 16 | initrd: 'guest/initrd', # path to initrd 17 | cmdline: 'console=tty0', # boot flags to linux 18 | blockdevs: 'loop.img', # path to img files to use as block devs 19 | uuid: 'a-valid-uuid', # a valid UUID 20 | serial: 'com2', # com1 / com2 (maps to ttyS0, ttyS1, etc) 21 | memory: '200M', # amount of memory in M/G 22 | processors: 1, # number of processors 23 | networking: true, # Enable networking? (requires sudo) 24 | acpi: true, # set up acpi? (required for clean shutdown) 25 | ) 26 | 27 | pid = guest.start # starting the guest spawns an xhyve subprocess, returning the pid 28 | guest.running? # is the guest running? 29 | guest.ip # get the IP of the guest 30 | guest.mac # get MAC address of the guest 31 | guest.stop # stop the guest 32 | guest.destroy # forcefully stop the guest 33 | ``` 34 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/extensiontask' 2 | require 'rspec/core/rake_task' 3 | require 'fileutils' 4 | 5 | XHYVE_TMP = 'tmp/xhyve' 6 | 7 | # Compile native extensions task 8 | Rake::ExtensionTask.new 'vmnet' do |ext| 9 | ext.lib_dir = 'lib/xhyve/vmnet' 10 | end 11 | 12 | # Spec test 13 | RSpec::Core::RakeTask.new(:spec) 14 | 15 | desc 'Build xhyve binary' 16 | task :vendor do 17 | Dir.chdir('tmp') do 18 | unless Dir.exist?('xhyve/.git') 19 | system('git clone https://github.com/mist64/xhyve.git') || fail('Could not clone xhyve') 20 | end 21 | Dir.chdir('xhyve') do 22 | system('git fetch') || fail('Could not fetch') 23 | system('git reset --hard origin/master') || fail('Could not reset head') 24 | system('make') || fail('Make failed') 25 | end 26 | end 27 | FileUtils.mkdir_p('lib/xhyve/vendor') 28 | FileUtils.cp('tmp/xhyve/build/xhyve', 'lib/xhyve/vendor') 29 | end 30 | 31 | desc 'Build the ruby gem' 32 | task :build do 33 | system('gem build xhyve-ruby.gemspec') || fail('Failed to build gem') 34 | end 35 | 36 | desc 'Install gem' 37 | task install: :build do 38 | system('gem install xhyve-ruby*.gem') || fail('Couldn not install gem') 39 | end 40 | 41 | # Deps and defaults 42 | task default: :spec 43 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/example/test.rb: -------------------------------------------------------------------------------- 1 | require 'xhyve' 2 | 3 | guest = Xhyve::Guest.new( 4 | kernel: 'spec/fixtures/guest/vmlinuz', # path to vmlinuz 5 | initrd: 'spec/fixtures/guest/initrd', # path to initrd 6 | cmdline: 'earlyprintk=true console=ttyS0', # boot flags to linux 7 | serial: 'com1', # com1 / com2 (maps to ttyS0, ttyS1, etc) 8 | memory: '200M', # amount of memory in M/G 9 | processors: 1, # number of processors 10 | networking: true, # use sudo? (required for network unless signed) 11 | acpi: true, # set up acpi? (required for clean shutdown) 12 | ) 13 | 14 | pid = guest.start # starting the guest spawns an xhyve subprocess, returning the pid 15 | puts pid 16 | puts guest.mac # get MAC address of the guest 17 | puts guest.ip # get the IP of the guest 18 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/rubygems_plugin.rb: -------------------------------------------------------------------------------- 1 | Gem.post_install do 2 | if Gem::Platform.local.os =~ /darwin/ 3 | # Required until https://github.com/mist64/xhyve/issues/60 is resolved 4 | bin = File.expand_path('../xhyve/vendor/xhyve', __FILE__) 5 | `/usr/bin/osascript -e 'do shell script "chown root #{bin} && chmod +s #{bin}" with administrator privileges'` 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/xhyve.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__)) 2 | 3 | require 'xhyve/version' 4 | require 'xhyve/guest' 5 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/xhyve/dhcp.rb: -------------------------------------------------------------------------------- 1 | module Xhyve 2 | # Parse DHCP leases file for a MAC address, and get its ip. 3 | module DHCP 4 | extend self 5 | LEASES_FILE = '/var/db/dhcpd_leases' 6 | WAIT_TIME = 1 7 | MAX_ATTEMPTS = 60 8 | 9 | def get_ip_for_mac(mac) 10 | normalized_mac = normalize_mac(mac) 11 | 12 | max = ENV.key?('MAX_IP_WAIT') ? ENV['MAX_IP_WAIT'].to_i : nil 13 | ip = wait_for(max: max) do 14 | ip = parse_lease_file_for_mac(normalized_mac) 15 | end 16 | end 17 | 18 | def parse_lease_file_for_mac(mac) 19 | lease_file = (ENV['LEASES_FILE'] || LEASES_FILE) 20 | contents = wait_for do 21 | File.read(lease_file) if File.exists?(lease_file) 22 | end 23 | pattern = contents.match(/ip_address=(\S+)\n\thw_address=\d+,#{mac}/) 24 | if pattern 25 | addrs = pattern.captures 26 | addrs.first if addrs 27 | end 28 | end 29 | 30 | private 31 | 32 | def wait_for(max: nil) 33 | attempts = 0 34 | max ||= MAX_ATTEMPTS 35 | while attempts < max 36 | attempts += 1 37 | result = yield 38 | return result if result 39 | sleep(WAIT_TIME) 40 | end 41 | end 42 | 43 | # macos dhcp represents mac addresses differently from xhyve. Specifically, 44 | # it doesn't display leading zeros. This function normalized the mac 45 | # address to the macos format 46 | def normalize_mac(mac) 47 | # don't try to normalize if it doesn't seem like a mac... 48 | return mac if mac !~ /.*:.*:.*:.*:.*:.*/ 49 | mac_parts = mac.to_s.split(":") 50 | normalized_parts = mac_parts.map {|s| Integer(s, 16).to_s(16) } 51 | normalized_parts.join(":") 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/xhyve/guest.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | require 'io/console' 3 | 4 | require 'xhyve/dhcp' 5 | 6 | module Xhyve 7 | BINARY_PATH = File.expand_path('../../../lib/xhyve/vendor/xhyve', __FILE__).freeze 8 | 9 | # An object to represent a guest that we can start and stop 10 | # Effectively, it's a command wrapper around xhyve to provide an 11 | # object oriented interface to a hypervisor guest 12 | class Guest 13 | PCI_BASE = 3 14 | NULLDEV = '/dev/null' 15 | 16 | attr_reader :pid, :uuid, :mac 17 | 18 | def initialize(**opts) 19 | @kernel = opts.fetch(:kernel) 20 | @initrd = opts.fetch(:initrd) 21 | @cmdline = opts.fetch(:cmdline) 22 | @blockdevs = [opts[:blockdevs] || []].flatten 23 | @memory = opts[:memory] || '500M' 24 | @processors = opts[:processors] || '1' 25 | @uuid = opts[:uuid] || SecureRandom.uuid 26 | @serial = opts[:serial] || 'com1' 27 | @acpi = opts.fetch(:acpi, true) 28 | @networking = opts.fetch(:networking, true) 29 | @foreground = opts[:foreground] || false 30 | @binary = opts[:binary] || BINARY_PATH 31 | @command = build_command 32 | @mac = find_mac 33 | end 34 | 35 | def start 36 | outfile, errfile, infile = redirection 37 | @pid = spawn(@command, out: outfile, err: errfile, in: infile) 38 | if @foreground 39 | Process.wait(@pid) 40 | outfile.cooked! 41 | infile.cooked! 42 | end 43 | @pid 44 | end 45 | 46 | def stop 47 | Process.kill('KILL', @pid) 48 | end 49 | 50 | def running? 51 | (true if Process.kill(0, @pid) rescue false) 52 | end 53 | 54 | def ip 55 | @ip ||= Xhyve::DHCP.get_ip_for_mac(@mac) 56 | end 57 | 58 | private 59 | 60 | def redirection 61 | if @foreground 62 | [$stdout.raw!, $stderr.raw!, $stdin.raw! ] 63 | else 64 | [NULLDEV, NULLDEV, NULLDEV] 65 | end 66 | end 67 | 68 | def find_mac 69 | `#{@command} -M`.strip.gsub(/MAC:\s+/,'') 70 | end 71 | 72 | def build_command 73 | [ 74 | "#{@binary}", 75 | "#{'-A' if @acpi}", 76 | '-U', @uuid, 77 | '-m', @memory, 78 | '-c', @processors, 79 | '-s', '0:0,hostbridge', 80 | "#{"-s #{PCI_BASE - 1}:0,virtio-net" if @networking }" , 81 | "#{"#{@blockdevs.each_with_index.map { |p, i| "-s #{PCI_BASE + i},virtio-blk,#{p}" }.join(' ')}" unless @blockdevs.empty? }", 82 | '-s', '31,lpc', 83 | '-l', "#{@serial},stdio", 84 | '-f' "kexec,#{@kernel},#{@initrd},'#{@cmdline}'" 85 | ].join(' ') 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/xhyve/vendor/xhyve: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/vendor/xhyve-ruby/lib/xhyve/vendor/xhyve -------------------------------------------------------------------------------- /vendor/xhyve-ruby/lib/xhyve/version.rb: -------------------------------------------------------------------------------- 1 | # Common Xhyve functionality nampesace 2 | module Xhyve 3 | VERSION = '0.0.6' 4 | end 5 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/fixtures/dhcpd_leases.txt: -------------------------------------------------------------------------------- 1 | { 2 | name=boxt 3 | ip_address=192.168.64.35 4 | hw_address=1,9a:65:1b:12:cf:f 5 | identifier=1,9a:65:1b:12:cf:f 6 | lease=0x56551008 7 | } 8 | { 9 | name=box 10 | ip_address=192.168.64.34 11 | hw_address=1,9a:65:1b:12:cf:32 12 | identifier=1,9a:65:1b:12:cf:32 13 | lease=0x56551009 14 | } 15 | { 16 | name=localhost 17 | ip_address=192.168.64.5 18 | hw_address=1,a6:84:b2:34:cf:32 19 | identifier=1,a6:84:b2:34:cf:32 20 | lease=0x5653f52c 21 | } 22 | { 23 | name=localhost 24 | ip_address=192.168.64.4 25 | hw_address=1,ea:28:a:33:cf:32 26 | identifier=1,ea:28:a:33:cf:32 27 | lease=0x5653f4eb 28 | } 29 | { 30 | name=localhost 31 | ip_address=192.168.64.3 32 | hw_address=1,e2:ff:e:70:cf:32 33 | identifier=1,e2:ff:e:70:cf:32 34 | lease=0x5653f496 35 | } 36 | { 37 | name=localhost 38 | ip_address=192.168.64.2 39 | hw_address=1,5a:90:52:13:cf:32 40 | identifier=1,5a:90:52:13:cf:32 41 | lease=0x5653f3c5 42 | } 43 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/fixtures/guest/README.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | We use tinycore linux, with a small persistence volume that just has openssh. 4 | 5 | To regenerate this persistence volume (on osx): 6 | 7 | ``` 8 | # Create a sparse filesystem 9 | 10 | dd if=/dev/zero of=loop.img bs=1 count=0 seek=10m 11 | 12 | # Mount the image in xhyve, (without opt=vda1) then create a partition table and mkfs.ext4 it. 13 | # It will likely be /dev/vda 14 | # Then reboot with opt=vda 15 | # confirm it's mounted over /opt, then make your changes as per 16 | # http://myblog-kenton.blogspot.ca/2012/03/install-openssh-server-on-tiny-core.html 17 | 18 | tce-load -iw openssh.tcz 19 | sudo cp /usr/local/etc/ssh/sshd_config_example /usr/local/etc/ssh/sshd_config 20 | cat >> /opt/.filetool.lst <> /opt/bootlocal.sh 27 | 28 | sudo /usr/local/etc/init.d/openssh start 29 | sudo filetool.sh -b 30 | 31 | ``` 32 | 33 | When booting set user=console boot flag, and it will create the console user with password defaulting to 'tcuser' 34 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/fixtures/guest/initrd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/vendor/xhyve-ruby/spec/fixtures/guest/initrd -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/fixtures/guest/vmlinuz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldpatricka/vagrant-xhyve/0e3c62ab15b18aa45c63d09498980cbbf77313ce/vendor/xhyve-ruby/spec/fixtures/guest/vmlinuz -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/lib/dhcp_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper.rb', __FILE__) 2 | 3 | RSpec.describe Xhyve::DHCP do 4 | let(:leases) do 5 | { 6 | '9a:65:1b:12:cf:0f' => '192.168.64.35', 7 | '9a:65:1b:12:cf:32' => '192.168.64.34', 8 | 'a6:84:b2:34:cf:32' => '192.168.64.5', 9 | 'ea:28:a:33:cf:32' => '192.168.64.4', 10 | 'e2:ff:e:70:cf:32' => '192.168.64.3', 11 | '5a:90:52:13:cf:32' => '192.168.64.2' 12 | } 13 | end 14 | 15 | before :all do 16 | ENV['LEASES_FILE'] = File.join(FIXTURE_PATH, 'dhcpd_leases.txt') 17 | ENV['MAX_IP_WAIT'] = '1' 18 | end 19 | 20 | after :all do 21 | ENV.delete('LEASES_FILE') 22 | ENV.delete('MAX_IP_WAIT') 23 | end 24 | 25 | it 'parses the leases file to get an IP from a MAC' do 26 | leases.each do |mac, ip| 27 | expect(Xhyve::DHCP.get_ip_for_mac(mac)).to_not be_nil 28 | expect(Xhyve::DHCP.get_ip_for_mac(mac)).to eq(ip) 29 | end 30 | end 31 | 32 | it 'returns nil if no lease is found' do 33 | expect(Xhyve::DHCP.get_ip_for_mac('fakemac')).to be_nil 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/lib/guest_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper.rb', __FILE__) 2 | 3 | RSpec.describe Xhyve::Guest do 4 | before :all do 5 | kernel = File.join(FIXTURE_PATH, 'guest', 'vmlinuz') 6 | initrd = File.join(FIXTURE_PATH, 'guest', 'initrd') 7 | blockdev = File.join(FIXTURE_PATH, 'guest', 'loop.img') 8 | cmdline = 'earlyprintk=true console=ttyS0 user=console opt=vda tce=vda' 9 | uuid = SecureRandom.uuid # '32e54269-d1e2-4bdf-b4ff-bbe0eb42572d' # 10 | 11 | @guest = Xhyve::Guest.new(kernel: kernel, initrd: initrd, cmdline: cmdline, blockdevs: blockdev, uuid: uuid, serial: 'com1') 12 | @guest.start 13 | end 14 | 15 | after :all do 16 | @guest.stop 17 | end 18 | 19 | it 'Can start a guest' do 20 | expect(@guest.pid).to_not be_nil 21 | expect(@guest.pid).to be > 0 22 | expect(@guest.running?).to eq(true) 23 | end 24 | 25 | it 'Can get the MAC of a guest' do 26 | expect(@guest.mac).to_not be_nil 27 | expect(@guest.mac).to_not be_empty 28 | expect(@guest.mac).to match(/\w\w:\w\w:\w\w:\w\w:\w\w:\w\w/) 29 | end 30 | 31 | it 'Can get the IP of a guest' do 32 | expect(@guest.ip).to_not be_nil 33 | expect(@guest.ip).to match(/\d+\.+\d+\.\d+\.\d+/) 34 | end 35 | 36 | it 'Can ping the guest' do 37 | expect(ping(@guest.ip)).to eq(true) 38 | end 39 | 40 | it 'Can ssh to the guest' do 41 | expect(on_guest(@guest.ip, 'hostname')).to eq('box') 42 | end 43 | 44 | it 'Correctly sets processors' do 45 | expect(on_guest(@guest.ip, "cat /proc/cpuinfo | grep 'cpu cores' | awk '{print $4}'")).to eq('1') 46 | end 47 | 48 | it 'Correctly sets memory' do 49 | expect(on_guest(@guest.ip, "free -mm | grep 'Mem:' | awk '{print $2}'").to_i).to be_within(50).of(500) 50 | end 51 | end unless ENV['TRAVIS'] 52 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start 3 | 4 | require 'securerandom' 5 | require 'net/ssh' 6 | require 'net/ping' 7 | require File.expand_path('../../lib/xhyve.rb', __FILE__) 8 | 9 | FIXTURE_PATH = File.expand_path('../../spec/fixtures', __FILE__) 10 | 11 | # def self.append_features(mod) 12 | # mod.class_eval %[ 13 | # around(:each) do |example| 14 | # example.run 15 | # end 16 | # ] 17 | # end 18 | # end 19 | 20 | def ping(ip) 21 | attempts = 0 22 | max_attempts = 60 23 | sleep_time = 1 24 | 25 | while attempts < max_attempts 26 | attempts += 1 27 | sleep(sleep_time) 28 | begin 29 | return true if Net::Ping::ICMP.new(ip).ping 30 | rescue 31 | end 32 | end 33 | end 34 | 35 | def on_guest(ip, command) 36 | output = '' 37 | Net::SSH.start(ip, 'console', password: 'tcuser') do |ssh| 38 | output = ssh.exec!(command) 39 | end 40 | output.strip 41 | end 42 | 43 | RSpec.configure do |config| 44 | config.order = :defined 45 | config.expect_with :rspec do |expectations| 46 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 47 | end 48 | 49 | config.mock_with :rspec do |mocks| 50 | mocks.verify_partial_doubles = true 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /vendor/xhyve-ruby/xhyve-ruby.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('../lib', __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'xhyve/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'xhyve-ruby' 7 | s.version = Xhyve::VERSION 8 | s.date = '2015-11-23' 9 | s.summary = 'Ruby wrapper for xhyve' 10 | s.description = 'Provides a means of interacting with xhyve from ruby' 11 | s.authors = ['Dale Hamel'] 12 | s.email = 'dale.hamel@srvthe.net' 13 | s.files = Dir['lib/**/*'] 14 | s.homepage = 15 | 'https://github.com/dalehamel/xhyve-ruby' 16 | s.license = 'MIT' 17 | s.add_development_dependency 'simplecov', ['=0.10.0'] 18 | s.add_development_dependency 'rspec', ['=3.2.0'] 19 | s.add_development_dependency 'net-ssh', ['=3.0.1'] 20 | s.add_development_dependency 'net-ping', ['=1.7.8'] 21 | s.add_development_dependency 'rake', ['=10.4.2'] 22 | s.add_development_dependency 'rake-compiler', ['=0.9.5'] 23 | end 24 | --------------------------------------------------------------------------------