openstack 1.0.0 → 1.0.1

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.
@@ -8,6 +8,40 @@ Currently supports both v1.0 and v2.0 (keystone) auth.
8
8
 
9
9
  Use OpenStack::Connection.create to get a handle to an OpenStack service - set the :service_type parameter to either 'compute' or 'object-store' (defaults to 'compute')
10
10
 
11
+ The OpenStack::Connection.create class method is a factory constructor which will return the appropriate Connection object, depending on the ':service_type' parameter passed with the options hash: set to either 'compute' or 'object-store' (defaults to 'compute') - see below for examples.
12
+
13
+ Other parameters for the create method:
14
+
15
+ * :auth_url - the OpenStack service provider specific authentication url endpoint.
16
+ * :auth_method - the type of authentication to be used with the above auth_url - either 'password' (username/password, 'key' (ec2 style key/private key) or 'rax-kskey'.
17
+ * :authtenant_name OR :authtenant_id - one of these MUST be specified when talking to a v2 authentication endpoint (keystone) - depending on whether you use tenant name (or tenant ID). Passing only :authtenant will result in that parameter being used as tenant name.
18
+ * :username - the username or public key (depending on auth_method)
19
+ * :api_key - the password or private key (denending on auth_method).
20
+
21
+ === Try it out:
22
+
23
+ sudo gem install openstack
24
+ [sudo] password for herp:
25
+ Successfully installed openstack-1.0.0
26
+
27
+ [herp@name lib]$ irb -rubygems
28
+ irb(main):001:0> require 'openstack'
29
+ => true
30
+
31
+ irb(main):002:0> os = OpenStack::Connection.create({:username => "herp@derp.net", :api_key=>"1234abcd", :auth_method=>"password", :auth_url => "https://regionerer-g.go-bar.identity.dacloudfoo.herpy:13327/v2.0/", :authtenant_name =>"herp@derp.net-default-tenant", :service_type=>"compute"})
32
+
33
+ => #<OpenStack::Compute::Connection:0xb7339070 @connection=#<OpenStack::Connection:0xb73392dc @service_scheme="https", @auth_host="regionerer-g.go-bar.identity.dacloudfoo.herpy", @http={}, @service_name=nil, @authuser="herp@derp.net", @proxy_port=nil, @auth_path="/v2.0/", @authtenant={:type=>"tenantName", :value=>"herp@derp.net-default-tenant"}, @service_port=443, @authkey="1235abcd", @authok=true, @service_type="compute", @auth_method="password", @auth_scheme="https", @service_host="az-2.region-a.geo-1.dacloudfoo.herpy", @is_debug=nil, @proxy_host=nil, @service_path="/v1.1/482195756462871", @auth_port=35357, @auth_url="https://regionerer-g.go-bar.identity.dacloudfoo.herpy:13327/v2.0/", @region=nil, @authtoken="Auth_543254fdsasabd546543a3", @retry_auth=nil>>
34
+
35
+ irb(main):003:0> os.servers
36
+ => []
37
+
38
+ irb(main):004:0> os = OpenStack::Connection.create({:username => "AWHFDADHJ32EL6V23GFK", :api_key=>"jd823jFDJEY2/82jfhYteG52AKJAUEY184JHRfeR", :auth_method=> "key", :auth_url => "https://regionerer-g.go-bar.identity.dacloudfoo.herpy:13327/v2.0/", :authtenant_id =>"482195756462871", :service_type=>"object-store"})
39
+
40
+ => #<OpenStack::Swift::Connection:0xb72ff2a8 @connection=#<OpenStack::Connection:0xb72ff460 @service_scheme="https", @auth_host="regionerer-g.go-bar.identity.dacloudfoo.herpy", @http={}, @service_name=nil, @authuser="AWHFDADHJ32EL6V23GFK", @proxy_port=nil, @auth_path="/v2.0/", @authtenant={:type=>"tenantId", :value=>"482195756462871"}, @service_port=443, @authkey="jd823jFDJEY2/82jfhYteG52AKJAUEY184JHRfeR", @authok=true, @service_type="object-store", @auth_method="key", @auth_scheme="https", @service_host="region-a.geo-1.objects.dacloudfoo.herpy", @is_debug=nil, @proxy_host=nil, @service_path="/v1.0/482195756462871", @auth_port=35357, @auth_url="https://regionerer-g.go-bar.identity.dacloudfoo.herpy:13327/v2.0/", @region=nil, @authtoken="Auth_543254fdsasabd546543a3", @retry_auth=nil>>
41
+
42
+ irb(main):006:0> os.containers
43
+ => ["herpy_Foo_container", "derpy_bar_bucket"]
44
+
11
45
  == Examples
12
46
 
13
47
  == For Compute:
@@ -59,10 +93,56 @@ See the class definitions for documentation on specific methods and operations.
59
93
  >> newserver.progress
60
94
  => 12
61
95
 
96
+ # Create a new server and specify the keyname to be used (some provider support the Keys extension):
97
+ >> server = os.create_server({:imageRef=>14075, :flavorRef=>100, :key_name=>"my_default_key", :name=>"marios_server"})
98
+ => #<OpenStack::Compute::Server:0x101433f08 ....
99
+ >> server.key_name
100
+ => "my_default_key"
101
+
62
102
  # Delete the new server
63
103
  >> newserver.delete!
64
104
  => true
65
105
 
106
+ === Compute API extensions.
107
+
108
+ # Get info on extensions offered by the given OpenStack provider:
109
+ >> os.api_extensions
110
+ => { :os-keypairs => { :links=>[], :updated=>"2011-08-08T00:00:00+00:00", :description=>"Keypair Support",
111
+ :namespace=>"http://docs.openstack.org/ext/keypairs/api/v1.1", :name=>"Keypairs",
112
+ :alias=>"os-keypairs" },
113
+ :os-floating_ips => { :links=>[], :updated=>"2011-06-16T00:00:00+00:00", :description=>"Floating IPs support",
114
+ :namespace=>"http://docs.openstack.org/ext/floating_ips/api/v1.1", :name=>"Floating_ips", :alias=>"os-floating-ips"},
115
+ ... }
116
+
117
+ # Get list of keypairs for current tenant/account:
118
+ >> os.keypairs
119
+ => { :key_one => { :fingerprint => "3f:12:4d:d1:54:f1:f4:3f:fe:a8:12:ec:1a:fb:35:b2",
120
+ :public_key => "ssh-rsa AAAAB3Nza923kJ ...
121
+ :name => "key_one"},
122
+ :key_two => { ... },
123
+ ... }
124
+
125
+ # Create new keypair:
126
+ >> os.create_keypair({:name=>"test_key"})
127
+ => { :name => "test_key",
128
+ :fingerprint => "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
129
+ :user_id => "dev_41247879706381",$
130
+ :public_key => "ssh-rsa AAAAB3NzaC1y ...
131
+ :private_key => "-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBA ... -----END RSA PRIVATE KEY-----\n"
132
+ }
133
+
134
+ # Import keypair:
135
+ >> os.create_keypair({:name=>"test_key_imported", :public_key=>"sh-rsa AAAAB3Nza923kJ ..."})
136
+ => { :name => "test_key_imported",
137
+ :fingerprint => "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
138
+ :user_id => "dev_41247879706381",$
139
+ :public_key => "ssh-rsa AAAAB3Nza ...
140
+ }
141
+
142
+ # Delete Keypair:
143
+ >> os.delete_keypair("test_key_imported")
144
+ => true
145
+
66
146
  == Examples for Object-Store:
67
147
 
68
148
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.0.1
@@ -202,6 +202,117 @@ module Compute
202
202
  OpenStack.symbolize_keys(JSON.parse(response.body)['limits'])
203
203
  end
204
204
 
205
+ # ==============================
206
+ # API EXTENSIONS
207
+ #
208
+ # http://nova.openstack.org/api_ext/index.html
209
+ # http://api.openstack.org/ (grep 'Compute API Extensions')
210
+ #
211
+
212
+
213
+ #query the openstack provider for any implemented extensions to the compute API
214
+ #returns a hash with openstack service provider's returned details
215
+ #about the implemented extensions, e.g.:
216
+ #
217
+ # { :os-floating_ips => { :links=>[],
218
+ # :updated=>"2011-06-16T00:00:00+00:00",
219
+ # :description=>"Floating IPs support",
220
+ # :namespace=>"http://docs.openstack.org/ext/floating_ips/api/v1.1",
221
+ # :name=>"Floating_ips", :alias=>"os-floating-ips"},
222
+ # :os-keypairs => { :links=>[],
223
+ # :updated=>"2011-08-08T00:00:00+00:00",
224
+ # :description=>"Keypair Support",
225
+ # :namespace=>"http://docs.openstack.org/ext/keypairs/api/v1.1",
226
+ # :name=>"Keypairs",
227
+ # :alias=>"os-keypairs"}
228
+ # }
229
+ #
230
+ def api_extensions
231
+ response = @connection.req("GET", "/extensions")
232
+ OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
233
+ res = OpenStack.symbolize_keys(JSON.parse(response.body))
234
+ res[:extensions].inject({}){|result, c| result[c[:alias].to_sym] = c ; result}
235
+ end
236
+
237
+ # Retrieve a list of key pairs associated with the current authenticated account
238
+ # Will return a hash:
239
+ # { :key_one => { :fingerprint => "3f:12:4d:d1:54:f1:f4:3f:fe:a8:12:ec:1a:fb:35:b2",
240
+ # :public_key => "ssh-rsa AAAAB3Nza923kJU123AADAQABAAAAg928JUwydszi029kIJudfOzQ7o160Ll1ItybDzYYcCAJ/N02loIKJU17264520bmXZFSsaZf2ErX3nSBNI3K+2zQzu832jkhkfdsa7GHH5hvNOxO7u800894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@nv-zz2232-api0002\n",
241
+ # :name => "key_one"},
242
+ #
243
+ # :key_two => { :fingerprint => "6b:32:dd:d2:51:c1:f2:3a:fb:a2:52:3a:1a:bb:25:1b",
244
+ # :public_key => "ssh-rsa AKIJUuw71645kJU123AADAQABAAAAg928019oiUJY12765IJudfOzQ7o160Ll1ItybDzYYcCAJ/N80438012480321jhkhKJlfdsazu832jkhkfdsa7GHH5fdasfdsajlj2999789799987989894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@bv-fdsa32-api0002\n",
245
+ # :name => "key_two"}
246
+ # }
247
+ #
248
+ # Raises OpenStack::Exception::NotImplemented if the current provider doesn't
249
+ # offer the os-keypairs extension
250
+ #
251
+ def keypairs
252
+ begin
253
+ response = @connection.req("GET", "/os-keypairs")
254
+ res = OpenStack.symbolize_keys(JSON.parse(response.body))
255
+ res[:keypairs].inject({}){|result, c| result[c[:keypair][:name].to_sym] = c[:keypair] ; result }
256
+ rescue OpenStack::Exception::ItemNotFound => not_found
257
+ msg = "The os-keypairs extension is not implemented for the provider you are talking to "+
258
+ "- #{@connection.http.keys.first}"
259
+ raise OpenStack::Exception::NotImplemented.new(msg, 501, "#{not_found.message}")
260
+ end
261
+ end
262
+
263
+ # Create a new keypair for use with launching servers. Raises
264
+ # a OpenStack::Exception::NotImplemented if os-keypairs extension
265
+ # is not implemented (or not advertised) by the OpenStack provider.
266
+ #
267
+ # The 'name' parameter MUST be supplied, otherwise a
268
+ # OpenStack::Exception::MissingArgument will be raised.
269
+ #
270
+ # Optionally requests can specify a 'public_key' parameter,
271
+ # with the full public ssh key (String) to be used to create the keypair
272
+ # (i.e. import a existing key).
273
+ #
274
+ # Returns a hash with details of the new key; the :private_key attribute
275
+ # must be saved must be saved by caller (not retrievable thereafter).
276
+ # NOTE: when the optional :public_key parameter is given, the response
277
+ # will obviously NOT contain the :private_key attribute.
278
+ #
279
+ # >> os.create_keypair({:name=>"test_key"})
280
+ # => { :name => "test_key",
281
+ # :fingerprint => "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
282
+ # :user_id => "dev_41247879706381",
283
+ # :public_key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDgGhDH3z9uMAvPV8ziE9BCEjHCPXGufy5bOgY5mY5jOSfdmKspdbl0z/LimHVKRDNX6HoL5qRg5V/tGH/NYP5sX2zF/XRKz16lfBxiUL1EONXA9fsTEBR3FGp8NcA7hW2+YiUxWafms4If3NFqttTQ11XqTU8JCMvms4D81lhbiQ== nova@use03147k5-eth0\n",
284
+ # :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"
285
+ # }
286
+ #
287
+ # Will raise an OpenStack::Exception::BadRequest if an invalid public_key is provided:
288
+ # >> os.create_keypair({:name=>"marios_keypair_test_invalid", :public_key=>"derp"})
289
+ # => OpenStack::Exception::BadRequest: Unexpected error while running command.
290
+ # Stdout: '/tmp/tmp4kI12a/import.pub is not a public key file.\n'
291
+ #
292
+ def create_keypair(options)
293
+ raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
294
+ raise OpenStack::Exception::MissingArgument, "Keypair name must be supplied" unless (options[:name])
295
+ data = JSON.generate(:keypair => options)
296
+ response = @connection.req("POST", "/os-keypairs", {:data=>data})
297
+ res = OpenStack.symbolize_keys(JSON.parse(response.body))
298
+ res[:keypair]
299
+ end
300
+
301
+ # Delete an existing keypair. Raises OpenStack::Exception::NotImplemented
302
+ # if os-keypairs extension is not implemented (or not advertised) by the OpenStack provider.
303
+ #
304
+ # Returns true if succesful.
305
+ # >> os.delete_keypair("marios_keypair")
306
+ # => true
307
+ #
308
+ # Will raise OpenStack::Exception::ItemNotFound if specified keypair doesn't exist
309
+ #
310
+ def delete_keypair(keypair_name)
311
+ raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
312
+ @connection.req("DELETE", "/os-keypairs/#{keypair_name}")
313
+ true
314
+ end
315
+
205
316
  end
206
317
  end
207
318
  end
@@ -16,6 +16,7 @@ module Compute
16
16
  attr_reader :flavor
17
17
  attr_reader :metadata
18
18
  attr_accessor :adminPass
19
+ attr_reader :key_name
19
20
 
20
21
  # This class is the representation of a single Server object. The constructor finds the server identified by the specified
21
22
  # ID number, accesses the API via the populate method to get information about that server, and returns the object.
@@ -61,6 +62,7 @@ module Compute
61
62
  @hostId = data["hostId"]
62
63
  @image = data["image"]
63
64
  @flavor = data["flavor"]
65
+ @key_name = data["key_name"] # if provider uses the keys API extension for accessing servers
64
66
  true
65
67
  end
66
68
  alias :refresh :populate
@@ -37,9 +37,11 @@ class Connection
37
37
  #
38
38
  # options hash:
39
39
  #
40
- # :username - Your OpenStack username *required*
41
- # :tenant - Your OpenStack tenant *required*. Defaults to username.
42
- # :api_key - Your OpenStack API key *required*
40
+ # :auth_method - Type of authentication - 'password', 'key', 'rax-kskey' - defaults to 'password'
41
+ # :username - Your OpenStack username or public key, depending on auth_method. *required*
42
+ # :authtenant_name OR :authtenant_id - Your OpenStack tenant name or id *required*. Defaults to username.
43
+ # passing :authtenant will default to using that parameter as tenant name.
44
+ # :api_key - Your OpenStack API key *required* (either private key or password, depending on auth_method)
43
45
  # :auth_url - Configurable auth_url endpoint.
44
46
  # :service_name - (Optional for v2.0 auth only). The optional name of the compute service to use.
45
47
  # :service_type - (Optional for v2.0 auth only). Defaults to "compute"
@@ -72,7 +74,7 @@ class Connection
72
74
  @authuser = options[:username] || (raise Exception::MissingArgument, "Must supply a :username")
73
75
  @authkey = options[:api_key] || (raise Exception::MissingArgument, "Must supply an :api_key")
74
76
  @auth_url = options[:auth_url] || (raise Exception::MissingArgument, "Must supply an :auth_url")
75
- @authtenant = options[:authtenant] || @authuser
77
+ @authtenant = (options[:authtenant_id])? {:type => "tenantId", :value=>options[:authtenant_id]} : {:type=>"tenantName", :value=>(options[:authtenant_name] || options[:authtenant] || @authuser)}
76
78
  @auth_method = options[:auth_method] || "password"
77
79
  @service_name = options[:service_name] || nil
78
80
  @service_type = options[:service_type] || "compute"
@@ -106,7 +108,7 @@ class Connection
106
108
  chunked = OpenStack::Swift::ChunkedConnectionWrapper.new(data, 65535)
107
109
  request.body_stream = chunked
108
110
  else
109
- headers['Content-Length'] = (body.respond_to?(:lstat))? body.lstat.size.to_s : ((body.respond_to?(:size))? body.size.to_s : "0")
111
+ headers['Content-Length'] = (data.respond_to?(:lstat))? data.lstat.size.to_s : ((data.respond_to?(:size))? data.size.to_s : "0")
110
112
  hdrhash = headerprep(headers)
111
113
  request = Net::HTTP::Put.new(path,hdrhash)
112
114
  request.body = data
@@ -260,12 +262,15 @@ class AuthV20
260
262
 
261
263
  @uri = String.new
262
264
 
263
- if connection.auth_method == "password"
264
- auth_data = JSON.generate({ "auth" => { "passwordCredentials" => { "username" => connection.authuser, "password" => connection.authkey }, "tenantName" => connection.authtenant}})
265
- elsif connection.auth_method == "rax-kskey"
266
- auth_data = JSON.generate({"auth" => {"RAX-KSKEY:apiKeyCredentials" => {"username" => connection.authuser, "apiKey" => connection.authkey}}})
267
- else
268
- raise Exception::InvalidArgument, "Unrecognized auth method #{connection.auth_method}"
265
+ case connection.auth_method
266
+ when "password"
267
+ auth_data = JSON.generate({ "auth" => { "passwordCredentials" => { "username" => connection.authuser, "password" => connection.authkey }, connection.authtenant[:type] => connection.authtenant[:value]}})
268
+ when "rax-kskey"
269
+ auth_data = JSON.generate({"auth" => {"RAX-KSKEY:apiKeyCredentials" => {"username" => connection.authuser, "apiKey" => connection.authkey}}})
270
+ when "key"
271
+ auth_data = JSON.generate({"auth" => { "apiAccessKeyCredentials" => {"accessKey" => connection.authuser, "secretKey" => connection.authkey}, connection.authtenant[:type] => connection.authtenant[:value]}})
272
+ else
273
+ raise Exception::InvalidArgument, "Unrecognized auth method #{connection.auth_method}"
269
274
  end
270
275
 
271
276
  response = server.post(connection.auth_path.chomp("/")+"/tokens", auth_data, {'Content-Type' => 'application/json'})
@@ -46,7 +46,7 @@ module Swift
46
46
  # cont.create_object("my_new_object", {}, "object data")
47
47
  #
48
48
  def self.create(container, objectname, headers={}, data=nil)
49
- provided_headers = (headers[:metadata] || {}).inject({}){|res, (k,v)| ((k.match /^X-Object-Meta-/i) ? res[k]=v : res["X-Object-Meta-#{k}"]=v) ;res}
49
+ provided_headers = (headers[:metadata] || {}).inject({}){|res, (k,v)| ((k.to_s.match /^X-Object-Meta-/i) ? res[k.to_s]=v : res["X-Object-Meta-#{k.to_s}"]=v) ;res}
50
50
  provided_headers["content-type"] = headers[:content_type] unless headers[:content_type].nil?
51
51
  provided_headers["ETag"] = headers[:etag] unless headers[:etag].nil?
52
52
  provided_headers["X-Object-Manifest"] = headers[:manifest] unless headers[:manifest].nil?
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openstack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 0
10
- version: 1.0.0
9
+ - 1
10
+ version: 1.0.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dan Prince
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-05-23 00:00:00 Z
19
+ date: 2012-06-22 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: json
@@ -58,7 +58,7 @@ files:
58
58
  - lib/openstack/swift/connection.rb
59
59
  - lib/openstack/swift/container.rb
60
60
  - lib/openstack/swift/storage_object.rb
61
- homepage: https://launchpad.net/ruby-openstack-compute
61
+ homepage: https://github.com/ruby-openstack/ruby-openstack
62
62
  licenses: []
63
63
 
64
64
  post_install_message: