1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge pull request #1627 from jyotty/bluebox-lb

add Bluebox Blocks Load Balancer API
This commit is contained in:
Wesley Beary 2013-03-05 08:12:39 -08:00
commit 2f30de2ab3
23 changed files with 800 additions and 0 deletions

View file

@ -7,6 +7,8 @@ class Bluebox < Fog::Bin
Fog::Compute::Bluebox
when :dns
Fog::DNS::Bluebox
when :blb
Fog::Bluebox::BLB
else
raise ArgumentError, "Unsupported #{self} service: #{key}"
end

View file

@ -5,6 +5,7 @@ module Fog
extend Fog::Provider
service(:blb, 'bluebox/blb', 'BLB')
service(:compute, 'bluebox/compute', 'Compute')
service(:dns, 'bluebox/dns', 'DNS')

85
lib/fog/bluebox/blb.rb Normal file
View file

@ -0,0 +1,85 @@
require 'fog/bluebox'
module Fog
module Bluebox
class BLB < Fog::Service
requires :bluebox_api_key, :bluebox_customer_id
recognizes :bluebox_host, :bluebox_port, :bluebox_scheme, :persistent
model_path 'fog/bluebox/models/blb'
model :lb_application
collection :lb_applications
model :lb_service
collection :lb_services
model :lb_backend
collection :lb_backends
request_path 'fog/bluebox/requests/blb'
request :get_lb_application
request :get_lb_applications
request :get_lb_service
request :get_lb_services
request :get_lb_backend
request :get_lb_backends
request :add_machine_to_lb_application
request :add_machine_to_lb_backend
request :remove_machine_from_lb_backend
request :update_lb_backend_machine
class Mock
end
class Real
def initialize(options={})
@bluebox_api_key = options[:bluebox_api_key]
@bluebox_customer_id = options[:bluebox_customer_id]
@connection_options = options[:connection_options] || {}
@host = options[:bluebox_host] || "boxpanel.bluebox.net"
@persistent = options[:persistent] || false
@port = options[:bluebox_port] || 443
@scheme = options[:bluebox_scheme] || 'https'
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
end
def reload
@connection.reset
end
def request(params)
params[:headers] ||= {}
params[:headers].merge!({
'Authorization' => "Basic #{Base64.encode64([@bluebox_customer_id, @bluebox_api_key].join(':')).delete("\r\n")}"
})
begin
response = @connection.request(params.merge!({:host => @host}))
rescue Excon::Errors::HTTPStatusError => error
raise case error
when Excon::Errors::NotFound
Fog::Compute::Bluebox::NotFound.slurp(error)
else
error
end
end
unless response.body.empty? || params[:headers]['Accept'] == 'text/plain'
response.body = Fog::JSON.decode(response.body)
end
response
end
end
end
end
end

View file

@ -0,0 +1,32 @@
require 'fog/core/model'
module Fog
module Bluebox
class BLB
class LbApplication < Fog::Model
identity :id
attribute :name
attribute :ip_v4
attribute :ip_v6
attribute :description
attribute :created, :aliases => 'created_at'
def add_machine(lb_machine_id, options)
requires :id
service.add_machine_to_lb_application(id, lb_machine_id, options)
end
def lb_services
Fog::Bluebox::BLB::LbServices.new({
:service => service,
:lb_application => self
})
end
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/bluebox/models/blb/lb_application'
module Fog
module Bluebox
class BLB
class LbApplications < Fog::Collection
model Fog::Bluebox::BLB::LbApplication
def all
data = service.get_lb_applications.body
load(data)
end
def get(application_id)
if application_id && application = service.get_lb_application(application_id).body
new(application)
end
rescue Fog::Bluebox::BLB::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,22 @@
require 'fog/core/model'
module Fog
module Bluebox
class BLB
class LbBackend < Fog::Model
identity :id
attribute :name
attribute :lb_machines
attribute :monitoring_url
attribute :monitoring_url_hostname
attribute :acl_name
attribute :acl_rule
attribute :check_interval
end
end
end
end

