deltacloud-core 0.1.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/DISCLAIMER +8 -0
  2. data/{COPYING → LICENSE} +0 -0
  3. data/NOTICE +13 -0
  4. data/Rakefile +50 -51
  5. data/bin/deltacloudd +8 -1
  6. data/config.ru +0 -2
  7. data/config/drivers.yaml +48 -0
  8. data/deltacloud-core.gemspec +75 -0
  9. data/deltacloud.rb +3 -2
  10. data/lib/deltacloud/backend_capability.rb +15 -3
  11. data/lib/deltacloud/base_driver.rb +0 -2
  12. data/lib/deltacloud/base_driver/base_driver.rb +85 -89
  13. data/lib/deltacloud/base_driver/features.rb +61 -7
  14. data/lib/deltacloud/base_driver/mock_driver.rb +42 -43
  15. data/lib/deltacloud/core_ext.rb +18 -0
  16. data/lib/deltacloud/core_ext/integer.rb +31 -0
  17. data/lib/deltacloud/core_ext/string.rb +50 -0
  18. data/lib/deltacloud/drivers/azure/azure_driver.rb +71 -22
  19. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +641 -584
  20. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +0 -2
  21. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +167 -0
  22. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +39 -1
  23. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +41 -25
  24. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob1.yml +6 -3
  25. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob2.yml +6 -3
  26. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob3.yml +4 -2
  27. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob4.yml +5 -2
  28. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob5.yml +4 -2
  29. data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +1 -0
  30. data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +1 -0
  31. data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +1 -0
  32. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +1 -0
  33. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +1 -0
  34. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +1 -0
  35. data/lib/deltacloud/drivers/mock/mock_driver.rb +138 -30
  36. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +13 -15
  37. data/lib/deltacloud/drivers/opennebula/occi_client.rb +13 -15
  38. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +13 -15
  39. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +224 -113
  40. data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +332 -0
  41. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +221 -170
  42. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +0 -1
  43. data/lib/deltacloud/drivers/sbc/sbc_client.rb +247 -0
  44. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +297 -0
  45. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +0 -2
  46. data/lib/deltacloud/hardware_profile.rb +1 -3
  47. data/lib/deltacloud/helpers.rb +0 -2
  48. data/lib/deltacloud/helpers/application_helper.rb +86 -12
  49. data/lib/deltacloud/helpers/blob_stream.rb +19 -2
  50. data/lib/deltacloud/helpers/conversion_helper.rb +0 -2
  51. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +0 -2
  52. data/lib/deltacloud/method_serializer.rb +0 -2
  53. data/lib/deltacloud/models/base_model.rb +0 -2
  54. data/lib/deltacloud/models/blob.rb +1 -2
  55. data/lib/deltacloud/models/bucket.rb +0 -2
  56. data/lib/deltacloud/models/image.rb +0 -2
  57. data/lib/deltacloud/models/instance.rb +19 -2
  58. data/lib/deltacloud/models/instance_profile.rb +4 -2
  59. data/lib/deltacloud/models/key.rb +0 -2
  60. data/lib/deltacloud/models/load_balancer.rb +0 -2
  61. data/lib/deltacloud/models/realm.rb +0 -2
  62. data/lib/deltacloud/models/storage_snapshot.rb +0 -2
  63. data/lib/deltacloud/models/storage_volume.rb +4 -2
  64. data/lib/deltacloud/runner.rb +132 -0
  65. data/lib/deltacloud/state_machine.rb +0 -2
  66. data/lib/deltacloud/validation.rb +9 -7
  67. data/lib/drivers.rb +36 -48
  68. data/lib/sinatra/accept_media_types.rb +26 -0
  69. data/lib/sinatra/lazy_auth.rb +16 -0
  70. data/lib/sinatra/rabbit.rb +112 -54
  71. data/lib/sinatra/rack_driver_select.rb +50 -16
  72. data/lib/sinatra/rack_etag.rb +79 -0
  73. data/lib/sinatra/rack_matrix_params.rb +84 -0
  74. data/lib/sinatra/rack_runtime.rb +47 -0
  75. data/lib/sinatra/static_assets.rb +16 -0
  76. data/lib/sinatra/url_for.rb +31 -4
  77. data/public/favicon.ico +0 -0
  78. data/public/images/bread-bg.png +0 -0
  79. data/public/images/error.png +0 -0
  80. data/public/images/pending.png +0 -0
  81. data/public/images/running.png +0 -0
  82. data/public/images/stopped.png +0 -0
  83. data/public/javascripts/application.js +35 -0
  84. data/public/stylesheets/compiled/application.css +59 -5
  85. data/public/stylesheets/compiled/screen.css +1 -1
  86. data/server.rb +293 -29
  87. data/support/fedora/deltacloud-core +78 -0
  88. data/support/fedora/deltacloud-core.spec +143 -0
  89. data/support/fedora/deltacloudd +78 -18
  90. data/support/fedora/rubygem-deltacloud-core.spec +76 -40
  91. data/tests/common.rb +172 -0
  92. data/tests/drivers/mock/api_test.rb +133 -0
  93. data/tests/drivers/mock/hardware_profiles_test.rb +134 -0
  94. data/tests/drivers/mock/images_test.rb +126 -0
  95. data/tests/drivers/mock/instance_states_test.rb +71 -0
  96. data/tests/drivers/mock/instances_test.rb +236 -0
  97. data/tests/drivers/mock/realms_test.rb +93 -0
  98. data/tests/drivers/mock/setup.rb +3 -0
  99. data/tests/drivers/mock/url_for_test.rb +67 -0
  100. data/tests/drivers/rackspace/api_test.rb +41 -0
  101. data/tests/drivers/rackspace/hardware_profiles_test.rb +53 -0
  102. data/tests/drivers/rackspace/images_test.rb +40 -0
  103. data/tests/drivers/rackspace/instances_test.rb +161 -0
  104. data/tests/drivers/rackspace/realms_test.rb +36 -0
  105. data/tests/drivers/rackspace/setup.rb +14 -0
  106. data/tests/drivers/rhevm/api_test.rb +39 -0
  107. data/tests/drivers/rhevm/hardware_profiles_test.rb +53 -0
  108. data/tests/drivers/rhevm/images_test.rb +42 -0
  109. data/tests/drivers/rhevm/instances_test.rb +179 -0
  110. data/tests/drivers/rhevm/realms_test.rb +35 -0
  111. data/tests/drivers/rhevm/setup.rb +14 -0
  112. data/tests/rabbit_test.rb +52 -0
  113. data/views/api/show.html.haml +2 -5
  114. data/views/blobs/new.html.haml +17 -1
  115. data/views/blobs/show.html.haml +6 -0
  116. data/views/blobs/show.xml.haml +5 -1
  117. data/views/buckets/index.html.haml +1 -12
  118. data/views/buckets/index.xml.haml +3 -5
  119. data/views/docs/operation.html.haml +23 -11
  120. data/views/drivers/index.html.haml +15 -0
  121. data/views/drivers/index.xml.haml +7 -0
  122. data/views/drivers/show.html.haml +20 -0
  123. data/views/drivers/show.xml.haml +7 -0
  124. data/views/error.html.haml +31 -0
  125. data/views/errors/auth_exception.xml.haml +2 -1
  126. data/views/errors/backend_capability_failure.xml.haml +2 -1
  127. data/views/errors/backend_error.html.haml +3 -0
  128. data/views/errors/backend_error.xml.haml +2 -2
  129. data/views/errors/validation_failure.xml.haml +3 -2
  130. data/views/images/index.html.haml +1 -6
  131. data/views/images/index.xml.haml +2 -0
  132. data/views/images/new.html.haml +14 -0
  133. data/views/images/show.xml.haml +2 -0
  134. data/views/instances/index.html.haml +8 -6
  135. data/views/instances/index.xml.haml +4 -0
  136. data/views/instances/new.html.haml +40 -11
  137. data/views/instances/run.html.haml +9 -0
  138. data/views/instances/run.xml.haml +7 -0
  139. data/views/instances/run_command.html.haml +16 -0
  140. data/views/instances/show.html.haml +14 -0
  141. data/views/instances/show.xml.haml +12 -4
  142. data/views/layout.html.haml +7 -2
  143. data/views/load_balancers/index.html.haml +1 -1
  144. data/views/load_balancers/new.html.haml +2 -2
  145. data/views/load_balancers/show.html.haml +1 -1
  146. data/views/storage_snapshots/index.html.haml +3 -0
  147. data/views/storage_snapshots/new.html.haml +9 -0
  148. data/views/storage_volumes/attach.html.haml +20 -0
  149. data/views/storage_volumes/index.html.haml +16 -1
  150. data/views/storage_volumes/index.xml.haml +1 -10
  151. data/views/storage_volumes/new.html.haml +17 -0
  152. data/views/storage_volumes/show.html.haml +8 -0
  153. data/views/storage_volumes/show.xml.haml +25 -3
  154. metadata +197 -127
  155. data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +0 -130
  156. data/parse.rb +0 -7
  157. data/test.rb +0 -3
  158. data/views/api/drivers.xml.haml +0 -6
