dropmyemail-openstack 1.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +7 -0
- data/README.rdoc +371 -0
- data/VERSION +1 -0
- data/lib/openstack.rb +112 -0
- data/lib/openstack/compute/address.rb +68 -0
- data/lib/openstack/compute/connection.rb +412 -0
- data/lib/openstack/compute/flavor.rb +35 -0
- data/lib/openstack/compute/image.rb +73 -0
- data/lib/openstack/compute/metadata.rb +116 -0
- data/lib/openstack/compute/personalities.rb +23 -0
- data/lib/openstack/compute/server.rb +254 -0
- data/lib/openstack/connection.rb +494 -0
- data/lib/openstack/image/connection.rb +16 -0
- data/lib/openstack/swift/connection.rb +185 -0
- data/lib/openstack/swift/container.rb +214 -0
- data/lib/openstack/swift/storage_object.rb +311 -0
- data/lib/openstack/volume/connection.rb +123 -0
- data/lib/openstack/volume/snapshot.rb +25 -0
- data/lib/openstack/volume/volume.rb +31 -0
- data/test/authentication_test.rb +134 -0
- data/test/connection_test.rb +39 -0
- data/test/exception_test.rb +49 -0
- data/test/metadata_test.rb +210 -0
- data/test/servers_test.rb +210 -0
- data/test/test_helper.rb +22 -0
- metadata +123 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
module OpenStack
|
3
|
+
module Compute
|
4
|
+
|
5
|
+
class AddressList < Array
|
6
|
+
def [](index)
|
7
|
+
addresses = Array.new
|
8
|
+
if index.class == Symbol then
|
9
|
+
self.each do |address|
|
10
|
+
if address.label == index.to_s then
|
11
|
+
addresses << address
|
12
|
+
end
|
13
|
+
end
|
14
|
+
addresses
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Address
|
22
|
+
|
23
|
+
attr_reader :address
|
24
|
+
attr_reader :label
|
25
|
+
attr_reader :version
|
26
|
+
|
27
|
+
def initialize(label, address, version = 4)
|
28
|
+
@label = label
|
29
|
+
if address.class == Hash then
|
30
|
+
@address = address["addr"]
|
31
|
+
@version = address["version"]
|
32
|
+
else
|
33
|
+
@address = address
|
34
|
+
@version = version
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
NON_ROUTABLE_ADDRESSES = [IPAddr.new("10.0.0.0/8"), IPAddr.new("192.168.0.0/16"), IPAddr.new("172.16.0.0/12")]
|
39
|
+
|
40
|
+
def self.is_private?(address_string)
|
41
|
+
NON_ROUTABLE_ADDRESSES.each do |no_route|
|
42
|
+
return true if no_route.include?(address_string)
|
43
|
+
end
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
#IN: { "private"=> [{"addr"=>"10.7.206.171", "version"=>4}, {"addr"=>"15.185.160.208", "version"=>4}]}
|
48
|
+
#OUT: { "private"=> [{"addr"=>"10.7.206.171", "version"=>4}],
|
49
|
+
# "public"=> [{"addr"=>"15.185.160.208", "version"=>4}] }
|
50
|
+
def self.fix_labels(addresses_info)
|
51
|
+
addresses_info.inject({"public"=>[], "private"=>[]}) do |res, (label,address_struct_list)|
|
52
|
+
address_struct_list.each do |address_struct|
|
53
|
+
if(address_struct["version"==6])#v6 addresses are all routable...
|
54
|
+
res["public"] << address_struct
|
55
|
+
else
|
56
|
+
is_private?(address_struct["addr"])? res["private"] << address_struct : res["public"] << address_struct
|
57
|
+
end
|
58
|
+
end
|
59
|
+
res
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,412 @@
|
|
1
|
+
module OpenStack
|
2
|
+
module Compute
|
3
|
+
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
attr_accessor :connection
|
7
|
+
attr_accessor :extensions
|
8
|
+
|
9
|
+
def initialize(connection)
|
10
|
+
@extensions = nil
|
11
|
+
@connection = connection
|
12
|
+
OpenStack::Authentication.init(@connection)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if the authentication was successful and returns false otherwise.
|
16
|
+
#
|
17
|
+
# cs.authok?
|
18
|
+
# => true
|
19
|
+
def authok?
|
20
|
+
@connection.authok
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the OpenStack::Compute::Server object identified by the given id.
|
24
|
+
#
|
25
|
+
# >> server = cs.get_server(110917)
|
26
|
+
# => #<OpenStack::Compute::Server:0x101407ae8 ...>
|
27
|
+
# >> server.name
|
28
|
+
# => "MyServer"
|
29
|
+
def get_server(id)
|
30
|
+
OpenStack::Compute::Server.new(self,id)
|
31
|
+
end
|
32
|
+
alias :server :get_server
|
33
|
+
|
34
|
+
# Returns an array of hashes, one for each server that exists under this account. The hash keys are :name and :id.
|
35
|
+
#
|
36
|
+
# You can also provide :limit and :offset parameters to handle pagination.
|
37
|
+
#
|
38
|
+
# >> cs.list_servers
|
39
|
+
# => [{:name=>"MyServer", :id=>110917}]
|
40
|
+
#
|
41
|
+
# >> cs.list_servers(:limit => 2, :offset => 3)
|
42
|
+
# => [{:name=>"demo-standingcloud-lts", :id=>168867},
|
43
|
+
# {:name=>"demo-aicache1", :id=>187853}]
|
44
|
+
def list_servers(options = {})
|
45
|
+
anti_cache_param="cacheid=#{Time.now.to_i}"
|
46
|
+
path = OpenStack.paginate(options).empty? ? "#{@connection.service_path}/servers?#{anti_cache_param}" : "#{@connection.service_path}/servers?#{OpenStack.paginate(options)}&#{anti_cache_param}"
|
47
|
+
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
|
48
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
49
|
+
OpenStack.symbolize_keys(JSON.parse(response.body)["servers"])
|
50
|
+
end
|
51
|
+
alias :servers :list_servers
|
52
|
+
|
53
|
+
# Returns an array of hashes with more details about each server that exists under this account. Additional information
|
54
|
+
# includes public and private IP addresses, status, hostID, and more. All hash keys are symbols except for the metadata
|
55
|
+
# hash, which are verbatim strings.
|
56
|
+
#
|
57
|
+
# You can also provide :limit and :offset parameters to handle pagination.
|
58
|
+
# >> cs.list_servers_detail
|
59
|
+
# => [{:name=>"MyServer", :addresses=>{:public=>["67.23.42.37"], :private=>["10.176.241.237"]}, :metadata=>{"MyData" => "Valid"}, :imageRef=>10, :progress=>100, :hostId=>"36143b12e9e48998c2aef79b50e144d2", :flavorRef=>1, :id=>110917, :status=>"ACTIVE"}]
|
60
|
+
#
|
61
|
+
# >> cs.list_servers_detail(:limit => 2, :offset => 3)
|
62
|
+
# => [{:status=>"ACTIVE", :imageRef=>10, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-standingcloud-lts", :id=>168867, :flavorRef=>1, :hostId=>"xxxxxx"},
|
63
|
+
# {:status=>"ACTIVE", :imageRef=>8, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-aicache1", :id=>187853, :flavorRef=>3, :hostId=>"xxxxxx"}]
|
64
|
+
def list_servers_detail(options = {})
|
65
|
+
path = OpenStack.paginate(options).empty? ? "#{@connection.service_path}/servers/detail" : "#{@connection.service_path}/servers/detail?#{OpenStack.paginate(options)}"
|
66
|
+
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
|
67
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
68
|
+
json_server_list = JSON.parse(response.body)["servers"]
|
69
|
+
json_server_list.each do |server|
|
70
|
+
server["addresses"] = OpenStack::Compute::Address.fix_labels(server["addresses"])
|
71
|
+
end
|
72
|
+
OpenStack.symbolize_keys(json_server_list)
|
73
|
+
end
|
74
|
+
alias :servers_detail :list_servers_detail
|
75
|
+
|
76
|
+
# Creates a new server instance on OpenStack Compute
|
77
|
+
#
|
78
|
+
# The argument is a hash of options. The keys :name, :flavorRef,
|
79
|
+
# and :imageRef are required; :metadata, :security_groups,
|
80
|
+
# :key_name and :personality are optional.
|
81
|
+
#
|
82
|
+
# :flavorRef and :imageRef are href strings identifying a particular
|
83
|
+
# server flavor and image to use when building the server. The :imageRef
|
84
|
+
# can either be a stock image, or one of your own created with the
|
85
|
+
# server.create_image method.
|
86
|
+
#
|
87
|
+
# The :metadata argument should be a hash of key/value pairs. This
|
88
|
+
# metadata will be applied to the server at the OpenStack Compute API level.
|
89
|
+
#
|
90
|
+
# The "Personality" option allows you to include up to five files, # of
|
91
|
+
# 10Kb or less in size, that will be placed on the created server.
|
92
|
+
# For :personality, pass a hash of the form {'local_path' => 'server_path'}.
|
93
|
+
# The file located at local_path will be base64-encoded and placed at the
|
94
|
+
# location identified by server_path on the new server.
|
95
|
+
#
|
96
|
+
# Returns a OpenStack::Compute::Server object. The root password is
|
97
|
+
# available in the adminPass instance method.
|
98
|
+
#
|
99
|
+
# >> server = cs.create_server(
|
100
|
+
# :name => 'NewServer',
|
101
|
+
# :imageRef => '3',
|
102
|
+
# :flavorRef => '1',
|
103
|
+
# :metadata => {'Racker' => 'Fanatical'},
|
104
|
+
# :personality => {'/home/bob/wedding.jpg' => '/root/wedding.jpg'},
|
105
|
+
# :key_name => "mykey",
|
106
|
+
# :security_groups => [ "devel", "test"])
|
107
|
+
# => #<OpenStack::Compute::Server:0x101229eb0 ...>
|
108
|
+
# >> server.name
|
109
|
+
# => "NewServer"
|
110
|
+
# >> server.status
|
111
|
+
# => "BUILD"
|
112
|
+
# >> server.adminPass
|
113
|
+
# => "NewServerSHMGpvI"
|
114
|
+
def create_server(options)
|
115
|
+
raise OpenStack::Exception::MissingArgument, "Server name, flavorRef, and imageRef, must be supplied" unless (options[:name] && options[:flavorRef] && options[:imageRef])
|
116
|
+
options[:personality] = Personalities.get_personality(options[:personality])
|
117
|
+
options[:security_groups] = (options[:security_groups] || []).inject([]){|res, c| res << {"name"=>c} ;res}
|
118
|
+
data = JSON.generate(:server => options)
|
119
|
+
response = @connection.csreq("POST",@connection.service_host,"#{@connection.service_path}/servers",@connection.service_port,@connection.service_scheme,{'content-type' => 'application/json'},data)
|
120
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
121
|
+
server_info = JSON.parse(response.body)['server']
|
122
|
+
server = OpenStack::Compute::Server.new(self,server_info['id'])
|
123
|
+
server.adminPass = server_info['adminPass']
|
124
|
+
return server
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns an array of hashes listing available server images that you have access too,
|
128
|
+
# including stock OpenStack Compute images and any that you have created. The "id" key
|
129
|
+
# in the hash can be used where imageRef is required. You can also provide :limit and
|
130
|
+
# :offset parameters to handle pagination.
|
131
|
+
#
|
132
|
+
# >> cs.list_images
|
133
|
+
# => [{:name=>"CentOS 5.2", :id=>2, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},
|
134
|
+
# {:name=>"Gentoo 2008.0", :id=>3, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},...
|
135
|
+
#
|
136
|
+
# >> cs.list_images(:limit => 3, :offset => 2)
|
137
|
+
# => [{:status=>"ACTIVE", :name=>"Fedora 11 (Leonidas)", :updated=>"2009-12-08T13:50:45-06:00", :id=>13},
|
138
|
+
# {:status=>"ACTIVE", :name=>"CentOS 5.3", :updated=>"2009-08-26T14:59:52-05:00", :id=>7},
|
139
|
+
# {:status=>"ACTIVE", :name=>"CentOS 5.4", :updated=>"2009-12-16T01:02:17-06:00", :id=>187811}]
|
140
|
+
def list_images(options = {})
|
141
|
+
path = OpenStack.paginate(options).empty? ? "#{@connection.service_path}/images/detail" : "#{@connection.service_path}/images/detail?#{OpenStack.paginate(options)}"
|
142
|
+
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
|
143
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
144
|
+
OpenStack.symbolize_keys(JSON.parse(response.body)['images'])
|
145
|
+
end
|
146
|
+
alias :images :list_images
|
147
|
+
|
148
|
+
# Returns a OpenStack::Compute::Image object for the image identified by the provided id.
|
149
|
+
#
|
150
|
+
# >> image = cs.get_image(8)
|
151
|
+
# => #<OpenStack::Compute::Image:0x101659698 ...>
|
152
|
+
def get_image(id)
|
153
|
+
OpenStack::Compute::Image.new(self,id)
|
154
|
+
end
|
155
|
+
alias :image :get_image
|
156
|
+
|
157
|
+
# Returns an array of hashes listing all available server flavors. The :id key in the hash can be used when flavorRef is required.
|
158
|
+
#
|
159
|
+
# You can also provide :limit and :offset parameters to handle pagination.
|
160
|
+
#
|
161
|
+
# >> cs.list_flavors
|
162
|
+
# => [{:name=>"256 server", :id=>1, :ram=>256, :disk=>10},
|
163
|
+
# {:name=>"512 server", :id=>2, :ram=>512, :disk=>20},...
|
164
|
+
#
|
165
|
+
# >> cs.list_flavors(:limit => 3, :offset => 2)
|
166
|
+
# => [{:ram=>1024, :disk=>40, :name=>"1GB server", :id=>3},
|
167
|
+
# {:ram=>2048, :disk=>80, :name=>"2GB server", :id=>4},
|
168
|
+
# {:ram=>4096, :disk=>160, :name=>"4GB server", :id=>5}]
|
169
|
+
def list_flavors(options = {})
|
170
|
+
path = OpenStack.paginate(options).empty? ? "#{@connection.service_path}/flavors/detail" : "#{@connection.service_path}/flavors/detail?#{OpenStack.paginate(options)}"
|
171
|
+
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
|
172
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
173
|
+
OpenStack.symbolize_keys(JSON.parse(response.body)['flavors'])
|
174
|
+
end
|
175
|
+
alias :flavors :list_flavors
|
176
|
+
|
177
|
+
# Returns a OpenStack::Compute::Flavor object for the flavor identified by the provided ID.
|
178
|
+
#
|
179
|
+
# >> flavor = cs.flavor(1)
|
180
|
+
# => #<OpenStack::Compute::Flavor:0x10156dcc0 @name="256 server", @disk=10, @id=1, @ram=256>
|
181
|
+
def get_flavor(id)
|
182
|
+
OpenStack::Compute::Flavor.new(self,id)
|
183
|
+
end
|
184
|
+
alias :flavor :get_flavor
|
185
|
+
|
186
|
+
# Returns the current state of the programatic API limits. Each account has certain limits on the number of resources
|
187
|
+
# allowed in the account, and a rate of API operations.
|
188
|
+
#
|
189
|
+
# The operation returns a hash. The :absolute hash key reveals the account resource limits, including the maxmimum
|
190
|
+
# amount of total RAM that can be allocated (combined among all servers), the maximum members of an IP group, and the
|
191
|
+
# maximum number of IP groups that can be created.
|
192
|
+
#
|
193
|
+
# The :rate hash key returns an array of hashes indicating the limits on the number of operations that can be performed in a
|
194
|
+
# given amount of time. An entry in this array looks like:
|
195
|
+
#
|
196
|
+
# {:regex=>"^/servers", :value=>50, :verb=>"POST", :remaining=>50, :unit=>"DAY", :resetTime=>1272399820, :URI=>"/servers*"}
|
197
|
+
#
|
198
|
+
# This indicates that you can only run 50 POST operations against URLs in the /servers URI space per day, we have not run
|
199
|
+
# any operations today (50 remaining), and gives the Unix time that the limits reset.
|
200
|
+
#
|
201
|
+
# Another example is:
|
202
|
+
#
|
203
|
+
# {:regex=>".*", :value=>10, :verb=>"PUT", :remaining=>10, :unit=>"MINUTE", :resetTime=>1272399820, :URI=>"*"}
|
204
|
+
#
|
205
|
+
# This says that you can run 10 PUT operations on all possible URLs per minute, and also gives the number remaining and the
|
206
|
+
# time that the limit resets.
|
207
|
+
#
|
208
|
+
# Use this information as you're building your applications to put in relevant pauses if you approach your API limitations.
|
209
|
+
def limits
|
210
|
+
response = @connection.csreq("GET",@connection.service_host,"#{@connection.service_path}/limits",@connection.service_port,@connection.service_scheme)
|
211
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
212
|
+
OpenStack.symbolize_keys(JSON.parse(response.body)['limits'])
|
213
|
+
end
|
214
|
+
|
215
|
+
# ==============================
|
216
|
+
# API EXTENSIONS
|
217
|
+
#
|
218
|
+
# http://nova.openstack.org/api_ext/index.html
|
219
|
+
# http://api.openstack.org/ (grep 'Compute API Extensions')
|
220
|
+
#
|
221
|
+
|
222
|
+
|
223
|
+
#query the openstack provider for any implemented extensions to the compute API
|
224
|
+
#returns a hash with openstack service provider's returned details
|
225
|
+
#about the implemented extensions, e.g.:
|
226
|
+
#
|
227
|
+
# { :os-floating_ips => { :links=>[],
|
228
|
+
# :updated=>"2011-06-16T00:00:00+00:00",
|
229
|
+
# :description=>"Floating IPs support",
|
230
|
+
# :namespace=>"http://docs.openstack.org/ext/floating_ips/api/v1.1",
|
231
|
+
# :name=>"Floating_ips", :alias=>"os-floating-ips"},
|
232
|
+
# :os-keypairs => { :links=>[],
|
233
|
+
# :updated=>"2011-08-08T00:00:00+00:00",
|
234
|
+
# :description=>"Keypair Support",
|
235
|
+
# :namespace=>"http://docs.openstack.org/ext/keypairs/api/v1.1",
|
236
|
+
# :name=>"Keypairs",
|
237
|
+
# :alias=>"os-keypairs"}
|
238
|
+
# }
|
239
|
+
#
|
240
|
+
def api_extensions
|
241
|
+
if @extensions.nil?
|
242
|
+
response = @connection.req("GET", "/extensions")
|
243
|
+
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
|
244
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
245
|
+
@extensions = res[:extensions].inject({}){|result, c| result[c[:alias].to_sym] = c ; result}
|
246
|
+
end
|
247
|
+
@extensions
|
248
|
+
end
|
249
|
+
|
250
|
+
# Retrieve a list of key pairs associated with the current authenticated account
|
251
|
+
# Will return a hash:
|
252
|
+
# { :key_one => { :fingerprint => "3f:12:4d:d1:54:f1:f4:3f:fe:a8:12:ec:1a:fb:35:b2",
|
253
|
+
# :public_key => "ssh-rsa AAAAB3Nza923kJU123AADAQABAAAAg928JUwydszi029kIJudfOzQ7o160Ll1ItybDzYYcCAJ/N02loIKJU17264520bmXZFSsaZf2ErX3nSBNI3K+2zQzu832jkhkfdsa7GHH5hvNOxO7u800894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@nv-zz2232-api0002\n",
|
254
|
+
# :name => "key_one"},
|
255
|
+
#
|
256
|
+
# :key_two => { :fingerprint => "6b:32:dd:d2:51:c1:f2:3a:fb:a2:52:3a:1a:bb:25:1b",
|
257
|
+
# :public_key => "ssh-rsa AKIJUuw71645kJU123AADAQABAAAAg928019oiUJY12765IJudfOzQ7o160Ll1ItybDzYYcCAJ/N80438012480321jhkhKJlfdsazu832jkhkfdsa7GHH5fdasfdsajlj2999789799987989894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@bv-fdsa32-api0002\n",
|
258
|
+
# :name => "key_two"}
|
259
|
+
# }
|
260
|
+
#
|
261
|
+
# Raises OpenStack::Exception::NotImplemented if the current provider doesn't
|
262
|
+
# offer the os-keypairs extension
|
263
|
+
#
|
264
|
+
def keypairs
|
265
|
+
begin
|
266
|
+
response = @connection.req("GET", "/os-keypairs")
|
267
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
268
|
+
res[:keypairs].inject({}){|result, c| result[c[:keypair][:name].to_sym] = c[:keypair] ; result }
|
269
|
+
rescue OpenStack::Exception::ItemNotFound => not_found
|
270
|
+
msg = "The os-keypairs extension is not implemented for the provider you are talking to "+
|
271
|
+
"- #{@connection.http.keys.first}"
|
272
|
+
raise OpenStack::Exception::NotImplemented.new(msg, 501, "#{not_found.message}")
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Create a new keypair for use with launching servers. Raises
|
277
|
+
# a OpenStack::Exception::NotImplemented if os-keypairs extension
|
278
|
+
# is not implemented (or not advertised) by the OpenStack provider.
|
279
|
+
#
|
280
|
+
# The 'name' parameter MUST be supplied, otherwise a
|
281
|
+
# OpenStack::Exception::MissingArgument will be raised.
|
282
|
+
#
|
283
|
+
# Optionally requests can specify a 'public_key' parameter,
|
284
|
+
# with the full public ssh key (String) to be used to create the keypair
|
285
|
+
# (i.e. import a existing key).
|
286
|
+
#
|
287
|
+
# Returns a hash with details of the new key; the :private_key attribute
|
288
|
+
# must be saved must be saved by caller (not retrievable thereafter).
|
289
|
+
# NOTE: when the optional :public_key parameter is given, the response
|
290
|
+
# will obviously NOT contain the :private_key attribute.
|
291
|
+
#
|
292
|
+
# >> os.create_keypair({:name=>"test_key"})
|
293
|
+
# => { :name => "test_key",
|
294
|
+
# :fingerprint => "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
|
295
|
+
# :user_id => "dev_41247879706381",
|
296
|
+
# :public_key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDgGhDH3z9uMAvPV8ziE9BCEjHCPXGufy5bOgY5mY5jOSfdmKspdbl0z/LimHVKRDNX6HoL5qRg5V/tGH/NYP5sX2zF/XRKz16lfBxiUL1EONXA9fsTEBR3FGp8NcA7hW2+YiUxWafms4If3NFqttTQ11XqTU8JCMvms4D81lhbiQ== nova@use03147k5-eth0\n",
|
297
|
+
# :private_key => "-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBAAKBgQDgGhDH3z9uMAvPV8ziE9BCEjHCPXGufy5bOgY5mY5jOSfdmKsp\ndbl0z/LimHVKRDNX6HoL5qRg5V/tGH/NYP5sX2zF/XRKz16lfBxiUL1EONXA9fsT\nEBR3FGp8NcA7hW2+YiUxWafms4If3NFqttTQ11XqTU8JCMvms4D81lhbiQIDAQAB\nAoGBAJ1akAfXwNEc2V4IV2sy4FtULS4nOKh+0szpjC9rm+gd3Nki9qQQ7lyQGwpy\nZID2LFsAeJnco/UJefaf6jUKcvnS7tlxMuQB8DBlepiDqnnec0EiEAVmmt9GWlYZ\nJgfGWqDzI1WtouDCIsOhx1Vq7Foue6pgOnibktg2kfYnH9IRAkEA9tKzhlr9rFui\nbVDkiRJK3VTIhKyk4davDjqPJFLJ+4+77wRW164sGxwReD7HtW6qVtJd1MFvqLDO\nqJJEsqDvXQJBAOhvGaWiAPSuP+/z6GE6VXB1pADQFTYIp2DXUa5DcStTGe7hGF1b\nDeAxpDNBbLO3YKYqi2L9vJcIsp5PkHlEVh0CQQCVLIkWBb5VQliryv0knuqiVFCQ\nZyuL1s2cQuYqZOLwaFGERtIZrom3pMImM4NN82F98cyF/pb2lE2CckyUzVF9AkEA\nqhwFjS9Pu8N7j8XWoLHsre2rJd0kaPNUbI+pe/xn6ula5XVgO5LUSOyL2+daAv2G\ngpZIhR5m07LN5wccGWRmEQJBALZRenXaSyX2R2C9ag9r2gaU8/h+aU9he5kjXIt8\n+B8wvpvfOkpOAVCQEMxtsDkEixUtI98YKZP60uw+Xzh40YU=\n-----END RSA PRIVATE KEY-----\n"
|
298
|
+
# }
|
299
|
+
#
|
300
|
+
# Will raise an OpenStack::Exception::BadRequest if an invalid public_key is provided:
|
301
|
+
# >> os.create_keypair({:name=>"marios_keypair_test_invalid", :public_key=>"derp"})
|
302
|
+
# => OpenStack::Exception::BadRequest: Unexpected error while running command.
|
303
|
+
# Stdout: '/tmp/tmp4kI12a/import.pub is not a public key file.\n'
|
304
|
+
#
|
305
|
+
def create_keypair(options)
|
306
|
+
raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
|
307
|
+
raise OpenStack::Exception::MissingArgument, "Keypair name must be supplied" unless (options[:name])
|
308
|
+
data = JSON.generate(:keypair => options)
|
309
|
+
response = @connection.req("POST", "/os-keypairs", {:data=>data})
|
310
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
311
|
+
res[:keypair]
|
312
|
+
end
|
313
|
+
|
314
|
+
# Delete an existing keypair. Raises OpenStack::Exception::NotImplemented
|
315
|
+
# if os-keypairs extension is not implemented (or not advertised) by the OpenStack provider.
|
316
|
+
#
|
317
|
+
# Returns true if succesful.
|
318
|
+
# >> os.delete_keypair("marios_keypair")
|
319
|
+
# => true
|
320
|
+
#
|
321
|
+
# Will raise OpenStack::Exception::ItemNotFound if specified keypair doesn't exist
|
322
|
+
#
|
323
|
+
def delete_keypair(keypair_name)
|
324
|
+
raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
|
325
|
+
@connection.req("DELETE", "/os-keypairs/#{keypair_name}")
|
326
|
+
true
|
327
|
+
end
|
328
|
+
|
329
|
+
#Security Groups:
|
330
|
+
#Returns a hash with the security group IDs as keys:
|
331
|
+
#=> { "1381" => { :tenant_id=>"12345678909876", :id=>1381, :name=>"default", :description=>"default",
|
332
|
+
# :rules=> [
|
333
|
+
# {:from_port=>22, :group=>{}, :ip_protocol=>"tcp", :to_port=>22,
|
334
|
+
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4902},
|
335
|
+
# {:from_port=>80, :group=>{}, :ip_protocol=>"tcp", :to_port=>80,
|
336
|
+
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4903},
|
337
|
+
# {:from_port=>443, :group=>{}, :ip_protocol=>"tcp", :to_port=>443,
|
338
|
+
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4904},
|
339
|
+
# {:from_port=>-1, :group=>{}, :ip_protocol=>"icmp", :to_port=>-1,
|
340
|
+
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4905}],
|
341
|
+
# ]
|
342
|
+
# },
|
343
|
+
# "1234" => { ... } }
|
344
|
+
#
|
345
|
+
def security_groups
|
346
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
347
|
+
response = @connection.req("GET", "/os-security-groups")
|
348
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
349
|
+
res[:security_groups].inject({}){|result, c| result[c[:id].to_s] = c ; result }
|
350
|
+
end
|
351
|
+
|
352
|
+
def security_group(id)
|
353
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
354
|
+
response = @connection.req("GET", "/os-security-groups/#{id}")
|
355
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
356
|
+
{res[:security_group][:id].to_s => res[:security_group]}
|
357
|
+
end
|
358
|
+
|
359
|
+
def create_security_group(name, description)
|
360
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
361
|
+
data = JSON.generate(:security_group => { "name" => name, "description" => description})
|
362
|
+
response = @connection.req("POST", "/os-security-groups", {:data => data})
|
363
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
364
|
+
{res[:security_group][:id].to_s => res[:security_group]}
|
365
|
+
end
|
366
|
+
|
367
|
+
def delete_security_group(id)
|
368
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
369
|
+
response = @connection.req("DELETE", "/os-security-groups/#{id}")
|
370
|
+
true
|
371
|
+
end
|
372
|
+
|
373
|
+
#params: { :ip_protocol=>"tcp", :from_port=>"123", :to_port=>"123", :cidr=>"192.168.0.1/16", :group_id:="123" }
|
374
|
+
#observed behaviour against Openstack@HP cloud - can specify either cidr OR group_id as source, but not both
|
375
|
+
#if both specified, the group is used and the cidr ignored.
|
376
|
+
def create_security_group_rule(security_group_id, params)
|
377
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
378
|
+
params.merge!({:parent_group_id=>security_group_id.to_s})
|
379
|
+
data = JSON.generate(:security_group_rule => params)
|
380
|
+
response = @connection.req("POST", "/os-security-group-rules", {:data => data})
|
381
|
+
res = OpenStack.symbolize_keys(JSON.parse(response.body))
|
382
|
+
{res[:security_group_rule][:id].to_s => res[:security_group_rule]}
|
383
|
+
end
|
384
|
+
|
385
|
+
def delete_security_group_rule(id)
|
386
|
+
raise OpenStack::Exception::NotImplemented.new("os-security-groups not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-security-groups"] or api_extensions[:security_groups]
|
387
|
+
response = @connection.req("DELETE", "/os-security-group-rules/#{id}")
|
388
|
+
true
|
389
|
+
end
|
390
|
+
|
391
|
+
#VOLUMES - attach detach
|
392
|
+
def attach_volume(server_id, volume_id, device_id)
|
393
|
+
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
|
394
|
+
data = JSON.generate(:volumeAttachment => {"volumeId" => volume_id, "device"=> device_id})
|
395
|
+
response = @connection.req("POST", "/servers/#{server_id}/os-volume_attachments", {:data=>data})
|
396
|
+
true
|
397
|
+
end
|
398
|
+
|
399
|
+
def list_attachments(server_id)
|
400
|
+
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
|
401
|
+
response = @connection.req("GET", "/servers/#{server_id}/os-volume_attachments")
|
402
|
+
OpenStack.symbolize_keys(JSON.parse(response.body))
|
403
|
+
end
|
404
|
+
|
405
|
+
def detach_volume(server_id, attachment_id)
|
406
|
+
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
|
407
|
+
response = @connection.req("DELETE", "/servers/#{server_id}/os-volume_attachments/#{attachment_id}")
|
408
|
+
true
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|