Hi,
I hopefully fixed GoGrid driver at all. All functionality should be now working. Cucumber tests and gogrid mock driver included.
- Michal
PS: Cucumber tests will fail in flavors/instances. It's because GoGrid require login to get flavors. Instances are hard to mock, because there is lot of generated stuff which is hard to 'simulate'.
--- .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb | 225 ++++++++++++++++++++ .../lib/deltacloud/drivers/gogrid/gogrid_test.rb | 73 +++++++ 2 files changed, 298 insertions(+), 0 deletions(-) create mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb create mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_test.rb
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb new file mode 100644 index 0000000..1593f79 --- /dev/null +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb @@ -0,0 +1,225 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require 'deltacloud/base_driver' +require 'right_gogrid' + +module Deltacloud + module Drivers + module Gogrid + +class GogridDriver < Deltacloud::BaseDriver + + # Storage capacity is same on all machines (10gb), it could be extended using 'Cloud Storage' + define_hardware_profile('server-with-512mb-ram') do + cpu 2 + memory 0.5 + storage 10 + architecture 'i386' + end + + define_hardware_profile('server-with-1gb-ram') do + cpu 2 + memory 1 + storage 10 + architecture 'i386' + end + + define_hardware_profile('server-with-2gb-ram') do + cpu 2 + memory 2 + storage 10 + architecture 'i386' + end + + define_hardware_profile('server-with-4gb-ram') do + cpu 2 + memory 4 + storage 10 + architecture 'i386' + end + + define_hardware_profile('server-with-8gb-ram') do + cpu 2 + memory 8 + storage 10 + architecture 'i386' + end + + # The only valid option for flavors is server RAM for now + def flavors(credentials, opts=nil) + results = [] + 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') + end + end + return results if ( opts.nil? ) + results = filter_on( results, :id, opts ) + results = filter_on( results, :architecture, opts ) + results + end + + def images(credentials, opts=nil) + images = [] + safely do + gg=new_client(credentials) + images = gg.list_images.collect { |image| convert_image(image, credentials[:name]) } + end + images = filter_on( images, :architecture, opts ) + images.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" + } )] + 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 + 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) + 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 + end + end + + def reboot_instance(credentials, id) + safely do + gg = new_client(credentials) + return gg.gogrid_power_server(:power => 'restart', :name => id) + end + end + + def stop_instance(credentials, id) + safely do + gg = new_client(credentials) + return gg.gogrid_power_server(:power => 'off', :name => id) + end + end + + def destroy_instance(credentials, id) + safely do + gg = new_client(credentials) + gg.gogrid_delete_server(:name => id) + end + end + + + define_instance_states do + start.to( :on ) .on( :create ) + pending.to( :on ) .automatically + running.to( :off ) .on( :reboot ) + running.to( :off ) .on( :stop ) + shutting_down.to( :off ) .automatically + stopped.to( :off ) .automatically + end + + private + + 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, + :architecture=>convert_arch(gg_image['description']), + } ) + end + + def new_client(credentials) + Rightscale::Gogrid.new(credentials.user, credentials.password) + 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 => server['name'], + :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 + + # We produce flavors from RAM + def convert_ram_to_flavor(ram) + 'server-with-'+ram.downcase+'-ram' + 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 + 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 + end + end + +end + + end + end +end + + diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb new file mode 100644 index 0000000..ab2c5af --- /dev/null +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_test.rb @@ -0,0 +1,73 @@ +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
--- server/features/support/env.rb | 1 + server/features/support/gogrid/config.yaml | 26 ++++++++ .../features/support/gogrid/fixtures/flavors.yaml | 7 ++ .../features/support/gogrid/fixtures/images.yaml | 13 ++++ .../support/gogrid/fixtures/instances.yaml | 42 +++++++++++++ server/features/support/gogrid/fixtures/ips.yaml | 11 ++++ server/features/support/gogrid/servers.yaml | 4 + .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb | 31 +++++++--- .../drivers/gogrid/gogrid_mock_driver.rb | 62 ++++++++++++++++++++ 9 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 server/features/support/gogrid/config.yaml create mode 100644 server/features/support/gogrid/fixtures/flavors.yaml create mode 100644 server/features/support/gogrid/fixtures/images.yaml create mode 100644 server/features/support/gogrid/fixtures/instances.yaml create mode 100644 server/features/support/gogrid/fixtures/ips.yaml create mode 100644 server/features/support/gogrid/servers.yaml create mode 100644 server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb
diff --git a/server/features/support/env.rb b/server/features/support/env.rb index 95d47cc..39f4195 100644 --- a/server/features/support/env.rb +++ b/server/features/support/env.rb @@ -14,6 +14,7 @@ World do def app @app = Rack::Builder.new do set :logging, true + set :raise_errors, true run Sinatra::Application end end diff --git a/server/features/support/gogrid/config.yaml b/server/features/support/gogrid/config.yaml new file mode 100644 index 0000000..b562ed8 --- /dev/null +++ b/server/features/support/gogrid/config.yaml @@ -0,0 +1,26 @@ +--- +:username: mockuser +:password: mockpassword +:storage_snapshot_id: snap-72a5401b +:driver_name: gogrid +:instances_count: 2 +:flavor_arch: i386 +:storage_snapshot_state: AVAILABLE +:realm_id: us +:instance_1_name: ami-e4b6538d +:image_owner: "mockuser" +:storage_snapshot_count: "2" +:realm_state: AVAILABLE +:instance_2_name: ami-e4b6538d +:image_arch: i386 +:storage_volume_id: vol-60957009 +:storage_volume_instance: i-123f1234 +:realm_count: 1 +:instance_image_id: 1 +:image_id: 1 +:storage_volume_state: AVAILABLE +:flavor_id: server-with-512mb-ram +:instance_realm: us +:image_count: 2 +:storage_volume_count: 0 +:flavor_count: 3 diff --git a/server/features/support/gogrid/fixtures/flavors.yaml b/server/features/support/gogrid/fixtures/flavors.yaml new file mode 100644 index 0000000..503fd6b --- /dev/null +++ b/server/features/support/gogrid/fixtures/flavors.yaml @@ -0,0 +1,7 @@ +--- +- name: 512MB + description: Server with 512MB RAM +- name: 1GB + description: Server with 1GB RAM +- name: 2GB + description: Server with 2GB RAM diff --git a/server/features/support/gogrid/fixtures/images.yaml b/server/features/support/gogrid/fixtures/images.yaml new file mode 100644 index 0000000..1e00abc --- /dev/null +++ b/server/features/support/gogrid/fixtures/images.yaml @@ -0,0 +1,13 @@ +--- +- id: 1 + name: w2k8_32_iis_php_mysql + description: Windows 2008 (32-bit) w/ IIS 7.0 + PHP 5.2 + MySQL 5.0 + friendlyName: Windows 2008 IIS +- id: 2 + name: GSI-95aa2817-7267-3325-ad01-1387a25f31ff + description: My second GSI + friendlyName: Test second GSI +- id: 3 + name: RHEL 5.1 (64-bit) + description: RHEL Linux 5.1 (64-bit) + friendlyName: RHEL diff --git a/server/features/support/gogrid/fixtures/instances.yaml b/server/features/support/gogrid/fixtures/instances.yaml new file mode 100644 index 0000000..a64bc41 --- /dev/null +++ b/server/features/support/gogrid/fixtures/instances.yaml @@ -0,0 +1,42 @@ +--- +- name: Example Web Server + type: + description: This server has a public connection to the Internet. + id: 1 + name: Web Server + object: option + id: 5075 + description: Some more info here + ip: + id: 499 + ip: 111.111.111.111 + object: ip + public: true + state: + description: IP is reserved + id: 2 + name: Assigned + object: option + subnet: "111.111.111.110/255.255.255.240" + os: + description: Windows 2008 Server (32-bit) + id: 11 + name: Windows 2008 Server (32-bit) + object: option + ram: + description: Server with 2GB RAM + id: 7 + name: 2GB + object: option + image: + id: 1 + name: w2k8_32_iis_php_mysql + description: Windows 2008 (32-bit) w/ IIS 7.0 + PHP 5.2 + MySQL 5.0 + object: option + object: server + state: + description: 'null' + id: 3 + name: 'Off' + object: option + diff --git a/server/features/support/gogrid/fixtures/ips.yaml b/server/features/support/gogrid/fixtures/ips.yaml new file mode 100644 index 0000000..cddc44b --- /dev/null +++ b/server/features/support/gogrid/fixtures/ips.yaml @@ -0,0 +1,11 @@ +--- +- public: true + id: '138273' + ip: '216.121.60.160' + subnet: "216.121.60.160/255.255.255.240" + object: 'ip' +- public: true + id: '138273' + ip: '216.121.60.161' + subnet: "216.121.60.161/255.255.255.240" + object: 'ip' diff --git a/server/features/support/gogrid/servers.yaml b/server/features/support/gogrid/servers.yaml new file mode 100644 index 0000000..662b34c --- /dev/null +++ b/server/features/support/gogrid/servers.yaml @@ -0,0 +1,4 @@ +--- +- name: 'From API' + os: + name: RHEL diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb index 1593f79..817e4d3 100644 --- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb @@ -66,7 +66,7 @@ class GogridDriver < Deltacloud::BaseDriver 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') + Flavor.new(:id => sr['description'].downcase.tr(' ', '-'), :memory => sr['name'], :architecture => 'i386', :storage => '10') end end return results if ( opts.nil? ) @@ -79,7 +79,7 @@ class GogridDriver < Deltacloud::BaseDriver images = [] safely do gg=new_client(credentials) - images = gg.list_images.collect { |image| convert_image(image, credentials[:name]) } + images = gg.list_images.collect { |image| convert_image(image, credentials.user) } end images = filter_on( images, :architecture, opts ) images.sort_by{|e| [e.owner_id,e.description]} @@ -141,12 +141,15 @@ class GogridDriver < Deltacloud::BaseDriver
define_instance_states do - start.to( :on ) .on( :create ) - pending.to( :on ) .automatically - running.to( :off ) .on( :reboot ) - running.to( :off ) .on( :stop ) - shutting_down.to( :off ) .automatically - stopped.to( :off ) .automatically + 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 end
private @@ -175,8 +178,8 @@ class GogridDriver < Deltacloud::BaseDriver def convert_srv_to_instance(srv, flavor_id) server = srv.kind_of?(Hash) ? srv : srv.first Instance.new({ - :id => server['name'], - :state => server['state']['name'], + :id => convert_server_name(server['name']), + :state => convert_server_state(server['state']['name']), :name => server['description'], :image_id => server['image']['id'], :owner_id => 'root', @@ -188,6 +191,14 @@ class GogridDriver < Deltacloud::BaseDriver }) end
+ def convert_server_state(state) + state.eql?('Off') ? 'STOPPED' : 'RUNNING' + end + + def convert_server_name(name) + name.gsub(/\W/, '_').downcase + end + # We produce flavors from RAM def convert_ram_to_flavor(ram) 'server-with-'+ram.downcase+'-ram' diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb new file mode 100644 index 0000000..c42beb8 --- /dev/null +++ b/server/lib/deltacloud/drivers/gogrid/gogrid_mock_driver.rb @@ -0,0 +1,62 @@ +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
deltacloud-devel@lists.fedorahosted.org