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.
- data/LICENSE +145 -0
- data/NOTICE +10 -1
- data/Rakefile +50 -2
- data/bin/deltacloudd +111 -14
- data/config/addresses.xml +14 -0
- data/config/condor.yaml +30 -0
- data/config/drivers/azure.yaml +3 -0
- data/config/drivers/condor.yaml +3 -0
- data/config/{drivers.yaml → drivers/ec2.yaml} +5 -34
- data/config/drivers/eucalyptus.yaml +8 -0
- data/config/drivers/gogrid.yaml +3 -0
- data/config/drivers/mock.yaml +3 -0
- data/config/drivers/opennebula.yaml +4 -0
- data/config/drivers/rackspace.yaml +3 -0
- data/config/drivers/rhevm.yaml +3 -0
- data/config/drivers/rimuhosting.yaml +3 -0
- data/config/drivers/sbc.yaml +2 -0
- data/config/drivers/terremark.yaml +3 -0
- data/config/drivers/vsphere.yaml +8 -0
- data/deltacloud-core.gemspec +13 -5
- data/deltacloud.rb +4 -2
- data/lib/deltacloud/backend_capability.rb +2 -2
- data/lib/deltacloud/base_driver/base_driver.rb +23 -52
- data/lib/deltacloud/base_driver/exceptions.rb +168 -0
- data/lib/deltacloud/base_driver/features.rb +31 -12
- data/lib/deltacloud/base_driver/mock_driver.rb +2 -1
- data/lib/deltacloud/core_ext/string.rb +2 -0
- data/lib/deltacloud/drivers/azure/azure_driver.rb +5 -5
- data/lib/deltacloud/drivers/condor/condor_client.rb +273 -0
- data/lib/deltacloud/drivers/condor/condor_driver.rb +236 -0
- data/lib/deltacloud/drivers/condor/ip_agents/confserver.rb +75 -0
- data/lib/deltacloud/drivers/condor/ip_agents/default.rb +84 -0
- data/lib/deltacloud/drivers/ec2/ec2_driver.rb +326 -95
- data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +3 -3
- data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +40 -8
- data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +7 -7
- data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +42 -25
- data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob1.yml +6 -4
- data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob2.yml +7 -5
- data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob3.yml +6 -4
- data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob4.yml +6 -4
- data/lib/deltacloud/drivers/mock/data/{buckets/blobs → blobs}/blob5.yml +6 -4
- data/lib/deltacloud/drivers/mock/data/buckets/bucket1.yml +7 -1
- data/lib/deltacloud/drivers/mock/data/buckets/bucket2.yml +6 -1
- data/lib/deltacloud/drivers/mock/data/images/img1.yml +6 -2
- data/lib/deltacloud/drivers/mock/data/images/img2.yml +6 -2
- data/lib/deltacloud/drivers/mock/data/images/img3.yml +6 -2
- data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +11 -10
- data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +14 -7
- data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +14 -7
- data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap1.yml +3 -2
- data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap2.yml +3 -2
- data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap3.yml +3 -2
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +4 -3
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +4 -3
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +4 -3
- data/lib/deltacloud/drivers/mock/mock_client.rb +101 -0
- data/lib/deltacloud/drivers/mock/mock_driver.rb +367 -429
- data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +6 -0
- data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +59 -9
- data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +62 -8
- data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +100 -45
- data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +3 -2
- data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +8 -11
- data/lib/deltacloud/drivers/sbc/sbc_client.rb +6 -6
- data/lib/deltacloud/drivers/sbc/sbc_driver.rb +16 -0
- data/lib/deltacloud/drivers/terremark/terremark_driver.rb +17 -12
- data/lib/deltacloud/drivers/vsphere/vsphere_client.rb +140 -0
- data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +405 -0
- data/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb +182 -0
- data/lib/deltacloud/hardware_profile.rb +1 -1
- data/lib/deltacloud/helpers.rb +2 -1
- data/lib/deltacloud/helpers/application_helper.rb +92 -20
- data/lib/deltacloud/helpers/blob_stream.rb +160 -12
- data/lib/deltacloud/helpers/conversion_helper.rb +6 -2
- data/lib/deltacloud/helpers/json_helper.rb +31 -0
- data/lib/deltacloud/models/address.rb +28 -0
- data/lib/deltacloud/models/base_model.rb +5 -1
- data/lib/deltacloud/models/blob.rb +1 -1
- data/lib/deltacloud/models/bucket.rb +10 -0
- data/lib/deltacloud/models/firewall.rb +22 -0
- data/lib/deltacloud/models/firewall_rule.rb +23 -0
- data/lib/deltacloud/models/image.rb +12 -0
- data/lib/deltacloud/models/instance.rb +20 -2
- data/lib/deltacloud/models/key.rb +1 -1
- data/lib/deltacloud/runner.rb +3 -3
- data/lib/deltacloud/validation.rb +3 -7
- data/lib/drivers.rb +7 -1
- data/lib/sinatra/body_proxy.rb +34 -0
- data/lib/sinatra/lazy_auth.rb +5 -0
- data/lib/sinatra/rabbit.rb +54 -31
- data/lib/sinatra/rack_accept.rb +157 -0
- data/lib/sinatra/rack_date.rb +38 -0
- data/lib/sinatra/rack_etag.rb +2 -3
- data/lib/sinatra/rack_matrix_params.rb +51 -29
- data/lib/sinatra/rack_runtime.rb +1 -1
- data/lib/sinatra/rack_syslog.rb +86 -0
- data/lib/sinatra/url_for.rb +14 -1
- data/public/images/address.png +0 -0
- data/public/images/balancer.png +0 -0
- data/public/images/blob.png +0 -0
- data/public/images/bucket.png +0 -0
- data/public/images/cloud.png +0 -0
- data/public/images/firewall.png +0 -0
- data/public/images/image.png +0 -0
- data/public/images/key.png +0 -0
- data/public/images/machine.png +0 -0
- data/public/images/profile.png +0 -0
- data/public/images/realm.png +0 -0
- data/public/images/snapshot.png +0 -0
- data/public/images/volume.png +0 -0
- data/public/javascripts/application.js +119 -16
- data/public/javascripts/jquery.min.js +18 -0
- data/public/javascripts/jquery.mobile-1.0b1.min.js +146 -0
- data/public/stylesheets/compiled/application.css +8 -0
- data/public/stylesheets/images/ajax-loader.png +0 -0
- data/public/{images → stylesheets/images}/bread-bg.png +0 -0
- data/public/{images → stylesheets/images}/error.png +0 -0
- data/public/{images → stylesheets/images}/grid.png +0 -0
- data/public/stylesheets/images/icon-search-black.png +0 -0
- data/public/stylesheets/images/icons-18-black.png +0 -0
- data/public/stylesheets/images/icons-18-white.png +0 -0
- data/public/stylesheets/images/icons-36-black.png +0 -0
- data/public/stylesheets/images/icons-36-white.png +0 -0
- data/public/{images → stylesheets/images}/logo-wide.png +0 -0
- data/public/{images → stylesheets/images}/pending.png +0 -0
- data/public/{images → stylesheets/images}/rails.png +0 -0
- data/public/{images → stylesheets/images}/running.png +0 -0
- data/public/{images → stylesheets/images}/stopped.png +0 -0
- data/public/{images → stylesheets/images}/topbar-bg.png +0 -0
- data/public/stylesheets/jquery.mobile-1.0b1.min.css +8 -0
- data/public/stylesheets/new.css +53 -0
- data/server.rb +487 -175
- data/support/condor/bash/cached_images.sh +8 -0
- data/support/condor/bash/cloud_exit_hook.sh +17 -0
- data/support/condor/bash/cloud_functions +175 -0
- data/support/condor/bash/cloud_prepare_hook.sh +20 -0
- data/support/condor/bash/libvirt_cloud_script.sh +13 -0
- data/support/condor/config/50condor_cloud.config +37 -0
- data/support/condor/config/50condor_cloud_node.config +37 -0
- data/support/condor/config/condor-cloud +2 -0
- data/support/condor/config/condor_config.local +44 -0
- data/support/fedora/deltacloud-core +48 -26
- data/support/fedora/deltacloud-core-config +26 -0
- data/support/fedora/deltacloud-core.spec +314 -68
- data/support/fedora/deltacloudd-fedora +5 -0
- data/tests/common.rb +34 -4
- data/tests/drivers/mock/api_test.rb +3 -3
- data/tests/drivers/mock/images_test.rb +12 -0
- data/tests/drivers/mock/instances_test.rb +2 -0
- data/tests/rabbit_test.rb +2 -2
- data/views/addresses/_address.html.haml +6 -0
- data/views/addresses/associate.html.haml +12 -0
- data/views/addresses/index.html.haml +9 -0
- data/views/addresses/index.xml.haml +4 -0
- data/views/addresses/show.html.haml +21 -0
- data/views/addresses/show.xml.haml +14 -0
- data/views/api/show.html.haml +6 -11
- data/views/api/show.xml.haml +2 -0
- data/views/blobs/new.html.haml +24 -23
- data/views/blobs/show.html.haml +30 -31
- data/views/buckets/index.html.haml +9 -21
- data/views/buckets/index.xml.haml +3 -7
- data/views/buckets/new.html.haml +13 -12
- data/views/buckets/show.html.haml +22 -22
- data/views/buckets/show.xml.haml +5 -3
- data/views/docs/collection.html.haml +23 -34
- data/views/docs/collection.xml.haml +2 -2
- data/views/docs/index.html.haml +9 -13
- data/views/docs/index.xml.haml +1 -1
- data/views/docs/operation.html.haml +28 -38
- data/views/docs/operation.xml.haml +1 -1
- data/views/drivers/index.html.haml +8 -13
- data/views/drivers/show.html.haml +18 -18
- data/views/error.html.haml +32 -27
- data/views/errors/400.html.haml +41 -0
- data/views/errors/{validation_failure.xml.haml → 400.xml.haml} +0 -4
- data/views/errors/401.html.haml +41 -0
- data/views/errors/{auth_exception.xml.haml → 401.xml.haml} +0 -0
- data/views/errors/403.html.haml +42 -0
- data/views/errors/{not_allowed.xml.haml → 403.xml.haml} +0 -0
- data/views/errors/404.html.haml +29 -0
- data/views/errors/{not_found.xml.haml → 404.xml.haml} +1 -1
- data/views/errors/405.html.haml +29 -0
- data/views/errors/405.xml.haml +5 -0
- data/views/errors/500.html.haml +43 -0
- data/views/errors/500.xml.haml +5 -0
- data/views/errors/502.html.haml +43 -0
- data/views/errors/{backend_error.xml.haml → 502.xml.haml} +1 -2
- data/views/errors/backend_capability_failure.html.haml +27 -9
- data/views/firewalls/index.html.haml +15 -0
- data/views/firewalls/index.xml.haml +28 -0
- data/views/firewalls/new.html.haml +11 -0
- data/views/firewalls/new_rule.html.haml +20 -0
- data/views/firewalls/show.html.haml +42 -0
- data/views/firewalls/show.xml.haml +26 -0
- data/views/hardware_profiles/index.html.haml +15 -23
- data/views/hardware_profiles/show.html.haml +22 -18
- data/views/images/index.html.haml +11 -23
- data/views/images/index.xml.haml +4 -13
- data/views/images/new.html.haml +12 -13
- data/views/images/show.html.haml +26 -20
- data/views/images/show.xml.haml +2 -1
- data/views/instance_states/show.html.haml +21 -25
- data/views/instances/index.html.haml +13 -30
- data/views/instances/index.xml.haml +2 -23
- data/views/instances/new.html.haml +83 -88
- data/views/instances/show.html.haml +53 -55
- data/views/instances/show.xml.haml +12 -10
- data/views/keys/index.html.haml +13 -24
- data/views/keys/new.html.haml +7 -7
- data/views/keys/show.html.haml +26 -21
- data/views/layout.html.haml +28 -27
- data/views/load_balancers/index.html.haml +11 -31
- data/views/load_balancers/index.xml.haml +0 -1
- data/views/load_balancers/new.html.haml +1 -1
- data/views/load_balancers/show.html.haml +33 -34
- data/views/load_balancers/show.xml.haml +2 -2
- data/views/realms/index.html.haml +11 -24
- data/views/realms/index.xml.haml +2 -8
- data/views/realms/show.html.haml +17 -15
- data/views/realms/show.xml.haml +2 -1
- data/views/storage_snapshots/index.html.haml +11 -21
- data/views/storage_snapshots/index.xml.haml +2 -5
- data/views/storage_snapshots/new.html.haml +1 -1
- data/views/storage_snapshots/show.html.haml +21 -13
- data/views/storage_snapshots/show.xml.haml +2 -1
- data/views/storage_volumes/index.html.haml +11 -34
- data/views/storage_volumes/new.html.haml +1 -1
- data/views/storage_volumes/show.html.haml +33 -27
- data/views/storage_volumes/show.xml.haml +2 -1
- metadata +266 -178
- data/lib/sinatra/respond_to.rb +0 -248
- data/support/fedora/deltacloudd +0 -128
- data/support/fedora/rubygem-deltacloud-core.spec +0 -127
- data/views/accounts/index.html.haml +0 -11
- data/views/accounts/show.html.haml +0 -30
- data/views/errors/auth_exception.html.haml +0 -8
- data/views/errors/backend_error.html.haml +0 -22
- data/views/errors/not_allowed.html.haml +0 -6
- data/views/errors/not_found.html.haml +0 -6
- 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,
|
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,
|
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,
|
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,
|
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, :
|
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 :
|
190
|
-
"Array of security group
|
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,
|
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
|
|
@@ -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
|
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(
|
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(
|
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
|
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(
|
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
|