vagrant-libvirt 0.0.1 → 0.0.2

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