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 2010/08/10 17:26:15 UTC

Load balancers (rev2)

Hi,

This patch add Load Balancing support for GoGrid and also
fixes some minor issues with load balancing under EC2.

There are still some issues with GoGrid driver. I get almost all
functionality to be same in both drivers.

For GoGrid there is some difficulties with additing Instances to LB.
In GoGrid  you don't assign an Instance to Load Balancer but instead
you use IP address of Instance you want to assign.
So to match functionality with EC2 I need to lookup for instances
which have IP address I want to assign.

At this point everything is working. Trouble starts when I want
to assign more instances to one load balancer.
When you use their 'realips.N.ip' variable in 'grid/loadbalancer/edit',
you need to increate number (N) in that list to assign more instances.
Unfortunatelly when you do this, you need to wait at least 2-3 minutes
for next API request. So I asked GoGrid support and response was:

"For each request through API needs at least 2-3 minute intervals"

So long story short: Load balancing for GoGrid is working, but it's limited
by this 'sad' thing.

 -- Michal

PS: In both GoGrid and Amazon, Load balancers are free to use, so anyone ca try
this patch, without starting 'real' instances. (except you want to test instance assign :)


[PATCH core 2/2] Load Balancers for GoGrid (rev 1)

Posted by mf...@redhat.com.
---
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb    |   22 ++++-
 .../lib/deltacloud/drivers/gogrid/gogrid_client.rb |    9 +-
 .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb |  116 +++++++++++++++++++-
 server/lib/deltacloud/drivers/gogrid/test.rb       |   13 --
 .../lib/deltacloud/helpers/application_helper.rb   |    4 +-
 server/lib/deltacloud/helpers/conversion_helper.rb |    6 +-
 server/lib/deltacloud/models/base_model.rb         |    2 +-
 server/public/javascripts/application.js           |   12 ++
 server/server.rb                                   |   21 ++++
 server/views/load_balancers/new.html.haml          |   16 ++-
 server/views/load_balancers/show.html.haml         |   23 +++-
 server/views/load_balancers/show.xml.haml          |    1 +
 12 files changed, 207 insertions(+), 38 deletions(-)
 delete mode 100644 server/lib/deltacloud/drivers/gogrid/test.rb

diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index d15a198..aba42b1 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -190,7 +190,7 @@ class EC2Driver < Deltacloud::BaseDriver
         :instance_initiated_shutdown_behavior => 'terminate'
       )
       instance = convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
-      if opts[:load_balancer_id]
+      if opts[:load_balancer_id] and opts[:load_balancer_id]!=""
         elb = new_client(credentials, :elb)
         elb.register_instances_with_load_balancer({
           :instances => [instance.id],
@@ -365,6 +365,24 @@ class EC2Driver < Deltacloud::BaseDriver
     end
   end
 
+  def lb_register_instance(credentials, opts={})
+    ec2 = new_client( credentials, :elb)
+    safely do
+      ec2.register_instances_with_load_balancer(:instances => [opts[:instance_id]],
+        :load_balancer_name => opts[:id])
+      load_balancer(credentials, :id => opts[:id])
+    end
+  end
+
+  def lb_unregister_instance(credentials, opts={})
+    ec2 = new_client( credentials, :elb)
+    safely do
+      ec2.deregister_instances_from_load_balancer(:instances => [opts[:instance_id]],
+        :load_balancer_name => opts[:id])
+      load_balancer(credentials, :id => opts[:id])
+    end
+  end
+
   private
 
   def new_client(credentials, type = :ec2)
@@ -404,7 +422,7 @@ class EC2Driver < Deltacloud::BaseDriver
     end
     loadbalancer.Instances.member.each do |instance|
       balancer.instances << instances(credentials, :id => instance['InstanceId']).first
-    end
+    end if loadbalancer.Instances
     balancer
   end
 
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
index c37f061..5492170 100644
--- a/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
+++ b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
@@ -9,7 +9,7 @@ class GoGridClient
                  apikey='YOUR API KEY',
                  secret='YOUR SHARED SECRET', 
                  format='json',
-                 version='1.5')
+                 version='1.6')
     @server = server
     @secret = secret
     @default_params = {'format'=>format, 'v'=>version,'api_key' => apikey}
@@ -36,11 +36,8 @@ class GoGridClient
     else
       @default_params['v'] = '1.5'
     end
-    begin
-      JSON::parse(sendAPIRequest(method, params))
-    rescue Exception => e
-      STDERR.puts("ERROR: #{e.message}")
-    end
+    request = sendAPIRequest(method, params)
+    JSON::parse(request)
   end
   
   def encode_params(params)
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
index e6c99c5..343102f 100644
--- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
+++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
@@ -36,6 +36,7 @@ module Deltacloud
 class GogridDriver < Deltacloud::BaseDriver
 
   feature :instances, :authentication_password
+  feature :instances, :register_to_load_balancer
 
   define_hardware_profile 'server' do
     cpu            2
@@ -45,7 +46,7 @@ class GogridDriver < Deltacloud::BaseDriver
 
   def supported_collections
     DEFAULT_COLLECTIONS.reject! { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
-    DEFAULT_COLLECTIONS + [ :keys ]
+    DEFAULT_COLLECTIONS + [ :keys, :load_balancers ]
   end
 
   def images(credentials, opts=nil)
@@ -107,6 +108,24 @@ class GogridDriver < Deltacloud::BaseDriver
     end
   end
 
+  def lb_register_instance(credentials, opts={})
+    client = new_client(credentials)
+    instance = instance(credentials, :id => opts[:instance_id])
+    balancer = client.request('grid/loadbalancer/get', { 'name' => opts[:id]})['list'].first
+    safely do
+      convert_load_balancer(credentials, client.request('grid/loadbalancer/edit', {
+        "id" => balancer['id'],
+        "realiplist.#{balancer['realiplist'].size}.ip" => instance.public_addresses.first,
+        "realiplist.#{balancer['realiplist'].size}.port" => balancer['virtualip']['port']
+      }))
+    end
+  end
+
+  def lb_unregister_instance(credentials, opts={})
+      raise Deltacloud::BackendFeatureUnsupported.new('501',
+    'Unregistering instances from load balancer is not supported in GoGrid')
+  end
+
   def list_instances(credentials, id)
     instances = []
     safely do
@@ -192,6 +211,67 @@ class GogridDriver < Deltacloud::BaseDriver
     return creds
   end
 
+
+  def create_load_balancer(credentials, opts={})
+    gogrid = new_client(credentials)
+    balancer, l_instance = nil, nil
+    safely do
+      virtip = get_free_ip_from_realm(credentials, opts['realm_id'])
+      if opts['instance_id']
+        l_instance = instance(credentials, :id => opts['instance_id'])
+        real_ip = {
+          'realiplist.0.port' => opts['listener_inst_port'],
+          'realiplist.0.ip' => l_instance ? l_instance.public_addresses.first : ""
+        }
+      else
+        real_ip = false
+      end
+      request = {
+        'name' => opts['name'],
+        'virtualip.ip' => virtip,
+        'virtualip.port' => opts['listener_lbr_port'],
+      }
+      request.merge!(real_ip) if real_ip
+      balancer = gogrid.request('grid/loadbalancer/add', request)['list'].first
+    end
+    balancer = convert_load_balancer(credentials, balancer)
+    balancer.instances = [l_instance] if l_instance
+    balancer
+  end
+
+  def destroy_load_balancer(credentials, id)
+    gogrid = new_client(credentials)
+    balancer = nil
+    safely do
+      balancer = gogrid.request('grid/loadbalancer/delete', { 'name' => id })
+      balancer = load_balancer(credentials, :id => id) unless balancer
+    end
+    convert_load_balancer(credentials, balancer)
+  end
+
+  def load_balancers(credentials, opts={})
+    gogrid = new_client(credentials)
+    balancers = []
+    safely do
+      balancer = gogrid.request('grid/loadbalancer/list', opts || {})['list'].each do |balancer|
+        balancers << balancer
+      end
+    end
+    balancers.collect { |b| convert_load_balancer(credentials, b) }
+  end
+
+  def load_balancer(credentials, opts={})
+    gogrid = new_client(credentials)
+    balancer = nil
+    begin
+      balancer = gogrid.request('grid/loadbalancer/get', { 'name' => opts[:id] })['list'].first
+      balancer['instances'] = instances(credentials)
+      return convert_load_balancer(credentials, balancer)
+    rescue OpenURI::HTTPError
+      balancer = load_balancers(credentials, :id => opts[:id]).first
+    end
+  end
+
   define_instance_states do
     start.to( :pending )         .automatically
     pending.to( :running )       .automatically
@@ -205,7 +285,37 @@ class GogridDriver < Deltacloud::BaseDriver
 
   def new_client(credentials)
     GoGridClient.new('https://api.gogrid.com/api', credentials.user, credentials.password)
+  end
+
+  def convert_load_balancer(credentials, loadbalancer)
+    if loadbalancer['datacenter']
+      b_realm = realm(credentials, :id => loadbalancer['datacenter']['id'])
+    else
+      # Report first Realm until loadbalancer become ready
+      b_realm = realm(credentials, :id => 1)
+    end
+    balancer = LoadBalancer.new({
+      :id => loadbalancer['name'],
+      :realms => [b_realm]
+    })
+    balancer.public_addresses = [loadbalancer['virtualip']['ip']['ip']] if loadbalancer['virtualip'] and loadbalancer['virtualip']['ip']
+    balancer.listeners = []
+    balancer.instances = []
+    instance_ips = []
+    loadbalancer['realiplist'].each do |instance_ip|
+      balancer.add_listener({
+        :protocol => 'TCP',
+        :load_balancer_port => loadbalancer['virtualip']['port'],
+        :instance_port => instance_ip['port']
+      })
+      instance_ips << instance_ip['ip']['ip']
+    end if loadbalancer['realiplist']
+    balancer.instances = get_load_balancer_instances(instance_ips, loadbalancer['instances'])
+    return balancer
+  end
 
+  def get_load_balancer_instances(instance_ips, instances)
+    instances.select { |i| instance_ips.include?(i.public_addresses.first) } if instances
   end
 
   def get_login_data(client, instance_id)
@@ -305,11 +415,11 @@ class GogridDriver < Deltacloud::BaseDriver
     state.eql?('Off') ? 'STOPPED' : 'RUNNING'
   end
 
-  def get_free_ip_from_realm(credentials, realm_id)
+  def get_free_ip_from_realm(credentials, realm_id, ip_type=1)
     ip = ""
     safely do
       ip = new_client(credentials).request('grid/ip/list', {
-        'ip.type' => '1',
+        'ip.type' => ip_type,
         'ip.state' => '1',
         'datacenter' => realm_id
       })['list'].first['ip']
diff --git a/server/lib/deltacloud/drivers/gogrid/test.rb b/server/lib/deltacloud/drivers/gogrid/test.rb
deleted file mode 100644
index 809081d..0000000
--- a/server/lib/deltacloud/drivers/gogrid/test.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'gogrid_client'
-require 'ap'
-
-user='fbb1de3897597ccf'
-password='ngieth10'
-
-client=GoGridClient.new('https://api.gogrid.com/api', user, password)
-
-ap client.request('grid/ip/list', {
-  'ip.type' => '1',
-  'ip.state' => '1',
-  'datacenter' => '1'
-})
diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 9a9dfdc..ab96f0c 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -41,8 +41,8 @@ module ApplicationHelper
     collections[:instances].operations[action.to_sym].method
   end
 
-  def driver_has_feature?(feature_name)
-    not driver.features(:instances).select{ |f| f.name.eql?(feature_name) }.empty?
+  def driver_has_feature?(feature_name, collection=:instances)
+    not driver.features(collection).select{ |f| f.name.eql?(feature_name) }.empty?
   end
 
   def driver_has_auth_features?
diff --git a/server/lib/deltacloud/helpers/conversion_helper.rb b/server/lib/deltacloud/helpers/conversion_helper.rb
index e96f9b7..7b0c57e 100644
--- a/server/lib/deltacloud/helpers/conversion_helper.rb
+++ b/server/lib/deltacloud/helpers/conversion_helper.rb
@@ -22,17 +22,17 @@ require 'deltacloud/base_driver'
 module ConversionHelper
 
   def convert_to_json(type, obj)
-    if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile ].include?( type ) )
+    if ( [ :account, :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile ].include?( type ) )
       if Array.eql?(obj.class)
         data = obj.collect do |o|
