Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[cloudstack|compute] volumes support

  • Loading branch information...
commit 54f1c921840af9eb6cbf3337fcc12057594ba1dd 1 parent 4859206
Josh Lane lanej authored
273 lib/fog/cloudstack/compute.rb
@@ -19,14 +19,16 @@ class Unauthorized < Fog::Compute::Cloudstack::Error; end
19 19
20 20 model_path 'fog/cloudstack/models/compute'
21 21 model :address
  22 + model :flavor
  23 + collection :flavors
22 24 model :job
23 25 collection :jobs
24 26 model :server
25 27 collection :servers
26 28 model :image
27 29 collection :images
28   - model :flavor
29   - collection :flavors
  30 + model :volume
  31 + collection :volumes
30 32 model :zone
31 33 collection :zones
32 34
@@ -180,21 +182,21 @@ def login(username,password,domain)
180 182
181 183 # Decode the login response
182 184 response = Fog::JSON.decode(response.body)
183   -
  185 +
184 186 user = response['loginresponse']
185 187 user.merge!('sessionid' => sessionid)
186   -
  188 +
187 189 @cloudstack_session_id = user['sessionid']
188 190 @cloudstack_session_key = user['sessionkey']
189   -
  191 +
190 192 user
191 193 end
192   -
  194 +
193 195 def request(params)
194 196 params.reject!{|k,v| v.nil?}
195   -
  197 +
196 198 params.merge!('response' => 'json')
197   -
  199 +
198 200 if has_session?
199 201 params, headers = authorize_session(params)
200 202 elsif has_keys?
@@ -210,20 +212,20 @@ def request(params)
210 212 def has_session?
211 213 @cloudstack_session_id && @cloudstack_session_key
212 214 end
213   -
  215 +
214 216 def has_keys?
215 217 @cloudstack_api_key && @cloudstack_secret_access_key
216 218 end
217   -
  219 +
218 220 def authorize_session(params)
219 221 # set the session id cookie for the request
220 222 headers = {'Cookie' => "JSESSIONID=#{@cloudstack_session_id};"}
221 223 # set the sesion key for the request, params are not signed using session auth
222 224 params.merge!('sessionkey' => @cloudstack_session_key)
223   -
  225 +
224 226 return params, headers
225 227 end
226   -
  228 +
227 229 def authorize_api_keys(params)
228 230 headers = {}
229 231 # merge the api key into the params
@@ -232,10 +234,10 @@ def authorize_api_keys(params)
232 234 signature = Fog::Cloudstack.signed_params(@cloudstack_secret_access_key,params)
233 235 # merge signature into request param
234 236 params.merge!({'signature' => signature})
235   -
  237 +
236 238 return params, headers
237 239 end
238   -
  240 +
239 241 def issue_request(params={},headers={},method='GET',expects=200)
240 242 begin
241 243 response = @connection.request({
@@ -244,13 +246,13 @@ def issue_request(params={},headers={},method='GET',expects=200)
244 246 :method => method,
245 247 :expects => expects
246 248 })
247   -
  249 +
248 250 rescue Excon::Errors::HTTPStatusError => error
249 251 error_response = Fog::JSON.decode(error.response.body)
250   -
  252 +
251 253 error_code = error_response.values.first['errorcode']
252 254 error_text = error_response.values.first['errortext']
253   -
  255 +
254 256 case error_code
255 257 when 401
256 258 raise Fog::Compute::Cloudstack::Unauthorized, error_text
@@ -285,140 +287,151 @@ def self.data
285 287 "haschild" => false,
286 288 "path" => "ROOT/accountname"
287 289 }
  290 + user = {
  291 + "id" => user_id,
  292 + "username" => "username",
  293 + "firstname" => "Bob",
  294 + "lastname" => "Lastname",
  295 + "email" => "email@example.com",
  296 + "created" => "2012-05-14T16:25:17-0500",
  297 + "state" => "enabled",
  298 + "account" => "accountname",
  299 + "accounttype" => 2,
  300 + "domainid" => domain_id,
  301 + "domain" => domain_name,
  302 + "apikey" => Fog::Cloudstack.uuid,
  303 + "secretkey" => Fog::Cloudstack.uuid
  304 + }