@@ -0,0 +1,332 @@
1
+ require 'rubygems'
2
+ require 'base64'
3
+ require 'restclient'
4
+ require 'nokogiri'
5
+ require 'digest/md5'
6
+ require 'json'
7
+
8
+ module RHEVM
9
+
10
+ def self.client(url)
11
+ RestClient::Resource.new(url)
12
+ end
13
+
14
+ class Client
15
+
16
+ attr_reader :credentials, :api_entrypoint
17
+
18
+ def initialize(username, password, api_entrypoint)
19
+ @credentials = { :username => username, :password => password }
20
+ @api_entrypoint = api_entrypoint
21
+ end
22
+
23
+ def vms(opts={})
24
+ headers = {
25
+ :accept => "application/xml; detail=disks; detail=nics; detail=hosts"
26
+ }
27
+ headers.merge!(auth_header)
28
+ if opts[:id]
29
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/vms/%s" % opts[:id]].get(headers)).root
30
+ [ RHEVM::VM::new(self, vm)]
31
+ else
32
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/vms"].get(headers)).xpath('/vms/vm').collect do |vm|
33
+ RHEVM::VM::new(self, vm)
34
+ end
35
+ end
36
+ end
37
+
38
+ def vm_action(id, action, headers={})
39
+ headers.merge!(auth_header)
40
+ headers.merge!({
41
+ :content_type => 'application/xml',
42
+ :accept => 'application/xml',
43
+ })
44
+ if action==:delete
45
+ RHEVM::client(@api_entrypoint)["/vms/%s" % id].delete(headers)
46
+ else
47
+ xml_response = Client::parse_response(RHEVM::client(@api_entrypoint)["/vms/%s/%s" % [id, action]].post('<action/>', headers))
48
+ return false if (xml_response/'action/status').first.text!="COMPLETE"
49
+ end
50
+ return true
51
+ end
52
+
53
+ def create_vm(template_id, opts={})
54
+ opts ||= {}
55
+ builder = Nokogiri::XML::Builder.new do
56
+ vm {
57
+ name opts[:name] || "i-#{Time.now.to_i}"
58
+ template_(:id => template_id)
59
+ cluster(:id => opts[:realm_id] || clusters.first.id)
60
+ type_ opts[:hwp_id] || 'desktop'
61
+ memory opts[:hwp_memory] || (512*1024*1024).to_s
62
+ cpu {
63
+ topology( :cores => (opts[:hwp_cpu] || '1'), :sockets => '1' )
64
+ }
65
+ }
66
+ end
67
+ headers = opts[:headers] || {}
68
+ headers.merge!({
69
+ :content_type => 'application/xml',
70
+ :accept => 'application/xml',
71
+ })
72
+ headers.merge!(auth_header)
73
+ vm = RHEVM::client(@api_entrypoint)["/vms"].post(Nokogiri::XML(builder.to_xml).root.to_s, headers)
74
+ RHEVM::VM::new(self, Nokogiri::XML(vm).root)
75
+ end
76
+
77
+ def templates(opts={})
78
+ headers = {
79
+ :accept => "application/xml"
80
+ }
81
+ headers.merge!(auth_header)
82
+ if opts[:id]
83
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/templates/%s" % opts[:id]].get(headers)).root
84
+ [ RHEVM::Template::new(self, vm)]
85
+ else
86
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/templates"].get(headers)).xpath('/templates/template').collect do |vm|
87
+ RHEVM::Template::new(self, vm)
88
+ end
89
+ end
90
+ end
91
+
92
+ def clusters(opts={})
93
+ headers = {
94
+ :accept => "application/xml; detail=datacenters"
95
+ }
96
+ headers.merge!(auth_header)
97
+ if opts[:id]
98
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/clusters/%s" % opts[:id]].get(headers)).root
99
+ [ RHEVM::Cluster::new(self, vm)]
100
+ else
101
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/clusters"].get(headers)).xpath('/clusters/cluster').collect do |vm|
102
+ RHEVM::Cluster::new(self, vm)
103
+ end
104
+ end
105
+ end
106
+
107
+ def datacenters(opts={})
108
+ headers = {
109
+ :accept => "application/xml"
110
+ }
111
+ headers.merge!(auth_header)
112
+ if opts[:id]
113
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/datacenters/%s" % opts[:id]].get(headers)).root
114
+ [ RHEVM::DataCenter::new(self, vm)]
115
+ else
116
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/datacenters"].get(headers)).xpath('/data_centers/data_center').collect do |vm|
117
+ RHEVM::DataCenter::new(self, vm)
118
+ end
119
+ end
120
+ end
121
+
122
+ def hosts(opts={})
123
+ headers = {
124
+ :accept => "application/xml"
125
+ }
126
+ headers.merge!(auth_header)
127
+ if opts[:id]
128
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/hosts/%s" % opts[:id]].get(headers)).root
129
+ [ RHEVM::Host::new(self, vm)]
130
+ else
131
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/hosts"].get(headers)).xpath('/hosts/host').collect do |vm|
132
+ RHEVM::Host::new(self, vm)
133
+ end
134
+ end
135
+ end
136
+
137
+ def storagedomains(opts={})
138
+ headers = {
139
+ :accept => "application/xml"
140
+ }
141
+ headers.merge!(auth_header)
142
+ if opts[:id]
143
+ vm = Client::parse_response(RHEVM::client(@api_entrypoint)["/storagedomains/%s" % opts[:id]].get(headers)).root
144
+ [ RHEVM::StorageDomain::new(self, vm)]
145
+ else
146
+ Client::parse_response(RHEVM::client(@api_entrypoint)["/storagedomains"].get(headers)).xpath('/storage_domains/storage_domain').collect do |vm|
147
+ RHEVM::StorageDomain::new(self, vm)
148
+ end
149
+ end
150
+ end
151
+
152
+ def auth_header
153
+ { :authorization => "Basic " + Base64.encode64("#{@credentials[:username]}:#{@credentials[:password]}"), }
154
+ end
155
+
156
+ def base_url
157
+ url = URI.parse(@api_entrypoint)
158
+ "#{url.scheme}://#{url.host}:#{url.port}"
159
+ end
160
+
161
+ def self.parse_response(response)
162
+ Nokogiri::XML(response)
163
+ end
164
+
165
+ end
166
+
167
+ class BaseObject
168
+ attr_accessor :id, :href, :name
169
+ attr_reader :client
170
+
171
+ def initialize(client, id, href, name)
172
+ @id, @href, @name = id, href, name
173
+ @client = client
174
+ self
175
+ end
176
+
177
+ end
178
+
179
+ class Link
180
+ attr_accessor :id, :href, :client
181
+
182
+ def initialize(client, id, href)
183
+ @id, @href = id, href
184
+ @client = client
185
+ end
186
+
187
+ def follow
188
+ xml = Client::parse_response(RHEVM::client(@client.base_url)[@href].get(@client.auth_header))
189
+ object_class = ::RHEVM.const_get(xml.root.name.camelize)
190
+ object_class.new(@client, (xml.root))
191
+ end
192
+
193
+ end
194
+
195
+ class VM < BaseObject
196
+ attr_reader :description, :status, :memory, :profile, :display, :host, :cluster, :template, :macs
197
+ attr_reader :storage, :cores, :username, :creation_time
198
+ attr_reader :ip
199
+
200
+ def initialize(client, xml)
201
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
202
+ @username = client.credentials[:username]
203
+ parse_xml_attributes!(xml)
204
+ self
205
+ end
206
+
207
+ private
208
+
209
+ def parse_xml_attributes!(xml)
210
+ @description = ((xml/'description').first.text rescue '')
211
+ @status = (xml/'status').first.text
212
+ @memory = (xml/'memory').first.text
213
+ @profile = (xml/'type').first.text
214
+ @template = Link::new(@client, (xml/'template').first[:id], (xml/'template').first[:href])
215
+ @host = Link::new(@client, (xml/'host').first[:id], (xml/'host').first[:href]) rescue nil
216
+ @cluster = Link::new(@client, (xml/'cluster').first[:id], (xml/'cluster').first[:href])
217
+ @display = {
218
+ :type => (xml/'display/type').first.text,
219
+ :address => ((xml/'display/address').first.text rescue nil),
220
+ :port => ((xml/'display/port').first.text rescue nil),
221
+ :monitors => (xml/'display/monitors').first.text
222
+ }
223
+ @cores = ((xml/'cpu/topology').first[:cores] rescue nil)
224
+ @storage = ((xml/'disks/disk/size').first.text rescue nil)
225
+ @macs = (xml/'nics/nic/mac').collect { |mac| mac[:address] }
226
+ @creation_time = (xml/'creation_time').text
227
+ @ip = ((xml/'guest_info/ip').first[:address] rescue nil)
228
+ end
229
+
230
+ end
231
+
232
+ class Template < BaseObject
233
+ attr_reader :description, :status, :cluster
234
+
235
+ def initialize(client, xml)
236
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
237
+ parse_xml_attributes!(xml)
238
+ self
239
+ end
240
+
241
+ private
242
+
243
+ def parse_xml_attributes!(xml)
244
+ @description = ((xml/'description').first.text rescue nil)
245
+ @status = (xml/'status').first.text
246
+ @cluster = Link::new(@client, (xml/'cluster').first[:id], (xml/'cluster').first[:href])
247
+ end
248
+ end
249
+
250
+ class Cluster < BaseObject
251
+ attr_reader :description, :datacenter
252
+
253
+ def initialize(client, xml)
254
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
255
+ parse_xml_attributes!(xml)
256
+ self
257
+ end
258
+
259
+ private
260
+
261
+ def parse_xml_attributes!(xml)
262
+ @description = ((xml/'description').first.text rescue nil)
263
+ @datacenter = Link::new(@client, (xml/'data_center').first[:id], (xml/'data_center').first[:href])
264
+ end
265
+ end
266
+
267
+ class DataCenter < BaseObject
268
+ attr_reader :description, :status
269
+
270
+ def initialize(client, xml)
271
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
272
+ parse_xml_attributes!(xml)
273
+ self
274
+ end
275
+
276
+ private
277
+
278
+ def parse_xml_attributes!(xml)
279
+ @description = ((xml/'description').first.text rescue nil)
280
+ @status = (xml/'status').first.text
281
+ end
282
+ end
283
+
284
+ class Host < BaseObject
285
+ attr_reader :description, :status, :cluster
286
+
287
+ def initialize(client, xml)
288
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
289
+ parse_xml_attributes!(xml)
290
+ self
291
+ end
292
+
293
+ private
294
+
295
+ def parse_xml_attributes!(xml)
296
+ @description = ((xml/'description').first.text rescue nil)
297
+ @status = (xml/'status').first.text
298
+ @clister = Link::new(@client, (xml/'cluster').first[:id], (xml/'cluster').first[:href])
299
+ end
300
+ end
301
+
302
+ class StorageDomain < BaseObject
303
+ attr_reader :available, :used, :kind, :address, :path
304
+
305
+ def initialize(client, xml)
306
+ super(client, xml[:id], xml[:href], (xml/'name').first.text)
307
+ parse_xml_attributes!(xml)
308
+ self
309
+ end
310
+
311
+ private
312
+
313
+ def parse_xml_attributes!(xml)
314
+ @available = (xml/'available').first.text
315
+ @used = (xml/'used').first.text
316
+ @kind = (xml/'storage/type').first.text
317
+ @address = ((xml/'storage/address').first.text rescue nil)
318
+ @path = ((xml/'storage/path').first.text rescue nil)
319
+ end
320
+ end
321
+
322
+ end
323
+
324
+ class String
325
+ unless method_defined?(:camelize)
326
+ # Camelize converts strings to UpperCamelCase
327
+ def camelize
328
+ self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
329
+ end
330
+ end
331
+ end
332
+
@@ -1,6 +1,4 @@
1
1
  #
