vagrant-libvirt 0.0.1 → 0.0.2

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 CHANGED
@@ -15,3 +15,6 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ Vagrantfile
19
+ !example_box/Vagrantfile
20
+ .vagrant
data/README.md CHANGED
@@ -4,16 +4,19 @@ This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that adds an
4
4
  [Libvirt](http://libvirt.org) provider to Vagrant, allowing Vagrant to
5
5
  control and provision machines via Libvirt toolkit.
6
6
 
7
+ This plugin is inspired by existing [vagrant-aws](https://github.com/mitchellh/vagrant-aws) provider.
8
+
7
9
  **Note:** This plugin requires Vagrant 1.1+.
8
10
 
9
- ## Features
11
+ ## Features (Version 0.0.2)
10
12
 
11
- * Upload of box image (qcow2 format) to Libvirt storage pool.
12
- * Volume creation as COW diff image for each new domain.
13
+ * Upload box image (qcow2 format) to Libvirt storage pool.
14
+ * Create volume as COW diff image for domains.
13
15
  * Create and boot Libvirt domains.
14
16
  * SSH into domains.
15
17
  * Provision domains with any built-in Vagrant provisioner.
16
18
  * Minimal synced folder support via `rsync`.
19
+ * Up, destroy, suspend, resume, halt, ssh, provision subcommands.
17
20
 
18
21
  ## Usage
19
22
 
@@ -29,5 +32,147 @@ $ vagrant up --provider=libvirt
29
32
  ```
30
33
 
31
34
  Of course prior to doing this, you'll need to obtain an Libvirt-compatible
32
- box file for Vagrant.
35
+ box file for Vagrant.
36
+
37
+ ### Problems with plugin installation
38
+
39
+ In case of problems with building nokogiri gem, install missing development
40
+ libraries libxslt and libxml2.
41
+
42
+ In Ubuntu, Debian, ...
43
+ ```
44
+ $ sudo apt-get install libxslt-dev libxml2-dev
45
+ ```
46
+
47
+ In RedHat, Centos, Fedora, ...
48
+ ```
49
+ # yum install libxslt-devel libxml2-devel
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ After installing the plugin (instructions above), the quickest way to get
55
+ started is to add Libvirt box and specify all the details manually within
56
+ a `config.vm.provider` block. So first, add Libvirt box using any name you
57
+ want. This is just an example of Libvirt CentOS 6.4 box available:
58
+
59
+ ```
60
+ $ vagrant box add centos64 http://kwok.cz/centos64.box
61
+ ...
62
+ ```
63
+
64
+ And then make a Vagrantfile that looks like the following, filling in
65
+ your information where necessary.
66
+
67
+ ```
68
+ Vagrant.configure("2") do |config|
69
+ config.vm.define :test_vm do |test_vm|
70
+ test_vm.vm.box = "centos64"
71
+ end
72
+
73
+ config.vm.provider :libvirt do |libvirt|
74
+ libvirt.driver = "qemu"
75
+ libvirt.host = "example.com"
76
+ libvirt.connect_via_ssh = true
77
+ libvirt.username = "root"
78
+ #libvirt.password = "secret"
79
+ libvirt.storage_pool_name = "default"
80
+ end
81
+ end
82
+
83
+ ```
84
+
85
+ And then run `vagrant up --provider=libvirt`. Other way to tell Vagrant to
86
+ use Libvirt provider is to setup environment variable `export VAGRANT_DEFAULT_PROVIDER=libvirt`.
87
+
88
+ This will first upload box image to remote Libvirt storage pool as new volume.
89
+ Then create and start a CentOS 6.4 domain on example.com Libvirt host. In this
90
+ example configuration, connection to Libvirt is tunneled via SSH.
91
+
92
+ ## Box Format
93
+
94
+ Every provider in Vagrant must introduce a custom box format. This
95
+ provider introduces `Libvirt` boxes. You can view an example box in
96
+ the [example_box/directory](https://github.com/pradels/vagrant-libvirt/tree/master/example_box). That directory also contains instructions on how to build a box.
97
+
98
+ The box format is qcow2 image file `box.img`, the required `metadata.json` file
99
+ along with a `Vagrantfile` that does default settings for the
100
+ provider-specific configuration for this provider.
101
+
102
+ ## Configuration
103
+
104
+ This provider exposes quite a few provider-specific configuration options:
105
+
106
+ * `driver` - A hypervisor name to access. For now only qemu is supported.
107
+ * `host` - The name of the server, where libvirtd is running.
108
+ * `connect_via_ssh` - If use ssh tunnel to connect to Libvirt.
109
+ * `username` - Username and password to access Libvirt.
110
+ * `password` - Password to access Libvirt.
111
+ * `storage_pool_name` - Libvirt storage pool name, where box image and
112
+ instance snapshots will be stored.
113
+
114
+ ## Networks
115
+
116
+ Networking features in the form of `config.vm.network` are supported only
117
+ in bridged format, no hostonly network is supported in current version of
118
+ provider.
119
+
120
+ Example of network interface definition:
121
+
122
+ ```
123
+ config.vm.define :test_vm do |test_vm|
124
+ test_vm.vm.network :bridged, :bridge => "default", :adapter => 1
125
+ end
126
+ ```
127
+
128
+ Bridged network adapter connected to network `default` is defined.
129
+
130
+ ## Getting IP address
131
+
132
+ There is a little problem to find out which IP address was assigned to remote
133
+ domain. Fog library uses SSH connection to remote libvirt host and by default
134
+ checks arpwatch entries there.
135
+
136
+ Vagrant Libvirt provider is using dnsmasq leases files to find out, which IPs
137
+ dhcp server offered. VMs IP address is then saved to `$data_dir/ip` file for
138
+ later use. Of course, VMs IP can be changed over time. That's why IP is
139
+ checked, if matches with VMs MAC address after each reading from this state
140
+ file. Mismatch error is shown if IP doesn't match.
141
+
142
+
143
+ ## Synced Folders
144
+
145
+ There is minimal support for synced folders. Upon `vagrant up`, the Libvirt
146
+ provider will use `rsync` (if available) to uni-directionally sync the folder
147
+ to the remote machine over SSH.
148
+
149
+ This is good enough for all built-in Vagrant provisioners (shell,
150
+ chef, and puppet) to work!
151
+
152
+ ## Development
153
+
154
+ To work on the `vagrant-libvirt` plugin, clone this repository out, and use
155
+ [Bundler](http://gembundler.com) to get the dependencies:
156
+
157
+ ```
158
+ $ bundle
159
+ ```
160
+
161
+ Once you have the dependencies, verify the unit tests pass with `rake`:
162
+
163
+ ```
164
+ $ bundle exec rake
165
+ ```
166
+
167
+ If those pass, you're ready to start developing the plugin. You can test
168
+ the plugin without installing it into your Vagrant environment by just
169
+ creating a `Vagrantfile` in the top level of this directory (it is gitignored)
170
+ that uses it, and uses bundler to execute Vagrant:
171
+
172
+ ```
173
+ $ bundle exec vagrant up --provider=libvirt
174
+ ```
175
+
176
+ ## Future work
33
177
 
178
+ Take a look on [open issues](https://github.com/pradels/vagrant-libvirt/issues?state=open).
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env rake
2
2
 
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- #require 'bundler/gem_tasks'
3
+ #require 'rubygems'
4
+ #require 'bundler/setup'
5
+ require 'bundler/gem_tasks'
6
6
  Bundler::GemHelper.install_tasks
7
7
 
@@ -12,3 +12,12 @@ $ tar cvzf custom_box.box ./metadata.json ./Vagrantfile ./box.img
12
12
  This box works by using Vagrant's built-in Vagrantfile merging to setup
13
13
  defaults for Libvirt. These defaults can easily be overwritten by higher-level
14
14
  Vagrantfiles (such as project root Vagrantfiles).
15
+
16
+ ## Box Metadata
17
+
18
+ Libvirt box should define at least three data fields in `metadata.json` file.
19
+
20
+ * provider - Provider name is libvirt.
21
+ * format - Currently supported format is qcow2.
22
+ * virtual_size - Virtual size of image in GBytes.
23
+
@@ -1,5 +1,5 @@
1
1
  {
2
- "provider" : "libvirt"
3
- "format" : "qcow2"
4
- "virtual_size" : "40"
2
+ "provider" : "libvirt",
3
+ "format" : "qcow2",
4
+ "virtual_size" : 40
5
5
  }
@@ -11,7 +11,7 @@ module VagrantPlugins
11
11
  # Hold connection handler so there is no need to connect more times than
12
12
  # one. This can be annoying when there are more machines to create, or when
13
13
  # doing state action first and then some other.
14
- #
14
+ #
15
15
  # TODO Don't sure if this is the best solution
16
16
  @@libvirt_connection = nil
17
17
  def self.libvirt_connection
@@ -24,7 +24,16 @@ module VagrantPlugins
24
24
 
25
25
  def self.source_root
26
26
  @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
27
- end
27
+ end
28
28
  end
29
29
  end
30
30
 
31
+ # set provider by bash env
32
+ # export VAGRANT_DEFAULT_PROVIDER=libvirt
33
+ Vagrant::Environment.class_eval do
34
+ def default_provider
35
+ (ENV['VAGRANT_DEFAULT_PROVIDER'] || :virtualbox).to_sym
36
+ end
37
+ end
38
+
39
+
@@ -12,23 +12,78 @@ module VagrantPlugins
12
12
  b.use ConfigValidate
13
13
  b.use ConnectLibvirt
14
14
  b.use Call, IsCreated do |env, b2|
15
- if env[:result]
16
- b2.use MessageAlreadyCreated
17
- next
15
+ # Create VM if not yet created.
16
+ if !env[:result]
17
+ b2.use SetNameOfDomain
18
+ b2.use HandleStoragePool
19
+ b2.use HandleBoxImage
20
+ b2.use CreateDomainVolume
21
+ b2.use CreateDomain
22
+ b2.use CreateNetworkInterfaces
23
+
24
+ b2.use TimedProvision
25
+ b2.use StartDomain
26
+ b2.use WaitTillUp
27
+ b2.use SyncFolders
28
+ else
29
+ b2.use action_start
18
30
  end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Assuming VM is created, just start it. This action is not called
36
+ # directly by any subcommand. VM can be suspended, already running or in
37
+ # poweroff state.
38
+ def self.action_start
39
+ Vagrant::Action::Builder.new.tap do |b|
40
+ b.use ConfigValidate
41
+ b.use ConnectLibvirt
42
+ b.use Call, IsRunning do |env, b2|
43
+ # If the VM is running, then our work here is done, exit
44
+ next if env[:result]
45
+
46
+ b2.use Call, IsSuspended do |env2, b3|
47
+ if env2[:result]
48
+ b3.use ResumeDomain
49
+ next
50
+ end
19
51
 
20
- b2.use SetNameOfDomain
21
- b2.use HandleStoragePool
22
- b2.use HandleBoxImage
23
- b2.use CreateDomainVolume
24
- b2.use CreateDomain
25
- b2.use CreateNetworkInterfaces
52
+ # VM is not running or suspended. Start it.. Machine should gain
53
+ # IP address when comming up, so wait for dhcp lease and store IP
54
+ # into machines data_dir.
55
+ b3.use StartDomain
56
+ b3.use WaitTillUp
57
+ end
26
58
  end
59
+ end
60
+ end
61
+
62
+ # This is the action that is primarily responsible for halting the
63
+ # virtual machine.
64
+ def self.action_halt
65
+ Vagrant::Action::Builder.new.tap do |b|
66
+ b.use ConfigValidate
67
+ b.use ConnectLibvirt
68
+ b.use Call, IsCreated do |env, b2|
69
+ if !env[:result]
70
+ b2.use MessageNotCreated
71
+ next
72
+ end
73
+
74
+ b2.use Call, IsSuspended do |env2, b3|
75
+ b3.use ResumeDomain if env2[:result]
76
+ end
27
77
 
28
- b.use TimedProvision
29
- b.use StartDomain
30
- b.use WaitTillUp
31
- b.use SyncFolders
78
+ b2.use Call, IsRunning do |env2, b3|
79
+ next if !env2[:result]
80
+
81
+ # VM is running, halt it.. Cleanup running instance data. Now
82
+ # only IP address is stored.
83
+ b3.use HaltDomain
84
+ b3.use CleanupDataDir
85
+ end
86
+ end
32
87
  end
33
88
  end
34
89
 
@@ -45,6 +100,102 @@ module VagrantPlugins
45
100
 
46
101
  b2.use ConnectLibvirt
47
102
  b2.use DestroyDomain
103
+
104
+ # Cleanup running instance data. Now only IP address is stored.
105
+ b2.use CleanupDataDir
106
+ end
107
+ end
108
+ end
109
+
110
+ # This action is called to SSH into the machine.
111
+ def self.action_ssh
112
+ Vagrant::Action::Builder.new.tap do |b|
113
+ b.use ConfigValidate
114
+ b.use Call, IsCreated do |env, b2|
115
+ if !env[:result]
116
+ b2.use MessageNotCreated
117
+ next
118
+ end
119
+
120
+ b2.use ConnectLibvirt
121
+ b2.use Call, IsRunning do |env2, b3|
122
+ if !env2[:result]
123
+ b3.use MessageNotRunning
124
+ next
125
+ end
126
+
127
+ b3.use SSHExec
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ # This action is called when `vagrant provision` is called.
134
+ def self.action_provision
135
+ Vagrant::Action::Builder.new.tap do |b|
136
+ b.use ConfigValidate
137
+ b.use Call, IsCreated do |env, b2|
138
+ if !env[:result]
139
+ b2.use MessageNotCreated
140
+ next
141
+ end
142
+
143
+ b2.use ConnectLibvirt
144
+ b2.use Call, IsRunning do |env2, b3|
145
+ if !env2[:result]
146
+ b3.use MessageNotRunning
147
+ next
148
+ end
149
+
150
+ b3.use Provision
151
+ b3.use SyncFolders
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ # This is the action that is primarily responsible for suspending
158
+ # the virtual machine.
159
+ def self.action_suspend
160
+ Vagrant::Action::Builder.new.tap do |b|
161
+ b.use ConfigValidate
162
+ b.use Call, IsCreated do |env, b2|
163
+ if !env[:result]
164
+ b2.use MessageNotCreated
165
+ next
166
+ end
167
+
168
+ b2.use ConnectLibvirt
169
+ b2.use Call, IsRunning do |env2, b3|
170
+ if !env2[:result]
171
+ b3.use MessageNotRunning
172
+ next
173
+ end
174
+ b3.use SuspendDomain
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ # This is the action that is primarily responsible for resuming
181
+ # suspended machines.
182
+ def self.action_resume
183
+ Vagrant::Action::Builder.new.tap do |b|
184
+ b.use ConfigValidate
185
+ b.use Call, IsCreated do |env, b2|
186
+ if !env[:result]
187
+ b2.use MessageNotCreated
188
+ next
189
+ end
190
+
191
+ b2.use ConnectLibvirt
192
+ b2.use Call, IsSuspended do |env2, b3|
193
+ if !env2[:result]
194
+ b3.use MessageNotSuspended
195
+ next
196
+ end
197
+ b3.use ResumeDomain
198
+ end
48
199
  end
49
200
  end
50
201
  end
@@ -73,8 +224,12 @@ module VagrantPlugins
73
224
  action_root = Pathname.new(File.expand_path("../action", __FILE__))
74
225
  autoload :ConnectLibvirt, action_root.join("connect_libvirt")
75
226
  autoload :IsCreated, action_root.join("is_created")
227
+ autoload :IsRunning, action_root.join("is_running")
228
+ autoload :IsSuspended, action_root.join("is_suspended")
76
229
  autoload :MessageAlreadyCreated, action_root.join("message_already_created")
77
230
  autoload :MessageNotCreated, action_root.join("message_not_created")
231
+ autoload :MessageNotRunning, action_root.join("message_not_running")
232
+ autoload :MessageNotSuspended, action_root.join("message_not_suspended")
78
233
  autoload :HandleStoragePool, action_root.join("handle_storage_pool")
79
234
  autoload :HandleBoxImage, action_root.join("handle_box_image")
80
235
  autoload :SetNameOfDomain, action_root.join("set_name_of_domain")
@@ -83,6 +238,10 @@ module VagrantPlugins
83
238
  autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
84
239
  autoload :DestroyDomain, action_root.join("destroy_domain")
85
240
  autoload :StartDomain, action_root.join("start_domain")
241
+ autoload :HaltDomain, action_root.join("halt_domain")
242
+ autoload :SuspendDomain, action_root.join("suspend_domain")
243
+ autoload :ResumeDomain, action_root.join("resume_domain")
244
+ autoload :CleanupDataDir, action_root.join("cleanup_data_dir")
86
245
  autoload :ReadState, action_root.join("read_state")
87
246
  autoload :ReadSSHInfo, action_root.join("read_ssh_info")
88
247
  autoload :TimedProvision, action_root.join("timed_provision")
@@ -0,0 +1,22 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module Libvirt
5
+ module Action
6
+ class CleanupDataDir
7
+ def initialize(app, env)
8
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::cleanup_data_dir")
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ # Remove file holding IP address
14
+ ip_file_path = env[:machine].data_dir + 'ip'
15
+ File.delete(ip_file_path) if File.exists?(ip_file_path)
16
+
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -48,11 +48,9 @@ module VagrantPlugins
48
48
  conn_attr[:libvirt_password] = config.password if config.password
49
49
 
50
50
  # Setup command for retrieving IP address for newly created machine
51
- # with some MAC address. Get it via arp table. This solution doesn't
52
- # require arpwatch to be installed.
53
- conn_attr[:libvirt_ip_command] = "arp -an | grep $mac | sed '"
54
- conn_attr[:libvirt_ip_command] << 's/.*(\([0-9\.]*\)).*/\1/'
55
- conn_attr[:libvirt_ip_command] << "'"
51
+ # with some MAC address. Get it from dnsmasq leases table.
52
+ conn_attr[:libvirt_ip_command] = "cat /var/lib/libvirt/dnsmasq/*.leases"
53
+ conn_attr[:libvirt_ip_command] << " | grep $mac | awk ' { print $3 }'"
56
54
 
57
55
  @logger.info("Connecting to Libvirt (#{uri}) ...")
58
56
  begin
@@ -14,11 +14,11 @@ module VagrantPlugins
14
14
 
15
15
  def call(env)
16
16
  # Gather some info about domain
17
- # TODO from Vagrantfile
18
17
  @name = env[:domain_name]
18
+
19
19
  @cpus = 1
20
20
  @memory_size = 512*1024
21
-
21
+
22
22
  # TODO get type from driver config option
23
23
  @domain_type = 'kvm'
24
24
 
@@ -37,6 +37,7 @@ module VagrantPlugins
37
37
  env[:ui].info(" -- Cpus: #{@cpus}")
38
38
  env[:ui].info(" -- Memory: #{@memory_size/1024}M")
39
39
  env[:ui].info(" -- Base box: #{env[:machine].box.name}")
40
+ env[:ui].info(" -- Storage pool: #{env[:machine].provider_config.storage_pool_name}")
40
41
  env[:ui].info(" -- Image: #{@domain_volume_path}")
41
42
 
42
43
  # Create libvirt domain.
@@ -4,15 +4,13 @@ module VagrantPlugins
4
4
  module Libvirt
5
5
  module Action
6
6
  class DestroyDomain
7
-
8
7
  def initialize(app, env)
9
8
  @logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_domain")
10
9
  @app = app
11
10
  end
12
11
 
13
12
  def call(env)
14
-
15
- # Destroy the server and remove the tracking ID
13
+ # Destroy the server, remove the tracking ID
16
14
  env[:ui].info(I18n.t("vagrant_libvirt.destroy_domain"))
17
15
 
18
16
  domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
@@ -21,7 +19,6 @@ module VagrantPlugins
21
19
 
22
20
  @app.call(env)
23
21
  end
24
-
25
22
  end
26
23
  end
27
24
  end
@@ -0,0 +1,36 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module Libvirt
5
+ module Action
6
+ # Halt the domain.
7
+ class HaltDomain
8
+ def initialize(app, env)
9
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::halt_domain")
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ env[:ui].info(I18n.t("vagrant_libvirt.halt_domain"))
15
+
16
+ domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
17
+ raise Errors::NoDomainError if domain == nil
18
+
19
+ @logger.info("Trying gracefull shutdown.")
20
+ domain.shutdown
21
+ begin
22
+ domain.wait_for(30) {
23
+ !ready?
24
+ }
25
+ rescue Fog::Errors::TimeoutError
26
+ @logger.info("VM is still running. Calling force poweroff.")
27
+ domain.poweroff
28
+ end
29
+
30
+ @app.call(env)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -12,7 +12,7 @@ module VagrantPlugins
12
12
  def call(env)
13
13
 
14
14
  # Verify box metadata for mandatory values.
15
- #
15
+ #
16
16
  # Virtual size has to be set for allocating space in storage pool.
17
17
  box_virtual_size = env[:machine].box.metadata['virtual_size']
18
18
  if box_virtual_size == nil
@@ -103,7 +103,7 @@ module VagrantPlugins
103
103
  sent = stream.send buff
104
104
  progress += sent
105
105
  yield progress
106
- end
106
+ end
107
107
  end
108
108
  rescue => e
109
109
  raise Errors::ImageUploadError,
@@ -0,0 +1,21 @@
1
+ module VagrantPlugins
2
+ module Libvirt
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is running and branch in the middleware.
6
+ class IsRunning
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
13
+ raise Errors::NoDomainError if domain == nil
14
+ env[:result] = domain.state.to_s == 'running'
15
+
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module VagrantPlugins
2
+ module Libvirt
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is suspended and branch in the middleware.
6
+ class IsSuspended
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
13
+ raise Errors::NoDomainError if domain == nil
14
+ env[:result] = domain.state.to_s == 'paused'
15
+
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Libvirt
3
+ module Action
4
+ class MessageNotRunning
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_libvirt.not_running"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Libvirt
3
+ module Action
4
+ class MessageNotSuspended
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_libvirt.not_suspended"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -18,23 +18,58 @@ module VagrantPlugins
18
18
  @app.call(env)
19
19
  end
20
20
 
21
-
22
- def read_ssh_info(libvirt, machine)
21
+ def read_ssh_info(libvirt,machine)
23
22
  return nil if machine.id.nil?
24
23
 
25
24
  # Find the machine
26
- server = libvirt.servers.get(machine.id)
27
- if server.nil?
25
+ domain = libvirt.servers.get(machine.id)
26
+ if domain.nil?
28
27
  # The machine can't be found
29
28
  @logger.info("Machine couldn't be found, assuming it got destroyed.")
30
29
  machine.id = nil
31
30
  return nil
32
31
  end
33
32
 
34
- # Get ip address of machine
35
- ip_address = server.public_ip_address
36
- ip_address = server.private_ip_address if ip_address == nil
37
- return nil if ip_address == nil
33
+ # IP address of machine is stored in $data_dir/ip file. Why? Commands
34
+ # like ssh or provision need to get IP of VM long time after it was
35
+ # started and gathered IP. Record in arp table is lost, and this is
36
+ # the way how to store this info. Not an ideal solution, but libvirt
37
+ # doesn't provide way how to get IP of some domain.
38
+ ip_file = machine.data_dir + 'ip'
39
+ raise Errors::NoIpAddressError if not File.exists?(ip_file)
40
+ ip_address = File.open(ip_file, 'r') do |file|
41
+ file.read
42
+ end
43
+
44
+ # Check if stored IP address matches with MAC address of machine.
45
+ # Command is executed either localy, or on remote libvirt hypervisor,
46
+ # depends on establised fog libvirt connection.
47
+ ip_match = false
48
+ ip_command = "ping -c1 #{ip_address} > /dev/null && "
49
+ ip_command << "arp -an | grep $mac | sed '"
50
+ ip_command << 's/.*(\([0-9\.]*\)).*/\1/' + "'"
51
+ options_hash = { :ip_command => ip_command }
52
+ 3.times do |x|
53
+ break if ip_match
54
+ domain.wait_for(1) {
55
+ begin
56
+ addresses(service, options_hash).each_pair do |type, ip|
57
+ if ip[0] != nil
58
+ ip_match = true
59
+ break
60
+ end
61
+ end
62
+ rescue Fog::Errors::Error
63
+ # Sometimes, if pinging happen too quickly after after IP
64
+ # assignment, machine is not responding yet. Give it a little
65
+ # time..
66
+ sleep 1
67
+ end
68
+
69
+ break if ip_match
70
+ }
71
+ end
72
+ raise Errors::IpAddressMismatchError if not ip_match
38
73
 
39
74
  # Return the info
40
75
  # TODO: Some info should be configurable in Vagrantfile
@@ -43,8 +78,7 @@ module VagrantPlugins
43
78
  :port => 22,
44
79
  :username => 'root',
45
80
  }
46
- end
47
-
81
+ end
48
82
  end
49
83
  end
50
84
  end
@@ -13,7 +13,6 @@ module VagrantPlugins
13
13
 
14
14
  def call(env)
15
15
  env[:machine_state_id] = read_state(env[:libvirt_compute], env[:machine])
16
-
17
16
  @app.call(env)
18
17
  end
19
18
 
@@ -0,0 +1,27 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module Libvirt
5
+ module Action
6
+ # Resume suspended domain.
7
+ class ResumeDomain
8
+ def initialize(app, env)
9
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::resume_domain")
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ env[:ui].info(I18n.t("vagrant_libvirt.resuming_domain"))
15
+
16
+ domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
17
+ raise Errors::NoDomainError if domain == nil
18
+
19
+ domain.resume
20
+ @logger.info("Machine #{env[:machine].id} is resumed.")
21
+
22
+ @app.call(env)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module Libvirt
5
+ module Action
6
+ # Suspend domain.
7
+ class SuspendDomain
8
+ def initialize(app, env)
9
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::suspend_domain")
10
+ @app = app
11
+ end
12
+
13
+ # make pause
14
+ def call(env)
15
+ env[:ui].info(I18n.t("vagrant_libvirt.suspending_domain"))
16
+
17
+ domain = env[:libvirt_compute].servers.get(env[:machine].id.to_s)
18
+ raise Errors::NoDomainError if domain == nil
19
+
20
+ domain.suspend
21
+ @logger.info("Machine #{env[:machine].id} is suspended ")
22
+
23
+ @app.call(env)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -47,6 +47,13 @@ module VagrantPlugins
47
47
  @logger.info("Got IP address #{env[:ip_address]}")
48
48
  @logger.info("Time for getting IP: #{env[:metrics]["instance_ip_time"]}")
49
49
 
50
+ # Save newly assigned IP address to machines data_dir
51
+ ip_file_path = env[:machine].data_dir + 'ip'
52
+ @logger.info("Saving IP address to #{ip_file_path} file.")
53
+ File.open(ip_file_path, 'w') do |file|
54
+ file.write(env[:ip_address])
55
+ end
56
+
50
57
  # Machine has ip address assigned, now wait till we are able to
51
58
  # connect via ssh.
52
59
  env[:metrics]["instance_ssh_time"] = Util::Timer.time do
@@ -85,6 +85,15 @@ module VagrantPlugins
85
85
  class AttachDeviceError < VagrantLibvirtError
86
86
  error_key(:attach_device_error)
87
87
  end
88
+
89
+ class NoIpAddressError < VagrantLibvirtError
90
+ error_key(:no_ip_address_error)
91
+ end
92
+
93
+ class IpAddressMismatchError < VagrantLibvirtError
94
+ error_key(:ip_address_mismatch_error)
95
+ end
96
+
88
97
  end
89
98
  end
90
99
  end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Libvirt
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
data/locales/en.yml CHANGED
@@ -1,13 +1,17 @@
1
1
  en:
2
2
  vagrant_libvirt:
3
3
  already_created: |-
4
- The machine is already created.
4
+ The domain is already created.
5
5
  not_created: |-
6
- Machine is not created. Please run `vagrant up` first.
6
+ Domain is not created. Please run `vagrant up` first.
7
+ not_running: |-
8
+ Domain is not running. Please run `vagrant up` or `vagrant resume` first.
9
+ not_suspended: |-
10
+ Domain is not suspended.
7
11
  finding_volume: |-
8
12
  Checking if volume is available.
9
13
  creating_domain: |-
10
- Creating machine with the following settings...
14
+ Creating domain with the following settings...
11
15
  uploading_volume: |-
12
16
  Uploading base box image as volume into libvirt storage...
13
17
  creating_domain_volume: |-
@@ -15,17 +19,23 @@ en:
15
19
  removing_domain_volume: |-
16
20
  Removing image (snapshot of base box volume).
17
21
  starting_domain: |-
18
- Starting machine.
22
+ Starting domain.
19
23
  terminating: |-
20
- Removing machine...
24
+ Removing domain...
21
25
  poweroff_domain: |-
22
- Poweroff machine.
26
+ Poweroff domain.
23
27
  destroy_domain: |-
24
- Removing machine...
28
+ Removing domain...
29
+ halt_domain: |-
30
+ Halting domain...
31
+ resuming_domain: |-
32
+ Resuming domain...
33
+ suspending_domain: |-
34
+ Suspending domain...
25
35
  waiting_for_ready: |-
26
- Waiting for machine to become "ready"...
36
+ Waiting for domain to become "ready"...
27
37
  waiting_for_ip: |-
28
- Waiting for machine to get an IP address...
38
+ Waiting for domain to get an IP address...
29
39
  waiting_for_ssh: |-
30
40
  Waiting for SSH to become available...
31
41
  booted: |-
@@ -89,8 +99,18 @@ en:
89
99
  No domain found. %{error_message}
90
100
  attach_device_error: |-
91
101
  Error while attaching new device to domain. %{error_message}
102
+ no_ip_address_error: |-
103
+ No IP address found.
104
+ ip_address_mismatch_error: |-
105
+ IP address mismatch. Possible reason is that domain IP address changed.
106
+ You can run `vagrant destroy` and then `vagrant up` to rebuild your
107
+ project.
92
108
 
93
109
  states:
110
+ short_paused: |-
111
+ pause
112
+ short_shutoff: |-
113
+ shutoff
94
114
  short_not_created: |-
95
115
  not created
96
116
  long_not_created: |-
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-libvirt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Lukas Stanek
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-03-26 00:00:00.000000000 Z
12
+ date: 2013-04-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: fog
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: ruby-libvirt
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rake
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ! '>='
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ! '>='
53
60
  - !ruby/object:Gem::Version
@@ -69,20 +76,28 @@ files:
69
76
  - example_box/metadata.json
70
77
  - lib/vagrant-libvirt.rb
71
78
  - lib/vagrant-libvirt/action.rb
79
+ - lib/vagrant-libvirt/action/cleanup_data_dir.rb
72
80
  - lib/vagrant-libvirt/action/connect_libvirt.rb
73
81
  - lib/vagrant-libvirt/action/create_domain.rb
74
82
  - lib/vagrant-libvirt/action/create_domain_volume.rb
75
83
  - lib/vagrant-libvirt/action/create_network_interfaces.rb
76
84
  - lib/vagrant-libvirt/action/destroy_domain.rb
85
+ - lib/vagrant-libvirt/action/halt_domain.rb
77
86
  - lib/vagrant-libvirt/action/handle_box_image.rb
78
87
  - lib/vagrant-libvirt/action/handle_storage_pool.rb
79
88
  - lib/vagrant-libvirt/action/is_created.rb
89
+ - lib/vagrant-libvirt/action/is_running.rb
90
+ - lib/vagrant-libvirt/action/is_suspended.rb
80
91
  - lib/vagrant-libvirt/action/message_already_created.rb
81
92
  - lib/vagrant-libvirt/action/message_not_created.rb
93
+ - lib/vagrant-libvirt/action/message_not_running.rb
94
+ - lib/vagrant-libvirt/action/message_not_suspended.rb
82
95
  - lib/vagrant-libvirt/action/read_ssh_info.rb
83
96
  - lib/vagrant-libvirt/action/read_state.rb
97
+ - lib/vagrant-libvirt/action/resume_domain.rb
84
98
  - lib/vagrant-libvirt/action/set_name_of_domain.rb
85
99
  - lib/vagrant-libvirt/action/start_domain.rb
100
+ - lib/vagrant-libvirt/action/suspend_domain.rb
86
101
  - lib/vagrant-libvirt/action/sync_folders.rb
87
102
  - lib/vagrant-libvirt/action/timed_provision.rb
88
103
  - lib/vagrant-libvirt/action/wait_till_up.rb
@@ -103,25 +118,26 @@ files:
103
118
  - vagrant-libvirt.gemspec
104
119
  homepage: http://www.vagrantup.com
105
120
  licenses: []
106
- metadata: {}
107
121
  post_install_message:
108
122
  rdoc_options: []
109
123
  require_paths:
110
124
  - lib
111
125
  required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
112
127
  requirements:
113
128
  - - ! '>='
114
129
  - !ruby/object:Gem::Version
115
130
  version: '0'
116
131
  required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
117
133
  requirements:
118
134
  - - ! '>='
119
135
  - !ruby/object:Gem::Version
120
136
  version: '0'
121
137
  requirements: []
122
138
  rubyforge_project:
123
- rubygems_version: 2.0.3
139
+ rubygems_version: 1.8.25
124
140
  signing_key:
125
- specification_version: 4
141
+ specification_version: 3
126
142
  summary: Vagrant provider for libvirt.
127
143
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 857a7572e2968583b54a002899aeb4ed206ce1a9
4
- data.tar.gz: b2272a7a10779ccbaef6f5c8aa1eda3d7cc77d9f
5
- SHA512:
6
- metadata.gz: 1eb41e05a79497bd68f8b8616d75b61ed8a4e7d4a9787ea90ec5ef0a061847f8ca12e2b6f80e3d95b941a6483cd2ef21df48ffd020cdda1949e4ea5d30784c2e
7
- data.tar.gz: cb6363c8c70efaac9b3ed73cdace2ef42c658b946571a5d5bc8693144273af6612b0afdcc79ca8de69b685380fc33c7b009ca46bcae8b1de0cf9d5ce37125e91