deltacloud-core 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. data/Rakefile +1 -24
  2. data/lib/deltacloud/base_driver/features.rb +15 -0
  3. data/lib/deltacloud/base_driver/mock_driver.rb +37 -0
  4. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +70 -48
  5. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +97 -14
  6. data/lib/deltacloud/drivers/{rimu/rimu_hosting_client.rb → rimuhosting/rimuhosting_client.rb} +5 -8
  7. data/lib/deltacloud/drivers/{rimu/rimu_hosting_driver.rb → rimuhosting/rimuhosting_driver.rb} +6 -5
  8. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +261 -0
  9. data/lib/deltacloud/hardware_profile.rb +22 -0
  10. data/lib/deltacloud/helpers/application_helper.rb +18 -0
  11. data/lib/deltacloud/helpers/conversion_helper.rb +2 -3
  12. data/lib/deltacloud/method_serializer.rb +84 -0
  13. data/lib/drivers.rb +4 -3
  14. data/lib/sinatra/accept_media_types.rb +128 -0
  15. data/lib/sinatra/respond_to.rb +23 -16
  16. data/public/javascripts/application.js +30 -0
  17. data/public/javascripts/jquery-1.4.2.min.js +154 -0
  18. data/server.rb +23 -21
  19. data/support/fedora/deltacloudd +68 -0
  20. data/support/fedora/rubygem-deltacloud-core.spec +91 -0
  21. data/views/instances/index.html.haml +2 -1
  22. data/views/instances/index.xml.haml +3 -3
  23. data/views/instances/new.html.haml +9 -3
  24. data/views/instances/show.html.haml +2 -1
  25. data/views/instances/show.xml.haml +16 -3
  26. data/views/layout.html.haml +2 -0
  27. metadata +19 -37
  28. data/lib/converters/xml_converter.rb +0 -133
  29. data/public/javascripts/controls.js +0 -963
  30. data/public/javascripts/dragdrop.js +0 -973
  31. data/public/javascripts/effects.js +0 -1128
  32. data/public/javascripts/prototype.js +0 -4320
@@ -24,17 +24,14 @@ require "deltacloud/base_driver"
24
24
 
25
25
  module Deltacloud
26
26
  module Drivers
27
- module Rimu
27
+ module RimuHosting
28
28
 
29
29
  class RimuHostingClient
30
30
  def initialize(credentials ,baseuri = 'https://rimuhosting.com/r')
31
31
  @uri = URI.parse(baseuri)
32
32
  @service = Net::HTTP.new(@uri.host, @uri.port)
33
33
  @service.use_ssl = true
34
- if credentials.provided?
35
- @auth = "rimuhosting apikey=#{credentials.password}"
36
- end
37
-
34
+ @auth = "rimuhosting apikey=#{credentials.password}"
38
35
  end
39
36
 
40
37
  def request(resource, data='', method='GET')
@@ -47,8 +44,9 @@ class RimuHostingClient
47
44
  res = JSON.parse(r.body)
48
45
  res = res[res.keys[0]]
49
46
 
50
- if(res['response_type'] == "ERROR" and res['error_info']['error_class'] == "PermissionException")
51
- raise DeltaCloud::AuthException.new
47
+ if(res['response_type'] == "ERROR" and ( (res['error_info']['error_class'] == "PermissionException") or
48
+ (res['error_info']['error_class'] == "LoginRequired") ))
49
+ raise Deltacloud::AuthException.new
52
50
  end
53
51
  res
54
52
  end
@@ -58,7 +56,6 @@ class RimuHostingClient
58
56
  end
59
57
 
60
58
  def list_plans
61
- puts "testsdasfdsf"
62
59
  request('/pricing-plans;server-type=VPS')["pricing_plan_infos"]
63
60
  end
64
61
 
@@ -17,11 +17,11 @@
17
17
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require "deltacloud/base_driver"
20
- require "deltacloud/drivers/rimu/rimu_hosting_client"
20
+ require "deltacloud/drivers/rimuhosting/rimuhosting_client"
21
21
 
22
22
  module Deltacloud
23
23
  module Drivers
24
- module Rimu
24
+ module RimuHosting
25
25
 