2
- # Copyright (C) 2009 Red Hat, Inc.
3
- #
4
2
  # Licensed to the Apache Software Foundation (ASF) under one or more
5
3
  # contributor license agreements. See the NOTICE file distributed with
6
4
  # this work for additional information regarding copyright ownership. The
@@ -16,8 +14,18 @@
16
14
  # License for the specific language governing permissions and limitations
17
15
  # under the License.
18
16
 
17
+ # Minihowto: Setting up this driver
18
+ #
19
+ # 1. Setup RHEV-M server
20
+ # 2. Setup RHEV-M API (git://git.fedorahosted.org/rhevm-api.git - follow README)
21
+ # 3. Set URL to API using shell variable (or HTTP header, see comment on provider_url)
22
+ # export API_PROVIDER="https://x.x.x.x/rhevm-api-powershell"
23
+ # 4. Start Deltacloud using: deltacloudd -i rhevm
24
+ # 5. Use RHEV-M credentials + append Windows Domain
25
+ # like: admin@rhevm.example.com
26
+
19
27
  require 'deltacloud/base_driver'
20
- require 'yaml'
28
+ require 'deltacloud/drivers/rhevm/rhevm_client'
21
29
 
22
30
  module Deltacloud
23
31
  module Drivers
@@ -25,218 +33,261 @@ module Deltacloud
25
33
 
