virtuoso 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ test.rb
5
+ .yardoc/
6
+ doc/
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -m markdown
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in virtuoso.gemspec
4
+ gem "virtuoso", :path => "."
5
+
6
+ # Use libvirt-rb straight from git, since Virtuoso dev requires
7
+ # the latest and greatest
8
+ gem "libvirt", :git => "git://github.com/mitchellh/libvirt-rb.git"
9
+
10
+ # Gems required for testing only.
11
+ group :development do
12
+ gem "protest", "~> 0.4.0"
13
+ gem "mocha", "~> 0.9.8"
14
+
15
+ # Not JRuby, which doesn't like bluecloth
16
+ platforms :ruby, :mri do
17
+ gem "yard", "~> 0.6.1"
18
+ gem "bluecloth", "~> 2.0.9"
19
+ end
20
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ GIT
2
+ remote: git://github.com/mitchellh/libvirt-rb.git
3
+ revision: d06c4aef6e52723816b76b4e46b5f8cc985d5d8a
4
+ specs:
5
+ libvirt (0.2.1.dev)
6
+ ffi (~> 0.6.3)
7
+ nokogiri (~> 1.4.3)
8
+
9
+ PATH
10
+ remote: .
11
+ specs:
12
+ virtuoso (0.0.1)
13
+ libvirt (~> 0.2)
14
+
15
+ GEM
16
+ remote: http://rubygems.org/
17
+ specs:
18
+ bluecloth (2.0.9)
19
+ ffi (0.6.3)
20
+ rake (>= 0.8.7)
21
+ mocha (0.9.10)
22
+ rake
23
+ nokogiri (1.4.4)
24
+ protest (0.4.2)
25
+ rake (0.8.7)
26
+ yard (0.6.3)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ bluecloth (~> 2.0.9)
33
+ libvirt!
34
+ mocha (~> 0.9.8)
35
+ protest (~> 0.4.0)
36
+ virtuoso!
37
+ yard (~> 0.6.1)
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Virtuoso
2
+
3
+ Virtuoso is a Ruby library that provides dead simple virtual machine
4
+ management across many hypervisors, using the powerful [libvirt](http://libvirt.org)
5
+ library underneath. Libvirt is an extremely powerful library, and the
6
+ focus of Virtuoso is to provide an extremely simple, common API for
7
+ managing virtual machines at the cost of sacrificing some of libvirt's
8
+ power.
9
+
10
+ Currently supported hypervisors:
11
+
12
+ - VirtualBox
13
+
14
+ Since Virtuoso is built on top of [libvirt](http://libvirt.org), it isn't
15
+ too difficult to add support for another hypervisor. The reason a libvirt-supported
16
+ hypervisor may not be supportd by Virtuoso at this time is most likely
17
+ because I don't have experience using that hypervisor. Open an issue if
18
+ you'd like to see support for another hypervisor.
19
+
20
+ ## Installation
21
+
22
+ The library is packaged as a gem:
23
+
24
+ gem install virtuoso
25
+
26
+ Additionally, you may need to install libvirt, the C-library used to
27
+ interface with the various hypervisors. On OS X the recommended way is
28
+ using [homebrew](http://github.com/mxcl/homebrew):
29
+
30
+ brew install libvirt
31
+
32
+ If you're on linux, your package manager should contain a compatible
33
+ version of libvirt.
34
+
35
+ ## Project Status and Warning
36
+
37
+ **Warning:** This project is extremely _alpha_. The API will most definitely
38
+ change multiple times in the near future and the project itself will be
39
+ a fast moving target. This status will be updated in time as the project
40
+ matures.
41
+
42
+ ## Usage
43
+
44
+ Below is an example of starting a VM with VirtualBox. All drivers (for
45
+ different hypervisors) are required to conform to the same API, so the
46
+ usage is the same for all other hypervisors.
47
+
48
+ require 'virtuoso'
49
+
50
+ # Connect to a libvirt instance. Virtuoso instantiates the proper
51
+ # hypervisor.
52
+ hypervisor = Virtuoso.connect("vbox:///session")
53
+
54
+ # Create a new VM within the hypervisor and start it
55
+ vm = hypervisor.new_vm
56
+ vm.name = "My Virtuoso VM"
57
+ vm.disk_image = "/home/mitchellh/lucid.vmdk"
58
+ vm.save
59
+ vm.start
60
+
61
+ # Watch it booting...
62
+ sleep 5
63
+
64
+ # Stop and destroy it
65
+ vm.stop
66
+ sleep 3
67
+ vm.destroy
68
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'bundler/setup'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :default => :test
7
+
8
+ desc "Run the test suite."
9
+ task :test do
10
+ $:.unshift File.expand_path("../test", __FILE__)
11
+ files = ENV["TEST"] ? [ENV["TEST"]] : Dir["test/**/*_test.rb"]
12
+ files.each { |f| load f }
13
+ end
14
+
15
+ begin
16
+ # Documentation task
17
+ require 'yard'
18
+ YARD::Rake::YardocTask.new
19
+ rescue LoadError
20
+ end
data/lib/virtuoso.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'libvirt'
2
+
3
+ module Virtuoso
4
+ autoload :Error, "virtuoso/error"
5
+ autoload :VirtualBox, "virtuoso/virtualbox"
6
+
7
+ # Holds all the "abstract" classes for specifying and documenting
8
+ # the Virtuoso API.
9
+ module API
10
+ autoload :Hypervisor, "virtuoso/api/hypervisor"
11
+ autoload :VM, "virtuoso/api/vm"
12
+ end
13
+
14
+ # Connects to a hypervisor given by the URL to a libvirt instance,
15
+ # and returns the proper hypervisor class based on the connection.
16
+ def self.connect(url=nil)
17
+ mapping = { "VBOX" => :VirtualBox }
18
+ conn = Libvirt.connect(url)
19
+ raise Error::UnsupportedHypervisorError, "Unsupported hypervisor: #{conn.hypervisor}" if !mapping[conn.hypervisor]
20
+ const_get(mapping[conn.hypervisor]).const_get(:Hypervisor).new(conn)
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Virtuoso
2
+ module API
3
+ # Base class specifying the API for all hypervisors. Every feature in
4
+ # this base class must be overloaded by any hypervisors.
5
+ class Hypervisor
6
+ # The libvirt connection instance.
7
+ attr_reader :connection
8
+
9
+ # Initializes a hypervisor with the given libvirt connection. The
10
+ # connection should be established through {Virtuoso.connect}, which
11
+ # also chooses the correct hypervisor.
12
+ def initialize(connection)
13
+ @connection = connection
14
+ end
15
+
16
+ # Returns a new {VM} instance that can be used to create a new virtual
17
+ # machine.
18
+ #
19
+ # @return [VM]
20
+ def new_vm; end
21
+
22
+ # Searches for a VM with the given ID and returns it if it finds it,
23
+ # and otherwise returns nil. The exact semantics of the find are up to
24
+ # the hypervisor but typically it searches by both name and UUID.
25
+ #
26
+ # @return [VM]
27
+ def find(id); end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,90 @@
1
+ module Virtuoso
2
+ module API
3
+ # Base class specifying the API that all VMs within a hypervisor must
4
+ # conform to.
5
+ class VM
6
+ # The libvirt connection instance.
7
+ attr_reader :connection
8
+
9
+ # The libvirt domain object.
10
+ attr_reader :domain
11
+
12
+ # The name of the VM.
13
+ attr_accessor :name
14
+
15
+ # The memory for the VM.
16
+ attr_accessor :memory
17
+
18
+ # The disk image to use as the main boot drive.
19
+ attr_accessor :disk_image
20
+
21
+ # Initializes a VM with the given libvirt connection.
22
+ def initialize(connection, domain=nil)
23
+ @connection = connection
24
+ @domain = domain
25
+
26
+ # Set reasonable defaults for fields if we can
27
+ @name = "My Virtuoso VM"
28
+ @memory = 524288 # 512 MB
29
+
30
+ # Load in the proper data
31
+ reload if domain
32
+ end
33
+
34
+ # Returns the current state of the VM. This is expected to always
35
+ # return the current, up-to-date state (therefore it is _not_ cached
36
+ # and updated only on {#reload}). The state is meant to be returned
37
+ # as a symbol.
38
+ #
39
+ # @return [Symbol]
40
+ def state; end
41
+
42
+ # Saves the VM. If the VM is new, this is expected to create it
43
+ # initially, otherwise this is expected to update the existing
44
+ # VM.
45
+ def save; end
46
+
47
+ # Destroys the VM, deleting any information about it. This will not
48
+ # destroy any disk images, nor will it stop the VM if it is running.
49
+ def destroy; end
50
+
51
+ # Starts the VM.
52
+ def start; end
53
+
54
+ # Stops the VM.
55
+ def stop; end
56
+
57
+ # Reloads information from about a VM which exists. Since Virtuoso
58
+ # can't enforce any sort of VM locking, it is possible a VM changes
59
+ # in the background by some other process while it is being modified.
60
+ # In that case, when you attempt to save, your changes will either
61
+ # overwrite the previous settings, or fail altogether (if someone else
62
+ # destroyed the VM, for example). It is up to the developer to be
63
+ # knowledgeable about his or her environment and account for this
64
+ # accordingly. If you know that a VM changed, or you're just being
65
+ # careful, {#reload} may be called to reload the data associated
66
+ # with this VM and bring it up to date.
67
+ def reload; end
68
+
69
+ protected
70
+
71
+ # A helper method for subclasses to mark methods which require an
72
+ # existing VM to function (these are methods like `start` and `stop`).
73
+ # This method will raise an {Error::NewVMError} if an existing VM
74
+ # is not set.
75
+ def requires_existing_vm
76
+ raise Error::NewVMError if !domain
77
+ end
78
+
79
+ # A helper method for subclasses to set a domain object to represent
80
+ # this VM. This properly sets up the object and reloads it for the
81
+ # most up to date information.
82
+ def set_domain(domain)
83
+ @domain = domain
84
+ @domain_spec = nil
85
+
86
+ reload if domain
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ module Virtuoso
2
+ # Any exceptions which are thrown by Virtuoso (not lower-level libraries)
3
+ # exist in this module.
4
+ module Error
5
+ class VirtuosoError < StandardError; end
6
+ class UnsupportedHypervisorError < StandardError; end
7
+ class NewVMError < VirtuosoError; end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Virtuoso
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ module Virtuoso
2
+ module VirtualBox
3
+ autoload :Hypervisor, "virtuoso/virtualbox/hypervisor"
4
+ autoload :VM, "virtuoso/virtualbox/vm"
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Virtuoso
2
+ module VirtualBox
3
+ # VirtualBox driver, allowing the control and management of
4
+ # VirtualBox virtual machines.
5
+ class Hypervisor < API::Hypervisor
6
+ def new_vm
7
+ VM.new(connection)
8
+ end
9
+
10
+ # Searches for a VM by name or UUID.
11
+ #
12
+ # @param [String] id Name or UUID
13
+ # @return [VM]
14
+ def find(id)
15
+ result = connection.domains.find(id)
16
+ return nil if !result
17
+ VM.new(connection, result)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,85 @@
1
+ module Virtuoso
2
+ module VirtualBox
3
+ # VirtualBox VM.
4
+ class VM < API::VM
5
+ def state
6
+ domain ? domain.state : :new
7
+ end
8
+
9
+ def save
10
+ # Setup the basic settings for the VM
11
+ d = Libvirt::Spec::Domain.new
12
+ d.hypervisor = :vbox
13
+ d.name = name
14
+ d.memory = memory
15
+ d.vcpu = 1
16
+ d.features = [:acpi, :pae]
17
+ d.clock.offset = :localtime
18
+ d.os.type = :hvm
19
+ d.os.arch = :i386
20
+ d.os.boot = [:cdrom, :hd]
21
+
22
+ # Attach the main hard disk
23
+ disk = Libvirt::Spec::Device.get(:disk).new
24
+ disk.type = :file
25
+ disk.device = :disk
26
+ disk.source = disk_image
27
+ disk.target_dev = :hda
28
+ disk.target_bus = :ide
29
+ d.devices << disk
30
+
31
+ # Attach a basic NAT network interface
32
+ nat = Libvirt::Spec::Device.get(:interface).new
33
+ nat.type = :user
34
+ nat.mac_address = "08:00:27:8f:7a:9f"
35
+ nat.model_type = "82540EM"
36
+ d.devices << nat
37
+
38
+ # Attach video information
39
+ video = Libvirt::Spec::Device.get(:video).new
40
+ model = Libvirt::Spec::Device::VideoModel.new
41
+ model.type = :vbox
42
+ model.vram = 12
43
+ model.heads = 1
44
+ model.accel3d = false
45
+ model.accel2d = false
46
+ video.models << model
47
+ d.devices << video
48
+
49
+ # At this point, assuming the virtuoso settings are correct, we
50
+ # should have a bootable VM spec, so define it and reload the VM
51
+ # information.
52
+ @domain = connection.domains.define(d)
53
+ reload
54
+ end
55
+
56
+ def destroy
57
+ requires_existing_vm
58
+ @domain.undefine
59
+ @domain = nil
60
+ end
61
+
62
+ def start
63
+ requires_existing_vm
64
+ domain.start
65
+ end
66
+
67
+ def stop
68
+ requires_existing_vm
69
+ domain.stop
70
+ end
71
+
72
+ def reload
73
+ # Load the main disk image path. We assume this is the first "disk"
74
+ # device, though this assumption is probably pretty weak.
75
+ spec = domain.spec
76
+ disk = spec.devices.find { |d| d.is_a?(Libvirt::Spec::Device::Disk) }
77
+ self.disk_image = disk.source
78
+
79
+ # Load the basic attributes
80
+ self.name = spec.name
81
+ self.memory = spec.memory
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,30 @@
1
+ require "test/unit/assertions"
2
+ require "protest"
3
+ require "mocha"
4
+ require "virtuoso"
5
+
6
+ class Protest::TestCase
7
+ include Test::Unit::Assertions
8
+ include Mocha::API
9
+
10
+ # Get Mocha integrated properly into the tests
11
+ alias :original_run :run
12
+ def run(report)
13
+ original_run(report)
14
+ mocha_verify
15
+ ensure
16
+ mocha_teardown
17
+ end
18
+
19
+ # Returns a connection to a libvirt test hypervisor.
20
+ def test_connection
21
+ Libvirt.connect("test:///default")
22
+ end
23
+
24
+ # Returns a domain object from the libvirt test hypervisor.
25
+ def test_domain
26
+ test_connection.domains.first
27
+ end
28
+ end
29
+
30
+ Protest.report_with(:progress)
@@ -0,0 +1,70 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("API::VM") do
4
+ setup do
5
+ @klass = Virtuoso::API::VM
6
+ end
7
+
8
+ context "requiring an existing VM" do
9
+ setup do
10
+ @impl = Class.new(@klass) do
11
+ def save
12
+ @domain = true
13
+ end
14
+
15
+ def start
16
+ requires_existing_vm
17
+ end
18
+ end
19
+
20
+ @instance = @impl.new(test_connection)
21
+ end
22
+
23
+ should "raise an exception if an existing VM is not set" do
24
+ assert_raises(Virtuoso::Error::NewVMError) { @instance.start }
25
+ end
26
+
27
+ should "not raise an exception if an existing VM is set" do
28
+ assert_nothing_raised {
29
+ @instance.save
30
+ @instance.start
31
+ }
32
+ end
33
+ end
34
+
35
+ context "initializing a VM" do
36
+ context "reloading" do
37
+ setup do
38
+ @impl = Class.new(@klass) do
39
+ def reload
40
+ throw :reloaded, :reload
41
+ end
42
+ end
43
+ end
44
+
45
+ should "reload if a domain is given" do
46
+ result = catch :reloaded do
47
+ @impl.new(test_connection, test_domain)
48
+ nil
49
+ end
50
+
51
+ assert_equal :reload, result
52
+ end
53
+
54
+ should "not load if a domain is not given" do
55
+ result = catch :reloaded do
56
+ @impl.new(test_connection)
57
+ nil
58
+ end
59
+
60
+ assert !result
61
+ end
62
+ end
63
+ end
64
+
65
+ context "with a VM object" do
66
+ setup do
67
+ @instance = @klass.new(test_connection)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("Virtuoso") do
4
+ setup do
5
+ @klass = Virtuoso
6
+ end
7
+
8
+ context "connecting" do
9
+ should "raise an exception if an unsupported hypervisor is connected to" do
10
+ assert_raises(Virtuoso::Error::UnsupportedHypervisorError) {
11
+ @klass.connect("test:///default")
12
+ }
13
+ end
14
+
15
+ # TODO: To test other connections, hypervisor must be present... so we
16
+ # can't guarantee and test that yet.
17
+ end
18
+ end
data/virtuoso.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "virtuoso/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "virtuoso"
7
+ s.version = Virtuoso::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mitchell Hashimoto"]
10
+ s.email = ["mitchell.hashimoto@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/virtuoso"
12
+ s.summary = "Dead simple virtual machine management for many hypervisors."
13
+ s.description = "Dead simple virtual machine management for many hypervisors."
14
+
15
+ s.rubyforge_project = "virtuoso"
16
+
17
+ s.add_dependency "libvirt", "~> 0.2"
18
+
19
+ s.add_development_dependency "protest", "~> 0.4.0"
20
+ s.add_development_dependency "mocha", "~> 0.9.8"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virtuoso
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Mitchell Hashimoto
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-07 00:00:00 -08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: libvirt
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 2
30
+ version: "0.2"
31
+ type: :runtime
32
+ prerelease: false
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: protest
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ - 4
44
+ - 0
45
+ version: 0.4.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: mocha
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ - 9
59
+ - 8
60
+ version: 0.9.8
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: *id003
64
+ description: Dead simple virtual machine management for many hypervisors.
65
+ email:
66
+ - mitchell.hashimoto@gmail.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files: []
72
+
73
+ files:
74
+ - .gitignore
75
+ - .yardopts
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - README.md
79
+ - Rakefile
80
+ - lib/virtuoso.rb
81
+ - lib/virtuoso/api/hypervisor.rb
82
+ - lib/virtuoso/api/vm.rb
83
+ - lib/virtuoso/error.rb
84
+ - lib/virtuoso/version.rb
85
+ - lib/virtuoso/virtualbox.rb
86
+ - lib/virtuoso/virtualbox/hypervisor.rb
87
+ - lib/virtuoso/virtualbox/vm.rb
88
+ - test/test_helper.rb
89
+ - test/virtuoso/api/vm_test.rb
90
+ - test/virtuoso/virtuoso_test.rb
91
+ - virtuoso.gemspec
92
+ has_rdoc: true
93
+ homepage: http://rubygems.org/gems/virtuoso
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: -4389485739309843364
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: -4389485739309843364
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project: virtuoso
122
+ rubygems_version: 1.3.7
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: Dead simple virtual machine management for many hypervisors.
126
+ test_files:
127
+ - test/test_helper.rb
128
+ - test/virtuoso/api/vm_test.rb
129
+ - test/virtuoso/virtuoso_test.rb