deltacloud-core 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. data/LICENSE +145 -0
  2. data/NOTICE +10 -1
  3. data/Rakefile +50 -2
  4. data/bin/deltacloudd +111 -14
  5. data/config/addresses.xml +14 -0
  6. data/config/condor.yaml +30 -0
  7. data/config/drivers/azure.yaml +3 -0
  8. data/config/drivers/condor.yaml +3 -0
  9. data/config/{drivers.yaml → drivers/ec2.yaml} +5 -34
  10. data/config/drivers/eucalyptus.yaml +8 -0
  11. data/config/drivers/gogrid.yaml +3 -0
  12. data/config/drivers/mock.yaml +3 -0
  13. data/config/drivers/opennebula.yaml +4 -0
  14. data/config/drivers/rackspace.yaml +3 -0
  15. data/config/drivers/rhevm.yaml +3 -0
  16. data/config/drivers/rimuhosting.yaml +3 -0
  17. data/config/drivers/sbc.yaml +2 -0
  18. data/config/drivers/terremark.yaml +3 -0
  19. data/config/drivers/vsphere.yaml +8 -0
  20. data/deltacloud-core.gemspec +13 -5
  21. data/deltacloud.rb +4 -2
  22. data/lib/deltacloud/backend_capability.rb +2 -2
  23. data/lib/deltacloud/base_driver/base_driver.rb +23 -52
  24. data/lib/deltacloud/base_driver/exceptions.rb +168 -0
  25. data/lib/deltacloud/base_driver/features.rb +31 -12
  26. data/lib/deltacloud/base_driver/mock_driver.rb +2 -1
  27. data/lib/deltacloud/core_ext/string.rb +2 -0
  28. data/lib/deltacloud/drivers/azure/azure_driver.rb +5 -5
  29. data/lib/deltacloud/drivers/condor/condor_client.rb +273 -0
  30. data/lib/deltacloud/drivers/condor/condor_driver.rb +236 -0
  31. data/lib/deltacloud/drivers/condor/ip_agents/confserver.rb +75 -0
  32. data/lib/deltacloud/drivers/condor/ip_agents/default.rb +84 -0
  33. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +326 -95
  34. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +3 -3
  35. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +40 -8
  36. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +7 -7
  37. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +42 -25
  38. data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob1.yml +6 -4
  39. data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob2.yml +7 -5
  40. data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob3.yml +6 -4
  41. data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob4.yml +6 -4
  42. data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob5.yml +6 -4
  43. data/lib/deltacloud/drivers/mock/data/buckets/bucket1.yml +7 -1
  44. data/lib/deltacloud/drivers/mock/data/buckets/bucket2.yml +6 -1
  45. data/lib/deltacloud/drivers/mock/data/images/img1.yml +6 -2
  46. data/lib/deltacloud/drivers/mock/data/images/img2.yml +6 -2
  47. data/lib/deltacloud/drivers/mock/data/images/img3.yml +6 -2
  48. data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +11 -10
  49. data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +14 -7
  50. data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +14 -7
  51. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap1.yml +3 -2
  52. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap2.yml +3 -2
  53. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap3.yml +3 -2
  54. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +4 -3
  55. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +4 -3
  56. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +4 -3
  57. data/lib/deltacloud/drivers/mock/mock_client.rb +101 -0
  58. data/lib/deltacloud/drivers/mock/mock_driver.rb +367 -429
  59. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +6 -0
  60. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +59 -9
  61. data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +62 -8
  62. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +100 -45
  63. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +3 -2
  64. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +8 -11
  65. data/lib/deltacloud/drivers/sbc/sbc_client.rb +6 -6
  66. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +16 -0
  67. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +17 -12
  68. data/lib/deltacloud/drivers/vsphere/vsphere_client.rb +140 -0
  69. data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +405 -0
  70. data/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb +182 -0
  71. data/lib/deltacloud/hardware_profile.rb +1 -1
  72. data/lib/deltacloud/helpers.rb +2 -1
  73. data/lib/deltacloud/helpers/application_helper.rb +92 -20
  74. data/lib/deltacloud/helpers/blob_stream.rb +160 -12
  75. data/lib/deltacloud/helpers/conversion_helper.rb +6 -2
  76. data/lib/deltacloud/helpers/json_helper.rb +31 -0
  77. data/lib/deltacloud/models/address.rb +28 -0
  78. data/lib/deltacloud/models/base_model.rb +5 -1
  79. data/lib/deltacloud/models/blob.rb +1 -1
  80. data/lib/deltacloud/models/bucket.rb +10 -0
  81. data/lib/deltacloud/models/firewall.rb +22 -0
  82. data/lib/deltacloud/models/firewall_rule.rb +23 -0
  83. data/lib/deltacloud/models/image.rb +12 -0
  84. data/lib/deltacloud/models/instance.rb +20 -2
  85. data/lib/deltacloud/models/key.rb +1 -1
  86. data/lib/deltacloud/runner.rb +3 -3
  87. data/lib/deltacloud/validation.rb +3 -7
  88. data/lib/drivers.rb +7 -1
  89. data/lib/sinatra/body_proxy.rb +34 -0
  90. data/lib/sinatra/lazy_auth.rb +5 -0
  91. data/lib/sinatra/rabbit.rb +54 -31
  92. data/lib/sinatra/rack_accept.rb +157 -0
  93. data/lib/sinatra/rack_date.rb +38 -0
  94. data/lib/sinatra/rack_etag.rb +2 -3
  95. data/lib/sinatra/rack_matrix_params.rb +51 -29
  96. data/lib/sinatra/rack_runtime.rb +1 -1
  97. data/lib/sinatra/rack_syslog.rb +86 -0
  98. data/lib/sinatra/url_for.rb +14 -1
  99. data/public/images/address.png +0 -0
  100. data/public/images/balancer.png +0 -0
  101. data/public/images/blob.png +0 -0
  102. data/public/images/bucket.png +0 -0
  103. data/public/images/cloud.png +0 -0
  104. data/public/images/firewall.png +0 -0
  105. data/public/images/image.png +0 -0
  106. data/public/images/key.png +0 -0
  107. data/public/images/machine.png +0 -0
  108. data/public/images/profile.png +0 -0
  109. data/public/images/realm.png +0 -0
  110. data/public/images/snapshot.png +0 -0
  111. data/public/images/volume.png +0 -0
  112. data/public/javascripts/application.js +119 -16
  113. data/public/javascripts/jquery.min.js +18 -0
  114. data/public/javascripts/jquery.mobile-1.0b1.min.js +146 -0
  115. data/public/stylesheets/compiled/application.css +8 -0
  116. data/public/stylesheets/images/ajax-loader.png +0 -0
  117. data/public/{images → stylesheets/images}/bread-bg.png +0 -0
  118. data/public/{images → stylesheets/images}/error.png +0 -0
  119. data/public/{images → stylesheets/images}/grid.png +0 -0
  120. data/public/stylesheets/images/icon-search-black.png +0 -0
  121. data/public/stylesheets/images/icons-18-black.png +0 -0
  122. data/public/stylesheets/images/icons-18-white.png +0 -0
  123. data/public/stylesheets/images/icons-36-black.png +0 -0
  124. data/public/stylesheets/images/icons-36-white.png +0 -0
  125. data/public/{images → stylesheets/images}/logo-wide.png +0 -0
  126. data/public/{images → stylesheets/images}/pending.png +0 -0
  127. data/public/{images → stylesheets/images}/rails.png +0 -0
  128. data/public/{images → stylesheets/images}/running.png +0 -0
  129. data/public/{images → stylesheets/images}/stopped.png +0 -0
  130. data/public/{images → stylesheets/images}/topbar-bg.png +0 -0
  131. data/public/stylesheets/jquery.mobile-1.0b1.min.css +8 -0
  132. data/public/stylesheets/new.css +53 -0
  133. data/server.rb +487 -175
  134. data/support/condor/bash/cached_images.sh +8 -0
  135. data/support/condor/bash/cloud_exit_hook.sh +17 -0
  136. data/support/condor/bash/cloud_functions +175 -0
  137. data/support/condor/bash/cloud_prepare_hook.sh +20 -0
  138. data/support/condor/bash/libvirt_cloud_script.sh +13 -0
  139. data/support/condor/config/50condor_cloud.config +37 -0
  140. data/support/condor/config/50condor_cloud_node.config +37 -0
  141. data/support/condor/config/condor-cloud +2 -0
  142. data/support/condor/config/condor_config.local +44 -0
  143. data/support/fedora/deltacloud-core +48 -26
  144. data/support/fedora/deltacloud-core-config +26 -0
  145. data/support/fedora/deltacloud-core.spec +314 -68
  146. data/support/fedora/deltacloudd-fedora +5 -0
  147. data/tests/common.rb +34 -4
  148. data/tests/drivers/mock/api_test.rb +3 -3
  149. data/tests/drivers/mock/images_test.rb +12 -0
  150. data/tests/drivers/mock/instances_test.rb +2 -0
  151. data/tests/rabbit_test.rb +2 -2
  152. data/views/addresses/_address.html.haml +6 -0
  153. data/views/addresses/associate.html.haml +12 -0
  154. data/views/addresses/index.html.haml +9 -0
  155. data/views/addresses/index.xml.haml +4 -0
  156. data/views/addresses/show.html.haml +21 -0
  157. data/views/addresses/show.xml.haml +14 -0
  158. data/views/api/show.html.haml +6 -11
  159. data/views/api/show.xml.haml +2 -0
  160. data/views/blobs/new.html.haml +24 -23
  161. data/views/blobs/show.html.haml +30 -31
  162. data/views/buckets/index.html.haml +9 -21
  163. data/views/buckets/index.xml.haml +3 -7
  164. data/views/buckets/new.html.haml +13 -12
  165. data/views/buckets/show.html.haml +22 -22
  166. data/views/buckets/show.xml.haml +5 -3
  167. data/views/docs/collection.html.haml +23 -34
  168. data/views/docs/collection.xml.haml +2 -2
  169. data/views/docs/index.html.haml +9 -13
  170. data/views/docs/index.xml.haml +1 -1
  171. data/views/docs/operation.html.haml +28 -38
  172. data/views/docs/operation.xml.haml +1 -1
  173. data/views/drivers/index.html.haml +8 -13
  174. data/views/drivers/show.html.haml +18 -18
  175. data/views/error.html.haml +32 -27
  176. data/views/errors/400.html.haml +41 -0
  177. data/views/errors/{validation_failure.xml.haml → 400.xml.haml} +0 -4
  178. data/views/errors/401.html.haml +41 -0
  179. data/views/errors/{auth_exception.xml.haml → 401.xml.haml} +0 -0
  180. data/views/errors/403.html.haml +42 -0
  181. data/views/errors/{not_allowed.xml.haml → 403.xml.haml} +0 -0
  182. data/views/errors/404.html.haml +29 -0
  183. data/views/errors/{not_found.xml.haml → 404.xml.haml} +1 -1
  184. data/views/errors/405.html.haml +29 -0
  185. data/views/errors/405.xml.haml +5 -0
  186. data/views/errors/500.html.haml +43 -0
  187. data/views/errors/500.xml.haml +5 -0
  188. data/views/errors/502.html.haml +43 -0
  189. data/views/errors/{backend_error.xml.haml → 502.xml.haml} +1 -2
  190. data/views/errors/backend_capability_failure.html.haml +27 -9
  191. data/views/firewalls/index.html.haml +15 -0
  192. data/views/firewalls/index.xml.haml +28 -0
  193. data/views/firewalls/new.html.haml +11 -0
  194. data/views/firewalls/new_rule.html.haml +20 -0
  195. data/views/firewalls/show.html.haml +42 -0
  196. data/views/firewalls/show.xml.haml +26 -0
  197. data/views/hardware_profiles/index.html.haml +15 -23
  198. data/views/hardware_profiles/show.html.haml +22 -18
  199. data/views/images/index.html.haml +11 -23
  200. data/views/images/index.xml.haml +4 -13
  201. data/views/images/new.html.haml +12 -13
  202. data/views/images/show.html.haml +26 -20
  203. data/views/images/show.xml.haml +2 -1
  204. data/views/instance_states/show.html.haml +21 -25
  205. data/views/instances/index.html.haml +13 -30
  206. data/views/instances/index.xml.haml +2 -23
  207. data/views/instances/new.html.haml +83 -88
  208. data/views/instances/show.html.haml +53 -55
  209. data/views/instances/show.xml.haml +12 -10
  210. data/views/keys/index.html.haml +13 -24
  211. data/views/keys/new.html.haml +7 -7
  212. data/views/keys/show.html.haml +26 -21
  213. data/views/layout.html.haml +28 -27
  214. data/views/load_balancers/index.html.haml +11 -31
  215. data/views/load_balancers/index.xml.haml +0 -1
  216. data/views/load_balancers/new.html.haml +1 -1
  217. data/views/load_balancers/show.html.haml +33 -34
  218. data/views/load_balancers/show.xml.haml +2 -2
  219. data/views/realms/index.html.haml +11 -24
  220. data/views/realms/index.xml.haml +2 -8
  221. data/views/realms/show.html.haml +17 -15
  222. data/views/realms/show.xml.haml +2 -1
  223. data/views/storage_snapshots/index.html.haml +11 -21
  224. data/views/storage_snapshots/index.xml.haml +2 -5
  225. data/views/storage_snapshots/new.html.haml +1 -1
  226. data/views/storage_snapshots/show.html.haml +21 -13
  227. data/views/storage_snapshots/show.xml.haml +2 -1
  228. data/views/storage_volumes/index.html.haml +11 -34
  229. data/views/storage_volumes/new.html.haml +1 -1
  230. data/views/storage_volumes/show.html.haml +33 -27
  231. data/views/storage_volumes/show.xml.haml +2 -1
  232. metadata +266 -178
  233. data/lib/sinatra/respond_to.rb +0 -248
  234. data/support/fedora/deltacloudd +0 -128
  235. data/support/fedora/rubygem-deltacloud-core.spec +0 -127
  236. data/views/accounts/index.html.haml +0 -11
  237. data/views/accounts/show.html.haml +0 -30
  238. data/views/errors/auth_exception.html.haml +0 -8
  239. data/views/errors/backend_error.html.haml +0 -22
  240. data/views/errors/not_allowed.html.haml +0 -6
  241. data/views/errors/not_found.html.haml +0 -6
  242. data/views/errors/validation_failure.html.haml +0 -11
