dropmyemail-openstack 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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