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/05 16:17:06 UTC

[PATCH core] Initial support for Load Balancers (rev 1)

---
 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 6442371..46b0a1a 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


Re: [PATCH core] Initial support for Load Balancers (rev 1)

Posted by Michal Fojtik <mf...@redhat.com>.
On 05/08/10 16:17 +0200, mfojtik@redhat.com wrote:
>---
> 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

Sorry for this file ^^ . It was typo and it will be removed in final patch.

  -- Michal

--------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com, +420 532 294 4307
Ruby / Ruby On Rails Developer
Deltacloud API: http://deltacloud.org
--------------------------------------------------------