288 305 {
  306 + :users => { user_id => user },
289 307 :networks => { network_id => {
290   - "id" => network_id,
291   - "name" => "10.56.23.0/26",
292   - "displaytext" => "10.56.23.0/26",
293   - "broadcastdomaintype" => "Vlan",
294   - "traffictype" => "Guest",
295   - "gateway" => "10.56.23.1",
296   - "netmask" => "255.255.255.192",
297   - "cidr" => "10.56.23.0/26",
298   - "zoneid" => zone_id,
299   - "zonename" => "zone-00",
300   - "networkofferingid" => "af0c9bd5-a1b2-4ad0-bf4b-d6fa9b1b9d5b",
301   - "networkofferingname" => "DefaultSharedNetworkOffering",
302   - "networkofferingdisplaytext" => "Offering for Shared networks",
  308 + "id" => network_id,
  309 + "name" => "10.56.23.0/26",
  310 + "displaytext" => "10.56.23.0/26",
  311 + "broadcastdomaintype" => "Vlan",
  312 + "traffictype" => "Guest",
  313 + "gateway" => "10.56.23.1",
  314 + "netmask" => "255.255.255.192",
  315 + "cidr" => "10.56.23.0/26",
  316 + "zoneid" => zone_id,
  317 + "zonename" => "zone-00",
  318 + "networkofferingid" => "af0c9bd5-a1b2-4ad0-bf4b-d6fa9b1b9d5b",
  319 + "networkofferingname" => "DefaultSharedNetworkOffering",
  320 + "networkofferingdisplaytext" => "Offering for Shared networks",
303 321 "networkofferingavailability" => "Optional",
304   - "issystem" => false,
305   - "state" => "Setup",
306   - "related" => "86bbc9fc-d92e-49db-9fdc-296189090017",
307   - "broadcasturi" => "vlan://800",
308   - "dns1" => "10.0.80.11",
309   - "type" => "Shared",
310   - "vlan" => "800",
311   - "acltype" => "Domain",
312   - "subdomainaccess" => true,
313   - "domainid" => domain_id,
314   - "domain" => "ROOT",
  322 + "issystem" => false,
  323 + "state" => "Setup",
  324 + "related" => "86bbc9fc-d92e-49db-9fdc-296189090017",
  325 + "broadcasturi" => "vlan://800",
  326 + "dns1" => "10.0.80.11",
  327 + "type" => "Shared",
  328 + "vlan" => "800",
  329 + "acltype" => "Domain",
  330 + "subdomainaccess" => true,
  331 + "domainid" => domain_id,
  332 + "domain" => "ROOT",
315 333 "service" => [
316 334 {"name" => "UserData"},
317 335 {"name" => "Dhcp"},
318   - {"name" => "Dns", "capability" => [
319   - {"name" => "AllowDnsSuffixModification",
320   - "value" => "true",
321   - "canchooseservicecapability" => false}]
  336 + {
  337 + "name" => "Dns",
  338 + "capability" => [
  339 + {
  340 + "name" => "AllowDnsSuffixModification",
  341 + "value" => "true",
  342 + "canchooseservicecapability" => false
  343 + }
  344 + ]
322 345 }],
323   - "networkdomain" => "cs1cloud.internal",
  346 + "networkdomain" => "cs1cloud.internal",
324 347 "physicalnetworkid" => "8f4627c5-1fdd-4504-8a92-f61b4e9cb3e3",
325   - "restartrequired" => false,
326   - "specifyipranges" => true}
  348 + "restartrequired" => false,
  349 + "specifyipranges" => true}
327 350 },
328 351 :zones => { zone_id => {
329   - "id" => zone_id,
330   - "name"=> "zone-00",
331   - "domainid" => 1,
332   - "domainname" => "ROOT",
333   - "networktype" => "Advanced",
  352 + "id" => zone_id,
  353 + "name" => "zone-00",
  354 + "domainid" => 1,
  355 + "domainname" => "ROOT",
  356 + "networktype" => "Advanced",
334 357 "securitygroupsenabled" => false,
335   - "allocationstate" => "Enabled",
336   - "zonetoken" => Fog::Cloudstack.uuid,
337   - "dhcpprovider" => "VirtualRouter"}},
  358 + "allocationstate" => "Enabled",
  359 + "zonetoken" => Fog::Cloudstack.uuid,
  360 + "dhcpprovider" => "VirtualRouter"}},