-          o.to_hash.merge({ :href => self.send(:"#{type}_url", type.eql?(:hardware_profile) ? o.name : o.id ) })
+          o.to_hash.merge({ :href => self.send(:"#{type}_url", type.eql?(:instance_profile) ? o.name : o.id ) })
         end
         type = type.to_s.pluralize
       else
         data = obj.to_hash
         data.merge!({ :href => self.send(:"#{type}_url", data[:id]) })
       end
-      return { :"#{type}" => data }.to_json
+      return JSON.generate(Hash[{ :"#{type}" => data }], :allow_nan => true )
     end
   end
 
diff --git a/server/lib/deltacloud/models/base_model.rb b/server/lib/deltacloud/models/base_model.rb
index aa997a5..13c65d6 100644
--- a/server/lib/deltacloud/models/base_model.rb
+++ b/server/lib/deltacloud/models/base_model.rb
@@ -48,7 +48,7 @@ class BaseModel
 
   def to_hash
     out = {}
-    self.attributes.each { |attribute| out.merge!({ attribute => self.send(:"#{attribute}") } ) }
+    self.attributes.each { |attribute| out.merge!({ attribute => self.send(:"#{attribute}").to_s } ) }
     out
   end
 
diff --git a/server/public/javascripts/application.js b/server/public/javascripts/application.js
index 80e1d1c..e2a2751 100644
--- a/server/public/javascripts/application.js
+++ b/server/public/javascripts/application.js
@@ -29,4 +29,16 @@ $(document).ready(function() {
     return false;
   })
 
