You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2011/02/14 16:19:35 UTC

[PATCH core 1/2] Revamped using 'cloudservers' gem (thanks Rackspace!)

From: Michal Fojtik <mf...@redhat.com>

---
 .../drivers/rackspace/rackspace_client.rb          |  130 --------------
 .../drivers/rackspace/rackspace_driver.rb          |  180 +++++++++++---------
 server/lib/deltacloud/models/instance.rb           |    2 +
 server/views/instances/show.html.haml              |    9 +
 4 files changed, 109 insertions(+), 212 deletions(-)
 delete mode 100644 server/lib/deltacloud/drivers/rackspace/rackspace_client.rb

diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_client.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_client.rb
deleted file mode 100644
index d803302..0000000
--- a/server/lib/deltacloud/drivers/rackspace/rackspace_client.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-#
-# Copyright (C) 2009, 2010  Red Hat, Inc.
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-require "net/http"
-require "net/https"
-require 'rubygems'
-require 'json'
-
-#
-# author: Michael Neale
-# TODO: catch generic errors in JSON response and throw (probably)
-#
-module Deltacloud
-  module Drivers
-    module Rackspace
-
-class RackspaceClient
-
-  @@AUTH_API = URI.parse('https://auth.api.rackspacecloud.com/v1.0')
-
-  def initialize(username, auth_key)
-    http = Net::HTTP.new(@@AUTH_API.host,@@AUTH_API.port)
-    http.use_ssl = true
-    authed = http.get(@@AUTH_API.path, {'X-Auth-User' => username, 'X-Auth-Key' => auth_key})
-    if authed.is_a?(Net::HTTPUnauthorized)
-      raise Deltacloud::AuthException, "Failed to authenticate to Rackspace"
-    elsif !authed.is_a?(Net::HTTPSuccess)
-      backend_error!(resp)
-    end
-    @auth_token  = authed.header['X-Auth-Token']
-    @service_uri = URI.parse(authed.header['X-Server-Management-Url'])
-    @service = Net::HTTP.new(@service_uri.host, @service_uri.port)
-    @service.use_ssl = true
-  end
-
-  def list_flavors
-    JSON.parse(get('/flavors/detail'))['flavors']
-  end
-
-  def list_images
-    JSON.parse(get('/images/detail'))['images']
-  end
-
-  def list_servers
-      JSON.parse(get('/servers/detail'))['servers']
-  end
-
-
-  def load_server_details( server_id )
-    JSON.parse(get("/servers/#{server_id}"))['server']
-  end
-
-
-  def start_server(image_id, flavor_id, name)
-    json = { :server => { :name => name,
-                          :imageId => image_id.to_i,
-                          :flavorId => flavor_id.to_i }}.to_json
-    # FIXME: The response has the root password in 'adminPass'; we somehow
-    # need to communicate this back since it's the only place where we can
-    # get it from
-    JSON.parse(post("/servers", json, headers).body)["server"]
-  end
-
-  def delete_server(server_id)
-    delete("/servers/#{server_id}", headers)
-  end
-
-  def reboot_server(server_id)
-    json = { :reboot => { :type => :SOFT }}.to_json
-    post("/servers/#{server_id}/action", json, headers)
-  end
-
-
-  def headers
-    {"Accept" => "application/json", "X-Auth-Token" => @auth_token, "Content-Type" => "application/json"}
-  end
-
-  private
-  def get(path)
-    resp = @service.get(@service_uri.path + path, {"Accept" => "application/json", "X-Auth-Token" => @auth_token})
-    unless resp.is_a?(Net::HTTPSuccess)
-      backend_error!(resp)
-    end
-    resp.body
-  end
-
-  def post(path, json, headers)
-    resp = @service.post(@service_uri.path + path, json, headers)
-    unless resp.is_a?(Net::HTTPSuccess)
-      backend_error!(resp)
-    end
-    resp
-  end
-
-  def delete(path, headers)
-    resp = @service.delete(@service_uri.path + path, headers)
-    unless resp.is_a?(Net::HTTPSuccess)
-      backend_error!(resp)
-    end
-    resp
-  end
-
-  def backend_error!(resp)
-    json = JSON.parse(resp.body)
-    cause = json.keys[0]
-    code = json[cause]["code"]
-    message = json[cause]["message"]
-    details = json[cause]["details"]
-    raise Deltacloud::BackendError.new(code, cause, message, details)
-  end
-
-end
-    end
-  end
-end
diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
index 8d743aa..028b63a 100644
--- a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
+++ b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
@@ -17,8 +17,8 @@
 # under the License.
 
 require 'deltacloud/base_driver'