338 361 :images => { image_id => {
339   - "id" => image_id,
340   - "name" => "CentOS 5.6(64-bit) no GUI (XenServer)",
341   - "displaytext" => "CentOS 5.6(64-bit) no GUI (XenServer)",
342   - "ispublic" => true,
343   - "created" => "2012-05-09T15:29:33-0500",
344   - "isready" => true,
  362 + "id" => image_id,
  363 + "name" => "CentOS 5.6(64-bit) no GUI (XenServer)",
  364 + "displaytext" => "CentOS 5.6(64-bit) no GUI (XenServer)",
  365 + "ispublic" => true,
  366 + "created" => "2012-05-09T15:29:33-0500",
  367 + "isready" => true,
345 368 "passwordenabled" => false,
346   - "format" => "VHD",
347   - "isfeatured" => true,
348   - "crossZones" => true,
349   - "ostypeid" => "a6a6694a-18f5-4765-8418-2b7a5f37cd0f",
350   - "ostypename" => "CentOS 5.3 (64-bit)",
351   - "account" => "system",
352   - "zoneid" => zone_id,
353   - "zonename" => "zone-00",
354   - "status" => "Download Complete",
355   - "size" => 21474836480,
356   - "templatetype" => "BUILTIN",
357   - "domain" => "ROOT",
358   - "domainid" => "6023b6fe-5bef-4358-bc76-9f4e75afa52f",
359   - "isextractable" => true,
360   - "checksum" => "905cec879afd9c9d22ecc8036131a180"}},
  369 + "format" => "VHD",
  370 + "isfeatured" => true,
  371 + "crossZones" => true,
  372 + "ostypeid" => "a6a6694a-18f5-4765-8418-2b7a5f37cd0f",
  373 + "ostypename" => "CentOS 5.3 (64-bit)",
  374 + "account" => "system",
  375 + "zoneid" => zone_id,
  376 + "zonename" => "zone-00",
  377 + "status" => "Download Complete",
  378 + "size" => 21474836480,
  379 + "templatetype" => "BUILTIN",
  380 + "domain" => "ROOT",
  381 + "domainid" => "6023b6fe-5bef-4358-bc76-9f4e75afa52f",
  382 + "isextractable" => true,
  383 + "checksum" => "905cec879afd9c9d22ecc8036131a180"}},
361 384 :flavors => { flavor_id => {
362   - "id" => flavor_id,
363   - "name" => "Medium Instance",
  385 + "id" => flavor_id,
  386 + "name" => "Medium Instance",
364 387 "displaytext" => "Medium Instance",
365   - "cpunumber" => 1,
366   - "cpuspeed" => 1000,
367   - "memory" => 1024,
368   - "created" => "2012-05-09T14:48:36-0500",
  388 + "cpunumber" => 1,
  389 + "cpuspeed" => 1000,
  390 + "memory" => 1024,
  391 + "created" => "2012-05-09T14:48:36-0500",
369 392 "storagetype" => "shared",
370   - "offerha" => false,
  393 + "offerha" => false,
371 394 "limitcpuuse" => false,
372   - "issystem" => false,
373   - "defaultuse" => false}},
  395 + "issystem" => false,
  396 + "defaultuse" => false}},