@@ -75,10 +75,11 @@ module Deltacloud
75
75
 
76
76
  # A specific feature enabled by a driver (see +feature+)
77
77
  class Feature
78
- attr_reader :decl
78
+ attr_reader :decl, :constraints
79
79
 
80
80
  def initialize(decl, &block)
81
81
  @decl = decl
82
+ @constraints = {}
82
83
  instance_eval &block if block_given?
83
84
  end
84
85
 
@@ -93,6 +94,10 @@ module Deltacloud
93
94
  def description
94
95
  decl.description
95
96
  end
97
+
98
+ def constraint(name, value)
99
+ @constraints[name] = value
100
+ end
96
101
  end
97
102
 
98
103
  def self.feature_decls
@@ -151,31 +156,38 @@ module Deltacloud
151
156
  declare_feature :images, :owner_id do
152
157
  description "Filter images using owner id"
153
158
  operation :index do
154
- param :owner_id, :string, :optional, nil, "Owner ID"
159
+ param :owner_id, :string, :optional, [], "Owner ID"
155
160
  end
156
161
  end
157
162
 
158
163
  declare_feature :instances, :user_name do
159
164
  description "Accept a user-defined name on instance creation"
160
165
  operation :create do
161
- param :name, :string, :optional, nil,
162
- "The user-defined name"
166
+ param :name, :string, :optional, [], "The user-defined name"
163
167
  end
