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

Add clodo support.

This commit is contained in:
Обоев Рулон ибн Хаттаб 2011-10-01 01:45:09 +04:00 committed by NomadRain
parent eb282c4fb6
commit 05d1ae032d
20 changed files with 878 additions and 0 deletions

31
lib/fog/bin/clodo.rb Normal file
View file

@ -0,0 +1,31 @@
class Clodo < Fog::Bin
class << self
def class_for(key)
case key
when :compute
Fog::Compute::Clodo
else
raise ArgumentError, "Unrecognized service: #{key}"
end
end
def [](service)
@@connections ||= Hash.new do |hash, key|
hash[key] = case key
when :compute
Formatador.display_line("[yellow][WARN] Clodo[:compute] is deprecated, use Compute[:clodo] instead[/]")
Fog::Compute.new(:provider => 'Clodo')
else
raise ArgumentError, "Unrecognized service: #{key.inspect}"
end
end
@@connections[service]
end
def services
Fog::Clodo.services
end
end
end

34
lib/fog/clodo.rb Normal file
View file

@ -0,0 +1,34 @@
require 'fog/core'
module Fog
module Clodo
extend Fog::Provider
service(:compute, 'clodo/compute', 'Compute')
def self.authenticate(options)
clodo_auth_url = options[:clodo_auth_url] || "api.clodo.ru"
url = clodo_auth_url.match(/^https?:/) ? \
clodo_auth_url : 'https://' + clodo_auth_url
uri = URI.parse(url)
connection = Fog::Connection.new(url)
@clodo_api_key = options[:clodo_api_key]
@clodo_username = options[:clodo_username]
response = connection.request({
:expects => [200, 204],
:headers => {
'X-Auth-Key' => @clodo_api_key,
'X-Auth-User' => @clodo_username
},
:host => uri.host,
:method => 'GET',
:path => (uri.path and not uri.path.empty?) ? uri.path : 'v1.0'
})
response.headers.reject do |key, value|
!['X-Server-Management-Url', 'X-Storage-Url', 'X-CDN-Management-Url', 'X-Auth-Token'].include?(key)
end
end # authenticate
end # module Clodo
end # module Fog

150
lib/fog/clodo/compute.rb Normal file
View file

@ -0,0 +1,150 @@
module Fog
module Compute
class Clodo < Fog::Service
requires :clodo_api_key, :clodo_username
recognizes :clodo_auth_url, :persistent
recognizes :clodo_auth_token, :clodo_management_url
model_path 'fog/clodo/models/compute'
model :image
collection :images
model :server
collection :servers
request_path 'fog/clodo/requests/compute'
request :create_server
request :delete_server
# request :get_image_details # Not supported by API
request :list_images
request :list_images_detail
request :list_servers
request :list_servers_detail
request :get_server_details
request :server_action
request :start_server
request :stop_server
request :reboot_server
request :rebuild_server
# request :list_addresses
# request :list_private_addresses
# request :list_public_addresses
# request :confirm_resized_server
# request :revert_resized_server
# request :resize_server
# request :update_server
class Mock
def self.data
@data ||= Hash.new do |hash, key|
hash[key] = {
:last_modified => {
:images => {},
:servers => {}
},
:images => {},
:servers => {}
}
end
end
def self.reset
@data = nil
end
def initialize(options={})
require 'multi_json'
@clodo_username = options[:clodo_username]
end
def data
self.class.data[@clodo_username]
end
def reset_data
self.class.data.delete(@clodo_username)
end
end
class Real
def initialize(options={})
require 'multi_json'
@clodo_api_key = options[:clodo_api_key]
@clodo_username = options[:clodo_username]
@clodo_auth_url = options[:clodo_auth_url]
@clodo_servicenet = options[:clodo_servicenet]
@clodo_auth_token = options[:clodo_auth_token]
@clodo_management_url = options[:clodo_management_url]
@clodo_must_reauthenticate = false
authenticate
Excon.ssl_verify_peer = false if options[:clodo_servicenet] == true
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", options[:persistent])
end
def reload
@connection.reset
end
def request(params)
begin
response = @connection.request(params.merge({
:headers => {
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Auth-Token' => @auth_token
}.merge!(params[:headers] || {}),
:host => @host,
:path => "#{@path}/#{params[:path]}",
:query => ('ignore_awful_caching' << Time.now.to_i.to_s)
}))
rescue Excon::Errors::Unauthorized => error
if error.response.body != 'Bad username or password' # token expiration
@clodo_must_reauthenticate = true
authenticate
retry
else # bad credentials
raise error
end
rescue Excon::Errors::HTTPStatusError => error
raise case error
when Excon::Errors::NotFound
Fog::Compute::Clodo::NotFound.slurp(error)
else
error
end
end
unless response.body.empty?
response.body = MultiJson.decode(response.body)
end
response
end
private
def authenticate
if @clodo_must_reauthenticate || @clodo_auth_token.nil?
options = {
:clodo_api_key => @clodo_api_key,
:clodo_username => @clodo_username,
:clodo_auth_url => @clodo_auth_url
}
credentials = Fog::Clodo.authenticate(options)
@auth_token = credentials['X-Auth-Token']
uri = URI.parse(credentials['X-Server-Management-Url'])
else
@auth_token = @clodo_auth_token
uri = URI.parse(@clodo_management_url)
end
@host = @clodo_servicenet == true ? "snet-#{uri.host}" : uri.host
@path = uri.path
@port = uri.port
@scheme = uri.scheme
end
end
end
end
end

