xencap 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ require 'xencap/xenapi'
2
+
3
+ module Xencap
4
+ module Plugin
5
+ attr_reader :session, :request_dispatcher
6
+
7
+ def setup(uri, options = {})
8
+ @session = XenAPI::Session.new(uri)
9
+ _ignore_ssl_errors if options.fetch(:ignore_ssl_errors, false)
10
+
11
+ @session.login_with_password(options.fetch(:login), options.fetch(:password))
12
+ @request_dispatcher = Xencap::RequestDispatcher.new(@session)
13
+ end
14
+
15
+ def teardown
16
+ @session.logout unless @session.nil?
17
+ end
18
+
19
+ def method_missing(method, *args)
20
+ @request_dispatcher.dispatch(method, *args)
21
+ end
22
+
23
+ def _ignore_ssl_errors
24
+ # Allow self-signed certs
25
+ @session.instance_variable_get(:@http).instance_variable_set(:@verify_mode, OpenSSL::SSL::VERIFY_NONE)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ class Xencap::RequestDispatcher
2
+ def initialize(session)
3
+ @session = session
4
+ @proxy_classes = Dir[File.expand_path("../session_proxy/*rb", __FILE__)]
5
+ @proxies = {}
6
+
7
+ end
8
+
9
+ def dispatch(scope, *args)
10
+ if @proxies.has_key?(scope)
11
+ # nothing to do here
12
+ elsif filename = @proxy_classes.detect {|file| File.basename(file, ".rb").downcase == scope.to_s}
13
+ require filename
14
+ @proxies[scope] = Xencap::SessionProxy.const_get(scope.capitalize).new(@session)
15
+ else
16
+ @proxies[scope] = Xencap::SessionProxy.new(@session, scope)
17
+ end
18
+
19
+ @proxies[scope]
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ class Xencap::SessionProxy::Vm < Xencap::SessionProxy
2
+ def initialize(session)
3
+ @session = session
4
+ @scope = "VM"
5
+ end
6
+
7
+ def get_control_dom0
8
+ get_all_records.select do |vm|
9
+ vm['is_control_domain']
10
+ end
11
+ end
12
+
13
+ def get_templates
14
+ get_all_records.select do |vm|
15
+ vm['is_a_template']
16
+ end
17
+ end
18
+
19
+ def get_vms
20
+ get_all_records.reject do |vm|
21
+ vm['is_a_template'] || vm['is_control_domain']
22
+ end
23
+ end
24
+
25
+ def clone_template(template_name, name)
26
+ template_ref, template_record = get_all_records.detect do |ref, record|
27
+ record['is_a_template'] == true && record['name_label'] == template_name
28
+ end
29
+ ref = clone(template_ref, name)
30
+ set_is_a_template(ref, false)
31
+ ref
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ class Xencap::SessionProxy
2
+ def initialize(session, scope)
3
+ @session = session
4
+
5
+ if scope.length <= 3
6
+ @scope = scope.upcase
7
+ else
8
+ @scope = scope
9
+ end
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ _request(method, *args)
14
+ end
15
+
16
+ def clone(*args)
17
+ if args.length > 0
18
+ _request(:clone, *args)
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def find_all_records(options = {})
25
+ get_all_records.select do |ref, record|
26
+ options.all? do |key, value|
27
+ record[key.to_s] == value
28
+ end
29
+ end
30
+ end
31
+
32
+ def find_record(options = {})
33
+ find_all_records(options).first
34
+ end
35
+
36
+ def _request(method, *args)
37
+ @session.send(@scope).send(method, *args)
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ Capistrano::Configuration.instance.load do
2
+ set :xencap_server_uri, nil
3
+ set :xencap_login, "root"
4
+ set :xencap_password, do
5
+ Capistrano::CLI.password_prompt("xen password: ")
6
+ end
7
+ set :xencap_ignore_ssl_errors, false
8
+
9
+ on :exit do
10
+ xencap.session.teardown
11
+ end
12
+
13
+ namespace :xencap do
14
+ namespace :session do
15
+ task :setup do
16
+ xencap_plugin.setup(
17
+ xencap_server_uri,
18
+ :login => xencap_login,
19
+ :password => xencap_password,
20
+ :ignore_ssl_errors => xencap_ignore_ssl_errors
21
+ )
22
+ end
23
+
24
+ task :teardown do
25
+ xencap_plugin.teardown
26
+ end
27
+ end
28
+
29
+ namespace :vm do
30
+ task :list do
31
+ xencap.session.setup
32
+ xencap_plugin.vm.get_all_records.reject {|ref, record| record['is_a_template'] || record['is_control_domain'] }.values.each {|v| puts v['name_label'] }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,232 @@
1
+ # ===========================================================================
2
+ # Copyright (c) 2010 Holger Just
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation
6
+ # files (the "Software"), to deal in the Software without
7
+ # restriction, including without limitation the rights to use,
8
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following
11
+ # conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ # OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+ # ===========================================================================
26
+ #
27
+ # This library is based on
28
+ # {XenAPI.py}[http://community.citrix.com/download/attachments/38633496/XenAPI.py]
29
+ # by XenSource Inc., licensed under the LGPL.
30
+ # ---------------------------------------------------------------------------
31
+ #
32
+ # ===========================================================================
33
+ # Grabbed on 2013-04-21 by plainlystated
34
+ # from: https://github.com/meineerde/xenapi.rb
35
+ # ---------------------------------------------------------------------------
36
+
37
+ require 'uri'
38
+ require 'xmlrpc/client'
39
+
40
+ module XenAPI
41
+ API_VERSION_1_1 = '1.1'
42
+ API_VERSION_1_2 = '1.2'
43
+
44
+ RETRY_COUNT = 3
45
+
46
+ class SessionInvalidError < Exception; end
47
+ class Failure < Exception
48
+ def initialize(details = [])
49
+ if details.is_a? Array
50
+ @error_type = details[0]
51
+ @error_details = details[1..-1] || []
52
+ else
53
+ @error_details = []
54
+ end
55
+ end
56
+
57
+ def to_s
58
+ details = case @error_details.length
59
+ when 0 then ""
60
+ when 1 then @error_details[0]
61
+ else @error_details.inspect
62
+ end
63
+
64
+ "#{@error_type.to_s}: #{details}"
65
+ end
66
+
67
+ attr_reader :error_type, :error_details
68
+ end
69
+
70
+ class Session < ::XMLRPC::Client
71
+ def initialize(uri, proxy_host=nil, proxy_port=nil)
72
+ # uri can be one of:
73
+ # * "http://server.name/path"
74
+ # * "https://server.name/path"
75
+ # * "socket:///var/xapi/xapi"
76
+ # proxy_host and proxy_port can be used to specify an HTTP proxy
77
+ @uri = URI.parse(uri)
78
+
79
+ case @uri.scheme.downcase
80
+ when 'http', 'https'
81
+ super(
82
+ @uri.host,
83
+ @uri.path.empty? ? "/" : @uri.path,
84
+ @uri.port,
85
+ proxy_host,
86
+ proxy_port,
87
+ nil, # user
88
+ nil, # password
89
+ (@uri.scheme.downcase == "https")
90
+ )
91
+ when 'socket'
92
+ raise NotImplementedError.new("Sockets are not supported yet. Sorry")
93
+ else
94
+ raise ArgumentError.new("Unknown scheme")
95
+ end
96
+
97
+ @api_version = API_VERSION_1_1
98
+ @session = ""
99
+ end
100
+
101
+ attr_reader :uri, :api_version
102
+ def session_id
103
+ @session
104
+ end
105
+
106
+ LOGIN_METHODS = %w(login_with_password slave_local_login_with_password)
107
+ LOGIN_METHODS.each do |method|
108
+ class_eval <<-"END_EVAL", __FILE__, __LINE__
109
+ def #{method}(*args)
110
+ begin
111
+ result = self.session.#{method}(*args)
112
+ rescue SessionInvalidError
113
+ raise ::XMLRPC::FaultException.new(500,
114
+ 'Received SESSION_INVALID when logging in')
115
+ end
116
+
117
+ @session = result
118
+ @last_login_method = :#{method}
119
+ @last_login_params = args
120
+ @api_version = _api_version
121
+ end
122
+ END_EVAL
123
+ end
124
+
125
+ def logout
126
+ # preferred method to logout the session
127
+ if @last_login_method.to_s.start_with?("slave_local")
128
+ self.session.local_logout
129
+ else
130
+ self.session.logout
131
+ end
132
+ end
133
+
134
+ def proxy(prefix=nil, *args)
135
+ # Overrides base method to use our custom Proxy class
136
+ XenAPIProxy.new(self, prefix, args, :call)
137
+ end
138
+
139
+ def proxy2(prefix=nil, *args)
140
+ # Overrides base method to use our custom Proxy class
141
+ XenAPIProxy.new(self, prefix, args, :call2)
142
+ end
143
+
144
+ def method_missing(sym, *args)
145
+ self.proxy(sym.to_s, *args)
146
+ end
147
+
148
+ private
149
+ def _api_version()
150
+ pool = self.pool.get_all()[0]
151
+ host = self.pool.get_master(pool)
152
+ major = self.host.get_API_version_major(host)
153
+ minor = self.host.get_API_version_minor(host)
154
+ "#{major}.#{minor}"
155
+ end
156
+
157
+ def _logout
158
+ # called from proxy object to release all session state
159
+ @session = ""
160
+ @last_login_method = nil
161
+ @last_login_params = nil
162
+ @api_version = API_VERSION_1_1
163
+ end
164
+ end
165
+
166
+ class XenAPIProxy < ::XMLRPC::Client::Proxy
167
+ def method_missing(method, *args, &block)
168
+ begin
169
+ if (@prefix == 'session.') && (Session::LOGIN_METHODS.include? method.to_s)
170
+ parse_result super(method, *args, &block)
171
+ else
172
+ retry_count = 0
173
+ while (retry_count < RETRY_COUNT)
174
+ session_args = [server(:session)] + args
175
+ begin
176
+ return parse_result super(method, *session_args, &block)
177
+ rescue SessionInvalidError
178
+ retry_count += 1
179
+ if server(:last_login_method)
180
+ @server.send(server(:last_login_method), *server(:last_login_params))
181
+ else
182
+ raise XMLRPC::FaultException.new(401, "You must log in")
183
+ end
184
+ end
185
+ end
186
+
187
+ raise ::XMLRPC::FaultException.new(500,
188
+ "Tried #{RETRY_COUNT} times to get a valid session, but failed")
189
+ end
190
+ ensure
191
+ # ensure we clear the global state on logout
192
+ @server.send(:_logout) if (@prefix == 'session.') && (method == :logout)
193
+ end
194
+ end
195
+
196
+ # method name clash between built-in clone and the method to clone a VM
197
+ def clone(*args)
198
+ args.length > 0 ? method_missing(:clone, *args) : super
199
+ end
200
+
201
+ private
202
+ def server(arg)
203
+ # returns an instance variable of the server
204
+ @server.instance_variable_get("@#{arg.to_s}")
205
+ end
206
+
207
+ def parse_result(result)
208
+ unless result.is_a?(Hash) && result.include?('Status')
209
+ raise XMLRPCClientError.new('Missing Status in response from server: ' + result.inspect)
210
+ end
211
+
212
+ if result['Status'] == 'Success'
213
+ if result.include? 'Value'
214
+ result['Value']
215
+ else
216
+ raise ::XMLRPC::FaultException.new('Missing Value in response from server')
217
+ end
218
+ else
219
+ if result.include? 'ErrorDescription'
220
+ if result['ErrorDescription'][0] == 'SESSION_INVALID'
221
+ raise SessionInvalidError
222
+ else
223
+ raise Failure.new(result['ErrorDescription'])
224
+ end
225
+ else
226
+ raise ::XMLRPC::FaultException.new('Missing ErrorDescription in response from server')
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+
data/lib/xencap.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Xencap
2
+ require File.expand_path('../xencap/tasks', __FILE__)
3
+
4
+ autoload :Plugin, File.expand_path('../xencap/plugin', __FILE__)
5
+ autoload :RequestDispatcher, File.expand_path('../xencap/request_dispatcher', __FILE__)
6
+ autoload :SessionProxy, File.expand_path('../xencap/session_proxy', __FILE__)
7
+ end
8
+
9
+ Capistrano.plugin :xencap_plugin, Xencap::Plugin
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xencap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Patrick Schless
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Should work with any xen system that uses XAPI (including XenServer &
15
+ XCP)
16
+ email: patrick@plainlystated.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ./lib/xencap/plugin.rb
22
+ - ./lib/xencap/request_dispatcher.rb
23
+ - ./lib/xencap/session_proxy/vm.rb
24
+ - ./lib/xencap/session_proxy.rb
25
+ - ./lib/xencap/tasks.rb
26
+ - ./lib/xencap/xenapi.rb
27
+ - ./lib/xencap.rb
28
+ homepage: https://github.com/plainlystated/xencap
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 1.8.24
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: Capistrano support for managing XenServer
52
+ test_files: []