26
26
  class RimuHostingDriver < Deltacloud::BaseDriver
27
27
 
@@ -51,8 +51,8 @@ class RimuHostingDriver < Deltacloud::BaseDriver
51
51
  # not contained in hte pricing_plan_infos
52
52
  HardwareProfile.new(plan["pricing_plan_code"]) do
53
53
  memory plan["minimum_memory_mb"].to_f
54
- storage => plan["minimum_disk_gb"].to_i
55
- architecture => "x86"
54
+ storage plan["minimum_disk_gb"].to_i
55
+ architecture "x86"
56
56
  end
57
57
  end
58
58
  filter_hardware_profiles(results, opts)
@@ -118,7 +118,8 @@ class RimuHostingDriver < Deltacloud::BaseDriver
118
118
  :realm_id => "RH",
119
119
  :owner_id => "root",
120
120
  :instance_profile => InstanceProfile.new("none"),
121
- :actions => instance_actions_for("RUNNING")
121
+ :actions => instance_actions_for("RUNNING"),
122
+ :public_addresses => inst["allocated_ips"]["primary_ip"]
122
123
  })
123
124
  end
124
125
 
@@ -0,0 +1,261 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+ #
17
+ # This driver uses the fog library (Geemus - Wesley Beary) to talk to terremark... see
18
+ # http://github.com/geemus/fog
19
+ # see terremark vcloud express api at:
20
+ # https://community.vcloudexpress.terremark.com/en-us/product_docs/w/wiki/d-complete-vcloud-express-api-document.aspx
21
+ #
22
+ # 02 May 2010
23
+ #
24
+ require 'deltacloud/base_driver'
25
+ require 'fog'
26
+ require 'excon'
27
+ require 'nokogiri'
28
+
29
+ module Deltacloud
30
+ module Drivers
31
+ module Terremark
32
+
33
+ class TerremarkDriver < Deltacloud::BaseDriver
34
+
35
+ feature :instances, :user_name
36
+
37
+ #--
38
+ # Vapp State Map... for use with convert_instance (get an integer back from terremark)
39
+ #--
40
+ VAPP_STATE_MAP = { "0" => "PENDING", "1" => "PENDING", "2" => "STOPPED", "4" => "RUNNING" }
41
+
42
+ #--
43
+ # HARDWARE PROFILES
44
+ #--
45
+ define_hardware_profile 'default' do
46
+ cpu [1,2,4,8]
47
+ memory [512, 1024, 2048, 4096, 8192]
48
+ storage (1..500).to_a
49
+ end
50
+ #storage_disks [1..15]
51
+
52
+ #--
53
+ # IMAGES
54
+ #--
55
+ #aka "vapp_templates"
56
+ def images(credentials, opts=nil)
57
+ image_list = []
58
+ terremark_client = new_client(credentials)
59
+ vdc_id = terremark_client.default_vdc_id
60
+ catalogItems = terremark_client.get_catalog(vdc_id).body['CatalogItems']
61
+ catalogItems.each{ |catalog_item|
62
+ current_item_id = catalog_item['href'].split('/').last
63
+ current_item = terremark_client.get_catalog_item(current_item_id).body['Entity']
64
+ if(current_item['type'] == 'application/vnd.vmware.vcloud.vAppTemplate+xml')
65
+ image_list << convert_image(current_item, credentials.user)
66
+ end
67
+ } #end of catalogItems.each
68
+ image_list = filter_on( image_list, :id, opts )
69
+ image_list = filter_on( image_list, :architecture, opts )
70
+ image_list = filter_on( image_list, :owner_id, opts )
71
+ image_list
72
+ end
73
+
74
+ #--
75
+ # REALMS
76
+ #--
77
+ #only one realm... everything in US?
78
+ def realms(credentials, opts=nil)
79
+ [Realm.new( {
80
+ :id=>"US-Miami",
81
+ :name=>"United States - Miami",
82
+ :state=> "AVAILABLE"
83
+ } )]
84
+ end
85
+
86
+ #--
87
+ # INSTANCES
88
+ #--
89
+ #aka vApps
90
+ def instances(credentials, opts=nil)
91
+ instances = []
92
+ terremark_client = new_client(credentials)
93
+ vdc_items = terremark_client.get_vdc(terremark_client.default_vdc_id()).body['ResourceEntities']
94
+ vdc_items.each{|current_item|
95
+ if(current_item['type'] == 'application/vnd.vmware.vcloud.vApp+xml')
96
+ vapp_id = current_item['href'].split('/').last
97
+ vapp = terremark_client.get_vapp(vapp_id)
98
+ instances << convert_instance(vapp, terremark_client, credentials.user)
99
+ end
100
+ }#end vdc_items.each
101
+ instances = filter_on( instances, :id, opts )
102
+ instances
103
+ end
104
+
105
+ #--
106
+ # FINITE STATE MACHINE
107
+ #--
108
+ #by default new instance --> powered_off
109
+ define_instance_states do
110
+ start.to(:pending) .on( :create )
111
+ pending.to(:stopped) .automatically
112
+ stopped.to(:running) .on( :start )
113
+ running.to(:running) .on( :reboot )
114
+ running.to(:shutting_down) .on( :stop )
115
+ shutting_down.to(:stopped) .automatically
116
+ stopped.to(:end) .on( :destroy )
117
+ end
118
+
119
+
120
+ #--
121
+ # CREATE INSTANCE
122
+ #--
123
+ #launch a vapp template. Needs a name, ram, no. cpus, id of vapp_template
124
+ def create_instance(credentials, image_id, opts)
125
+ new_vapp = nil
126
+ vapp_opts = {} #assemble options to pass to Fog::Terremark::Real.instantiate_vapp_template
127
+ terremark_hwp = hardware_profiles(credentials, {:name => 'default'}).first #sanity check values against default
128
+ name = opts['name'] #name could be nil or length 0 or too long
129
+ name = "inst#{Time.now.to_i}" if (name.nil? || (name.length == 0))
130
+ name = name.slice(0..13) #name < 15 chars (says terremark)
131
+ unless ( (terremark_hwp.include?(:cpu, opts[:hwp_cpu].to_i)) &&
132
+ (terremark_hwp.include?(:memory, opts[:hwp_memory].to_i)) ) then
133
+ 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]}")
134
+ end
135
+ vapp_opts['cpus'] = opts[:hwp_cpu]
136
+ vapp_opts['memory'] = opts[:hwp_memory]
137
+ terremark_client = new_client(credentials)
138
+ #######
139
+ #FIXME# what happens if there is an issue getting the new vapp id? (eg even though created succesfully)
140
+ #######
141
+ vapp_id = terremark_client.instantiate_vapp_template(name, image_id, vapp_opts).body['href'].split('/').last
142
+ new_vapp = terremark_client.get_vapp(vapp_id)
143
+ return convert_instance(new_vapp, terremark_client, credentials.user) #return an Instance object
144
+ end
145
+
146
+ #--
147
+ # REBOOT INSTANCE
148
+ #--
149
+ def reboot_instance(credentials, id)
150
+ terremark_client = new_client(credentials)
151
+ terremark_client.power_reset(id)
152
+ end
153
+
154
+ #--
155
+ # START INSTANCE
156
+ #--
157
+ def start_instance(credentials, id)
158
+ terremark_client = new_client(credentials)
159
+ terremark_client.power_on(id)
160
+ end
161
+
162
+ #--
163
+ # STOP INSTANCE
164
+ #--
165
+ def stop_instance(credentials, id)
166
+ terremark_client = new_client(credentials)
167
+ terremark_client.power_shutdown(id)
168
+ end
169
+
170
+ #--
171
+ # DESTROY INSTANCE
172
+ #--
173
+ #shuts down... in terremark need to do a futher delete to get rid of a vapp entirely
174
+ def destroy_instance(credentials, id)
175
+ terremark_client = new_client(credentials)
176
+ terremark_client.delete_vapp(id)
177
+ end
178
+
179
+ #--
180
+ # PRIVATE METHODS:
181
+ #--
182
+
183
+ private
184
+
185
+ #--
186
+ # CONVERT IMAGE
187
+ #--
188
+ #gets a vapp_template from a catalog and makes it a Image
189
+ def convert_image(catalog_vapp_template, account_name)
190
+ name = catalog_vapp_template['name']
191
+ #much fudging ensues
192
+ #arch = name.scan(/(36|24).bit/).first
193
+ #k enuf o'that now!
194
+ arch = "n/a" #Leaving out entirely as we don't get one from terremark (could parse but its a fudge)
195
+ Image.new( {
196
+ :id => catalog_vapp_template['href'].split('/').last,
197
+ :name => catalog_vapp_template['name'],
198
+ :architecture => arch,
199
+ :owner_id => account_name,
200
+ :description => catalog_vapp_template['name']
201
+ })
202
+ end
203
+
204
+ #--
205
+ # CONVERT INSTANCE
206
+ #--
207
+ def convert_instance(vapp, terremark_client, account_name)
208
+ vapp_private_ip = vapp.body['IpAddress']
209
+ 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)
210
+ vapp_status = vapp.body['status']
211
+ current_state = VAPP_STATE_MAP[vapp_status] #status == 0->BEING_CREATED 2->OFF 4->ON
212
+ profile = InstanceProfile.new("default")
213
+ name = vapp.body['name']
214
+ if current_state != "PENDING" #can only grab this stuff after instance is created
215
+ profile.cpu = vapp.body['VirtualHardware']['cpu']
216
+ profile.memory = vapp.body['VirtualHardware']['ram']
217
+ #######
218
+ #FIXME# could be more that one disk... but for now capture only first
219
+ #######
220
+ disk = ((vapp.body['VirtualHardware']['disks'].first.to_i) / 1024 / 1024).to_s
221
+ profile.storage = disk
222
+ #######
223
+ #FIXME# this is a hack, shouldn't place this info next to name as some clients may rely on name field... probably will introduce
224
+ ####### a new field in the API for this (e.g. description/text field... human readable)
225
+ #name = "#{name} - [ #{vapp.body['OperatingSystem']['Description']} ]"
226
+ end
227
+ Instance.new( {
228
+ :id => vapp.body['href'].split('/').last,
229
+ :owner_id => "#{account_name}",
230
+ #:image_id => "n/a", #cant get this... see https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60/t/376.aspx
231
+ :name => name,
232
+ :realm_id => "US-Miami",
233
+ :state => current_state,
234
+ :actions => instance_actions_for(current_state),
235
+ :public_addresses => vapp_public_ip,
236
+ :private_addresses => vapp_private_ip,
237
+ :instance_profile => profile
238
+ } )
239
+ end
240
+
241
+ #--
242
+ # NEW CLIENT
243
+ #--
244
+ #use supplied credentials to make a new client for talking to terremark
245
+ def new_client(credentials)
246
+ #Fog constructor expecting credentials[:terremark_password] and credentials[:terremark_username]
247
+ terremark_credentials = {:terremark_vcloud_username => "#{credentials.user}", :terremark_vcloud_password => "#{credentials.password}" }
248
+ terremark_client = Fog::Terremark::Vcloud.new(terremark_credentials)
249
+ vdc_id = terremark_client.default_vdc_id
250
+ if (vdc_id.nil?)
251
+ raise DeltaCloud::AuthException.new
252
+ end
253
+ terremark_client
254
+ end
255
+
256
+
257
+ end
258
+
259
+ end
260
+ end
261
+ end
@@ -117,6 +117,28 @@ module Deltacloud
117
117
  p && p.default.to_s == v
