---
server/deltacloud-core.gemspec | 1 +
server/lib/deltacloud/base_driver/features.rb | 5 +++
server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 1 +
.../drivers/mock/data/instances/inst3.yml | 9 ++++++
server/lib/deltacloud/drivers/mock/mock_driver.rb | 1 +
.../lib/deltacloud/helpers/application_helper.rb | 23 +++++++++++++++
server/lib/deltacloud/models/instance.rb | 18 ++++++++++++
server/server.rb | 30 ++++++++++++++++++++
server/views/instances/run.html.haml | 17 +++++++++++
server/views/instances/run.xml.haml | 14 +++++++++
10 files changed, 119 insertions(+), 0 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/mock/data/instances/inst3.yml
create mode 100644 server/views/instances/run.html.haml
create mode 100644 server/views/instances/run.xml.haml
diff --git a/server/deltacloud-core.gemspec b/server/deltacloud-core.gemspec
index 6b5a966..53571e4 100644
--- a/server/deltacloud-core.gemspec
+++ b/server/deltacloud-core.gemspec
@@ -65,6 +65,7 @@ require 'rake'
s.add_dependency('thin', '>= 1.2.5')
s.add_dependency('rerun', '>= 0.5.2')
s.add_dependency('json', '>= 1.2.3')
+ s.add_dependency('net-ssh', '>= 2.0.23')
s.add_development_dependency('compass', '>= 0.8.17')
s.add_development_dependency('nokogiri', '>= 1.4.1')
s.add_development_dependency('rack-test', '>= 0.5.3')
diff --git a/server/lib/deltacloud/base_driver/features.rb
b/server/lib/deltacloud/base_driver/features.rb
index 8ad354a..2addbf5 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -155,5 +155,10 @@ module Deltacloud
description "Size instances according to changes to a hardware profile"
# The parameters are filled in from the hardware profiles
end
+
+ declare_feature :instances, :run_command do
+ description "Run an arbitrary command against an instance"
+ end
+
end
end
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index f59142c..5ad930b 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -37,6 +37,7 @@ class EC2Driver < Deltacloud::BaseDriver
feature :instances, :user_data
feature :instances, :authentication_key
+ feature :instances, :run_command
define_hardware_profile('m1.small') do
cpu 1
diff --git a/server/lib/deltacloud/drivers/mock/data/instances/inst3.yml
b/server/lib/deltacloud/drivers/mock/data/instances/inst3.yml
new file mode 100644
index 0000000..a84feca
--- /dev/null
+++ b/server/lib/deltacloud/drivers/mock/data/instances/inst3.yml
@@ -0,0 +1,9 @@
+:name: InstanceWithLocalhostAddress
+:state: RUNNING
+:image_id: img3
+:owner_id: mockuser
+:public_addresses: [ localhost ]
+:private_addresses: [ localhost ]
+:realm_id: us
+:instance_profile: !ruby/object:InstanceProfile
+ id: m1-small
diff --git a/server/lib/deltacloud/drivers/mock/mock_driver.rb
b/server/lib/deltacloud/drivers/mock/mock_driver.rb
index 94f3c75..b8302c7 100644
--- a/server/lib/deltacloud/drivers/mock/mock_driver.rb
+++ b/server/lib/deltacloud/drivers/mock/mock_driver.rb
@@ -77,6 +77,7 @@ class MockDriver < Deltacloud::BaseDriver
end
feature :instances, :user_name
+ feature :instances, :run_command
def initialize
if ENV["DELTACLOUD_MOCK_STORAGE"]
diff --git a/server/lib/deltacloud/helpers/application_helper.rb
b/server/lib/deltacloud/helpers/application_helper.rb
index 94396d2..9d92bc5 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -106,4 +106,27 @@ module ApplicationHelper
end
end
+ def store_private_key(id, key)
+ path = File.join('tmp', 'keys')
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
+ FileUtils.chmod 0600, path
+ end
+ filename = File.join(path, "instance_#{id}.key")
+ File.open(filename, 'w') do |f|
+ f.puts key
+ end
+ return filename
+ end
+
+ def remove_private_key(key)
+ File.delete(key) if File.exists?(key)
+ end
+
+ def cdata(&block)
+ text = capture_haml(&block)
+ text.gsub!("\n", "\n ")
+ "<![CDATA[\n #{text}\n]]>"
+ end
+
end
diff --git a/server/lib/deltacloud/models/instance.rb
b/server/lib/deltacloud/models/instance.rb
index 9cf69b8..c2c9ff4 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -16,6 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+require 'net/ssh'
class Instance < BaseModel
@@ -35,4 +36,21 @@ class Instance < BaseModel
self.public_addresses = [] if self.public_addresses.nil?
self.private_addresses = [] if self.private_addresses.nil?
end
+
+ def run_command(cmd, username='', opts={})
+ hostname = self.public_addresses.first
+ return "No hostname/IP address specified" unless hostname
+ output = ""
+ Net::SSH.start(hostname, username || 'root', opts) do |session|
+ session.open_channel do |channel|
+ channel.on_data do |ch, data|
+ output += data
+ end
+ channel.exec(cmd)
+ end
+ session.loop
+ end
+ return output
+ end
+
end
diff --git a/server/server.rb b/server/server.rb
index 2516d3e..7f9f603 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -161,6 +161,12 @@ get "/api/instances/new" do
end
end
+get "/api/instances/:id/run" do
+ respond_to do |format|
+ format.html { haml :"instances/run"}
+ end
+end
+
collection :instances do
description <<END
An instance is a concrete machine realized from an image.
@@ -226,6 +232,30 @@ END
param :id, :string, :required
control { instance_action(:destroy) }
end
+
+ operation :run, :method => :post, :member => true do
+ description "Run command on instance"
+ param :id, :string, :required
+ param :cmd, :string, :required
+ param :private_key, :string
+ param :username, :string
+ control do
+ @instance = driver.instance(credentials, { :id => params[:id] })
+ private_key = store_private_key(params[:id], params[:private_key])
+ begin
+ @[email protected]_command(params[:cmd], params[:username], { :keys
=> private_key })
+ rescue Exception => e
+ @failed = true
+ @output = "#{e.message}\n#{e.backtrace.join("\n")}"
+ ensure
+ remove_private_key(private_key)
+ end
+ respond_to do |format|
+ format.xml { haml :"instances/run" }
+ end
+ end
+ end
+
end
collection :hardware_profiles do
diff --git a/server/views/instances/run.html.haml
b/server/views/instances/run.html.haml
new file mode 100644
index 0000000..488322c
--- /dev/null
+++ b/server/views/instances/run.html.haml
@@ -0,0 +1,17 @@
+%h1 Run command
+
+%form{ :action => "/api/instances/#{params[:id]}/run", :method => :post}
+ %label
+ Command:
+ %input{ :name => 'cmd', :size => 30}/
+ %h3 Private key
+ %label
+ Paste private key here:
+ %p
+ %textarea{ :name => 'private_key', :cols => 30}
+ %h3 Authentication options
+ %label
+ Username:
+ %input{ :name => 'username', :size => 30}
+ %br/
+ %input{ :type => 'submit', :value => "Run"}
diff --git a/server/views/instances/run.xml.haml
b/server/views/instances/run.xml.haml
new file mode 100644
index 0000000..b91dbd7
--- /dev/null
+++ b/server/views/instances/run.xml.haml
@@ -0,0 +1,14 @@
+!!! XML
+%instance{:href => instance_url(@instance.id)}
+ %id<
+ [email protected]
+ %name<
+ [email protected]
+ %status
+ =...@failed ? "FAILED" : "SUCCESS"
+ %command<
+ =cdata do
+ =params[:cmd]
+ %output<
+ =cdata do
+ =...@output
--
1.7.1.1