diff --git a/lib/fog/cloudsigma/compute.rb b/lib/fog/cloudsigma/compute.rb index 1e47a66f5..59a8be6dd 100644 --- a/lib/fog/cloudsigma/compute.rb +++ b/lib/fog/cloudsigma/compute.rb @@ -18,6 +18,15 @@ module Fog request :update_volume request :delete_volume request :clone_volume + + model :snapshot + collection :snapshots + request :create_snapshot + request :get_snapshot + request :list_snapshots + request :update_snapshot + request :delete_snapshot + request :clone_snapshot model :lib_volume collection :lib_volumes diff --git a/lib/fog/cloudsigma/models/server.rb b/lib/fog/cloudsigma/models/server.rb index efe6ee7bb..af5049115 100644 --- a/lib/fog/cloudsigma/models/server.rb +++ b/lib/fog/cloudsigma/models/server.rb @@ -71,6 +71,11 @@ module Fog requires :identity service.stop_server(identity) end + + def shutdown + requires :identity + service.stop_server(identity, true) + end def ready? status == "running" diff --git a/lib/fog/cloudsigma/models/snapshot.rb b/lib/fog/cloudsigma/models/snapshot.rb new file mode 100644 index 000000000..4ea5f501e --- /dev/null +++ b/lib/fog/cloudsigma/models/snapshot.rb @@ -0,0 +1,77 @@ +require 'fog/cloudsigma/nested_model' +require 'fog/cloudsigma/models/volume' + +module Fog + module Compute + class CloudSigma + class Snapshot < Fog::CloudSigma::CloudsigmaModel + identity :uuid + + attribute :allocated_size, :type => :integer + attribute :drive + attribute :grantees, :type => :array + attribute :meta + attribute :name, :type => :string + attribute :owner + attribute :permissions, :type => :array + attribute :resource_uri, :type => :string + attribute :status, :type => :string + attribute :tags + attribute :timestamp, :type => :string + + + def save + if persisted? + update + else + create + end + end + + def create + requires :name, :drive + data = attributes + + response = service.create_snapshot(data) + new_attributes = response.body['objects'].first + merge_attributes(new_attributes) + end + + def update + requires :identity, :name + + data = attributes + + response = service.update_snapshot(identity, data) + new_attributes = response.body + merge_attributes(new_attributes) + end + + def destroy + requires :identity + + service.delete_snapshot(identity) + + true + end + + alias_method :delete, :destroy + + def clone(clone_params={}) + requires :identity + response = service.clone_snapshot(identity, clone_params) + + Volume.new(response.body) + end + + alias_method :promote, :clone + + + def available? + status == 'available' + end + + end + end + end +end diff --git a/lib/fog/cloudsigma/models/snapshots.rb b/lib/fog/cloudsigma/models/snapshots.rb new file mode 100644 index 000000000..9eb470587 --- /dev/null +++ b/lib/fog/cloudsigma/models/snapshots.rb @@ -0,0 +1,26 @@ +require 'fog/core/collection' +require 'fog/cloudsigma/models/snapshot' + +module Fog + module Compute + class CloudSigma + class Snapshots < Fog::Collection + model Fog::Compute::CloudSigma::Snapshot + + def all + resp = service.list_snapshots + data = resp.body['objects'] + load(data) + end + + def get(snap_id) + resp = service.get_snapshot(snap_id) + data = resp.body + new(data) + rescue Fog::CloudSigma::Errors::NotFound + return nil + end + end + end + end +end diff --git a/lib/fog/cloudsigma/models/volume.rb b/lib/fog/cloudsigma/models/volume.rb index 314108594..be4d7db0b 100644 --- a/lib/fog/cloudsigma/models/volume.rb +++ b/lib/fog/cloudsigma/models/volume.rb @@ -1,4 +1,5 @@ require 'fog/cloudsigma/nested_model' +require 'fog/cloudsigma/models/snapshot' module Fog module Compute @@ -19,6 +20,7 @@ module Fog attribute :affinities, :type => :array attribute :size, :type => :integer attribute :resource_uri, :type => :string + model_attribute_array :snapshots, Snapshot def save if persisted? @@ -63,6 +65,19 @@ module Fog self.class.new(response.body['objects'].first) end + + def create_snapshot(snapshot_params={}) + requires :identity + snapshot_params[:drive] = identity + response = service.create_snapshot(snapshot_params) + + Snapshot.new(response.body['objects'].first) + end + + def available? + status == 'unmounted' + end + end end end diff --git a/lib/fog/cloudsigma/requests/clone_snapshot.rb b/lib/fog/cloudsigma/requests/clone_snapshot.rb new file mode 100644 index 000000000..4d6127e06 --- /dev/null +++ b/lib/fog/cloudsigma/requests/clone_snapshot.rb @@ -0,0 +1,31 @@ +module Fog + module Compute + class CloudSigma + class Real + def clone_snapshot(snap_id, clone_params={}) + request(:path => "snapshots/#{snap_id}/action/", + :method => 'POST', + :query => {:do => :clone}, + :body => clone_params, + :expects => [200, 202]) + end + end + + class Mock + def clone_snapshot(snap_id, clone_params={}) + snapshot = self.data[:snapshots][snap_id].dup + uuid = self.class.random_uuid + snapshot['uuid'] = uuid + + self.data[:snapshots][uuid] = snapshot + + response = Excon::Response.new + response.status = 200 + response.body = snapshot + + response + end + end + end + end +end diff --git a/lib/fog/cloudsigma/requests/create_snapshot.rb b/lib/fog/cloudsigma/requests/create_snapshot.rb new file mode 100644 index 000000000..1914be14f --- /dev/null +++ b/lib/fog/cloudsigma/requests/create_snapshot.rb @@ -0,0 +1,27 @@ +module Fog + module Compute + class CloudSigma + class Real + def create_snapshot(data) + create_request("snapshots/", data) + end + end + + class Mock + def create_snapshot(data) + uuid = self.class.random_uuid + + defaults = {'uuid' => uuid, + 'timestamp' => Time.now.strftime("%Y-%m-%d %H:%M:%S.%6N%z"), + 'status' => 'creating', + 'tags' => [], + 'grantees' => [], + 'allocated_size' => 0 + } + + mock_create(:snapshots, 201, data, uuid, defaults) + end + end + end + end +end diff --git a/lib/fog/cloudsigma/requests/delete_snapshot.rb b/lib/fog/cloudsigma/requests/delete_snapshot.rb new file mode 100644 index 000000000..18478cb5a --- /dev/null +++ b/lib/fog/cloudsigma/requests/delete_snapshot.rb @@ -0,0 +1,17 @@ +module Fog + module Compute + class CloudSigma + class Real + def delete_snapshot(snap_id) + delete_request("snapshots/#{snap_id}/") + end + end + + class Mock + def delete_snapshot(snap_id) + mock_delete(:snapshots, 204, snap_id) + end + end + end + end +end diff --git a/lib/fog/cloudsigma/requests/get_snapshot.rb b/lib/fog/cloudsigma/requests/get_snapshot.rb new file mode 100644 index 000000000..b5341cc32 --- /dev/null +++ b/lib/fog/cloudsigma/requests/get_snapshot.rb @@ -0,0 +1,17 @@ +module Fog + module Compute + class CloudSigma + class Real + def get_snapshot(snap_id) + get_request("snapshots/#{snap_id}/") + end + end + + class Mock + def get_snapshot(snap_id) + mock_get(:snapshots, 200, snap_id) + end + end + end + end +end diff --git a/lib/fog/cloudsigma/requests/list_snapshots.rb b/lib/fog/cloudsigma/requests/list_snapshots.rb new file mode 100644 index 000000000..743197e58 --- /dev/null +++ b/lib/fog/cloudsigma/requests/list_snapshots.rb @@ -0,0 +1,17 @@ +module Fog + module Compute + class CloudSigma + class Real + def list_snapshots + list_request('snapshots/detail/') + end + end + + class Mock + def list_snapshots + mock_list(:snapshots, 200) + end + end + end + end +end diff --git a/lib/fog/cloudsigma/requests/stop_server.rb b/lib/fog/cloudsigma/requests/stop_server.rb index 5356764a9..5b2078eab 100644 --- a/lib/fog/cloudsigma/requests/stop_server.rb +++ b/lib/fog/cloudsigma/requests/stop_server.rb @@ -2,23 +2,24 @@ module Fog module Compute class CloudSigma class Real - def stop_server(server_id) + def stop_server(server_id, graceful_acpi = false) + action = graceful_acpi ? :shutdown : :stop request(:path => "servers/#{server_id}/action/", :method => 'POST', - :query => {:do => :stop}, + :query => {:do => action}, :expects => [200, 202]) end end class Mock - def stop_server(server_id) + def stop_server(server_id, graceful_acpi = false) server = self.data[:servers][server_id] server['status'] = 'stopped' response = Excon::Response.new response.status = 200 response.body = { - 'action' => 'stop', + 'action' => graceful_acpi ? 'shutdown' : 'stop', 'result' => 'success', 'uuid' => server_id } diff --git a/lib/fog/cloudsigma/requests/update_snapshot.rb b/lib/fog/cloudsigma/requests/update_snapshot.rb new file mode 100644 index 000000000..a052e6ed7 --- /dev/null +++ b/lib/fog/cloudsigma/requests/update_snapshot.rb @@ -0,0 +1,17 @@ +module Fog + module Compute + class CloudSigma + class Real + def update_snapshot(snap_id, data) + update_request("snapshots/#{snap_id}/", data) + end + end + + class Mock + def update_snapshot(snap_id, data) + mock_update(data, :snapshots, 200, snap_id) + end + end + end + end +end diff --git a/tests/cloudsigma/models/snapshot_tests.rb b/tests/cloudsigma/models/snapshot_tests.rb new file mode 100644 index 000000000..deba85c3e --- /dev/null +++ b/tests/cloudsigma/models/snapshot_tests.rb @@ -0,0 +1,23 @@ +Shindo.tests('Fog::Compute[:cloudsigma] | snapshot model', ['cloudsigma']) do + volume = Fog::Compute[:cloudsigma].volumes.create(:name => 'fogmodeltest', :size => 1024**3, :media => :disk) + volume.wait_for { available? } unless Fog.mocking? + + snapshots = Fog::Compute[:cloudsigma].snapshots + snapshot_create_args = {:name => 'fogtest', :drive => volume.uuid} + + model_tests(snapshots, snapshot_create_args, true) do + @instance.wait_for(timeout=60) { available? } + + tests('#update').succeeds do + @instance.name = 'fogtest_renamed' + @instance.save + + @instance.reload + + returns('fogtest_renamed') { @instance.name } + end + end + + volume.destroy + +end diff --git a/tests/cloudsigma/models/snapshots_tests.rb b/tests/cloudsigma/models/snapshots_tests.rb new file mode 100644 index 000000000..b42d037c6 --- /dev/null +++ b/tests/cloudsigma/models/snapshots_tests.rb @@ -0,0 +1,15 @@ +Shindo.tests('Fog::Compute[:cloudsigma] | snapshots collection', ['cloudsigma']) do + + volume = Fog::Compute[:cloudsigma].volumes.create(:name => 'fogtest', :size => 1024**3, :media => :disk) + volume.wait_for { available? } unless Fog.mocking? + + snapshots = Fog::Compute[:cloudsigma].snapshots + snapshot_create_args = {:name => 'fogtest', :drive => volume.uuid} + + collection_tests(snapshots, snapshot_create_args, true) do + @instance.wait_for(timeout=60) { available? } + end + + volume.destroy + +end diff --git a/tests/cloudsigma/models/volume_tests.rb b/tests/cloudsigma/models/volume_tests.rb index baebee22c..9798599bf 100644 --- a/tests/cloudsigma/models/volume_tests.rb +++ b/tests/cloudsigma/models/volume_tests.rb @@ -1,9 +1,9 @@ Shindo.tests('Fog::Compute[:cloudsigma] | volume model', ['cloudsigma']) do volumes = Fog::Compute[:cloudsigma].volumes - volume_create_args = {:name => 'fogmodeltest', :size => 1000**3, :media => :cdrom} + volume_create_args = {:name => 'fogmodeltest', :size => 1024**3, :media => :cdrom} model_tests(volumes, volume_create_args, true) do - @instance.wait_for(timeout=60) { status == 'unmounted' } + @instance.wait_for(timeout=60) { available? } tests('#update').succeeds do @instance.media = 'disk' @@ -11,7 +11,7 @@ Shindo.tests('Fog::Compute[:cloudsigma] | volume model', ['cloudsigma']) do @instance.save @instance.reload - @instance.wait_for(timeout=60) { status == 'unmounted' } + @instance.wait_for(timeout=60) { available? } #returns(1024**3) { @instance.size } returns('disk') { @instance.media } diff --git a/tests/cloudsigma/requests/server_tests.rb b/tests/cloudsigma/requests/server_tests.rb index 6a99de036..879190d47 100644 --- a/tests/cloudsigma/requests/server_tests.rb +++ b/tests/cloudsigma/requests/server_tests.rb @@ -2,23 +2,23 @@ Shindo.tests('Fog::Compute[:cloudsigma] | server requests', ['cloudsigma']) do @server_format = { 'uuid' => String, - 'status' => String, - 'vnc_password' => String, - 'name' => String, + 'cpu' => Integer, 'cpus_instead_of_cores' => Fog::Boolean, - 'tags' => Array, - 'mem' => Integer, + 'drives' => Array, 'enable_numa' => Fog::Boolean, - 'smp' => Integer, 'hv_relaxed' => Fog::Boolean, 'hv_tsc' => Fog::Boolean, + 'mem' => Integer, 'meta' => Fog::Nullable::Hash, + 'name' => String, + 'nics' => Array, 'owner' => Fog::Nullable::Hash, - 'runtime' => Fog::Nullable::Hash, - 'cpu' => Integer, 'resource_uri' => Fog::Nullable::String, - 'drives' => Array, - 'nics' => Array + 'runtime' => Fog::Nullable::Hash, + 'smp' => Integer, + 'status' => String, + 'tags' => Array, + 'vnc_password' => String } @server_create_args = {:name => 'fogtest', :cpu => 2000, :mem => 512*1024**2, :vnc_password => 'myrandompass'} diff --git a/tests/cloudsigma/requests/snapshots_tests.rb b/tests/cloudsigma/requests/snapshots_tests.rb new file mode 100644 index 000000000..c58cb381e --- /dev/null +++ b/tests/cloudsigma/requests/snapshots_tests.rb @@ -0,0 +1,83 @@ +Shindo.tests('Fog::Compute[:cloudsigma] | snapshot requests', ['cloudsigma']) do + + @snapshot_format = { + 'uuid' => String, + 'allocated_size' => Fog::Nullable::Integer, + 'drive' => Fog::Nullable::Hash, + 'meta' => Fog::Nullable::Hash, + 'name' => String, + 'owner' => Fog::Nullable::Hash, + 'resource_uri' => Fog::Nullable::String, + 'status' => String, + 'tags' => Array, + 'timestamp' => String + } + @promoted_volume_format = { + 'uuid' => String, + 'affinities' => Array, + 'allow_multimount' => Fog::Boolean, + 'jobs' => Array, + 'licenses' => Array, + 'media' => String, + 'meta' => Fog::Nullable::Hash, + 'mounted_on' => Array, + 'name' => String, + 'owner' => Fog::Nullable::Hash, + 'resource_uri' => Fog::Nullable::Hash, # this field's type is the only difference from volume format + 'size' => Integer, + 'status' => String, + 'storage_type' => String, + 'tags' => Array + } + + @volume = Fog::Compute[:cloudsigma].volumes.create(:name => 'fogsnapshottest', :size => 1024**3, :media => :disk) + @volume.wait_for { available? } unless Fog.mocking? + @snapshot_create_args = {:name => 'fogtest', :drive => @volume.uuid} + + tests('success') do + + tests("#create_snapshot(#@snapshot_create_args)").formats(@snapshot_format, false) do + @resp_snapshot = Fog::Compute[:cloudsigma].create_snapshot(@snapshot_create_args).body['objects'].first + @snapshot_uuid = @resp_snapshot['uuid'] + + @resp_snapshot + end + + @snapshot = Fog::Compute[:cloudsigma].snapshots.get(@snapshot_uuid) + @snapshot.wait_for { available? } + + tests("#update_snapshot(#@snapshot_uuid)").formats(@snapshot_format, false) do + @resp_snapshot['name'] = 'fogtest_renamed' + @resp_snapshot = Fog::Compute[:cloudsigma].update_snapshot(@snapshot_uuid, @resp_snapshot).body + + @resp_snapshot + end + + # promote snapshot to a drive + tests("#promote_snapshot(#@snapshot_uuid)").formats(@promoted_volume_format, false) do + @resp_promoted_volume = Fog::Compute[:cloudsigma].clone_snapshot(@snapshot_uuid).body + + @resp_promoted_volume + end + # cleanup + @promoted_volume = Fog::Compute[:cloudsigma].volumes.get(@resp_promoted_volume['uuid']) + @promoted_volume.wait_for { available? } unless Fog.mocking? + @promoted_volume.destroy + + tests("#delete_snapshot(#@snapshot_uuid)").succeeds do + resp = Fog::Compute[:cloudsigma].delete_snapshot(@snapshot_uuid) + + resp.body.empty? && resp.status == 204 + end + + end + + tests('failure') do + tests("#get_snapshot(#@snapshot_uuid)|deleted|").raises(Fog::CloudSigma::Errors::NotFound) do + Fog::Compute[:cloudsigma].get_snapshot(@snapshot_uuid).body + end + end + + @volume.destroy + +end diff --git a/tests/cloudsigma/requests/volumes_tests.rb b/tests/cloudsigma/requests/volumes_tests.rb index 21c541ce0..13bb6df41 100644 --- a/tests/cloudsigma/requests/volumes_tests.rb +++ b/tests/cloudsigma/requests/volumes_tests.rb @@ -2,19 +2,20 @@ Shindo.tests('Fog::Compute[:cloudsigma] | volume requests', ['cloudsigma']) do @volume_format = { 'uuid' => String, - 'size' => Integer, - 'status' => String, - 'name' => String, - 'tags' => Array, + 'affinities' => Array, + 'allow_multimount' => Fog::Boolean, + 'jobs' => Array, + 'licenses' => Array, + 'media' => String, 'meta' => Fog::Nullable::Hash, + 'mounted_on' => Array, + 'name' => String, 'owner' => Fog::Nullable::Hash, 'resource_uri' => Fog::Nullable::String, - 'licenses' => Array, - 'jobs' => Array, - 'affinities' => Array, - 'mounted_on' => Array, - 'media' => String, - 'allow_multimount' => Fog::Boolean + 'size' => Integer, + 'status' => String, + 'storage_type' => String, + 'tags' => Array } @volume_create_args = {:name => 'fogtest', :size => 1024**3, :media => :cdrom} @@ -47,7 +48,7 @@ Shindo.tests('Fog::Compute[:cloudsigma] | volume requests', ['cloudsigma']) do end tests('failure') do - tests("#get_volume(#@server_uuid)|deleted|").raises(Fog::CloudSigma::Errors::NotFound) do + tests("#get_volume(#@volume_uuid)|deleted|").raises(Fog::CloudSigma::Errors::NotFound) do Fog::Compute[:cloudsigma].get_volume(@volume_uuid).body end end