118
118
  end
119
119
 
120
+ def to_hash
121
+ props = []
122
+ self.each_property do |p|
123
+ if p.kind.eql? :fixed
124
+ props << { :kind => p.kind, :value => p.value, :name => p.name, :unit => p.unit }
125
+ else
126
+ param = { :operation => "create", :method => "post", :name => p.name }
127
+ if p.kind.eql? :range
128
+ param[:range] = { :first => p.first, :last => p.last }
129
+ elsif p.kind.eql? :enum
130
+ param[:enum] = p.values.collect { |v| { :entry => v } }
131
+ end
132
+ param
133
+ props << { :kind => p.kind, :value => p.default, :name => p.name, :unit => p.unit, :param => param }
134
+ end
135
+ end
136
+ {
137
+ :id => self.name,
138
+ :properties => props
139
+ }
140
+ end
141
+
120
142
  def include?(prop, v)
121
143
  p = @properties[prop]
122
144
  p.nil? || p.include?(v)
@@ -35,4 +35,22 @@ module ApplicationHelper
35
35
  end
36
36
  s+="</ul>"
37
37
  end
38
+
39
+ def instance_action_method(action)
40
+ collections[:instances].operations[action.to_sym].method
41
+ end
42
+
43
+ def driver_has_feature?(feature_name)
44
+ not driver.features(:instances).select{ |f| f.name.eql?(feature_name) }.empty?
45
+ end
46
+
47
+ def driver_has_auth_features?
48
+ driver_has_feature?(:authentication_password) || driver_has_feature?(:authentication_key)
49
+ end
50
+
51
+ def driver_auth_feature_name
52
+ return 'key' if driver_has_feature?(:authentication_key)
53
+ return 'password' if driver_has_feature?(:authentication_password)
54
+ end
55
+
38
56
  end
