rackspace-cloud 0.5
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.
- data/History.txt +4 -0
- data/Manifest.txt +22 -0
- data/PostInstall.txt +4 -0
- data/README.rdoc +72 -0
- data/Rakefile +17 -0
- data/lib/rackspace-cloud.rb +1 -0
- data/lib/rackspace.rb +21 -0
- data/lib/rackspace/cloud_servers/base.rb +133 -0
- data/lib/rackspace/cloud_servers/flavor.rb +13 -0
- data/lib/rackspace/cloud_servers/image.rb +8 -0
- data/lib/rackspace/cloud_servers/server.rb +8 -0
- data/lib/rackspace/connection.rb +115 -0
- data/lib/rackspace/exceptions.rb +7 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/test_base.rb +149 -0
- data/test/test_connection.rb +219 -0
- data/test/test_flavor.rb +138 -0
- data/test/test_helper.rb +64 -0
- data/test/test_image.rb +177 -0
- data/test/test_server.rb +259 -0
- metadata +114 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
lib/rackspace.rb
|
7
|
+
lib/rackspace-cloud.rb
|
8
|
+
lib/rackspace/connection.rb
|
9
|
+
lib/rackspace/exceptions.rb
|
10
|
+
lib/rackspace/cloud_servers/base.rb
|
11
|
+
lib/rackspace/cloud_servers/server.rb
|
12
|
+
lib/rackspace/cloud_servers/flavor.rb
|
13
|
+
lib/rackspace/cloud_servers/image.rb
|
14
|
+
script/console
|
15
|
+
script/destroy
|
16
|
+
script/generate
|
17
|
+
test/test_base.rb
|
18
|
+
test/test_connection.rb
|
19
|
+
test/test_flavor.rb
|
20
|
+
test/test_helper.rb
|
21
|
+
test/test_image.rb
|
22
|
+
test/test_server.rb
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= rackspace-cloud
|
2
|
+
|
3
|
+
* http://github.com/edraper/rackspace-cloud
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
rackspace-cloud is a Ruby gem that provides easy, simple access to the Rackspace Cloud APIs, and specifically the Cloud Servers API.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
Currently just has support for Cloud Servers, and not Cloud Files.
|
12
|
+
|
13
|
+
Server addresses, IP groups, backup schedules, and reboots/rebuilds/resizes not yet implemented for Cloud Servers.
|
14
|
+
|
15
|
+
== SYNOPSIS:
|
16
|
+
|
17
|
+
To initialize:
|
18
|
+
|
19
|
+
Rackspace::Connection.init "user", "key"
|
20
|
+
|
21
|
+
Optionally, you can specify a version of the API to use too (defaults to "v1.0"):
|
22
|
+
|
23
|
+
Rackspace::Connection.init "user", "key", "v1.1"
|
24
|
+
|
25
|
+
To authenticate:
|
26
|
+
|
27
|
+
Rackspace::Connection.authenticate
|
28
|
+
|
29
|
+
For Cloud Servers:
|
30
|
+
|
31
|
+
Rackspace::CloudServers::Server.all
|
32
|
+
|
33
|
+
Rackspace::CloudServers::Server.create(:name => "api-test", :imageId => 8, :flavorId => 1)
|
34
|
+
|
35
|
+
Rackspace::CloudServers::Image.all
|
36
|
+
|
37
|
+
Rackspace::CloudServers::Flavor.all
|
38
|
+
|
39
|
+
== REQUIREMENTS:
|
40
|
+
|
41
|
+
rest-client (1.0.3+)
|
42
|
+
|
43
|
+
activesupport (2.3.4+)
|
44
|
+
|
45
|
+
== INSTALL:
|
46
|
+
|
47
|
+
gem install rackspace-cloud
|
48
|
+
|
49
|
+
== LICENSE:
|
50
|
+
|
51
|
+
(The MIT License)
|
52
|
+
|
53
|
+
Copyright (c) 2009 Elliott Draper <el@ejdraper.com>
|
54
|
+
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
56
|
+
a copy of this software and associated documentation files (the
|
57
|
+
'Software'), to deal in the Software without restriction, including
|
58
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
59
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
60
|
+
permit persons to whom the Software is furnished to do so, subject to
|
61
|
+
the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be
|
64
|
+
included in all copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
67
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
68
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
69
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
70
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
71
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
72
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require File.join(File.dirname(__FILE__), "lib", "rackspace-cloud")
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
|
9
|
+
$hoe = Hoe.spec 'rackspace-cloud' do
|
10
|
+
self.developer 'Elliott Draper', 'el@ejdraper.com'
|
11
|
+
self.post_install_message = 'PostInstall.txt'
|
12
|
+
self.rubyforge_name = self.name
|
13
|
+
self.extra_deps = [['rest-client','>= 1.0.3'], ['activesupport','>= 2.3.4']]
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'newgem/tasks'
|
17
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "rackspace")
|
data/lib/rackspace.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
gem "activesupport"
|
5
|
+
require "active_support"
|
6
|
+
gem "rest-client"
|
7
|
+
require "rest_client"
|
8
|
+
|
9
|
+
module Rackspace
|
10
|
+
VERSION = '0.5'
|
11
|
+
|
12
|
+
module CloudServers
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require File.join(File.dirname(__FILE__), "rackspace", "exceptions")
|
17
|
+
require File.join(File.dirname(__FILE__), "rackspace", "connection")
|
18
|
+
require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "base")
|
19
|
+
require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "server")
|
20
|
+
require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "flavor")
|
21
|
+
require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "image")
|
@@ -0,0 +1,133 @@
|
|
1
|
+
class Rackspace::CloudServers::Base
|
2
|
+
attr_accessor :id
|
3
|
+
|
4
|
+
# The resource can be established with a hash of parameters
|
5
|
+
def initialize(attribs = {})
|
6
|
+
set_attributes(attribs)
|
7
|
+
end
|
8
|
+
|
9
|
+
# This sets the relevant accessors using the specified attributes
|
10
|
+
def set_attributes(attribs)
|
11
|
+
attribs.each_pair do |key, value|
|
12
|
+
self.send("#{key}=", value) if self.respond_to?("#{key}=")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# This returns true if the record hasn't yet been persisted, false otherwise
|
17
|
+
def new_record?
|
18
|
+
self.id.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
# This creates the record if it is new, otherwise it attempts to update the record
|
22
|
+
def save
|
23
|
+
self.new_record? ? create : update
|
24
|
+
end
|
25
|
+
|
26
|
+
# This creates the new record using the API
|
27
|
+
def create
|
28
|
+
set_attributes(JSON.parse(Rackspace::Connection.post(self.class.resource_url, {self.class.resource.singularize => JSON.parse(self.to_json)}))[self.class.resource.singularize])
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
# This updates the existing record using the API
|
33
|
+
def update
|
34
|
+
attribs = JSON.parse(self.to_json)
|
35
|
+
unless self.attributes_for_update.nil?
|
36
|
+
attribs.keys.each do |key|
|
37
|
+
attribs.delete(key) unless self.attributes_for_update.include?(key.to_sym)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
Rackspace::Connection.put(self.class.resource_url(self.id), {self.class.resource.singularize => attribs})
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
# This deletes the record using the API
|
45
|
+
def destroy
|
46
|
+
Rackspace::Connection.delete(self.class.resource_url(self.id))
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
# These are the attributes used for the update operations
|
51
|
+
# Empty array means no properties will be updated, but this can be overridden with
|
52
|
+
# nil (all properties are updated), or an explicit array of properties that can be updated
|
53
|
+
def attributes_for_update
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
# This reloads the current object with the latest persisted data
|
58
|
+
def reload
|
59
|
+
return false if self.new_record?
|
60
|
+
result = Rackspace::Connection.get(self.class.resource_url(self.id))
|
61
|
+
return nil if result.to_s.blank?
|
62
|
+
json = JSON.parse(result.to_s)
|
63
|
+
self.set_attributes(json[self.class.resource.singularize])
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
# This returns the name of the resource, used for the API URLs
|
69
|
+
def resource
|
70
|
+
self.name.split("::").last.tableize
|
71
|
+
end
|
72
|
+
|
73
|
+
# This returns the resource URL
|
74
|
+
def resource_url(id = nil)
|
75
|
+
root = "#{Rackspace::Connection.server_management_url}/#{self.resource}"
|
76
|
+
id.nil? ? root : "#{root}/#{id}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# This returns all records for the resource
|
80
|
+
def all
|
81
|
+
self.find
|
82
|
+
end
|
83
|
+
|
84
|
+
# This returns the first record for the resource
|
85
|
+
def first
|
86
|
+
self.find(:first)
|
87
|
+
end
|
88
|
+
|
89
|
+
# This returns the last record for the resource
|
90
|
+
def last
|
91
|
+
self.find(:last)
|
92
|
+
end
|
93
|
+
|
94
|
+
# This finds all records for the resource, or a specific resource by ID
|
95
|
+
def find(action = :all)
|
96
|
+
result = case action
|
97
|
+
when :all
|
98
|
+
Rackspace::Connection.get "#{self.resource_url}/detail"
|
99
|
+
when :first
|
100
|
+
Rackspace::Connection.get "#{self.resource_url}/detail"
|
101
|
+
when :last
|
102
|
+
Rackspace::Connection.get "#{self.resource_url}/detail"
|
103
|
+
else
|
104
|
+
Rackspace::Connection.get self.resource_url(action)
|
105
|
+
end
|
106
|
+
return nil if result.to_s.blank?
|
107
|
+
json = JSON.parse(result.to_s)
|
108
|
+
case action
|
109
|
+
when :all
|
110
|
+
json[self.resource].collect { |h| self.new(h) }
|
111
|
+
when :first
|
112
|
+
json[self.resource].collect { |h| self.new(h) }.first
|
113
|
+
when :last
|
114
|
+
json[self.resource].collect { |h| self.new(h) }.last
|
115
|
+
else
|
116
|
+
self.new(json[self.resource.singularize])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# This returns the amount of records for this resource
|
121
|
+
def count
|
122
|
+
records = self.all
|
123
|
+
records.nil? ? 0 : records.length
|
124
|
+
end
|
125
|
+
|
126
|
+
# This creates and saves a resource with the specified attributes in one call
|
127
|
+
def create(attribs = {})
|
128
|
+
o = self.new(attribs)
|
129
|
+
o.save
|
130
|
+
o
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Rackspace::CloudServers::Flavor < Rackspace::CloudServers::Base
|
2
|
+
attr_accessor :name, :ram, :disk
|
3
|
+
|
4
|
+
# Saving (create/update) isn't allowed for flavors
|
5
|
+
def save
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
# Deletion isn't allowed for flavors
|
10
|
+
def destroy
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Rackspace::CloudServers::Server < Rackspace::CloudServers::Base
|
2
|
+
attr_accessor :name, :imageId, :flavorId, :hostId, :status, :progress, :addresses, :metadata, :personality, :adminPass
|
3
|
+
|
4
|
+
# Overriding the update attributes so that just name and adminPass are persisted on an update
|
5
|
+
def attributes_for_update
|
6
|
+
[:name, :adminPass]
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
class Rackspace::Connection
|
2
|
+
AUTH_URL = "https://auth.api.rackspacecloud.com"
|
3
|
+
VERSION_URL = "https://servers.api.rackspacecloud.com"
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# This initializes the Rackspace environment with the data necessary to make API calls
|
7
|
+
def init(user, key, version = "v1.0")
|
8
|
+
@user = user
|
9
|
+
@key = key
|
10
|
+
@version = version
|
11
|
+
raise Rackspace::InvalidVersion unless self.versions.include?(version)
|
12
|
+
@initialized = true
|
13
|
+
end
|
14
|
+
|
15
|
+
# This returns the API user being used for calls
|
16
|
+
def api_user
|
17
|
+
@user
|
18
|
+
end
|
19
|
+
|
20
|
+
# This returns the API key being used for calls
|
21
|
+
def api_key
|
22
|
+
@key
|
23
|
+
end
|
24
|
+
|
25
|
+
# This returns the API version being used for calls
|
26
|
+
def api_version
|
27
|
+
@version
|
28
|
+
end
|
29
|
+
|
30
|
+
# This returns whether or not we've been initialized yet
|
31
|
+
def initialized?
|
32
|
+
@initialized || false
|
33
|
+
end
|
34
|
+
|
35
|
+
# This authenticates with Rackspace and returns the information necessary to make subsequent authenticated calls to the API
|
36
|
+
def authenticate
|
37
|
+
raise Rackspace::NotInitialized unless self.initialized?
|
38
|
+
headers = RestClient::Request.execute(:method => :get, :url => "#{AUTH_URL}/#{self.api_version}", :headers => {"X-Auth-User" => self.api_user, "X-Auth-Key" => self.api_key}, :raw_response => true).headers
|
39
|
+
{:auth_token => headers[:x_auth_token], :storage_url => headers[:x_storage_url], :server_management_url => headers[:x_server_management_url], :cdn_management_url => headers[:x_cdn_management_url]}
|
40
|
+
end
|
41
|
+
|
42
|
+
# These are default headers we need to use on all requests
|
43
|
+
def default_headers
|
44
|
+
{:accept => "application/json", :content_type => "application/json"}
|
45
|
+
end
|
46
|
+
|
47
|
+
# This returns the available versions of the API
|
48
|
+
def versions
|
49
|
+
JSON.parse(RestClient.get("#{VERSION_URL}/.json", self.default_headers))["versions"].collect { |v| v["id"] }.uniq
|
50
|
+
end
|
51
|
+
|
52
|
+
# This caches the authentication response for subsequent usage
|
53
|
+
def auth_response
|
54
|
+
@auth_response ||= self.authenticate
|
55
|
+
end
|
56
|
+
|
57
|
+
# This is the auth token provided by Rackspace after a successful authentication
|
58
|
+
def auth_token
|
59
|
+
self.auth_response[:auth_token]
|
60
|
+
end
|
61
|
+
|
62
|
+
# This returns the root URL for Cloud Files API queries (not yet implemented)
|
63
|
+
def storage_url
|
64
|
+
self.auth_response[:storage_url]
|
65
|
+
end
|
66
|
+
|
67
|
+
# This returns the root URL for Cloud Servers API queries
|
68
|
+
def server_management_url
|
69
|
+
self.auth_response[:server_management_url]
|
70
|
+
end
|
71
|
+
|
72
|
+
# This returns the root URL for CDN Cloud Files API queries (not yet implemented)
|
73
|
+
def cdn_management_url
|
74
|
+
self.auth_response[:cdn_management_url]
|
75
|
+
end
|
76
|
+
|
77
|
+
# This performs a basic GET request using the supplied URL and headers
|
78
|
+
def get(url, headers = {})
|
79
|
+
http :get, "#{url}.json", headers
|
80
|
+
end
|
81
|
+
|
82
|
+
# This performs a basic POST request using the supplied URL, payload and headers
|
83
|
+
def post(url, payload = {}, headers = {})
|
84
|
+
http :post, "#{url}.json", payload.to_json, headers
|
85
|
+
end
|
86
|
+
|
87
|
+
# This performs a basic PUT request using the supplied URL, payload and headers
|
88
|
+
def put(url, payload = {}, headers = {})
|
89
|
+
http :put, "#{url}.json", payload.to_json, headers
|
90
|
+
end
|
91
|
+
|
92
|
+
# This performs a basic DELETE request using the supplied URL and headers
|
93
|
+
def delete(url, headers = {})
|
94
|
+
http :delete, "#{url}.json", headers
|
95
|
+
end
|
96
|
+
|
97
|
+
# This will perform an HTTP call with the specified method, and arguments
|
98
|
+
# It will also pick up if the response is that the request was unauthorized, and will attempt
|
99
|
+
# the same request again after re-authenticating (in case the auth token has expired)
|
100
|
+
def http(method, *args)
|
101
|
+
args.last.merge!(self.default_headers).merge!("X-Auth-Token" => self.auth_token)
|
102
|
+
response = RestClient.send(method, *args)
|
103
|
+
@retried = false
|
104
|
+
response
|
105
|
+
rescue RestClient::Unauthorized
|
106
|
+
@auth_response = nil
|
107
|
+
if @retried
|
108
|
+
raise
|
109
|
+
else
|
110
|
+
@retried = true
|
111
|
+
retry
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|