openstack-compute 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +7 -0
- data/README.rdoc +72 -0
- data/VERSION +1 -0
- data/lib/openstack/compute.rb +87 -0
- data/lib/openstack/compute/authentication.rb +42 -0
- data/lib/openstack/compute/connection.rb +349 -0
- data/lib/openstack/compute/entity_manager.rb +6 -0
- data/lib/openstack/compute/exception.rb +87 -0
- data/lib/openstack/compute/flavor.rb +33 -0
- data/lib/openstack/compute/image.rb +62 -0
- data/lib/openstack/compute/server.rb +287 -0
- data/lib/openstack/compute/shared_ip_group.rb +54 -0
- metadata +91 -0
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.
|
data/README.rdoc
ADDED
@@ -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,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
|
+
|