164
168
  end
165
169
 
166
170
  declare_feature :instances, :user_data do
167
171
  description "Make user-defined data available on a special webserver"
168
172
  operation :create do
169
- param :user_data, :string, :optional, nil,
173
+ param :user_data, :string, :optional, [],
170
174
  "Base64 encoded user data will be published to internal webserver"
171
175
  end
172
176
  end
173
177
 
178
+ declare_feature :instances, :user_iso do
179
+ description "Make user-defined ISO available inside instance"
180
+ operation :create do
181
+ param :user_iso, :string, :optional, [],
182
+ "Base64 encoded gzipped ISO file will be accessible as CD-ROM drive in instance"
183
+ end
184
+ end
185
+
174
186
  declare_feature :instances, :user_files do
175
187
  description "Accept up to 5 files to be placed into the instance before launch."
176
188
  operation :create do
177
189
  1.upto(5) do |i|
178
- param :"path#{i}", :string, :optional, nil,
190
+ param :"path#{i}", :string, :optional, [],
179
191
  "Path where to place the #{i.ordinalize} file, up to 255 characters"
180
192
  param :"content#{i}", :string, :optional, nil,
181
193
  "Contents for the #{i.ordinalize} file, up to 10 kB, Base64 encoded"
@@ -183,18 +195,17 @@ module Deltacloud
183
195
  end
