diff --git a/lib/fog/cloudstack.rb b/lib/fog/cloudstack.rb index aefc049b3..0ea0d64c7 100644 --- a/lib/fog/cloudstack.rb +++ b/lib/fog/cloudstack.rb @@ -17,9 +17,6 @@ module Fog end def self.signed_params(key,params) - # remove empty attributes, cloudstack will not takem them into account when verifying signature - params.reject!{|k,v| v.nil? || v.to_s == ''} - query = params.to_a.sort.collect{|c| "#{c[0]}=#{escape(c[1].to_s)}"}.join('&').downcase signed_string = Base64.encode64(OpenSSL::HMAC.digest(DIGEST,key,query)).strip diff --git a/lib/fog/cloudstack/compute.rb b/lib/fog/cloudstack/compute.rb index b221bded8..6d558d682 100644 --- a/lib/fog/cloudstack/compute.rb +++ b/lib/fog/cloudstack/compute.rb @@ -1,43 +1,48 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'cloudstack')) require 'fog/compute' +require 'digest/md5' module Fog module Compute class Cloudstack < Fog::Service - class BadRequest < Fog::Errors::Error; end + class BadRequest < Fog::Compute::Cloudstack::Error; end + class Unauthorized < Fog::Compute::Cloudstack::Error; end - requires :cloudstack_api_key, :cloudstack_secret_access_key, :host - recognizes :host, :port, :path, :scheme, :persistent + requires :cloudstack_host + + recognizes :cloudstack_api_key, :cloudstack_secret_access_key, :cloudstack_session_key, :cloudstack_session_id, + :cloudstack_port, :cloudstack_path, :cloudstack_scheme, :cloudstack_persistent request_path 'fog/cloudstack/requests/compute' + request :acquire_ip_address + request :assign_to_load_balancer_rule + request :attach_volume request :authorize_security_group_ingress - request :create_account request :create_domain + request :create_port_forwarding_rule + request :create_load_balancer_rule request :create_security_group request :create_snapshot request :create_user - + request :create_volume request :delete_account request :delete_domain + request :delete_load_balancer_rule + request :delete_port_forwarding_rule request :delete_security_group request :delete_snapshot request :delete_user - + request :delete_volume + request :detach_volume request :deploy_virtual_machine - request :destroy_virtual_machine - request :disable_user - request :enable_user - request :generate_usage_records - request :get_vm_password - request :list_accounts request :list_alerts request :list_async_jobs @@ -56,11 +61,15 @@ module Fog request :list_hypervisors request :list_instance_groups request :list_isos + request :list_load_balancer_rules + request :list_load_balancer_rule_instances request :list_network_offerings request :list_networks request :list_os_categories request :list_os_types request :list_pods + request :list_port_forwarding_rules + request :list_public_ip_addresses request :list_resource_limits request :list_security_groups request :list_service_offerings @@ -73,17 +82,14 @@ module Fog request :list_virtual_machines request :list_volumes request :list_zones - request :query_async_job_result - request :reboot_virtual_machine request :register_user_keys - + request :remove_from_load_balancer_rule + request :reset_password_for_virtual_machine request :revoke_security_group_ingress - request :start_virtual_machine request :stop_virtual_machine - request :update_account request :update_domain request :update_user @@ -121,54 +127,122 @@ module Fog require 'multi_json' @cloudstack_api_key = options[:cloudstack_api_key] @cloudstack_secret_access_key = options[:cloudstack_secret_access_key] - @host = options[:host] - @path = options[:path] || '/client/api' - @port = options[:port] || 443 - @scheme = options[:scheme] || 'https' - @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", options[:persistent]) + @cloudstack_session_id = options[:cloudstack_session_id] + @cloudstack_session_key = options[:cloudstack_session_key] + @host = options[:cloudstack_host] + @path = options[:cloudstack_path] || '/client/api' + @port = options[:cloudstack_port] || 443 + @scheme = options[:cloudstack_scheme] || 'https' + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", options[:cloudstack_persistent]) end def reload @connection.reset end - def request(params) - params.merge!({ - 'apiKey' => @cloudstack_api_key, - 'response' => 'json' + def login(username,password,domain) + response = issue_request({ + 'response' => 'json', + 'command' => 'login', + 'username' => username, + 'password' => Digest::MD5.hexdigest(password), + 'domain' => domain }) - - signature = Fog::Cloudstack.signed_params(@cloudstack_secret_access_key,params) + + # Parse response cookies to retrive JSESSIONID token + cookies = CGI::Cookie.parse(response.get_header('Set-Cookie')) + sessionid = cookies['JSESSIONID'].first + + # Decode the login response + response = MultiJson.decode(response.body) + user = response['loginresponse'] + user.merge!('sessionid' => sessionid) + + @cloudstack_session_id = user['sessionid'] + @cloudstack_session_key = user['sessionkey'] + + user + end + + def request(params) + params.reject!{|k,v| v.nil?} + + params.merge!('response' => 'json') + + if has_session? + params, headers = authorize_session(params) + elsif has_keys? + params, headers = authorize_api_keys(params) + end + + response = issue_request(params,headers) + response = MultiJson.decode(response.body) unless response.body.empty? + response + end + + private + def has_session? + @cloudstack_session_id && @cloudstack_session_key + end + + def has_keys? + @cloudstack_api_key && @cloudstack_secret_access_key + end + + def authorize_session(params) + # set the session id cookie for the request + headers = {'Cookie' => "JSESSIONID=#{@cloudstack_session_id};"} + # set the sesion key for the request, params are not signed using session auth + params.merge!('sessionkey' => @cloudstack_session_key) + + return params, headers + end + + def authorize_api_keys(params) + headers = {} + # merge the api key into the params + params.merge!('apiKey' => @cloudstack_api_key) + # sign the request parameters + signature = Fog::Cloudstack.signed_params(@cloudstack_secret_access_key,params) + # merge signature into request param params.merge!({'signature' => signature}) + return params, headers + end + + def issue_request(params={},headers={},method='GET',expects=200) begin + puts params.inspect response = @connection.request({ :query => params, - :method => 'GET', - :expects => 200 + :headers => headers, + :method => method, + :expects => expects }) + rescue Excon::Errors::HTTPStatusError => error error_response = MultiJson.decode(error.response.body) error_code = error_response.values.first['errorcode'] error_text = error_response.values.first['errortext'] + puts error_response.inspect + + puts error_code + puts error_text + case error_code + when 401 + raise Fog::Compute::Cloudstack::Unauthorized, error_text when 431 - raise Fog::Compute::Cloudstack::BadRequest.new(error_text) + raise Fog::Compute::Cloudstack::BadRequest, error_text else - raise Fog::Compute::Cloudstack::Error.new(error_text) + raise Fog::Compute::Cloudstack::Error, error_text end end - unless response.body.empty? - response = MultiJson.decode(response.body) - end - - response end - end end end diff --git a/lib/fog/cloudstack/requests/compute/acquire_ip_address.rb b/lib/fog/cloudstack/requests/compute/acquire_ip_address.rb new file mode 100644 index 000000000..88a05a317 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/acquire_ip_address.rb @@ -0,0 +1,21 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates an account. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/associateIpAddress.html] + def acquire_ip_address(options={}) + options.merge!( + 'command' => 'associateIpAddress' + ) + + request(options) + end + + end + end + end +end + diff --git a/lib/fog/cloudstack/requests/compute/assign_to_load_balancer_rule.rb b/lib/fog/cloudstack/requests/compute/assign_to_load_balancer_rule.rb new file mode 100644 index 000000000..696b99add --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/assign_to_load_balancer_rule.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Assigns virtual machine or a list of virtual machines to a load balancer rule. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/assignToLoadBalancerRule.html] + def assign_to_load_balancer_rule(id,virtualmachineids=[]) + virtualmachineids = [*virtualmachineids] + + options = { + 'command' => 'assignToLoadBalancerRule', + 'id' => id, + 'virtualmachineids' => virtualmachineids.join(',') + } + + request(options) + end + + end + end + end +end + diff --git a/lib/fog/cloudstack/requests/compute/attach_volume.rb b/lib/fog/cloudstack/requests/compute/attach_volume.rb new file mode 100644 index 000000000..eee1506a4 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/attach_volume.rb @@ -0,0 +1,23 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Attaches a disk volume to a virtual machine. + # + # {CloudStack API Reference}[http://http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/attachVolume.html] + def attach_volume(id,virtualmachineid,deviceid=nil) + options = { + 'command' => 'attachVolume', + 'id' => id, + 'virtualmachineid' => virtualmachineid, + 'deviceid' => deviceid + } + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/create_load_balancer_rule.rb b/lib/fog/cloudstack/requests/compute/create_load_balancer_rule.rb new file mode 100644 index 000000000..958153cfc --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/create_load_balancer_rule.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates a load balancer rule + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.11/global_admin/createLoadBalancerRule.html] + def create_load_balancer_rule(options={}) + options.merge!( + 'command' => 'createLoadBalancerRule' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/create_port_forwarding_rule.rb b/lib/fog/cloudstack/requests/compute/create_port_forwarding_rule.rb new file mode 100644 index 000000000..fa595c911 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/create_port_forwarding_rule.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates a domain. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/createPortForwardingRule.html] + def create_port_forwarding_rule(options={}) + options.merge!( + 'command' => 'createPortForwardingRule' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/create_volume.rb b/lib/fog/cloudstack/requests/compute/create_volume.rb new file mode 100644 index 000000000..c9341234a --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/create_volume.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates a volume for an account that already exists. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/createVolume.html] + def create_volume(options={}) + options.merge!( + 'command' => 'createVolume' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/delete_load_balancer_rule.rb b/lib/fog/cloudstack/requests/compute/delete_load_balancer_rule.rb new file mode 100644 index 000000000..425a902ea --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/delete_load_balancer_rule.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates a domain. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/deleteLoadBalancerRule.html] + def delete_load_balancer_rule(options={}) + options.merge!( + 'command' => 'deleteLoadBalancerRule' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/delete_port_forwarding_rule.rb b/lib/fog/cloudstack/requests/compute/delete_port_forwarding_rule.rb new file mode 100644 index 000000000..aa14a53da --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/delete_port_forwarding_rule.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Creates a domain. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/deletePortForwardingRule.html] + def delete_port_forwarding_rule(options={}) + options.merge!( + 'command' => 'deletePortForwardingRule' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/delete_volume.rb b/lib/fog/cloudstack/requests/compute/delete_volume.rb new file mode 100644 index 000000000..2ed3003fc --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/delete_volume.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Deletes a specified user. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/deleteVolume.html] + def delete_volume(options={}) + options.merge!( + 'command' => 'deleteVolume' + ) + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb b/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb index d07a4ab5a..fd7c57596 100644 --- a/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb +++ b/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb @@ -12,7 +12,7 @@ module Fog ) if ( securitygroupids = options.delete('securitygroupids') ).is_a?(Array) - options.merge!('securitygroupids' => securitygroupnames.join(',')) + options.merge!('securitygroupids' => securitygroupids.join(',')) end if ( securitygroupnames = options.delete('securitygroupnames') ).is_a?(Array) diff --git a/lib/fog/cloudstack/requests/compute/detach_volume.rb b/lib/fog/cloudstack/requests/compute/detach_volume.rb new file mode 100644 index 000000000..e63875faf --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/detach_volume.rb @@ -0,0 +1,21 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Deletes a specified domain. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/detachVolume.html] + def detach_volume(id) + options = { + 'command' => 'detachVolume', + 'id' => id + } + + request(options) + end + + end + end + end +end diff --git a/lib/fog/cloudstack/requests/compute/list_load_balancer_rule_instances.rb b/lib/fog/cloudstack/requests/compute/list_load_balancer_rule_instances.rb new file mode 100644 index 000000000..cc56f6f00 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/list_load_balancer_rule_instances.rb @@ -0,0 +1,23 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Lists resource limits. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/listLoadBalancerRuleInstances.html] + def list_load_balancer_rule_instances(load_balancer_rule_id,options={}) + options.merge!( + 'command' => 'listLoadBalancerRuleInstances', + 'id' => load_balancer_rule_id + ) + + request(options) + end + + end + end + end +end + + diff --git a/lib/fog/cloudstack/requests/compute/list_load_balancer_rules.rb b/lib/fog/cloudstack/requests/compute/list_load_balancer_rules.rb new file mode 100644 index 000000000..9be207951 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/list_load_balancer_rules.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Lists resource limits. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/listLoadBalancerRules.html] + def list_load_balancer_rules(options={}) + options.merge!( + 'command' => 'listLoadBalancerRules' + ) + + request(options) + end + + end + end + end +end + + diff --git a/lib/fog/cloudstack/requests/compute/list_port_forwarding_rules.rb b/lib/fog/cloudstack/requests/compute/list_port_forwarding_rules.rb new file mode 100644 index 000000000..9ce663021 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/list_port_forwarding_rules.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Lists resource limits. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/listPortForwardingRules.html] + def list_port_forwarding_rules(options={}) + options.merge!( + 'command' => 'listPortForwardingRules' + ) + + request(options) + end + + end + end + end +end + + diff --git a/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb b/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb new file mode 100644 index 000000000..5c6c10ea0 --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/list_public_ip_addresses.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Lists resource limits. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/listPublicIpAddresses.html] + def list_public_ip_addresses(options={}) + options.merge!( + 'command' => 'listPublicIpAddresses' + ) + + request(options) + end + + end + end + end +end + + diff --git a/lib/fog/cloudstack/requests/compute/remove_from_load_balancer_rule.rb b/lib/fog/cloudstack/requests/compute/remove_from_load_balancer_rule.rb new file mode 100644 index 000000000..913813bfa --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/remove_from_load_balancer_rule.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Removes a virtual machine or a list of virtual machines from a load balancer rule. + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/removeFromLoadBalancerRule.html] + def remove_from_load_balancer_rule(id,virtualmachineids=[]) + virtualmachineids = [*virtualmachineids] + + options = { + 'command' => 'removeFromLoadBalancerRule', + 'id' => id, + 'virtualmachineids' => virtualmachineids.join(',') + } + + request(options) + end + + end + end + end +end + diff --git a/lib/fog/cloudstack/requests/compute/reset_password_for_virtual_machine.rb b/lib/fog/cloudstack/requests/compute/reset_password_for_virtual_machine.rb new file mode 100644 index 000000000..b044bd0df --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/reset_password_for_virtual_machine.rb @@ -0,0 +1,21 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Returns an encrypted password for the VM + # + # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/resetPasswordForVirtualMachine.html] + def reset_password_for_virtual_machine(id) + options = { + 'command' => 'resetPasswordForVirtualMachine', + 'id' => id + } + + request(options) + end + + end + end + end +end