You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by lu...@apache.org on 2011/02/08 01:45:58 UTC

svn commit: r1068240 - in /incubator/deltacloud/trunk/server/lib: deltacloud/drivers/sbc/ deltacloud/drivers/sbc/sbc_client.rb deltacloud/drivers/sbc/sbc_driver.rb drivers.rb

Author: lutter
Date: Tue Feb  8 00:45:57 2011
New Revision: 1068240

URL: http://svn.apache.org/viewvc?rev=1068240&view=rev
Log:
SBC driver: new driver for the IBM SBC cloud

Driver contributed by Eric Woods

Fixes https://issues.apache.org/jira/browse/DTACLOUD-15

Added:
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_client.rb
    incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_driver.rb
Modified:
    incubator/deltacloud/trunk/server/lib/drivers.rb

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_client.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_client.rb?rev=1068240&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_client.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_client.rb Tue Feb  8 00:45:57 2011
@@ -0,0 +1,180 @@
+#
+# Copyright (C) 2011 IBM Corporation
+#
+# 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 'base64'
+require 'net/https'
+require 'json'
+
+module Deltacloud
+  module Drivers
+    module SBC
+#
+# Client for the IBM Smart Business Cloud (SBC).
+#
+# 31 January 2011
+#
+class SBCClient
+  API_URL = URI.parse('https://www-147.ibm.com/computecloud/enterprise/api/rest/20100331')
+
+  #
+  # Initialize the client
+  #
+  def initialize(username, password)
+    @username, @password = username, password
+    @rest_base = '/computecloud/enterprise/api/rest/20100331'
+    @service = Net::HTTP.new(API_URL.host, API_URL.port)
+    @service.use_ssl = true
+  end
+
+  #
+  # Retrieve instances
+  #
+  def list_instances(instance_id=nil)
+    if instance_id.nil?
+      JSON.parse(get('/instances', default_headers))['instances']
+    else
+      [ JSON.parse(get('/instances/' + instance_id, default_headers)) ]
+    end
+  end
+
+  #
+  # Reboot an instance
+  #
+  def reboot_instance(instance_id)
+    headers = default_headers
+    headers['Content-Type'] = 'application/x-www-form-urlencoded'
+    put('/instances/' + instance_id, 'state=restart', headers)
+  end
+
+  #
+  # Delete an instance
+  #
+  def delete_instance(instance_id)
+    delete('/instances/' + instance_id, default_headers)
+  end
+
+  #
+  # Retrieve images
+  #
+  def list_images(image_id=nil)
+    if image_id.nil?
+      JSON.parse(get('/offerings/image', default_headers))['images']
+    else
+      [ JSON.parse(get('/offerings/image/' + image_id, default_headers)) ]
+    end
+  end
+
+  #
+  # Retrieve locations; returns an XML document.
+  #
+  def list_locations
+    headers = default_headers
+    headers['Accept'] = 'text/xml'	# JSON locations not supported
+    Nokogiri.XML(get('/locations', headers))
+  end
+
+  #
+  # Creates an instance
+  #
+  # body is a name/value hash to configure the instance
+  #
+  def create_instance(body)
+    headers = default_headers
+    headers['Content-Type'] = 'application/x-www-form-urlencoded'
+    JSON.parse(post('/instances', urlencode(body), headers))['instances']
+  end
+
+  #
+  # -------------------- Private Helpers -----------------
+  #
+  private
+
+  #
+  # HTTP GET
+  #
+  def get(path, headers)
+    resp = @service.get(@rest_base + path, headers)
+    unless resp.is_a?(Net::HTTPSuccess)
+      backend_error!(resp)
+    end
+    resp.body
+  end
+
+  #
+  # HTTP PUT
+  #
+  def put(path, body, headers)
+    resp = @service.put(@rest_base + path, body, headers)
+    unless resp.is_a?(Net::HTTPSuccess)
+      backend_error!(resp)
+    end
+    resp.body
+  end
+
+  #
+  # HTTP POST
+  #
+  def post(path, body, headers)
+    resp = @service.post(@rest_base + path, body, headers)
+    unless resp.is_a?(Net::HTTPSuccess)
+      backend_error!(resp)
+    end
+    resp.body
+  end
+
+  #
+  # HTTP DELETE
+  #
+  def delete(path, headers)
+    resp = @service.delete(@rest_base + path, headers)
+    unless resp.is_a?(Net::HTTPSuccess)
+      backend_error!(resp)
+    end
+    resp.body
+  end
+
+  #
+  # Default request headers.
+  #
+  def default_headers
+    {"Accept" => "application/json",
+      "User-Agent" => "deltacloud",
+      "Authorization" => "Basic " + Base64.encode64("#{@username}:#{@password}")}
+  end
+
+  #
+  # Handle request error
+  #
+  def backend_error!(resp)
+    if resp.is_a?(Net::HTTPUnauthorized)
+      raise Deltacloud::AuthException, resp.message
+    else
+      raise Deltacloud::BackendError.new(resp.code, resp.body, resp.message, '')
+    end
+  end
+
+  #
+  # Utility to URL encode a hash.
+  #
+  def urlencode(hash)
+    hash.keys.map { |k| "#{URI.encode(k)}=#{URI.encode(hash[k])}" }.join("&")
+  end
+end
+    end
+  end
+end

