openstack-compute 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,7 @@
1
+ Unless otherwise noted, all files are released under the MIT license, exceptions contain licensing information in them.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,72 @@
1
+ = Ruby OpenStack
2
+
3
+ == Description
4
+
5
+ Ruby Openstack Compute binding.
6
+
7
+ == Examples
8
+
9
+ See the class definitions for documentation on specific methods and operations.
10
+
11
+ require 'openstack/compute'
12
+
13
+ cs = OpenStack::Compute::Connection.new(:username => USERNAME, :api_key => API_KEY, :api_url => API_URL)
14
+
15
+ # Get a listing of all current servers
16
+ >> cs.servers
17
+ => [{:name=>"RenamedRubyTest", :id=>110917}]
18
+
19
+ # Access a specific server
20
+ >> server = cs.server(110917)
21
+ >> server.name
22
+ => "RenamedRubyTest"
23
+
24
+ # or...
25
+ server_manager.find(110917)
26
+
27
+
28
+ # See what type of server this is
29
+ >> server.flavor.name
30
+ => "256 server"
31
+ >> server.image.name
32
+ => "Ubuntu 8.04.2 LTS (hardy)"
33
+
34
+ # Soft-reboot the server
35
+ >> server.reboot
36
+ => true
37
+
38
+ # Create a new 512MB CentOS 5.2 server. The root password is returned in the adminPass method.
39
+ >> image = cs.get_image(8)
40
+ => #<OpenStack::Compute::Image:0x1014a8060 ...>, status"ACTIVE"
41
+ >> image.name
42
+ => "CentOS 5.2"
43
+ >> flavor = cs.get_flavor(2)
44
+ => #<OpenStack::Compute::Flavor:0x101469130 @disk=20, @name="512 server", @id=2, @ram=512>
45
+ >> flavor.name
46
+ => "512 server"
47
+ >> newserver = cs.create_server(:name => "New Server", :imageId => image.id, :flavorId => flavor.id)
48
+ => #<OpenStack::Compute::Server:0x101433f08 ....
49
+ >> newserver.status
50
+ => "BUILD"
51
+ >> newserver.progress
52
+ => 0
53
+ >> newserver.adminPass
54
+ => "NewServerMbhzUnO"
55
+ >> newserver.refresh
56
+ => true
57
+ >> newserver.progress
58
+ => 12
59
+
60
+ # Delete the new server
61
+ >> newserver.delete!
62
+ => true
63
+
64
+ == Authors
65
+
66
+ By Dan Prince <dan.prince@rackspace.com>.
67
+
68
+ Based on the Rackspace Cloud Servers Ruby API.
69
+
70
+ == License
71
+
72
+ See COPYING for license information.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # == Ruby OpenStack Compute API
4
+ #
5
+ # See COPYING for license information.
6
+ # ----
7
+ #
8
+ # === Documentation & Examples
9
+ # To begin reviewing the available methods and examples, view the README.rdoc file, or begin by looking at documentation for the OpenStack::Compute::Connection class.
10
+ #
11
+ # Example:
12
+ # OpenStack::Compute::Connection.new(:username => USERNAME, :api_key => API_KEY, :api_url => API_URL) method.
13
+ module OpenStack
14
+ module Compute
15
+
16
+ VERSION = IO.read(File.dirname(__FILE__) + '/../../VERSION')
17
+ require 'net/http'
18
+ require 'net/https'
19
+ require 'uri'
20
+ require 'rubygems'
21
+ require 'json'
22
+ require 'date'
23
+
24
+ unless "".respond_to? :each_char
25
+ require "jcode"
26
+ $KCODE = 'u'
27
+ end
28
+
29
+ $:.unshift(File.dirname(__FILE__))
30
+ require 'compute/authentication'
31
+ require 'compute/connection'
32
+ require 'compute/server'
33
+ require 'compute/image'
34
+ require 'compute/flavor'
35
+ require 'compute/shared_ip_group'
36
+ require 'compute/exception'
37
+
38
+ # Constants that set limits on server creation
39
+ MAX_PERSONALITY_ITEMS = 5
40
+ MAX_PERSONALITY_FILE_SIZE = 10240
41
+ MAX_SERVER_PATH_LENGTH = 255
42
+ MAX_PERSONALITY_METADATA_ITEMS = 5
43
+
44
+ # Helper method to recursively symbolize hash keys.
45
+ def self.symbolize_keys(obj)
46
+ case obj
47
+ when Array
48
+ obj.inject([]){|res, val|
49
+ res << case val
50
+ when Hash, Array
51
+ symbolize_keys(val)
52
+ else
53
+ val
54
+ end
55
+ res
56
+ }
57
+ when Hash
58
+ obj.inject({}){|res, (key, val)|
59
+ nkey = case key
60
+ when String
61
+ key.to_sym
62
+ else
63
+ key
64
+ end
65
+ nval = case val
66
+ when Hash, Array
67
+ symbolize_keys(val)
68
+ else
69
+ val
70
+ end
71
+ res[nkey] = nval
72
+ res
73
+ }
74
+ else
75
+ obj
76
+ end
77
+ end
78
+
79
+ def self.paginate(options = {})
80
+ path_args = []
81
+ path_args.push(URI.encode("limit=#{options[:limit]}")) if options[:limit]
82
+ path_args.push(URI.encode("offset=#{options[:offset]}")) if options[:offset]
83
+ path_args.join("&")
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,42 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Authentication
4
+
5
+ # Performs an authentication to the OpenStack authorization servers. Opens a new HTTP connection to the API server,
6
+ # sends the credentials, and looks for a successful authentication. If it succeeds, it sets the svrmgmthost,
7
+ # svrmgtpath, svrmgmtport, svrmgmtscheme, authtoken, and authok variables on the connection. If it fails, it raises
8
+ # an exception.
9
+ #
10
+ # Should probably never be called directly.
11
+ def initialize(connection)
12
+ path = '/v1.0'
13
+ hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
14
+ begin
15
+ server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.api_host, connection.api_port)
16
+ if connection.api_scheme == "https"
17
+ server.use_ssl = true
18
+ server.verify_mode = OpenSSL::SSL::VERIFY_NONE
19
+ end
20
+ server.start
21
+ rescue
22
+ raise OpenStack::Compute::Exception::Connection, "Unable to connect to #{server}"
23
+ end
24
+ response = server.get(path,hdrhash)
25
+ if (response.code == "204")
26
+ connection.authtoken = response["x-auth-token"]
27
+ connection.svrmgmthost = URI.parse(response["x-server-management-url"]).host
28
+ connection.svrmgmtpath = URI.parse(response["x-server-management-url"]).path
29
+ # Force the path into the v1.0 URL space
30
+ connection.svrmgmtpath.sub!(/\/.*?\//, '/v1.0/')
31
+ connection.svrmgmtport = URI.parse(response["x-server-management-url"]).port
32
+ connection.svrmgmtscheme = URI.parse(response["x-server-management-url"]).scheme
33
+ connection.authok = true
34
+ else
35
+ connection.authtoken = false
36
+ raise OpenStack::Compute::Exception::Authentication, "Authentication failed with response code #{response.code}"
37
+ end
38
+ server.finish
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,349 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Connection
4
+
5
+ attr_reader :authuser
6
+ attr_reader :authkey
7
+ attr_accessor :authtoken
8
+ attr_accessor :authok
9
+ attr_accessor :svrmgmthost
10
+ attr_accessor :svrmgmtpath
11
+ attr_accessor :svrmgmtport
12
+ attr_accessor :svrmgmtscheme
13
+ attr_reader :api_host
14
+ attr_reader :api_port
15
+ attr_reader :api_scheme
16
+ attr_reader :proxy_host
17
+ attr_reader :proxy_port
18
+
19
+ # Creates a new OpenStack::Compute::Connection object. Uses OpenStack::Compute::Authentication to perform the login for the connection.
20
+ #
21
+ # The constructor takes a hash of options, including:
22
+ #
23
+ # :username - Your Openstack username *required*
24
+ # :api_key - Your Openstack API key *required*
25
+ # :api_url - The url of the Openstack Compute API server.
26
+ # :retry_auth - Whether to retry if your auth token expires (defaults to true)
27
+ # :proxy_host - If you need to connect through a proxy, supply the hostname here
28
+ # :proxy_port - If you need to connect through a proxy, supply the port here
29
+ #
30
+ # cf = OpenStack::Compute::Connection.new(:username => 'USERNAME', :api_key => 'API_KEY', :api_url => 'API_URL')
31
+ def initialize(options = {:retry_auth => true})
32
+ @authuser = options[:username] || (raise Exception::MissingArgument, "Must supply a :username")
33
+ @authkey = options[:api_key] || (raise Exception::MissingArgument, "Must supply an :api_key")
34
+ @api_url = options[:api_url] || (raise Exception::MissingArgument, "Must supply an :api_url")
35
+
36
+ api_uri=nil
37
+ begin
38
+ api_uri=URI.parse(@api_url)
39
+ rescue Exception => e
40
+ raise Exception::InvalidArgument, "Invalid :api_url parameter: #{e.message}"
41
+ end
42
+ raise Exception::InvalidArgument, "Invalid :api_url parameter." if api_uri.nil? or api_uri.host.nil?
43
+ @api_host = api_uri.host
44
+ @api_port = api_uri.port
45
+ @api_scheme = api_uri.scheme
46
+
47
+ @retry_auth = options[:retry_auth]
48
+ @proxy_host = options[:proxy_host]
49
+ @proxy_port = options[:proxy_port]
50
+ @authok = false
51
+ @http = {}
52
+ OpenStack::Compute::Authentication.new(self)
53
+ end
54
+
55
+ # Returns true if the authentication was successful and returns false otherwise.
56
+ #
57
+ # cs.authok?
58
+ # => true
59
+ def authok?
60
+ @authok
61
+ end
62
+
63
+ # This method actually makes the HTTP REST calls out to the server
64
+ def csreq(method,server,path,port,scheme,headers = {},data = nil,attempts = 0) # :nodoc:
65
+ start = Time.now
66
+ hdrhash = headerprep(headers)
67
+ start_http(server,path,port,scheme,hdrhash)
68
+ request = Net::HTTP.const_get(method.to_s.capitalize).new(path,hdrhash)
69
+ request.body = data
70
+ response = @http[server].request(request)
71
+ raise OpenStack::Compute::Exception::ExpiredAuthToken if response.code == "401"
72
+ response
73
+ rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError
74
+ # Server closed the connection, retry
75
+ raise OpenStack::Compute::Exception::Connection, "Unable to reconnect to #{server} after #{attempts} attempts" if attempts >= 5
76
+ attempts += 1
77
+ @http[server].finish if @http[server].started?
78
+ start_http(server,path,port,scheme,headers)
79
+ retry
80
+ rescue OpenStack::Compute::Exception::ExpiredAuthToken
81
+ raise OpenStack::Compute::Exception::Connection, "Authentication token expired and you have requested not to retry" if @retry_auth == false
82
+ CloudFiles::Authentication.new(self)
83
+ retry
84
+ end
85
+
86
+ # Returns the OpenStack::Compute::Server object identified by the given id.
87
+ #
88
+ # >> server = cs.get_server(110917)
89
+ # => #<OpenStack::Compute::Server:0x101407ae8 ...>
90
+ # >> server.name
91
+ # => "MyServer"
92
+ def get_server(id)
93
+ OpenStack::Compute::Server.new(self,id)
94
+ end
95
+ alias :server :get_server
96
+
97
+ # Returns an array of hashes, one for each server that exists under this account. The hash keys are :name and :id.
98
+ #
99
+ # You can also provide :limit and :offset parameters to handle pagination.
100
+ #
101
+ # >> cs.list_servers
102
+ # => [{:name=>"MyServer", :id=>110917}]
103
+ #
104
+ # >> cs.list_servers(:limit => 2, :offset => 3)
105
+ # => [{:name=>"demo-standingcloud-lts", :id=>168867},
106
+ # {:name=>"demo-aicache1", :id=>187853}]
107
+ def list_servers(options = {})
108
+ anti_cache_param="cacheid=#{Time.now.to_i}"
109
+ path = OpenStack::Compute.paginate(options).empty? ? "#{svrmgmtpath}/servers?#{anti_cache_param}" : "#{svrmgmtpath}/servers?#{OpenStack::Compute.paginate(options)}&#{anti_cache_param}"
110
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
111
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
112
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)["servers"])
113
+ end
114
+ alias :servers :list_servers
115
+
116
+ # Returns an array of hashes with more details about each server that exists under this account. Additional information
117
+ # includes public and private IP addresses, status, hostID, and more. All hash keys are symbols except for the metadata
118
+ # hash, which are verbatim strings.
119
+ #
120
+ # You can also provide :limit and :offset parameters to handle pagination.
121
+ # >> cs.list_servers_detail
122
+ # => [{:name=>"MyServer", :addresses=>{:public=>["67.23.42.37"], :private=>["10.176.241.237"]}, :metadata=>{"MyData" => "Valid"}, :imageId=>10, :progress=>100, :hostId=>"36143b12e9e48998c2aef79b50e144d2", :flavorId=>1, :id=>110917, :status=>"ACTIVE"}]
123
+ #
124
+ # >> cs.list_servers_detail(:limit => 2, :offset => 3)
125
+ # => [{:status=>"ACTIVE", :imageId=>10, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-standingcloud-lts", :id=>168867, :flavorId=>1, :hostId=>"xxxxxx"},
126
+ # {:status=>"ACTIVE", :imageId=>8, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-aicache1", :id=>187853, :flavorId=>3, :hostId=>"xxxxxx"}]
127
+ def list_servers_detail(options = {})
128
+ path = OpenStack::Compute.paginate(options).empty? ? "#{svrmgmtpath}/servers/detail" : "#{svrmgmtpath}/servers/detail?#{OpenStack::Compute.paginate(options)}"
129
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
130
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
131
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)["servers"])
132
+ end
133
+ alias :servers_detail :list_servers_detail
134
+
135
+ # Creates a new server instance on OpenStack Compute
136
+ #
137
+ # The argument is a hash of options. The keys :name, :flavorId, and :imageId are required, :metadata and :personality are optional.
138
+ #
139
+ # :flavorId and :imageId are numbers identifying a particular server flavor and image to use when building the server. The :imageId can either
140
+ # be a stock image, or one of your own created with the server.create_image method.
141
+ #
142
+ # The :metadata argument will take a hash of up to five key/value pairs. This metadata will be applied to the server at the OpenStack Compute
143
+ # API level.
144
+ #
145
+ # The "Personality" option allows you to include up to five files, of 10Kb or less in size, that will be placed on the created server.
146
+ # For :personality, pass a hash of the form {'local_path' => 'server_path'}. The file located at local_path will be base64-encoded
147
+ # and placed at the location identified by server_path on the new server.
148
+ #
149
+ # Returns a OpenStack::Compute::Server object. The root password is available in the adminPass instance method.
150
+ #
151
+ # >> server = cs.create_server(:name => "New Server", :imageId => 2, :flavorId => 2, :metadata => {'Racker' => 'Fanatical'}, :personality => {'/Users/me/Pictures/wedding.jpg' => '/root/me.jpg'})
152
+ # => #<OpenStack::Compute::Server:0x101229eb0 ...>
153
+ # >> server.name
154
+ # => "NewServer"
155
+ # >> server.status
156
+ # => "BUILD"
157
+ # >> server.adminPass
158
+ # => "NewServerSHMGpvI"
159
+ def create_server(options)
160
+ raise OpenStack::Compute::Exception::MissingArgument, "Server name, flavor ID, and image ID must be supplied" unless (options[:name] && options[:flavorId] && options[:imageId])
161
+ options[:personality] = get_personality(options[:personality])
162
+ raise TooManyMetadataItems, "Metadata is limited to a total of #{MAX_PERSONALITY_METADATA_ITEMS} key/value pairs" if options[:metadata].is_a?(Hash) && options[:metadata].keys.size > MAX_PERSONALITY_METADATA_ITEMS
163
+ data = JSON.generate(:server => options)
164
+ response = csreq("POST",svrmgmthost,"#{svrmgmtpath}/servers",svrmgmtport,svrmgmtscheme,{'content-type' => 'application/json'},data)
165
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
166
+ server_info = JSON.parse(response.body)['server']
167
+ server = OpenStack::Compute::Server.new(self,server_info['id'])
168
+ server.adminPass = server_info['adminPass']
169
+ return server
170
+ end
171
+
172
+ # Returns an array of hashes listing available server images that you have access too, including stock OpenStack Compute images and
173
+ # any that you have created. The "id" key in the hash can be used where imageId is required.
174
+ #
175
+ # You can also provide :limit and :offset parameters to handle pagination.
176
+ #
177
+ # >> cs.list_images
178
+ # => [{:name=>"CentOS 5.2", :id=>2, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},
179
+ # {:name=>"Gentoo 2008.0", :id=>3, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},...
180
+ #
181
+ # >> cs.list_images(:limit => 3, :offset => 2)
182
+ # => [{:status=>"ACTIVE", :name=>"Fedora 11 (Leonidas)", :updated=>"2009-12-08T13:50:45-06:00", :id=>13},
183
+ # {:status=>"ACTIVE", :name=>"CentOS 5.3", :updated=>"2009-08-26T14:59:52-05:00", :id=>7},
184
+ # {:status=>"ACTIVE", :name=>"CentOS 5.4", :updated=>"2009-12-16T01:02:17-06:00", :id=>187811}]
185
+ def list_images(options = {})
186
+ path = OpenStack::Compute.paginate(options).empty? ? "#{svrmgmtpath}/images/detail" : "#{svrmgmtpath}/images/detail?#{OpenStack::Compute.paginate(options)}"
187
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
188
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
189
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)['images'])
190
+ end
191
+ alias :images :list_images
192
+
193
+ # Returns a OpenStack::Compute::Image object for the image identified by the provided id.
194
+ #
195
+ # >> image = cs.get_image(8)
196
+ # => #<OpenStack::Compute::Image:0x101659698 ...>
197
+ def get_image(id)
198
+ OpenStack::Compute::Image.new(self,id)
199
+ end
200
+ alias :image :get_image
201
+
202
+ # Returns an array of hashes listing all available server flavors. The :id key in the hash can be used when flavorId is required.
203
+ #
204
+ # You can also provide :limit and :offset parameters to handle pagination.
205
+ #
206
+ # >> cs.list_flavors
207
+ # => [{:name=>"256 server", :id=>1, :ram=>256, :disk=>10},
208
+ # {:name=>"512 server", :id=>2, :ram=>512, :disk=>20},...
209
+ #
210
+ # >> cs.list_flavors(:limit => 3, :offset => 2)
211
+ # => [{:ram=>1024, :disk=>40, :name=>"1GB server", :id=>3},
212
+ # {:ram=>2048, :disk=>80, :name=>"2GB server", :id=>4},
213
+ # {:ram=>4096, :disk=>160, :name=>"4GB server", :id=>5}]
214
+ def list_flavors(options = {})
215
+ path = OpenStack::Compute.paginate(options).empty? ? "#{svrmgmtpath}/flavors/detail" : "#{svrmgmtpath}/flavors/detail?#{OpenStack::Compute.paginate(options)}"
216
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
217
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
218
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)['flavors'])
219
+ end
220
+ alias :flavors :list_flavors
221
+
222
+ # Returns a OpenStack::Compute::Flavor object for the flavor identified by the provided ID.
223
+ #
224
+ # >> flavor = cs.flavor(1)
225
+ # => #<OpenStack::Compute::Flavor:0x10156dcc0 @name="256 server", @disk=10, @id=1, @ram=256>
226
+ def get_flavor(id)
227
+ OpenStack::Compute::Flavor.new(self,id)
228
+ end
229
+ alias :flavor :get_flavor
230
+
231
+ # Returns an array of hashes for all Shared IP Groups that are available. The :id key can be used to find that specific object.
232
+ #
233
+ # You can also provide :limit and :offset parameters to handle pagination.
234
+ #
235
+ # >> cs.list_shared_ip_groups
236
+ # => [{:name=>"New Group", :id=>127}]
237
+ def list_shared_ip_groups(options = {})
238
+ path = OpenStack::Compute.paginate(options).empty? ? "#{svrmgmtpath}/shared_ip_groups/detail" : "#{svrmgmtpath}/shared_ip_groups/detail?#{OpenStack::Compute.paginate(options)}"
239
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
240
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
241
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)['sharedIpGroups'])
242
+ end
243
+ alias :shared_ip_groups :list_shared_ip_groups
244
+
245
+ # Returns a OpenStack::Compute::SharedIPGroup object for the IP group identified by the provided ID.
246
+ #
247
+ # >> sig = cs.get_shared_ip_group(127)
248
+ # => #<OpenStack::Compute::SharedIPGroup:0x10153ca30 ...>
249
+ def get_shared_ip_group(id)
250
+ OpenStack::Compute::SharedIPGroup.new(self,id)
251
+ end
252
+ alias :shared_ip_group :get_shared_ip_group
253
+
254
+ # Creates a new Shared IP group. Takes a hash as an argument.
255
+ #
256
+ # Valid hash keys are :name (required) and :server (optional), which indicates the one server to place into this group by default.
257
+ #
258
+ # >> sig = cs.create_shared_ip_group(:name => "Production Web", :server => 110917)
259
+ # => #<OpenStack::Compute::SharedIPGroup:0x101501d18 ...>
260
+ # >> sig.name
261
+ # => "Production Web"
262
+ # >> sig.servers
263
+ # => [110917]
264
+ def create_shared_ip_group(options)
265
+ data = JSON.generate(:sharedIpGroup => options)
266
+ response = csreq("POST",svrmgmthost,"#{svrmgmtpath}/shared_ip_groups",svrmgmtport,svrmgmtscheme,{'content-type' => 'application/json'},data)
267
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
268
+ ip_group = JSON.parse(response.body)['sharedIpGroup']
269
+ OpenStack::Compute::SharedIPGroup.new(self,ip_group['id'])
270
+ end
271
+
272
+ # Returns the current state of the programatic API limits. Each account has certain limits on the number of resources
273
+ # allowed in the account, and a rate of API operations.
274
+ #
275
+ # The operation returns a hash. The :absolute hash key reveals the account resource limits, including the maxmimum
276
+ # amount of total RAM that can be allocated (combined among all servers), the maximum members of an IP group, and the
277
+ # maximum number of IP groups that can be created.
278
+ #
279
+ # The :rate hash key returns an array of hashes indicating the limits on the number of operations that can be performed in a
280
+ # given amount of time. An entry in this array looks like:
281
+ #
282
+ # {:regex=>"^/servers", :value=>50, :verb=>"POST", :remaining=>50, :unit=>"DAY", :resetTime=>1272399820, :URI=>"/servers*"}
283
+ #
284
+ # This indicates that you can only run 50 POST operations against URLs in the /servers URI space per day, we have not run
285
+ # any operations today (50 remaining), and gives the Unix time that the limits reset.
286
+ #
287
+ # Another example is:
288
+ #
289
+ # {:regex=>".*", :value=>10, :verb=>"PUT", :remaining=>10, :unit=>"MINUTE", :resetTime=>1272399820, :URI=>"*"}
290
+ #
291
+ # This says that you can run 10 PUT operations on all possible URLs per minute, and also gives the number remaining and the
292
+ # time that the limit resets.
293
+ #
294
+ # Use this information as you're building your applications to put in relevant pauses if you approach your API limitations.
295
+ def limits
296
+ response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/limits",svrmgmtport,svrmgmtscheme)
297
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
298
+ OpenStack::Compute.symbolize_keys(JSON.parse(response.body)['limits'])
299
+ end
300
+
301
+ private
302
+
303
+ # Sets up standard HTTP headers
304
+ def headerprep(headers = {}) # :nodoc:
305
+ default_headers = {}
306
+ default_headers["X-Auth-Token"] = @authtoken if (authok? && @account.nil?)
307
+ default_headers["X-Storage-Token"] = @authtoken if (authok? && !@account.nil?)
308
+ default_headers["Connection"] = "Keep-Alive"
309
+ default_headers["User-Agent"] = "OpenStack::Compute Ruby API #{VERSION}"
310
+ default_headers["Accept"] = "application/json"
311
+ default_headers.merge(headers)
312
+ end
313
+
314
+ # Starts (or restarts) the HTTP connection
315
+ def start_http(server,path,port,scheme,headers) # :nodoc:
316
+ if (@http[server].nil?)
317
+ begin
318
+ @http[server] = Net::HTTP::Proxy(self.proxy_host, self.proxy_port).new(server,port)
319
+ if scheme == "https"
320
+ @http[server].use_ssl = true
321
+ @http[server].verify_mode = OpenSSL::SSL::VERIFY_NONE
322
+ end
323
+ @http[server].start
324
+ rescue
325
+ raise ConnectionException, "Unable to connect to #{server}"
326
+ end
327
+ end
328
+ end
329
+
330
+ # Handles parsing the Personality hash to load it up with Base64-encoded data.
331
+ def get_personality(options)
332
+ return if options.nil?
333
+ require 'base64'
334
+ data = []
335
+ itemcount = 0
336
+ options.each do |localpath,svrpath|
337
+ raise OpenStack::Compute::Exception::TooManyPersonalityItems, "Personality files are limited to a total of #{MAX_PERSONALITY_ITEMS} items" if itemcount >= MAX_PERSONALITY_ITEMS
338
+ raise OpenStack::Compute::Exception::PersonalityFilePathTooLong, "Server-side path of #{svrpath} exceeds the maximum length of #{MAX_SERVER_PATH_LENGTH} characters" if svrpath.size > MAX_SERVER_PATH_LENGTH
339
+ raise OpenStack::Compute::Exception::PersonalityFileTooLarge, "Local file #{localpath} exceeds the maximum size of #{MAX_PERSONALITY_FILE_SIZE} bytes" if File.size(localpath) > MAX_PERSONALITY_FILE_SIZE
340
+ b64 = Base64.encode64(IO.read(localpath))
341
+ data.push({:path => svrpath, :contents => b64})
342
+ itemcount += 1
343
+ end
344
+ return data
345
+ end
346
+
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,6 @@
1
+ module OpenStack
2
+ module Compute
3
+ class EntityManager
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,87 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Exception
4
+
5
+ class ComputeError < StandardError
6
+
7
+ attr_reader :response_body
8
+ attr_reader :response_code
9
+
10
+ def initialize(message, code, response_body)
11
+ @response_code=code
12
+ @response_body=response_body
13
+ super(message)
14
+ end
15
+
16
+ end
17
+
18
+ class ComputeFault < ComputeError # :nodoc:
19
+ end
20
+ class ServiceUnavailable < ComputeError # :nodoc:
21
+ end
22
+ class Unauthorized < ComputeError # :nodoc:
23
+ end
24
+ class BadRequest < ComputeError # :nodoc:
25
+ end
26
+ class OverLimit < ComputeError # :nodoc:
27
+ end
28
+ class BadMediaType < ComputeError # :nodoc:
29
+ end
30
+ class BadMethod < ComputeError # :nodoc:
31
+ end
32
+ class ItemNotFound < ComputeError # :nodoc:
33
+ end
34
+ class BuildInProgress < ComputeError # :nodoc:
35
+ end
36
+ class ServerCapacityUnavailable < ComputeError # :nodoc:
37
+ end
38
+ class BackupOrResizeInProgress < ComputeError # :nodoc:
39
+ end
40
+ class ResizeNotAllowed < ComputeError # :nodoc:
41
+ end
42
+ class NotImplemented < ComputeError # :nodoc:
43
+ end
44
+ class Other < ComputeError # :nodoc:
45
+ end
46
+
47
+ # Plus some others that we define here
48
+
49
+ class ExpiredAuthToken < StandardError # :nodoc:
50
+ end
51
+ class MissingArgument < StandardError # :nodoc:
52
+ end
53
+ class InvalidArgument < StandardError # :nodoc:
54
+ end
55
+ class TooManyPersonalityItems < StandardError # :nodoc:
56
+ end
57
+ class PersonalityFilePathTooLong < StandardError # :nodoc:
58
+ end
59
+ class PersonalityFileTooLarge < StandardError # :nodoc:
60
+ end
61
+ class Authentication < StandardError # :nodoc:
62
+ end
63
+ class Connection < StandardError # :nodoc:
64
+ end
65
+
66
+ # In the event of a non-200 HTTP status code, this method takes the HTTP response, parses
67
+ # the JSON from the body to get more information about the exception, then raises the
68
+ # proper error. Note that all exceptions are scoped in the OpenStack::Compute::Exception namespace.
69
+ def self.raise_exception(response)
70
+ return if response.code =~ /^20.$/
71
+ begin
72
+ fault = nil
73
+ info = nil
74
+ JSON.parse(response.body).each_pair do |key, val|
75
+ fault=key
76
+ info=val
77
+ end
78
+ exception_class = self.const_get(fault[0,1].capitalize+fault[1,fault.length])
79
+ raise exception_class.new(info["message"], response.code, response.body)
80
+ rescue NameError
81
+ raise OpenStack::Compute::Exception::Other.new("The server returned status #{response.code}", response.code, response.body)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,33 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Flavor
4
+
5
+ attr_reader :id
6
+ attr_reader :name
7
+ attr_reader :ram
8
+ attr_reader :disk
9
+
10
+ # This class provides an object for the "Flavor" of a server. The Flavor can generally be taken as the server specification,
11
+ # providing information on things like memory and disk space.
12
+ #
13
+ # The disk attribute is an integer representing the disk space in GB. The memory attribute is an integer representing the RAM in MB.
14
+ #
15
+ # This is called from the get_flavor method on a OpenStack::Compute::Connection object, returns a OpenStack::Compute::Flavor object, and will likely not be called directly.
16
+ #
17
+ # >> flavor = cs.get_flavor(1)
18
+ # => #<OpenStack::Compute::Flavor:0x1014f8bc8 @name="256 server", @disk=10, @id=1, @ram=256>
19
+ # >> flavor.name
20
+ # => "256 server"
21
+ def initialize(connection,id)
22
+ response = connection.csreq("GET",connection.svrmgmthost,"#{connection.svrmgmtpath}/flavors/#{URI.escape(id.to_s)}",connection.svrmgmtport,connection.svrmgmtscheme)
23
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
24
+ data = JSON.parse(response.body)['flavor']
25
+ @id = data['id']
26
+ @name = data['name']
27
+ @ram = data['ram']
28
+ @disk = data['disk']
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Image
4
+
5
+ attr_reader :id
6
+ attr_reader :name
7
+ attr_reader :serverId
8
+ attr_reader :updated
9
+ attr_reader :created
10
+ attr_reader :status
11
+ attr_reader :progress
12
+
13
+ # This class provides an object for the "Image" of a server. The Image refers to the Operating System type and version.
14
+ #
15
+ # Returns the Image object identifed by the supplied ID number. Called from the get_image instance method of OpenStack::Compute::Connection,
16
+ # it will likely not be called directly from user code.
17
+ #
18
+ # >> cs = OpenStack::Compute::Connection.new(USERNAME,API_KEY)
19
+ # >> image = cs.get_image(2)
20
+ # => #<OpenStack::Compute::Image:0x1015371c0 ...>
21
+ # >> image.name
22
+ # => "CentOS 5.2"
23
+ def initialize(connection,id)
24
+ @id = id
25
+ @connection = connection
26
+ populate
27
+ end
28
+
29
+ # Makes the HTTP call to load information about the provided image. Can also be called directly on the Image object to refresh data.
30
+ # Returns true if the refresh call succeeds.
31
+ #
32
+ # >> image.populate
33
+ # => true
34
+ def populate
35
+ response = @connection.csreq("GET",@connection.svrmgmthost,"#{@connection.svrmgmtpath}/images/#{URI.escape(self.id.to_s)}",@connection.svrmgmtport,@connection.svrmgmtscheme)
36
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
37
+ data = JSON.parse(response.body)['image']
38
+ @id = data['id']
39
+ @name = data['name']
40
+ @serverId = data['serverId']
41
+ @updated = DateTime.parse(data['updated'])
42
+ @created = DateTime.parse(data['created'])
43
+ @status = data['status']
44
+ @progress = data['progress']
45
+ return true
46
+ end
47
+ alias :refresh :populate
48
+
49
+ # Delete an image. This should be returning invalid permissions when attempting to delete system images, but it's not.
50
+ # Returns true if the deletion succeeds.
51
+ #
52
+ # >> image.delete!
53
+ # => true
54
+ def delete!
55
+ response = @connection.csreq("DELETE",@connection.svrmgmthost,"#{@connection.svrmgmtpath}/images/#{URI.escape(self.id.to_s)}",@connection.svrmgmtport,@connection.svrmgmtscheme)
56
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
57
+ true
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,287 @@
1
+ module OpenStack
2
+ module Compute
3
+ class Server
4
+
5
+ attr_reader :id
6
+ attr_reader :name
7
+ attr_reader :status
8
+ attr_reader :progress
9
+ attr_reader :addresses
10
+ attr_reader :metadata
11
+ attr_reader :hostId
12
+ attr_reader :imageId
13
+ attr_reader :flavorId
14
+ attr_reader :metadata
15
+ attr_accessor :adminPass
16
+
17
+ # This class is the representation of a single Server object. The constructor finds the server identified by the specified
18
+ # ID number, accesses the API via the populate method to get information about that server, and returns the object.
19
+ #
20
+ # Will be called via the get_server or create_server methods on the OpenStack::Compute::Connection object, and will likely not be called directly.
21
+ #
22
+ # >> server = cs.get_server(110917)
23
+ # => #<OpenStack::Compute::Server:0x1014e5438 ....>
24
+ # >> server.name
25
+ # => "RenamedRubyTest"
26
+ def initialize(connection,id)
27
+ @connection = connection
28
+ @id = id
29
+ @svrmgmthost = connection.svrmgmthost
30
+ @svrmgmtpath = connection.svrmgmtpath
31
+ @svrmgmtport = connection.svrmgmtport
32
+ @svrmgmtscheme = connection.svrmgmtscheme
33
+ populate
34
+ return self
35
+ end
36
+
37
+ # Makes the actual API call to get information about the given server object. If you are attempting to track the status or project of
38
+ # a server object (for example, when rebuilding, creating, or resizing a server), you will likely call this method within a loop until
39
+ # the status becomes "ACTIVE" or other conditions are met.
40
+ #
41
+ # Returns true if the API call succeeds.
42
+ #
43
+ # >> server.refresh
44
+ # => true
45
+ def populate
46
+ response = @connection.csreq("GET",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(@id.to_s)}",@svrmgmtport,@svrmgmtscheme)
47
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
48
+ data = JSON.parse(response.body)["server"]
49
+ @id = data["id"]
50
+ @name = data["name"]
51
+ @status = data["status"]
52
+ @progress = data["progress"]
53
+ @addresses = OpenStack::Compute.symbolize_keys(data["addresses"])
54
+ @metadata = data["metadata"]
55
+ @hostId = data["hostId"]
56
+ @imageId = data["imageId"]
57
+ @flavorId = data["flavorId"]
58
+ @metadata = data["metadata"]
59
+ true
60
+ end
61
+ alias :refresh :populate
62
+
63
+ # Returns a new OpenStack::Compute::Flavor object for the flavor assigned to this server.
64
+ #
65
+ # >> flavor = server.flavor
66
+ # => #<OpenStack::Compute::Flavor:0x1014aac20 @name="256 server", @disk=10, @id=1, @ram=256>
67
+ # >> flavor.name
68
+ # => "256 server"
69
+ def flavor
70
+ OpenStack::Compute::Flavor.new(@connection,self.flavorId)
71
+ end
72
+
73
+ # Returns a new OpenStack::Compute::Image object for the image assigned to this server.
74
+ #
75
+ # >> image = server.image
76
+ # => #<OpenStack::Compute::Image:0x10149a960 ...>
77
+ # >> image.name
78
+ # => "Ubuntu 8.04.2 LTS (hardy)"
79
+ def image
80
+ OpenStack::Compute::Image.new(@connection,self.imageId)
81
+ end
82
+
83
+ # Sends an API request to reboot this server. Takes an optional argument for the type of reboot, which can be "SOFT" (graceful shutdown)
84
+ # or "HARD" (power cycle). The hard reboot is also triggered by server.reboot!, so that may be a better way to call it.
85
+ #
86
+ # Returns true if the API call succeeds.
87
+ #
88
+ # >> server.reboot
89
+ # => true
90
+ def reboot(type="SOFT")
91
+ data = JSON.generate(:reboot => {:type => type})
92
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
93
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
94
+ true
95
+ end
96
+
97
+ # Sends an API request to hard-reboot (power cycle) the server. See the reboot method for more information.
98
+ #
99
+ # Returns true if the API call succeeds.
100
+ #
101
+ # >> server.reboot!
102
+ # => true
103
+ def reboot!
104
+ self.reboot("HARD")
105
+ end
106
+
107
+ # Updates various parameters about the server. Currently, the only operations supported are changing the server name (not the actual hostname
108
+ # on the server, but simply the label in the Servers API) and the administrator password (note: changing the admin password will trigger
109
+ # a reboot of the server). Other options are ignored. One or both key/value pairs may be provided. Keys are case-sensitive.
110
+ #
111
+ # Input hash key values are :name and :adminPass. Returns true if the API call succeeds.
112
+ #
113
+ # >> server.update(:name => "MyServer", :adminPass => "12345")
114
+ # => true
115
+ # >> server.name
116
+ # => "MyServer"
117
+ def update(options)
118
+ data = JSON.generate(:server => options)
119
+ response = @connection.csreq("PUT",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
120
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
121
+ # If we rename the instance, repopulate the object
122
+ self.populate if options[:name]
123
+ true
124
+ end
125
+
126
+ # Deletes the server from Openstack Compute. The server will be shut down, data deleted, and billing stopped.
127
+ #
128
+ # Returns true if the API call succeeds.
129
+ #
130
+ # >> server.delete!
131
+ # => true
132
+ def delete!
133
+ response = @connection.csreq("DELETE",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}",@svrmgmtport,@svrmgmtscheme)
134
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
135
+ true
136
+ end
137
+
138
+ # Takes the existing server and rebuilds it with the image identified by the imageId argument. If no imageId is provided, the current image
139
+ # will be used.
140
+ #
141
+ # This will wipe and rebuild the server, but keep the server ID number, name, and IP addresses the same.
142
+ #
143
+ # Returns true if the API call succeeds.
144
+ #
145
+ # >> server.rebuild!
146
+ # => true
147
+ def rebuild!(imageId = self.imageId)
148
+ data = JSON.generate(:rebuild => {:imageId => imageId})
149
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
150
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
151
+ self.populate
152
+ true
153
+ end
154
+
155
+ # Takes a snapshot of the server and creates a server image from it. That image can then be used to build new servers. The
156
+ # snapshot is saved asynchronously. Check the image status to make sure that it is ACTIVE before attempting to perform operations
157
+ # on it.
158
+ #
159
+ # A name string for the saved image must be provided. A new OpenStack::Compute::Image object for the saved image is returned.
160
+ #
161
+ # The image is saved as a backup, of which there are only three available slots. If there are no backup slots available,
162
+ # A OpenStack::Compute::Exception::OpenStackComputeFault will be raised.
163
+ #
164
+ # >> image = server.create_image("My Rails Server")
165
+ # =>
166
+ def create_image(name)
167
+ data = JSON.generate(:image => {:serverId => self.id, :name => name})
168
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/images",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
169
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
170
+ OpenStack::Compute::Image.new(@connection,JSON.parse(response.body)['image']['id'])
171
+ end
172
+
173
+ # Resizes the server to the size contained in the server flavor found at ID flavorId. The server name, ID number, and IP addresses
174
+ # will remain the same. After the resize is done, the server.status will be set to "VERIFY_RESIZE" until the resize is confirmed or reverted.
175
+ #
176
+ # Refreshes the OpenStack::Compute::Server object, and returns true if the API call succeeds.
177
+ #
178
+ # >> server.resize!(1)
179
+ # => true
180
+ def resize!(flavorId)
181
+ data = JSON.generate(:resize => {:flavorId => flavorId})
182
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
183
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
184
+ self.populate
185
+ true
186
+ end
187
+
188
+ # After a server resize is complete, calling this method will confirm the resize with the Openstack API, and discard the fallback/original image.
189
+ #
190
+ # Returns true if the API call succeeds.
191
+ #
192
+ # >> server.confirm_resize!
193
+ # => true
194
+ def confirm_resize!
195
+ # If the resize bug gets figured out, should put a check here to make sure that it's in the proper state for this.
196
+ data = JSON.generate(:confirmResize => nil)
197
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
198
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
199
+ self.populate
200
+ true
201
+ end
202
+
203
+ # After a server resize is complete, calling this method will reject the resized server with the Openstack API, destroying
204
+ # the new image and replacing it with the pre-resize fallback image.
205
+ #
206
+ # Returns true if the API call succeeds.
207
+ #
208
+ # >> server.confirm_resize!
209
+ # => true
210
+ def revert_resize!
211
+ # If the resize bug gets figured out, should put a check here to make sure that it's in the proper state for this.
212
+ data = JSON.generate(:revertResize => nil)
213
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/action",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
214
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
215
+ self.populate
216
+ true
217
+ end
218
+
219
+ # Provides information about the backup schedule for this server. Returns a hash of the form
220
+ # {"weekly" => state, "daily" => state, "enabled" => boolean}
221
+ #
222
+ # >> server.backup_schedule
223
+ # => {"weekly"=>"THURSDAY", "daily"=>"H_0400_0600", "enabled"=>true}
224
+ def backup_schedule
225
+ response = @connection.csreq("GET",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(@id.to_s)}/backup_schedule",@svrmgmtport,@svrmgmtscheme)
226
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
227
+ JSON.parse(response.body)['backupSchedule']
228
+ end
229
+
230
+ # Updates the backup schedule for the server. Takes a hash of the form: {:weekly => state, :daily => state, :enabled => boolean} as an argument.
231
+ # All three keys (:weekly, :daily, :enabled) must be provided or an exception will get raised.
232
+ #
233
+ # >> server.backup_schedule=({:weekly=>"THURSDAY", :daily=>"H_0400_0600", :enabled=>true})
234
+ # => {:weekly=>"THURSDAY", :daily=>"H_0400_0600", :enabled=>true}
235
+ def backup_schedule=(options)
236
+ data = JSON.generate('backupSchedule' => options)
237
+ response = @connection.csreq("POST",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/backup_schedule",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
238
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
239
+ true
240
+ end
241
+
242
+ # Removes the existing backup schedule for the server, setting the backups to disabled.
243
+ #
244
+ # Returns true if the API call succeeds.
245
+ #
246
+ # >> server.disable_backup_schedule!
247
+ # => true
248
+ def disable_backup_schedule!
249
+ response = @connection.csreq("DELETE",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/backup_schedule",@svrmgmtport,@svrmgmtscheme)
250
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
251
+ true
252
+ end
253
+
254
+ # Share IP between servers in Shared IP group.
255
+ # Takes a hash of the form: {:sharedIpGroupId => "1234", :ipAddress => "67.23.10.132", :configureServer => false} as an argument.
256
+ # The :sharedIpGroupId key is required.
257
+ # The :ipAddress key is required.
258
+ # The :configureServer key is optional and defaults to false.
259
+ #
260
+ # >> server.share_ip(:sharedIpGroupId => 100, :ipAddress => "67.23.10.132")
261
+ # => true
262
+ def share_ip(options)
263
+ raise OpenStack::Compute::Exception::MissingArgument, "Shared IP Group ID must be supplied" unless options[:sharedIpGroupId]
264
+ raise OpenStack::Compute::Exception::MissingArgument, "Ip Address must be supplied" unless options[:ipAddress]
265
+ options[:configureServer] = false if options[:configureServer].nil?
266
+ data = JSON.generate(:shareIp => {:sharedIpGroupId => options[:sharedIpGroupId], :configureServer => options[:configureServer]})
267
+ response = @connection.csreq("PUT",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/ips/public/#{options[:ipAddress]}",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
268
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
269
+ true
270
+ end
271
+
272
+ # Unshare an IP address.
273
+ # Takes a hash of the form: {:ipAddress => "67.23.10.132"} as an argument.
274
+ # The :ipAddress key is required.
275
+ #
276
+ # >> server.unshare_ip(:ipAddress => "67.23.10.132")
277
+ # => true
278
+ def unshare_ip(options)
279
+ raise OpenStack::Compute::Exception::MissingArgument, "Ip Address must be supplied" unless options[:ipAddress]
280
+ response = @connection.csreq("DELETE",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/ips/public/#{options[:ipAddress]}",@svrmgmtport,@svrmgmtscheme)
281
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
282
+ true
283
+ end
284
+
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,54 @@
1
+ module OpenStack
2
+ module Compute
3
+ class SharedIPGroup
4
+
5
+ attr_reader :id
6
+ attr_reader :name
7
+ attr_reader :servers
8
+
9
+ # Creates a new Shared IP Group object, with information on the group identified by the ID number. Will most likely be called
10
+ # by the get_shared_ip_group method on a OpenStack::Compute::Connection object.
11
+ #
12
+ # >> sig = cs.get_shared_ip_group(127)
13
+ # => #<OpenStack::Compute::SharedIPGroup:0x101513798 ...>
14
+ # >> sig.name
15
+ # => "New Group"
16
+ def initialize(connection,id)
17
+ @connection = connection
18
+ @id = id
19
+ populate
20
+ end
21
+
22
+ # Makes the API call that populates the OpenStack::Compute::SharedIPGroup object with information on the group. Can also be called directly on
23
+ # an existing object to update its information.
24
+ #
25
+ # Returns true if the API call succeeds.
26
+ #
27
+ # >> sig.populate
28
+ # => true
29
+ def populate
30
+ response = @connection.csreq("GET",@connection.svrmgmthost,"#{@connection.svrmgmtpath}/shared_ip_groups/#{URI.escape(self.id.to_s)}",@connection.svrmgmtport,@connection.svrmgmtscheme)
31
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
32
+ data = JSON.parse(response.body)['sharedIpGroup']
33
+ @id = data['id']
34
+ @name = data['name']
35
+ @servers = data['servers']
36
+ true
37
+ end
38
+ alias :refresh :populate
39
+
40
+ # Deletes the Shared IP Group identified by the current object.
41
+ #
42
+ # Returns true if the API call succeeds.
43
+ #
44
+ # >> sig.delete!
45
+ # => true
46
+ def delete!
47
+ response = @connection.csreq("DELETE",@connection.svrmgmthost,"#{@connection.svrmgmtpath}/shared_ip_groups/#{URI.escape(self.id.to_s)}",@connection.svrmgmtport,@connection.svrmgmtscheme)
48
+ OpenStack::Compute::Exception.raise_exception(response) unless response.code.match(/^20.$/)
49
+ true
50
+ end
51
+
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openstack-compute
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Dan Prince
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: json
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: API Binding for OpenStack Compute
36
+ email: dan.prince@rackspace.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ files:
44
+ - COPYING
45
+ - README.rdoc
46
+ - VERSION
47
+ - lib/openstack/compute.rb
48
+ - lib/openstack/compute/authentication.rb
49
+ - lib/openstack/compute/connection.rb
50
+ - lib/openstack/compute/entity_manager.rb
51
+ - lib/openstack/compute/exception.rb
52
+ - lib/openstack/compute/flavor.rb
53
+ - lib/openstack/compute/image.rb
54
+ - lib/openstack/compute/server.rb
55
+ - lib/openstack/compute/shared_ip_group.rb
56
+ has_rdoc: true
57
+ homepage: https://launchpad.net/ruby-openstack-compute
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.7
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: OpenStack Compute Ruby API
90
+ test_files: []
91
+