184
196
  end
185
197
 
186
- declare_feature :instances, :security_group do
187
- description "Put instance in one or more security groups on launch"
198
+ declare_feature :instances, :firewalls do
199
+ description "Put instance in one or more firewalls (security groups) on launch"
188
200
  operation :create do
189
- param :security_group, :array, :optional, nil,
190
- "Array of security group names"
201
+ param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
202
+ "Array of firewall (security group) id"
191
203
  end
192
204
  end
193
205
 
194
206
  declare_feature :instances, :authentication_key do
195
207
  operation :create do
196
- param :keyname, :string, :optional, nil
197
- "EC2 key authentification method"
208
+ param :keyname, :string, :optional, [], "Key authentification method"
198
209
  end
199
210
  operation :show do
200
211
  end
@@ -232,6 +243,14 @@ module Deltacloud
232
243
  end
233
244
  end
234
245
 
246
+ declare_feature :instances, :attach_snapshot do
247
+ description "Attach an snapshot to instance on create"
248
+ operation :create do
249
+ param :snapshot_id, :string, :optional
250
+ param :device_name, :string, :optional
251
+ end
252
+ end
253
+
235
254
  declare_feature :instances, :sandboxing do
236
255
  description "Allow lanuching sandbox images"
237
256
  operation :create do