View file

@ -0,0 +1,30 @@
require 'fog/core/collection'
require 'fog/bluebox/models/blb/lb_backend'
module Fog
module Bluebox
class BLB
class LbBackends < Fog::Collection
model Fog::Bluebox::BLB::LbBackend
attr_accessor :lb_service
def all
data = service.get_lb_backends(lb_service.id).body
load(data)
end
def get(lb_backend_id)
if lb_backend = service.get_lb_backend(lb_service.id, lb_backend_id).body
new(lb_backend)
end
rescue Fog::Compute::Bluebox::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,35 @@
require 'fog/core/model'
module Fog
module Bluebox
class BLB
class LbService < Fog::Model
identity :id
attribute :name
attribute :port
attribute :service_type
attribute :private
attribute :status_url
attribute :status_username
attribute :status_password
attribute :created, :aliases => 'created_at'
def lb_application
collection.lb_application
end
def lb_backends
Fog::Bluebox::BLB::LbBackends.new({
:service => service,
:lb_service => self
})
end
end
end
end
end

View file

@ -0,0 +1,29 @@
require 'fog/core/collection'
require 'fog/bluebox/models/blb/lb_service'
module Fog
module Bluebox
class BLB
class LbServices < Fog::Collection
model Fog::Bluebox::BLB::LbService
attr_accessor :lb_application
def all
data = service.get_lb_services(lb_application.id).body
load(data)
end
def get(lb_service_id)
if lb_service = service.get_lb_service(lb_application.id, lb_service_id).body
new(lb_service)
end
rescue Fog::Bluebox::BLB::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Bluebox
class BLB
class Real
# Add machine to default lb_backend for each lb_service
# in the application.
#
# === Parameters
# * lb_application_id<~String> - ID of application
# * lb_machine_id<~String> - ID of machine
# * options<~Hash>:
# * port<~Integer> - port machine listens on; defaults to service listening port
# * maxconn<~Integer> - maximum number of connections server can be sent
# * backup<~Boolean> - only send traffic to machine if all others are down
#
def add_machine_to_lb_application(lb_application_id, lb_machine_id, options = {})
# convert to CGI array args
body = Hash[options.map {|k,v| ["lb_options[#{k}]", v] }]
body['lb_machine'] = lb_machine_id
request(
:expects => 200,
:method => 'POST',
:path => "/api/lb_applications/add_machine/#{lb_application_id}.json",
:body => body.map {|k,v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}"}.join('&')
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,31 @@
module Fog
module Bluebox
class BLB
class Real
# Add machine to specified lb_backend
#
# === Parameters
# * lb_backend_id<~String> - ID of backend
# * lb_machine_id<~String> - ID of machine
# * options<~Hash>:
# * port<~Integer> - port machine listens on; defaults to service listening port
# * maxconn<~Integer> - maximum number of connections server can be sent
# * backup<~Boolean> - only send traffic to machine if all others are down
def add_machine_to_lb_backend(lb_backend_id, lb_machine_id, options = {})
# convert to CGI array args
body = Hash[options.map {|k,v| ["lb_options[#{k}]", v] }]
body['lb_machine'] = lb_machine_id
request(
:expects => 200,
:method => 'POST',
:path => "/api/lb_backends/#{lb_backend_id}/lb_machines.json",
:body => body.map {|k,v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}"}.join('&')
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Bluebox
class BLB
class Real
# Get details of an lb_application.
#
# ==== Parameters
# * lb_application_id<~Integer> - ID of application
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'id'<~String> - UUID of application
# * 'ip_v4'<~Array> - IPv4 addresses
# * 'ip_v6'<~String> - IPv6 address
# * 'name'<~String> - The hostname
# * 'lb_services'<~Array> - Listening services
# * 'source_ip_v4'<~String> - address that application connects to pool members from (v1 only)
def get_lb_application(lb_application_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_applications/#{lb_application_id}.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,31 @@
module Fog
module Bluebox
class BLB
class Real
# Get list of applications
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Array>:
# * 'application'<~Hash>:
# * 'id'<~String> - UUID of application
# * 'ip_v4'<~Array> - IPv4 addresses
# * 'ip_v6'<~String> - IPv6 address
# * 'name'<~String> - The hostname
# * 'lb_services'<~Array> - Listening services
# * 'source_ip_v4'<~String> - address that application connects to pool members from (v1 only)
def get_lb_applications
request(
:expects => 200,
:method => 'GET',
:path => 'api/lb_applications.json'
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Bluebox
class BLB
class Real
# Get details of an lb_backend.
#
# ==== Parameters
# * lb_service_id<~String> - service backend belongs to
# * lb_backend_id<~String> - backend to look up
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * id<~String> - backend ID
# * backend_name<~String>
# * lb_machines<~Array> - array of backend members
# * acl_name<~String> - name of ACL for this backend
# * acl_rule<~String>
# * monitoring_url_hostname<~String> - HTTP host header for health check
# * monitoring_url<~String> - URL for health check
# * check_interval<~Integer> - time between checks, in milliseconds
def get_lb_backend(lb_service_id, lb_backend_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_services/#{lb_service_id}/lb_backends/#{lb_backend_id}.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Bluebox
class BLB
class Real
# Get list of backends
#
# ==== Parameters
# * lb_service_id<~String> - service containing backends
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Array>:
# * backend<~Hash>:
# * id<~String> - backend ID
# * backend_name<~String>
# * lb_machines<~Array> - array of backend members
# * acl_name<~String> - name of ACL for this backend
# * acl_rule<~String>
# * monitoring_url_hostname<~String> - HTTP host header for health check
# * monitoring_url<~String> - URL for health check
# * check_interval<~Integer> - time between checks, in milliseconds
def get_lb_backends(lb_service_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_services/#{lb_service_id}/lb_backends.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Bluebox
class BLB
class Real
# Get details of an lb_machine.
#
# ==== Parameters
# * lb_backend_id<~String> - backend machine belongs to
# * lb_machine_id<~String> - machine to look up
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * id<~String> - machine ID
# * ip<~String> - machine IP address for this member (v4 or v6)
# * port<~Integer> - service port for this member
# * hostname<~String> - name as registered with Box Panel
# * acl_name<~String> - name of ACL for this machine
# * created<~DateTime> - when machine was added to load balancer backend
# * maxconn<~Integer> - maximum concurrent connections for this member (BLBv2 only)
def get_lb_machine(lb_backend_id, lb_machine_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_backends/#{lb_backend_id}/lb_machines/#{lb_machine_id}.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Bluebox
class BLB
class Real
# Get list of machines
#
# ==== Parameters
# * lb_backend_id<~String> - backend containing machines
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Array>:
# * machine<~Hash>:
# * id<~String> - machine ID
# * ip<~String> - machine IP address for this member (v4 or v6)
# * port<~Integer> - service port for this member
# * hostname<~String> - name as registered with Box Panel
# * acl_name<~String> - name of ACL for this machine
# * created<~DateTime> - when machine was added to load balancer backend
# * maxconn<~Integer> - maximum concurrent connections for this member (BLBv2 only)
def get_lb_machines(lb_backend_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_backends/#{lb_backend_id}/lb_machines.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Bluebox
class BLB
class Real
# Get details of a lb_service.
#
# ==== Parameters
# * lb_application_id<~String> - ID of application the service belongs to
# * lb_service_id<~String> - ID of service to look up
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * name<~String> - service name
# * port<~Integer> - port of load balanced service
# * private<~Boolean> - whether service is only available internally
# * status_username<~String> - HTTP basic auth username
# * status_password<~String> - HTTP basic auth password
# * status_url<~String> - URL of stats page
# * service_type<~String> - proto being load balanced (e.g. 'http', 'tcp')
# * created<~DateTime> - when service was created
def get_lb_service(lb_application_id, lb_service_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_applications/#{lb_application_id}/lb_services/#{lb_service_id}.json",
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Bluebox
class BLB
class Real
# Get list of load balancing services
#
# ==== Parameters
# * lb_application_id<~String> - Id of application services to list
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Array>:
# * backend<~Hash>:
# * name<~String> - service name
# * port<~Integer> - port of load balanced service
# * private<~Boolean> - whether service is only available internally
# * status_username<~String> - HTTP basic auth username
# * status_password<~String> - HTTP basic auth password
# * status_url<~String> - URL of stats page
# * service_type<~String> - proto being load balanced (e.g. 'http', 'tcp')
# * created<~DateTime> - when service was created
def get_lb_services(lb_application_id)
request(
:expects => 200,
:method => 'GET',
:path => "api/lb_applications/#{lb_application_id}/lb_services.json"
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Bluebox
class BLB
class Real
# remove machine from single backend
#
# === Parameters
# * lb_backend_id<~String> - ID of backend
# * lb_machine_id<~String> - ID of machine
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~String> - success or failure message
def remove_machine_from_lb_backend(lb_backend_id, lb_machine_id)
request(
:expects => 200,
:method => 'DELETE',
:path => "/api/lb_backends/#{lb_backend_id}/lb_machines/#{lb_machine_id}",
:headers => {"Accept" => "text/plain"},
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module Bluebox
class BLB
class Real
# change machine attributes (port &c) in a single backend
#
# === Parameters
# * lb_backend_id<~String> - ID of backend
# * lb_machine_id<~String> - ID of machine
# * options<~Hash>:
# * port<~Integer> - port machine listens on
# * maxconn<~Integer> - maximum number of connections server can be sent
# * backup<~Boolean> - only send traffic to machine if all others are down
def update_lb_backend_machine(lb_backend_id, lb_machine_id, options = {})
# inconsistent, no?
request(
:expects => 202,
:method => 'PUT',
:path => "/api/lb_backends/#{lb_backend_id}/lb_machines/#{lb_machine_id}",
:body => options.map {|k,v| "#{CGI.escape(k)}=#{CGI.escape(v.to_s)}"}.join('&')
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,64 @@
class Bluebox
module BLB
module Formats
LB_APPLICATION = {
'id' => String,
'ip_v4' => String,
'ip_v6' => String,
'name' => String,
'lb_services' => Array,
'source_ip_v4' => Fog::Nullable::String,
}
LB_APPLICATIONS = [LB_APPLICATION]
LB_SERVICE = {
'name' => String,
'port' => Integer,
'private' => Fog::Boolean,
'status_username' => String,
'status_password' => String,
'status_url' => String,
'service_type' => String,
'created' => String,
'lb_backends' => Array,
'pem_file_uploaded?' => Fog::Nullable::Boolean,
}
LB_SERVICES = [LB_SERVICE]
LB_BACKEND = {
'id' => String,
'backend_name' => String,
'lb_machines' => Array,
'acl_name' => String,
'acl_name' => String,
'monitoring_url_hostname' => String,
'monitoring_url' => String,
'monitoring_url_hostname' => String,
'check_interval' => Integer,
'rise' => Fog::Nullable::Integer,
'order' => Fog::Nullable::Integer,
'fall' => Fog::Nullable::Integer,
}
LB_BACKENDS = [LB_BACKEND]
LB_MACHINE = {
'port' => Integer,
'id' => String,
'ip' => String,
'maxconn' => Integer,
'hostname' => String,
'created' => String,
}
ADD_MACHINE_TO_LB = [
{ 'text' => String },
{ 'status' => String },
]
REMOVE_MACHINE_FROM_BACKEND = 'Record Removed.'
end
end
end

View file

@ -0,0 +1,75 @@
require 'securerandom'
Shindo.tests('Bluebox::BLB | lb_tests', ['bluebox']) do
pending if Fog.mocking?
tests('success') do
@flavor_id = compute_providers[:bluebox][:server_attributes][:flavor_id]
@image_id = compute_providers[:bluebox][:server_attributes][:image_id]
@location_id = compute_providers[:bluebox][:server_attributes][:location_id]
@password = SecureRandom.base64(18)
tests("get_lb_applications").formats(Bluebox::BLB::Formats::LB_APPLICATIONS) do
@lb_applications = Fog::Bluebox[:blb].get_lb_applications.body
end
tests("get_lb_application").formats(Bluebox::BLB::Formats::LB_APPLICATION) do
Fog::Bluebox[:blb].get_lb_application(@lb_applications.first['id']).body
end
tests("get_lb_services").formats(Bluebox::BLB::Formats::LB_SERVICES) do
@lb_services = Fog::Bluebox[:blb].get_lb_services(@lb_applications.first['id']).body
end
tests("get_lb_service").formats(Bluebox::BLB::Formats::LB_SERVICE) do
Fog::Bluebox[:blb].get_lb_service(@lb_applications.first['id'], @lb_services.first['id']).body
end
tests("get_lb_backends").formats(Bluebox::BLB::Formats::LB_BACKENDS) do
@lb_backends = Fog::Bluebox[:blb].get_lb_backends(@lb_services.first['id']).body
end
tests("get_lb_backend").formats(Bluebox::BLB::Formats::LB_BACKEND) do
Fog::Bluebox[:blb].get_lb_backend(@lb_services.first['id'], @lb_backends.first['id']).body
end
# create block
data = Fog::Compute[:bluebox].create_block(@flavor_id, @image_id, @location_id, {'password' => @password}).body
@block_id = data['id']
Fog::Compute[:bluebox].servers.get(@block_id).wait_for { ready? }
tests("add_machine_to_lb_application").formats(Bluebox::BLB::Formats::ADD_MACHINE_TO_LB) do
Fog::Bluebox[:blb].add_machine_to_lb_application(@lb_applications.first['id'], @block_id).body
end
@default_backend = @lb_backends.select { |x| x['backend_name'] == 'default' }.first
@id_in_backend = @default_backend['lb_machines'].last['id']
@machine_opts = { 'port' => 4361, 'backup' => true };
tests("update_lb_backend_machine(#{@lb_backends.first['id']}, #{@id_in_backend}, #{@machine_opts})").formats(Bluebox::BLB::Formats::LB_MACHINE) do
Fog::Bluebox[:blb].update_lb_backend_machine(@lb_backends.first['id'], @id_in_backend, @machine_opts).body
end
tests("remove_machine_from_lb_backend(#{@default_backend['id']}, #{@id_in_backend})").formats(Bluebox::BLB::Formats::REMOVE_MACHINE_FROM_BACKEND) do
Fog::Bluebox[:blb].remove_machine_from_lb_backend(@default_backend['id'], @id_in_backend).body
end
tests("add_machine_to_lb_backend(#{@default_backend['id']}, #{@block_id})").formats(Bluebox::BLB::Formats::ADD_MACHINE_TO_LB) do
Fog::Bluebox[:blb].add_machine_to_lb_backend(@default_backend['id'], @block_id).body
end
Fog::Compute[:bluebox].destroy_block(@block_id).body
end
tests('failure') do
tests('get_lb_application').raises(Fog::Compute::Bluebox::NotFound) do
Fog::Bluebox[:blb].get_lb_application('00000000-0000-0000-0000-000000000000')
end
tests('get_lb_service').raises(Fog::Compute::Bluebox::NotFound) do
Fog::Bluebox[:blb].get_lb_service('00000000-0000-0000-0000-000000000000','00000000-0000-0000-0000-000000000000')
end
tests('get_lb_backend').raises(Fog::Compute::Bluebox::NotFound) do
Fog::Bluebox[:blb].get_lb_backend('00000000-0000-0000-0000-000000000000','00000000-0000-0000-0000-000000000000')
end
end
end