incubator-deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mohammed Morsi <mmo...@redhat.com>
Subject [PATCH] execute arbitrary remote shell command against running instance (rev 2)
Date Thu, 29 Jul 2010 23:23:39 GMT
---
 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
+        @output=@instance.run_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<
+    =@instance.id
+  %name<
+    =@instance.name
+  %status
+    =@failed ? "FAILED" : "SUCCESS"
+  %command<
+    =cdata do
+      =params[:cmd]
+  %output<
+    =cdata do
+      =@output
-- 
1.7.1.1


Mime
View raw message