+  if ($('select#list_instances').length) {
+    $('select#list_instances').html("<option>Loading instances...</option>");
+    $.getJSON("/api/instances.json?state=RUNNING",
+      function(data){
+        $('select#list_instances').empty();
+        $.each(data.instances, function(i,item){
+          $('select#list_instances').append('<option value="'+item.id+'">'+item.id+'</option>');
+        });
+      }
+    );
+  }
+
 })
diff --git a/server/server.rb b/server/server.rb
index e0e8155..55791a9 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -358,6 +358,7 @@ end
 
 get '/api/load_balancers/new' do
   @realms = driver.realms(credentials)
+  @instances = driver.instances(credentials) if driver_has_feature?(:register_instance, :load_balancers)
   respond_to do |format|
     format.html { haml :"load_balancers/new" }
   end
@@ -404,4 +405,24 @@ collection :load_balancers do
     end
   end
 
+  operation :register, :method => :post, :member => true do
+    description "Add instance to loadbalancer"
+    param :id,  :string,  :required
+    param :instance_id, :string,  :required
+    control do
+      driver.lb_register_instance(credentials, params)
+      redirect(load_balancer_url(params[:id]))
+    end
+  end
+
+  operation :unregister, :method => :post, :member => true do
+    description "Remove instance from loadbalancer"
+    param :id,  :string,  :required
+    param :instance_id, :string,  :required
+    control do
+      driver.lb_unregister_instance(credentials, params)
+      redirect(load_balancer_url(params[:id]))
+    end
+  end
+
 end
diff --git a/server/views/load_balancers/new.html.haml b/server/views/load_balancers/new.html.haml
index 734dc44..3d734b1 100644
--- a/server/views/load_balancers/new.html.haml
+++ b/server/views/load_balancers/new.html.haml
@@ -5,25 +5,33 @@
     %label
       Name:
     %input{ :name => 'name', :size => 30 }/
+  -if @instances
+    %p
+      %label
+        Running instance:
+      %select{ :name => 'instance_id'}
+        - @instances.select{|i| i.state=="RUNNING"}.each do |instance|
+          %option{ :value => instance.id } #{instance.id}
   %p
     %label
       Realm:
     %select{ :name => 'realm_id'}
       - @realms.each do |realm|
-        %option{ :value => realm.id } #{realm.id}
+        %option{ :value => realm.id } #{realm.id} - #{realm.name}
+  %hr
   %p
     %label
-      Listener protocol:
+      Protocol:
     %select{ :name => 'listener_protocol'}
       %option{ :value => 'HTTP'}  HTTP
       %option{ :value => 'TCP'} TCP
   %p
     %label
-      Listener load balancer port:
+      Load balancer port:
     %input{ :name => "listener_lbr_port", :size => 30}
   %p
     %label
-      Listener instance port:
+      Instances port:
     %input{ :name => "listener_inst_port", :size => 30}
   %p
     %input{ :type => :submit, :name => "commit", :value => "create" }/
