virtuoso 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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