Added: incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_driver.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_driver.rb?rev=1068240&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_driver.rb (added)
+++ incubator/deltacloud/trunk/server/lib/deltacloud/drivers/sbc/sbc_driver.rb Tue Feb  8 00:45:57 2011
@@ -0,0 +1,299 @@
+#
+# Copyright (C) 2011 IBM Corporation
+#
+# 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 'nokogiri'
+require 'deltacloud/drivers/sbc/sbc_client'
+
+module Deltacloud
+  module Drivers
+    module SBC
+#
+# Driver for the IBM Smart Business Cloud (SBC).
+#
+# 31 January 2011
+#
+class SBCDriver < Deltacloud::BaseDriver
+  #
+  # Retrieves images
+  #
+  def images(credentials, opts={})
+    sbc_client = new_client(credentials)
+    opts ||= {}
+    images = []
+    images = sbc_client.list_images(opts[:id]).map do |image|
+      # Cache image for create_instance; hwp is image-specific. In the
+      # current flow of the server, images is always called before a
+      # create_instance, making this caching profitable
+      @last_image = image
+      convert_image(image)
+    end
+    images = filter_on(images, :architecture, opts)
+    images = filter_on(images, :owner_id, opts)
+    images
+  end
+
+  #
+  # Retrieves realms
+  #
+  def realms(credentials, opts={})
+    sbc_client = new_client(credentials)
+    doc = sbc_client.list_locations
+    doc.xpath('ns2:DescribeLocationsResponse/Location').map { |loc| convert_location(loc) }
+  end
+
+  #
+  # Retrieves instances
+  #
+  def instances(credentials, opts={})
+    sbc_client = new_client(credentials)
+    opts ||= {}
+    instances = []
+    instances = sbc_client.list_instances(opts[:id]).map do |instance|
+      convert_instance(instance)
+    end
+  end
+
+  #
+  # Creates an instance
+  #
+  def create_instance(credentials, image_id, opts={})
+    sbc_client = new_client(credentials)
+
+    # Copy opts to body; keywords are mapped later
+    body = opts.dup
+    body.delete('image_id')
+    body.delete('hwp_id')
+    body.delete('realm_id')
+
+    # Lookup image if nil; avoids extra lookup
+    if @last_image.nil? || @last_image['id'] != opts[:image_id]
+      @last_image = sbc_client.list_images(image_id).map[0]
+    end
+
+    # Map DeltaCloud keywords to SBC
+    body['imageID'] = opts[:image_id]
+    body['location'] = opts[:realm_id] || @last_image['location']
+    body['instanceType'] = opts[:hwp_id] || @last_image['supportedInstanceTypes'][0]['id']
+
+    # Submit instance, parse response
+    convert_instance(sbc_client.create_instance(body).map[0])
+  end
+
+  #
+  # Reboots an instance
+  #
+  def reboot_instance(credentials, instance_id)
+    sbc_client = new_client(credentials)
+    sbc_client.reboot_instance(instance_id)
+    instance(credentials, instance_id)
+  end
+
+  #
+  # Stops an instance
+  #
+  def stop_instance(credentials, instance_id)
+    # Stop not supported; rebooting
+    reboot_instance(credentials, instance_id)
+  end
+
+  #
+  # Destroys an instance
+  #
+  def destroy_instance(credentials, instance_id)
+    sbc_client = new_client(credentials)
+    sbc_client.delete_instance(instance_id)
+    instance(credentials, instance_id)
+  end
+
+  #
+  # --------------------- Private helpers ---------------------
+  #
+  private
+
+  # SBC instance states mapped to DeltaCloud
+  @@INSTANCE_STATE_MAP  = {
+    0 => "PENDING",			# New
+    1 => "PENDING",			# Provisioning
+    2 => "STOPPED",			# Failed
+    3 => "STOPPED",			# Removed
+    4 => "STOPPED",			# Rejected
+    5 => "RUNNING",			# Active
+    6 => "STOPPED",			# Unknown
+    7 => "PENDING",			# Deprovisioning
+    8 => "PENDING",			# Restarting
+    9 => "PENDING",			# Starting
+    10 => "SHUTTING_DOWN",	# Stopping
+    11 => "STOPPED",		# Stopped
+    12 => "PENDING",		# Deprovision pending
+    13 => "PENDING",		# Restart pending
+    14 => "PENDING",		# Attaching
+    15 => "PENDING"			# Detaching
+  }
+
+  # SBC image states mapped to DeltaCloud
+  @@IMAGE_STATE_MAP = {
+    0 => "UNAVAILABLE",		# New
+    1 => "AVAILABLE",		# Available
+    2 => "UNAVAILABLE",		# Unavailable
+    3 => "UNAVAILABLE",		# Deleted
+    4 => "UNAVAILABLE"		# Capturing
+  }
+
+  # SBC location states mapped to DeltaCloud
+  @@LOCATION_STATE_MAP = {
+    0 => "UNAVAILABLE",		# Unavailable
+    1 => "AVAILABLE"		# Available
+  }
+
+  #
+  # Define state machine for instances
+  #
+  define_instance_states do
+    start.to( :pending )		.automatically
+    pending.to( :running )		.automatically
+    running.to( :running )		.on( :reboot )
+    running.to( :finish )		.on( :destroy )
+  end
+
+  #
+  # Creates an IBM SBC client
+  #
+  def new_client(credentials)
+    safely do
+      return SBCClient.new(credentials.user, credentials.password)
+    end
+  end
+
+  #
+  # Converts JSON to an instance
+  #
+  def convert_instance(instance)
+    state = @@INSTANCE_STATE_MAP[instance["status"]]
+    Instance.new(
+      :id => instance["id"],
+      :owner_id => instance["owner"],
+      :image_id => instance["imageId"],
+      :name => instance["name"],
+      :realm_id => instance["location"],
+      :state => state,
+      :actions => instance_actions_for(state),
+      :public_addresses => [instance["primaryIP"]["ip"]],
+      :private_addresses => [],
+      :instance_profile => InstanceProfile.new(instance["instanceType"]), # TODO: InstanceProfile isn't looking up HW profiles?
+      :launch_time => instance["launchTime"],
+      :keyname => instance["keyName"]
+    )
+  end
+
+  #
+  # Converts JSON to an image
+  #
+  def convert_image(image)
+    Image.new(
+      :id => image["id"],
+      :name => image["name"],
+      :owner_id => image["owner"],
+      :description => image["description"],
+      :architecture => "i386",	# TODO: parse this from supportedInstanceType IDs w/ HW profile lookup
+      :state => @@IMAGE_STATE_MAP[image["state"]]
+    )
+  end
+
+  #
+  # Converts XML to a location
+  #
+  def convert_location(location)
+    Realm.new(
+      :id => location.xpath('ID').text,
+      :name => location.xpath('Name').text,
+      :limit => :unlimited,
+      :state => @@LOCATION_STATE_MAP[location.xpath('State').text.to_i]
+    )
+  end
+
+  #
+  # -------------------- Hardware Profiles -----------------
+  #
+  # TODO: HWP IDs contain '/'; results in invalid URL
+  #
+  define_hardware_profile('COP32.1/2048/60') do
+    cpu				1
+    memory			2 * 1024
+    storage			60
+    architecture	'i386'
+  end
+
+  define_hardware_profile('COP64.2/4096/60') do
+    cpu				2
+    memory			4 * 1024
+    storage			60
+    architecture	'i386_x64'
+  end
+
+  define_hardware_profile('BRZ32.1/2048/60*175') do
+    cpu				1
+    memory			2 * 1024
+    storage			175
+    architecture	'i386'
+  end
+
+  define_hardware_profile('BRZ64.2/4096/60*500*350') do
+    cpu				2
+    memory			4 * 1024
+    storage			850
+    architecture	'i386_x64'
+  end
+
+  define_hardware_profile('SLV32.2/4096/60*350') do
+    cpu				3
+    memory			5 * 1024
+    storage			350
+    architecture	'i386'
+  end
+
+  define_hardware_profile('SLV64.4/8192/60*500*500') do
+    cpu				4
+    memory			8 * 1024
+    storage			1024
+    architecture	'i386_x64'
+  end
+
+  define_hardware_profile('GLD32.4/4096/60*350') do
+    cpu				4
+    memory			4 * 1024
+    storage			350
+    architecture	'i386'
+  end
+
+  define_hardware_profile('GLD64.8/16384/60*500*500') do
+    cpu				8
+    memory			16 * 1024
+    storage			1024
+    architecture	'i386_x64'
+  end
+
+  define_hardware_profile('PLT64.16/16384/60*500*500*500*500') do
+    cpu				16
+    memory			16 * 1024
+    storage			2048
+    architecture	'i386_x64'
+  end
+end
+    end
+  end
+end

Modified: incubator/deltacloud/trunk/server/lib/drivers.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/lib/drivers.rb?rev=1068240&r1=1068239&r2=1068240&view=diff
==============================================================================
--- incubator/deltacloud/trunk/server/lib/drivers.rb (original)
+++ incubator/deltacloud/trunk/server/lib/drivers.rb Tue Feb  8 00:45:57 2011
@@ -20,6 +20,7 @@
 module Deltacloud
   DRIVERS = {
     :ec2 => { :name => "EC2" },
+	:sbc => { :name => "SBC" },
     :rackspace => { :name => "Rackspace" },
     :gogrid => { :name => "Gogrid" },
     :rhevm => { :name => "RHEVM" },