-require 'deltacloud/drivers/rackspace/rackspace_client'
 require 'cloudfiles'
+require 'cloudservers'
 
 module Deltacloud
   module Drivers
@@ -27,20 +27,21 @@ module Deltacloud
 class RackspaceDriver < Deltacloud::BaseDriver
 
   feature :instances, :user_name
+  feature :instances, :authentication_password
 
   def supported_collections
     DEFAULT_COLLECTIONS + [ :buckets ]
   end
 
-  def hardware_profiles(credentials, opts = nil)
-    racks = new_client( credentials )
-    results=""
+  def hardware_profiles(credentials, opts = {})
+    rs = new_client( credentials )
+    results = []
     safely do
-      results = racks.list_flavors.map do |flav|
-        HardwareProfile.new(flav["id"].to_s) do
+      results = rs.list_flavors.collect do |f|
+        HardwareProfile.new(f[:id].to_s) do
           architecture 'x86_64'
-          memory flav["ram"].to_i
-          storage flav["disk"].to_i
+          memory f[:ram].to_i
+          storage f[:disk].to_i
         end
       end
     end
@@ -48,22 +49,21 @@ class RackspaceDriver < Deltacloud::BaseDriver
   end
 
   def images(credentials, opts=nil)
-    racks = new_client( credentials )
-    results=""
+    rs = new_client(credentials)
+    results = []
     safely do
-      results = racks.list_images.map do |img|
-        Image.new( {
-                     :id=>img["id"].to_s,
-                     :name=>img["name"],
-                     :description => img["name"] + " " + img["status"] + "",
-                     :owner_id=>"root",
-                     :architecture=>'x86_64'
-                   } )
+      results = rs.list_images.collect do |img|
+        Image.new(
+          :id => img[:id].to_s,
+          :name => img[:name],
+          :description => img[:name],
+          :owner_id => credentials.user,
+          :state => img[:status],
+          :architecture => 'x86_64'
+        )
       end
     end
-    results.sort_by{|e| [e.description]}
-    results = filter_on( results, :id, opts )
-    results
+    filter_on( results, :id, opts )
   end
 
   #rackspace does not at this stage have realms... its all US/TX, all the time (at least at time of writing)
@@ -75,67 +75,102 @@ class RackspaceDriver < Deltacloud::BaseDriver
     } )]
   end
 
-  def reboot_instance(credentials, id)
-    racks = new_client(credentials)
+  #
+  # create instance. Default to flavor 1 - really need a name though...
+  # In rackspace, all flavors work with all images.
+  #
+  def create_instance(credentials, image_id, opts)
+    rs = new_client( credentials )
+    result = nil
     safely do
-      racks.reboot_server(id)
+      server = rs.create_server(:name => opts[:name] || Time.now.to_s, 
+                       :imageId => image_id.to_i, 
+                       :flavorId => opts[:hwp_id].to_i || hardware_profiles(credentials).first.id.to_i)
+      result = convert_instance_after_create(server, credentials.user, server.adminPass)
     end
-    Instance.new( {
-      :id => id,
-      :state => "RUNNING",
-      :actions => instance_actions_for( "RUNNING" ),
-    } )
+    result
   end
 
-  def stop_instance(credentials, id)
-    destroy_instance(credentials, id)
+  def reboot_instance(credentials, instance_id)
+    rs = new_client(credentials)
+    safely do
+      server = rs.get_server(instance_id.to_i)
+      server.reboot!
+      convert_instance_after_create(server, credentials.user)
+    end
   end
 
-  def destroy_instance(credentials, id)
-    racks = new_client(credentials)
+  def destroy_instance(credentials, instance_id)
+    rs = new_client(credentials)
     safely do
-      racks.delete_server(id)
+      server = rs.get_server(instance_id.to_i)
+      server.delete!
+      convert_instance_after_create(server, credentials.user)
     end