374 397 :accounts => { account_id => {
375   - "id" => account_id,
376   - "name" => "accountname",
377   - "accounttype" => 2,
378   - "domainid" => domain_id,
379   - "domain" => domain_name,
380   - "receivedbytes" => 0,
381   - "sentbytes" => 0,
382   - "vmlimit" => "Unlimited",
383   - "vmtotal" => 0,
384   - "vmavailable" => "Unlimited",
385   - "iplimit" => "Unlimited",
386   - "iptotal" => 0,
387   - "ipavailable" => "Unlimited",
388   - "volumelimit" => "Unlimited",
389   - "volumetotal" => 0,
390   - "volumeavailable" => "Unlimited",
391   - "snapshotlimit" => "Unlimited",
392   - "snapshottotal" => 0,
  398 + "id" => account_id,
  399 + "name" => "accountname",
  400 + "accounttype" => 2,
  401 + "domainid" => domain_id,
  402 + "domain" => domain_name,
  403 + "receivedbytes" => 0,
  404 + "sentbytes" => 0,
  405 + "vmlimit" => "Unlimited",
  406 + "vmtotal" => 0,
  407 + "vmavailable" => "Unlimited",
  408 + "iplimit" => "Unlimited",
  409 + "iptotal" => 0,
  410 + "ipavailable" => "Unlimited",
  411 + "volumelimit" => "Unlimited",
  412 + "volumetotal" => 0,
  413 + "volumeavailable" => "Unlimited",
  414 + "snapshotlimit" => "Unlimited",
  415 + "snapshottotal" => 0,
393 416 "snapshotavailable" => "Unlimited",
394   - "templatelimit" => "Unlimited",
395   - "templatetotal" => 0,
  417 + "templatelimit" => "Unlimited",
  418 + "templatetotal" => 0,
396 419 "templateavailable" => "Unlimited",
397   - "vmstopped" => 0,
398   - "vmrunning" => 0,
399   - "projectlimit" => "Unlimited",
400   - "projecttotal" => 1,
401   - "projectavailable" => "Unlimited",
402   - "networklimit" => "Unlimited",
403   - "networktotal" => 0,
404   - "networkavailable" => "Unlimited",
405   - "state" => "enabled",
406   - "user" =>
407   - [{"id" => user_id,
408   - "username" => "username",
409   - "firstname" => "Bob",
410   - "lastname" => "Lastname",
411   - "email" => "email@example.com",
412   - "created" => "2012-05-14T16:25:17-0500",
413   - "state" => "enabled",
414   - "account" => "accountname",
415   - "accounttype" => 2,
416   - "domainid" => domain_id,
417   - "domain" => domain_name,
418   - "apikey" => Fog::Cloudstack.uuid,
419   - "secretkey" => Fog::Cloudstack.uuid}]}},
  420 + "vmstopped" => 0,
  421 + "vmrunning" => 0,
  422 + "projectlimit" => "Unlimited",
  423 + "projecttotal" => 1,
  424 + "projectavailable" => "Unlimited",
  425 + "networklimit" => "Unlimited",
  426 + "networktotal" => 0,
  427 + "networkavailable" => "Unlimited",
  428 + "state" => "enabled",
  429 + "user" => [user]}
  430 + },
420 431 :domains => { domain_id => domain },
421   - :servers => {}
  432 + :servers => {},
  433 + :jobs => {},
  434 + :volumes => {}
422 435 }
423 436 end
424 437 end
12 lib/fog/cloudstack/models/compute/job.rb
@@ -7,18 +7,22 @@ class Job < Fog::Model
7 7 attribute :user_id, :aliases => 'userid'
8 8 attribute :account_id, :aliases => 'accountid'
9 9 attribute :cmd
10   - attribute :job_status, :aliases => 'jobstatus'
  10 + attribute :job_status, :aliases => 'jobstatus', :type => :integer
