diff --git a/fog.gemspec b/fog.gemspec index 9aed3df1e..d5f2433c3 100644 --- a/fog.gemspec +++ b/fog.gemspec @@ -50,7 +50,6 @@ Gem::Specification.new do |s| s.add_dependency('net-ssh', '>=2.0.23') s.add_dependency('nokogiri', '>=1.4.4') s.add_dependency('ruby-hmac') - s.add_dependency('xml-simple') ## List your development dependencies here. Development dependencies are ## those that are only needed during development diff --git a/lib/fog/compute/parsers/voxel/devices_list.rb b/lib/fog/compute/parsers/voxel/devices_list.rb new file mode 100644 index 000000000..304f857fb --- /dev/null +++ b/lib/fog/compute/parsers/voxel/devices_list.rb @@ -0,0 +1,70 @@ +module Fog + module Parsers + module Voxel + module Compute + + class DevicesList < Fog::Parsers::Base + + def reset + @device = {} + @response = { :stat => nil, :devices => [] } + @in_accessmethod = false + @in_storage = false + end + + def start_element(name, attrs = []) + super + + case name + when 'device' + @device = { + :id => attr_value('id', attrs).to_i, + :name => attr_value('label', attrs) + } + when 'type' + @device[:type] = attr_value('id', attrs) + when 'rsp' + @response[:stat] = attr_value('stat', attrs) + when 'ipassignment' + @device[:addresses] ||= {} + @current_type = attr_value('type', attrs).to_sym + @device[:addresses][@current_type] = [] + when "facility" + @device[:facility] = attr_value('code', attrs) + when "storage" + @in_storage = true + when "accessmethod" + if attr_value('type', attrs) == "admin" + @in_accessmethod = true + end + end + end + + def end_element(name) + case name + when 'device' + @response[:devices] << @device + @device = {} + when 'cores' + @device[:processing_cores] = @value.to_i + when 'ipassignment' + @device[:addresses][@current_type] << @value + when "size" + if @in_storage + @device[:disk_size] = @value.to_i + @in_storage = false + end + when "password" + if @in_accessmethod + @device[:password] = @value + @in_accessmethod = false + end + end + end + + end + + end + end + end +end diff --git a/lib/fog/compute/parsers/voxel/images_list.rb b/lib/fog/compute/parsers/voxel/images_list.rb new file mode 100644 index 000000000..32d061f75 --- /dev/null +++ b/lib/fog/compute/parsers/voxel/images_list.rb @@ -0,0 +1,40 @@ +module Fog + module Parsers + module Voxel + module Compute + + class ImagesList < Fog::Parsers::Base + + def reset + @image = {} + @response = { :stat => nil, :images => [] } + end + + def start_element(name, attrs = []) + super + + case name + when 'image' + @image = { + :id => attr_value('id', attrs).to_i, + :name => attr_value('summary', attrs) + } + when 'rsp' + @response[:stat] = attr_value('stat', attrs) + end + end + + def end_element(name) + case name + when 'image' + @response[:images] << @image + @image = {} + end + end + + end + + end + end + end +end diff --git a/lib/fog/compute/parsers/voxel/voxcloud_create.rb b/lib/fog/compute/parsers/voxel/voxcloud_create.rb new file mode 100644 index 000000000..e24277c30 --- /dev/null +++ b/lib/fog/compute/parsers/voxel/voxcloud_create.rb @@ -0,0 +1,33 @@ +module Fog + module Parsers + module Voxel + module Compute + + class VoxcloudCreate < Fog::Parsers::Base + + def reset + @response = { :stat => nil, :device => {} } + end + + def start_element(name, attrs = []) + super + + case name + when 'rsp' + @response[:stat] = attr_value('stat', attrs) + end + end + + def end_element(name) + case name + when 'id' + @response[:device][:id] = @value + end + end + + end + + end + end + end +end diff --git a/lib/fog/compute/parsers/voxel/voxcloud_delete.rb b/lib/fog/compute/parsers/voxel/voxcloud_delete.rb new file mode 100644 index 000000000..ebfa2a016 --- /dev/null +++ b/lib/fog/compute/parsers/voxel/voxcloud_delete.rb @@ -0,0 +1,28 @@ +module Fog + module Parsers + module Voxel + module Compute + + class VoxcloudDelete < Fog::Parsers::Base + + def reset + @response = { :stat => nil } + end + + def start_element(name, attrs = []) + super + + case name + when 'rsp' + @response[:stat] = attr_value('stat', attrs) + when 'err' + @response[:error] = attr_value('msg', attrs) + end + end + + end + + end + end + end +end diff --git a/lib/fog/compute/parsers/voxel/voxcloud_status.rb b/lib/fog/compute/parsers/voxel/voxcloud_status.rb new file mode 100644 index 000000000..31f932a74 --- /dev/null +++ b/lib/fog/compute/parsers/voxel/voxcloud_status.rb @@ -0,0 +1,39 @@ +module Fog + module Parsers + module Voxel + module Compute + class VoxcloudStatus < Fog::Parsers::Base + def reset + @response = { :stat => nil, :devices => [] } + @device = {} + end + + def start_element(name, attrs = []) + super + + case name + when 'rsp' + @response[:stat] = attr_value('stat', attrs) + when 'err' + @response[:error] = attr_value('msg', attrs) + when 'device' + @device = {} + end + end + + def end_element(name) + case name + when 'device' + @response[:devices] << @device + @device = {} + when 'id' + @device[:id] = @value + when 'status' + @device[:status] = @value + end + end + end + end + end + end +end diff --git a/lib/fog/compute/requests/voxel/devices_list.rb b/lib/fog/compute/requests/voxel/devices_list.rb index f5830e4a2..b1f29e0d4 100644 --- a/lib/fog/compute/requests/voxel/devices_list.rb +++ b/lib/fog/compute/requests/voxel/devices_list.rb @@ -10,28 +10,19 @@ module Fog options[:device_id] = device_id end - data = request("voxel.devices.list", options) + data = request("voxel.devices.list", options, Fog::Parsers::Voxel::Compute::DevicesList.new).body - if data['stat'] == 'fail' + if data[:stat] == 'fail' raise Fog::Voxel::Compute::NotFound - elsif data['devices'].empty? + elsif data[:devices].empty? [] else - devices = data['devices']['device'] - devices = [ devices ] if devices.is_a?(Hash) + devices = data[:devices] ## TODO find both voxserver and voxcloud devices - devices.select { |d| d['type']['id'] == '3' }.map do |device| - { :id => device['id'].to_i, - :name => device['label'], - :image_id => 0, - :addresses => { - :public => device['ipassignments']['ipassignment'].select { |i| i['type'] == "frontend" }.first['content'], - :private => device['ipassignments']['ipassignment'].select { |i| i['type'] == "backend" }.first['content'] }, - :processing_cores => device['processor']['cores'].to_i, - :facility => device['location']['facility']['code'], - :disk_size => device['storage']['drive']['size'].to_i, - :password => device['accessmethods']['accessmethod'].select { |am| am['type'] == 'admin' }.first['password'] } + devices.select { |d| d[:type] == '3' }.map do |device| + device.delete(:type) + device.merge( :image_id => 0 ) end end end diff --git a/lib/fog/compute/requests/voxel/images_list.rb b/lib/fog/compute/requests/voxel/images_list.rb index b2b51451c..db079cd4e 100644 --- a/lib/fog/compute/requests/voxel/images_list.rb +++ b/lib/fog/compute/requests/voxel/images_list.rb @@ -10,13 +10,10 @@ module Fog options[:image_id] = image_id end - data = request("voxel.images.list", options) + data = request("voxel.images.list", options, Fog::Parsers::Voxel::Compute::ImagesList.new).body - if data['stat'] == "ok" - images = data['images']['image'] - images = [ images ] if images.is_a?(Hash) - - images.map { |i| { :id => i['id'].to_i, :name => i['summary'] } } + if data[:stat] == "ok" + data[:images] else raise Fog::Voxel::Compute::NotFound end diff --git a/lib/fog/compute/requests/voxel/voxcloud_create.rb b/lib/fog/compute/requests/voxel/voxcloud_create.rb index 504257586..1068c40e4 100644 --- a/lib/fog/compute/requests/voxel/voxcloud_create.rb +++ b/lib/fog/compute/requests/voxel/voxcloud_create.rb @@ -11,13 +11,13 @@ module Fog options.delete(:password) end - data = request("voxel.voxcloud.create", options) + data = request("voxel.voxcloud.create", options, Fog::Parsers::Voxel::Compute::VoxcloudCreate.new).body - unless data['stat'] == 'ok' + unless data[:stat] == 'ok' raise Fog::Voxel::Compute::Error, "Error from Voxel hAPI: #{data['err']['msg']}" end - devices_list(data['device']['id']) + devices_list(data[:device][:id]) end end @@ -30,7 +30,7 @@ module Fog @data[:servers].push( options.merge( { :password => "CHANGEME", :id => device_id, - :addresses => { :private => '0.0.0.0', :public => '0.0.0.0' } + :addresses => { :backend => [ '0.0.0.0' ], :frontend => [ '0.0.0.0' ] } } ) ) devices_list(device_id) end diff --git a/lib/fog/compute/requests/voxel/voxcloud_delete.rb b/lib/fog/compute/requests/voxel/voxcloud_delete.rb index be255d627..0bf154b8f 100644 --- a/lib/fog/compute/requests/voxel/voxcloud_delete.rb +++ b/lib/fog/compute/requests/voxel/voxcloud_delete.rb @@ -3,20 +3,20 @@ module Fog class Compute class Real def voxcloud_delete( device_id ) - options = { :device_id => device_id } - data = request("voxel.voxcloud.delete", options ) + options = { :device_id => device_id } + data = request( "voxel.voxcloud.delete", { :device_id => device_id }, Fog::Parsers::Voxel::Compute::VoxcloudDelete.new ).body - unless data['stat'] == 'ok' - raise Fog::Voxel::Compute::NotFound, "Error from Voxel hAPI: #{data['err']['msg']}" + unless data[:stat] == 'ok' + raise Fog::Voxel::Compute::NotFound, "Error from Voxel hAPI: #{data[:error]}" end - true - end + true + end end class Mock def voxcloud_delete( device_id ) - device = @data[:servers].select { |d| d[:id] == device_id } + device = @data[:servers].select { |d| d[:id] == device_id } if device.empty? raise Fog::Voxel::Compute::NotFound @@ -25,7 +25,7 @@ module Fog @data[:statuses].delete(device_id) true end - end + end end end end diff --git a/lib/fog/compute/requests/voxel/voxcloud_status.rb b/lib/fog/compute/requests/voxel/voxcloud_status.rb index 0e4aa3899..a0aff84d4 100644 --- a/lib/fog/compute/requests/voxel/voxcloud_status.rb +++ b/lib/fog/compute/requests/voxel/voxcloud_status.rb @@ -9,20 +9,14 @@ module Fog options[:device_id] = device_id end - data = request("voxel.voxcloud.status", options) + data = request("voxel.voxcloud.status", options, Fog::Parsers::Voxel::Compute::VoxcloudStatus.new ).body - if data['stat'] == 'fail' - raise Fog::Voxel::Compute::NotFound - else - if data['devices']['device'].is_a?(Hash) - devices = [ data['devices']['device'] ] - else - devices = data['devices']['device'] - end - - devices.map { |d| { :id => d['id'], :status => d['status'] } } - end - end + if data[:stat] == 'fail' + raise Fog::Voxel::Compute::NotFound + else + data[:devices] + end + end end class Mock @@ -42,14 +36,14 @@ module Fog if device_id.nil? @data[:statuses].map { |status| { :id => status[0], :status => status[1] } } - else + else if @data[:statuses].has_key?(device_id) - [ { :id => device_id, :status => @data[:statuses][device_id] } ] + [ { :id => device_id, :status => @data[:statuses][device_id] } ] else raise Fog::Voxel::Compute::NotFound end - end - end + end + end end end end diff --git a/lib/fog/compute/voxel.rb b/lib/fog/compute/voxel.rb index 1565fe9e3..522d5e0e4 100644 --- a/lib/fog/compute/voxel.rb +++ b/lib/fog/compute/voxel.rb @@ -3,6 +3,7 @@ module Fog class Compute < Fog::Service requires :voxel_api_key, :voxel_api_secret + recognizes :provider, :host, :port, :scheme, :persistent model_path 'fog/compute/models/voxel' model :image @@ -22,13 +23,26 @@ module Fog def self.data @data ||= { - :last_modified => { :servers => {}, :statuses => {}, :images => {} }, - :servers => [], - :statuses => {}, - :images => [ - { :id => 1, :name => "CentOS 5 x64" }, - { :id => 2, :name => "Ubuntu 10.04 LTS x64" } ] - } + :last_modified => { :servers => {}, :statuses => {}, :images => {} }, + :servers => [], + :statuses => {}, + :images => [ + {:id=>1, :name=>"CentOS 4, 32-bit, base install"}, + {:id=>2, :name=>"CentOS 4, 64-bit, base install"}, + {:id=>3, :name=>"CentOS 5, 32-bit, base install"}, + {:id=>4, :name=>"CentOS 5, 64-bit, base install"}, + {:id=>7, :name=>"Fedora 10, 32-bit, base install"}, + {:id=>8, :name=>"Fedora 10, 64-bit, base install"}, + {:id=>10, :name=>"OpenSUSE 11, 64-bit, base install"}, + {:id=>11, :name=>"Debian 5.0 \"lenny\", 32-bit, base install"}, + {:id=>12, :name=>"Debian 5.0 \"lenny\", 64-bit, base install"}, + {:id=>13, :name=>"Ubuntu 8.04 \"Hardy\", 32-bit, base install"}, + {:id=>14, :name=>"Ubuntu 8.04 \"Hardy\", 64-bit, base install"}, + {:id=>15, :name=>"Voxel Server Environment (VSE), 32-bit, base install"}, + {:id=>16, :name=>"Voxel Server Environment (VSE), 64-bit, base install"}, + {:id=>32, :name=>"Pantheon Official Mercury Stack for Drupal (based on VSE/64)"}, + {:id=>55, :name=>"Ubuntu 10.04 \"Lucid\", 64-bit, base install"} ] + } end def self.reset_data(keys=data.keys) @@ -49,21 +63,30 @@ module Fog require 'time' require 'digest/md5' + require 'fog/compute/parsers/voxel/images_list' + require 'fog/compute/parsers/voxel/devices_list' + require 'fog/compute/parsers/voxel/voxcloud_create' + require 'fog/compute/parsers/voxel/voxcloud_status' + require 'fog/compute/parsers/voxel/voxcloud_delete' + @voxel_api_key = options[:voxel_api_key] @voxel_api_secret = options[:voxel_api_secret] + @host = options[:host] || "api.voxel.net" + @port = options[:port] || 443 + @scheme = options[:scheme] || 'https' + @persistent = options[:persistent] || false + Excon.ssl_verify_peer = false - @connection = Fog::Connection.new("https://api.voxel.net:443") + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent) end - def request(method_name, options = {}) + def request(method_name, options = {}, parser) begin options.merge!( { :method => method_name, :timestamp => Time.now.xmlschema, :key => @voxel_api_key } ) options[:api_sig] = create_signature(@voxel_api_secret, options) - require 'xmlsimple' - response = @connection.request( :host => "api.voxel.net", :path => "/version/1.0/", :query => options ) - XmlSimple.xml_in( response.body, 'ForceArray' => false ) + @connection.request( :path => "/version/1.0/", :method => "POST", :query => options, :parser => parser ) rescue Excon::Errors::HTTPStatusError => error raise case error when Excon::Errors::NotFound diff --git a/tests/compute/requests/voxel/server_tests.rb b/tests/compute/requests/voxel/server_tests.rb index 9a2b6dad0..0edd1414c 100644 --- a/tests/compute/requests/voxel/server_tests.rb +++ b/tests/compute/requests/voxel/server_tests.rb @@ -8,15 +8,15 @@ Shindo.tests('Voxel::Compute | server requests', ['voxel']) do :facility => String, :disk_size => Integer, :addresses => { - :private => String, - :public => String + :frontend => [ String ], + :backend => [ String ] }, :password => String } tests('success') do - @server_id = nil + @server_id = nil tests('#voxcloud_create( :name => "fog.test", :disk_size => 10, :processing_cores => 1, :image_id => 16, :facility => "LDJ1" )').formats([@server_format]) do data = Voxel[:compute].voxcloud_create( :name => "fog.test", :disk_size => 10, :processing_cores => 1, :image_id => 16, :facility => "LDJ1" )