@@ -43,6 +43,7 @@ module Mock
43
43
  :create_key_pair,
44
44
  :delete_key_pair,
45
45
  :create_volume,
46
+ :get_console_output,
46
47
  :describe_volumes,
47
48
  :delete_volume,
48
49
  :attach_volume,
@@ -55,7 +56,7 @@ module Mock
55
56
 
56
57
  MethodSerializer::Cache::wrap_methods(self, :cache_dir => File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'tests', 'ec2', 'support'))
57
58
  end
58
-
59
+
59
60
  end
60
61
 
61
62
 
@@ -33,10 +33,12 @@ class String
33
33
  end
34
34
 
35
35
  def pluralize
36
+ return self + 'es' if self =~ /ess$/
36
37
  self + "s"
37
38
  end
38
39
 
39
40
  def singularize
41
+ return self.gsub(/es$/, '') if self =~ /sses$/
40
42
  self.gsub(/s$/, '')
41
43
  end
42
44
 
@@ -105,7 +105,7 @@ class AzureDriver < Deltacloud::BaseDriver
105
105
  def create_blob(credentials, bucket_id, blob_id, blob_data, opts={})
106
106
  azure_connect(credentials)
107
107
  #insert azure-specific header for user metadata ... x-ms-meta-kEY = VALUE
108
- opts.gsub_keys("HTTP_X_Deltacloud_Blobmeta_", "x-ms-meta-")
108
+ BlobHelper::rename_metadata_headers(opts, "x-ms-meta-")
109
109
  safely do
110
110
  #get a handle to the bucket in order to put there
111
111
  the_bucket = WAZ::Blobs::Container.find(bucket_id)
@@ -116,7 +116,7 @@ class AzureDriver < Deltacloud::BaseDriver
116
116
  :content_lengh => blob_data[:tempfile].length,
117
117
  :content_type => blob_data[:type],
118
118
  :last_modified => '',
119
- :user_metadata => opts.gsub_keys('x-ms-meta-','')
119
+ :user_metadata => opts.gsub_keys(/x-ms-meta-/,'')
120
120
  } )
121
121
  end
122
122
 
@@ -144,7 +144,7 @@ class AzureDriver < Deltacloud::BaseDriver
144
144
  end
145
145
  user_meta = {}
146
146
  all_meta.inject({}){|result_hash, (k,v)| user_meta[k]=v if k.to_s.match(/x_ms_meta/i)}
147
- user_meta.gsub_keys('x_ms_meta_','')
147
+ user_meta.gsub_keys(/x_ms_meta_/,'')
148
148
  end
149
149
 
150
150
  #-
@@ -153,7 +153,7 @@ class AzureDriver < Deltacloud::BaseDriver
153
153
  def update_blob_metadata(credentials, opts={})
154
154
  azure_connect(credentials)
155
155
  meta_hash = opts['meta_hash']
156
- meta_hash.gsub_keys("HTTP_X_Deltacloud_Blobmeta_", "x-ms-meta-")
156
+ BlobHelper::rename_metadata_headers(meta_hash, "x-ms-meta-")
157
157
  safely do
158
158
  the_blob = WAZ::Blobs::Container.find(opts['bucket'])[opts[:id]]
159
159
  the_blob.put_metadata!(meta_hash)
@@ -189,7 +189,7 @@ class AzureDriver < Deltacloud::BaseDriver
189
189
  blob_metadata = {}
