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.
- data/DISCLAIMER +8 -0
- data/{COPYING → LICENSE} +0 -0
- data/NOTICE +13 -0
- data/Rakefile +50 -51
- data/bin/deltacloudd +8 -1
- data/config.ru +0 -2
- data/config/drivers.yaml +48 -0
- data/deltacloud-core.gemspec +75 -0
- data/deltacloud.rb +3 -2
- data/lib/deltacloud/backend_capability.rb +15 -3
- data/lib/deltacloud/base_driver.rb +0 -2
- data/lib/deltacloud/base_driver/base_driver.rb +85 -89
- data/lib/deltacloud/base_driver/features.rb +61 -7
- data/lib/deltacloud/base_driver/mock_driver.rb +42 -43
- data/lib/deltacloud/core_ext.rb +18 -0
- data/lib/deltacloud/core_ext/integer.rb +31 -0
- data/lib/deltacloud/core_ext/string.rb +50 -0
- data/lib/deltacloud/drivers/azure/azure_driver.rb +71 -22
- data/lib/deltacloud/drivers/ec2/ec2_driver.rb +641 -584
- data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +0 -2
- data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +167 -0
- data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +39 -1
- data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +41 -25
- data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob1.yml +6 -3
- data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob2.yml +6 -3
- data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob3.yml +4 -2
- data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob4.yml +5 -2
- data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob5.yml +4 -2
- data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +1 -0
- data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +1 -0
- data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +1 -0
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +1 -0
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +1 -0
- data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +1 -0
- data/lib/deltacloud/drivers/mock/mock_driver.rb +138 -30
- data/lib/deltacloud/drivers/opennebula/cloud_client.rb +13 -15
- data/lib/deltacloud/drivers/opennebula/occi_client.rb +13 -15
- data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +13 -15
- data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +224 -113
- data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +332 -0
- data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +221 -170
- data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +0 -1
- data/lib/deltacloud/drivers/sbc/sbc_client.rb +247 -0
- data/lib/deltacloud/drivers/sbc/sbc_driver.rb +297 -0
- data/lib/deltacloud/drivers/terremark/terremark_driver.rb +0 -2
- data/lib/deltacloud/hardware_profile.rb +1 -3
- data/lib/deltacloud/helpers.rb +0 -2
- data/lib/deltacloud/helpers/application_helper.rb +86 -12
- data/lib/deltacloud/helpers/blob_stream.rb +19 -2
- data/lib/deltacloud/helpers/conversion_helper.rb +0 -2
- data/lib/deltacloud/helpers/hardware_profiles_helper.rb +0 -2
- data/lib/deltacloud/method_serializer.rb +0 -2
- data/lib/deltacloud/models/base_model.rb +0 -2
- data/lib/deltacloud/models/blob.rb +1 -2
- data/lib/deltacloud/models/bucket.rb +0 -2
- data/lib/deltacloud/models/image.rb +0 -2
- data/lib/deltacloud/models/instance.rb +19 -2
- data/lib/deltacloud/models/instance_profile.rb +4 -2
- data/lib/deltacloud/models/key.rb +0 -2
- data/lib/deltacloud/models/load_balancer.rb +0 -2
- data/lib/deltacloud/models/realm.rb +0 -2
- data/lib/deltacloud/models/storage_snapshot.rb +0 -2
- data/lib/deltacloud/models/storage_volume.rb +4 -2
- data/lib/deltacloud/runner.rb +132 -0
- data/lib/deltacloud/state_machine.rb +0 -2
- data/lib/deltacloud/validation.rb +9 -7
- data/lib/drivers.rb +36 -48
- data/lib/sinatra/accept_media_types.rb +26 -0
- data/lib/sinatra/lazy_auth.rb +16 -0
- data/lib/sinatra/rabbit.rb +112 -54
- data/lib/sinatra/rack_driver_select.rb +50 -16
- data/lib/sinatra/rack_etag.rb +79 -0
- data/lib/sinatra/rack_matrix_params.rb +84 -0
- data/lib/sinatra/rack_runtime.rb +47 -0
- data/lib/sinatra/static_assets.rb +16 -0
- data/lib/sinatra/url_for.rb +31 -4
- data/public/favicon.ico +0 -0
- data/public/images/bread-bg.png +0 -0
- data/public/images/error.png +0 -0
- data/public/images/pending.png +0 -0
- data/public/images/running.png +0 -0
- data/public/images/stopped.png +0 -0
- data/public/javascripts/application.js +35 -0
- data/public/stylesheets/compiled/application.css +59 -5
- data/public/stylesheets/compiled/screen.css +1 -1
- data/server.rb +293 -29
- data/support/fedora/deltacloud-core +78 -0
- data/support/fedora/deltacloud-core.spec +143 -0
- data/support/fedora/deltacloudd +78 -18
- data/support/fedora/rubygem-deltacloud-core.spec +76 -40
- data/tests/common.rb +172 -0
- data/tests/drivers/mock/api_test.rb +133 -0
- data/tests/drivers/mock/hardware_profiles_test.rb +134 -0
- data/tests/drivers/mock/images_test.rb +126 -0
- data/tests/drivers/mock/instance_states_test.rb +71 -0
- data/tests/drivers/mock/instances_test.rb +236 -0
- data/tests/drivers/mock/realms_test.rb +93 -0
- data/tests/drivers/mock/setup.rb +3 -0
- data/tests/drivers/mock/url_for_test.rb +67 -0
- data/tests/drivers/rackspace/api_test.rb +41 -0
- data/tests/drivers/rackspace/hardware_profiles_test.rb +53 -0
- data/tests/drivers/rackspace/images_test.rb +40 -0
- data/tests/drivers/rackspace/instances_test.rb +161 -0
- data/tests/drivers/rackspace/realms_test.rb +36 -0
- data/tests/drivers/rackspace/setup.rb +14 -0
- data/tests/drivers/rhevm/api_test.rb +39 -0
- data/tests/drivers/rhevm/hardware_profiles_test.rb +53 -0
- data/tests/drivers/rhevm/images_test.rb +42 -0
- data/tests/drivers/rhevm/instances_test.rb +179 -0
- data/tests/drivers/rhevm/realms_test.rb +35 -0
- data/tests/drivers/rhevm/setup.rb +14 -0
- data/tests/rabbit_test.rb +52 -0
- data/views/api/show.html.haml +2 -5
- data/views/blobs/new.html.haml +17 -1
- data/views/blobs/show.html.haml +6 -0
- data/views/blobs/show.xml.haml +5 -1
- data/views/buckets/index.html.haml +1 -12
- data/views/buckets/index.xml.haml +3 -5
- data/views/docs/operation.html.haml +23 -11
- data/views/drivers/index.html.haml +15 -0
- data/views/drivers/index.xml.haml +7 -0
- data/views/drivers/show.html.haml +20 -0
- data/views/drivers/show.xml.haml +7 -0
- data/views/error.html.haml +31 -0
- data/views/errors/auth_exception.xml.haml +2 -1
- data/views/errors/backend_capability_failure.xml.haml +2 -1
- data/views/errors/backend_error.html.haml +3 -0
- data/views/errors/backend_error.xml.haml +2 -2
- data/views/errors/validation_failure.xml.haml +3 -2
- data/views/images/index.html.haml +1 -6
- data/views/images/index.xml.haml +2 -0
- data/views/images/new.html.haml +14 -0
- data/views/images/show.xml.haml +2 -0
- data/views/instances/index.html.haml +8 -6
- data/views/instances/index.xml.haml +4 -0
- data/views/instances/new.html.haml +40 -11
- data/views/instances/run.html.haml +9 -0
- data/views/instances/run.xml.haml +7 -0
- data/views/instances/run_command.html.haml +16 -0
- data/views/instances/show.html.haml +14 -0
- data/views/instances/show.xml.haml +12 -4
- data/views/layout.html.haml +7 -2
- data/views/load_balancers/index.html.haml +1 -1
- data/views/load_balancers/new.html.haml +2 -2
- data/views/load_balancers/show.html.haml +1 -1
- data/views/storage_snapshots/index.html.haml +3 -0
- data/views/storage_snapshots/new.html.haml +9 -0
- data/views/storage_volumes/attach.html.haml +20 -0
- data/views/storage_volumes/index.html.haml +16 -1
- data/views/storage_volumes/index.xml.haml +1 -10
- data/views/storage_volumes/new.html.haml +17 -0
- data/views/storage_volumes/show.html.haml +8 -0
- data/views/storage_volumes/show.xml.haml +25 -3
- metadata +197 -127
- data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +0 -130
- data/parse.rb +0 -7
- data/test.rb +0 -3
- data/views/api/drivers.xml.haml +0 -6
@@ -1,6 +1,4 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2009,2010 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
|
@@ -63,7 +61,7 @@ module Deltacloud
|
|
63
61
|
end
|
64
62
|
|
65
63
|
def param
|
66
|
-
"hwp_#{name}"
|
64
|
+
:"hwp_#{name}"
|
67
65
|
end
|
68
66
|
|
69
67
|
def fixed?
|
data/lib/deltacloud/helpers.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# Copyright (C) 2009, 2010 Red Hat, Inc.
|
2
|
-
#
|
3
1
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
4
2
|
# contributor license agreements. See the NOTICE file distributed with
|
5
3
|
# this work for additional information regarding copyright ownership. The
|
@@ -17,8 +15,12 @@
|
|
17
15
|
|
18
16
|
# Methods added to this helper will be available to all templates in the application.
|
19
17
|
|
18
|
+
require 'benchmark'
|
19
|
+
|
20
20
|
module ApplicationHelper
|
21
21
|
|
22
|
+
include Deltacloud
|
23
|
+
|
22
24
|
def bread_crumb
|
23
25
|
s = "<ul class='breadcrumb'><li class='first'><a href='#{url_for('/')}'>δ</a></li>"
|
24
26
|
url = request.path_info.split('?') #remove extra query string parameters
|
@@ -34,11 +36,16 @@ module ApplicationHelper
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
39
|
+
s+="<li class='docs'>#{link_to_documentation}</li>"
|
37
40
|
s+="</ul>"
|
38
41
|
end
|
39
42
|
|
40
43
|
def instance_action_method(action)
|
41
|
-
|
44
|
+
action_method(action, :instances)
|
45
|
+
end
|
46
|
+
|
47
|
+
def action_method(action, collection)
|
48
|
+
collections[collection].operations[action.to_sym].method
|
42
49
|
end
|
43
50
|
|
44
51
|
def driver_has_feature?(feature_name, collection_name = :instances)
|
@@ -67,9 +74,12 @@ module ApplicationHelper
|
|
67
74
|
filter.merge!(:architecture => params[:architecture]) if params[:architecture]
|
68
75
|
filter.merge!(:owner_id => params[:owner_id]) if params[:owner_id]
|
69
76
|
filter.merge!(:state => params[:state]) if params[:state]
|
70
|
-
filter =
|
77
|
+
filter = {} if filter.keys.size.eql?(0)
|
71
78
|
singular = model.to_s.singularize.to_sym
|
72
|
-
@
|
79
|
+
@benchmark = Benchmark.measure do
|
80
|
+
@elements = driver.send(model.to_sym, credentials, filter)
|
81
|
+
end
|
82
|
+
headers['X-Backend-Runtime'] = @benchmark.real.to_s
|
73
83
|
instance_variable_set(:"@#{model}", @elements)
|
74
84
|
respond_to do |format|
|
75
85
|
format.html { haml :"#{model}/index" }
|
@@ -79,7 +89,9 @@ module ApplicationHelper
|
|
79
89
|
end
|
80
90
|
|
81
91
|
def show(model)
|
82
|
-
@
|
92
|
+
@benchmark = Benchmark.measure do
|
93
|
+
@element = driver.send(model, credentials, { :id => params[:id]} )
|
94
|
+
end
|
83
95
|
instance_variable_set("@#{model}", @element)
|
84
96
|
if @element
|
85
97
|
respond_to do |format|
|
@@ -97,7 +109,7 @@ module ApplicationHelper
|
|
97
109
|
response.status = status
|
98
110
|
respond_to do |format|
|
99
111
|
format.xml { haml :"errors/#{template}", :layout => false }
|
100
|
-
format.html { haml :"errors/#{template}" }
|
112
|
+
format.html { haml :"errors/#{template}", :layout => :error }
|
101
113
|
end
|
102
114
|
end
|
103
115
|
|
@@ -112,7 +124,13 @@ module ApplicationHelper
|
|
112
124
|
|
113
125
|
@instance = driver.send(:"#{name}_instance", credentials, params["id"])
|
114
126
|
|
115
|
-
|
127
|
+
if name == :destroy or @instance.class!=Instance
|
128
|
+
respond_to do |format|
|
129
|
+
format.xml { return 204 }
|
130
|
+
format.json { return 204 }
|
131
|
+
format.html { return redirect(instances_url) }
|
132
|
+
end
|
133
|
+
end
|
116
134
|
|
117
135
|
respond_to do |format|
|
118
136
|
format.xml { haml :"instances/show" }
|
@@ -121,8 +139,8 @@ module ApplicationHelper
|
|
121
139
|
end
|
122
140
|
end
|
123
141
|
|
124
|
-
def cdata(&block)
|
125
|
-
text
|
142
|
+
def cdata(text = nil, &block)
|
143
|
+
text ||= capture_haml(&block)
|
126
144
|
"<![CDATA[#{text.strip}]]>"
|
127
145
|
end
|
128
146
|
|
@@ -131,9 +149,8 @@ module ApplicationHelper
|
|
131
149
|
end
|
132
150
|
|
133
151
|
def link_to_action(action, url, method)
|
134
|
-
return link_to(action, url) if method.eql? :get
|
135
152
|
capture_haml do
|
136
|
-
haml_tag :form, :method => :post, :action => url, :class => :link do
|
153
|
+
haml_tag :form, :method => :post, :action => url, :class => [:link, method] do
|
137
154
|
haml_tag :input, :type => :hidden, :name => '_method', :value => method
|
138
155
|
haml_tag :button, :type => :submit do
|
139
156
|
haml_concat action
|
@@ -142,4 +159,61 @@ module ApplicationHelper
|
|
142
159
|
end
|
143
160
|
end
|
144
161
|
|
162
|
+
def link_to_format(format)
|
163
|
+
return '' unless request.env['REQUEST_URI']
|
164
|
+
uri = request.env['REQUEST_URI']
|
165
|
+
return if uri.include?('format=')
|
166
|
+
if uri.include?('?')
|
167
|
+
uri+="&format=#{format}"
|
168
|
+
else
|
169
|
+
uri+="?format=#{format}"
|
170
|
+
end
|
171
|
+
'<a href="%s">%s</a>' % [uri, "#{format}".upcase]
|
172
|
+
end
|
173
|
+
|
174
|
+
def link_to_documentation
|
175
|
+
return '' unless request.env['REQUEST_URI']
|
176
|
+
uri = request.env['REQUEST_URI'].dup
|
177
|
+
uri.gsub!('/api', '/api/docs/') unless uri.include?("docs") #i.e. if already serving under /api/docs, leave it be
|
178
|
+
'<a href="%s">[ Documentation ]</a>' % uri
|
179
|
+
end
|
180
|
+
|
181
|
+
def action_url
|
182
|
+
if [:index].include?(@operation.name)
|
183
|
+
url_for("/api/#{@collection.name.to_s}")
|
184
|
+
elsif [:show, :stop, :start, :reboot, :attach, :detach].include?(@operation.name)
|
185
|
+
url_for("/api/#{@collection.name.to_s}/:id/#{@operation.name}")
|
186
|
+
elsif [:destroy].include?(@operation.name)
|
187
|
+
url_for("/api/#{@collection.name.to_s}/:id")
|
188
|
+
else
|
189
|
+
url_for("/api/#{@collection.name}/#{@operation.name}")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def image_for_state(state)
|
194
|
+
state_img = "stopped" if (state!='RUNNING' or state!='PENDING')
|
195
|
+
"<img src='/images/#{state.downcase}.png' title='#{state}'/>"
|
196
|
+
end
|
197
|
+
|
198
|
+
def truncate_words(text, length = 10)
|
199
|
+
return nil unless text
|
200
|
+
return text if text.length<=length
|
201
|
+
end_string = "...#{text[(text.length-(length/2))..text.length]}"
|
202
|
+
"#{text[0..(length/2)]}#{end_string}"
|
203
|
+
end
|
204
|
+
|
205
|
+
# Reverse the entrypoints hash for a driver from drivers.yaml; note that
|
206
|
+
# +d+ is a hash, not an actual driver object
|
207
|
+
def driver_provider(d)
|
208
|
+
result = {}
|
209
|
+
if d[:entrypoints]
|
210
|
+
d[:entrypoints].each do |kind, details|
|
211
|
+
details.each do |prov, url|
|
212
|
+
result[prov] ||= {}
|
213
|
+
result[prov][kind] = url
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
result
|
218
|
+
end
|
145
219
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# Copyright (C) 2010 Red Hat, Inc.
|
2
|
-
#
|
3
1
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
4
2
|
# contributor license agreements. See the NOTICE file distributed with
|
5
3
|
# this work for additional information regarding copyright ownership. The
|
@@ -15,6 +13,7 @@
|
|
15
13
|
# License for the specific language governing permissions and limitations
|
16
14
|
# under the License.
|
17
15
|
|
16
|
+
include Deltacloud
|
18
17
|
begin
|
19
18
|
require 'eventmachine'
|
20
19
|
#--
|
@@ -62,3 +61,21 @@ rescue LoadError => e
|
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
64
|
+
|
65
|
+
class Hash
|
66
|
+
|
67
|
+
def gsub_keys(pattern, replacement)
|
68
|
+
rgx_pattern = Regexp.compile(pattern, true)
|
69
|
+
remove = []
|
70
|
+
self.each_key do |key|
|
71
|
+
if key.to_s.match(rgx_pattern)
|
72
|
+
new_key = key.to_s.gsub(rgx_pattern, replacement)
|
73
|
+
self[new_key] = self[key]
|
74
|
+
remove << key
|
75
|
+
end #key.match
|
76
|
+
end # each_key do
|
77
|
+
#remove the original keys
|
78
|
+
self.delete_if{|k,v| remove.include?(k)}
|
79
|
+
end #def
|
80
|
+
|
81
|
+
end #class
|
@@ -1,6 +1,4 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2009, 2010 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
|
@@ -24,5 +22,6 @@ class Blob < BaseModel
|
|
24
22
|
attr_accessor :content_type
|
25
23
|
attr_accessor :last_modified
|
26
24
|
attr_accessor :content
|
25
|
+
attr_accessor :user_metadata
|
27
26
|
|
28
27
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2009, 2010 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
|
@@ -30,6 +28,25 @@ class Instance < BaseModel
|
|
30
28
|
attr_accessor :launch_time
|
31
29
|
attr_accessor :keyname
|
32
30
|
attr_accessor :authn_error
|
31
|
+
attr_accessor :username
|
32
|
+
attr_accessor :password
|
33
|
+
attr_accessor :create_image
|
34
|
+
|
35
|
+
def can_create_image?
|
36
|
+
self.create_image
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
name
|
41
|
+
end
|
42
|
+
|
43
|
+
def hardware_profile
|
44
|
+
instance_profile
|
45
|
+
end
|
46
|
+
|
47
|
+
def hardware_profile=(profile)
|
48
|
+
instance_profile = profile
|
49
|
+
end
|
33
50
|
|
34
51
|
def initialize(init=nil)
|
35
52
|
super(init)
|
@@ -1,6 +1,4 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2009, 2010 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
|
@@ -38,6 +36,10 @@ class InstanceProfile < BaseModel
|
|
38
36
|
id
|
39
37
|
end
|
40
38
|
|
39
|
+
def to_s
|
40
|
+
name
|
41
|
+
end
|
42
|
+
|
41
43
|
def overrides
|
42
44
|
[:memory, :storage, :architecture, :cpu].inject({}) do |h, p|
|
43
45
|
if v = instance_variable_get("@#{p}")
|
@@ -1,6 +1,4 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2009, 2010 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
|
@@ -24,5 +22,9 @@ class StorageVolume < BaseModel
|
|
24
22
|
attr_accessor :capacity
|
25
23
|
attr_accessor :instance_id
|
26
24
|
attr_accessor :device
|
25
|
+
attr_accessor :realm_id
|
26
|
+
attr_accessor :actions
|
27
|
+
attr_accessor :name
|
28
|
+
attr_accessor :kind
|
27
29
|
|
28
30
|
end
|
@@ -0,0 +1,132 @@
|
|
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 'net/ssh'
|
17
|
+
require 'socket'
|
18
|
+
require 'tempfile'
|
19
|
+
|
20
|
+
module Deltacloud
|
21
|
+
|
22
|
+
module Runner
|
23
|
+
|
24
|
+
class RunnerError < StandardError
|
25
|
+
attr_reader :message
|
26
|
+
def initialize(message)
|
27
|
+
@message = message
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class InstanceSSHError < RunnerError; end
|
33
|
+
|
34
|
+
def self.execute(command, opts={})
|
35
|
+
|
36
|
+
if opts[:credentials] and (not opts[:credentials][:password] and not opts[:private_key])
|
37
|
+
raise RunnerError::new("Either password or key must be specified")
|
38
|
+
end
|
39
|
+
|
40
|
+
# First check networking and firewalling
|
41
|
+
network = Network::new(opts[:ip], opts[:port])
|
42
|
+
|
43
|
+
# Then check SSH availability
|
44
|
+
ssh = SSH::new(network, opts[:credentials], opts[:private_key])
|
45
|
+
|
46
|
+
# Finaly execute SSH command on instance
|
47
|
+
ssh.execute(command)
|
48
|
+
end
|
49
|
+
|
50
|
+
class Network
|
51
|
+
attr_accessor :ip, :port
|
52
|
+
|
53
|
+
def initialize(ip, port)
|
54
|
+
@ip, @port = ip, port
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class SSH
|
59
|
+
|
60
|
+
attr_reader :network
|
61
|
+
attr_accessor :credentials, :key
|
62
|
+
attr_reader :command
|
63
|
+
|
64
|
+
def initialize(network, credentials, key=nil)
|
65
|
+
@network, @credentials, @key = network, credentials, key
|
66
|
+
@result = ""
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute(command)
|
70
|
+
@command = command
|
71
|
+
config = ssh_config(@network, @credentials, @key)
|
72
|
+
begin
|
73
|
+
session = nil
|
74
|
+
# Default timeout for connecting to an instance.
|
75
|
+
# 20 seconds should be OK for most of connections, if you are
|
76
|
+
# experiencing some Exceptions with Timeouts increase this value.
|
77
|
+
# Please keep in mind that the HTTP request timeout is set to 60
|
78
|
+
# seconds, so you need to fit into this time
|
79
|
+
Timeout::timeout(20) do
|
80
|
+
session = Net::SSH.start(@network.ip, 'root', config)
|
81
|
+
end
|
82
|
+
session.open_channel do |channel|
|
83
|
+
channel.on_data do |ch, data|
|
84
|
+
@result += data
|
85
|
+
end
|
86
|
+
channel.exec(command)
|
87
|
+
session.loop
|
88
|
+
end
|
89
|
+
session.close
|
90
|
+
rescue Exception => e
|
91
|
+
raise InstanceSSHError.new("#{e.class.name}: #{e.message}")
|
92
|
+
ensure
|
93
|
+
# FileUtils.rm(config[:keys].first) rescue nil
|
94
|
+
end
|
95
|
+
Deltacloud::Runner::Response.new(self, @result)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def ssh_config(network, credentials, key)
|
101
|
+
config = { :port => network.port }
|
102
|
+
config.merge!({ :password => credentials[:password ]}) if credentials[:password]
|
103
|
+
config.merge!({ :keys => [ keyfile(key) ] }) unless key.nil?
|
104
|
+
config
|
105
|
+
end
|
106
|
+
|
107
|
+
# Right now there is no way howto pass private_key using String
|
108
|
+
# eg. without saving key to temporary file.
|
109
|
+
def keyfile(key)
|
110
|
+
keyfile = Tempfile.new("ec2_private.key")
|
111
|
+
key_material = ""
|
112
|
+
key.split("\n").each { |line| key_material+="#{line.strip}\n" if line.strip.size>0 }
|
113
|
+
keyfile.write(key_material) && keyfile.close
|
114
|
+
puts "[*] Using #{keyfile.path} as private key"
|
115
|
+
keyfile.path
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
class Response
|
121
|
+
|
122
|
+
attr_reader :body
|
123
|
+
attr_reader :ssh
|
124
|
+
|
125
|
+
def initialize(ssh, response_body)
|
126
|
+
@body, @ssh = response_body, ssh
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|