rackspace-cloud 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.5 2009-10-11
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
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
@@ -0,0 +1,4 @@
1
+
2
+ For more information on rackspace-cloud, see http://github.com/edraper/rackspace-cloud
3
+
4
+
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::Image < Rackspace::CloudServers::Base
2
+ attr_accessor :name, :serverId, :updated, :created, :status, :progress
3
+
4
+ # Updating isn't allowed for images
5
+ def update
6
+ false
7
+ end
8
+ 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
@@ -0,0 +1,7 @@
1
+ module Rackspace
2
+ class InvalidVersion < Exception
3
+ end
4
+
5
+ class NotInitialized < Exception
6
+ end
7
+ end