-    Instance.new( {
-      :id => id,
-      :state => "STOPPED",
-      :actions => instance_actions_for( "STOPPED" ),
-    } )
   end
 
+  alias_method :stop_instance, :destroy_instance
+
+  def convert_instance_after_create(server, user_name, password='')
+    inst = Instance.new(
+      :id => server.id.to_s,
+      :realm_id => 'us',
+      :owner_id => user_name,
+      :description => server.name,
+      :name => server.name,
+      :state => (server.status == 'ACTIVE') ? 'RUNNING' : 'PENDING',
+      :architecture => 'x86_64',
+      :image_id => server.imageId.to_s,
+      :instance_profile => InstanceProfile::new(server.flavorId.to_s),
+      :public_addresses => server.addresses[:public],
+      :private_addresses => server.addresses[:private],
+      :username => 'root',
+      :password => password ? password : nil
+    )
+    inst.actions = instance_actions_for(inst.state)
+    inst
+  end
 
-  #
-  # create instance. Default to flavor 1 - really need a name though...
-  # In rackspace, all flavors work with all images.
-  #
-  def create_instance(credentials, image_id, opts)
-    racks = new_client( credentials )
-    hwp_id = opts[:hwp_id] || 1
-    name = Time.now.to_s
-    if (opts[:name]) then name = opts[:name] end
-    safely do
-      return convert_srv_to_instance(racks.start_server(image_id, hwp_id, name))
-    end
+  def convert_instance(server, user_name = '')
+    inst = Instance.new(
+      :id => server[:id].to_s,
+      :realm_id => 'us',
+      :owner_id => user_name,
+      :description => server[:name],
+      :name => server[:name],
+      :state => (server[:status] == 'ACTIVE') ? 'RUNNING' : 'PENDING',
+      :architecture => 'x86_64',
+      :image_id => server[:imageId].to_s,
+      :instance_profile => InstanceProfile::new(server[:flavorId].to_s),
+      :public_addresses => server[:addresses][:public],
+      :private_addresses => server[:addresses][:private]
+    )
+    inst.actions = instance_actions_for(inst.state)
+    inst
   end
 
   #
   # Instances
   #
-  def instances(credentials, opts=nil)
-    racks = new_client(credentials)
-    instances = []
+  def instances(credentials, opts={})
+
+    rs = new_client(credentials)
+    insts = []
+
     safely do
-      if (opts.nil?)
-        instances = racks.list_servers.map do |srv|
-          convert_srv_to_instance(srv)
-        end
+      if opts[:id]
+        server = rs.get_server(opts[:id].to_i)
+        insts << convert_instance_after_create(server, credentials.user)
       else
-        instances << convert_srv_to_instance(racks.load_server_details(opts[:id]))
+        insts = rs.list_servers_detail.collect do |server|
+          convert_instance(server, credentials.user)
+        end
       end
     end
-    instances = filter_on( instances, :id, opts )
-    instances = filter_on( instances, :state, opts )
-    instances
+
+    insts = filter_on( insts, :id, opts )
+    insts = filter_on( insts, :state, opts )
+    insts
   end
 
   def valid_credentials?(credentials)
@@ -149,14 +184,10 @@ class RackspaceDriver < Deltacloud::BaseDriver
 
   define_instance_states do
     start.to( :pending )          .on( :create )
-
     pending.to( :running )        .automatically
-
     running.to( :running )        .on( :reboot )
     running.to( :shutting_down )  .on( :stop )
-
     shutting_down.to( :stopped )  .automatically
-
     stopped.to( :finish )         .automatically
   end
 
@@ -290,25 +321,10 @@ class RackspaceDriver < Deltacloud::BaseDriver
 
 private
 
-  def convert_srv_to_instance(srv)
-    inst = Instance.new(:id => srv["id"].to_s,
-                        :owner_id => "root",
-                        :realm_id => "us")
-    inst.name = srv["name"]
-    inst.state = srv["status"] == "ACTIVE" ? "RUNNING" : "PENDING"
-    inst.actions = instance_actions_for(inst.state)
-    inst.image_id = srv["imageId"].to_s
-    inst.instance_profile = InstanceProfile.new(srv["flavorId"].to_s)
-    if srv["addresses"]
-      inst.public_addresses  = srv["addresses"]["public"]
-      inst.private_addresses = srv["addresses"]["private"]
-    end
-    inst
-  end
 
   def new_client(credentials)
     safely do
