xenapi 0.2.7

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