190
190
  waz_blob.metadata.inject({}) { |result_hash, (k,v)| blob_metadata[k]=v if k.to_s.match(/x_ms_meta/i)}
191
191
  #strip off the x_ms_meta_ from each key
192
- blob_metadata.gsub_keys('x_ms_meta_', '')
192
+ blob_metadata.gsub_keys(/x_ms_meta_/, '')
193
193
  Blob.new({ :id => waz_blob.name,
194
194
  :bucket => bucket,
195
195
  :content_length => waz_blob.metadata[:content_length],
@@ -0,0 +1,273 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ require 'pp'
17
+ require 'nokogiri'
18
+ require 'etc'
19
+ require 'tempfile'
20
+ require 'yaml'
21
+ require 'time'
22
+ require 'deltacloud/drivers/condor/ip_agents/default'
23
+ require 'deltacloud/drivers/condor/ip_agents/confserver'
24
+
25
+ module CondorCloud
26
+
27
+ class CondorAddress
28
+ attr_accessor :ip
29
+ attr_accessor :mac
30
+
31
+ def initialize(opts={})
32
+ @ip, @mac = opts[:ip], opts[:mac]
33
+ end
34
+ end
35
+
36
+
37
+ class DefaultExecutor
38
+
39
+ CONDOR_Q_CMD = ENV['CONDOR_Q_CMD'] || "condor_q"
40
+ CONDOR_RM_CMD = ENV['CONDOR_RM_CMD'] || "condor_rm"
41
+ CONDOR_SUBMIT_CMD = ENV['CONDOR_SUBMIT_CMD'] || 'condor_submit'
42
+
43
+ # This directory needs to be readable for user running Deltacloud API
44
+ CONDOR_CONFIG = ENV['CONDOR_CONFIG'] || 'config/condor.yaml'
45
+
46
+ attr_accessor :ip_agent
47
+ attr_reader :config
48
+
49
+ # You can use your own IP agent using :ip_agent option.
50
+ # IPAgent should have parent class set to 'IPAgent' and implement all
51
+ # methods from this class. You can pass options to ip_agent using
52
+ # :ip_agent_args hash.
53
+ #
54
+ def initialize(opts={}, &block)
55
+ load_config!
56
+ if opts[:ip_agent]
57
+ @ip_agent = opts[:ip_agent]
58
+ else
59
+ default_ip_agent = CondorCloud::const_get(@config[:default_ip_agent])
60
+ @ip_agent = default_ip_agent.new(:config => @config)
61
+ end
62
+ yield self if block_given?
63
+ self
64
+ end
65
+
66
+ def load_config!
67
+ @config = YAML::load(File.open(CONDOR_CONFIG))
68
+ end
69
+
70
+ # List instances using ENV['CONDOR_Q_CMD'] command.
71
+ # Retrieve XML from this command and parse it using Nokogiri. Then this XML
72
+ # is converted to CondorCloud::Instance class
73
+ #
74
+ # @opts - This Hash can be used for filtering instances using :id => 'instance_id'
75
+ #
76
+ def instances(opts={})
77
+ bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
78
+ parse_condor_q_output(bare_xml, opts)
79
+ end
80
+
81
+ # List all files in ENV['STORAGE_DIRECTORY'] or fallback to '/home/cloud/images'
82
+ # Convert files to CondorCloud::Image class
83
+ #
84
+ # @opts - This Hash can be used for filtering images using :id => 'SHA1 of
85
+ # name'
86
+ #
87
+ def images(opts={})
88
+ Dir["#{@config[:image_storage]}/*"].collect do |file|
89
+ next unless File::file?(file)
90
+ next unless File::readable?(file)
91
+ image = Image.new(
92
+ :name => File::basename(file).downcase.tr('.', '-'),
93
+ :owner_id => Etc.getpwuid(File.stat(file).uid).name,
94
+ :description => file
95
+ )
96
+ next if opts[:id] and opts[:id]!=image.id
97
+ image
98
+ end.compact
99
+ end
100
+
101
+ # Launch a new instance in Condor cloud using ENV['CONDOR_SUBMIT_CMD'].
102
+ # Return CondorCloud::Instance.
103
+ #
104
+ # @image - Expecting CondorCloud::Image here
105
+ # @hardware_profile - Expecting CondorCloud::HardwareProfile here
106
+ #
107
+ # @opts - You can specify additional parameters like :name here
108
+ # You can set additional parameters for libvirt using :user_data
109
+ # specified in JSON format.
110
+ #
111
+ # Parameters are:
112
+ #
113
+ # { 'bridge_dev' : 'br0' }
114
+ # { 'smbios' : 'sysinfo' }
115
+ # { 'vnc_port' : '5900' }
116
+ # { 'vnc_ip' : '0.0.0.0' }
117
+ # { 'features' : ['acpi', 'apic', 'pae'] }
118
+ # { 'sysinfo' : { 'bios_vendor' : 'Lenovo', 'system_manufacturer' : 'Virt', 'system_vendor' : 'IBM' } }
119
+ #
120
+ # Of course you can combine them as you want, like (:user_data => "{ 'bridge_dev' : 'br0', 'vnc_ip' : 127.0.0.1 }")
121
+ #
122
+ def launch_instance(image, hardware_profile, opts={})
123
+ raise "Image object must be not nil" unless image
124
+ raise "HardwareProfile object must be not nil" unless hardware_profile
125
+ opts[:name] ||= "i-#{Time.now.to_i}"
126
+
127
+ # This needs to be determined by the mac/ip translation stuff.
128
+ # We need to call into it and have it return these variables, or at least the MAC if not the IP.
129
+ mac_addr = @ip_agent.find_free_mac
130
+ ip_addr = @ip_agent.find_ip_by_mac(mac_addr) if mac_addr && !mac_addr.empty?
131
+
132
+ libvirt_xml = "+VM_XML=\"<domain type='kvm'>
133
+ <name>{NAME}</name>
134
+ <memory>#{hardware_profile.memory.value.to_i * 1024}</memory>
135
+ <vcpu>#{hardware_profile.cpu.value}</vcpu>
136
+ <os>
137
+ <type arch='x86_64'>hvm</type>
138
+ <boot dev='hd'/>
139
+ <smbios mode='sysinfo'/>
140
+ </os>
141
+ <sysinfo type='smbios'>
142
+ <system>
143
+ <entry name='manufacturer'>#{opts[:config_server_address]}</entry>
144
+ <entry name='product'>#{opts[:uuid]}</entry>
145
+ <entry name='serial'>#{opts[:otp]}</entry>
146
+ </system>
147
+ </sysinfo>
148
+ <features>
149
+ <acpi/><apic/><pae/>
150
+ </features>
151
+ <clock offset='utc'/>
152
+ <on_poweroff>destroy</on_poweroff>
153
+ <on_reboot>restart</on_reboot>
154
+ <on_crash>restart</on_crash>
155
+ <devices>
156
+ <disk type='file' device='disk'>
157
+ <source file='{DISK}'/>
158
+ <target dev='vda' bus='virtio'/>
159
+ <driver name='qemu' type='qcow2'/>
160
+ </disk>
161
+ <interface type='bridge'>
162
+ #{"<mac address='" + mac_addr + "'/>" if mac_addr && !mac_addr.empty?}
163
+ <source bridge='#{@config[:default_bridge]}'/>
164
+ </interface>
165
+ <graphics type='vnc' port='#{@config[:vnc_listen_port]}' autoport='yes' keymap='en-us' listen='#{@config[:vnc_listen_ip]}'/>
166
+ </devices>
167
+ </domain>\"".gsub(/(\s{2,})/, ' ').gsub(/\>\s\</, '><')
168
+
169
+ # I use the 2>&1 to get stderr and stdout together because popen3 does not support
170
+ # the ability to get the exit value of the command in ruby 1.8.
171
+ pipe = IO.popen("#{CONDOR_SUBMIT_CMD} 2>&1", "w+")
172
+ pipe.puts "universe=vm"
173
+ pipe.puts "vm_type=kvm"
174
+ pipe.puts "vm_memory=#{hardware_profile.memory.value}"
175
+ pipe.puts "request_cpus=#{hardware_profile.cpu.value}"
176
+ pipe.puts "vm_disk=#{image.description}:null:null"
177
+ pipe.puts "executable=#{image.description}"
178
+ pipe.puts "vm_macaddr=#{mac_addr}"
179
+
180
+ # Only set the ip if it is available, and this should depend on the IP mapping used.
181
+ # With the fixed mapping method we know the IP address right away before we start the
182
+ # instance, so fill it in here. If it is not set I think we should set it to an empty
183
+ # string and we'll fill it in later using a condor tool to update the job.
184
+ pipe.puts "+vm_ipaddr=\"#{ip_addr}\""
185
+ pipe.puts '+HookKeyword="CLOUD"'
186
+ pipe.puts "+Cmd=\"#{opts[:name]}\""
187
+ # Really the image should not be a full path to begin with I think..
188
+ pipe.puts "+cloud_image=\"#{File.basename(image.description)}\""
189
+ pipe.puts libvirt_xml
190
+ pipe.puts "queue"
191
+ pipe.puts ""
192
+ pipe.close_write
193
+ out = pipe.read
194
+ pipe.close
195
+
196
+ if $? != 0
197
+ raise "Error starting VM in condor_submit: #{out}"
198
+ end
199
+
200
+ bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
201
+ parse_condor_q_output(bare_xml, :name => opts[:name])
202
+ end
203
+
204
+ def destroy_instance(instance_id)
205
+ bare_xml = Nokogiri::XML(`#{CONDOR_Q_CMD} -xml`)
206
+ cluster_id = (bare_xml/'/classads/c/a[@n="GlobalJobId"]/s').collect do |id|
207
+ id.text.split('#')[1] if id.text.split('#').last==instance_id
208
+ end.compact.first
209
+ `#{CONDOR_RM_CMD} #{cluster_id}`
210
+ end
211
+
212
+ # List hardware profiles available for Condor.
213
+ # Basically those profiles are static 'small', 'medium' and 'large'
214
+ #
215
+ # Defined as:
216
+ #
217
+ # when { :memory => '512', :cpus => '1' } then 'small'
218
+ # when { :memory => '1024', :cpus => '2' } then 'medium'
219
+ # when { :memory => '2047', :cpus => '4' } then 'large'
220
+ #
221
+ # @opts - You can filter hardware_profiles using :id
222
+ #
223
+ def hardware_profiles(opts={})
224
+ return [
225
+ { :name => 'small', :cpus => 1, :memory => 512 },
226
+ { :name => 'medium', :cpus => 2, :memory => 1024 },
227
+ { :name => 'large', :cpus => 4, :memory => 2048 }
228
+ ]
229
+ end
230
+
231
+ private
232
+
233
+ def convert_image_name_to_id(name)
234
+ Digest::SHA1.hexdigest(name).to_s
235
+ end
236
+
237
+ def parse_condor_q_output(bare_xml, opts={})
238
+ inst_array = []
239
+ (bare_xml/"/classads/c").each do |c|
240
+ unless opts[:id].nil?
241
+ next unless (c/'a[@n="GlobalJobId"]/s').text.strip.split('#').last==opts[:id]
242
+ end
243
+ unless opts[:name].nil?
244
+ next unless (c/'a[@n="Cmd"]/s').text.strip==opts[:name]
245
+ end
246
+ # Even with the checks above this can still fail because there may be other condor jobs
247
+ # in the queue formatted in ways we don't know.
248
+ begin
249
+ inst_array << Instance.new(
250
+ :id => (c/'a[@n="GlobalJobId"]/s').text.strip.split('#').last,
251
+ :name => (c/'a[@n="Cmd"]/s').text.strip,
252
+ :state => Instance::convert_condor_state((c/'a[@n="JobStatus"]/i').text.to_i),
253
+ :public_addresses => [
254
+ CondorAddress.new(:mac => (c/'a[@n="JobVM_MACADDR"]/s').text, :ip => (c/'a[@n="vm_ipaddr"]/s').text)
255
+ ],
256
+ :instance_profile => HardwareProfile.new(:hwp_memory => (c/'a[@n="JobVMMemory"]/i').text, :hwp_cpu => (c/'a[@n="JobVM_VCPUS"]/i').text),
257
+ :owner_id => (c/'a[@n="User"]/s').text,
258
+ :image_id => convert_image_name_to_id(File::basename((c/'a[@n="VMPARAM_vm_Disk"]/s').text.split(':').first).downcase.tr('.', '-')),
259
+ :realm => Realm.new(:id => (c/'a[@n="JobVMType"]/s').text),
260
+ :launch_time => Time.at((c/'a[@n="JobStartDate"]/i').text.to_i)
261
+ )
262
+ rescue Exception => e
263
+ puts "Caught exception (may be safe to ignore if other jobs present): #{e}"
264
+ puts e.message
265
+ puts e.backtrace
266
+ # Be nice to log something here in case we start getting silent failures.
267
+ end
268
+ end
269
+ inst_array
270
+ end
271
+
272
+ end
273
+ end