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

Merge branch 'master' of github.com:fog/fog

This commit is contained in:
Nat Welch 2013-10-27 07:46:37 -07:00
commit fc5675dfdb
30 changed files with 1342 additions and 183 deletions

View file

@ -9,10 +9,17 @@ rvm:
- 1.9.2
- 1.9.3
- 2.0.0
- jruby-18mode
- jruby-19mode
script: bundle exec rake travis
matrix:
allow_failures:
- rvm: jruby-18mode
gemfile: Gemfile.1.8.7
- rvm: jruby-19mode
gemfile: Gemfile
exclude:
- rvm: 1.8.7
gemfile: Gemfile
@ -22,6 +29,10 @@ matrix:
gemfile: Gemfile.1.8.7
- rvm: 2.0.0
gemfile: Gemfile.1.8.7
- rvm: jruby-18mode
gemfile: Gemfile
- rvm: jruby-19mode
gemfile: Gemfile.1.8.7
notifications:
email: false

View file

@ -135,6 +135,31 @@ for more details and examples. Once you are ready to start scripting fog, here i
geemus says: "That should give you everything you need to get started, but let me know if there is anything I can do to help!"
## Versioning
Fog library aims to adhere to [Semantic Versioning 2.0.0][semver], although it does not
address challenges of multi-provider libraries. Semantic versioning is only guaranteed for
the common API, not any provider-specific extensions. You may also need to update your
configuration from time to time (even between Fog releases) as providers update or deprecate
services.
However, we still aim for forwards compatibility within Fog major versions. As a result of this policy, you can (and
should) specify a dependency on this gem using the [Pessimistic Version
Constraint][pvc] with two digits of precision. For example:
```ruby
spec.add_dependency 'fog', '~> 1.0'
```
This means your project is compatible with Fog 1.0 up until 2.0. You can also set a higher minimum version:
```ruby
spec.add_dependency 'fog', '~> 1.16'
```
[semver]: http://semver.org/
[pvc]: http://guides.rubygems.org/patterns/
## Contributing
* Find something you would like to work on.

View file

