xenapi 0.2.7

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/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ lib/**/*.rb -
2
+ LICENSE HISTORY
data/HISTORY ADDED
@@ -0,0 +1,45 @@
1
+ = XenApi History
2
+
3
+ === 0.2.7 - 2011-05-09
4
+ * Add specific Exceptions for Xen API method errors. These are kept under XenApi::Errors.
5
+ * Massive internal cleanup and documentation
6
+
7
+ === 0.2.6 - 2011-05-06
8
+ * [Enhancement]: Changed XenApi::Client#after_login to return self. Allows for adding
9
+ an #after_login block while creating the XenApi::Client such as
10
+
11
+ client = XenApi::Client.new("http://192.168.1.2").after_login do |c|
12
+ c.event.register(['vm'])
13
+ end
14
+
15
+ === 0.2.5 - 2010-02-16
16
+ * [BugFix]: Fix issues with API calls which are passed arrays as their arguments
17
+
18
+ === 0.2.4 - 2010-02-16
19
+ * [BugFix]: Fix undef of clone method on Dispatcher
20
+
21
+ === 0.2.3 - 2010-02-16
22
+ * [BugFix]: Reattempted calls would fail after a re-login occurred
23
+
24
+ === 0.2.2 - 2010-02-16
25
+ * Prevent the Ruby :clone method from masking the API method
26
+
27
+ === 0.2.1 - 2010-02-15
28
+ * Allow for calling the after_login with the client object
29
+
30
+ === 0.2.0 - 2010-02-15
31
+ * Add after_login callback
32
+ * Expose Session as xenapi_session
33
+
34
+ === 0.1.2 - 2010-02-15
35
+ * Prevent explosions when an empty path is provided to the XMLRPC::Client
36
+
37
+ === 0.1.1 - 2010-02-15
38
+ * Codename: I find your lack of tests disturbing
39
+ * Correct async => Async
40
+
41
+ === 0.1.0 - 2010-02-15
42
+ * Added support for 'async' prefixed requests
43
+
44
+ === 0.0.0 - 2010-02-11
45
+ * Initial Release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Geoff Garside
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ = XenApi
2
+
3
+ Xen API XMLRPC Client library for working with XenServers
4
+
5
+ == Quick Example
6
+
7
+ In this quick example we connect, login and invoke an API method.
8
+
9
+ client = XenApi::Client.new('http://xenapi.test')
10
+ client.login_with_password('root', 'password')
11
+ client.VM.get_all
12
+
13
+ This will likely be enough information to get you on your feet with
14
+ the API, for more details see {XenApi::Client}.
15
+
16
+ == Note on Patches/Pull Requests
17
+
18
+ * Fork the project.
19
+ * Make your feature addition or bug fix.
20
+ * Add tests for it. This is important so I don't break it in a
21
+ future version unintentionally.
22
+ * Commit, do not mess with rakefile, version, or history.
23
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
24
+ * Send me a pull request. Bonus points for topic branches.
25
+
26
+ == Copyright
27
+
28
+ Copyright (c) 2010 Geoff Garside. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "xenapi"
8
+ gem.summary = %Q{Xen API XMLRPC Client}
9
+ gem.description = %Q{Xen API XMLRPC Client library for working with XenServers}
10
+ gem.email = "geoff+xenapi@geoffgarside.co.uk"
11
+ gem.homepage = "http://github.com/geoffgarside/xenapi"
12
+ gem.authors = ["Geoff Garside"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "xenapi #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
54
+
55
+ begin
56
+ require 'yard'
57
+ YARD::Rake::YardocTask.new { |y| y.options += %w(--no-private) }
58
+ YARD::Rake::YardocTask.new('yard:dev') { |y| y.options += %w(--protected --private) }
59
+ rescue LoadError
60
+ task :yardoc do
61
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
62
+ end
63
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.7
data/lib/xen_api.rb ADDED
@@ -0,0 +1 @@
1
+ require 'xenapi'
data/lib/xenapi.rb ADDED
@@ -0,0 +1,6 @@
1
+ module XenApi #:nodoc:
2
+ autoload :Client, File.expand_path('../xenapi/client', __FILE__)
3
+ autoload :Errors, File.expand_path('../xenapi/errors', __FILE__)
4
+ autoload :Dispatcher, File.expand_path('../xenapi/dispatcher', __FILE__)
5
+ autoload :AsyncDispatch, File.expand_path('../xenapi/async_dispatcher', __FILE__)
6
+ end
@@ -0,0 +1,36 @@
1
+ module XenApi #:nodoc:
2
+ # @private
3
+ # This class helps to provide the ability for the +XenApi::Client+
4
+ # to accept +async+ method calls. Calls are similar to synchronous
5
+ # method calls except that the names are prefixed with 'Async'.
6
+ #
7
+ # client = XenApi::Client.new('http://xenapi.test/')
8
+ # client.async #=> AsyncDispatcher instance
9
+ # client.async.VM #=> Dispatcher instance for 'Async.VM'
10
+ # client.async.VM.start() #=> Performs XMLRPC 'Async.VM.start' call
11
+ #
12
+ # further calls on instances of this object will create a +Dispatcher+
13
+ # instance which then handle actual method calls.
14
+ class AsyncDispatcher
15
+ # @param [Client] client XenApi::Client instance
16
+ # @param [Symbol] sender XenApi::Client method to call when prefix method is invoked
17
+ def initialize(client, sender)
18
+ @client = client
19
+ @sender = sender
20
+ end
21
+
22
+ # @see Object#inspect
23
+ def inspect
24
+ "#<#{self.class}>"
25
+ end
26
+
27
+ # Create a new +Dispatcher+ instance to handle the +Async.meth+ prefix.
28
+ #
29
+ # @param [String,Symbol] meth Method prefix name
30
+ # @param [...] args Method arguments
31
+ # @return [Dispatcher] dispatcher instance to handle the +Async.meth+ prefix
32
+ def method_missing(meth, *args)
33
+ Dispatcher.new(@client, "Async.#{meth}", @sender)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,258 @@
1
+ require 'uri'
2
+ require 'xmlrpc/client'
3
+
4
+ module XenApi #:nodoc:
5
+ # This class permits the invocation of XMLRPC API calls
6
+ # through a ruby-like interface
7
+ #
8
+ # client = XenApi::Client.new('http://xenapi.test')
9
+ # client.login_with_password('root', 'password')
10
+ # client.VM.get_all
11
+ #
12
+ # == Authenticating with the API
13
+ # Authentication with the API takes place through the API
14
+ # +session+ class, usually using the +login_with_password+
15
+ # method. The +Client+ handles this method specially to
16
+ # enable it to retain the session identifier to pass to
17
+ # invoked methods and perform reauthentication should the
18
+ # session become stale.
19
+ #
20
+ # client = XenApi::Client.new('http://xenapi.test')
21
+ # client.login_with_password('root', 'password')
22
+ #
23
+ # It is worth noting that only +login*+ matching methods
24
+ # are specially passed through to the +session+ class.
25
+ #
26
+ # == Running code after API login
27
+ # The +Client+ provides the ability for running code
28
+ # after the client has successfully authenticated with
29
+ # the API. This is useful for either logging authentication
30
+ # or for registering for certain information from the API.
31
+ #
32
+ # The best example of this is when needing to make use of
33
+ # the Xen API +event+ class for asynchronous event handling.
34
+ # To use the API +event+ class you first have to register
35
+ # your interest in a specific set of event types.
36
+ #
37
+ # client = XenApi::Client.new('http://xenapi.test')
38
+ # client.after_login do |c|
39
+ # c.event.register %w(vm) # register for 'vm' events
40
+ # end
41
+ #
42
+ # == Asynchronous Methods
43
+ # To call asynchronous methods on the Xen XMLRPC API you
44
+ # first call +Async+ on the +Client+ instance followed by
45
+ # the normal method name.
46
+ # For example:
47
+ #
48
+ # client = XenApi::Client.new('http://xenapi.test')
49
+ # client.login_with_password('root', 'password')
50
+ # client.Async.VM.get_all
51
+ # client.async.VM.get_all
52
+ #
53
+ # Calling either +Async+ or +async+ will work as the
54
+ # capitalised form will always be sent when calling
55
+ # a method asynchronously.
56
+ class Client
57
+ # The +LoginRequired+ exception is raised when
58
+ # an API request requires login and no login
59
+ # credentials have yet been provided.
60
+ #
61
+ # If you don't perform a login before receiving this
62
+ # exception then you will want to catch it, log into
63
+ # the API and then retry your request.
64
+ class LoginRequired < RuntimeError; end
65
+
66
+ # The +SessionInvalid+ exception is raised when the
67
+ # API session has become stale or is otherwise invalid.
68
+ #
69
+ # Internally this exception will be handled a number of
70
+ # times before being raised up to the calling code.
71
+ class SessionInvalid < RuntimeError; end
72
+
73
+ # The +ResponseMissingStatusField+ exception is raised
74
+ # when the XMLRPC response is missing the +Status+ field.
75
+ # This typically indicates an unrecoverable error with
76
+ # the API itself.
77
+ class ResponseMissingStatusField < RuntimeError; end
78
+
79
+ # The +ResponseMissingValueField+ exception is raised
80
+ # when the XMLRPC response is missing the +Value+ field.
81
+ # This typically indicates an unrecoverable error with
82
+ # the API itself.
83
+ class ResponseMissingValueField < RuntimeError; end
84
+
85
+ # The +ResponseMissingErrorDescriptionField+ exception
86
+ # is raised when an error is returned in the XMLRPC
87
+ # response, but the type of error cannot be determined
88
+ # due to the lack of the +ErrorDescription+ field.
89
+ class ResponseMissingErrorDescriptionField < RuntimeError; end
90
+
91
+ # @see Object#inspect
92
+ def inspect
93
+ "#<#{self.class} #{@uri}>"
94
+ end
95
+
96
+ # @param [String] uri URL to the Xen API endpoint
97
+ # @param [Integer] timeout Maximum number of seconds to wait for an API response
98
+ def initialize(uri, timeout = 10)
99
+ @timeout = timeout
100
+ @uri = URI.parse(uri)
101
+ @uri.path = '/' if @uri.path == ''
102
+ end
103
+
104
+ # @overload after_login
105
+ # Adds a block to be called after successful login to the XenAPI.
106
+ # @note The block will be called whenever the receiver has to authenticate
107
+ # with the XenAPI. This includes the first time the receiver recieves a
108
+ # +login_*+ method call and any time the session becomes invalid.
109
+ # @yield client
110
+ # @yieldparam [optional, Client] client Client instance
111
+ # @overload after_login
112
+ # Calls the created block, this is primarily for internal use only
113
+ # @return [Client] receiver
114
+ def after_login(&block)
115
+ if block
116
+ @after_login = block
117
+ elsif @after_login
118
+ case @after_login.arity
119
+ when 1
120
+ @after_login.call(self)
121
+ else
122
+ @after_login.call
123
+ end
124
+ end
125
+ self
126
+ end
127
+
128
+ # Returns the current session identifier.
129
+ #
130
+ # @return [String] session identifier
131
+ def xenapi_session
132
+ @session
133
+ end
134
+
135
+ # Handle API method calls.
136
+ #
137
+ # If the method called starts with +login+ then the method is
138
+ # assumed to be part of the +session+ namespace and will be
139
+ # called directly. For example +login_with_password+
140
+ #
141
+ # client = XenApi::Client.new('http://xenapi.test/')
142
+ # client.login_with_password('root', 'password)
143
+ #
144
+ # If the method called is +async+ then an +AsyncDispatcher+
145
+ # will be created to handle the asynchronous API method call.
146
+ #
147
+ # client = XenApi::Client.new('http://xenapi.test/')
148
+ # client.async.host.get_servertime(ref)
149
+ #
150
+ # The final case will create a +Dispatcher+ to handle the
151
+ # subsequent method call such as.
152
+ #
153
+ # client = XenApi::Client.new('http://xenapi.test/')
154
+ # client.host.get_servertime(ref)
155
+ #
156
+ # @note +meth+ names are not validated
157
+ #
158
+ # @param [String,Symbol] meth Method name
159
+ # @param [...] args Method args
160
+ # @return [true,AsyncDispatcher,Dispatcher]
161
+ def method_missing(meth, *args)
162
+ case meth.to_s
163
+ when /^login/
164
+ _login(meth, *args)
165
+ when /^async/i
166
+ AsyncDispatcher.new(self, :_call)
167
+ else
168
+ Dispatcher.new(self, meth, :_call)
169
+ end
170
+ end
171
+ protected
172
+ # @param [String,Symbol] meth API method to call
173
+ # @param [Array] args Arguments to pass to the method call
174
+ # @raise [SessionInvalid] Reauthentication failed
175
+ # @raise [LoginRequired] Authentication required, unable to login automatically
176
+ # @raise [EOFError] XMLRPC::Client exception
177
+ # @raise [Errno::EPIPE] XMLRPC::Client exception
178
+ def _call(meth, *args)
179
+ begin
180
+ _do_call(meth, args.dup.unshift(@session))
181
+ rescue SessionInvalid
182
+ _relogin_attempts = (_relogin_attempts || 0) + 1
183
+ _relogin
184
+ retry unless _relogin_attempts > 2
185
+ raise
186
+ rescue EOFError
187
+ _eof_retries = (_eof_retries || 0) + 1
188
+ @client = nil
189
+ retry unless _eof_retries > 1
190
+ raise
191
+ rescue Errno::EPIPE
192
+ _epipe_retries = (_epipe_retries || 0) + 1
193
+ @client = nil
194
+ retry unless _epipe_retries > 1
195
+ raise
196
+ end
197
+ end
198
+ private
199
+ # Reauthenticate with the API
200
+ # @raise [LoginRequired] Missing authentication credentials
201
+ def _relogin
202
+ raise LoginRequired if @login_meth.nil? || @login_args.nil? || @login_args.empty?
203
+ _login(@login_meth, *@login_args)
204
+ end
205
+
206
+ # Login to the API
207
+ #
208
+ # @note Will call the +after_login+ block if login is successful
209
+ #
210
+ # @param [String,Symbol] meth Login method name
211
+ # @param [...] args Arguments to pass to the login method
212
+ # @return [Boolean] true
213
+ # @raise [Exception] any exception raised by +_do_call+ or +after_login+
214
+ def _login(meth, *args)
215
+ begin
216
+ @session = _do_call("session.#{meth}", args)
217
+ @login_meth = meth
218
+ @login_args = args
219
+ after_login
220
+ true
221
+ rescue Exception => e
222
+ raise e
223
+ end
224
+ end
225
+
226
+ # Return or initialize new +XMLRPC::Client+
227
+ #
228
+ # @return [XMLRPC::Client] XMLRPC client instance
229
+ def _client
230
+ @client ||= XMLRPC::Client.new(@uri.host, @uri.path, @uri.port, nil, nil, nil, nil, @uri.port == 443, @timeout)
231
+ end
232
+
233
+ # Perform XMLRPC method call.
234
+ #
235
+ # @param [String,Symbol] meth XMLRPC method to call
236
+ # @param [Array] args XMLRPC method arguments
237
+ # @param [Integer] attempts Number of times to retry the call, presently unused
238
+ # @return [Object] method return value
239
+ # @raise [ResponseMissingStatusField] XMLRPC response does not have a +Status+ field
240
+ # @raise [ResponseMissingValueField] XMLRPC response does not have a +Value+ field
241
+ # @raise [ResponseMissingErrorDescriptionField] API response error missing +ErrorDescription+ field
242
+ # @raise [SessionInvalid] API session has expired
243
+ # @raise [Errors::GenericError] API method specific error
244
+ def _do_call(meth, args, attempts = 3)
245
+ r = _client.call(meth, *args)
246
+ raise ResponseMissingStatusField unless r.has_key?('Status')
247
+
248
+ if r['Status'] == 'Success'
249
+ return r['Value'] if r.has_key?('Value')
250
+ raise ResponseMissingValueField
251
+ else
252
+ raise ResponseMissingErrorDescriptionField unless r.has_key?('ErrorDescription')
253
+ raise SessionInvalid if r['ErrorDescription'][0] == 'SESSION_INVALID'
254
+ raise Errors.exception_class_from_desc(r['ErrorDescription'].shift), r['ErrorDescription'].inspect
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,48 @@
1
+ module XenApi #:nodoc:
2
+ # @private
3
+ # This class helps to provide XMLRPC method dispatching.
4
+ #
5
+ # Calls made to the top level +XenApi::Client+ instance
6
+ # will generate instances of this class to provide scoping
7
+ # of methods by their prefix. All Xen API method calls are
8
+ # two level, the first level specifies a namespace or prefix
9
+ # for the second level method call. Taking +VM.start+ as
10
+ # an example, +VM+ is the namespace prefix and +start+ is
11
+ # the method name.
12
+ #
13
+ # Calling Xen API XMLRPC methods therefore consists of
14
+ # first creating a +Dispatcher+ instance with the prefix
15
+ # name and then calling a method on the +Dispatcher+
16
+ # instance to create the XMLRPC method name to be called
17
+ # by the +XenApi::Client+ instance.
18
+ #
19
+ # client = XenApi::Client.new('http://xenapi.test/')
20
+ # client.VM #=> Dispatcher instance for 'VM'
21
+ # client.VM.start() #=> Performs XMLRPC 'VM.start' call
22
+ class Dispatcher
23
+ undef :clone # to allow for VM.clone calls
24
+
25
+ # @param [Client] client XenApi::Client instance
26
+ # @param [String] prefix Method prefix name
27
+ # @param [Symbol] sender XenApi::Client method to call when prefix method is invoked
28
+ def initialize(client, prefix, sender)
29
+ @client = client
30
+ @prefix = prefix
31
+ @sender = sender
32
+ end
33
+
34
+ # @see Object#inspect
35
+ def inspect
36
+ "#<#{self.class} #{@prefix}>"
37
+ end
38
+
39
+ # Calls a method on +XenApi::Client+ to perform the XMLRPC method
40
+ #
41
+ # @param [String,Symbol] meth Method name to be combined with the receivers +prefix+
42
+ # @param [...] args Method arguments
43
+ # @return [Object] XMLRPC response value
44
+ def method_missing(meth, *args)
45
+ @client.send(@sender, "#{@prefix}.#{meth}", *args)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,451 @@
1
+ module XenApi #:nodoc:
2
+ module Errors #:nodoc:
3
+ # Generic errror case, all XenApi exceptions inherit from this for ease of catching
4
+ class GenericError < RuntimeError; end
5
+
6
+ # The bootloader returned an error.
7
+ #
8
+ # Raised by
9
+ # - VM.start
10
+ # - VM.start_on
11
+ class BootloaderFailed < GenericError; end
12
+
13
+ # The device is not currently attached
14
+ #
15
+ # Raised by
16
+ # - VBD.unplug
17
+ class DeviceAlreadyDetached < GenericError; end
18
+
19
+ # The VM rejected the attempt to detach the device.
20
+ #
21
+ # Raised by
22
+ # - VBD.unplug
23
+ class DeviceDetachRejected < GenericError; end
24
+
25
+ # Some events have been lost from the queue and cannot be retrieved.
26
+ #
27
+ # Raised by
28
+ # - event.next
29
+ class EventsLost < GenericError; end
30
+
31
+ # This operation cannot be performed because it would invalidate VM
32
+ # failover planning such that the system would be unable to guarantee
33
+ # to restart protected VMs after a Host failure.
34
+ #
35
+ # Raised by
36
+ # - VM.set_memory_static_max
37
+ class HAOperationWouldBreakFailoverPlan < GenericError; end
38
+
39
+ # The host name is invalid
40
+ #
41
+ # Raised by
42
+ # - host.set_hostname_live
43
+ class HostNameInvalid < GenericError; end
44
+
45
+ # Not enough host memory is available to perform this operation
46
+ #
47
+ # Raised by
48
+ # - VM.assert_can_boot_here
49
+ class HostNotEnoughFreeMemory < GenericError; end
50
+
51
+ # You tried to create a VLAN or tunnel on top of a tunnel access
52
+ # PIF - use the underlying transport PIF instead.
53
+ #
54
+ # Raised by
55
+ # - tunnel.create
56
+ class IsTunnelAccessPIF < GenericError; end
57
+
58
+ # The host joining the pool cannot contain any shared storage.
59
+ #
60
+ # Raised by
61
+ # - pool.join
62
+ class JoiningHostCannotContainSharedSRs < GenericError; end
63
+
64
+ # This operation is not allowed under your license.
65
+ # Please contact your support representative.
66
+ #
67
+ # Raised by
68
+ # - VM.start
69
+ class LicenceRestriction < GenericError; end
70
+
71
+ # There was an error processing your license. Please contact
72
+ # your support representative.
73
+ #
74
+ # Raised by
75
+ # - host.license_apply
76
+ class LicenseProcessingError < GenericError; end
77
+
78
+ # There were no hosts available to complete the specified operation.
79
+ #
80
+ # Raised by
81
+ # - VM.start
82
+ class NoHostsAvailable < GenericError; end
83
+
84
+ # This operation needs the OpenVSwitch networking backend to be
85
+ # enabled on all hosts in the pool.
86
+ #
87
+ # Raised by
88
+ # - tunnel.create
89
+ class OpenvswitchNotActive < GenericError; end
90
+
91
+ # You attempted an operation that was not allowed.
92
+ #
93
+ # Raised by
94
+ # - task.cancel
95
+ # - VM.checkpoint
96
+ # - VM.clean_reboot
97
+ # - VM.clean_shutdown
98
+ # - VM.clone
99
+ # - VM.copy
100
+ # - VM.hard_reboot
101
+ # - VM.hard_shutdown
102
+ # - VM.pause
103
+ # - VM.pool_migrate
104
+ # - VM.provision
105
+ # - VM.resume
106
+ # - VM.resume_on
107
+ # - VM.revert
108
+ # - VM.snapshot
109
+ # - VM.snapshot_with_quiesce
110
+ # - VM.start
111
+ # - VM.start_on
112
+ # - VM.suspend
113
+ # - VM.unpause
114
+ class OperationNotAllowed < GenericError; end
115
+
116
+ # Another operation involving the object is currently in progress
117
+ #
118
+ # Raised by
119
+ # - VM.clean_reboot
120
+ # - VM.clean_shutdown
121
+ # - VM.hard_reboot
122
+ # - VM.hard_shutdown
123
+ # - VM.pause
124
+ # - VM.pool_migrate
125
+ # - VM.start
126
+ # - VM.start_on
127
+ # - VM.suspend
128
+ class OtherOperationInProgress < GenericError; end
129
+
130
+ # You tried to destroy a PIF, but it represents an aspect of the physical
131
+ # host configuration, and so cannot be destroyed. The parameter echoes the
132
+ # PIF handle you gave.
133
+ #
134
+ # Raised by
135
+ # - PIF.destroy
136
+ # @deprecated the PIF.destroy method is deprecated in XenServer 4.1 and replaced
137
+ # by VLAN.destroy and Bond.destroy
138
+ class PIFIsPhysical < GenericError; end
139
+
140
+ # Operation cannot proceed while a tunnel exists on this interface.
141
+ #
142
+ # Raised by
143
+ # - PIF.forget
144
+ class PIFTunnelStillExists < GenericError; end
145
+
146
+ # The credentials given by the user are incorrect, so access has been
147
+ # denied, and you have not been issued a session handle.
148
+ #
149
+ # Raised by
150
+ # - session.login_with_password
151
+ class SessionAuthenticationFailed < GenericError; end
152
+
153
+ # This session is not registered to receive events. You must call
154
+ # event.register before event.next. The session handle you are
155
+ # using is echoed.
156
+ #
157
+ # Raised by
158
+ # - event.next
159
+ class SessionNotRegistered < GenericError; end
160
+
161
+ # The SR is full. Requested new size exceeds the maximum size
162
+ #
163
+ # Raised by
164
+ # - VM.checkpoint
165
+ # - VM.clone
166
+ # - VM.copy
167
+ # - VM.provision
168
+ # - VM.revert
169
+ # - VM.snapshot
170
+ # - VM.snapshot_with_quiesce
171
+ class SRFull < GenericError; end
172
+
173
+ # The SR is still connected to a host via a PBD. It cannot be destroyed.
174
+ #
175
+ # Raised by
176
+ # - SR.destroy
177
+ # - SR.forget
178
+ class SRHasPDB < GenericError; end
179
+
180
+ # The SR backend does not support the operation (check the SR's allowed operations)
181
+ #
182
+ # Raised by
183
+ # - VDI.introduce
184
+ # - VDI.update
185
+ class SROperationNotSupported < GenericError; end
186
+
187
+ # The SR could not be connected because the driver was not recognised.
188
+ #
189
+ # Raised by
190
+ # - PBD.plug
191
+ # - SR.create
192
+ class SRUnknownDriver < GenericError; end
193
+
194
+ # The tunnel transport PIF has no IP configuration set.
195
+ #
196
+ # Raised by
197
+ # - PIF.plug
198
+ # - tunnel.create
199
+ class TransportPIFNotConfigured < GenericError; end
200
+
201
+ # The requested bootloader is unknown
202
+ #
203
+ # Raised by
204
+ # - VM.start
205
+ # - VM.start_on
206
+ class UnknownBootloader < GenericError; end
207
+
208
+ # Operation could not be performed because the drive is empty
209
+ #
210
+ # Raised by
211
+ # - VBD.eject
212
+ class VBDIsEmpty < GenericError; end
213
+
214
+ # Operation could not be performed because the drive is not empty
215
+ #
216
+ # Raised by
217
+ # - VBD.insert
218
+ class VBDNotEmpty < GenericError; end
219
+
220
+ # Media could not be ejected because it is not removable
221
+ #
222
+ # Raised by
223
+ # - VBD.eject
224
+ # - VBD.insert
225
+ class VBDNotRemovableMedia < GenericError; end
226
+
227
+ # You tried to create a VLAN, but the tag you gave was invalid --
228
+ # it must be between 0 and 4094. The parameter echoes the VLAN tag you gave.
229
+ #
230
+ # Raised by
231
+ # - PIF.create_VLAN (deprecated)
232
+ # - pool.create_VLAN (deprecated)
233
+ # - pool.create_VLAN_from_PIF
234
+ class VlanTagInvalid < GenericError; end
235
+
236
+ # You attempted an operation on a VM that was not in an appropriate
237
+ # power state at the time; for example, you attempted to start a VM
238
+ # that was already running. The parameters returned are the VM's
239
+ # handle, and the expected and actual VM state at the time of the call.
240
+ #
241
+ # Raised by
242
+ # - VM.checkpoint
243
+ # - VM.clean_reboot
244
+ # - VM.clean_shutdown
245
+ # - VM.checkpoint
246
+ # - VM.clean_reboot
247
+ # - VM.clean_shutdown
248
+ # - VM.clone
249
+ # - VM.copy
250
+ # - VM.hard_reboot
251
+ # - VM.hard_shutdown
252
+ # - VM.pause
253
+ # - VM.pool_migrate
254
+ # - VM.provision
255
+ # - VM.resume
256
+ # - VM.resume_on
257
+ # - VM.revert
258
+ # - VM.send_sysrq
259
+ # - VM.send_trigger
260
+ # - VM.snapshot
261
+ # - VM.snapshot_with_quiesce
262
+ # - VM.start
263
+ # - VM.start_on
264
+ # - VM.suspend
265
+ # - VM.unpause
266
+ class VMBadPowerState < GenericError; end
267
+
268
+ # An error occured while restoring the memory image of the
269
+ # specified virtual machine
270
+ #
271
+ # Raised by
272
+ # - VM.checkpoint
273
+ class VMCheckpointResumeFailed < GenericError; end
274
+
275
+ # An error occured while saving the memory image of the
276
+ # specified virtual machine
277
+ #
278
+ # Raised by
279
+ # - VM.checkpoint
280
+ class VMCheckpointSuspendFailed < GenericError; end
281
+
282
+ # HVM is required for this operation
283
+ #
284
+ # Raised by
285
+ # - VM.start
286
+ class VMHvmRequired < GenericError; end
287
+
288
+ # The operation attempted is not valid for a template VM
289
+ #
290
+ # Raised by
291
+ # - VM.clean_reboot
292
+ # - VM.clean_shutdown
293
+ # - VM.hard_reboot
294
+ # - VM.hard_shutdown
295
+ # - VM.pause
296
+ # - VM.pool_migrate
297
+ # - VM.resume
298
+ # - VM.resume_on
299
+ # - VM.start
300
+ # - VM.start_on
301
+ # - VM.suspend
302
+ # - VM.unpause
303
+ class VMIsTemplate < GenericError; end
304
+
305
+ # An error occurred during the migration process.
306
+ #
307
+ # Raised by
308
+ # - VM.pool_migrate
309
+ class VMMigrateFailed < GenericError; end
310
+
311
+ # You attempted an operation on a VM which requires PV drivers
312
+ # to be installed but the drivers were not detected.
313
+ #
314
+ # Raised by
315
+ # -VM.pool_migrate
316
+ class VMMissingPVDrivers < GenericError; end
317
+
318
+ # You attempted to run a VM on a host which doesn't have access to an SR
319
+ # needed by the VM. The VM has at least one VBD attached to a VDI in the SR
320
+ #
321
+ # Raised by
322
+ # - VM.assert_can_boot_here
323
+ class VMRequiresSR < GenericError; end
324
+
325
+ # An error occured while reverting the specified virtual machine
326
+ # to the specified snapshot
327
+ #
328
+ # Raised by
329
+ # - VM.revert
330
+ class VMRevertFailed < GenericError; end
331
+
332
+ # The quiesced-snapshot operation failed for an unexpected reason
333
+ #
334
+ # Raised by
335
+ # - VM.snapshot_with_quiesce
336
+ class VMSnapshotWithQuiesceFailed < GenericError; end
337
+
338
+ # The VSS plug-in is not installed on this virtual machine
339
+ #
340
+ # Raised by
341
+ # - VM.snapshot_with_quiesce
342
+ class VMSnapshotWithQuiesceNotSupported < GenericError; end
343
+
344
+ # The VSS plug-in cannot be contacted
345
+ #
346
+ # Raised by
347
+ # - VM.snapshot_with_quiesce
348
+ class VMSnapshotWithQuiescePluginDoesNotRespond < GenericError; end
349
+
350
+ # The VSS plug-in has timed out
351
+ #
352
+ # Raised by
353
+ # - VM.snapshot_with_quiesce
354
+ class VMSnapshotWithQuiesceTimeout < GenericError; end
355
+
356
+ # Returns the class for the exception appropriate for the error description given
357
+ #
358
+ # @param [String] desc ErrorDescription value from the API
359
+ # @return [Class] Appropriate exception class for the given description
360
+ def self.exception_class_from_desc(desc)
361
+ case desc
362
+ when 'BOOTLOADER_FAILED'
363
+ BootloaderFailed
364
+ when 'DEVICE_ALREADY_DETACHED'
365
+ DeviceAlreadyDetached
366
+ when 'DEVICE_DETACH_REJECTED'
367
+ DeviceDetachRejected
368
+ when 'EVENTS_LOST'
369
+ EventsLost
370
+ when 'HA_OPERATION_WOULD_BREAK_FAILOVER_PLAN'
371
+ HAOperationWouldBreakFailoverPlan
372
+ when 'HOST_NAME_INVALID'
373
+ HostNameInvalid
374
+ when 'HOST_NOT_ENOUGH_FREE_MEMORY'
375
+ HostNotEnoughFreeMemory
376
+ when 'IS_TUNNEL_ACCESS_PIF'
377
+ IsTunnelAccessPIF
378
+ when 'JOINING_HOST_CANNOT_CONTAIN_SHARED_SRS'
379
+ JoiningHostCannotContainSharedSRs
380
+ when 'LICENCE_RESTRICTION'
381
+ LicenceRestriction
382
+ when 'LICENSE_PROCESSING_ERROR'
383
+ LicenseProcessingError
384
+ when 'NO_HOSTS_AVAILABLE'
385
+ NoHostsAvailable
386
+ when 'OPENVSWITCH_NOT_ACTIVE'
387
+ OpenvswitchNotActive
388
+ when 'OPERATION_NOT_ALLOWED'
389
+ OperationNotAllowed
390
+ when 'OTHER_OPERATION_IN_PROGRESS'
391
+ OtherOperationInProgress
392
+ when 'PIF_IS_PHYSICAL'
393
+ PIFIsPhysical
394
+ when 'PIF_TUNNEL_STILL_EXISTS'
395
+ PIFTunnelStillExists
396
+ when 'SESSION_AUTHENTICATION_FAILED'
397
+ SessionAuthenticationFailed
398
+ when 'SESSION_NOT_REGISTERED'
399
+ SessionNotRegistered
400
+ when 'SR_FULL'
401
+ SRFull
402
+ when 'SR_HAS_PDB'
403
+ SRHasPDB
404
+ when 'SR_OPERATION_NOT_SUPPORTED'
405
+ SROperationNotSupported
406
+ when 'SR_UNKNOWN_DRIVER'
407
+ SRUnknownDriver
408
+ when 'TRANSPORT_PIF_NOT_CONFIGURED'
409
+ TransportPIFNotConfigured
410
+ when 'UNKNOWN_BOOTLOADER'
411
+ UnknownBootloader
412
+ when 'VBD_IS_EMPTY'
413
+ VBDIsEmpty
414
+ when 'VBD_NOT_EMPTY'
415
+ VBDNotEmpty
416
+ when 'VBD_NOT_REMOVABLE_MEDIA'
417
+ VBDNotRemovableMedia
418
+ when 'VLAN_TAG_INVALID'
419
+ VlanTagInvalid
420
+ when 'VM_BAD_POWER_STATE'
421
+ VMBadPowerState
422
+ when 'VM_CHECKPOINT_RESUME_FAILED'
423
+ VMCheckpointResumeFailed
424
+ when 'VM_CHECKPOINT_SUSPEND_FAILED'
425
+ VMCheckpointSuspendFailed
426
+ when 'VM_HVM_REQUIRED'
427
+ VMHVMRequired
428
+ when 'VM_IS_TEMPLATE'
429
+ VMIsTemplate
430
+ when 'VM_MIGRATE_FAILED'
431
+ VMMigrateFailed
432
+ when 'VM_MISSING_PV_DRIVERS'
433
+ VMMissingPVDrivers
434
+ when 'VM_REQUIRES_SR'
435
+ VMRequiresSR
436
+ when 'VM_REVERT_FAILED'
437
+ VMRevertFailed
438
+ when 'VM_SNAPSHOT_WITH_QUIESCE_FAILED'
439
+ VMSnapshotWithQuiesceFailed
440
+ when 'VM_SNAPSHOT_WITH_QUIESCE_NOT_SUPPORTED'
441
+ VMSnapshotWithQuiesceNotSupported
442
+ when 'VM_SNAPSHOT_WITH_QUIESCE_PLUGIN_DOES_NOT_RESPOND'
443
+ VMSnapshotWithQuiescePluginDoesNotRespond
444
+ when 'VM_SNAPSHOT_WITH_QUIESCE_TIMEOUT'
445
+ VMSnapshotWithQuiesceTimeout
446
+ else
447
+ GenericError
448
+ end
449
+ end
450
+ end
451
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'xenapi'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestXenapi < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
data/xenapi.gemspec ADDED
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{xenapi}
8
+ s.version = "0.2.7"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Geoff Garside"]
12
+ s.date = %q{2011-05-09}
13
+ s.description = %q{Xen API XMLRPC Client library for working with XenServers}
14
+ s.email = %q{geoff+xenapi@geoffgarside.co.uk}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".yardopts",
21
+ "HISTORY",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/xen_api.rb",
27
+ "lib/xenapi.rb",
28
+ "lib/xenapi/async_dispatcher.rb",
29
+ "lib/xenapi/client.rb",
30
+ "lib/xenapi/dispatcher.rb",
31
+ "lib/xenapi/errors.rb",
32
+ "test/helper.rb",
33
+ "test/test_xenapi.rb",
34
+ "xenapi.gemspec"
35
+ ]
36
+ s.homepage = %q{http://github.com/geoffgarside/xenapi}
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.7.2}
39
+ s.summary = %q{Xen API XMLRPC Client}
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
46
+ else
47
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
51
+ end
52
+ end
53
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xenapi
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 7
10
+ version: 0.2.7
11
+ platform: ruby
12
+ authors:
13
+ - Geoff Garside
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-09 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ description: Xen API XMLRPC Client library for working with XenServers
35
+ email: geoff+xenapi@geoffgarside.co.uk
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - LICENSE
42
+ - README.rdoc
43
+ files:
44
+ - .yardopts
45
+ - HISTORY
46
+ - LICENSE
47
+ - README.rdoc
48
+ - Rakefile
49
+ - VERSION
50
+ - lib/xen_api.rb
51
+ - lib/xenapi.rb
52
+ - lib/xenapi/async_dispatcher.rb
53
+ - lib/xenapi/client.rb
54
+ - lib/xenapi/dispatcher.rb
55
+ - lib/xenapi/errors.rb
56
+ - test/helper.rb
57
+ - test/test_xenapi.rb
58
+ - xenapi.gemspec
59
+ homepage: http://github.com/geoffgarside/xenapi
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options: []
64
+
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.7.2
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Xen API XMLRPC Client
92
+ test_files: []
93
+