@@ -17,15 +17,14 @@
17
17
 
18
18
 
19
19
  require 'deltacloud/base_driver'
20
- require 'converters/xml_converter'
21
20
 
22
21
  module ConversionHelper
23
22
 
24
23
  def convert_to_json(type, obj)
25
- if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot ].include?( type ) )
24
+ if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile ].include?( type ) )
26
25
  if Array.eql?(obj.class)
27
26
  data = obj.collect do |o|
28
- o.to_hash.merge({ :href => self.send(:"#{type}_url", o.id ) })
27
+ o.to_hash.merge({ :href => self.send(:"#{type}_url", type.eql?(:hardware_profile) ? o.name : o.id ) })
29
28
  end
30
29
  type = type.to_s.pluralize
31
30
  else
@@ -0,0 +1,84 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'base64'
19
+ require 'digest'
20
+
21
+ module MethodSerializer
22
+
23
+ module Cache
24
+
25
+ def cache_dir
26
+ storage_dir = $methods_cache_dir || File.join(File.dirname(__FILE__), 'cache')
27
+ class_dir = self.class.name.split('::').last
28
+ class_dir ||= self.class.name
29
+ File.join(storage_dir, class_dir.downcase)
30
+ end
31
+
32
+ def serialize_data(method_name, args, data)
33
+ File.open(cache_file_name(method_name, args), 'w') do |f|
34
+ f.puts(Base64.encode64(Marshal.dump(data)))
35
+ end
36
+ return data
37
+ end
38
+
39
+ def deserialize_data(method_name, args)
40
+ begin
41
+ data = File.readlines(cache_file_name(method_name, args)).join
42
+ Marshal.load(Base64.decode64(data))
43
+ rescue Errno::ENOENT
44
+ return false
45
+ end
46
+ end
47
+
48
+ def args_hash(args)
49
+ if args.class == Hash
50
+ args = args.to_a.collect {|i| [i[0].to_s, i[1]]}.sort
51
+ end
52
+ Digest::SHA1.hexdigest(args.to_s)
53
+ end
54
+
55
+ def cache_file_name(method_name, args)
56
+ FileUtils.mkdir_p(cache_dir) unless File.directory?(cache_dir)
57
+ method_name = $scenario_prefix ? "#{$scenario_prefix}_#{method_name}" : method_name
58
+ File.join(cache_dir, "#{method_name}.#{args_hash(args)}")
59
+ end
60
+
61
+ def self.wrap_methods(c, opts={})
62
+ $methods_cache_dir = opts[:cache_dir]
63
+ $scenario_prefix = nil
64
+ c.class_eval do
65
+ cached_methods.each do |m|
66
+ next if c.instance_methods(false).include?("original_#{m}")
67
+ alias_method "original_#{m}".to_sym, m.to_sym
68
+ define_method m.to_sym do |*args|
69
+ args = args.first if args.size.eql?(1) and not args.first.class.eql?(Array)
70
+ output = deserialize_data(m, args)
71
+ unless output
72
+ output = method("original_#{m}".to_sym).to_proc[args]
73
+ return serialize_data(m, args, output)
74
+ else
75
+ return output
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end