rxen 0.1.1

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