Latest version of terremark vcloud express driver - incorporates edits suggested by Lutter. This driver relies on latest fog gem (v.0.0.93 at time of writing). Unfortunately and as experienced over the last couple weeks fog is still prone to (major) changes so I will keep an eye on this to make sure nothing breaks. David/Michal can you please ack and push this as I don't have privileges for that (or not if there are problems... ;) )
--marios
From: marios marios@redhat.com
--- .../drivers/terremark/terremark_driver.rb | 261 ++++++++++++++++++++ server/lib/drivers.rb | 11 +- 2 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 server/lib/deltacloud/drivers/terremark/terremark_driver.rb
diff --git a/server/lib/deltacloud/drivers/terremark/terremark_driver.rb b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb new file mode 100644 index 0000000..173a174 --- /dev/null +++ b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb @@ -0,0 +1,261 @@ +# Copyright (C) 2010 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 +# +# This driver uses the fog library (Geemus - Wesley Beary) to talk to terremark... see +# http://github.com/geemus/fog +# see terremark vcloud express api at: +# https://community.vcloudexpress.terremark.com/en-us/product_docs/w/wiki/d-co... +# +# 02 May 2010 +# +require 'deltacloud/base_driver' +require 'fog' +require 'excon' +require 'nokogiri' + +module Deltacloud + module Drivers + module Terremark + +class TerremarkDriver < Deltacloud::BaseDriver + + feature :instances, :user_name + +#-- +# Vapp State Map... for use with convert_instance (get an integer back from terremark) +#-- +VAPP_STATE_MAP = { "0" => "PENDING", "1" => "PENDING", "2" => "STOPPED", "4" => "RUNNING" } + +#-- +# HARDWARE PROFILES +#-- + define_hardware_profile 'default' do + cpu [1,2,4,8] + memory [512, 1024, 2048, 4096, 8192] + storage (1..500).to_a + end + #storage_disks [1..15] + +#-- +# IMAGES +#-- +#aka "vapp_templates" + def images(credentials, opts=nil) + image_list = [] + terremark_client = new_client(credentials) + vdc_id = terremark_client.default_vdc_id + catalogItems = terremark_client.get_catalog(vdc_id).body['CatalogItems'] + catalogItems.each{ |catalog_item| + current_item_id = catalog_item['href'].split('/').last + current_item = terremark_client.get_catalog_item(current_item_id).body['Entity'] + if(current_item['type'] == 'application/vnd.vmware.vcloud.vAppTemplate+xml') + image_list << convert_image(current_item, credentials.user) + end + } #end of catalogItems.each + image_list = filter_on( image_list, :id, opts ) + image_list = filter_on( image_list, :architecture, opts ) + image_list = filter_on( image_list, :owner_id, opts ) + image_list + end + +#-- +# REALMS +#-- +#only one realm... everything in US? + def realms(credentials, opts=nil) + [Realm.new( { + :id=>"US-Miami", + :name=>"United States - Miami", + :state=> "AVAILABLE" + } )] + end + +#-- +# INSTANCES +#-- +#aka vApps + def instances(credentials, opts=nil) + instances = [] + terremark_client = new_client(credentials) + vdc_items = terremark_client.get_vdc(terremark_client.default_vdc_id()).body['ResourceEntities'] + vdc_items.each{|current_item| + if(current_item['type'] == 'application/vnd.vmware.vcloud.vApp+xml') + vapp_id = current_item['href'].split('/').last + vapp = terremark_client.get_vapp(vapp_id) + instances << convert_instance(vapp, terremark_client, credentials.user) + end + }#end vdc_items.each + instances = filter_on( instances, :id, opts ) + instances + end + +#-- +# FINITE STATE MACHINE +#-- +#by default new instance --> powered_off + define_instance_states do + start.to(:pending) .on( :create ) + pending.to(:stopped) .automatically + stopped.to(:running) .on( :start ) + running.to(:running) .on( :reboot ) + running.to(:shutting_down) .on( :stop ) + shutting_down.to(:stopped) .automatically + stopped.to(:end) .on( :destroy ) + end + + +#-- +# CREATE INSTANCE +#-- +#launch a vapp template. Needs a name, ram, no. cpus, id of vapp_template + def create_instance(credentials, image_id, opts) + new_vapp = nil + vapp_opts = {} #assemble options to pass to Fog::Terremark::Real.instantiate_vapp_template + terremark_hwp = hardware_profiles(credentials, {:name => 'default'}).first #sanity check values against default + name = opts['name'] #name could be nil or length 0 or too long + name = "inst#{Time.now.to_i}" if (name.nil? || (name.length == 0)) + name = name.slice(0..13) #name < 15 chars (says terremark) + unless ( (terremark_hwp.include?(:cpu, opts[:hwp_cpu].to_i)) && + (terremark_hwp.include?(:memory, opts[:hwp_memory].to_i)) ) then + raise Deltacloud::Validation::Failure.new(Deltacloud::Validation::Param.new(["cpu"]), "Error with cpu and/or memory values. you said cpu->#{opts[:hwp_cpu]} and mem->#{opts[:hwp_memory]}") + end + vapp_opts['cpus'] = opts[:hwp_cpu] + vapp_opts['memory'] = opts[:hwp_memory] + terremark_client = new_client(credentials) +####### +#FIXME# what happens if there is an issue getting the new vapp id? (eg even though created succesfully) +####### + vapp_id = terremark_client.instantiate_vapp_template(name, image_id, vapp_opts).body['href'].split('/').last + new_vapp = terremark_client.get_vapp(vapp_id) + return convert_instance(new_vapp, terremark_client, credentials.user) #return an Instance object + end + +#-- +# REBOOT INSTANCE +#-- + def reboot_instance(credentials, id) + terremark_client = new_client(credentials) + terremark_client.power_reset(id) + end + +#-- +# START INSTANCE +#-- +def start_instance(credentials, id) + terremark_client = new_client(credentials) + terremark_client.power_on(id) +end + +#-- +# STOP INSTANCE +#-- +def stop_instance(credentials, id) + terremark_client = new_client(credentials) + terremark_client.power_shutdown(id) +end + +#-- +# DESTROY INSTANCE +#-- +#shuts down... in terremark need to do a futher delete to get rid of a vapp entirely +def destroy_instance(credentials, id) + terremark_client = new_client(credentials) + terremark_client.delete_vapp(id) +end + +#-- +# PRIVATE METHODS: +#-- + + private + +#-- +# CONVERT IMAGE +#-- +#gets a vapp_template from a catalog and makes it a Image + def convert_image(catalog_vapp_template, account_name) + name = catalog_vapp_template['name'] + #much fudging ensues + #arch = name.scan(/(36|24).bit/).first + #k enuf o'that now! + arch = "n/a" #Leaving out entirely as we don't get one from terremark (could parse but its a fudge) + Image.new( { + :id => catalog_vapp_template['href'].split('/').last, + :name => catalog_vapp_template['name'], + :architecture => arch, + :owner_id => account_name, + :description => catalog_vapp_template['name'] + }) + end + +#-- +# CONVERT INSTANCE +#-- + def convert_instance(vapp, terremark_client, account_name) + vapp_private_ip = vapp.body['IpAddress'] + vapp_public_ip = terremark_client.get_public_ips(terremark_client.default_vdc_id).body['PublicIpAddresses'].first['name']#get_public_address(terremark_client, vapp_private_ip) + vapp_status = vapp.body['status'] + current_state = VAPP_STATE_MAP[vapp_status] #status == 0->BEING_CREATED 2->OFF 4->ON + profile = InstanceProfile.new("default") + name = vapp.body['name'] + if current_state != "PENDING" #can only grab this stuff after instance is created + profile.cpu = vapp.body['VirtualHardware']['cpu'] + profile.memory = vapp.body['VirtualHardware']['ram'] +####### +#FIXME# could be more that one disk... but for now capture only first +####### + disk = ((vapp.body['VirtualHardware']['disks'].first.to_i) / 1024 / 1024).to_s + profile.storage = disk +####### +#FIXME# this is a hack, shouldn't place this info next to name as some clients may rely on name field... probably will introduce +####### a new field in the API for this (e.g. description/text field... human readable) + #name = "#{name} - [ #{vapp.body['OperatingSystem']['Description']} ]" + end + Instance.new( { + :id => vapp.body['href'].split('/').last, + :owner_id => "#{account_name}", + #:image_id => "n/a", #cant get this... see https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60/t... + :name => name, + :realm_id => "US-Miami", + :state => current_state, + :actions => instance_actions_for(current_state), + :public_addresses => vapp_public_ip, + :private_addresses => vapp_private_ip, + :instance_profile => profile + } ) + end + +#-- +# NEW CLIENT +#-- +#use supplied credentials to make a new client for talking to terremark + def new_client(credentials) + #Fog constructor expecting credentials[:terremark_password] and credentials[:terremark_username] + terremark_credentials = {:terremark_vcloud_username => "#{credentials.user}", :terremark_vcloud_password => "#{credentials.password}" } + terremark_client = Fog::Terremark::Vcloud.new(terremark_credentials) + vdc_id = terremark_client.default_vdc_id + if (vdc_id.nil?) + raise DeltaCloud::AuthException.new + end + terremark_client + end + + +end + + end + end +end diff --git a/server/lib/drivers.rb b/server/lib/drivers.rb index 1add916..269fe75 100644 --- a/server/lib/drivers.rb +++ b/server/lib/drivers.rb @@ -5,6 +5,7 @@ DRIVERS = { :rhevm => { :name => "RHEVM" }, :rimu => { :name => "Rimu", :class => "RimuHostingDriver"}, :opennebula => { :name => "Opennebula", :class => "OpennebulaDriver" }, + :terremark => { :name => "Terremark"}, :mock => { :name => "Mock" } }
@@ -27,15 +28,7 @@ def driver_mock_source_name end
def driver - - begin - require driver_source_name - rescue LoadError => e - gem_name = e.message.match(/ -- (.+)$/).to_a.last - gem_name = "amazon-ec2" if gem_name.eql?('AWS') - $stderr.puts "ERROR: Please install required gem first. (gem install #{gem_name})" - exit 1 - end + require driver_source_name
if Sinatra::Application.environment.eql? :test require driver_mock_source_name
On Tue, 2010-05-11 at 14:55 +0100, mandreou@redhat.com wrote:
From: marios marios@redhat.com
.../drivers/terremark/terremark_driver.rb | 261 ++++++++++++++++++++ server/lib/drivers.rb | 11 +- 2 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 server/lib/deltacloud/drivers/terremark/terremark_driver.rb
ACK. There's acouple minor nits: one is that there is a lot of trailing whitespace that you should remove (I can send you Emacs macros that do that automatically)
The other issue is
diff --git a/server/lib/drivers.rb b/server/lib/drivers.rb index 1add916..269fe75 100644 --- a/server/lib/drivers.rb +++ b/server/lib/drivers.rb
@@ -27,15 +28,7 @@ def driver_mock_source_name end
def driver
- begin
- require driver_source_name
- rescue LoadError => e
- gem_name = e.message.match(/ -- (.+)$/).to_a.last
- gem_name = "amazon-ec2" if gem_name.eql?('AWS')
- $stderr.puts "ERROR: Please install required gem first. (gem install #{gem_name})"
- exit 1
- end
- require driver_source_name
Leave the rescue in there - that's the central place to catch LoadError that I was talking about. Michal: is there a way to put this even higher, into deltacloudd or is it too hard to catch the error there ?
David
ok terremark driver now pushed to core repo, (still couple of FIXMEs but we defer them for now)
marios
On 11/05/10 23:45, David Lutterkort wrote:
On Tue, 2010-05-11 at 14:55 +0100, mandreou@redhat.com wrote:
From: mariosmarios@redhat.com
.../drivers/terremark/terremark_driver.rb | 261 ++++++++++++++++++++ server/lib/drivers.rb | 11 +- 2 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 server/lib/deltacloud/drivers/terremark/terremark_driver.rb
ACK. There's acouple minor nits: one is that there is a lot of trailing whitespace that you should remove (I can send you Emacs macros that do that automatically)
The other issue is
diff --git a/server/lib/drivers.rb b/server/lib/drivers.rb index 1add916..269fe75 100644 --- a/server/lib/drivers.rb +++ b/server/lib/drivers.rb
@@ -27,15 +28,7 @@ def driver_mock_source_name end
def driver
- begin
- require driver_source_name
- rescue LoadError => e
- gem_name = e.message.match(/ -- (.+)$/).to_a.last
- gem_name = "amazon-ec2" if gem_name.eql?('AWS')
- $stderr.puts "ERROR: Please install required gem first. (gem install #{gem_name})"
- exit 1
- end
- require driver_source_name
Leave the rescue in there - that's the central place to catch LoadError that I was talking about. Michal: is there a way to put this even higher, into deltacloudd or is it too hard to catch the error there ?
David
deltacloud-devel mailing list deltacloud-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/deltacloud-devel
On Tue, 2010-05-11 at 14:55 +0100, mandreou@redhat.com wrote:
Latest version of terremark vcloud express driver - incorporates edits suggested by Lutter. This driver relies on latest fog gem (v.0.0.93 at time of writing). Unfortunately and as experienced over the last couple weeks fog is still prone to (major) changes so I will keep an eye on this to make sure nothing breaks. David/Michal can you please ack and push this as I don't have privileges for that (or not if there are problems... ;) )
Might as well get used to the fun: you need to apply for membership in the gitdeltacloud[1] group in the Fedora Account System [2]. Before you can do that, you need to have a Fedora Account, and sign the CLA.
David
[1] https://admin.fedoraproject.org/accounts/group/view/gitdeltacloud [2] https://fedoraproject.org/wiki/Infrastructure/AccountSystem
deltacloud-devel@lists.fedorahosted.org