ggoodale-rackspace_cloud 0.3.0 → 0.4.0
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/README +1 -1
- data/VERSION +1 -1
- data/lib/rackspace_cloud.rb +1 -63
- data/lib/rackspace_cloud/base.rb +95 -9
- data/lib/rackspace_cloud/flavor.rb +0 -6
- data/lib/rackspace_cloud/image.rb +17 -8
- data/lib/rackspace_cloud/server.rb +20 -17
- data/lib/rackspace_cloud/shared_ip_group.rb +33 -0
- data/rackspace_cloud.gemspec +3 -2
- metadata +5 -3
data/README
CHANGED
@@ -7,7 +7,7 @@ Example:
|
|
7
7
|
=> nil
|
8
8
|
>> cloud.servers
|
9
9
|
=> []
|
10
|
-
>> new_server = cloud.create_server('development', 1, 8) # create a 256MB
|
10
|
+
>> new_server = cloud.create_server('development', 1, 8) # create a 256MB Ubuntu 9.04 instance)
|
11
11
|
=> #<RackspaceCloud::Server:0x1054a78 @image=#<RackspaceCloud::Image:0x10598e8 @name="Ubuntu 9.04 (jaunty)", @updated="2009-07-20T09:14:37-05:00", @created="2009-07-20T09:14:37-05:00", @status="ACTIVE", @rackspace_id=8>, @public_ips=["174.xxx.xxx.xxx"], @flavor=#<RackspaceCloud::Flavor:0x105d40c @name="256 slice", @ram=256, @disk=10, @rackspace_id=1>, @name="development", @progress=80, @adminPass="developmentU5fG3", @host_id="a7765fde333eead221", @private_ips=["10.xxx.xxx.xxx"], @rackspace_id=1, @status="BUILD">
|
12
12
|
|
13
13
|
... wait a while ...
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/rackspace_cloud.rb
CHANGED
@@ -26,29 +26,6 @@ module RackspaceCloud
|
|
26
26
|
#This should eventually check the versions and raise an APIVersionError if there's a mismatch.
|
27
27
|
end
|
28
28
|
|
29
|
-
# storage for the lists of flavors and images we request at auth time
|
30
|
-
FLAVORS = {}
|
31
|
-
def RackspaceCloud.populate_flavors
|
32
|
-
request("/flavors/detail")['flavors'].each do |flavor|
|
33
|
-
FLAVORS[flavor['id']] = RackspaceCloud::Flavor.new(flavor)
|
34
|
-
end
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
|
38
|
-
IMAGES = {}
|
39
|
-
def RackspaceCloud.populate_images
|
40
|
-
request("/images/detail")['images'].each do |image|
|
41
|
-
IMAGES[image['id']] = RackspaceCloud::Image.new(image)
|
42
|
-
end
|
43
|
-
nil
|
44
|
-
end
|
45
|
-
|
46
|
-
LIMITS = {}
|
47
|
-
def RackspaceCloud.get_limits
|
48
|
-
LIMITS.merge!(request("/limits")['limits'])
|
49
|
-
nil
|
50
|
-
end
|
51
|
-
|
52
29
|
def RackspaceCloud.request_authorization(user, access_key)
|
53
30
|
session = Patron::Session.new
|
54
31
|
session.base_url = BASE_AUTH_URI
|
@@ -59,48 +36,9 @@ module RackspaceCloud
|
|
59
36
|
|
60
37
|
case response.status
|
61
38
|
when 204 # "No Content", which means success
|
62
|
-
|
63
|
-
@storage_url = response.headers['X-Storage-Url']
|
64
|
-
@storage_token = response.headers['X-Storage-Token']
|
65
|
-
@cdn_management_url = response.headers['X-CDN-Management-Url']
|
66
|
-
@auth_token = response.headers['X-Auth-Token']
|
67
|
-
@@authorized = true
|
39
|
+
response.headers
|
68
40
|
else
|
69
41
|
raise AuthorizationError, "Error during authorization: #{response.status}"
|
70
42
|
end
|
71
|
-
nil
|
72
|
-
end
|
73
|
-
|
74
|
-
def RackspaceCloud.request(path, options={})
|
75
|
-
raise RuntimeError, "Please authorize before using by calling connect()" unless defined?(@@authorized) && @@authorized
|
76
|
-
@session ||= begin
|
77
|
-
s = Patron::Session.new
|
78
|
-
s.base_url = @server_management_url
|
79
|
-
s.headers['X-Auth-Token'] = @auth_token
|
80
|
-
s.headers["User-Agent"] = "rackspacecloud_ruby_gem"
|
81
|
-
s.timeout = 10
|
82
|
-
s
|
83
|
-
end
|
84
|
-
response = case options[:method]
|
85
|
-
when :post
|
86
|
-
@session.headers['Accept'] = "application/json"
|
87
|
-
@session.headers['Content-Type'] = "application/json"
|
88
|
-
@session.post("#{path}", options[:data].to_json)
|
89
|
-
when :put
|
90
|
-
@session.headers['Content-Type'] = "application/json"
|
91
|
-
@session.put("#{path}", options[:data].to_json)
|
92
|
-
when :delete
|
93
|
-
@session.delete("#{path}")
|
94
|
-
else
|
95
|
-
@session.get("#{path}.json")
|
96
|
-
end
|
97
|
-
|
98
|
-
case response.status
|
99
|
-
when 200, 202, 204
|
100
|
-
JSON.parse(response.body) unless response.body.empty?
|
101
|
-
else
|
102
|
-
puts response.body
|
103
|
-
raise RuntimeError, "Error fetching #{path}: #{response.status}"
|
104
|
-
end
|
105
43
|
end
|
106
44
|
end
|
data/lib/rackspace_cloud/base.rb
CHANGED
@@ -6,22 +6,54 @@ module RackspaceCloud
|
|
6
6
|
validate_config(config)
|
7
7
|
@user = config[:user]
|
8
8
|
@access_key = config[:access_key]
|
9
|
+
@authorized = false
|
9
10
|
end
|
10
11
|
|
11
12
|
def connect
|
12
|
-
RackspaceCloud.request_authorization(@user, @access_key)
|
13
|
+
configure_service_urls(RackspaceCloud.request_authorization(@user, @access_key))
|
13
14
|
RackspaceCloud.check_version_compatibility
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
@authorized = true
|
16
|
+
populate_flavors
|
17
|
+
populate_images
|
18
|
+
get_limits
|
19
|
+
end
|
20
|
+
|
21
|
+
def authorized?
|
22
|
+
@authorized
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
}
|
25
|
+
# storage for the lists of flavors and images we request at auth time
|
26
|
+
attr_reader :flavors
|
27
|
+
def populate_flavors
|
28
|
+
@flavors ||= {}
|
29
|
+
request("/flavors/detail")['flavors'].each do |flavor|
|
30
|
+
@flavors[flavor['id']] = RackspaceCloud::Flavor.new(flavor)
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :images
|
36
|
+
def populate_images
|
37
|
+
@images ||= {}
|
38
|
+
request("/images/detail")['images'].each do |image|
|
39
|
+
@images[image['id']] = RackspaceCloud::Image.new(self, image)
|
40
|
+
end
|
41
|
+
nil
|
23
42
|
end
|
24
43
|
|
44
|
+
attr_reader :limits
|
45
|
+
def get_limits
|
46
|
+
@limits ||= {}
|
47
|
+
@limits.merge!(request("/limits")['limits'])
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def servers
|
52
|
+
request("/servers/detail")["servers"].collect {|server_json|
|
53
|
+
RackspaceCloud::Server.new(self, server_json)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
25
57
|
def create_server(name, flavor, image, metadata={}, personality=[])
|
26
58
|
new_server_data = {'server' => {
|
27
59
|
'name' => name,
|
@@ -31,7 +63,53 @@ module RackspaceCloud
|
|
31
63
|
'personality' => personality
|
32
64
|
}}
|
33
65
|
|
34
|
-
RackspaceCloud::Server.new(
|
66
|
+
RackspaceCloud::Server.new(self, request("/servers", :method => :post, :data => new_server_data)['server'])
|
67
|
+
end
|
68
|
+
|
69
|
+
def shared_ip_groups
|
70
|
+
request("/shared_ip_groups/detail")['sharedIpGroups'].collect {|group_json|
|
71
|
+
RackspaceCloud::SharedIPGroup.new(self, group_json)
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Yes, you can only specify at most a single server to initially populate a shared IP group. Odd, that.
|
76
|
+
def create_shared_ip_group(name, server=nil)
|
77
|
+
new_group_data = {'sharedIpGroup' => {'name' => name, 'server' => server}}
|
78
|
+
new_group_data['sharedIpGroup']['server'] = server.to_i unless server.nil?
|
79
|
+
RackspaceCloud::SharedIPGroup.new(self, request("/shared_ip_groups", :method => :post, :data => new_group_data))
|
80
|
+
end
|
81
|
+
|
82
|
+
def request(path, options={})
|
83
|
+
raise RuntimeError, "Please authorize before using by calling connect()" unless authorized?
|
84
|
+
@session ||= begin
|
85
|
+
s = Patron::Session.new
|
86
|
+
s.base_url = @server_management_url
|
87
|
+
s.headers['X-Auth-Token'] = @auth_token
|
88
|
+
s.headers["User-Agent"] = "rackspacecloud_ruby_gem"
|
89
|
+
s.timeout = 10
|
90
|
+
s
|
91
|
+
end
|
92
|
+
response = case options[:method]
|
93
|
+
when :post
|
94
|
+
@session.headers['Accept'] = "application/json"
|
95
|
+
@session.headers['Content-Type'] = "application/json"
|
96
|
+
@session.post("#{path}", options[:data].to_json)
|
97
|
+
when :put
|
98
|
+
@session.headers['Content-Type'] = "application/json"
|
99
|
+
@session.put("#{path}", options[:data].to_json)
|
100
|
+
when :delete
|
101
|
+
@session.delete("#{path}")
|
102
|
+
else
|
103
|
+
@session.get("#{path}.json")
|
104
|
+
end
|
105
|
+
|
106
|
+
case response.status
|
107
|
+
when 200, 201, 202, 204
|
108
|
+
JSON.parse(response.body) unless response.body.empty?
|
109
|
+
else
|
110
|
+
puts response.body
|
111
|
+
raise RuntimeError, "Error fetching #{path}: #{response.status}"
|
112
|
+
end
|
35
113
|
end
|
36
114
|
|
37
115
|
def api_version
|
@@ -46,5 +124,13 @@ module RackspaceCloud
|
|
46
124
|
(config[:access_key] && config[:access_key].length) || errors << "missing access_key"
|
47
125
|
raise(ArgumentError, "Error: invalid configuration: #{errors.join(';')}") unless errors.empty?
|
48
126
|
end
|
127
|
+
|
128
|
+
def configure_service_urls(url_hash = {})
|
129
|
+
@server_management_url = url_hash['X-Server-Management-Url']
|
130
|
+
@storage_url = url_hash['X-Storage-Url']
|
131
|
+
@storage_token = url_hash['X-Storage-Token']
|
132
|
+
@cdn_management_url = url_hash['X-CDN-Management-Url']
|
133
|
+
@auth_token = url_hash['X-Auth-Token']
|
134
|
+
end
|
49
135
|
end
|
50
136
|
end
|
@@ -2,12 +2,6 @@ module RackspaceCloud
|
|
2
2
|
class Flavor
|
3
3
|
attr_reader :name, :disk, :ram, :rackspace_id
|
4
4
|
|
5
|
-
class << self
|
6
|
-
def lookup_by_id(id)
|
7
|
-
RackspaceCloud::FLAVORS[id]
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
5
|
def initialize(flavor_json)
|
12
6
|
@rackspace_id = flavor_json['id']
|
13
7
|
@name = flavor_json['name']
|
@@ -3,21 +3,26 @@ module RackspaceCloud
|
|
3
3
|
|
4
4
|
MAX_RACKSPACE_IMAGE_ID = 10 # the highest id in use by a standard rackspace image (vs. a user backup)
|
5
5
|
|
6
|
-
attr_reader :name, :created, :updated, :status, :progress, :rackspace_id
|
6
|
+
attr_reader :name, :created, :updated, :status, :progress, :rackspace_id, :base
|
7
7
|
|
8
8
|
class << self
|
9
|
-
def
|
10
|
-
|
9
|
+
def create_from_server(server, name=nil)
|
10
|
+
name ||= generate_timestamped_name(server)
|
11
|
+
image_json = server.base.request("/images", :method => :post, :data => {'image' => {'name' => name, 'serverId' => @rackspace_id}})['image']
|
12
|
+
new_image = RackspaceCloud::Image.new(image_json)
|
13
|
+
@images[new_image.rackspace_id] = new_image
|
14
|
+
new_image
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
def initialize(image_json)
|
18
|
+
def initialize(base, image_json)
|
19
|
+
@base = base
|
15
20
|
populate(image_json)
|
16
21
|
end
|
17
22
|
|
18
23
|
def delete
|
19
|
-
raise RuntimeError, "Can't delete Rackspace
|
20
|
-
|
24
|
+
raise RuntimeError, "Can't delete Rackspace standard images" if @rackspace_id <= MAX_RACKSPACE_IMAGE_ID
|
25
|
+
@base.request("/images/#{@rackspace_id}", :method => :delete)
|
21
26
|
end
|
22
27
|
|
23
28
|
def to_i
|
@@ -25,8 +30,8 @@ module RackspaceCloud
|
|
25
30
|
end
|
26
31
|
|
27
32
|
def refresh
|
28
|
-
populate(
|
29
|
-
|
33
|
+
populate(@base.request("/images/#{@rackspace_id}")['image'])
|
34
|
+
self
|
30
35
|
end
|
31
36
|
|
32
37
|
protected
|
@@ -39,5 +44,9 @@ module RackspaceCloud
|
|
39
44
|
@status = image_json['status']
|
40
45
|
@progress = image_json['progress']
|
41
46
|
end
|
47
|
+
|
48
|
+
def generate_timestamped_name(server)
|
49
|
+
"#{server.downcase.gsub!(/\W/, "-")}-#{Time.nowTime.now.strftime('%Y%m%d%H%M%S%z')}"
|
50
|
+
end
|
42
51
|
end
|
43
52
|
end
|
@@ -1,17 +1,18 @@
|
|
1
1
|
module RackspaceCloud
|
2
2
|
class Server
|
3
|
-
attr_reader :name, :status, :flavor, :image
|
3
|
+
attr_reader :name, :status, :flavor, :image, :base
|
4
4
|
attr_reader :rackspace_id, :host_id, :progress
|
5
5
|
attr_reader :public_ips, :private_ips, :adminPass
|
6
6
|
|
7
7
|
STATUS_VALUES = %w{ACTIVE BUILD REBUILD SUSPENDED QUEUE_RESIZE PREP_RESIZE VERIFY_RESIZE PASSWORD RESCUE REBOOT HARD_REBOOT SHARE_IP SHARE_IP_NO_CONFIG DELETE_IP UNKNOWN}
|
8
8
|
|
9
|
-
def initialize(server_json)
|
9
|
+
def initialize(base, server_json)
|
10
|
+
@base = base
|
10
11
|
populate(server_json)
|
11
12
|
end
|
12
13
|
|
13
14
|
def ready?
|
14
|
-
|
15
|
+
self.refresh.status == 'ACTIVE'
|
15
16
|
end
|
16
17
|
|
17
18
|
def reboot
|
@@ -38,42 +39,44 @@ module RackspaceCloud
|
|
38
39
|
action_request('revertResize' => nil)
|
39
40
|
end
|
40
41
|
|
42
|
+
def share_ip_address(ip_address, shared_ip_address_group, configure_server = true)
|
43
|
+
@base.request("/servers/#{@rackspace_id}/ips/public/#{ip_address}",
|
44
|
+
:method => :put, :data => {'shareIp' => {'sharedIpGroupId' => shared_ip_address_group.to_i, 'configureServer' => configure_server}})
|
45
|
+
end
|
46
|
+
|
47
|
+
def unshare_ip_address(ip_address)
|
48
|
+
@base.request("/servers/#{@rackspace_id}/ips/public/#{ip_address}", :method => :delete)
|
49
|
+
end
|
50
|
+
|
41
51
|
def delete
|
42
|
-
|
52
|
+
@base.request("/servers/#{@rackspace_id}", :method => :delete)
|
43
53
|
end
|
44
54
|
|
45
55
|
def update_server_name(new_name)
|
46
|
-
|
56
|
+
@base.request("/servers/#{@rackspace_id}", :method => :put, :data => {"server" => {"name" => new_name}})
|
47
57
|
end
|
48
58
|
|
49
59
|
def update_admin_password(new_password)
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
def save_as_image(name)
|
54
|
-
image_json = RackspaceCloud.request("/images", :method => :post, :data => {'image' => {'name' => name, 'serverId' => @rackspace_id}})['image']
|
55
|
-
new_image = RackspaceCloud::Image.new(image_json)
|
56
|
-
RackspaceCloud::IMAGES[new_image.rackspace_id] = new_image
|
57
|
-
new_image
|
60
|
+
@base.request("/servers/#{@rackspace_id}", :method => :put, :data => {"server" => {"adminPass" => new_password}})
|
58
61
|
end
|
59
62
|
|
60
63
|
# update this server's status and progress by calling /servers/<id>
|
61
64
|
def refresh
|
62
|
-
populate(
|
65
|
+
populate(@base.request("/servers/#{@rackspace_id}")['server'])
|
63
66
|
self
|
64
67
|
end
|
65
68
|
|
66
69
|
protected
|
67
70
|
|
68
71
|
def action_request(data)
|
69
|
-
|
72
|
+
@base.request("/servers/#{@rackspace_id}/action", :method => :post, :data => data)
|
70
73
|
end
|
71
74
|
|
72
75
|
def populate(server_json)
|
73
76
|
@name = server_json['name']
|
74
77
|
@status = server_json['status']
|
75
|
-
@flavor =
|
76
|
-
@image =
|
78
|
+
@flavor = @base.flavors[server_json['flavorId']]
|
79
|
+
@image = @base.images[server_json['imageId']]
|
77
80
|
@rackspace_id = server_json['id']
|
78
81
|
@host_id = server_json['hostId']
|
79
82
|
@progress = server_json['progress']
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RackspaceCloud
|
2
|
+
class SharedIPGroup
|
3
|
+
attr_reader :rackspace_id, :name, :servers
|
4
|
+
|
5
|
+
def initialize(base, group_json)
|
6
|
+
@base = base
|
7
|
+
populate(group_json)
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete
|
11
|
+
@base.request("/shared_ip_groups/#{@rackspace_id}", :method => :delete)
|
12
|
+
end
|
13
|
+
|
14
|
+
def refresh
|
15
|
+
populate(@base.request("/shared_ip_groups/detail")['sharedIpGroup'])
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_i
|
19
|
+
@rackspace_id
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def populate(group_json)
|
25
|
+
puts group_json.inspect
|
26
|
+
@rackspace_id = group_json['id']
|
27
|
+
@name = group_json['name']
|
28
|
+
unless group_json['servers'].nil?
|
29
|
+
@servers = @base.servers.select{|server| group_json['servers'].include? server.rackspace_id}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/rackspace_cloud.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rackspace_cloud}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.4.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Grant Goodale"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-08-03}
|
10
10
|
s.description = %q{Gem enabling the management of Rackspace Cloud Server instances.}
|
11
11
|
s.email = %q{grant@moreblinktag.com}
|
12
12
|
s.extra_rdoc_files = [
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
"lib/rackspace_cloud/flavor.rb",
|
25
25
|
"lib/rackspace_cloud/image.rb",
|
26
26
|
"lib/rackspace_cloud/server.rb",
|
27
|
+
"lib/rackspace_cloud/shared_ip_group.rb",
|
27
28
|
"rackspace_cloud.gemspec",
|
28
29
|
"test/authentication_test.rb",
|
29
30
|
"test/configuration_test.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ggoodale-rackspace_cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Grant Goodale
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-03 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,12 +42,14 @@ files:
|
|
42
42
|
- lib/rackspace_cloud/flavor.rb
|
43
43
|
- lib/rackspace_cloud/image.rb
|
44
44
|
- lib/rackspace_cloud/server.rb
|
45
|
+
- lib/rackspace_cloud/shared_ip_group.rb
|
45
46
|
- rackspace_cloud.gemspec
|
46
47
|
- test/authentication_test.rb
|
47
48
|
- test/configuration_test.rb
|
48
49
|
- test/test_helper.rb
|
49
50
|
has_rdoc: false
|
50
51
|
homepage: http://github.com/ggoodale/rackspace_cloud
|
52
|
+
licenses:
|
51
53
|
post_install_message:
|
52
54
|
rdoc_options:
|
53
55
|
- --charset=UTF-8
|
@@ -68,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
70
|
requirements: []
|
69
71
|
|
70
72
|
rubyforge_project:
|
71
|
-
rubygems_version: 1.
|
73
|
+
rubygems_version: 1.3.5
|
72
74
|
signing_key:
|
73
75
|
specification_version: 3
|
74
76
|
summary: Gem enabling the management of Rackspace Cloud Server instances
|