diff --git a/server/views/load_balancers/show.html.haml b/server/views/load_balancers/show.html.haml
index a8ceb87..615bd78 100644
--- a/server/views/load_balancers/show.html.haml
+++ b/server/views/load_balancers/show.html.haml
@@ -6,12 +6,13 @@
     %dt Public addresses
     %dd
       = @load_balancer.public_addresses.join(',')
-    %dt Created at
-    %dd
-      = @load_balancer.created_at
+    - if @load_balancer.created_at
+      %dt Created at
+      %dd
+        = @load_balancer.created_at
     %dt Realms
     %dd
-      = @load_balancer.realms.collect { |r| r.id }.join(',')
+      = @load_balancer.realms.collect { |r| "#{r.id} - #{r.name}" }.join(',')
     %dt Listeners
     %dd
       - @load_balancer.listeners.each do |listener|
@@ -19,3 +20,17 @@
         %br
         ="Instance port: #{listener.instance_port}"
         %br
+    - if @load_balancer.instances.class.eql?(Array)
+      %dt Instances
+      - @load_balancer.instances.each do |inst|
+        %dd
+          =inst.id
+          %a{:class => :post, :href => unregister_load_balancer_url(@load_balancer.id, :instance_id => inst.id)} Delete
+
+%form{:action => url_for("/api/load_balancers/#{@load_balancer.id}/register"), :method => :post}
+  %p
+    %strong Assign instance to this load balancer
+  %p
+    %label Instance
+    %select{:name => :instance_id, :id => "list_instances"}
+    %input{:type => :submit, :value => "Assign"}
diff --git a/server/views/load_balancers/show.xml.haml b/server/views/load_balancers/show.xml.haml
index c012699..1e50ff9 100644
--- a/server/views/load_balancers/show.xml.haml
+++ b/server/views/load_balancers/show.xml.haml
@@ -17,3 +17,4 @@
   %instances
     - @load_balancer.instances.each do |instance|
       %instance{:href => instance_url(instance.id), :id => instance.id}
+        %link{:rel => "unregister", :href => unregister_load_balancer_url(@load_balancer.id, { :instance_id => instance.id})}
-- 
1.7.2


[PATCH core 1/2] Load Balancers for EC2 (rev 2)

Posted by mf...@redhat.com.
---
 server/deltacloud.rb                            |    1 +
 server/lib/deltacloud/base_driver/features.rb   |    7 ++
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb |   95 +++++++++++++++++++++-
 server/lib/deltacloud/models/load_balancer.rb   |   38 +++++++++
 server/server.rb                                |   53 +++++++++++++
 server/views/instances/new.html.haml            |    8 ++
 server/views/load_balancers/index.html.haml     |   32 ++++++++
 server/views/load_balancers/index.xml.haml      |    5 +
 server/views/load_balancers/new.html.haml       |   29 +++++++
 server/views/load_balancers/show.html.haml      |   21 +++++
 server/views/load_balancers/show.xml.haml       |   19 +++++
 11 files changed, 303 insertions(+), 5 deletions(-)
 create mode 100644 server/lib/deltacloud/models/load_balancer.rb
 create mode 100644 server/views/load_balancers/index.html.haml
 create mode 100644 server/views/load_balancers/index.xml.haml
 create mode 100644 server/views/load_balancers/new.html.haml
 create mode 100644 server/views/load_balancers/show.haml.haml
 create mode 100644 server/views/load_balancers/show.html.haml
 create mode 100644 server/views/load_balancers/show.xml.haml

diff --git a/server/deltacloud.rb b/server/deltacloud.rb
index 6799b2f..f6e9ada 100644
--- a/server/deltacloud.rb
+++ b/server/deltacloud.rb
@@ -10,6 +10,7 @@ require 'deltacloud/models/realm'
 require 'deltacloud/models/image'
 require 'deltacloud/models/instance'
 require 'deltacloud/models/key'
+require 'deltacloud/models/load_balancer'
 require 'deltacloud/models/instance_profile'
 require 'deltacloud/models/storage_snapshot'
 require 'deltacloud/models/storage_volume'
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
index 3ed4085..c3fc3ba 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -162,5 +162,12 @@ 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, :register_to_load_balancer do
+      description "Register instance to load balancer"
+      operation :create do
+        param :load_balancer_id, :string, :optional
+      end
+    end
   end
 end
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 909eca3..d15a198 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -36,12 +36,13 @@ module Deltacloud
 class EC2Driver < Deltacloud::BaseDriver
 
   def supported_collections