-      return RackspaceClient.new(credentials.user, credentials.password)
+      CloudServers::Connection.new(:username => credentials.user, :api_key => credentials.password)
     end
   end
 
diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb
index 467d93f..6345590 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -30,6 +30,8 @@ class Instance < BaseModel
   attr_accessor :launch_time
   attr_accessor :keyname
   attr_accessor :authn_error
+  attr_accessor :username
+  attr_accessor :password
 
   def to_s
     name
diff --git a/server/views/instances/show.html.haml b/server/views/instances/show.html.haml
index 0a6e115..76d77ba 100644
--- a/server/views/instances/show.html.haml
+++ b/server/views/instances/show.html.haml
@@ -36,6 +36,15 @@
     %dt Private Addresses
     %dd
       = @instance.private_addresses.collect { |address| "<div>#{address}</div>" }.join
+  - if @instance.password
+    %di
+      %dt Username
+      %dd
+        = @instance.username
+    %di
+      %dt Password
+      %dd
+        = @instance.password
   %di
     %dt
     %dd
-- 
1.7.4


Re: [PATCH core 1/2] Revamped using 'cloudservers' gem (thanks Rackspace!)

Posted by David Lutterkort <lu...@redhat.com>.
On Tue, 2011-02-15 at 11:15 +0100, Michal Fojtik wrote:
> On 14/02/11 17:20 -0800, David Lutterkort wrote:
> >Make sure you also update the gemspec/Gemfile to reflect the new
> >dependencies.
> 
> Sure, thanks for noticing this.
> Btw. David we should create some wrapper for our drivers, which will 
> check if all needed gems are present and if they are not, they should
> be installed using Bundler or just throw some notification to user to
> indicate that they are needed.

We should just produce a correct Gemfile/gemspec ;) Of course, that
could be greatly aided by a doodad that intercepts require and records
what libraries/gems are needed by what driver.

David



Re: [PATCH core 1/2] Revamped using 'cloudservers' gem (thanks Rackspace!)

Posted by Michal Fojtik <mf...@redhat.com>.
On 14/02/11 17:20 -0800, David Lutterkort wrote:
>On Mon, 2011-02-14 at 16:19 +0100, mfojtik@redhat.com wrote:
>> From: Michal Fojtik <mf...@redhat.com>
>>
>> ---
>>  .../drivers/rackspace/rackspace_client.rb          |  130 --------------
>>  .../drivers/rackspace/rackspace_driver.rb          |  180 +++++++++++---------
>>  server/lib/deltacloud/models/instance.rb           |    2 +
>>  server/views/instances/show.html.haml              |    9 +
>>  4 files changed, 109 insertions(+), 212 deletions(-)
>>  delete mode 100644 server/lib/deltacloud/drivers/rackspace/rackspace_client.rb
>
>Make sure you also update the gemspec/Gemfile to reflect the new
>dependencies.

Sure, thanks for noticing this.
Btw. David we should create some wrapper for our drivers, which will 
check if all needed gems are present and if they are not, they should
be installed using Bundler or just throw some notification to user to
indicate that they are needed.

   -- Michal

>
>David
>
>

-- 
--------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
--------------------------------------------------------

Re: [PATCH core 1/2] Revamped using 'cloudservers' gem (thanks Rackspace!)

Posted by David Lutterkort <lu...@redhat.com>.
On Mon, 2011-02-14 at 16:19 +0100, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
> 
> ---
>  .../drivers/rackspace/rackspace_client.rb          |  130 --------------
>  .../drivers/rackspace/rackspace_driver.rb          |  180 +++++++++++---------
>  server/lib/deltacloud/models/instance.rb           |    2 +
>  server/views/instances/show.html.haml              |    9 +
>  4 files changed, 109 insertions(+), 212 deletions(-)
>  delete mode 100644 server/lib/deltacloud/drivers/rackspace/rackspace_client.rb

Make sure you also update the gemspec/Gemfile to reflect the new
dependencies.

David