--- .../lib/deltacloud/drivers/gogrid/gogrid_client.rb | 45 ++++ .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb | 220 +++++++++++--------- .../drivers/gogrid/gogrid_mock_driver.rb | 62 ------ .../lib/deltacloud/drivers/gogrid/gogrid_test.rb | 73 ------- 4 files changed, 164 insertions(+), 236 deletions(-) create mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_client.rb delete mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb delete mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_test.rb
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb new file mode 100644 index 0000000..70e0cfc --- /dev/null +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb @@ -0,0 +1,45 @@ +require 'digest/md5' +require 'cgi' +require 'open-uri' +require 'json' + +class GoGridClient + + def initialize(server='https://api.gogrid.com/api', + apikey='YOUR API KEY', + secret='YOUR SHARED SECRET', + format='json', + version='1.4') + @server = server + @secret = secret + @default_params = {'format'=>format, 'v'=>version,'api_key' => apikey} + end + + def getRequestURL(method,params) + requestURL = @server+'/'+method+'?' + call_params = @default_params.merge(params) + call_params['sig']=getSignature(@default_params['api_key'],@secret) + requestURL = requestURL+encode_params(call_params) + end + + def getSignature(key,secret) + Digest::MD5.hexdigest(key+secret+"%.0f"%Time.new.to_f) + end + + def sendAPIRequest(method,params={}) + open(getRequestURL(method,params)).read + end + + def request(method, params={}) + begin + JSON::parse(sendAPIRequest(method, params)) + rescue Exception => e + STDERR.puts("ERROR: #{e.message}") + end + end + + def encode_params(params) + params.map {|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&") + end + +end diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb index 817e4d3..6c6ac67 100644 --- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb @@ -16,7 +16,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA require 'deltacloud/base_driver' -require 'right_gogrid' +require 'deltacloud/drivers/gogrid/gogrid_client' +require 'ap' module Deltacloud module Drivers @@ -62,168 +63,185 @@ class GogridDriver < Deltacloud::BaseDriver # The only valid option for flavors is server RAM for now def flavors(credentials, opts=nil) - results = [] + flavors = [] safely do - gg=new_client(credentials) - results = gg.list_common_lookup('server.ram').collect do |sr| - Flavor.new(:id => sr['description'].downcase.tr(' ', '-'), :memory => sr['name'], :architecture => 'i386', :storage => '10') + flavors=new_client(credentials).request('common/lookup/list', { 'lookup' => 'server.ram' })['list'].collect do |flavor| + convert_flavor(flavor) end end - return results if ( opts.nil? ) - results = filter_on( results, :id, opts ) - results = filter_on( results, :architecture, opts ) - results + flavors = filter_on( flavors, :id, opts ) + flavors = filter_on( flavors, :architecture, opts ) + flavors end def images(credentials, opts=nil) - images = [] - safely do - gg=new_client(credentials) - images = gg.list_images.collect { |image| convert_image(image, credentials.user) } + imgs = [] + if opts and opts[:id] + safely do + imgs = [convert_image(new_client(credentials).request('grid/image/get', { 'id' => opts[:id] })['list'].first)] + end + else + safely do + imgs = new_client(credentials).request('grid/image/list', { 'state' => 'Available'})['list'].collect do |image| + convert_image(image, credentials.user) + end + end end - images = filter_on( images, :architecture, opts ) - images.sort_by{|e| [e.owner_id,e.description]} + imgs = filter_on( imgs, :architecture, opts ) + imgs.sort_by{|e| [e.owner_id, e.description]} end - # gogrid doesn't supports realm yet (it's US/CA from whois) def realms(credentials, opts=nil) - [Realm.new({ - :id => "us", - :name => "United States", - :state => "AVAILABLE" - } )] + safely do + new_client(credentials).request('common/lookup/list', { 'lookup' => 'image.type' })['list'].collect do |realm| + convert_realm(realm) + end + end end - # Name consist from credentials name and timestamp - # def create_instance(credentials, image_id, opts=nil) - flavor_id =(opts && opts[:flavor_id]) ? opts[:flavor_id] : 'server-with-512mb-ram' - # TODO: Fix naming here (IDEA: add 'name' to instance model) - name = 'Node '+Time.now.to_i.to_s + flavor_id = opts[:flavor_id] || '1' + name = (opts[:name] && opts[:name]!='') ? opts[:name] : get_random_instance_name safely do - grid = new_client(credentials) - flavor = flavors(credentials).select { |f| f.id.eql?(flavor_id) }.first - image = images(credentials).select { |i| i.id.eql?(image_id) }.first - return convert_srv_to_instance(grid.add_server(name, image.name, flavor.memory, get_free_ip(credentials), image.description), flavor_id) + convert_instance(new_client(credentials).request('grid/server/add', { + 'name' => name, + 'image' => image_id, + 'server.ram' => flavor_id, + 'ip' => get_next_free_ip(credentials) + })['list'].first, credentials.user) end end def instances(credentials, opts=nil) - safely do - gg = new_client(credentials) - instances = gg.list_servers.collect { |server| convert_srv_to_instance(server, convert_ram_to_flavor(server['ram']['name']))} - instances = filter_on( instances, :id, opts ) - instances = filter_on( instances, :state, opts ) - return instances + instances = [] + if opts and opts[:id] + safely do + instance = new_client(credentials).request('grid/server/get', { 'id' => opts[:id]})['list'].first + instances = [convert_instance(instance, credentials.user)] + end + else + safely do + instances = new_client(credentials).request('grid/server/list')['list'].collect do |instance| + convert_instance(instance, credentials.user) + end + end end + instances = filter_on( instances, :state, opts ) + instances end def reboot_instance(credentials, id) safely do - gg = new_client(credentials) - return gg.gogrid_power_server(:power => 'restart', :name => id) + new_client(credentials).request('grid/server/power', { 'id' => id, 'power' => 'reboot'}) end end def stop_instance(credentials, id) safely do - gg = new_client(credentials) - return gg.gogrid_power_server(:power => 'off', :name => id) + new_client(credentials).request('grid/server/power', { 'id' => id, 'power' => 'off'}) end end def destroy_instance(credentials, id) safely do - gg = new_client(credentials) - gg.gogrid_delete_server(:name => id) + new_client(credentials).request('grid/server/delete', { 'id' => id}) end end - define_instance_states do - start.to( :pending ) .automatically - pending.to( :running ) .automatically - pending.to( :stopping ) .on( :start ) - pending.to( :stopped ) .automatically - stopped.to( :running ) .on( :start ) - running.to( :running ) .on( :reboot ) - running.to( :stopping ) .on( :stop ) - shutting_down.to( :stopped ) .automatically - stopped.to( :finish ) .automatically + start.to( :pending ) .automatically + pending.to( :running ) .automatically + running.to( :stopped ) .on( :stop ) + stopped.to( :running ) .on( :start ) + stopped.to( :finish ) .automatically end private + def new_client(credentials) + GoGridClient.new('https://api.gogrid.com/api', credentials.user, credentials.password) + end + def convert_image(gg_image, owner_id=nil) Image.new( { :id=>gg_image['id'], - :name => gg_image['name'], - :description=>gg_image['friendlyName'], - :owner_id=>owner_id, + :name => gg_image['friendlyName'], + :description=> convert_description(gg_image), + :owner_id=>gg_image['owner']['name'], :architecture=>convert_arch(gg_image['description']), } ) end - def new_client(credentials) - Rightscale::Gogrid.new(credentials.user, credentials.password) + def convert_description(image) + if image['price'].eql?(0) + image['description'] + else + "#{image['description']} (#{image['price']}$)" + end + end + + def convert_flavor(flavor) + Flavor.new( + :id => flavor['id'], + :architecture => 'x86', + :memory => flavor['name'].tr('G', ''), + :storage => '1' + ) + end + + def convert_realm(realm) + Realm.new( + :id => realm['id'], + :name => realm['name'], + :state => :unlimited, + :storage => :unlimited + ) end - # Determine architecture from description def convert_arch(description) - return 'x86_64' if description.include?('64-bit') - return 'i386' - end - - # NOTE: GoGrid doesn't use an ID for your instances. Instead they using a 'friendly name' - def convert_srv_to_instance(srv, flavor_id) - server = srv.kind_of?(Hash) ? srv : srv.first - Instance.new({ - :id => convert_server_name(server['name']), - :state => convert_server_state(server['state']['name']), - :name => server['description'], - :image_id => server['image']['id'], - :owner_id => 'root', - :realm => 'us', - :public_addresses => server['ip']['ip'], - :private_addresses => nil, - :flavor_id => flavor_id, - :actions => instance_actions_for(server['state']['name']) - }) - end - - def convert_server_state(state) - state.eql?('Off') ? 'STOPPED' : 'RUNNING' + description.include?('64-bit') ? 'x86_64' : 'i386' end - def convert_server_name(name) - name.gsub(/\W/, '_').downcase + def convert_instance(instance, owner_id) + Instance.new( + :id => instance['id'], + :owner_id => owner_id, + :image_id => instance['image']['id'], + :flavor_id => instance['ram']['id'], + :name => instance['name'], + :realm_id => instance['type']['id'], + :state => convert_server_state(instance['state']['name'], instance['id']), + :actions => instance_actions_for(convert_server_state(instance['state']['name'], instance['id'])), + :public_addresses => [ instance['ip']['ip'] ], + :private_addresses => [] + ) end - # We produce flavors from RAM - def convert_ram_to_flavor(ram) - 'server-with-'+ram.downcase+'-ram' + def get_random_instance_name + "Server #{Time.now.to_i.to_s.reverse[0..3]}#{rand(9)}" end - # You need a free IP from subnet you have assigned in case you want to start an instance - def get_free_ip(credentials) - gg=new_client(credentials) - # TODO: I'm not sure about this, they changed API recently and right_gogrid doesn't work well there - ip=gg.list_ips('Unassigned').select {|i| i['public'] }.last - return ip['ip'] if ip and ip['ip'] - return nil + def convert_server_state(state, id) + return 'PENDING' unless id + state.eql?('Off') ? 'STOPPED' : 'RUNNING' + end + + def get_next_free_ip(credentials) + ip = "" + safely do + ip = new_client(credentials).request('grid/ip/list', { + 'ip.type' => '1', + 'ip.state' => '1' + })['list'].first['ip'] + end + return ip end def safely(&block) begin block.call - rescue Rightscale::GogridError => e - if ( e.include?( /Forbidden/ ) ) - raise Deltacloud::AuthException.new - else - e.errors.each do |error| - puts "ERROR #{error.inspect}" - end - end + rescue Exception => e + puts "ERROR: #{e.message}" end end diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb deleted file mode 100644 index c42beb8..0000000 --- a/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Rightscale - class MockGogrid - def initialize(username, password) - end - - def list_common_lookup(type) - case type - when 'server.ram' - load_fixtures_for :flavors - end - end - - def list_images() - load_fixtures_for :images - end - - def list_servers(server_type=nil) - load_fixtures_for :instances - end - - def list_ips(state=nil, type=nil) - load_fixtures_for :ips - end - - def add_server(name, image, ram, ip, description='') - load_fixtures_for :instances - end - - private - - def driver_dir - File::expand_path(File::join(File::dirname(__FILE__), '../../../../features/support/gogrid')) - end - - def fixtures_path - File::expand_path(File::join(driver_dir, 'fixtures')) - end - - def load_fixtures_for(collection) - YAML.load_file(File::join(fixtures_path, "#{collection}.yaml")) - end - - def update_fixtures_for(collection, new_data) - File.open(File::join(fixtures_path, "#{collection}.yaml"), 'w' ) do |out| - YAML.dump(new_data, out) - end - return new_data - end - - end -end - -Deltacloud::Drivers::Gogrid::GogridDriver.class_eval do - alias_method :original_new_client, :new_client - - def new_client(credentials, opts={}) - if credentials.user != 'mockuser' and credentials.password != 'mockpassword' - raise Deltacloud::AuthException.new - end - Rightscale::MockGogrid.new(credentials.user, credentials.password) - end -end diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb deleted file mode 100644 index ab2c5af..0000000 --- a/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'lib/deltacloud/drivers/gogrid/gogrid_driver' -require 'test/unit' - -CREDS = nil - -class GoGridTest < Test::Unit::TestCase - - def test_valid_flavors - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - assert_kind_of Array, gd.flavors(CREDS) - assert_kind_of Flavor, gd.flavors(CREDS).first - end - - def test_valid_images - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - assert_kind_of Array, gd.images(CREDS) - assert_kind_of Image, gd.images(CREDS).first - end - - def test_create_instance - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - images = gd.images(CREDS) - assert_kind_of Array, images - instance = gd.create_instance(CREDS, images.first.id) - assert_kind_of Instance, instance - end - - def test_list_instances - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - instances = gd.instances(CREDS) - assert_kind_of Array, instances - assert_kind_of Instance, instances.first - end - - def test_get_instance - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - instances = gd.instances(CREDS) - if instances.kind_of?(Array) - assert_kind_of Instance, gd.instance(CREDS, { :id => instances.first.id }) - end - end - - def test_list_realms - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - assert_kind_of Array, gd.realms(CREDS) - assert_kind_of Realm, gd.realms(CREDS).first - end - - # This tests fails anyway, because created instance isn't ready for it. - # If you want to really test, if these actions work properly, you could create an instance - # first and change instances.last.id to ID of your instance. In this way tests will work. - - def test_reboot_instance - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - instances = gd.instances(CREDS) - assert_kind_of Hash, gd.reboot_instance(CREDS, instances.last.id) - end - - def test_stop_instance - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - instances = gd.instances(CREDS) - assert_kind_of Hash, gd.stop_instance(CREDS, instances.last.id) - end - - def test_destroy_instance - gd=Deltacloud::Drivers::Gogrid::GogridDriver.new - instances = gd.instances(CREDS) - assert_kind_of Hash, gd.destroy_instance(CREDS, instances.last.id) - end - - - -end -- 1.6.6.2 _______________________________________________ deltacloud-devel mailing list [email protected] https://fedorahosted.org/mailman/listinfo/deltacloud-devel