26
34
  class RHEVMDriver < Deltacloud::BaseDriver
27
35
 
28
- SCRIPT_DIR = File.dirname(__FILE__) + '/scripts'
29
- CONFIG = YAML.load_file(File.dirname(__FILE__) + '/../../../../config/rhevm_config.yml')
30
- SCRIPT_DIR_ARG = '"' + SCRIPT_DIR + '"'
31
- DELIM_BEGIN="<_OUTPUT>"
32
- DELIM_END="</_OUTPUT>"
33
- POWERSHELL="c:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"
34
- NO_OWNER=""
35
-
36
36
  feature :instances, :user_name
37
37
 
38
- def supported_collections
39
- DEFAULT_COLLECTIONS.reject { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
40
- end
41
-
42
- #
43
- # Execute a Powershell command, and convert the output
44
- # to YAML in order to get back an array of maps.
38
+ # FIXME: These values are just for ilustration
39
+ # Also I choosed 'SERVER' and 'DESKTOP' names
40
+ # because they are referred by VM (API type)
45
41
  #
46
- def execute(credentials, command, *args)
47
- args = args.to_a
48
- argString = genArgString(credentials, args)
49
- puts argString
50
- outputMaps = {}
51
- output = `#{POWERSHELL} -command "&{#{File.join(SCRIPT_DIR, command)} #{argString}; exit $LASTEXITCODE}`
52
- exitStatus = $?.exitstatus
53
- puts(output)
54
- puts("EXITSTATUS #{exitStatus}")
55
- st = output.index(DELIM_BEGIN)
56
- if (st)
57
- st += DELIM_BEGIN.length
58
- ed = output.index(DELIM_END)
59
- output = output.slice(st, (ed-st))
60
- # Lets make it yaml
61
- output.strip!
62
- if (output.length > 0)
63
- outputMaps = YAML.load(self.toYAML(output))
64
- end
65
- end
66
- outputMaps
42
+ # Values like RAM or STORAGE are reported by VM
43
+ # so they are not static.
44
+
45
+ define_hardware_profile 'SERVER' do
46
+ cpu ( 1..16 )
47
+ memory ( 512 .. 32*1024 )
48
+ storage ( 1 .. 100*1024 )
49
+ architecture 'x86_64'
67
50
  end
68
51
 
69
- def genArgString(credentials, args)
70
- commonArgs = [SCRIPT_DIR_ARG, credentials.user, credentials.password, CONFIG["domain"]]
71
- commonArgs.concat(args)
72
- commonArgs.join(" ")
52
+ define_hardware_profile 'DESKTOP' do
53
+ cpu ( 1..16 )
54
+ memory ( 512 .. 32*1024 )
55
+ storage ( 1 .. 100*1024 )
56
+ architecture 'x86_64'
73
57
  end
74
58
 
75
- def toYAML(output)
76
- yOutput = "- \n" + output
77
- yOutput.gsub!(/^(\w*)[ ]*:[ ]*([A-Z0-9a-z._ -:{}]*)/,' \1: "\2"')
78
- yOutput.gsub!(/^[ ]*$/,"- ")
79
- puts(yOutput)
80
- yOutput
59
+ # Instead of setting a URL for RHEV provider
60
+ # do it here in driver, so it can be altered by HTTP headers
61
+ #
62
+ def provider_uri=(uri)
63
+ @RHEVM_URI = uri
81
64
  end
82
65
 
83
- def statify(state)
84
- st = state.nil? ? "" : state.upcase()
85
- case st
86
- when "UP"
87
- "RUNNING"
88
- when "DOWN"
89
- "STOPPED"
90
- when "POWERING UP"
91
- "PENDING"
92
- end
66
+ # Default Provider URI.
67
+ #
68
+ # IMPORTANT:
69
+ # This URI can be overridden using shell variable API_PROVIDER
70
+ # or setting provider using HTTP header X-Deltacloud-Provider to URL.
71
+ #
72
+ def provider_uri
73
+ Deltacloud::Drivers::driver_config[:rhevm][:entrypoints]['default']['default']
93
74
  end
94
75
 
95
- define_hardware_profile 'rhevm'
76
+ define_instance_states do
77
+ start.to( :pending ) .automatically
78
+ pending.to( :running ) .automatically
79
+ pending.to( :stopped ) .automatically
80
+ stopped.to( :running ) .on( :start )
81
+ running.to( :stopping ) .on( :stop )
82
+ stopped.to( :finish ) .on( :destroy )
83
+ end
96
84
 
97
85
  #
98
86
  # Realms
99
87
  #
100
88
 
101
89
  def realms(credentials, opts=nil)
102
- domains = execute(credentials, "storageDomains.ps1")
103
- if (!opts.nil? && opts[:id])
104
- domains = domains.select{|d| opts[:id] == d["StorageId"]}
90
+ client = new_client(credentials)
91
+ realm_arr = []
92
+ safely do
93
+ clusters = client.clusters
94
+ clusters.each do |r|
95
+ d = client.datacenters(:id => r.datacenter.id).first
96
+ realm_arr << convert_realm(r, d)
97
+ end
105
98
  end
99
+ realm_arr
100
+ end
106
101
 
107
- realms = []
108
- domains.each do |dom|
109
- realms << domain_to_realm(dom)
102
+ def images(credentials, opts={})
103
+ client = new_client(credentials)
104
+ img_arr = []
105
+ safely do
106
+ templates = client.templates
107
+ if (!opts.nil? && opts[:id])
108
+ templates = templates.select{|t| opts[:id] == t.id}
109
+ end
110
+ templates.each do |t|
111
+ img_arr << convert_image(client, t)
112
+ end
110
113
  end
111
- realms
114
+ img_arr = filter_on( img_arr, :architecture, opts )
115
+ img_arr.sort_by{|e| [e.owner_id, e.name]}
112
116
  end
113
117
 
114
- def domain_to_realm(dom)
115
- Realm.new({
116
- :id => dom["StorageId"],
117
- :name => dom["Name"],
118
- :limit => dom["AvailableDiskSize"]
119
- })
118
+ def instances(credentials, opts={})
119
+ client = new_client(credentials)
120
+ inst_arr = []
121
+ safely do
122
+ if opts[:id]
123
+ vms = client.vms(:id => opts[:id])
124
+ else
125
+ vms = client.vms
126
+ end
127
+ vms.each do |vm|
128
+ inst_arr << convert_instance(client, vm)
129
+ end
130
+ end
131
+ inst_arr = filter_on( inst_arr, :id, opts )
132
+ filter_on( inst_arr, :state, opts )
120
133
  end
121
134
 
135
+ def start_instance(credentials, id)
136
+ client = new_client(credentials)
137
+ safely do
138
+ raise "ERROR: Operation start failed" unless client.vm_action(id, :start)
139
+ end
140
+ end
122
141
 
142
+ def stop_instance(credentials, id)
143
+ client = new_client(credentials)
144
+ safely do
145
+ raise "ERROR: Operation start failed" unless client.vm_action(id, :shutdown)
146
+ end
147
+ end
123
148
 
124
- #
125
- # Images
126
- #
149
+ def destroy_instance(credentials, id)
150
+ client = new_client(credentials)
151
+ safely do
152
+ raise "ERROR: Operation start failed" unless client.vm_action(id, :delete)
153
+ end
154
+ end
127
155
 
128
- def images(credentials, opts=nil )
129
- templates = []
130
- if (opts.nil?)
131
- templates = execute(credentials, "templates.ps1")
132
- else
133
- if (opts[:id])
134
- templates = execute(credentials, "templateById.ps1", opts[:id])
156
+ def storage_volumes(credentials, opts={})
157
+ client = new_client(credentials)
158
+ domains_arr = []
159
+ safely do
160
+ client.storagedomains.each do |s|
161
+ domains_arr << convert_volume(s)
135
162
  end
136
163
  end
137
- images = []
138
- templates.each do |templ|
139
- images << template_to_image(templ)
140
- end
141
- images
164
+ domains_arr = filter_on( domains_arr, :id, opts )
165
+ filter_on( domains_arr, :state, opts )
142
166
  end
143
167
 
144
- def template_to_image(templ)
145
- Image.new({
146
- :id => templ["TemplateId"],
147
- :name => templ["Name"],
148
- :description => templ["Description"],
149
- :architecture => templ["OperatingSystem"],
150
- :owner_id => NO_OWNER,
151
- :mem_size_md => templ["MemSizeMb"],
152
- :instance_count => templ["ChildCount"],
153
- :state => templ["Status"],
154
- :capacity => templ["SizeGB"]
155
- })
168
+ def create_instance(credentials, image_id, opts={})
169
+ client = new_client(credentials)
170
+ params = {}
171
+ safely do
172
+ params[:name] = opts[:name] if opts[:name]
173
+ params[:realm_id] = opts[:realm_id] if opts[:realm_id]
174
+ params[:hwp_id] = opts[:hwp_id] if opts[:hwp_id]
175
+ params[:hwp_memory] = opts[:hwp_memory] if opts[:hwp_memory]
176
+ params[:hwp_cpu] = opts[:hwp_cpu] if opts[:hwp_cpu]
177
+ convert_instance(client, client.create_vm(image_id, params))
178
+ end
156
179
  end
157
180
 
158
- #
159
- # Instances
160
- #
161
-
162
- define_instance_states do
163
- start.to(:stopped) .on( :create )
164
-
165
- pending.to(:shutting_down) .on( :stop )
166
- pending.to(:running) .automatically
181
+ def valid_credentials?(credentials)
182
+ retval = true
183
+ begin
184
+ realms(credentials)
185
+ rescue Deltacloud::BackendError
186
+ retval = false
187
+ end
188
+ retval
189
+ end
167
190
 
168
- running.to(:pending) .on( :reboot )
169
- running.to(:shutting_down) .on( :stop )
191
+ private
170
192
 
171
- shutting_down.to(:stopped) .automatically
172
- stopped.to(:pending) .on( :start )
173
- stopped.to(:finish) .on( :destroy )
193
+ def new_client(credentials)
194
+ url = (Thread.current[:provider] || ENV['API_PROVIDER'] || provider_uri)
195
+ safely do
196
+ ::RHEVM::Client.new(credentials.user, credentials.password, url)
197
+ end
174
198
  end
175
199
 
176
- def instances(credentials, opts=nil)
177
- vms = []
178
- if (opts.nil?)
179
- vms = execute(credentials, "vms.ps1")
200
+ def convert_instance(client, inst)
201
+ state = convert_state(inst.status)
202
+ storage_size = inst.storage.nil? ? 1 : (inst.storage.to_i/1024/1024/1024)
203
+ profile = InstanceProfile::new(inst.profile.upcase,
204
+ :hwp_memory => inst.memory.to_i/1024/1024,
205
+ :hwp_cpu => inst.cores,
206
+ :hwp_storage => "#{storage_size}"
207
+ )
208
+ # Include VNC and SPICE addresses
209
+ if inst.ip
210
+ public_addresses = [ inst.ip ]
180
211
  else
181
- if (opts[:id])
182
- vms = execute(credentials, "vmById.ps1", opts[:id])
183
- end
184
- end
185
- instances = []
186
- vms.each do |vm|
187
- instances << vm_to_instance(vm)
212
+ public_addresses = [ inst.macs ]
188
213
  end
189
- instances = filter_on( instances, :id, opts )
190
- instances = filter_on( instances, :state, opts )
191
- instances
192
- end
193
-
194
- def vm_to_instance(vm)
195
- Instance.new({
196
- :id => vm["VmId"],
197
- :description => vm["Description"],
198
- :name => vm["Name"],
199
- :architecture => vm["OperatingSystem"],
200
- :owner_id => NO_OWNER,
201
- :image_id => vm["TemplateId"],
202
- :state => statify(vm["Status"]),
203
- :instance_profile => InstanceProfile.new("rhevm"),
204
- :actions => instance_actions_for(statify(vm["Status"])),
205
- })
206
- end
207
-
208
- def start_instance(credentials, image_id)
209
- vm = execute(credentials, "startVm.ps1", image_id)
210
- vm_to_instance(vm[0])
211
- end
212
-
213
- def stop_instance(credentials, image_id)
214
- vm = execute(credentials, "stopVm.ps1", image_id)
215
- vm_to_instance(vm[0])
216
- end
217
-
218
- def create_instance(credentials, image_id, opts)
219
- name = opts[:name]
220
- name = "Inst-#{rand(10000)}" if (name.nil? or name.empty?)
221
- realm_id = opts[:realm_id]
222
- if (realm_id.nil?)
223
- realms = filter_on(realms(credentials, opts), :name, :name => "data")
224
- puts realms[0]
225
- realm_id = realms[0].id
214
+ Instance.new(
215
+ :id => inst.id,
216
+ :name => inst.name,
217
+ :state => state,
218
+ :image_id => inst.template.id,
219
+ :realm_id => inst.cluster.id,
220
+ :owner_id => inst.username,
221
+ :launch_time => inst.creation_time,
222
+ :instance_profile => profile,
223
+ :hardware_profile_id => profile.id,
224
+ :actions=>instance_actions_for( state ),
225
+ :public_addresses => public_addresses,
226
+ :private_addresses => []
227
+ )
228
+ end
229
+
230
+ # STATES:
231
+ #
232
+ # UNASSIGNED, DOWN, UP, POWERING_UP, POWERED_DOWN, PAUSED, MIGRATING_FROM, MIGRATING_TO,
233
+ # UNKNOWN, NOT_RESPONDING, WAIT_FOR_LAUNCH, REBOOT_IN_PROGRESS, SAVING_STATE, RESTORING_STATE,
234
+ # SUSPENDED, IMAGE_ILLEGAL, IMAGE_LOCKED or POWERING_DOWN
235
+ #
236
+ def convert_state(state)
237
+ case state
238
+ when 'WAIT_FOR_LAUNCH', 'REBOOT_IN_PROGRESS', 'SAVING_STATE',
239
+ 'RESTORING_STATE', 'POWERING_DOWN', 'POWERING_UP', 'IMAGE_LOCKED', 'SAVING_STATE' then
240
+ 'PENDING'
241
+ when 'UNASSIGNED', 'DOWN', 'PAUSED', 'NOT_RESPONDING', 'SUSPENDED', 'IMAGE_ILLEGAL', 'UNKNOWN' then
242
+ 'STOPPED'
243
+ when 'UP', 'MIGRATING_TO', 'MIGRATING_FROM'
244
+ 'RUNNING'
226
245
  end
227
- vm = execute(credentials, "addVm.ps1", image_id, name, realm_id)
228
- vm_to_instance(vm[0])
229
246
  end
230
247
 
231
- def reboot_instance(credentials, image_id)
232
- vm = execute(credentials, "rebootVm.ps1", image_id)
233
- vm_to_instance(vm[0])
248
+ def convert_volume(volume)
249
+ StorageVolume.new(
250
+ :id => volume.id,
251
+ :state => 'AVAILABLE',
252
+ :capacity => ((volume.available.to_i-volume.used.to_i)/1024/1024/1024).abs,
253
+ :instance_id => nil,
254
+ :kind => volume.kind,
255
+ :name => volume.name,
256
+ :device => volume.address ? "#{volume.address}:#{volume.path}" : nil
257
+ )
258
+ end
259
+
260
+ def convert_image(client, img)
261
+ Image.new(
262
+ :id => img.id,
263
+ :name => img.name,
264
+ :description => img.description,
265
+ :owner_id => client.credentials[:username],
266
+ :architecture => 'x86_64', # All RHEV-M VMs are x86_64
267
+ :state => img.status
268
+ )
234
269
  end
235
270
 
236
- def destroy_instance(credentials, image_id)
237
- vm = execute(credentials, "deleteVm.ps1", image_id)
238
- vm_to_instance(vm[0])
271
+ def convert_realm(r, dc)
272
+ Realm.new(
273
+ :id => r.id,
274
+ :name => dc.name,
275
+ :state => dc.status == 'UP' ? 'AVAILABLE' : 'DOWN',
276
+ :limit => :unlimited
277
+ )
239
278
  end
279
+
280
+ # Disabling this error catching will lead to more verbose messages
281
+ # on console (eg. response from RHEV-M API (so far I didn't figure our
282
+ # how to pass those message to our exception handling tool)
283
+ def catched_exceptions_list
284
+ {
285
+ :auth => [RestClient::Unauthorized],
286
+ :error => [RestClient::InternalServerError],
287
+ :glob => [ /(RestClient|RHEVM)::(\w+)/ ]
288
+ }
289
+ end
290
+
240
291
  end
241
292
 
242
293
  end