11 11 attribute :job_result_type, :aliases => 'jobresulttype'
12   - attribute :job_result_code, :aliases => 'jobresultcode'
13   - attribute :job_proc_status, :aliases => 'jobprocstatus'
  12 + attribute :job_result_code, :aliases => 'jobresultcode', :type => :integer
  13 + attribute :job_proc_status, :aliases => 'jobprocstatus', :type => :integer
14 14
15   - attribute :created_at, :aliases => 'created', :type => :time
  15 + attribute :created_at, :aliases => 'created', :type => :time
16 16 attribute :job_result, :aliases => 'jobresult'
17 17
18 18 def reload
19 19 requires :id
20 20 merge_attributes(connection.query_async_job_result('jobid' => self.id)['queryasyncjobresultresponse'])
21 21 end
  22 +
  23 + def finished?
  24 + self.job_status != 0
  25 + end
22 26 end # Job
23 27 end # Cloudstack
24 28 end # Compute
119 lib/fog/cloudstack/models/compute/volume.rb
... ... @@ -0,0 +1,119 @@
  1 +module Fog
  2 + module Compute
  3 + class Cloudstack
  4 + class Volume < Fog::Model
  5 + identity :id, :aliases => 'id'
  6 +
  7 + attribute :name, :aliases => 'name'
  8 + attribute :zone_id, :aliases => 'zoneid'
  9 + attribute :zone_name, :aliases => 'zonename'
  10 + attribute :type, :aliases => 'type'
  11 + attribute :size, :aliases => 'size'
  12 + attribute :created, :aliases => 'created'
  13 + attribute :state, :aliases => 'state'
  14 + attribute :account, :aliases => 'account'
  15 + attribute :domain_id, :aliases => 'domainid'
  16 + attribute :domain, :aliases => 'domain'
  17 + attribute :storage_type, :aliases => 'storagetype'
  18 + attribute :hypervisor, :aliases => 'hypervisor'
  19 + attribute :disk_offering_id, :aliases => 'diskofferingid'
  20 + attribute :disk_offering_name, :aliases => 'diskofferingname'
  21 + attribute :disk_offering_display_text, :aliases => 'diskofferingdisplaytext'
  22 + attribute :storage, :aliases => 'storage'
  23 + attribute :destroyed, :aliases => 'destroyed'
  24 + attribute :is_extractable, :aliases => 'isextractable', :type => :boolean
  25 + attribute :server_id, :aliases => 'virtualmachineid'
  26 + attribute :server_name, :aliases => 'vmname'
  27 + attribute :server_display_name, :aliases => 'vmdisplayname'
  28 +
  29 + attr_accessor :snapshot_id, :project_id
  30 +
  31 + def save
  32 + requires :name, :disk_offering_id, :zone_id
  33 +
  34 + options = {
  35 + 'size' => size,
  36 + 'name' => name,
  37 + 'diskofferingid' => disk_offering_id,
  38 + 'zoneid' => zone_id,
  39 + 'snapshotid' => snapshot_id,
  40 + 'projectid' => project_id
  41 + }
  42 + data = connection.create_volume(options)
  43 + merge_attributes(data['createvolumeresponse'])
  44 + end
  45 +
  46 + def ready?
  47 + state == 'Allocated' || state == 'Ready'
  48 + end
  49 +
  50 + def flavor
  51 + connection.disk_offerings.get(self.disk_offering_id)
  52 + end
  53 + alias disk_offering flavor
  54 +
  55 + def server
  56 + if server_id
  57 + connection.servers.get(server_id)
  58 + end
  59 + end
  60 +
  61 + def reload
  62 + requires :identity
  63 +
  64 + return unless data = begin
  65 + collection.get(identity)
  66 + rescue Excon::Errors::SocketError
  67 + nil
  68 + end
  69 +
  70 + new_attributes = {
  71 + 'virtualmachineid' => nil,
  72 + 'vmname' => nil,
  73 + 'vmdisplayname' => nil
  74 + }.merge(data.attributes)
  75 +
  76 + merge_attributes(new_attributes)
  77 + self
  78 + end
  79 +
  80 +
  81 + def attach(instance_or_id, mountpoint=nil)
  82 + requires :id
  83 + instance_id = instance_or_id.is_a?(Server) ? instance_or_id.id : instance_or_id
  84 + unless instance_id
  85 + raise ArgumentError, "Missing required argument: instance_or_id"
  86 + end
  87 +
  88 + options = {
  89 + 'id' => id,
  90 + 'virtualmachineid' => instance_id,
  91 + }
  92 + options.merge!('deviceid' => mountpoint) if mountpoint
  93 +
  94 + data = connection.attach_volume(options)
  95 +
  96 + Job.new(data["attachvolumeresponse"]).tap do |job|
  97 + job.connection= self.connection
  98 + end
  99 + end
  100 +
  101 + def detach
  102 + requires :id
  103 +
  104 + data = connection.detach_volume('id' => id)
  105 +
  106 + Job.new(data["detachvolumeresponse"]).tap do |job|
  107 + job.connection= self.connection
  108 + end
  109 + end
  110 +
  111 + def destroy
  112 + requires :id
  113 + connection.delete_volume('id' => id)
  114 + true
  115 + end
  116 + end # Volume
  117 + end # Cloudstack
  118 + end # Compute
  119 +end # Fog
