rxen 0.1.1

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/README.rdoc ADDED
@@ -0,0 +1,70 @@
1
+ == rxen
2
+
3
+ Ruby wrapper to access the Xen XML-RPC API via simple to use ruby methods, not
4
+ all methods are implemented, yet. They can be easily added by simply extending the
5
+ Session class RPC_METHODS Hash to include the correct Regular Expression mathing the
6
+ call.
7
+
8
+ == example
9
+
10
+ # Get the rxen gem
11
+ require 'rxen'
12
+
13
+ # create a Session with the Xen Server you want to control
14
+ xs = Session.new("https://my.xenserver.company.com/")
15
+
16
+ # login with username and password
17
+ xs.login_with_password("root", "password")
18
+
19
+ # Some example calls, all possible calls can be found in the XenAPI
20
+ # Documentation. Calls are build by class_function(parameters), it is not needed
21
+ # to pass the session id since it is done automatically.
22
+ # Get IDs for the VMs running on the Server
23
+ xs.VM_get_all()
24
+
25
+ # register for all task events
26
+ xs.event_register(["task"])
27
+
28
+ # get the next event from the queue
29
+ xs.event_next()
30
+
31
+ # unregister from the task events
32
+ xs.event_unregister(["task"])
33
+
34
+ # logout of the XenServer
35
+ xs.logout()
36
+
37
+ Alternativly config based login can be done via
38
+
39
+ xs = Session.new_with_config("configfile.json")
40
+
41
+ == license
42
+
43
+ (the BSD license)
44
+
45
+ Copyright 2010 Philipp Fehre. All rights reserved.
46
+
47
+ Redistribution and use in source and binary forms, with or without modification, are
48
+ permitted provided that the following conditions are met:
49
+
50
+ 1. Redistributions of source code must retain the above copyright notice, this list of
51
+ conditions and the following disclaimer.
52
+
53
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
54
+ of conditions and the following disclaimer in the documentation and/or other materials
55
+ provided with the distribution.
56
+
57
+ THIS SOFTWARE IS PROVIDED BY PHILIPP FEHRE ``AS IS'' AND ANY EXPRESS OR IMPLIED
58
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
59
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
60
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
62
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
63
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
64
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
65
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
+
67
+ The views and conclusions contained in the software and documentation are those of the
68
+ authors and should not be interpreted as representing official policies, either expressed
69
+ or implied, of Philipp Fehre.
70
+
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
4
+ require 'rake/gempackagetask'
5
+ require 'rubygems/specification'
6
+
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = "rxen"
10
+ s.version = "0.1.1"
11
+ s.authors = ['Philipp Fehre']
12
+ s.email = "philipp.fehre@googlemail.com"
13
+ s.homepage = "http://sideshowcoder.com/rxen"
14
+ s.description = "Ruby wrapper to acces the Xen XML-RPC API via simple ruby methods"
15
+ s.summary = "Handles login, and after that all other Xen API methods exposed via XML-RPC by XenServer"
16
+
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = true
19
+ s.extra_rdoc_files = ["README.rdoc"]
20
+
21
+ s.require_path = 'lib'
22
+ s.autorequire = 'rxen'
23
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,tests,bin}/*")
24
+
25
+ s.bindir = 'bin'
26
+ s.executables = ['rxen']
27
+ s.test_files = Dir.glob('tests/*.rb')
28
+ s.add_dependency('json')
29
+ end
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.need_tar = true
33
+ end
34
+
35
+ task :default => "pkg/#{spec.name}-#{spec.version}.gem" do
36
+ puts "generated latest version"
37
+ end
data/bin/rxen ADDED
File without changes
data/lib/rxen.rb ADDED
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Access via XML-RPC
4
+ require "xmlrpc/client"
5
+
6
+ # Read Config json file
7
+ require "rubygems"
8
+ require "json"
9
+
10
+ # Override XMLRPC Client to not throw a waring on self sign Certificate because
11
+ # XenServer Certificates are always self signed
12
+
13
+ require "net/https"
14
+ require "openssl"
15
+ require "pp"
16
+
17
+ # enable debugger
18
+ # require "ruby-debug"
19
+
20
+
21
+ # Get rid of message that Cerificate from Server is self signed, since XenServer uses
22
+ # a self signed Cert in the default case and we know to which Server we connect anyway
23
+ module SELF_SSL
24
+
25
+ class Net_HTTP < Net::HTTP
26
+ def initialize(*args)
27
+ super
28
+ @ssl_context = OpenSSL::SSL::SSLContext.new
29
+ # Set verify mode to VERIFY_NONE to not display a warning on
30
+ # self signed certificates
31
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
+ end
33
+ end
34
+
35
+ class XMLRPC_Client < XMLRPC::Client
36
+ def initialize(*args)
37
+ super
38
+ # Use patched NET::HTTP module for XMLRPC
39
+ @http = SELF_SSL::Net_HTTP.new( @host, @port,
40
+ @proxy_host,@proxy_port )
41
+ @http.use_ssl = @use_ssl if @use_ssl
42
+ @http.read_timeout = @timeout
43
+ @http.open_timeout = @timeout
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+
50
+ # XenAPI
51
+
52
+ # Define Xen Api Error to make clearer it clearer Exception belongs to XenApi
53
+ class XenApiError < RuntimeError; end
54
+ # Define Config Error
55
+ class XenApiConfigError < RuntimeError; end
56
+
57
+ # Handle a session with a given XenServer
58
+ class Session
59
+
60
+ # Methods to respond to
61
+ RPC_METHODS = {
62
+ # Login / Logout
63
+ :login => /^login/,
64
+ :logout => /^logout/,
65
+
66
+ # Session
67
+ :session_change_password => /^session_change_password/,
68
+ :session_get_all_subject_identifiers => /^session_get_all_subject_identifiers/,
69
+ :session_logout_subject_identifier => /^session_logout_subject_identifier/,
70
+ :session_get_uuid => /^session_get_uuid/,
71
+ :session_get_this_user => /^session_get_this_user/,
72
+ :session_get_this_host => /^session_get_this_host/,
73
+ :session_get_last_active => /^session_get_last_active/,
74
+ :session_get_pool => /^session_get_pool/,
75
+ :session_get_other_config => /^session_get_other_config/,
76
+ :session_set_other_config => /^session_set_other_config/,
77
+
78
+ # Task
79
+ :task_create => /^task_create/,
80
+ :task_destroy => /^task_destroy/,
81
+ :task_get_all => /^task_get_all/,
82
+
83
+ # Event
84
+ :event_register => /^event_register/,
85
+ :event_unregister => /^event_unregister/,
86
+ :event_next => /^event_next/,
87
+ :event_get_current_id => /^event_get_current_id/,
88
+
89
+ # VM
90
+ :vm_snapshot => /^VM_snapshot/,
91
+ :vm_clone => /^VM_clone/,
92
+ :vm_copy => /^VM_copy/,
93
+ :vm_start => /^VM_start/,
94
+ :vm_start_on => /^VM_start_on/,
95
+ :vm_pause => /^VM_pause/,
96
+ :vm_unpause => /^VM_unpause/,
97
+ :vm_suspend => /^VM_suspend/,
98
+ :vm_resume => /^VM_resume/,
99
+ :vm_clean_shutdown => /^VM_clean_shutdown/,
100
+ :vm_clean_reboot => /^VM_clean_reboot/,
101
+ :vm_hard_showdown => /^VM_hard_shutdown/,
102
+ :vm_pool_migrate => /^VM_pool_migrate/,
103
+ :vm_get_possible_hosts => /^VM_get_possible_hosts/,
104
+ :vm_assert_agile => /^VM_assert_agile/,
105
+ :vm_get_uuid => /^VM_get_uuid/,
106
+ :vm_get_powerstate => /^VM_get_powerstate/,
107
+ :vm_get_name_label => /^VM_name_label/,
108
+ :vm_get_resident_on => /^VM_get_resident_on/,
109
+ :vm_get_all => /^VM_get_all/
110
+ }
111
+
112
+ # Access the Session ID
113
+ attr_reader :session, :xenserver
114
+
115
+ # Access and set session attributes
116
+ attr_accessor :uri, :user, :password
117
+
118
+ # Initialize with Server URI
119
+ def initialize(uri)
120
+ @uri = uri
121
+ @xenserver = SELF_SSL::XMLRPC_Client.new2(@uri)
122
+ end
123
+
124
+ # Initialize with JSON config
125
+ class << self
126
+ def new_with_config(config)
127
+ config = JSON.parse(File.read(config))["xenserver"]
128
+ if config.nil?
129
+ raise XenApiConfigError, "malformed config"
130
+ end
131
+
132
+ @password = config["password"]
133
+ @user = config["user"]
134
+ @uri = config["uri"]
135
+ if @password.nil? || @user.nil? || @uri.nil?
136
+ raise XenApiConfigError, "missing uri, user, or password"
137
+ end
138
+
139
+ s = self.new(@uri)
140
+ s.login_with_password(@user, @password)
141
+ return s
142
+ end
143
+ end
144
+
145
+ # Since methods are just forwarded to the Server with params they don't have to be implemented itself
146
+ # but via responding to method_missing in the correct form
147
+ def method_missing(method, *args, &block)
148
+ RPC_METHODS.each do |m|
149
+ if method.to_s =~ m.last
150
+ return xenapi_request(method, *args)
151
+ end
152
+ end
153
+ # Call super class method missing
154
+ super
155
+ end
156
+
157
+ # If methods are only implemented via method_missing the responds to is not working correctly anymore
158
+ # therefor it needs to be updated with the correct methods to be called
159
+
160
+ def respond_to?(method)
161
+ # Check if any RPC methods match the call
162
+ RPC_METHODS.each do |m|
163
+ return true if method.to_s =~ m.last
164
+ end
165
+ # Call super class responds_to?
166
+ super
167
+ end
168
+
169
+ # Declare a XenAPI request to be only called by object itself
170
+ private
171
+
172
+ def xenapi_request(method, *params)
173
+ # Catch login method
174
+ if method.to_s =~ RPC_METHODS[:login]
175
+ return login(method, *params)
176
+ end
177
+
178
+ if method.to_s =~ RPC_METHODS[:logout]
179
+ return logout(method)
180
+ end
181
+
182
+ # Pass method to server
183
+ # First part of method name is the class, so _ got to be replace by a .
184
+ method = method.to_s
185
+ method.sub!(/_/, ".")
186
+
187
+ # Check if we are logged in
188
+ raise XenApiError, "not logged in" unless @session
189
+
190
+ # Call Server with method passed
191
+ res = @xenserver.call(method, @session, *params)
192
+ if res["Status"] == "Success"
193
+ # return the result value passed back via XMLRPC
194
+ return res["Value"]
195
+ else
196
+ # get the error if one occurs
197
+ raise XenApiError, res["ErrorDescription"][0]
198
+ end
199
+
200
+ end
201
+
202
+ # Login Method needs to save the session cookie for further requests so it
203
+ # does not have to be passed every single call
204
+ def login(method, *params)
205
+ # Use saved passwords if nothing is passed
206
+ if params.empty?
207
+ params << @user
208
+ params << @password
209
+ else
210
+ # save password and user for quicker reloggon
211
+ @user = params[0]
212
+ @password = params[1]
213
+ end
214
+
215
+ res = @xenserver.call("session.#{method}", *params)
216
+
217
+ if res["Status"] == "Success"
218
+ @session = res["Value"]
219
+ return @session
220
+ else
221
+ raise XenApiError, res["ErrorDescription"][0]
222
+ end
223
+ end
224
+
225
+ # Logout needs to clear the session cookie saved by login
226
+ def logout(method)
227
+ # Check if logged on, else session should be nil
228
+ unless @session.nil?
229
+ res = @xenserver.call("session.#{method}", @session)
230
+ else
231
+ # If not logged in logout will always be successful (common sense)
232
+ return true
233
+ end
234
+
235
+ # Check if logout was Successful and clear session if so
236
+ if res["Status"] == "Success"
237
+ @session = nil
238
+ return true
239
+ else
240
+ raise XenApiError, res["ErrorDescription"][0]
241
+ end
242
+ end
243
+
244
+ end
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "test_helper"
4
+ require "test/unit"
5
+ require "rxen"
6
+ require "net/http"
7
+ require "net/https"
8
+ require "json"
9
+
10
+ class SessionTest < Test::Unit::TestCase
11
+
12
+ # define Server config file
13
+ SERVER_CONFIG = "test_server.json"
14
+ MALFORMED_CONFIG = "test_malformed.json"
15
+ MALFORMED_CONFIG_USER = "test_malformed_user.json"
16
+
17
+ # Methods that can be called on the xensession at the moment
18
+ METHODS = [
19
+ :session_change_password,
20
+ :session_get_all_subject_identifiers,
21
+ :session_logout_subject_identifier,
22
+ :session_get_uuid,
23
+ :session_get_this_user,
24
+ :session_get_this_host,
25
+ :session_get_last_active,
26
+ :session_get_pool,
27
+ :session_get_other_config,
28
+ :session_set_other_config,
29
+ :task_create,
30
+ :task_destroy,
31
+ :task_get_all,
32
+ :event_register,
33
+ :event_unregister,
34
+ :event_next,
35
+ :event_get_current_id,
36
+ :VM_snapshot,
37
+ :VM_clone,
38
+ :VM_copy,
39
+ :VM_start,
40
+ :VM_start_on,
41
+ :VM_pause,
42
+ :VM_unpause,
43
+ :VM_suspend,
44
+ :VM_resume,
45
+ :VM_clean_shutdown,
46
+ :VM_clean_reboot,
47
+ :VM_hard_shutdown,
48
+ :VM_pool_migrate,
49
+ :VM_get_possible_hosts,
50
+ :VM_assert_agile,
51
+ :VM_get_uuid,
52
+ :VM_get_powerstate,
53
+ :VM_name_label,
54
+ :VM_get_resident_on,
55
+ :VM_get_all
56
+ ]
57
+
58
+ # Methods used for testing do to them being non blocking and do not require specific order
59
+ # of execution
60
+ METHODS_TEST_SAVE = {
61
+ :session_get_all_subject_identifiers => [],
62
+ :task_get_all => [],
63
+ :event_register => ["task"],
64
+ :event_unregister => ["task"],
65
+ :event_get_current_id => [],
66
+ :VM_get_all => []
67
+ }
68
+
69
+ # Setup
70
+ def setup
71
+ config = JSON.parse(File.read(SERVER_CONFIG))["xenserver"]
72
+ @serveruri = config["uri"]
73
+ @user = config["user"]
74
+ @password = config["password"]
75
+ @xensession = Session.new(@serveruri)
76
+ end
77
+
78
+ def test_login
79
+ # Check if Session responds to login
80
+ assert_respond_to(@xensession, :login_with_password)
81
+
82
+ # Check if login works
83
+ assert_nothing_raised(XenApiError) do
84
+ @xensession.login_with_password(@user, @password)
85
+ end
86
+ assert_not_nil( @xensession.session )
87
+ assert_not_nil( @xensession.user )
88
+ assert_not_nil( @xensession.password )
89
+
90
+ # Login credentials should be saved, and not be required again
91
+ assert_nothing_raised(XenApiError) do
92
+ @xensession.login_with_password()
93
+ end
94
+
95
+ # Check if login fails with wrong password and username, overwriting
96
+ # saved ones
97
+ assert_raise(XenApiError) do
98
+ @xensession.login_with_password("toor", "12345")
99
+ end
100
+ assert_equal( @xensession.user, "toor" )
101
+ assert_equal( @xensession.password, "12345" )
102
+
103
+ end
104
+
105
+ def test_new_with_config
106
+ test_session = nil
107
+ assert_nothing_raised(XenApiConfigError) do
108
+ test_session = Session.new_with_config(SERVER_CONFIG)
109
+ end
110
+
111
+ assert_not_nil( test_session.session )
112
+
113
+ assert_raises(XenApiConfigError) do
114
+ Session.new_with_config(MALFORMED_CONFIG)
115
+ end
116
+
117
+ assert_raises(XenApiConfigError) do
118
+ Session.new_with_config(MALFORMED_CONFIG_USER)
119
+ end
120
+ end
121
+
122
+ def test_logout
123
+ # Check if Session responds to logout
124
+ assert_respond_to(@xensession, :logout)
125
+
126
+ # Logout should always be successful if not logged in
127
+ assert_equal(true, @xensession.logout())
128
+
129
+ # Login first
130
+ @xensession.login_with_password(@user, @password)
131
+
132
+ # Check if we are logged in
133
+ assert_not_nil( @xensession.session )
134
+
135
+ # Logout
136
+ assert_nothing_raised(XenApiError) do
137
+ @xensession.logout()
138
+ end
139
+ assert_nil( @xensession.session )
140
+ end
141
+
142
+ def test_respond_to_method
143
+ # Check to see if we are responding to correct methods
144
+ METHODS.each do |m|
145
+ assert_respond_to( @xensession, m )
146
+ end
147
+ end
148
+
149
+ def test_method_basic_function
150
+ # Check if no methods defined throw an error
151
+ # Start by loggin in
152
+ assert_nothing_raised(XenApiError) {@xensession.login_with_password(@user, @password)}
153
+
154
+ # Methods able to call
155
+ METHODS_TEST_SAVE.each do |m|
156
+ assert_nothing_raised(XenApiError) do
157
+ if m[1].empty?
158
+ @xensession.send(m[0])
159
+ else
160
+ @xensession.send(m[0], m[1])
161
+ end
162
+ end
163
+ end
164
+
165
+ # End by loggin out
166
+ assert_nothing_raised(XenApiError) {@xensession.logout()}
167
+ end
168
+
169
+ def test_method_call_deep
170
+ # We should not be able to call a method unless we are logged in
171
+ assert_raise(XenApiError) do
172
+ @xensession.VM_get_all()
173
+ end
174
+
175
+ # Login now
176
+ @xensession.login_with_password(@user, @password)
177
+
178
+ # Method should run fine now
179
+ assert_nothing_raised(XenApiError) do
180
+ @xensession.VM_get_all()
181
+ end
182
+ end
183
+
184
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ $LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
4
+ $LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../bin' )
@@ -0,0 +1,6 @@
1
+ {"server":{
2
+ "uri":"https://xxxx/",
3
+ "user":"root",
4
+ "password":"xxxx"
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ {"xenserver":{
2
+ "uri":"https://xxxx/",
3
+ "password":"xxxx"
4
+ }
5
+ }
@@ -0,0 +1,6 @@
1
+ {"xenserver":{
2
+ "uri":"https://xxxx/",
3
+ "user":"root",
4
+ "password":"xxxx"
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "test/unit"
3
+ require "tc_rxen_session"
4
+
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rxen
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Philipp Fehre
13
+ autorequire: rxen
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-26 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description: Ruby wrapper to acces the Xen XML-RPC API via simple ruby methods
33
+ email: philipp.fehre@googlemail.com
34
+ executables:
35
+ - rxen
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - README.rdoc
40
+ files:
41
+ - README.rdoc
42
+ - Rakefile
43
+ - lib/rxen.rb
44
+ - tests/tc_rxen_session.rb
45
+ - tests/test_helper.rb
46
+ - tests/test_malformed.json
47
+ - tests/test_malformed_user.json
48
+ - tests/test_server.json
49
+ - tests/ts_alltests.rb
50
+ - bin/rxen
51
+ has_rdoc: true
52
+ homepage: http://sideshowcoder.com/rxen
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.6
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Handles login, and after that all other Xen API methods exposed via XML-RPC by XenServer
81
+ test_files:
82
+ - tests/tc_rxen_session.rb
83
+ - tests/test_helper.rb
84
+ - tests/ts_alltests.rb