rackspace_cloudservers_api 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.
@@ -0,0 +1,46 @@
1
+ #
2
+ # Copyright (c) 2009 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ require 'benchmark'
25
+ require 'net/https'
26
+ require 'uri'
27
+ require "base64"
28
+ require 'rubygems'
29
+ require 'json'
30
+ require 'right_http_connection'
31
+
32
+ $:.unshift(File.dirname(__FILE__))
33
+ require 'benchmark_fix'
34
+ require 'support'
35
+ require 'rackspace_base'
36
+ require 'rackspace'
37
+
38
+ module RightRackspace #:nodoc:
39
+ module VERSION #:nodoc:
40
+ MAJOR = 0
41
+ MINOR = 0
42
+ TINY = 0
43
+
44
+ STRING = [MAJOR, MINOR, TINY].join('.')
45
+ end
46
+ end
data/lib/support.rb ADDED
@@ -0,0 +1,124 @@
1
+ # If ActiveSupport is loaded, then great - use it. But we don't
2
+ # want a dependency on it, so if it's not present, define the few
3
+ # extensions that we want to use...
4
+ unless defined? ActiveSupport::CoreExtensions
5
+ # These are ActiveSupport-;like extensions to do a few handy things in the gems
6
+ # Derived from ActiveSupport, so the AS copyright notice applies:
7
+ #
8
+ #
9
+ #
10
+ # Copyright (c) 2005 David Heinemeier Hansson
11
+ #
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+ #
32
+ #
33
+ class String #:nodoc:
34
+
35
+ # Constantize tries to find a declared constant with the name specified
36
+ # in the string. It raises a NameError when the name is not in CamelCase
37
+ # or is not initialized.
38
+ #
39
+ # Examples
40
+ # "Module".constantize #=> Module
41
+ # "Class".constantize #=> Class
42
+ def constantize()
43
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
44
+ raise NameError, "#{self.inspect} is not a valid constant name!"
45
+ end
46
+
47
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
48
+ end
49
+
50
+ def camelize()
51
+ self.dup.split(/_/).map{ |word| word.capitalize }.join('')
52
+ end
53
+
54
+ end
55
+
56
+
57
+ class Object #:nodoc:
58
+ # "", " ", nil, [], and {} are blank
59
+ def blank?
60
+ if respond_to?(:empty?) && respond_to?(:strip)
61
+ empty? or strip.empty?
62
+ elsif respond_to?(:empty?)
63
+ empty?
64
+ else
65
+ !self
66
+ end
67
+ end
68
+ end
69
+
70
+ class NilClass #:nodoc:
71
+ def blank?
72
+ true
73
+ end
74
+ end
75
+
76
+ class FalseClass #:nodoc:
77
+ def blank?
78
+ true
79
+ end
80
+ end
81
+
82
+ class TrueClass #:nodoc:
83
+ def blank?
84
+ false
85
+ end
86
+ end
87
+
88
+ class Array #:nodoc:
89
+ alias_method :blank?, :empty?
90
+ end
91
+
92
+ class Hash #:nodoc:
93
+ alias_method :blank?, :empty?
94
+
95
+ # Return a new hash with all keys converted to symbols.
96
+ def symbolize_keys
97
+ inject({}) do |options, (key, value)|
98
+ options[key.to_sym] = value
99
+ options
100
+ end
101
+ end
102
+ end
103
+
104
+ class String #:nodoc:
105
+ def blank?
106
+ empty? || strip.empty?
107
+ end
108
+ end
109
+
110
+ class Numeric #:nodoc:
111
+ def blank?
112
+ false
113
+ end
114
+ end
115
+ end
116
+
117
+ # We also use String#first from 1.8.7 that doesn't exist in 1.8.6
118
+ unless String.instance_methods.include? "first"
119
+ class String
120
+ def first
121
+ self
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,41 @@
1
+ class TestCredentials
2
+
3
+ @@username = nil
4
+ @@auth_key = nil
5
+
6
+ def self.username
7
+ @@username
8
+ end
9
+ def self.username=(newval)
10
+ @@username = newval
11
+ end
12
+ def self.auth_key
13
+ @@auth_key
14
+ end
15
+ def self.auth_key=(newval)
16
+ @@auth_key = newval
17
+ end
18
+
19
+ # Make sure you have environment vars set:
20
+ #
21
+ # export RACKSPACE_USERNAME ='your_rackspace_username'
22
+ # export RACKSPACE_AUTH_KEY ='your_rackspace_auth_key'
23
+ #
24
+ # or you have a file: ~/.rightscale/test_rackspace_credentials.rb with text:
25
+ #
26
+ # TestCredentials.username = 'your_rackspace_username'
27
+ # TestCredentials.auth_key = 'your_rackspace_auth_key'
28
+ #
29
+ def self.get_credentials
30
+ Dir.chdir do
31
+ begin
32
+ Dir.chdir('./.rightscale') do
33
+ require 'test_rackspace_credentials'
34
+ end
35
+ rescue Exception => e
36
+ puts "Couldn't chdir to ~/.rightscale: #{e.message}"
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,301 @@
1
+ # Unit test for Rackspace gem
2
+ # Specify your gogrid account credentials as described in test_credentials.rb
3
+
4
+ require File.dirname(__FILE__) + '/_test_credentials'
5
+ require File.dirname(__FILE__) + '/../lib/right_rackspace'
6
+
7
+ class TestRightRackspace < Test::Unit::TestCase
8
+
9
+ TEST_SERVER_NAME = 'right-rackspace-gem-test-server-0123456789'
10
+ TEST_IMAGE_ID = 8
11
+ TEST_FLAVOR_ID = 2
12
+ TEST_METADATA = {'key1' => 'value1', 'key2' => 'value2'}
13
+ TEST_PERSONALITIES = { '/home/1.txt' => 'WooHoo', '/home/2.rb' => 'puts "Hello World!"'}
14
+
15
+ def setup
16
+ $stdout.sync = true
17
+ ::TestCredentials.get_credentials
18
+ # TODO: remove :auth_endpoint and :service_endpoint when the service is released publicly
19
+ @rackspace = Rightscale::Rackspace::Interface.new(TestCredentials.username, TestCredentials.auth_key,
20
+ :logger => Logger.new('/dev/null'))
21
+ end
22
+
23
+ # -------------
24
+ # Helpers
25
+ # -------------
26
+
27
+ def get_test_server_id(detail=false)
28
+ @rackspace.list_servers(:detail => detail)['servers'].each do |server|
29
+ return server['id'] if server['name'] == TEST_SERVER_NAME
30
+ end
31
+ nil
32
+ end
33
+
34
+ def wail_until(reason, &block)
35
+ initial_time = Time.now
36
+ puts
37
+ print reason
38
+ loop do
39
+ break if block.call
40
+ print '*'
41
+ sleep 10
42
+ end
43
+ puts
44
+ puts "(waited: #{Time.now - initial_time} seconds)"
45
+ puts
46
+ end
47
+
48
+ def resources_test(name)
49
+ # puts ">>> Resource test: #{name}"
50
+ resource_type = ''
51
+ name.split('_').each_with_index do |w, i|
52
+ resource_type += (i==0 ? w.downcase : w.capitalize)
53
+ end
54
+ # get resources
55
+ resources = @rackspace.send("list_#{name}")[resource_type]
56
+ # assert the list is an Array
57
+ assert resources.is_a?(Array)
58
+ # assert the custom resource is not an empty Hash
59
+ resource = resources.first
60
+ if resource
61
+ assert resource.is_a?(Hash)
62
+ assert !resource.blank?
63
+ end
64
+
65
+ # get the detailed list of resources
66
+ detailed_resources = @rackspace.send("list_#{name}", :detail => true)[resource_type]
67
+ assert detailed_resources.is_a?(Array)
68
+ # assert the custom detailed resource is not an empty Hash
69
+ detailed_resource = detailed_resources.first
70
+ if detailed_resource
71
+ assert detailed_resource.is_a?(Hash)
72
+ assert !detailed_resource.blank?
73
+ end
74
+
75
+ # make sure the detailed resource contains more data then non detailed
76
+ if resource && detailed_resource
77
+ assert detailed_resource.size > resource.size
78
+ end
79
+
80
+ # Make a custom resource tests
81
+ if resource
82
+ # singularise name (drop a tailing 's')
83
+ name.chop!
84
+ resource_type.chop!
85
+ single_resource = @rackspace.send("get_#{name}", resource['id'])[resource_type]
86
+ assert single_resource.is_a?(Hash)
87
+ assert single_resource['id']
88
+ end
89
+ end
90
+
91
+ def right_rackspace_level_caching_test(cache_key, *api_call_data)
92
+ # puts ">>> Rackspace gem level caching test: #{cache_key}"
93
+ # Assert there are no any exceptions while the caching is off
94
+ @rackspace.send(*api_call_data)
95
+ @rackspace.send(*api_call_data)
96
+ # Enable the caching
97
+ @rackspace.params[:caching] = true
98
+ # fill the internal cache with the response
99
+ first_response = @rackspace.send(*api_call_data)
100
+ # make another call and check the cache hit
101
+ assert_raise(Rightscale::Rackspace::NoChange) do
102
+ @rackspace.send(*api_call_data)
103
+ end
104
+ # get the data from the cache and make sure it is equal to the initial call
105
+ assert_equal first_response, @rackspace.cache[cache_key][:data]
106
+ # disable the caching
107
+ @rackspace.params[:caching] = false
108
+ end
109
+
110
+ def rackspace_service_level_caching_test(*api_call_data)
111
+ # puts ">>> Rackspace service caching test: #{cache_key}"
112
+ assert_raise(Rightscale::Rackspace::NoChange) do
113
+ opts = api_call_data.last.is_a?(Hash) ? api_call_data.pop : {}
114
+ opts[:vars] = { 'changes-since' => Time.now.utc }
115
+ api_call_data << opts
116
+ @rackspace.send(*api_call_data)
117
+ end
118
+ end
119
+
120
+ # -------------
121
+ # Tests
122
+ # -------------
123
+
124
+ def test_001_login
125
+ # should fail with a wrong username
126
+ username = @rackspace.username
127
+ @rackspace.username = 'ohohohoho'
128
+ assert_raise Rightscale::Rackspace::Error do
129
+ @rackspace.login
130
+ end
131
+ # should login successfully with the valid username
132
+ @rackspace.username = username
133
+ assert_nothing_raised do
134
+ @rackspace.login
135
+ end
136
+ end
137
+
138
+ def test_003_limits
139
+ # right_rackspace_level_caching_test('/limits', :list_limits)
140
+ limits = nil
141
+ assert_nothing_raised do
142
+ limits = @rackspace.list_limits
143
+ end
144
+ assert limits.is_a?(Hash)
145
+ assert limits['limits'].is_a?(Hash)
146
+ assert limits['limits']['rate'].is_a?(Array)
147
+ end
148
+
149
+ def test_005_api_version
150
+ right_rackspace_level_caching_test('/', :list_api_versions)
151
+ end
152
+
153
+ def test_010_images
154
+ resources_test('images')
155
+ right_rackspace_level_caching_test('/images', :list_images)
156
+ right_rackspace_level_caching_test('/images/detail', :list_images, :detail => true)
157
+ end
158
+
159
+ def test_020_flavors
160
+ resources_test('flavors')
161
+ right_rackspace_level_caching_test('/flavors', :list_flavors)
162
+ right_rackspace_level_caching_test('/flavors/detail', :list_flavors, :detail => true)
163
+ rackspace_service_level_caching_test(:list_flavors)
164
+ end
165
+
166
+ def test_030_list_shared_ip_groups
167
+ resources_test('shared_ip_groups')
168
+ right_rackspace_level_caching_test('/shared_ip_groups', :list_shared_ip_groups)
169
+ right_rackspace_level_caching_test('/shared_ip_groups/detail', :list_shared_ip_groups, :detail => true)
170
+ rackspace_service_level_caching_test(:list_shared_ip_groups)
171
+ end
172
+
173
+ def test_040_servers
174
+ resources_test('servers')
175
+ right_rackspace_level_caching_test('/servers', :list_servers)
176
+ right_rackspace_level_caching_test('/servers/detail', :list_servers, :detail => true)
177
+ rackspace_service_level_caching_test(:list_servers)
178
+ end
179
+
180
+ def test_041_make_sure_the_test_server_is_off
181
+ id = get_test_server_id
182
+ if id
183
+ assert @rackspace.delete_server(id)
184
+ puts
185
+ puts "> The old test server is just deleted"
186
+ wail_until("> Wait until the old test server disappears from list_servers responses: ") do
187
+ id1 = get_test_server_id
188
+ id2 = get_test_server_id(true)
189
+ !id1 && !id2
190
+ end
191
+ end
192
+ end
193
+
194
+ def test_042_create_server
195
+ puts
196
+ puts "> Launch a new test server "
197
+ server = @rackspace.create_server(
198
+ :name => TEST_SERVER_NAME,
199
+ :image_id => TEST_IMAGE_ID,
200
+ :flavor_id => TEST_FLAVOR_ID,
201
+ :metadata => TEST_METADATA,
202
+ :personalities => TEST_PERSONALITIES
203
+ )
204
+ assert server.is_a?(Hash)
205
+ assert_equal TEST_SERVER_NAME, server['server']['name']
206
+ assert_equal TEST_METADATA, server['server']['metadata']
207
+ assert_equal TEST_IMAGE_ID, server['server']['imageId']
208
+ assert_equal TEST_FLAVOR_ID, server['server']['flavorId']
209
+ end
210
+
211
+ # KD: 2009-08-26: That guys are having problems with caching
212
+ # Makesure the server appears in both list servers calls
213
+ def test_044_wait_for_the_server_active_state
214
+ wail_until("> Wait until the new test server appears in list_servers responses:") do
215
+ id1 = get_test_server_id
216
+ id2 = get_test_server_id(true)
217
+ id1 && id2
218
+ end
219
+ end
220
+
221
+ def test_045_wait_for_the_server_active_state
222
+ id = get_test_server_id
223
+ # wait a while the server is being built
224
+ wail_until('> Wait while the new test server is being built') do
225
+ @rackspace.get_server(id)['server']['status'] == 'ACTIVE'
226
+ end
227
+ end
228
+
229
+ def test_050_update_server_password
230
+ id = get_test_server_id
231
+ assert @rackspace.update_server(id, :password => '1234567890')
232
+ # wait as bit while the password is being changed
233
+ wail_until('> Wait while the password is being updated') do
234
+ @rackspace.get_server(id)['server']['status'] == 'ACTIVE'
235
+ end
236
+ end
237
+
238
+ def test_052_backup_schedule
239
+ id = get_test_server_id
240
+ # get a schedule
241
+ schedule = nil
242
+ assert_nothing_raised do
243
+ schedule = @rackspace.get_backup_schedule(id)
244
+ end
245
+ assert schedule['backupSchedule'].is_a?(Hash)
246
+ assert !schedule['backupSchedule']['enabled']
247
+ # Set a new schedule
248
+ assert_nothing_raised do
249
+ assert @rackspace.update_backup_schedule(id, {:enabled => true, :daily => "H_0400_0600", :weekly => 'MONDAY'})
250
+ end
251
+ # delete the schedule
252
+ assert @rackspace.update_backup_schedule(id)
253
+ end
254
+
255
+ def test_054_list_addresses_test
256
+ id = get_test_server_id
257
+ # all addresses
258
+ addresses = nil
259
+ assert_nothing_raised do
260
+ addresses = @rackspace.list_addresses(id)
261
+ end
262
+ assert addresses['addresses'].is_a?(Hash)
263
+ assert addresses['addresses']['public'].is_a?(Array)
264
+ assert addresses['addresses']['private'].is_a?(Array)
265
+ # public addresses
266
+ public_addresses = nil
267
+ assert_nothing_raised do
268
+ public_addresses = @rackspace.list_addresses(id, :public)
269
+ end
270
+ assert public_addresses.is_a?(Hash)
271
+ assert public_addresses['public'].is_a?(Array)
272
+ assert !public_addresses['public'].blank?
273
+ # private addresses
274
+ private_addresses = nil
275
+ assert_nothing_raised do
276
+ private_addresses = @rackspace.list_addresses(id, :private)
277
+ end
278
+ assert private_addresses.is_a?(Hash)
279
+ assert private_addresses['private'].is_a?(Array)
280
+ assert !private_addresses['private'].blank?
281
+ end
282
+
283
+ def test_060_delete_server
284
+ puts
285
+ puts "> Delete the test server "
286
+ assert_nothing_raised do
287
+ @rackspace.delete_server(get_test_server_id)
288
+ end
289
+ end
290
+
291
+ # KD: 2009-08-26:
292
+ # Makesure the server disappeared from both list servers calls
293
+ def test_061_wait_for_the_server_active_state
294
+ wail_until("> Wait until the test server disappears from list_servers responses:") do
295
+ id1 = get_test_server_id
296
+ id2 = get_test_server_id(true)
297
+ !id1 && !id2
298
+ end
299
+ end
300
+
301
+ end