View file

@ -0,0 +1,31 @@
require 'fog/core/model'
module Fog
module Compute
class Clodo
class Image < Fog::Model
identity :id
attribute :name
attribute :vps_type
attribute :status
attribute :os_type
attribute :os_bits
attribute :os_hvm
def initialize(new_attributes)
super(new_attributes)
merge_attributes(new_attributes['_attr']) if new_attributes['_attr']
end
def ready?
status == 'ACTIVE'
end
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/clodo/models/compute/image'
module Fog
module Compute
class Clodo
class Images < Fog::Collection
model Fog::Compute::Clodo::Image
def all
data = connection.list_images_detail.body['images']
load(data)
end
def get(image_id)
nil
end
end
end
end
end

View file

@ -0,0 +1,157 @@
require 'fog/core/model'
module Fog
module Compute
class Clodo
class Server < Fog::Model
identity :id
attribute :addresses
attribute :name
attribute :image_id, :aliases => 'imageId'
attribute :state, :aliases => 'status'
attribute :vps_memory
attribute :vps_memory_max
attribute :vps_os_title
attribute :vps_os_bits
attribute :vps_os_type
attribute :vps_vnc
attribute :vps_cpu_load
attribute :vps_cpu_max
attribute :vps_cpu_1h_min
attribute :vps_cpu_1h_max
attribute :vps_mem_load
attribute :vps_mem_max
attribute :vps_mem_1h_min
attribute :vps_mem_1h_max
attribute :vps_hdd_load
attribute :vps_hdd_max
attribute :vps_traf_rx
attribute :vps_traf_tx
attribute :vps_createdate
attribute :vps_billingdate
attribute :vps_update
attribute :vps_update_days
attribute :vps_root_pass, :aliases => ['adminPass','password']
attribute :vps_user_pass
attribute :vps_vnc_pass
attr_writer :private_key, :private_key_path, :public_key, :public_key_path, :username
def initialize(attributes={})
self.image_id ||= attributes[:vps_os] ? attributes[:vps_os] : 666
super
end
def destroy
requires :id
connection.delete_server(id)
true
end
def image
requires :image_id
image_id # API does not support image details request. :-(
end
def private_ip_address
nil
end
def private_key_path
@private_key_path ||= Fog.credentials[:private_key_path]
@private_key_path &&= File.expand_path(@private_key_path)
end
def private_key
@private_key ||= private_key_path && File.read(private_key_path)
end
def public_ip_address
pubaddrs = addresses && addresses['public'] ? addresses['public'].select {|ip| ip['primary_ip']} : nil
pubaddrs && !pubaddrs.empty? ? pubaddrs.first['ip'] : nil
end
def public_key_path
@public_key_path ||= Fog.credentials[:public_key_path]
@public_key_path &&= File.expand_path(@public_key_path)
end
def public_key
@public_key ||= public_key_path && File.read(public_key_path)
end
def ready?
self.state == 'is_running'
end
def reboot(type = 'SOFT')
requires :id
connection.reboot_server(id, type)
true
end
def save
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if identity
requires :image_id
options = {
'name' => name
}
options = options.reject {|key, value| value.nil?}
data = connection.create_server(image_id, options)
merge_attributes(data.body['server'])
true
end
def setup(credentials = {})
requires :public_ip_address, :identity, :public_key, :username
Fog::SSH.new(public_ip_address, username, credentials).run([
%{mkdir .ssh},
%{echo "#{public_key}" >> ~/.ssh/authorized_keys},
%{passwd -l #{username}},
%{echo "#{MultiJson.encode(attributes)}" >> ~/attributes.json},
%{echo "#{MultiJson.encode(metadata)}" >> ~/metadata.json}
])
rescue Errno::ECONNREFUSED
sleep(1)
retry
end
def ssh(commands)
requires :public_ip_address, :identity, :username
options = {}
options[:key_data] = [private_key] if private_key
Fog::SSH.new(public_ip_address, username, options).run(commands)
end
def scp(local_path, remote_path, upload_options = {})
requires :public_ip_address, :username
scp_options = {}
scp_options[:key_data] = [private_key] if private_key
Fog::SCP.new(public_ip_address, username, scp_options).upload(local_path, remote_path, upload_options)
end
def username
@username ||= 'root'
end
def password
vps_root_pass
end
private
### def adminPass=(new_admin_pass)
### @vps_root_pass= new_admin_pass
### end
end
end
end
end

View file

@ -0,0 +1,36 @@
require 'fog/core/collection'
require 'fog/clodo/models/compute/server'
module Fog
module Compute
class Clodo
class Servers < Fog::Collection
model Fog::Compute::Clodo::Server
def all
data = connection.list_servers_detail.body['servers']
load(data)
end
def bootstrap(new_attributes = {})
server = create(new_attributes)
server.wait_for { ready? }
server.setup(:password => server.password)
server
end
def get(server_id)
if server = connection.get_server_details(server_id).body['server']
new(server)
end
rescue Fog::Compute::Clodo::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,63 @@
module Fog
module Compute
class Clodo
class Real
# Вход:
# name - название VPS
# vps_title - название VPS (может использоваться либо этот параметр, либо "name")
# vps_type - тип VPS (VirtualServer,ScaleServer)
# vps_memory - память (для ScaleServer - нижняя граница) (в MB)
# vps_memory_max - верхняя граница памяти для ScaleServer (в MB)
# vps_hdd - размер диска (в GB)
# vps_admin - тип поддержки (1 - обычная, 2 - расширенная, 3 - VIP)
# vps_os - id ОС
# Выход:
# id - номер VPS
# name - название VPS
# imageId - id ОС
# adminPass - пароль root
def create_server(image_id, options = {})
data = {
'server' => {
:vps_os => image_id,
:vps_type => options[:vps_type]?options[:vps_type]:'ScaleServer',
:vps_hdd => options[:vps_hdd]?options[:vps_hdd]:5,
:vps_memory => options[:vps_memory]?options[:vps_memory]:256,
:vps_memory_max => options[:vps_memory_max]?options[:vps_memory_max]:1024,
:vps_admin => options[:vps_admin]?options[:vps_admin]:1
}
}
data['server'].merge! options if options
request(
:body => MultiJson.encode(data),
:expects => [200, 202],
:method => 'POST',
:path => 'servers'
)
end
class Mock
def create_server(image_id, options = {})
response = Excon::response.new
response.status = 202
data = {
'id' => Fog::Mock.random_numbers(6).to_i,
'imageId' => image_id,
'name' => options['name'] || "server_#{rand(999)}",
'adminPass' => '23ryh8udbcbyt'
}
self.data[:last_modified][:servers][data['id']] = Time.now
self.data[:servers][data['id']] = data
response.body = { 'server' => data.merge({'adminPass' => 'password'}) }
response
end
end
end
end
end
end

View file

@ -0,0 +1,43 @@
module Fog
module Compute
class Clodo
class Real
# Delete an existing server
#
# ==== Parameters
# * id<~Integer> - Id of server to delete
#
def delete_server(server_id)
request(
:expects => 204,
:method => 'DELETE',
:path => "servers/#{server_id}"
)
end
end
class Mock
def delete_server(server_id)
response = Excon::Response.new
if server = list_servers_detail.body['servers'].detect {|_| _['id'] == server_id}
if server['status'] == 'BUILD'
response.status = 405
raise(Excon::Errors.status_error({:expects => 204}, response))
else
self.data[:last_modified][:servers].delete(server_id)
self.data[:servers].delete(server_id)
response.status = 204
end
response
else
raise Fog::Compute::Clodo::NotFound
end
end
end
end
end
end

View file

@ -0,0 +1,13 @@
module Fog
module Compute
class Clodo
class Real
def get_image_details(image_id)
request(:expects => [200,203],
:method => 'GET',
:path => "images/#{image_id}")
end
end
end
end
end

View file

@ -0,0 +1,48 @@
module Fog
module Compute
class Clodo
class Real
# Get details about a server
#
# ==== Parameters
# * server_id<~Integer> - Id of server to get details for
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'server'<~Hash>:
# * 'addresses'<~Hash>:
# * 'public'<~Array> - public address strings
# * 'private'<~Array> - private address strings
# * 'id'<~Integer> - Id of server
# * 'imageId'<~Integer> - Id of image used to boot server
# * 'name<~String> - Name of server
# * 'status'<~String> - Current server status
def get_server_details(server_id)
request(
:expects => [200, 203],
:method => 'GET',
:path => "servers/#{server_id}"
)
end
end
class Mock
def get_server_details(server_id)
response = Excon::Response.new
if server = list_servers_detail.body['servers'].detect {|_| _['id'] == server_id}
response.status = [200, 203][rand(1)]
response.body = { 'server' => server }
response
else
raise Fog::Compute::Clodo::NotFound
end
end
end
end
end
end

View file

@ -0,0 +1,42 @@
module Fog
module Compute
class Clodo
class Real
# List all images (IDs and names only)
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'id'<~Integer> - Id of the image
# * 'name'<~String> - Name of the image
# * 'status'<~String> - Status of the image
# * 'vps_type'<~String> - VirtualServer or ScaleServer
def list_images
request(
:expects => [200, 203],
:method => 'GET',
:path => 'images'
)
end
end
class Mock
def list_images
response = Excon::Response.new
response.status = 200
response.body = {
'images' => [
{ 'name' => 'Debian 5.0.6 64 bits', 'id' => 1, 'vps_type' => 'VirtualServer', 'status' => 'ACTIVE' },
{ 'name' => 'CentOS 5.5 32 bits', 'id' => 3, 'vps_type' => 'VirtualServer', 'status' => 'ACTIVE' }
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,47 @@
module Fog
module Compute
class Clodo
class Real
# List all images
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'os_type'<~String> - OS distribution
# * 'os_bits'<~Integer> - OS bits
# * 'os_hvm'<~Integer> - HVM flag
# * '_attr'<~Hash>:
# * 'id'<~Integer> - Id of the image
# * 'name'<~String> - Name of the image
# * 'status'<~String> - Status of the image
# * 'vps_type'<~String> - VirtualServer or ScaleServer
def list_images_detail
request(
:expects => [200, 203],
:method => 'GET',
:path => 'images/detail'
)
end
end
class Mock
def list_images_detail
response = Excon::Response.new
response.status = 200
response.body = {
'images' => [
{ 'name' => 'Debian 5.0.6 64 bits', 'id' => 1, 'vps_type' => 'VirtualServer', 'status' => 'ACTIVE', 'os_type' => 'debian', 'os_bits' => 64, 'os_hvm' => 0 },
{ 'name' => 'CentOS 5.5 32 bits', 'id' => 1, 'vps_type' => 'VirtualServer', 'status' => 'ACTIVE', 'os_type' => 'centos', 'os_bits' => 32, 'os_hvm' => 0 }
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,41 @@
module Fog
module Compute
class Clodo
class Real
# List all servers (IDs and names only)
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'servers'<~Array>:
# * 'id'<~Integer> - Id of server
# * 'name<~String> - Name of server
def list_servers
request(
:expects => [200, 203],
:method => 'GET',
:path => 'servers'
)
end
end
class Mock
def list_servers
response = Excon::Response.new
data = list_servers_detail.body['servers']
servers = []
for server in data
servers << server.reject { |key, value| !['id', 'name'].include?(key) }
end
response.status = [200, 203][rand(1)]
response.body = { 'servers' => servers }
response
end
end
end
end
end

View file

@ -0,0 +1,53 @@
module Fog
module Compute
class Clodo
class Real
# List all servers details
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'servers'<~Array>:
# * 'id'<~Integer> - Id of server
# * 'name<~String> - Name of server
# * 'imageId'<~Integer> - Id of image used to boot server
# * 'status'<~String> - Current server status
# * 'addresses'<~Hash>:
# * 'public'<~Array> - public address strings
# * 'private'<~Array> - private address strings
# * 'metadata'<~Hash> - metadata
def list_servers_detail
request(
:expects => [200, 203],
:method => 'GET',
:path => 'servers/detail'
)
end
end
class Mock
def list_servers_detail
response = Excon::Response.new
servers = self.data[:servers].values
for server in servers
case server['status']
when 'BUILD'
if Time.now - self.data[:last_modified][:servers][server['id']] > Fog::Mock.delay * 2
server['status'] = 'ACTIVE'
end
end
end
response.status = [200, 203][rand(1)]
response.body = { 'servers' => servers }
response
end
end
end
end
end

View file

@ -0,0 +1,12 @@
module Fog
module Compute
class Clodo
class Real
def reboot_server(id, type)
body = {'reboot' => {}}
server_action(id, body)
end
end
end
end
end

View file

@ -0,0 +1,13 @@
module Fog
module Compute
class Clodo
class Real
def rebuild_server(id, image_id, vps_isp = nil)
body = {'rebuild' => {'imageId' => image_id}}
body['rebuild']['vps_isp'] = vps_isp if vps_isp
server_action(id, body)
end
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Clodo
class Real
def server_action(id, action)
request(
:body => MultiJson.encode(action),
:expects => [204],
:method => 'POST',
:path => "servers/#{id}/action")
end
end
end
end
end

View file

@ -0,0 +1,12 @@
module Fog
module Compute
class Clodo
class Real
def start_server(id)
body = {'start' => {}}
server_action(id, body)
end
end
end
end
end

View file

@ -0,0 +1,12 @@
module Fog
module Compute
class Clodo
class Real
def stop_server(id)
body = {'stop' => {}}
server_action(id, body)
end
end
end
end
end