deltacloud-core 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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