-    DEFAULT_COLLECTIONS + [ :keys ]
+    DEFAULT_COLLECTIONS + [ :keys, :load_balancers ]
   end
 
   feature :instances, :user_data
   feature :instances, :authentication_key
   feature :images, :owner_id
+  feature :instances, :register_to_load_balancer
 
   define_hardware_profile('m1.small') do
     cpu                1
@@ -188,7 +189,15 @@ class EC2Driver < Deltacloud::BaseDriver
         :disable_api_termination => false,
         :instance_initiated_shutdown_behavior => 'terminate'
       )
-      return convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
+      instance = convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
+      if opts[:load_balancer_id]
+        elb = new_client(credentials, :elb)
+        elb.register_instances_with_load_balancer({
+          :instances => [instance.id],
+          :load_balancer_name => opts[:load_balancer_id]
+        })
+      end
+      return instance
     end
   end
 
@@ -284,9 +293,9 @@ class EC2Driver < Deltacloud::BaseDriver
   def keys(credentials, opts=nil)
     ec2 = new_client( credentials )
     opts[:key_name] = opts[:id] if opts and opts[:id]
-    keypairs = ec2.describe_keypairs(opts || {})
     result = []
     safely do
+      keypairs = ec2.describe_keypairs(opts || {})
       keypairs.keySet.item.each do |keypair|
         result << convert_key(keypair)
       end
@@ -310,17 +319,93 @@ class EC2Driver < Deltacloud::BaseDriver
     end
   end
 
+  def load_balancer(credentials, opts={})
+    load_balancers(credentials, {
+      :load_balancer_names => [opts[:id]]
+    }).first
+  end
+
+  def load_balancers(credentials, opts=nil)
+    ec2 = new_client( credentials, :elb )
+    result = []
+    safely do
+      loadbalancers = ec2.describe_load_balancers(opts || {})
+      return [] unless loadbalancers.DescribeLoadBalancersResult.LoadBalancerDescriptions
+      loadbalancers.DescribeLoadBalancersResult.LoadBalancerDescriptions.member.each do |loadbalancer|
+        result << convert_load_balancer(credentials, loadbalancer)
+      end
+    end
+    return result
+  end
+
+  def create_load_balancer(credentials, opts={})
+    ec2 = new_client( credentials, :elb )
+    safely do
+      ec2.create_load_balancer({
+        :load_balancer_name => opts['name'],
+        # TODO: Add possibility to push more listeners/realms in one request
+        # Something like 'Hash' in 'Array' parameter
+        :availability_zones => [opts['realm_id']],
+        :listeners => [{
+          :protocol => opts['listener_protocol'],
+          :load_balancer_port => opts['listener_lbr_port'],
+          :instance_port => opts['listener_inst_port']
+         }]
+      })
+      return load_balancer(credentials, opts['name'])
+    end
+  end
+
+  def destroy_load_balancer(credentials, id)
+    ec2 = new_client( credentials, :elb )
+    safely do
+      ec2.delete_load_balancer({
+        :load_balancer_name => id
+      })
+    end
+  end
+
   private
 
-  def new_client(credentials)
+  def new_client(credentials, type = :ec2)
     opts = {
       :access_key_id => credentials.user,
       :secret_access_key => credentials.password
     }
     opts[:server] = ENV['DCLOUD_EC2_URL'] if ENV['DCLOUD_EC2_URL']
     safely do
-      AWS::EC2::Base.new(opts)
+      case type
+        when :ec2
+          AWS::EC2::Base.new(opts)
+        when :elb
+          AWS::ELB::Base.new(opts)
+      end
+    end
+  end
+
+  def convert_load_balancer(credentials, loadbalancer)
+    balancer_realms = loadbalancer.AvailabilityZones.member.collect do |m|
+      realm(credentials, m)
+    end
+    balancer = LoadBalancer.new({
+      :id => loadbalancer['LoadBalancerName'],
+      :created_at => loadbalancer['CreatedTime'],
+      :public_addresses => [loadbalancer['DNSName']],
+      :realms =>  balancer_realms
+    })
+    balancer.listeners = []
+    balancer.instances = []
+    loadbalancer.Listeners.member.each do |listener|
+      balancer.add_listener({
+        :protocol => listener['Protocol'],
+        :load_balancer_port => listener['LoadBalancerPort'],
+        :instance_port => listener['InstancePort']
+      })
+    end
+    loadbalancer.Instances.member.each do |instance|
+      balancer.instances << instances(credentials, :id => instance['InstanceId']).first
     end
