dropmyemail-openstack 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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