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 13:48:50 UTC

[PATCH core] 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