+    balancer
   end
 
   def convert_key(key)
diff --git a/server/lib/deltacloud/models/load_balancer.rb b/server/lib/deltacloud/models/load_balancer.rb
new file mode 100644
index 0000000..2900004
--- /dev/null
+++ b/server/lib/deltacloud/models/load_balancer.rb
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2009  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.
+
+
+class LoadBalancer < BaseModel
+
+  attr_accessor :realms
+  attr_accessor :listeners
+  attr_accessor :instances
+  attr_accessor :public_addresses
+  attr_accessor :created_at
+
+  def add_listener(opts)
+    @listeners << Listener.new(opts)
+  end
+
+  class Listener < BaseModel
+    attr_accessor :protocol
+    attr_accessor :load_balancer_port
+    attr_accessor :instance_port
+  end
+
+end
diff --git a/server/server.rb b/server/server.rb
index 1373fa3..e0e8155 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -155,6 +155,9 @@ get "/api/instances/new" do
   @image   = driver.image( credentials, :id => params[:image_id] )
   @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
   @realms = driver.realms(credentials)
+  if driver_has_feature?(:register_to_load_balancer)
+    @load_balancers = driver.load_balancers(credentials)
+  end
   respond_to do |format|
     format.html { haml :"instances/new" }
   end
@@ -352,3 +355,53 @@ collection :keys do
   end
 
 end
+
+get '/api/load_balancers/new' do
+  @realms = driver.realms(credentials)
+  respond_to do |format|
+    format.html { haml :"load_balancers/new" }
+  end
+end
+
+collection :load_balancers do
+  description "Load balancers"
+
+  operation :index do
+    description "List of all active load balancers"
+    control do
+      filter_all :load_balancers
+    end
+  end
+
+  operation :show do
+    description "Show details about given load balancer"
+    param :id,  :string,  :required
+    control { show :load_balancer }
+  end
+
+  operation :create do
+    description "Create a new load balancer"
+    param :name,  :string,  :required
+    param :realm_id,  :string,  :required
+    param :listener_protocol,  :string,  :required, ['HTTP', 'TCP']
+    param :listener_lbr_port,  :string,  :required
+    param :listener_inst_port,  :string,  :required
+    control do
+      @load_balancer = driver.create_load_balancer(credentials, params)
+      respond_to do |format|
+        format.xml { haml :"load_balancers/show" }
+        format.html { haml :"load_balancers/show" }
+      end
+    end
+  end
+
+  operation :destroy do
+    description "Destroy given load balancer"
+    param :id,  :string,  :required
+    control do
+      driver.destroy_load_balancer(credentials, params[:id])
+      redirect(load_balancers_url)
+    end
+  end
+
+end
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index 6d67c2d..3547707 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -14,6 +14,14 @@
       %label
         Instance Keyname:
         %input{:name => 'keyname', :size => 30 }
+  -if driver_has_feature?(:register_to_load_balancer)
+    %p
+      %label
+        Load Balancer:
+        %select{:name => 'load_balancer_id'}
+          %option{:value => ""}
+          - @load_balancers.each do |load_balancer|
+            %option{:value => load_balancer.id} #{load_balancer.id}
   - if !@hardware_profiles.empty?
     %h3 What size machine?
     - for hwp in @hardware_profiles