28 lib/fog/cloudstack/models/compute/volumes.rb
... ... @@ -0,0 +1,28 @@
  1 +require 'fog/core/collection'
  2 +require 'fog/cloudstack/models/compute/volume'
  3 +
  4 +module Fog
  5 + module Compute
  6 + class Cloudstack
  7 +
  8 + class Volumes < Fog::Collection
  9 +
  10 + model Fog::Compute::Cloudstack::Volume
  11 +
  12 + def all
  13 + data = connection.list_volumes["listvolumesresponse"]["volume"] || []
  14 + load(data)
  15 + end
  16 +
  17 + def get(volume_id)
  18 + if volume = connection.list_volumes('id' => volume_id)["listvolumesresponse"]["volume"].first
  19 + new(volume)
  20 + end
  21 + rescue Fog::Compute::Cloudstack::BadRequest
  22 + nil
  23 + end
  24 + end
  25 +
  26 + end
  27 + end
  28 +end
67 lib/fog/cloudstack/requests/compute/attach_volume.rb
@@ -6,18 +6,65 @@ class Real
6 6 # Attaches a disk volume to a virtual machine.
7 7 #
8 8 # {CloudStack API Reference}[http://http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/attachVolume.html]
9   - def attach_volume(id,virtualmachineid,deviceid=nil)
10   - options = {
  9 + def attach_volume(options={})
  10 + options.merge!(
11 11 'command' => 'attachVolume',
12   - 'id' => id,
13   - 'virtualmachineid' => virtualmachineid,
14   - 'deviceid' => deviceid
15   - }
  12 + )
