diff --git a/lib/fog/cloudstack.rb b/lib/fog/cloudstack.rb index e1fc80931..b426fb246 100644 --- a/lib/fog/cloudstack.rb +++ b/lib/fog/cloudstack.rb @@ -17,12 +17,24 @@ module Fog end def self.signed_params(key,params) - query = params.to_a.sort.collect{|c| "#{c[0]}=#{escape(c[1].to_s)}"}.join('&').downcase + query = params.map{|k,v| [k.to_s, v]}.sort.collect{|c| "#{c[0]}=#{escape(c[1].to_s)}"}.join('&').downcase signed_string = Base64.encode64(OpenSSL::HMAC.digest(@@digest,key,query)).strip signed_string end + + def self.uuid + [8,4,4,4,12].map{|i| Fog::Mock.random_hex(i)}.join("-") + end + + def self.ip_address + 4.times.map{ Fog::Mock.random_numbers(3) }.join(".") + end + + def self.mac_address + 6.times.map{ Fog::Mock.random_numbers(2) }.join(":") + end end end diff --git a/lib/fog/cloudstack/compute.rb b/lib/fog/cloudstack/compute.rb index 860d39e6e..01c45ba15 100644 --- a/lib/fog/cloudstack/compute.rb +++ b/lib/fog/cloudstack/compute.rb @@ -10,12 +10,24 @@ module Fog class Unauthorized < Fog::Compute::Cloudstack::Error; end 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' - + + + model_path 'fog/cloudstack/models/compute' + model :address + model :server + collection :servers + model :image + collection :images + model :flavor + collection :flavors + model :zone + collection :zones + request :acquire_ip_address request :assign_to_load_balancer_rule request :attach_volume @@ -40,6 +52,7 @@ module Fog request :delete_ssh_key_pair request :delete_snapshot request :delete_snapshot_policies + request :delete_template request :delete_user request :delete_volume request :detach_volume @@ -135,14 +148,14 @@ module Fog class Real def initialize(options={}) - @cloudstack_api_key = options[:cloudstack_api_key] + @cloudstack_api_key = options[:cloudstack_api_key] @cloudstack_secret_access_key = options[:cloudstack_secret_access_key] - @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' + @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], {:ssl_verify_peer => false}) end @@ -245,9 +258,177 @@ module Fog raise Fog::Compute::Cloudstack::Error, error_text end end - + + end + end # Real + + class Mock + def self.data + @data ||= begin + rc_options = Fog.credentials[:cloudstack] || {} + zone_id = rc_options[:zone_id] ||"c554c592-e09c-9df5-7688-4a32754a4305" + image_id = rc_options[:image_id] || "8a31cf9c-f248-0588-256e-9dbf58785216" + flavor_id = rc_options[:flavor_id] || "4437ac6c-9fe3-477a-57ec-60a5a45896a4" + account_id = "8bec6f15-e2b8-44fc-a8f3-a022b2873440" + user_id = Fog::Cloudstack.uuid + domain_id = Fog::Cloudstack.uuid + network_id = (Array(rc_options[:network_ids]) || [Fog::Cloudstack.uuid]).first + domain_name = "exampleorg" + domain = { + "id" => domain_id, + "name" => domain_name, + "level" => 1, + "parentdomainid" => Fog::Cloudstack.uuid, + "parentdomainname" => "ROOT", + "haschild" => false, + "path" => "ROOT/accountname" + } + { + :networks => { network_id => { + "id" => network_id, + "name" => "10.56.23.0/26", + "displaytext" => "10.56.23.0/26", + "broadcastdomaintype" => "Vlan", + "traffictype" => "Guest", + "gateway" => "10.56.23.1", + "netmask" => "255.255.255.192", + "cidr" => "10.56.23.0/26", + "zoneid" => zone_id, + "zonename" => "zone-00", + "networkofferingid" => "af0c9bd5-a1b2-4ad0-bf4b-d6fa9b1b9d5b", + "networkofferingname" => "DefaultSharedNetworkOffering", + "networkofferingdisplaytext" => "Offering for Shared networks", + "networkofferingavailability" => "Optional", + "issystem" => false, + "state" => "Setup", + "related" => "86bbc9fc-d92e-49db-9fdc-296189090017", + "broadcasturi" => "vlan://800", + "dns1" => "10.0.80.11", + "type" => "Shared", + "vlan" => "800", + "acltype" => "Domain", + "subdomainaccess" => true, + "domainid" => domain_id, + "domain" => "ROOT", + "service" => [ + {"name" => "UserData"}, + {"name" => "Dhcp"}, + {"name" => "Dns", "capability" => [ + {"name" => "AllowDnsSuffixModification", + "value" => "true", + "canchooseservicecapability" => false}] + }], + "networkdomain" => "cs1cloud.internal", + "physicalnetworkid" => "8f4627c5-1fdd-4504-8a92-f61b4e9cb3e3", + "restartrequired" => false, + "specifyipranges" => true} + }, + :zones => { zone_id => { + "id" => zone_id, + "name"=> "zone-00", + "domainid" => 1, + "domainname" => "ROOT", + "networktype" => "Advanced", + "securitygroupsenabled" => false, + "allocationstate" => "Enabled", + "zonetoken" => Fog::Cloudstack.uuid, + "dhcpprovider" => "VirtualRouter"}}, + :images => { image_id => { + "id" => image_id, + "name" => "CentOS 5.6(64-bit) no GUI (XenServer)", + "displaytext" => "CentOS 5.6(64-bit) no GUI (XenServer)", + "ispublic" => true, + "created" => "2012-05-09T15:29:33-0500", + "isready" => true, + "passwordenabled" => false, + "format" => "VHD", + "isfeatured" => true, + "crossZones" => true, + "ostypeid" => "a6a6694a-18f5-4765-8418-2b7a5f37cd0f", + "ostypename" => "CentOS 5.3 (64-bit)", + "account" => "system", + "zoneid" => zone_id, + "zonename" => "zone-00", + "status" => "Download Complete", + "size" => 21474836480, + "templatetype" => "BUILTIN", + "domain" => "ROOT", + "domainid" => "6023b6fe-5bef-4358-bc76-9f4e75afa52f", + "isextractable" => true, + "checksum" => "905cec879afd9c9d22ecc8036131a180"}}, + :flavors => { flavor_id => { + "id" => flavor_id, + "name" => "Medium Instance", + "displaytext" => "Medium Instance", + "cpunumber" => 1, + "cpuspeed" => 1000, + "memory" => 1024, + "created" => "2012-05-09T14:48:36-0500", + "storagetype" => "shared", + "offerha" => false, + "limitcpuuse" => false, + "issystem" => false, + "defaultuse" => false}}, + :accounts => { account_id => { + "id" => account_id, + "name" => "accountname", + "accounttype" => 2, + "domainid" => domain_id, + "domain" => domain_name, + "receivedbytes" => 0, + "sentbytes" => 0, + "vmlimit" => "Unlimited", + "vmtotal" => 0, + "vmavailable" => "Unlimited", + "iplimit" => "Unlimited", + "iptotal" => 0, + "ipavailable" => "Unlimited", + "volumelimit" => "Unlimited", + "volumetotal" => 0, + "volumeavailable" => "Unlimited", + "snapshotlimit" => "Unlimited", + "snapshottotal" => 0, + "snapshotavailable" => "Unlimited", + "templatelimit" => "Unlimited", + "templatetotal" => 0, + "templateavailable" => "Unlimited", + "vmstopped" => 0, + "vmrunning" => 0, + "projectlimit" => "Unlimited", + "projecttotal" => 1, + "projectavailable" => "Unlimited", + "networklimit" => "Unlimited", + "networktotal" => 0, + "networkavailable" => "Unlimited", + "state" => "enabled", + "user" => + [{"id" => user_id, + "username" => "username", + "firstname" => "Bob", + "lastname" => "Lastname", + "email" => "email@example.com", + "created" => "2012-05-14T16:25:17-0500", + "state" => "enabled", + "account" => "accountname", + "accounttype" => 2, + "domainid" => domain_id, + "domain" => domain_name, + "apikey" => Fog::Cloudstack.uuid, + "secretkey" => Fog::Cloudstack.uuid}]}}, + :domains => { domain_id => domain }, + :servers => {} + } + end + end + + def self.reset + @data= nil + end + + def data + self.class.data end end - end - end -end + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/models/compute/address.rb b/lib/fog/cloudstack/models/compute/address.rb new file mode 100644 index 000000000..e3924cdf1 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/address.rb @@ -0,0 +1,17 @@ +module Fog + module Compute + class Cloudstack + class Address < Fog::Model + identity :id, :aliases => 'id' + attribute :network_id, :aliases => 'networkid' + attribute :ip_address, :aliases => 'ipaddress' + attribute :traffic_type, :aliases => 'traffictype' + attribute :is_default, :aliases => 'isdefault' + attribute :mac_address, :aliases => 'macaddress' + attribute :netmask + attribute :gateway + attribute :type + end + end + end +end diff --git a/lib/fog/cloudstack/models/compute/flavor.rb b/lib/fog/cloudstack/models/compute/flavor.rb new file mode 100644 index 000000000..f255ac0a6 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/flavor.rb @@ -0,0 +1,33 @@ +module Fog + module Compute + class Cloudstack + class Flavor < Fog::Model + identity :id, :aliases => 'id' + attribute :cpu_number, :aliases => 'cpunumber' + attribute :cpu_speed, :aliases => 'cpuspeed' + attribute :created + attribute :default_use, :aliases => 'defaultuse' + attribute :display_text, :aliases => 'display_text' + attribute :domain + attribute :host_tags, :aliases => 'host_tags' + attribute :is_system, :aliases => 'is_system' + attribute :limit_cpu_use, :aliases => 'limitcpuuse' + attribute :tags + attribute :system_vm_type, :aliases => 'systemvm' + attribute :storage_type, :aliases => 'storagetype' + attribute :offer_ha, :aliases => 'offerha' + attribute :network_rate, :aliases => 'networkrate' + attribute :name + attribute :memory + + def save + raise Fog::Errors::Error.new('Creating a flavor is not supported') + end + + def destroy + raise Fog::Errors::Error.new('Destroying a flavor is not supported') + end + end # Server + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/models/compute/flavors.rb b/lib/fog/cloudstack/models/compute/flavors.rb new file mode 100644 index 000000000..1125681f9 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/flavors.rb @@ -0,0 +1,28 @@ +require 'fog/core/collection' +require 'fog/cloudstack/models/compute/flavor' + +module Fog + module Compute + class Cloudstack + + class Flavors < Fog::Collection + + model Fog::Compute::Cloudstack::Flavor + + def all + data = connection.list_service_offerings["listserviceofferingsresponse"]["serviceoffering"] || [] + load(data) + end + + def get(flavor_id) + if flavor = connection.list_service_offerings('id' => flavor_id)["listserviceofferingsresponse"]["serviceoffering"].first + new(flavor) + end + rescue Fog::Compute::Cloudstack::BadRequest + nil + end + end + + end + end +end diff --git a/lib/fog/cloudstack/models/compute/image.rb b/lib/fog/cloudstack/models/compute/image.rb new file mode 100644 index 000000000..1033c32ca --- /dev/null +++ b/lib/fog/cloudstack/models/compute/image.rb @@ -0,0 +1,72 @@ +module Fog + module Compute + class Cloudstack + class Image < Fog::Model + identity :id, :aliases => 'id' + attribute :account + attribute :account_id, :aliases => 'accountid' + attribute :bootable + attribute :checksum + attribute :created + attribute :cross_zones, :aliases => 'crossZones' + attribute :details + attribute :display_text, :aliases => 'displaytext' + attribute :domain + attribute :domain_id, :aliases => 'domainid' + attribute :format + attribute :host_id, :aliases => 'hostid' + attribute :host_name, :aliases => 'hostname' + attribute :hypervisor + attribute :job_id, :aliases => 'jobid' + attribute :job_status, :aliases => 'jobstatus' + attribute :is_extractable, :aliases => 'isextractable' + attribute :is_featured, :aliases => 'isfeatured' + attribute :is_public, :aliases => 'ispublic' + attribute :is_ready, :aliases => 'isready' + attribute :name + attribute :os_type_id, :aliases => 'ostypeid' + attribute :os_type_name, :aliases => 'ostypename' + attribute :password_enabled, :aliases => 'ostypename' + attribute :project + attribute :project_id, :aliases => 'projectid' + attribute :removed + attribute :size + attribute :source_template_id, :aliases => 'sourcetemplateid' + attribute :status + attribute :template_tag, :aliases => 'templatetag' + attribute :template_type, :aliases => 'templatetype' + attribute :zone_id, :aliases => 'zoneid' + attribute :zone_name, :aliases => 'zonename' + + attr_accessor :bits, :requires_hvm, :snapshot_id, :url, :virtual_machine_id, :volume_id + + def save + options = { + 'displaytext' => display_text, + 'name' => name, + 'ostypeid' => os_type_id, + 'bits' => bits, + 'details' => details, + 'isfeatured' => is_featured, + 'ispublic' => is_public, + 'passwordenabled' => password_enabled, + 'requireshvm' => requires_hvm, + 'snapshotid' => snapshot_id, + 'templatetag' => template_tag, + 'url' => url, + 'virtualmachineid' => virtual_machine_id, + 'volumeid' => volume_id + } + data = connection.create_template(options) + merge_attributes(data['createtemplateresponse']) + end + + def destroy + requires :id + connection.delete_template('id' => self.id) + true + end + end # Server + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/models/compute/images.rb b/lib/fog/cloudstack/models/compute/images.rb new file mode 100644 index 000000000..363be52c2 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/images.rb @@ -0,0 +1,32 @@ +require 'fog/core/collection' +require 'fog/cloudstack/models/compute/image' + +module Fog + module Compute + class Cloudstack + + class Images < Fog::Collection + + model Fog::Compute::Cloudstack::Image + + def all(filters={}) + options = { + 'templatefilter' => 'self' + }.merge(filters) + + data = connection.list_templates(options)["listtemplatesresponse"]["template"] || [] + load(data) + end + + def get(template_id) + if template = connection.list_templates('id' => template_id)["listtemplatesresponse"]["template"].first + new(template) + end + rescue Fog::Compute::Cloudstack::BadRequest + nil + end + end + + end + end +end diff --git a/lib/fog/cloudstack/models/compute/server.rb b/lib/fog/cloudstack/models/compute/server.rb new file mode 100644 index 000000000..40d508dfd --- /dev/null +++ b/lib/fog/cloudstack/models/compute/server.rb @@ -0,0 +1,84 @@ +require 'fog/compute/models/server' + +module Fog + module Compute + class Cloudstack + class Server < Fog::Compute::Server + identity :id, :aliases => 'id' + attribute :name + attribute :account + attribute :domain + attribute :created + attribute :state + attribute :haenable + attribute :memory + attribute :display_name, :aliases => 'displayname' + attribute :domain_id, :aliases => 'domainid' + attribute :host_id, :aliases => 'hostid' + attribute :host_name, :aliases => 'hostname' + attribute :project_id, :aliases => 'projectid' + attribute :zone_id, :aliases => 'zoneid' + attribute :zone_name, :aliases => 'zonename' + attribute :image_id, :aliases => ['templateid', :template_id] + attribute :image_name, :aliases => ['templatename', :template_name] + attribute :templated_display_text, :aliases => 'templatedisplaytext' + attribute :password_enabled, :aliases => 'passwordenabled' + attribute :flavor_id, :aliases => ['serviceofferingid', :service_offering_id] + attribute :flavor_name, :aliases => ['serviceofferingname', :service_offering_name] + attribute :cpu_number, :aliases => 'cpunumber' + attribute :cpu_speed, :aliases => 'cpuspeed' + attribute :cpu_used, :aliases => 'cpuused' + attribute :network_kbs_read, :aliases => 'networkkbsread' + attribute :network_kbs_write, :aliases => 'networkkbswrite' + attribute :guest_os_id, :aliases => 'guestosid' + attribute :root_device_id, :aliases => 'rootdeviceid' + attribute :root_device_type, :aliases => 'rootdevicetype' + attribute :security_group, :aliases => 'securitygroup' + attribute :nics, :aliases => 'nic' + + attr_accessor :network_ids, :disk_offering_id, :ip_address, :ip_to_network_list + + def ready? + state == 'Running' + end + + def save + requires :image_id, :flavor_id, :zone_id + + options = { + 'templateid' => image_id, + 'serviceofferingid' => flavor_id, + 'zoneid' => zone_id, + 'networkids' => network_ids, + 'diskofferingid' => disk_offering_id, + 'displayname' => display_name, + 'domainid' => domain_id, + 'hostid' => host_id, + 'ipaddress' => ip_address, + 'iptonetworklist' => ip_to_network_list, + 'projectid' => project_id + } + + options.merge!('networkids' => network_ids) if network_ids + + data = connection.deploy_virtual_machine(options) + merge_attributes(data['deployvirtualmachineresponse']) + end + + def addresses + nics.map{|nic| Address.new(nic)} + end + + def flavor + connection.flavors.get(self.flavor_id) + end + + def destroy + requires :id + connection.destroy_virtual_machine(:id => id) + true + end + end # Server + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/models/compute/servers.rb b/lib/fog/cloudstack/models/compute/servers.rb new file mode 100644 index 000000000..ea403d8d4 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/servers.rb @@ -0,0 +1,34 @@ +require 'fog/core/collection' +require 'fog/cloudstack/models/compute/server' + +module Fog + module Compute + class Cloudstack + + class Servers < Fog::Collection + + model Fog::Compute::Cloudstack::Server + + def all + data = connection.list_virtual_machines["listvirtualmachinesresponse"]["virtualmachine"] || [] + load(data) + end + + def bootstrap(new_attributes = {}) + server = create(new_attributes) + server.wait_for { ready? } + server + end + + def get(server_id) + if server = connection.list_virtual_machines('id' => server_id)["listvirtualmachinesresponse"]["virtualmachine"].first + new(server) + end + rescue Fog::Compute::Cloudstack::BadRequest + nil + end + end + + end + end +end diff --git a/lib/fog/cloudstack/models/compute/zone.rb b/lib/fog/cloudstack/models/compute/zone.rb new file mode 100644 index 000000000..8676a01f1 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/zone.rb @@ -0,0 +1,39 @@ +module Fog + module Compute + class Cloudstack + class Zone < Fog::Model + identity :id, :aliases => 'id' + attribute :name + attribute :domain_id, :aliases => 'domainid' + attribute :domain_name, :aliases => ['domainname', 'domain'] + attribute :network_type, :aliases => 'networktype' + attribute :security_groups_enabled, :aliases => ['securitygroupsenabled', 'securitygroupenabled'] + attribute :allocation_state, :aliases => 'allocationstate' + attribute :zone_token, :aliases => 'zonetoken' + attribute :dhcp_provider, :aliases => 'dhcpprovider' + + attr_accessor :dns1, :dns2, :internaldns1, :internaldns2, :guest_cidr_address + + def save + options = { + 'dns1' => dns1, + 'internaldns1' => internaldns1, + 'name' => name, + 'networktype' => network_type, + 'allocationstate' => allocation_state, + 'dns2' => dns2, + 'domain' => domain_name, + 'domainid' => domain_id, + 'guestcidraddress' => guest_cidr_address, + 'internaldns2' => internaldns2, + 'securitygroupenabled' => security_group_enabled, + } + data = connection.create_zone(options) + merge_attributes(data['createzoneresponse']) + end + + end # Zone + end # Cloudstack + end # Compute +end # Fog + diff --git a/lib/fog/cloudstack/models/compute/zones.rb b/lib/fog/cloudstack/models/compute/zones.rb new file mode 100644 index 000000000..38b46e1b6 --- /dev/null +++ b/lib/fog/cloudstack/models/compute/zones.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/cloudstack/models/compute/zone' + +module Fog + module Compute + class Cloudstack + + class Zones < Fog::Collection + + model Fog::Compute::Cloudstack::Zone + + def all(filters={}) + options = { + 'templatefilter' => 'self' + }.merge(filters) + + data = connection.list_zones(options)["listzonesresponse"]["zone"] || [] + load(data) + end + + def get(zone_id) + if zone = connection.list_zones('id' => zone_id)["listzonesresponse"]["zone"].first + new(zone) + end + rescue Fog::Compute::Cloudstack::BadRequest + nil + end + end + + end + end +end + diff --git a/lib/fog/cloudstack/requests/compute/delete_template.rb b/lib/fog/cloudstack/requests/compute/delete_template.rb new file mode 100644 index 000000000..6e6c1125a --- /dev/null +++ b/lib/fog/cloudstack/requests/compute/delete_template.rb @@ -0,0 +1,20 @@ +module Fog + module Compute + class Cloudstack + class Real + + # Deletes a specified template. + # + # {CloudStack API Reference}[http://http://download.cloud.com/releases/3.0.0/api_3.0.0/user/deleteTemplate.html] + def delete_template(options={}) + options.merge!( + 'command' => 'deleteTemplate' + ) + + 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 49c424f34..52aeb8574 100644 --- a/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb +++ b/lib/fog/cloudstack/requests/compute/deploy_virtual_machine.rb @@ -11,23 +11,119 @@ module Fog 'command' => 'deployVirtualMachine' ) - if ( securitygroupids = options.delete('securitygroupids') ).is_a?(Array) - options.merge!('securitygroupids' => securitygroupids.join(',')) + if security_group_ids = options.delete('securitygroupids') + options.merge!('securitygroupids' => Array(security_group_ids).join(',')) end - - if ( securitygroupnames = options.delete('securitygroupnames') ).is_a?(Array) - options.merge!('securitygroupnames' => securitygroupnames.join(',')) + + if security_group_names = options.delete('securitygroupnames') + options.merge!('securitygroupnames' => Array(security_group_names).join(',')) end - - if ( networkids = options.delete('networkids') ).is_a?(Array) - options.merge!('networkids' => networkids.join(',')) + + if network_ids = options.delete('networkids') + options.merge!('networkids' => Array(network_ids).join(',')) end - - + request(options) end + end # Real - end - end - end -end + class Mock + + def deploy_virtual_machine(options={}) + zone_id = options['zoneid'] + unless zone_id + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command deployvirtualmachine due to missing parameter zoneid') + end + unless zone = self.data[:zones][zone_id] + raise Fog::Compute::Cloudstack::BadRequest.new("Unable to execute API command deployvirtualmachine due to invalid value. Object zone(uuid: #{zone_id}) does not exist.") + end + zone_name = zone[:name] + + template_id = options['templateid'] + unless template = self.data[:images][template_id] + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command deployvirtualmachine due to missing parameter templateid') + end + template_name = template[:name] + template_display_text = template[:display_text] + + service_offering_id = options['serviceofferingid'] + unless service_offering = self.data[:flavors][service_offering_id] + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command deployvirtualmachine due to missing parameter serviceofferingid') + end + + service_offering_name = service_offering[:name] + service_offering_cpu_number = service_offering[:cpunumber] + service_offering_cpu_speed = service_offering[:cpuspeed] + service_offering_memory = service_offering[:cpumemory] + + identity = Fog::Cloudstack.uuid + name = options['name'] || Fog::Cloudstack.uuid + display_name = options['displayname'] || name + account_name = options['account'] || self.data[:accounts].first[1]["name"] + + domain = options['domainid'] ? self.data[:domains][options['domainid']] : self.data[:domains].first[1] + domain_id = domain[:id] + domain_name = domain[:name] + + # how is this setup + password = nil + password_enabled = false + + guest_os_id = Fog::Cloudstack.uuid + + security_group_ids = options['securitygroupids'] || [] # TODO: for now + + network_ids = Array(options['networkids']) || [self.data[:networks].first[1]["id"]] + networks = network_ids.map{|nid| self.data[:networks][nid]} + nic = networks.map do |network| + { + "id" => Fog::Cloudstack.uuid, + "networkid" => network["id"], + "netmask" => Fog::Cloudstack.ip_address, + "gateway" => network["gateway"], + "ipaddress" => Fog::Cloudstack.ip_address, + "traffictype" => "Guest", # TODO: ? + "type" => network["type"], + "isdefault" => true, # TODO: ? + "macaddress" => Fog::Cloudstack.mac_address + } + end + + virtual_machine = { + "id" => identity, + "name" => name, + "displayname" => display_name, + "account" => account_name, + "domainid" => domain_id, + "domain" => domain_name, + "created" => Time.now.to_s, + "state" => "Running", + "haenable" => false, + "zoneid" => zone_id, + "zonename" => zone_name, + "templateid" => template_id, + "templatename" => template_name, + "templatedisplaytext" => template_display_text, + "passwordenabled" => false, + "serviceofferingid" => service_offering_id, + "serviceofferingname" => service_offering_name, + "cpunumber" => service_offering_cpu_number, + "cpuspeed" => service_offering_cpu_speed, + "memory" => service_offering_memory, + "cpuused" => "0%", + "networkkbsread" => 0, + "networkkbswrite" => 0, + "guestosid" => guest_os_id, + "rootdeviceid" => 0, + "rootdevicetype" => "NetworkFilesystem", + "securitygroup" => security_group_ids, # TODO: mayhaps? + "nic" => nic + } + + self.data[:servers][identity]= virtual_machine + {'deployvirtualmachineresponse' => virtual_machine} + end + end # Mock + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/requests/compute/destroy_virtual_machine.rb b/lib/fog/cloudstack/requests/compute/destroy_virtual_machine.rb index 747782e1e..cbe2895ea 100644 --- a/lib/fog/cloudstack/requests/compute/destroy_virtual_machine.rb +++ b/lib/fog/cloudstack/requests/compute/destroy_virtual_machine.rb @@ -14,7 +14,16 @@ module Fog request(options) end + end # Real + + class Mock + + def destroy_virtual_machine(options={}) + identity = options[:id] + + self.data[:servers].delete(identity) + end end - end - end -end + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/requests/compute/list_capabilities.rb b/lib/fog/cloudstack/requests/compute/list_capabilities.rb index 2b56549d7..047fd776a 100644 --- a/lib/fog/cloudstack/requests/compute/list_capabilities.rb +++ b/lib/fog/cloudstack/requests/compute/list_capabilities.rb @@ -10,7 +10,7 @@ module Fog options.merge!( 'command' => 'listCapabilities' ) - + request(options) end diff --git a/lib/fog/cloudstack/requests/compute/list_service_offerings.rb b/lib/fog/cloudstack/requests/compute/list_service_offerings.rb index c7324c434..691caa4c2 100644 --- a/lib/fog/cloudstack/requests/compute/list_service_offerings.rb +++ b/lib/fog/cloudstack/requests/compute/list_service_offerings.rb @@ -10,11 +10,26 @@ module Fog options.merge!( 'command' => 'listServiceOfferings' ) - + request(options) end - end - end - end -end + end # Real + + class Mock + + def list_service_offerings(options={}) + flavors = self.data[:flavors].values + + { + "listserviceofferingsresponse" => + { + "count" => flavors.size, + "serviceoffering"=> flavors + } + } + end + end # Mock + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/requests/compute/list_templates.rb b/lib/fog/cloudstack/requests/compute/list_templates.rb index 1b230e0ef..09020f51e 100644 --- a/lib/fog/cloudstack/requests/compute/list_templates.rb +++ b/lib/fog/cloudstack/requests/compute/list_templates.rb @@ -10,11 +10,26 @@ module Fog options.merge!( 'command' => 'listTemplates' ) - + request(options) end - end - end - end -end + end # Real + + class Mock + + def list_templates(options={}) + templates = self.data[:images].values + + { + "listtemplatesresponse" => + { + "count" => templates.size, + "template"=> templates + } + } + end + end # Mock + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/requests/compute/list_virtual_machines.rb b/lib/fog/cloudstack/requests/compute/list_virtual_machines.rb index 2150619dc..7371a943f 100644 --- a/lib/fog/cloudstack/requests/compute/list_virtual_machines.rb +++ b/lib/fog/cloudstack/requests/compute/list_virtual_machines.rb @@ -10,11 +10,17 @@ module Fog options.merge!( 'command' => 'listVirtualMachines' ) - + request(options) end + end # Real - end - end - end -end + class Mock + def list_virtual_machines(options={}) + {"listvirtualmachinesresponse" => + {"count" => self.data[:servers].values.size, "virtualmachine" => self.data[:servers].values}} + end + end # Mock + end # Cloudstack + end # Compute +end # Fog diff --git a/lib/fog/cloudstack/requests/compute/list_zones.rb b/lib/fog/cloudstack/requests/compute/list_zones.rb index 639f1969c..e1b140c43 100644 --- a/lib/fog/cloudstack/requests/compute/list_zones.rb +++ b/lib/fog/cloudstack/requests/compute/list_zones.rb @@ -14,7 +14,21 @@ module Fog request(options) end - end - end - end -end + end # Real + + class Mock + def list_zones(options={}) + zones = self.data[:zones].values + + { + "listzonesresponse"=> + { + "count" => zones.size, + "zone" => zones + } + } + end + end # Mock + end # Cloudstack + end # Compute +end # Fog diff --git a/tests/cloudstack/requests/virtual_machine_tests.rb b/tests/cloudstack/requests/virtual_machine_tests.rb index fa209e96b..9cc745579 100644 --- a/tests/cloudstack/requests/virtual_machine_tests.rb +++ b/tests/cloudstack/requests/virtual_machine_tests.rb @@ -4,35 +4,35 @@ Shindo.tests('Fog::Compute[:cloudstack] | virtual machine requests', ['cloudstac 'listvirtualmachinesresponse' => { 'count' => Integer, 'virtualmachine' => [ - 'id' => Integer, + 'id' => String, 'name' => String, 'displayname' => String, 'account' => String, - 'domainid' => Integer, + 'domainid' => String, 'domain' => String, 'created' => String, 'state' => String, 'haenable' => Fog::Boolean, - 'zoneid' => Integer, + 'zoneid' => String, 'zonename' => String, 'hostid' => Fog::Nullable::String, 'hostname' => Fog::Nullable::String, - 'templateid' => Integer, + 'templateid' => String, 'templatename' => String, 'templatedisplaytext' => String, 'passwordenabled' => Fog::Boolean, - 'serviceofferingid' => Integer, + 'serviceofferingid' => String, 'serviceofferingname' => String, 'cpunumber' => Integer, 'cpuspeed' => Integer, 'networkkbsread' => Fog::Nullable::Integer, 'memory' => Integer, 'cpuused' => Fog::Nullable::String, - 'guestosid' => Integer, + 'guestosid' => String, 'networkkbswrite' => Fog::Nullable::Integer, 'rootdeviceid' => Integer, 'rootdevicetype' => String, - 'hypervisor' => String, + 'hypervisor' => Fog::Nullable::String, 'group' => Fog::Nullable::String, 'groupid' => Fog::Nullable::Integer, 'isoname' => Fog::Nullable::String, @@ -43,8 +43,8 @@ Shindo.tests('Fog::Compute[:cloudstack] | virtual machine requests', ['cloudstac 'description' => Fog::Nullable::String ], 'nic' => [ - 'id' => Integer, - 'networkid' => Integer, + 'id' => String, + 'networkid' => String, 'netmask' => String, 'gateway' => String, 'ipaddress' => String, @@ -68,4 +68,4 @@ Shindo.tests('Fog::Compute[:cloudstack] | virtual machine requests', ['cloudstac end -end \ No newline at end of file +end diff --git a/tests/compute/helper.rb b/tests/compute/helper.rb index 6d4e6f34f..d651af13d 100644 --- a/tests/compute/helper.rb +++ b/tests/compute/helper.rb @@ -19,6 +19,14 @@ def compute_providers }, :mocked => false }, + :openstack => { :mocked => true}, + :cloudstack => { + :provider_attributes => { + :cloudstack_host => 'http://host.foo' + }, + :server_attributes => Fog.credentials[:cloudstack], + :mocked => true + }, :glesys => { :mocked => false }, diff --git a/tests/compute/models/servers_tests.rb b/tests/compute/models/servers_tests.rb index ccb78b93b..a11db78d5 100644 --- a/tests/compute/models/servers_tests.rb +++ b/tests/compute/models/servers_tests.rb @@ -1,3 +1,4 @@ + for provider, config in compute_providers Shindo.tests("Fog::Compute[:#{provider}] | servers", [provider.to_s]) do diff --git a/tests/helpers/mock_helper.rb b/tests/helpers/mock_helper.rb index 02ff590b3..f4d9d4d34 100644 --- a/tests/helpers/mock_helper.rb +++ b/tests/helpers/mock_helper.rb @@ -15,6 +15,7 @@ if Fog.mock? :bluebox_customer_id => 'bluebox_customer_id', :brightbox_client_id => 'brightbox_client_id', :brightbox_secret => 'brightbox_secret', + :cloudstack_host => 'http://cloudstack.example.org', :clodo_api_key => 'clodo_api_key', :clodo_username => 'clodo_username', :dnsimple_email => 'dnsimple_email', @@ -77,5 +78,5 @@ if Fog.mock? :libvirt_uri => 'qemu:///system', :libvirt_username => 'root', :libvirt_password => 'password' - } + }.merge(Fog.credentials) end