diff --git a/server/views/load_balancers/index.html.haml b/server/views/load_balancers/index.html.haml
new file mode 100644
index 0000000..877ca07
--- /dev/null
+++ b/server/views/load_balancers/index.html.haml
@@ -0,0 +1,32 @@
+%h1 Load Balancers
+
+%table.display
+  %thead
+    %tr
+      %th ID
+      %th Hostname
+      %th Realm
+      %th Balancer port
+      %th Instances port
+      %th Actions
+  %tbody
+    - @elements.each do |balancer|
+      %tr
+        %td
+          = link_to balancer.id, load_balancer_url( balancer.id )
+        %td
+          = balancer.public_addresses.first
+        %td
+          = link_to balancer.realms.first.id, realm_url( balancer.realms.first.id )
+        %td
+          - balancer.listeners.each do |listener|
+            ="#{listener.protocol}[#{listener.load_balancer_port}]<br/>"
+        %td
+          - balancer.listeners.each do |listener|
+            ="#{listener.protocol}[#{listener.load_balancer_port}]<br/>"
+        %td
+          =link_to 'Destroy', destroy_load_balancer_url(balancer.id), :class => 'delete'
+  %tfoot
+    %tr
+      %td{:colspan => 6, :style => "text-align:right;"}
+        =link_to 'Create &raquo;', "#{url_for('/api/load_balancers/new')}", :class => 'button'
diff --git a/server/views/load_balancers/index.xml.haml b/server/views/load_balancers/index.xml.haml
new file mode 100644
index 0000000..22c6911
--- /dev/null
+++ b/server/views/load_balancers/index.xml.haml
@@ -0,0 +1,5 @@
+
+!!!XML
+%load_balancers
+  - @elements.each do |c|
+    = haml :'load_balancers/show', :locals => { :@load_balancer => c, :partial => true }
diff --git a/server/views/load_balancers/new.html.haml b/server/views/load_balancers/new.html.haml
new file mode 100644
index 0000000..734dc44
--- /dev/null
+++ b/server/views/load_balancers/new.html.haml
@@ -0,0 +1,29 @@
+%h1 New Load Balancer
+
+%form{ :action => '/api/load_balancers', :method => :post }
+  %p
+    %label
+      Name:
+    %input{ :name => 'name', :size => 30 }/
+  %p
+    %label
+      Realm:
+    %select{ :name => 'realm_id'}
+      - @realms.each do |realm|
+        %option{ :value => realm.id } #{realm.id}
+  %p
+    %label
+      Listener protocol:
+    %select{ :name => 'listener_protocol'}
+      %option{ :value => 'HTTP'}  HTTP
+      %option{ :value => 'TCP'} TCP
+  %p
+    %label
+      Listener load balancer port:
+    %input{ :name => "listener_lbr_port", :size => 30}
+  %p
+    %label
+      Listener instance port:
+    %input{ :name => "listener_inst_port", :size => 30}
+  %p
+    %input{ :type => :submit, :name => "commit", :value => "create" }/
diff --git a/server/views/load_balancers/show.haml.haml b/server/views/load_balancers/show.haml.haml
new file mode 100644
index 0000000..e69de29
diff --git a/server/views/load_balancers/show.html.haml b/server/views/load_balancers/show.html.haml
new file mode 100644
index 0000000..a8ceb87
--- /dev/null
+++ b/server/views/load_balancers/show.html.haml
@@ -0,0 +1,21 @@
+%h1
+  = @load_balancer.id
+
+%dl
+  %di
+    %dt Public addresses
+    %dd
+      = @load_balancer.public_addresses.join(',')
+    %dt Created at
+    %dd
+      = @load_balancer.created_at
+    %dt Realms
+    %dd
+      = @load_balancer.realms.collect { |r| r.id }.join(',')
+    %dt Listeners
+    %dd
+      - @load_balancer.listeners.each do |listener|
+        ="Load balancer port: #{listener.load_balancer_port}"
+        %br
+        ="Instance port: #{listener.instance_port}"
+        %br
diff --git a/server/views/load_balancers/show.xml.haml b/server/views/load_balancers/show.xml.haml
new file mode 100644
index 0000000..c012699
--- /dev/null
+++ b/server/views/load_balancers/show.xml.haml
@@ -0,0 +1,19 @@
+- unless defined?(partial)
+  !!! XML
+%load_balancer{ :href => key_url(@load_balancer.id), :id => @load_balancer.id}
+  %actions
+    %link{ :rel => "destroy", :method => "delete", :href => destroy_load_balancer_url(@load_balancer.id)}
+  %public_addresses
+    - @load_balancer.public_addresses.each do |address|
+      %address #{address}
+  %created_at<
+    = @load_balancer.created_at
+  %realm{ :href => realm_url(@load_balancer.realms.first.id), :id => @load_balancer.realms.first.id}
+  %listeners
+    - @load_balancer.listeners.each do |listener|
+      %listener{ :protocol => listener.protocol}
+        %load_balancer_port #{listener.load_balancer_port}
+        %instance_port #{listener.instance_port}
+  %instances
+    - @load_balancer.instances.each do |instance|
+      %instance{:href => instance_url(instance.id), :id => instance.id}
-- 
1.7.2