openstack 1.0.0 → 1.0.1

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