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:05 UTC
Initial Load Balancers support
Hi all,
I added initial support for managing load balancers in cloud providers.
As far I know, this collection is supported in EC2 and GoGrid.
My first shoot was on EC2, I created some basic concept, which could
be used for GoGrid as well.
So what basically are load balancers ? Long story short:
They are just simple routing services, which can be used for
routing traffic and balancing it for multiple instances.
Now, how can you test this patch with your EC2 account:
1.) Create new load balancer using /api/load_balancers/new
2.) Create new instance using /api/images -> Lauch instance
3.) Choose created load balancer from select box
4.) Hit create.
5.) Enter /api/load_balancers/<name>.xml and you should see your
instance here. You can create multiple instances using same
loadbalancer.
As I mention above, after reading GoGrid docs, they are using something
very similar, so this concept will for for them too.
-- Michal
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
--------------------------------------------------------
[PATCH core] Initial support for Load Balancers (rev 1)
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 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 »', "#{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