ApacheCon Europe 2012

Rhein-Neckar-Arena, Sinsheim, Germany

5–8 November 2012

Deepdive into deltacloud

Marios Andreou

Audience level:
Advanced
Track:
Cloud

Description

Apache Deltacloud is a cross-cloud abstraction server written in ruby and providing a RESTful API. We look at a particular aspect of Deltacloud in detail, showing how a Deltacloud cloud 'driver' works and how to extend the driver to implement a new cloud 'collection'. The idea is to provide lower level detail for developers that are interested in participating in the Deltacloud community.

Abstract

Deepdive into deltacloud:

Apache Deltacloud is a cross-cloud abstraction server written in ruby and providing a RESTful API. This talk looks at a particular aspect of Deltacloud in detail. We will show how a Deltacloud cloud 'driver' works and how one would go about extending the driver to implement a new cloud 'collection'. The idea is to provide lower level detail for developers that are interested in participating in the Deltacloud community.


I. Introduction to Deltacloud drivers:

A Deltacloud driver requires 3 key ingredients:

1) the HTTP 'route' handlers (e.g. GET /foo, POST /derp/herp etc) for the driver collections you are implementing.

2) some way of "speaking" to the given cloud provider (this happens in the driver file itself),

3) the driver file implementing the Deltacloud API (or a subset, depending on what your driver supports),

The first you get 'for free' - as the HTTP routes are already defined by the Deltacloud code and are common across all cloud providers (that's the point - a common API). The second you also get 'for free' - in most cases - you will find ruby gems for talking to various cloud providers; the 'problem' is usually choosing the 'best' one to use. So all that is left is working on the cloud provider driver itself.


Example - launch a new "Instance" (virtual machine) POST /api/instances:

The Route handler - server/lib/deltacloud/collections/instances.rb:

    46       operation :create, :with_capability => :create_instance do
    47         param :image_id,     :string, :required
    48         param :realm_id,     :string, :optional
    49         param :hwp_id,       :string, :optional
    50         param :keyname,      :string, :optional
    51         control do
    52           @instance = driver.create_instance(credentials, params[:image_id], params)

^^^^^invoke the driver to create the instance using provided params

    60           status 201  # Created
    61           respond_to do |format|
    62             format.xml  { haml :"instances/#{action_handler}" }
    63             format.json { xml_to_json("instances/#{action_handler}") }
    64             format.html { haml :"instances/show" }

^^^^^then return the instance in an appropriate format in the response


The Driver - server/lib/deltacloud/drivers/openstack/openstack_driver.rb:

    134         def create_instance(credentials, image_id, opts)
    135           os = new_client( credentials )

^^^^^need a handle - some way of 'speaking' to the cloud provider

    138           params = {}
    139           params[:personality] = extract_personality(opts)
    140           params[:name] = (opts[:name] && opts[:name].length>0)? opts[:name] : Time.now.to_s
    141           params[:imageRef] = image_id
    142           params[:flavorRef] =  (opts[:hwp_id] && opts[:hwp_id].length>0) ?
    143                           opts[:hwp_id] : hardware_profiles(credentials).first.name

^^^^^gather parameters as provided by the caller

    150           safely do
    151             server = os.create_server(params)
    152             result = convert_from_server(server, os.connection.authuser)

^^^^^launch the machine and format the response as a Deltacloud Instance

    153           end
    154           result
    155         end

II. Implementing the 'Keys' collection for the Openstack driver:

The Deltacloud API defines four 'routes' of keys:

  • GET /api/keys #index - get a list of all keys
  • GET /api/keys/:id #show - get details of a particular key
  • POST /api/keys #create - create a new key
  • DELETE /api/keys/:id #delete - destroy a key

We focus on the first - the 'index' operation GET /api/keys. The flow is always the same for all collections:

1) get a handle for talking to the back-end cloud

2) make the request and get a response from the back-end cloud

3) format the response into a Deltacloud object


1. Get a handle for talking to the back-end cloud

Every driver has this "new_client" method:

    17 require 'openstack'   #the rubygem we are using to talk to openstack
    (...)
    320         def new_client(credentials, type = :compute)
    (...)
    330           OpenStack::Connection.create(:username => user_name, :api_key => credentials.password, :authtenant => tenant_name,     :auth_url => api_provider)
    338         end

2. Make the request and get a response from the back-end cloud:

    293         def keys(credentials, opts={})  
    294           os = new_client(credentials)
^^^^^ get a handle to the Openstack service:
    295           keys = []        
    296           safely do
    297             os.keypairs.values.each{|key| keys << convert_key(key)}
^^^^^ make the request and format response into Deltacloud Key objects:
    298           end
    299           filter_on(keys, :id, opts)      
    300         end

3. Format the response into a Deltacloud object - the convert_key method:

    444         def convert_key(key)
    445           Key.new(
    446             :id => key[:name],
    447             :fingerprint => key[:fingerprint],
    448             :credential_type => :key,
    449             :pem_rsa_key => key[:private_key], # only available once, on create_key
    450             :state => "AVAILABLE"
    451           )
    452         end

The 'Key' object model is defined in server/lib/deltacloud/models/key.rb:

    17 class Key < BaseModel
    18 
    19   attr_accessor :credential_type  
    20   attr_accessor :fingerprint
    21   attr_accessor :username  
    22   attr_accessor :password
    23   attr_accessor :pem_rsa_key
    24   attr_accessor :state