@ -50,11 +50,10 @@ Gem::Specification.new do |s|
s.add_dependency('net-ssh', '>=2.1.3')
s.add_dependency('nokogiri', '~>1.5')
s.add_dependency('ruby-hmac')
s.add_dependency('unicode', "~> 0.4.4")
## List your development dependencies here. Development dependencies are
## those that are only needed during development
s.add_development_dependency('jekyll')
s.add_development_dependency('jekyll') unless RUBY_PLATFORM == 'java'
s.add_development_dependency('rake')
s.add_development_dependency('rbvmomi')
s.add_development_dependency('yard')
@ -65,7 +64,10 @@ Gem::Specification.new do |s|
s.add_development_dependency('fission')
s.add_development_dependency('pry')
s.add_development_dependency('google-api-client', '~>0.6.2')
# s.add_development_dependency('ruby-libvirt','~>0.4.0')
s.add_development_dependency('unf')
if ENV["FOG_USE_LIBVIRT"] && RUBY_PLATFORM != 'java'
s.add_development_dependency('ruby-libvirt','~>0.4.0')
end
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {spec,tests}/*`.split("\n")

View file

@ -1,7 +1,13 @@
require 'fog/core'
require 'fog/aws/credential_fetcher'
require 'fog/aws/signaturev4'
require 'unicode'
begin
require 'unf/normalizer'
rescue LoadError
Fog::Logger.warning("Unable to load the 'unf' gem. Your AWS strings may not be properly encoded.")
end
module Fog
module AWS
extend Fog::Provider
@ -86,7 +92,7 @@ module Fog
end
def self.escape(string)
string = Unicode::normalize_C(string)
string = defined?(::UNF::Normalizer) ? ::UNF::Normalizer.normalize(string, :nfc) : string
string.gsub(/([^a-zA-Z0-9_.\-~]+)/) {
"%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
}

View file

@ -168,9 +168,9 @@ module Fog
"/#{escape(bucket_name.to_s)}#{path}"
end
# NOTE: differs fram Fog::AWS.escape by NOT escaping `/`
# NOTE: differs from Fog::AWS.escape by NOT escaping `/`
def escape(string)
string = Unicode::normalize_C(string)
string = defined?(::UNF::Normalizer) ? ::UNF::Normalizer.normalize(string, :nfc) : string
string.gsub(/([^a-zA-Z0-9_.\-~\/]+)/) {
"%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
}

View file

@ -42,6 +42,7 @@ module Fog
request :insert_image
request :insert_network
request :insert_server
request :insert_snapshot
request :set_metadata

View file

@ -84,6 +84,29 @@ module Fog
self
end
def create_snapshot(snap_name, snap_desc)
requires :name
requires :zone_name
if snap_name.nil? or snap_name.empty?
raise ArgumentError, 'Invalid snapshot name'
end
options = {
'name' => snap_name,
'description' => snap_desc,
}
service.insert_snapshot(name, self.zone, service.project, options)
data = service.backoff_if_unfound {
service.get_snapshot(snap_name, service.project).body
}
service.snapshots.merge_attributes(data)
# Try to return the representation of the snapshot we created
service.snapshots.get(snap_name)
end
RUNNING_STATE = "READY"
end

View file

@ -15,6 +15,7 @@ module Fog
attribute :source_disk , :aliases => 'sourceDisk'
attribute :source_disk_id , :aliases => 'sourceDiskId'
attribute :description
attribute :status
def reload
requires :name

View file

@ -0,0 +1,49 @@
module Fog
module Compute
class Google
class Mock
def insert_snapshot(snap_name)
Fog::Mock.not_implemented
end
end
class Real
def insert_snapshot(disk_name, zone_name, project=@project, opts={})
# This is unfortunate, since we might be called from 2 contexts
# 1. disk.snapshot <-- here validation of disk_name is not needed
# 2. snapshot.create <-- here we must validate the disk_name
#
# Validation would involve 'get'ing the disk by that name. This is
# redundant (and expensive) for case (1) which is likely the common
# codepath. So we won't do it.
api_method = @compute.disks.create_snapshot
parameters = {
'disk' => disk_name,
'zone' => zone_name,
'project' => @project,
}
snap_name = opts.delete('name')
raise ArgumentError.new('Must specify snapshot name') unless snap_name
body_object = { 'name' => snap_name }
# Merge in any remaining options (description)
body_object.merge(opts)
result = self.build_result(api_method, parameters,
body_object)
response = self.build_response(result)
end
end
end
end
end

View file

@ -187,7 +187,7 @@ module Fog
request :get_vm_pending_question
request :get_vms
request :get_vms_by_metadata
request :get_vms_disks_attached_to
request :get_vms_disk_attached_to
request :get_vms_in_lease_from_query
request :instantiate_vapp_template # to be deprecated
request :post_acquire_ticket
@ -231,6 +231,7 @@ module Fog
request :post_update_vapp_metadata
request :post_update_vapp_template_metadata
request :post_upgrade_hw_version
request :post_upload_disk
request :post_upload_media
request :post_upload_vapp_template
request :put_catalog_item_metadata_item_metadata
@ -482,6 +483,7 @@ module Fog
:name => 'vAppTemplate 1'
}
},
:disks => {},
:edge_gateways => {
uuid => {
:name => 'MockEdgeGateway',
@ -627,6 +629,63 @@ module Fog
[8,4,4,4,12].map {|i| Fog::Mock.random_hex(i)}.join('-')
end
# Create a task.
#
# @param [String] operation A message describing the operation that is
# tracked by this task.
# @param [String] operation_name The short name of the operation that
# is tracked by this task.
# @param [Hash] owner Reference to the owner of the task. This is
# typically the object that the task is creating or updating.
# * :href<~String> - Contains the URI to the entity.
# * :type<~String> - Contains the MIME type of the entity.
# @return [String] Object identifier of the task.
def enqueue_task(operation, operation_name, owner, options={})
task_id = uuid
now = DateTime.now
data[:tasks][task_id] = {
:cancel_requested => false,
:details => '',
:expiry_time => now + 86400, # in 24 hours
:name => 'task',
:progress => 1,
:service_namespace => 'com.vmware.vcloud',
:start_time => now,
:status => 'running',
}.merge(options).merge(
:operation => operation,
:operation_name => operation_name,
:owner => owner
)
task_id
end
# @note As far as this method is concerned, the only possible task
# states are 'running' and 'success'.
#
# @param [Hash] response_body
# @return [Boolean]
def process_task(response_body)
task_id = response_body[:href].split('/').last
task = data[:tasks][task_id]
if task[:status] == 'running'
task[:end_time] = DateTime.now
task[:progress] = 100
task[:status] = 'success'
if task[:on_success]
task[:on_success].call
task.delete(:on_success)
end
end
return true if task[:status] == 'success'
raise TaskError.new "status: #{task[:status]}, error: #{task[:Error]}"
end
def add_id_from_href!(data={})
data[:id] = data[:href].split('/').last
end

View file

@ -22,6 +22,39 @@ module Fog
)
end
end
class Mock
def delete_disk(id)
unless data[:disks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"(com.vmware.vcloud.entity.disk:#{id})\""
)
end
owner = {
:href => make_href("disk/#{id}"),
:type => 'application/vnd.vmware.vcloud.disk+xml'
}
task_id = enqueue_task(
"Deleting Disk(#{id})", 'vdcDeleteDisk', owner,
:on_success => lambda do
data[:disks].delete(id)
end
)
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location,
}.merge(task_body(task_id))
Excon::Response.new(
:status => 202,
:headers => {'Content-Type' => "#{body[:type]};version=#{api_version}"},
:body => body
)
end
end
end
end
end

View file

@ -10,6 +10,67 @@ module Fog
# @param [String] id Object identifier of the media object.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the entity.
# * :type<~String> - The MIME type of the entity.
# * :id<~String> - The entity identifier, expressed in URN format.
# The value of this attribute uniquely identifies the entity,
# persists for the life of the entity, and is never reused.
# * :operationKey<~String> - Optional unique identifier to support
# idempotent semantics for create and delete operations.
# * :name<~String> - The name of the entity.
# * :cancelRequested<~String> - Whether user has requested this
# processing to be canceled.
# * :endTime<~String> - The date and time that processing of the
# task was completed. May not be present if the task is still
# being executed.
# * :expiryTime<~String> - The date and time at which the task
# resource will be destroyed and no longer available for
# retrieval. May not be present if the task has not been executed
# or is still being executed.
# * :operation<~String> - A message describing the operation that
# is tracked by this task.
# * :operationName<~String> - The short name of the operation that
# is tracked by this task.
# * :serviceNamespace<~String> - Identifier of the service that
# created the task.
# * :startTime<~String> - The date and time the system started
# executing the task. May not be present if the task has not been
# executed yet.
# * :status<~String> - The execution status of the task.
# * :Link<~Array<Hash>>:
# * :Description<~String> - Optional description.
# * :Owner<~Hash> - Reference to the owner of the task. This is
# typically the object that the task is creating or updating.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Error<~Hash> - Represents error information from a failed
# task.
# * :majorErrorCode<~String> - The class of the error. Matches
# the HTTP status code.
# * :message<~String> - An one line, human-readable message
# describing the error that occurred.
# * :minorErrorCode<~String> - Resource-specific error code.
# * :stackTrace<~String> - The stack trace of the exception.
# * :vendorSpecificErrorCode<~String> - A vendor- or
# implementation-specific error code that can reference
# specific modules or source lines for diagnostic purposes.
# * :User<~Hash> - The user who started the task.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Organization<~Hash> - The organization to which the :User
# belongs.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Progress<~String> - Read-only indicator of task progress as
# an approximate percentage between 0 and 100. Not available
# for all tasks.
# * :Params
# * :Details<~String> - Detailed message about the task. Also
# contained by the :Owner entity when task status is
# preRunning.
#
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
@ -33,8 +94,27 @@ module Fog
)
end
Fog::Mock.not_implemented
media.is_used_here # avoid warning from syntax checker
owner = {
:href => make_href("media/#{id}"),
:type => 'application/vnd.vmware.vcloud.media+xml'
}
task_id = enqueue_task(
"Deleting Media File(#{media[:file][:uuid]})", 'vdcDeleteMedia', owner,
:on_success => lambda do
data[:medias].delete(id)
end
)
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location,
}.merge(task_body(task_id))
Excon::Response.new(
:status => 202,
:headers => {'Content-Type' => "#{body[:type]};version=#{api_version}"},
:body => body
)
end
end
end

View file

@ -7,17 +7,115 @@ module Fog
# @param [String] id Object identifier of the disk.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the disk.
# * :type<~String> - The MIME type of the disk.
# * :id<~String> - The disk identifier, expressed in URN format.
# * :name<~String> - The name of the disk.
# * :status<~String> - Creation status of the disk.
# * :busSubType<~String> - Disk bus sub type.
# * :busType<~String> - Disk bus type.
# * :size<~String> - Size of the disk.
# * :Description<~String> - Optional description.
# * :Tasks<~Hash> - A list of queued, running, or recently
# completed tasks associated with this disk.
# * :StorageProfile<~Hash> - Storage profile of the disk.
# * :Owner<~Hash> - Disk owner.
#
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/GET-Disk.html
# @since vCloud API version 5.1
def get_disk(id)
request(
response = request(
:expects => 200,
:idempotent => true,
:method => 'GET',
:parser => Fog::ToHashDocument.new,
:path => "disk/#{id}"
)
ensure_list! response.body, :Link
ensure_list! response.body, :Tasks, :Task
ensure_list! response.body, :Files, :File
response
end
end
class Mock
def get_disk(id)
unless data[:disks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'No access to entity "com.vmware.vcloud.entity.disk:%s".' % id
)
end
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(disk_body(id))
Excon::Response.new(
:status => 200,
:headers => {'Content-Type' => "#{body[:type]};version=#{@version}"},
:body => body
)
end
private
def disk_body(id)
disk = data[:disks][id]
storage_class_id = disk[:vdc_storage_class]
body = {
:href => make_href("disk/#{id}"),
:type => 'application/vnd.vmware.vcloud.disk+xml',
:id => "urn:vcloud:disk:#{id}",
:name => disk[:name],
:status => disk[:status].to_s,
:busSubType => disk[:bus_sub_type],
:busType => disk[:bus_type],
:size => disk[:size].to_s,
:Link => [
{
:href => make_href("vdc/#{disk[:vdc_id]}"),
:rel => 'up',
:type => 'application/vnd.vmware.vcloud.vdc+xml'
}
],
:Description => disk[:description],
:Tasks => {
# FIXME: there's only one for now
:Task => disk[:tasks].map {|task_id| task_body(task_id)}.first
},
:Files => {
:File => []
},
:StorageProfile => {
:href => make_href("vdcStorageProfile/#{storage_class_id}"),
:name => data[:vdc_storage_classes][storage_class_id][:name],
:type => 'application/vnd.vmware.vcloud.vdcStorageProfile+xml',
},
:Owner => {
:type => 'application/vnd.vmware.vcloud.owner+xml',
:User => {
:href => make_href("admin/user/#{user_uuid}"),
:name => user_name,
:type => 'application/vnd.vmware.admin.user+xml',
}
}
}
if api_version.to_f >= 5.1
storage_class_id = disk[:vdc_storage_class]
body[:VdcStorageProfile] = {
:href => make_href("vdcStorageProfile/#{storage_class_id}"),
:name => data[:vdc_storage_classes][storage_class_id][:name],
:type => 'application/vnd.vmware.vcloud.vdcStorageProfile+xml',
}
end
body
end
end
end

View file

@ -7,6 +7,19 @@ module Fog
# @param [String] id Object identifier of the disk.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the disk.
# * :type<~String> - The MIME type of the disk.
# * :Link<~Hash>:
# * :href<~String> -
# * :type<~String> -
# * :rel<~String> -
# * :User<~Hash> - Reference to the user who is the owner of this
# disk.
# * :href<~String> - The URI of the user.
# * :name<~String> - The name of the user.
# * :type<~String> - The MIME type of the user.
#
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/GET-DiskOwner.html
# @since vCloud API version 5.1
@ -20,6 +33,38 @@ module Fog
)
end
end
class Mock
def get_disk_owner(id)
unless data[:disks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'No access to entity "com.vmware.vcloud.entity.disk:%s".' % id
)
end
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location,
:Link => {
:href => make_href("disk/#{id}"),
:type => 'application/vnd.vmware.vcloud.disk+xml',
:rel => 'up'
},
:User => {
:href => make_href("admin/user/#{user_uuid}"),
:name => user_name,
:type => 'application/vnd.vmware.admin.user+xml',
}
}
Excon::Response.new(
:status => 200,
:headers => {'Content-Type' => "#{body[:type]};version=#{@version}"},
:body => body
)
end
end
end
end
end

View file

@ -13,26 +13,97 @@ module Fog
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/GET-Media.html
# @since vCloud API version 0.9
def get_media(id)
request(
response = request(
:expects => 200,
:idempotent => true,
:method => 'GET',
:parser => Fog::ToHashDocument.new,
:path => "media/#{id}"
)
ensure_list! response.body, :Files, :File
response
end
end
class Mock
def get_media(id)
unless media = data[:medias][id]
unless data[:medias][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"(com.vmware.vcloud.entity.media:#{id})\"."
)
end
Fog::Mock.not_implemented
media.is_used_here # avoid warning from syntax checker
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(media_body(id))
Excon::Response.new(
:status => 200,
:headers => {'Content-Type' => "#{body[:type]};version=>#{@api_version}"},
:body => body
)
end
private
def media_body(id)
media = data[:medias][id]
body = {
:size => media[:size].to_s,
:imageType => media[:image_type],
:status => media[:status].to_s,
:name => media[:name],
:id => "urn:vcloud:media:#{id}",
:type => 'application/vnd.vmware.vcloud.media+xml',
:href => make_href("media/#{id}"),
:Link => {
:href => make_href("vdc/#{media[:vdc_id]}"),
:type => 'application/vnd.vmware.vcloud.vdc+xml',
:rel => 'up'
},
:Description => media[:description] || '',
:Tasks => {
# FIXME: there's only one for now
:Task => media[:tasks].map {|task_id| task_body(task_id)}.first
},
:Files => {
:File => []
},
:Owner => {
:type => 'application/vnd.vmware.vcloud.owner+xml',
:User => {
:href => make_href("admin/user/#{user_uuid}"),
:name => user_uuid,
:type => 'application/vnd.vmware.admin.user+xml',
}
}
}
if media[:status] == 0
body[:Files][:File] << {
:size => media[:size].to_s,
:bytesTransferred => media[:file][:bytes_transferred].to_s,
:name => 'file',
:Link=> {
:href => make_href("transfer/#{media[:file][:uuid]}/file"),
:rel => 'upload:default'
}
}
end
if api_version.to_f >= 5.1
storage_class_id = media[:vdc_storage_class]
body[:VdcStorageProfile] = {
:href => make_href("vdcStorageProfile/#{storage_class_id}"),
:name => data[:vdc_storage_classes][storage_class_id][:name],
:type => 'application/vnd.vmware.vcloud.vdcStorageProfile+xml',
}
end
body
end
end
end

View file

@ -7,6 +7,17 @@ module Fog
# @param [String] id Object identifier of the media object.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the media object.
# * :type<~String> - The MIME type of the media object.
# * :Link<~Hash>:
# * :href<~String> -
# * :type<~String> -
# * :rel<~String> -
# * :User<~Hash> - Reference to the user who is the owner of this
# media object.
# * :href<~String> - The URI of the user.
# * :name<~String> - The name of the user.
# * :type<~String> - The MIME type of the user.
#
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
@ -25,14 +36,33 @@ module Fog
class Mock
def get_media_owner(id)
unless media = data[:medias][id]
unless data[:medias][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"com.vmware.vcloud.entity.media:#{id}\"."
'No access to entity "com.vmware.vcloud.entity.media:%s".' % id
)
end
Fog::Mock.not_implemented
media.is_used_here # avoid warning from syntax checker
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location,
:Link => {
:href => make_href("media/#{id}"),
:type => 'application/vnd.vmware.vcloud.media+xml',
:rel => 'up'
},
:User => {
:href => make_href("admin/user/#{user_uuid}"),
:name => user_name,
:type => 'application/vnd.vmware.admin.user+xml',
}
}
Excon::Response.new(
:status => 200,
:headers => {'Content-Type' => "#{body[:type]};version=#{@version}"},
:body => body
)
end
end
end

View file

@ -1,3 +1,5 @@
require 'date'
module Fog
module Compute
class VcloudDirector
@ -7,6 +9,66 @@ module Fog
# @param [String] id The object identifier of the task.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the entity.
# * :type<~String> - The MIME type of the entity.
# * :id<~String> - The entity identifier, expressed in URN format.
# The value of this attribute uniquely identifies the entity,
# persists for the life of the entity, and is never reused.
# * :operationKey<~String> - Optional unique identifier to support
# idempotent semantics for create and delete operations.
# * :name<~String> - The name of the entity.
# * :cancelRequested<~String> - Whether user has requested this
# processing to be canceled.
# * :endTime<~String> - The date and time that processing of the
# task was completed. May not be present if the task is still
# being executed.
# * :expiryTime<~String> - The date and time at which the task
# resource will be destroyed and no longer available for
# retrieval. May not be present if the task has not been executed
# or is still being executed.
# * :operation<~String> - A message describing the operation that
# is tracked by this task.
# * :operationName<~String> - The short name of the operation that
# is tracked by this task.
# * :serviceNamespace<~String> - Identifier of the service that
# created the task.
# * :startTime<~String> - The date and time the system started
# executing the task. May not be present if the task has not been
# executed yet.
# * :status<~String> - The execution status of the task.
# * :Link<~Array<Hash>>:
# * :Description<~String> - Optional description.
# * :Owner<~Hash> - Reference to the owner of the task. This is
# typically the object that the task is creating or updating.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Error<~Hash> - Represents error information from a failed
# task.
# * :majorErrorCode<~String> - The class of the error. Matches
# the HTTP status code.
# * :message<~String> - An one line, human-readable message
# describing the error that occurred.
# * :minorErrorCode<~String> - Resource-specific error code.
# * :stackTrace<~String> - The stack trace of the exception.
# * :vendorSpecificErrorCode<~String> - A vendor- or
# implementation-specific error code that can reference
# specific modules or source lines for diagnostic purposes.
# * :User<~Hash> - The user who started the task.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Organization<~Hash> - The organization to which the :User
# belongs.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Progress<~String> - Read-only indicator of task progress as an
# approximate percentage between 0 and 100. Not available for all
# tasks.
# * :Params
# * :Details<~String> - Detailed message about the task. Also
# contained by the :Owner entity when task status is preRunning.
#
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
@ -25,14 +87,71 @@ module Fog
class Mock
def get_task(id)
unless task = data[:tasks][id]
unless data[:tasks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'This operation is denied.'
)
end
Fog::Mock.not_implemented
task.is_used_here # avoid warning from syntax checker
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(task_body(id))
Excon::Response.new(
:status => 200,
:headers => {'Type' => "application/#{body[:type]};version=#{api_version}"},
:body => body
)
end
private
# @param [String] id Object identifier of the task.
# @return [Hash]
def task_body(id)
task = data[:tasks][id]
body = {
:href => make_href("tasks/#{id}"),
:type => 'application/vnd.vmware.vcloud.task+xml',
:id => "urn:vcloud:tasl:#{id}",
:name => task[:name],
:cancelRequested => task[:cancel_requested].to_s,
:expiryTime => task[:expiry_time].strftime('%Y-%m-%dT%H:%M:%S%z'),
:operation => task[:operation],
:operationName => task[:operation_name],
:serviceNamespace => task[:service_namespace],
:status => task[:status],
:Link => [],
:Owner => task[:owner],
:User => { # for now, always the current user
:href => make_href("admin/user/#{user_uuid}"),
:name => user_name,
:type => 'application/vnd.vmware.admin.user+xml',
},
:Organization => { # for now, always the current org
:href => make_href("org/#{data[:org][:uuid]}"),
:name => data[:org][:name],
:type => 'application/vnd.vmware.vcloud.org+xml',
},
:Progress => task[:progress].to_s,
:Details => task[:details] || '',
}
body[:endTime] = task[:end_time].strftime('%Y-%m-%dT%H:%M:%S%z') if task[:end_time]
body[:startTime] = task[:start_time].strftime('%Y-%m-%dT%H:%M:%S%z') if task[:start_time]
body[:Description] = task[:description] if task[:description]
if task[:status] == 'running'
body[:Link] << {
:href => make_href("task/#{id}/action/cancel"),
:type => 'application/vnd.vmware.vcloud.task+xml',
:rel => 'cancel',
}
end
body
end
end
end

View file

@ -11,7 +11,78 @@ module Fog
# @param [String] id Object identifier of the organization.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the entity.
# * :type<~String> - The MIME type of the entity.
# * :id<~String> - The entity identifier, expressed in URN format.
# The value of this attribute uniquely identifies the entity,
# persists for the life of the entity, and is never reused.
# * :name<~String> - The name of the entity.
# * :Task<~Array<Hash>>:
# * :href<~String> - The URI of the entity.
# * :type<~String> - The MIME type of the entity.
# * :id<~String> - The entity identifier, expressed in URN
# format. The value of this attribute uniquely identifies the
# entity, persists for the life of the entity, and is never
# reused.
# * :operationKey<~String> - Optional unique identifier to
# support idempotent semantics for create and delete
# operations.
# * :name<~String> - The name of the entity.
# * :cancelRequested<~String> - Whether user has requested this
# processing to be canceled.
# * :endTime<~String> - The date and time that processing of the
# task was completed. May not be present if the task is still
# being executed.
# * :expiryTime<~String> - The date and time at which the task
# resource will be destroyed and no longer available for
# retrieval. May not be present if the task has not been
# executed or is still being executed.
# * :operation<~String> - A message describing the operation that
# is tracked by this task.
# * :operationName<~String> - The short name of the operation
# that is tracked by this task.
# * :serviceNamespace<~String> - Identifier of the service that
# created the task.
# * :startTime<~String> - The date and time the system started
# executing the task. May not be present if the task has not
# been executed yet.
# * :status<~String> - The execution status of the task.
# * :Link<~Array<Hash>>:
# * :Description<~String> - Optional description.
# * :Owner<~Hash> - Reference to the owner of the task. This is
# typically the object that the task is creating or updating.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Error<~Hash> - Represents error information from a failed
# task.
# * :majorErrorCode<~String> - The class of the error. Matches
# the HTTP status code.
# * :message<~String> - An one line, human-readable message
# describing the error that occurred.
# * :minorErrorCode<~String> - Resource-specific error code.
# * :stackTrace<~String> - The stack trace of the exception.
# * :vendorSpecificErrorCode<~String> - A vendor- or
# implementation-specific error code that can reference
# specific modules or source lines for diagnostic purposes.
# * :User<~Hash> - The user who started the task.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Organization<~Hash> - The organization to which the :User
# belongs.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Progress<~String> - Read-only indicator of task progress as
# an approximate percentage between 0 and 100. Not available
# for all tasks.
# * :Params
# * :Details<~String> - Detailed message about the task. Also
# contained by the :Owner entity when task status is
# preRunning.
#
# @raise [Fog::Compute::VcloudDirector::BadRequest]
# @raise [Fog::Compute::VcloudDirector::Forbidden]
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/GET-TaskList.html
@ -37,43 +108,15 @@ module Fog
)
end
body =
{:xmlns=>xmlns,
:xmlns_xsi=>xmlns_xsi,
:name=>"Tasks Lists",
:type=>"application/vnd.vmware.vcloud.tasksList+xml",
:href=>make_href("tasksList/#{id}"),
:xsi_schemaLocation=>xsi_schema_location}
tasks = data[:tasks].map do |task_id, task|
{:status => task[:status],
:startTime => task[:startTime].iso8601,
:operationName => task[:operationName],
:operation => task[:operation],
:expiryTime => task[:expiryTime].utc.iso8601,
:endTime => task[:endTime].iso8601,
:cancelRequested => task[:cancelRequested].to_s,
:name => task[:name],
:id => "urn:vcloud:task:#{id}",
:type => 'application/vnd.vmware.vcloud.task+xml',
:href => make_href("task/#{task_id}"),
:Owner =>
{:type => "application/vnd.vmware.vcloud.vApp+xml",
:name => "vApp_#{user_name}_0",
:href=> make_href("vApp/vapp-#{uuid}")},
:User =>
{:type => "application/vnd.vmware.admin.user+xml",
:name => user_name,
:href => make_href("admin/user/#{user_uuid}")},
:Organization =>
{:type => "application/vnd.vmware.vcloud.org+xml",
:name => org_name,
:href => make_href("org/#{org_uuid}")},
:Progress => task[:Progress].to_s,
:Details => task[:Details] || ''}
end
tasks = tasks.first if tasks.size == 1
body[:Task] = tasks
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location,
:href => make_href("tasksList/#{id}"),
:type => "application/vnd.vmware.vcloud.tasksList+xml",
:name => "Tasks Lists",
:Task => data[:tasks].keys.map {|task_id| task_body(task_id)}
}
Excon::Response.new(
:status => 200,

View file

@ -10,14 +10,28 @@ module Fog
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/GET-VmsDisksAttachedTo.html
# @since vCloud API version 5.1
def get_vms_disks_attached_to(id)
request(
def get_vms_disk_attached_to(id)
response = request(
:expects => 200,
:idempotent => true,
:method => 'GET',
:parser => Fog::ToHashDocument.new,
:path => "disk/#{id}/attachedVms"
)
ensure_list! response.body, :VmReference
response
end
end
class Mock
def get_vms_disk_attached_to(id)
unless data[:disks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'No access to entity "com.vmware.vcloud.entity.disk:%s".' % id
)
end
Fog::Mock.not_implemented
end
end
end

View file

@ -20,6 +20,24 @@ module Fog
)
end
end
class Mock
def post_cancel_task(id)
unless task = data[:tasks][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'No access to entity "com.vmware.vcloud.entity.task:%s"' % id
)
end
# @note Tasks don't actually get cancelled (confirmed VCloud Director
# bug) so we'll emulate that. Set the flag and we're done!
task[:cancel_requested] = true
Excon::Response.new(
:status => 204
)
end
end
end
end
end

View file

@ -36,7 +36,7 @@ module Fog
}
end.to_xml
request(
response = request(
:body => body,
:expects => 201,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.cloneMediaParams+xml'},
@ -44,6 +44,57 @@ module Fog
:parser => Fog::ToHashDocument.new,
:path => "vdc/#{vdc_id}/action/cloneMedia"
)
ensure_list! response.body, :Files, :File
response
end
end
class Mock
def post_clone_media(vdc_id, source_id, options={})
# TODO: check this happens.
unless source_media = data[:medias][source_id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"(com.vmware.vcloud.entity.media:#{source_id})\"."
)
end
unless data[:vdcs][vdc_id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"(com.vmware.vcloud.entity.vdc:#{vdc_id})\"."
)
end
media_id = uuid
media_name = "#{source_media[:name]}-copy-#{uuid}"
owner = {
:href => make_href("media/#{media_id}"),
:type => 'application/vnd.vmware.vcloud.media+xml'
}
task_id = enqueue_task(
"Copy Media File #{media_name}(#{media_id})", 'vdcCopyMedia', owner,
:on_success => lambda do
data[:medias][media_id][:status] = 1
end
)
media = source_media.dup.merge(
:name => media_name,
:status => 0,
:tasks => [task_id]
)
data[:medias][media_id] = media
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(media_body(media_id))
Excon::Response.new(
:status => 201,
:headers => {'Content-Type' => "#{body[:type]};version=#{api_version}"},
:body => body
)
end
end
end

View file

@ -0,0 +1,133 @@
module Fog
module Compute
class VcloudDirector
class Real
# Create a disk.
#
# @param [String] id Object identifier of the vDC.
# @param [String] name The name of the disk.
# @param [Integer] size Size of the disk. For modify operation this is
# required only for the XSD validation it could not be changed.
# @param [Hash] options
# @option options [String] :operationKey Optional unique identifier to
# support idempotent semantics for create and delete operations.
# @option options [Integer] :busSubType Disk bus sub type.
# @option options [Integer] :busType Disk bus type.
# @option options [String] :Description Optional description.
# @return [Excon::Response]
# * body<~Hash>:
# * :href<~String> - The URI of the disk.
# * :type<~String> - The MIME type of the disk.
# * :id<~String> - The disk identifier, expressed in URN format.
# * :operationKey<~String> - Optional unique identifier to support
# idempotent semantics for create and delete operations.
# * :name<~String> - The name of the disk.
# * :status<~String> - Creation status of the disk.
# * :busSubType<~String> - Disk bus sub type.
# * :busType<~String> - Disk bus type.
# * :size<~String> - Size of the disk.
# * :Link:
# * :Description<~String> - Optional description.
# * :Tasks<~Hash>:
# * :StorageProfile<~Hash> - Storage profile of the disk.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
# * :Owner<~Hash> - Disk owner.
# * :type<~String> - The MIME type of the entity.
# * :User<~Hash> - Reference to the user who is the owner of this
# disk.
# * :href<~String> - Contains the URI to the entity.
# * :name<~String> - Contains the name of the entity.
# * :type<~String> - Contains the type of the entity.
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/POST-CreateDisk.html
# @since vCloud API version 5.1
def post_upload_disk(id, name, size, options={})
body = Nokogiri::XML::Builder.new do
DiskCreateParams(:xmlns => 'http://www.vmware.com/vcloud/v1.5') {
attrs = {
:name => name,
:size => size
}
attrs[:operationKey] = options[:operationKey] if options.key?(:operationKey)
attrs[:busSubType] = options[:busSubType] if options.key?(:busSubType)
attrs[:busType] = options[:busType] if options.key?(:busType)
Disk(attrs) {
if options.key?(:Description)
Description options[:Description]
end
if options.key?(:StorageProfile)
attrs = {
:href => options[:StorageProfile][:href]
}
StorageProfile(attrs)
end
}
}
end.to_xml
request(
:body => body,
:expects => 201,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.diskCreateParams+xml'},
:method => 'POST',
:parser => Fog::ToHashDocument.new,
:path => "vdc/#{id}/disk"
)
end
end
class Mock
def post_upload_disk(id, name, size, options={})
unless size.to_s =~ /^\d+$/
raise Fog::Compute::VcloudDirector::BadRequest.new(
"validation error on field 'diskSpec.sizeBytes': must be greater than or equal to 0"
)
end
unless data[:vdcs][id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
'No access to entity "(com.vmware.vcloud.entity.vdc:%s)".' % id
)
end
disk_id = uuid
owner = {
:href => make_href("disk/#{disk_id}"),
:type => 'application/vnd.vmware.vcloud.disk+xml'
}
task_id = enqueue_task(
"Creating Disk #{name}(#{disk_id})", 'vdcCreateDisk', owner,
:on_success => lambda do
data[:disks][disk_id][:status] = 1
end
)
disk = {
:description => options[:Description],
:name => name,
:size => size.to_i,
:status => 0,
:tasks => [task_id],
:vdc_id => id,
:vdc_storage_class => data[:vdc_storage_classes].detect {|k,v| v[:default]}.first
}
data[:disks][disk_id] = disk
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(disk_body(disk_id))
Excon::Response.new(
:status => 201,
:headers => {'Content-Type' => "#{body[:type]};version=#{api_version}"},
:body => body
)
end
end
end
end
end

View file

@ -22,6 +22,7 @@ module Fog
#
# @see http://pubs.vmware.com/vcd-51/topic/com.vmware.vcloud.api.reference.doc_51/doc/operations/POST-UploadMedia.html
# @since vCloud API version 0.9
# @todo Support vDC Storage Profiles.
def post_upload_media(vdc_id, name, image_type, size, options={})
body = Nokogiri::XML::Builder.new do
attrs = {
@ -38,7 +39,7 @@ module Fog
}
end.to_xml
request(
response = request(
:body => body,
:expects => 201,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.media+xml'},
@ -46,6 +47,8 @@ module Fog
:parser => Fog::ToHashDocument.new,
:path => "vdc/#{vdc_id}/media"
)
ensure_list! response.body, :Files, :File
response
end
end
@ -61,14 +64,55 @@ module Fog
'validation error on field \'size\': must be greater than or equal to 0'
)
end
unless vdc = data[:vdcs][vdc_id]
unless data[:vdcs][vdc_id]
raise Fog::Compute::VcloudDirector::Forbidden.new(
"No access to entity \"(com.vmware.vcloud.entity.vdc:#{vdc_id})\"."
)
end
Fog::Mock.not_implemented
vdc.is_used_here # avoid warning from syntax checker
media_id = uuid
file_id = uuid
owner = {
:href => make_href("media/#{media_id}"),
:type => 'application/vnd.vmware.vcloud.media+xml'
}
task_id = enqueue_task(
"Importing Media File #{name}(#{file_id})", 'vdcUploadMedia', owner,
:on_success => lambda do
media = data[:medias][media_id]
media[:file][:bytes_transferred] = media[:size]
media[:status] = 1
end
)
media = {
:description => options[:Description],
:file => {
:bytes_transferred => 0,
:uuid => file_id
},
:image_type => image_type,
:name => name,
:size => size.to_i,
:status => 0,
:tasks => [task_id],
:vdc_id => vdc_id,
:vdc_storage_class => data[:vdc_storage_classes].detect {|k,v| v[:default]}.first
}
data[:medias][media_id] = media
body = {
:xmlns => xmlns,
:xmlns_xsi => xmlns_xsi,
:xsi_schemaLocation => xsi_schema_location
}.merge(media_body(media_id))
Excon::Response.new(
:status => 201,
:headers => {'Content-Type' => "#{body[:type]};version=#{api_version}"},
:body => body
)
end
end
end

View file

@ -54,7 +54,13 @@ Shindo.tests do
returns(nil, 'File.expand_path raises because of non-absolute path') {
ENV.delete('FOG_RC')
ENV['HOME'] = '.'
Fog.credentials_path
if RUBY_PLATFORM == 'java'
Fog::Logger.warning("Stubbing out non-absolute path credentials test due to JRuby bug: https://github.com/jruby/jruby/issues/1163")
nil
else
Fog.credentials_path
end
}
returns(nil, 'returns nil when neither FOG_RC or HOME are set') {

View file

@ -46,7 +46,7 @@ posuere eu odio. Donec sodales, ante porta condimentum
}
@xmlNS = %{
<myns:MyResponse>
<myns:MyResponse xmlns:myns="http://www.example.com/">
<myns:MyObject>
<myns:key1>value1</myns:key1>
<myns:key2>value2</myns:key2>

View file

@ -1,6 +1,166 @@
Shindo.tests('Compute::VcloudDirector | disk requests', ['vclouddirector']) do
@service = Fog::Compute::VcloudDirector.new
@disk_name = VcloudDirector::Compute::Helper.test_name
tests('error conditions') do
tests('#post_upload_disk') do
tests('Invalid size').raises(Fog::Compute::VcloudDirector::BadRequest) do
@service.post_upload_disk('00000000-0000-0000-0000-000000000000', @disk_name, -1)
end
end
tests('Upload to non-existent vDC').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.post_upload_disk('00000000-0000-0000-0000-000000000000', @disk_name, 0)
end
tests('Retrieve non-existent Disk').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_disk('00000000-0000-0000-0000-000000000000')
end
tests('Retrieve owner of non-existent Disk').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_disk_owner('00000000-0000-0000-0000-000000000000')
end
tests('Retrieve VM list for non-existent Disk').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_vms_disk_attached_to('00000000-0000-0000-0000-000000000000')
end
tests('Delete non-existent Disk').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.delete_disk('00000000-0000-0000-0000-000000000000')
end
end
@org = VcloudDirector::Compute::Helper.current_org(@service)
@size = 1024
tests('Upload and manipulate a disk') do
tests('#post_upload_disk').data_matches_schema(VcloudDirector::Compute::Schema::DISK_TYPE) do
@vdc_id = VcloudDirector::Compute::Helper.first_vdc_id(@org)
@disk = @service.post_upload_disk(@vdc_id, @disk_name, @size).body
end
@service.process_task(@disk[:Tasks][:Task])
@disk_id = @disk[:href].split('/').last
tests("#get_disk(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::DISK_TYPE) do
@disk = @service.get_disk(@disk_id).body
end
tests("disk[:name]").returns(@disk_name) { @disk[:name] }
tests("disk[:size]").returns(@size) { @disk[:size].to_i }
tests("#get_disk_owner(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::OWNER_TYPE) do
@owner = @service.get_disk_owner(@disk_id).body
end
tests("owner[:User][:name]").returns(@service.user_name) { @owner[:User][:name] }
#tests("#put_disk(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
# @disk_name += '-renamed'
# @task = @service.put_disk(@disk_id, @disk_name).body
#end
#@service.process_task(@task)
#tests("#get_disk(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::DISK_TYPE) do
# @disk = @service.get_disk(@disk_id).body
#end
#tests("disk[:name]").returns(@disk_name) { @disk[:name] }
#tests("disk[:size]").returns(@size) { @disk[:size].to_i } # shouldn't change
tests('disk metadata') do
pending if Fog.mocking?
tests("#put_disk_metadata_item_metadata(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
@task = @service.put_disk_metadata_item_metadata(@disk_id, 'fog-test-key', 'fog-test-value').body
end
@service.process_task(@task)
tests("#put_disk_metadata_item_metadata(#{@disk_id})") do
tests("#put_disk_metadata_item_metadata(Boolean)").returns(true) do
task = @service.put_disk_metadata_item_metadata(@disk_id, 'fog-test-boolean', true).body
@service.process_task(task)
end
tests("#put_disk_metadata_item_metadata(DateTime)").returns(true) do
task = @service.put_disk_metadata_item_metadata(@disk_id, 'fog-test-datetime', DateTime.now).body
@service.process_task(task)
end
tests("#put_disk_metadata_item_metadata(Number)").returns(true) do
task = @service.put_disk_metadata_item_metadata(@disk_id, 'fog-test-number', 111).body
@service.process_task(task)
end
end
tests("#post_update_disk_metadata(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
metadata = {
'fog-test-key-update' => 'fog-test-value-update',
'fog-test-boolean-update' => false,
'fog-test-datetime-update' => DateTime.now,
'fog-test-number-update' => 222
}
@task = @service.post_update_disk_metadata(@disk_id, metadata).body
end
@service.process_task(@task)
tests("#get_disk_metadata(#{@disk_id})") do
tests('response format').data_matches_schema(VcloudDirector::Compute::Schema::METADATA_TYPE) do
@metadata = @service.get_disk_metadata(@disk_id).body
end
tests('TypedValue') do
pending if @service.api_version.to_f < 5.1
tests('key').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key'}
entry[:TypedValue][:xsi_type]
end
tests('boolean').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean'}
entry[:TypedValue][:xsi_type]
end
tests('datetime').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime'}
entry[:TypedValue][:xsi_type]
end
tests('number').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number'}
entry[:TypedValue][:xsi_type]
end
tests('key-update').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key-update'}
entry[:TypedValue][:xsi_type]
end
tests('boolean-update').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean-update'}
entry[:TypedValue][:xsi_type]
end
tests('datetime-update').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime-update'}
entry[:TypedValue][:xsi_type]
end
tests('number-update').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number-update'}
entry[:TypedValue][:xsi_type]
end
end
end
end
tests("#get_vms_disk_attached_to(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::VMS_TYPE) do
pending if Fog.mocking?
@service.get_vms_disk_attached_to(@disk_id).body
end
tests("#delete_disk(#{@disk_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
@task = @service.delete_disk(@disk_id).body
end
@service.process_task(@task)
end
tests('Disk no longer exists') do
tests("#get_disk(#{@disk_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_disk(@disk_id)
end
tests("#get_disk_owner(#{@disk_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_disk_owner(@disk_id)
end
tests("#get_disk_metadata(#{@disk_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
pending if Fog.mocking?
@service.get_disk_metadata(@disk_id)
end
tests("#delete_disk(#{@disk_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.delete_disk(@disk_id)
end
end
tests('#get_disks_from_query') do
pending if Fog.mocking?
@ -15,9 +175,4 @@ Shindo.tests('Compute::VcloudDirector | disk requests', ['vclouddirector']) do
end
end
tests('Retrieve non-existent Disk').raises(Fog::Compute::VcloudDirector::Forbidden) do
pending if Fog.mocking?
@service.get_disk('00000000-0000-0000-0000-000000000000')
end
end

View file

@ -11,11 +11,15 @@ class VcloudDirector
end
def self.current_org(service)
service.get_organization(current_org_id(service)).body
end
def self.current_org_id(service)
session = service.get_current_session.body
link = session[:Link].detect do |l|
l[:type] == 'application/vnd.vmware.vcloud.org+xml'
end
service.get_organization(link[:href].split('/').last).body
link[:href].split('/').last
end
def self.first_vdc_id(org)

View file

@ -1,31 +1,60 @@
Shindo.tests('Compute::VcloudDirector | media requests', ['vclouddirector']) do
@service = Fog::Compute::VcloudDirector.new
tests('error conditions') do
tests('#post_upload_media') do
tests('Invalid image_type').raises(Fog::Compute::VcloudDirector::BadRequest) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'isox', 0)
end
tests('Invalid size').raises(Fog::Compute::VcloudDirector::BadRequest) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'iso', -1)
end
end
tests('Upload to non-existent vDC').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'iso', 0)
end
tests('Retrieve non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_media('00000000-0000-0000-0000-000000000000')
end
tests('Retrieve owner of non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_media_owner('00000000-0000-0000-0000-000000000000')
end
tests('Delete non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.delete_media('00000000-0000-0000-0000-000000000000')
end
end
@org = VcloudDirector::Compute::Helper.current_org(@service)
@media_name = VcloudDirector::Compute::Helper.test_name
tests('Upload and manipulate a media object') do
pending if Fog.mocking?
File.open(VcloudDirector::Compute::Helper.fixture('test.iso'), 'rb') do |iso|
tests('#post_upload_media').data_matches_schema(VcloudDirector::Compute::Schema::MEDIA_TYPE) do
@vdc_id = VcloudDirector::Compute::Helper.first_vdc_id(@org)
@media = @service.post_upload_media(@vdc_id, @media_name, 'iso', iso.size).body
@size = File.size(iso.path)
@media = @service.post_upload_media(@vdc_id, @media_name, 'iso', @size).body
end
tests('media object has an upload link').returns(true) do
@link = @media[:Files][:File][:Link]
tests('media object has exactly one file').returns(true) do
@media[:Files][:File].size == 1
end
tests('media object file has an upload link').returns(true) do
@link = @media[:Files][:File].first[:Link]
@link[:rel] == 'upload:default'
end
headers = {
'Content-Length' => iso.size,
'Content-Type' => 'application/octet-stream',
'x-vcloud-authorization' => @service.vcloud_token
}
Excon.put(
@link[:href], :body => iso.read, :expects => 200, :headers => headers
)
unless Fog.mocking?
headers = {
'Content-Length' => @size,
'Content-Type' => 'application/octet-stream',
'x-vcloud-authorization' => @service.vcloud_token
}
Excon.put(
@link[:href], :body => iso.read, :expects => 200, :headers => headers
)
end
@service.process_task(@media[:Tasks][:Task])
@media_id = @media[:href].split('/').last
@ -35,81 +64,85 @@ Shindo.tests('Compute::VcloudDirector | media requests', ['vclouddirector']) do
end
tests("media[:name]").returns(@media_name) { @media[:name] }
tests("media[:imageType]").returns('iso') { @media[:imageType] }
tests("media[:size]").returns(iso.size) { @media[:size].to_i }
tests("media[:size]").returns(@size) { @media[:size].to_i }
tests("#get_media_owner(#{@media_id})").data_matches_schema(VcloudDirector::Compute::Schema::OWNER_TYPE) do
@owner = @service.get_media_owner(@media_id).body
end
tests("owner[:User][:name]").returns(@service.user_name) { @owner[:User][:name] }
tests("#put_media_metadata_item_metadata(#{@media_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
@task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-key', 'fog-test-value').body
end
@service.process_task(@task)
tests('media metadata') do
pending if Fog.mocking?
tests("#put_media_metadata_item_metadata(#{@media_id})") do
tests("#put_media_metadata_item_metadata(Boolean)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-boolean', true).body
@service.process_task(task)
tests("#put_media_metadata_item_metadata(#{@media_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
@task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-key', 'fog-test-value').body
end
tests("#put_media_metadata_item_metadata(DateTime)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-datetime', DateTime.now).body
@service.process_task(task)
end
tests("#put_media_metadata_item_metadata(Number)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-number', 111).body
@service.process_task(task)
end
end
@service.process_task(@task)
tests("#post_update_media_metadata(#{@media_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
metadata = {
'fog-test-key-update' => 'fog-test-value-update',
'fog-test-boolean-update' => false,
'fog-test-datetime-update' => DateTime.now,
'fog-test-number-update' => 222
}
@task = @service.post_update_media_metadata(@media_id, metadata).body
end
@service.process_task(@task)
tests("#get_media_metadata(#{@media_id})") do
tests('response format').data_matches_schema(VcloudDirector::Compute::Schema::METADATA_TYPE) do
@metadata = @service.get_media_metadata(@media_id).body
tests("#put_media_metadata_item_metadata(#{@media_id})") do
tests("#put_media_metadata_item_metadata(Boolean)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-boolean', true).body
@service.process_task(task)
end
tests("#put_media_metadata_item_metadata(DateTime)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-datetime', DateTime.now).body
@service.process_task(task)
end
tests("#put_media_metadata_item_metadata(Number)").returns(true) do
task = @service.put_media_metadata_item_metadata(@media_id, 'fog-test-number', 111).body
@service.process_task(task)
end
end
tests('TypedValue') do
pending if @service.api_version.to_f < 5.1
tests('key').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key'}
entry[:TypedValue][:xsi_type]
tests("#post_update_media_metadata(#{@media_id})").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
metadata = {
'fog-test-key-update' => 'fog-test-value-update',
'fog-test-boolean-update' => false,
'fog-test-datetime-update' => DateTime.now,
'fog-test-number-update' => 222
}
@task = @service.post_update_media_metadata(@media_id, metadata).body
end
@service.process_task(@task)
tests("#get_media_metadata(#{@media_id})") do
tests('response format').data_matches_schema(VcloudDirector::Compute::Schema::METADATA_TYPE) do
@metadata = @service.get_media_metadata(@media_id).body
end
tests('boolean').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean'}
entry[:TypedValue][:xsi_type]
end
tests('datetime').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime'}
entry[:TypedValue][:xsi_type]
end
tests('number').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number'}
entry[:TypedValue][:xsi_type]
end
tests('key-update').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key-update'}
entry[:TypedValue][:xsi_type]
end
tests('boolean-update').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean-update'}
entry[:TypedValue][:xsi_type]
end
tests('datetime-update').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime-update'}
entry[:TypedValue][:xsi_type]
end
tests('number-update').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number-update'}
entry[:TypedValue][:xsi_type]
tests('TypedValue') do
pending if @service.api_version.to_f < 5.1
tests('key').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key'}
entry[:TypedValue][:xsi_type]
end
tests('boolean').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean'}
entry[:TypedValue][:xsi_type]
end
tests('datetime').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime'}
entry[:TypedValue][:xsi_type]
end
tests('number').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number'}
entry[:TypedValue][:xsi_type]
end
tests('key-update').returns('MetadataStringValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-key-update'}
entry[:TypedValue][:xsi_type]
end
tests('boolean-update').returns('MetadataBooleanValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-boolean-update'}
entry[:TypedValue][:xsi_type]
end
tests('datetime-update').returns('MetadataDateTimeValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-datetime-update'}
entry[:TypedValue][:xsi_type]
end
tests('number-update').returns('MetadataNumberValue') do
entry = @metadata[:MetadataEntry].detect {|e| e[:Key] == 'fog-test-number-update'}
entry[:TypedValue][:xsi_type]
end
end
end
end
@ -131,7 +164,6 @@ Shindo.tests('Compute::VcloudDirector | media requests', ['vclouddirector']) do
end
tests('Media item no longer exists') do
pending if Fog.mocking?
tests("#get_media(#{@media_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_media(@media_id)
end
@ -139,6 +171,7 @@ Shindo.tests('Compute::VcloudDirector | media requests', ['vclouddirector']) do
@service.get_media_owner(@media_id)
end
tests("#get_media_metadata(#{@media_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
pending if Fog.mocking?
@service.get_media_metadata(@media_id)
end
tests("#delete_media(#{@media_id})").raises(Fog::Compute::VcloudDirector::Forbidden) do
@ -159,27 +192,4 @@ Shindo.tests('Compute::VcloudDirector | media requests', ['vclouddirector']) do
end
end
tests('Invalid image_type').raises(Fog::Compute::VcloudDirector::BadRequest) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'isox', 0)
end
tests('Invalid size').raises(Fog::Compute::VcloudDirector::BadRequest) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'iso', -1)
end
tests('Upload to non-existent vDC').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.post_upload_media('00000000-0000-0000-0000-000000000000', 'test.iso', 'iso', 0)
end
tests('Retrieve non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_media('00000000-0000-0000-0000-000000000000')
end
tests('Retrieve owner of non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_media_owner('00000000-0000-0000-0000-000000000000')
end
tests('Delete non-existent Media').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.delete_media('00000000-0000-0000-0000-000000000000')
end
end

View file

@ -81,7 +81,7 @@ class VcloudDirector
# Represents a list of files to be transferred (uploaded or downloaded).
FILES_LIST_TYPE = {
:File => FILE_TYPE
:File => [FILE_TYPE]
}
# Container for query result sets.
@ -123,7 +123,7 @@ class VcloudDirector
:status => Fog::Nullable::String,
:Description => Fog::Nullable::String,
# :Tasks => TASKS_IN_PROGRESS_TYPE,
# :Files => FILES_LIST_TYPE
# :Files => FILES_LIST_TYPE
})
# Container for references to ResourceEntity objects in this vDC.
@ -620,6 +620,19 @@ class VcloudDirector
:RoleReferences => ROLE_REFERENCES_TYPE,
:Networks => NETWORKS_TYPE
})
# Represents a named disk.
DISK_TYPE = RESOURCE_ENTITY_TYPE.merge({
:busSubType => Fog::Nullable::String,
:busType => Fog::Nullable::String,
:size => String,
:StorageProfile => REFERENCE_TYPE,
:Owner => OWNER_TYPE
})
VMS_TYPE = RESOURCE_TYPE.merge({
:VmReference => [REFERENCE_TYPE]
})
end
end
end

View file

@ -2,6 +2,20 @@ Shindo.tests('Compute::VcloudDirector | task requests', ['vclouddirector']) do
@service = Fog::Compute::VcloudDirector.new
tests('error conditions') do
tests('retrieve non-existent TasksList').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_task_list('00000000-0000-0000-0000-000000000000')
end
tests('retrieve non-existent Task').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_task('00000000-0000-0000-0000-000000000000')
end
tests('cancel non-existent Task').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.post_cancel_task('00000000-0000-0000-0000-000000000000')
end
end
@org_id = VcloudDirector::Compute::Helper.current_org_id(@service)
tests('#get_task_list').data_matches_schema(VcloudDirector::Compute::Schema::TASKS_LIST_TYPE) do
session = @service.get_current_session.body
org_href = session[:Link].detect {|l| l[:type] == 'application/vnd.vmware.vcloud.org+xml'}[:href]
@ -9,12 +23,13 @@ Shindo.tests('Compute::VcloudDirector | task requests', ['vclouddirector']) do
@tasks_list = @service.get_task_list(@org_uuid).body
end
tests('retrieve non-existent TasksList').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_task_list('00000000-0000-0000-0000-000000000000')
end
tests('retrieve non-existent Task').raises(Fog::Compute::VcloudDirector::Forbidden) do
@service.get_task('00000000-0000-0000-0000-000000000000')
tests('each task in the task list') do
@tasks_list[:Task].each do |task|
task_id = task[:href].split('/').last
tests("#get_task(#{task_id}").data_matches_schema(VcloudDirector::Compute::Schema::TASK_TYPE) do
@service.get_task(task_id).body
end
end
end
end