16 13
17 14 request(options)
18 15 end
19 16
20   - end
21   - end
22   - end
23   -end
  17 + end # Real
  18 +
  19 + class Mock
  20 +
  21 + def attach_volume(options={})
  22 + volume_id = options['id']
  23 + server_id = options['virtualmachineid']
  24 +
  25 + volume = self.data[:volumes][volume_id]
  26 + unless volume
  27 + raise Fog::Compute::Cloudstack::BadRequest.new("Unable to execute API command attachvolume due to invalid value. Object volumes(uuid: #{volume_id}) does not exist.")
  28 + end
  29 +
  30 + server = self.data[:servers][server_id]
  31 + unless server
  32 + raise Fog::Compute::Cloudstack::BadRequest.new("Unable to execute API command attachvolume due to invalid value. Object vm_instance(uuid: #{server_id}) does not exist.")
  33 + end
  34 +
  35 + volume['virtualmachineid']= server['id']
  36 + volume['vmname']= server['name']
  37 + volume['vmdisplayname']= server['displayname']
  38 +
  39 + job_id = Fog::Cloudstack.uuid
  40 +
  41 + # FIXME: need to determine current user
  42 + account_id = self.data[:accounts].first
  43 + user_id = self.data[:users].first
  44 +
  45 + job = {
  46 + "accountid" => account_id,
  47 + "userid" => user_id,
  48 + "cmd" => "com.cloud.api.commands.AttachVolumeCmd",
  49 + "created" => Time.now.iso8601,
  50 + "jobid" => job_id,
  51 + "jobstatus" => 1,
  52 + "jobprocstatus" => 0,
  53 + "jobresultcode" => 0,
  54 + "jobresulttype" => "object",
  55 + "jobresult" =>
  56 + {"volume" => volume}
  57 + }
  58 +
  59 + self.data[:jobs][job_id]= job
  60 +
  61 + {
  62 + "attachvolumeresponse" => {
  63 + "jobid" => job_id
  64 + }
  65 + }
  66 + end
  67 + end # Mock
  68 + end # Cloudstack
  69 + end # Compute
  70 +end # Fog
43 lib/fog/cloudstack/requests/compute/create_volume.rb
@@ -14,6 +14,49 @@ def create_volume(options={})
14 14 request(options)
15 15 end
16 16
  17 + end # Real
  18 +
  19 + class Mock
  20 + def create_volume(options={})
  21 + volume_id = Fog::Cloudstack.uuid
  22 +
  23 + unless volume_name = options['name']
  24 + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command createvolume due to missing parameter name')
  25 + end
  26 +
  27 + unless zone_id = options['zoneid']
  28 + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command createvolume due to missing parameter zoneid')
  29 + end
  30 +
  31 + unless disk_offering_id = options['diskofferingid']
  32 + raise Fog::Compute::Cloudstack::BadRequest.new('Unable to execute API command createvolume due to missing parameter diskofferingid')
  33 + end
  34 +
  35 + volume = {
  36 + "id" => volume_id,
  37 + "name" => volume_name,
  38 + "zoneid" => zone_id,
  39 + "zonename" => "ey-wdc-00",
  40 + "type" => "DATADISK",
  41 + "size" => 5368709120,
  42 + "created" => "2012-05-22T14:52:55-0500",
  43 + "state" => "Allocated",
  44 + "account" => "accountname",
  45 + "domainid" => "6023b6fe-5bef-4358-bc76-9f4e75afa52f",
  46 + "domain" => "ROOT",
  47 + "storagetype" => "shared",
  48 + "hypervisor" => "None",
  49 + "diskofferingid" => disk_offering_id,
  50 + "diskofferingname" => "Small",
  51 + "diskofferingdisplaytext" => "Small Disk, 5 GB",
  52 + "storage" => "none",
  53 + "destroyed" => false,
  54 + "isextractable" => false
  55 + }
  56 +
  57 + self.data[:volumes][volume_id]= volume
  58 + {'createvolumeresponse' => volume}
  59 + end
17 60 end
18 61 end
19 62 end
28 lib/fog/cloudstack/requests/compute/delete_volume.rb
@@ -14,7 +14,27 @@ def delete_volume(options={})
14 14 request(options)
15 15 end
16 16
17   - end
18   - end
19   - end
20   -end
  17 + end # Real
  18 + class Mock
  19 + def delete_volume(options={})
  20 + volume_id = options['id']
  21 + if self.data[:volumes][volume_id]
  22 + self.data[:volumes].delete(volume_id)
  23 + {
  24 + "deletevolumeresponse" => {
  25 + "success" => "true"
  26 + }
  27 + }
  28 + else # FIXME: mayhaps
  29 + self.data[:volumes].delete(volume_id)
  30 + {
  31 + "deletevolumeresponse" => {
  32 + "success" => "false"
  33 + }
  34 + }
  35 + end
  36 + end
  37 + end # Mock
  38 + end # Cloudstack
  39 + end # Compute
  40 +end # Fog
53 lib/fog/cloudstack/requests/compute/detach_volume.rb
@@ -6,16 +6,57 @@ class Real
6 6 # Deletes a specified domain.
7 7 #
8 8 # {CloudStack API Reference}[http://download.cloud.com/releases/2.2.0/api_2.2.4/global_admin/detachVolume.html]
9   - def detach_volume(id)
10   - options = {
11   - 'command' => 'detachVolume',
12   - 'id' => id
13   - }
  9 + def detach_volume(options={})
  10 + options.merge!(
  11 + 'command' => 'detachVolume'
  12 + )
14 13
15 14 request(options)
16 15 end
17 16
18   - end
  17 + end # Real
  18 +
  19 + class Mock
  20 +
  21 + def detach_volume(options={})
  22 + volume_id = options['id']
  23 +
  24 + volume = self.data[:volumes][volume_id]
  25 + unless volume
  26 + raise Fog::Compute::Cloudstack::BadRequest.new("Unable to execute API command attachvolume due to invalid value. Object volumes(uuid: #{volume_id}) does not exist.")
  27 + end
  28 +
  29 + volume['virtualmachineid']= volume['vmname']= volume['vmdisplayname']= nil
  30 +
  31 + job_id = Fog::Cloudstack.uuid
  32 +
  33 + # FIXME: need to determine current user
  34 + account_id = self.data[:accounts].first
  35 + user_id = self.data[:users].first
  36 +
  37 + job = {
  38 + "accountid" => account_id,
  39 + "userid" => user_id,
  40 + "cmd" => "com.cloud.api.commands.DetachVolumeCmd",
  41 + "created" => Time.now.iso8601,
  42 + "jobid" => job_id,
  43 + "jobstatus" => 1,
  44 + "jobprocstatus" => 0,
  45 + "jobresultcode" => 0,
  46 + "jobresulttype" => "object",
  47 + "jobresult" =>
  48 + {"volume" => volume}
  49 + }
  50 +
  51 + self.data[:jobs][job_id]= job
  52 +
  53 + {
  54 + "detachvolumeresponse" => {
  55 + "jobid" => job_id
  56 + }
  57 + }
  58 + end
  59 + end # Mock
19 60 end
20 61 end
21 62 end
28 lib/fog/cloudstack/requests/compute/list_volumes.rb
@@ -10,11 +10,29 @@ def list_volumes(options={})
10 10 options.merge!(
11 11 'command' => 'listVolumes'
12 12 )
13   -
  13 +
14 14 request(options)
15 15 end
16 16
17   - end
18   - end
19   - end
20   -end
  17 + end # Real
  18 +
  19 + class Mock
  20 + def list_volumes(options={})
  21 + volume_id = options.delete('id')
  22 + if volume_id
  23 + volumes = [self.data[:volumes][volume_id]]
  24 + else
  25 + volumes = self.data[:volumes].values
  26 + end
  27 +
  28 + {
  29 + 'listvolumesresponse' => {
  30 + 'count' => volumes.size,
  31 + 'volume' => volumes
  32 + }
  33 + }
  34 + end
  35 + end # Mock
  36 + end # Cloudstack
  37 + end # Compute
  38 +end #Fog
4 lib/fog/core/collection.rb
@@ -99,8 +99,8 @@ def model
99 99 end
100 100
101 101 def new(attributes = {})
102   - unless attributes.is_a?(Hash)
103   - raise(ArgumentError.new("Initialization parameters must be an attributes hash, got #{attributes.inspect}"))
  102 + unless attributes.is_a?(::Hash)
  103 + raise(ArgumentError.new("Initialization parameters must be an attributes hash, got #{attributes.class} #{attributes.inspect}"))
104 104 end
105 105 model.new(
106 106 attributes.merge(

0 comments on commit 54f1c92

Please sign in to comment.
Something went wrong with that request. Please try again.