Ruby OpenStack

This fork assumes Swauth authentication for Swift object store.

Description

Ruby Openstack Compute, Object-Store and Block Storage bindings for the OpenStack API.

api.openstack.org/api-reference.html

Currently supports both v1.0 and v2.0 (keystone) auth.

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’). If the requested service is not deployed the gem will throw a OpenStack::Exception::NotImplemented (501) - e.g. :service_type is ‘object-store’ but swift service isn’t deployed.

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’, ‘volume’, or ‘object-store’ (defaults to ‘compute’) - see below for examples.

Other parameters for the create method:

  • :auth_url - the OpenStack service provider specific authentication url endpoint.

  • :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’.

  • :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.

  • :username - the username or public key (depending on auth_method)

  • :api_key - the password or private key (denending on auth_method).

Try it out:

sudo gem install openstack
  [sudo] password for herp:
  Successfully installed openstack-1.0.0

[herp@name lib]$ irb -rubygems
irb(main):001:0> require 'openstack'
  => true

irb(main):002:0> os = OpenStack::Connection.create({:username => "[email protected]", :api_key=>"1234abcd", :auth_method=>"password", :auth_url => "https://regionerer-g.go-bar.identity.dacloudfoo.herpy:13327/v2.0/", :authtenant_name =>"[email protected]", :service_type=>"compute"})

  => #<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="[email protected]", @proxy_port=nil, @auth_path="/v2.0/", @authtenant={:type=>"tenantName", :value=>"[email protected]"}, @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>>

irb(main):003:0> os.servers
  => []

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"})

  => #<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>>

irb(main):006:0> os.containers
  => ["herpy_Foo_container", "derpy_bar_bucket"]

Examples

For Compute:

See the class definitions for documentation on specific methods and operations.

require 'openstack'

os = OpenStack::Connection.create(:username => USERNAME, :api_key => API_KEY, :authtenant => TENANT, :auth_url => API_URL, :service_type => "compute")

# Get a listing of all current servers
>> os.servers
=> [{:name=>"Server1", :id=>110917}]

# Access a specific server
>> server = os.server(110917)
>> server.name
=> "Server1"

# See what type of server this is
>> server.flavor.name
=> "256 server"
>> server.image.name
=> "Ubuntu 8.04.2 LTS (hardy)"

# Soft-reboot the server
>> server.reboot
=> true

# Create a new 512MB CentOS 5.2 server.  The root password is returned in the adminPass method.
>> image = os.get_image(8)
=> #<OpenStack::Compute::Image:0x1014a8060 ...>, status"ACTIVE"
>> image.name
=> "CentOS 5.2"
>> flavor = os.get_flavor(2)
=> #<OpenStack::Compute::Flavor:0x101469130 @disk=20, @name="512 server", @id=2, @ram=512>
>> flavor.name
=> "512 server"
>> newserver = os.create_server(:name => "New Server", :imageRef => image.id, :flavorRef => flavor.id)
=> #<OpenStack::Compute::Server:0x101433f08 ....
>> newserver.status
=> "BUILD"
>> newserver.progress
=> 0
>> newserver.adminPass
=> "NewServerMbhzUnO"
>> newserver.refresh
=> true
>> newserver.progress
=> 12

# Create a new server and specify the keyname and security_groups to be used (dependent on provider support for these API extensions):
>> server = os.create_server({:imageRef=>14075, :flavorRef=>100, :key_name=>"my_default_key", :security_groups=>["test", "default"], :name=>"marios_server"})
=> #<OpenStack::Compute::Server:0x101433f08 ....
>> server.key_name
=> "my_default_key"
>> server.security_groups

# Delete the new server
>> newserver.delete!
=> true

Compute API extensions.

# Get info on extensions offered by the given OpenStack provider:
>> os.api_extensions
=> { :os-keypairs     =>  { :links=>[], :updated=>"2011-08-08T00:00:00+00:00", :description=>"Keypair Support",
                            :namespace=>"http://docs.openstack.org/ext/keypairs/api/v1.1", :name=>"Keypairs",
                            :alias=>"os-keypairs" },
     :os-floating_ips =>  { :links=>[], :updated=>"2011-06-16T00:00:00+00:00", :description=>"Floating IPs support",
                            :namespace=>"http://docs.openstack.org/ext/floating_ips/api/v1.1", :name=>"Floating_ips", :alias=>"os-floating-ips"},
      ... }

# Get list of keypairs for current tenant/account:
>> os.keypairs
=> { :key_one => {  :fingerprint  =>  "3f:12:4d:d1:54:f1:f4:3f:fe:a8:12:ec:1a:fb:35:b2",
                    :public_key   =>  "ssh-rsa AAAAB3Nza923kJ  ...
                    :name         =>  "key_one"},
     :key_two =>  { ... },
    ... }

# Create new keypair:
>> os.create_keypair({:name=>"test_key"})
=> {  :name         =>  "test_key",
      :fingerprint  =>  "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
      :user_id      =>  "dev_41247879706381",$
      :public_key   =>  "ssh-rsa AAAAB3NzaC1y    ...
      :private_key  =>  "-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBA ... -----END RSA PRIVATE KEY-----\n"
   }

# Import keypair:
>> os.create_keypair({:name=>"test_key_imported", :public_key=>"sh-rsa AAAAB3Nza923kJ  ..."})
=> {  :name         =>  "test_key_imported",
      :fingerprint  =>  "f1:f3:a2:d3:ca:75:da:f1:06:f4:f7:dc:cc:7d:e1:ca",
      :user_id      =>  "dev_41247879706381",$
      :public_key   =>  "ssh-rsa AAAAB3Nza ...
   }

# Delete Keypair:
>> os.delete_keypair("test_key_imported")
=> true

# List all security groups:
>> os.security_groups
=>  { "1381" => { :tenant_id=>"12345678909876", :id=>1381, :name=>"default", :description=>"default",
                  :rules=> [
                      {:from_port=>22, :group=>{}, :ip_protocol=>"tcp", :to_port=>22,
                       :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4902},
                           ]
                 },
      "1234" => { ... } }

# Get a specific security group:
>> os.security_group(1381)
=>  { "1381" => { :tenant_id=>"12345678909876", :id=>1381, :name=>"default", :description=>"default",
                  :rules=> [
                      {:from_port=>22, :group=>{}, :ip_protocol=>"tcp", :to_port=>22,
                       :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4902},
                           ]
                 }}

#Create a new security group:
>> os.create_security_group("devel_group", "all development machines")
=> {"9573"=>{:rules=>[], :tenant_id=>"46871569847393", :id=>9573, :name=>"devel_group", :description=>"all development machines"}}

#Create a new security group rule - first param is id of the security group for this rule. Instead of :cidr you may specify :group_id to use another group as source:
>> os.create_security_group_rule(9567, {:ip_protocol=>"tcp", :from_port=>"123", :to_port=>"123", :cidr=>"192.168.0.1/16" })
=> => {"27375"=>{:from_port=>123, :group=>{}, :ip_protocol=>"tcp", :to_port=>123, :parent_group_id=>9573, :ip_range=>{:cidr=>"192.168.0.1/16"}, :id=>27375}}

#Delete a security group rule:
>> os.delete_security_group_rule(27375)
=> true

#Delete a security group:
>>  os.delete_security_group(9571)
=> true

#Attach a volume to a server - params in order are: server_id, volume_id, attachment_point

>> os.attach_volume(704289, 90805, "/dev/sde")
=> true

#List attachments for a server:

>> os.list_attachments 704289
=> {:volumeAttachments=>[{:device=>"/dev/sde", :serverId=>704289, :id=>90805, :volumeId=>90805}]}

#Detach volume from server - params in order are server_id and attachment_id
>> os.detach_volume(704289, 90805)
=> true

Examples for Volumes and Snaphots:

#NOTE - attach/detach operations are implemented for the compute service as the OS API defines these operations as extensions to Openstack Compute.

vs = OpenStack::Connection.create({:username => "username", :api_key=>"pass", :auth_url => "https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/", :authtenant=>"username-default-tenant", :service_type=>"volume"})

# Create a volume: - MUST specify display_name and size parameters... optional params are {:display_description, :metadata=>{:key=>val, ...}, :availability_zone, :volume_type }

>> volume = vs.create_volume({:display_name=>"marios volume", :size=>1, :display_description=>"some new volume bla"})
=> #<OpenStack::Volume::Volume:0x8a3c534 @id=90805, @display_name="marios volume", @display_description="some new volume bla", @size=1, @volume_type=nil, @metadata={}, @availability_zone="nova", @snapshot_id="", @attachments=[{}], @created_at="2012-11-15 13:51:23">

>> volume.size
=> 1

>> volume.attachments
=> [{}]

# List volumes:

>> vs.list_volumes                    #aliased as just 'volumes' ... vs.volumes
=> [#<OpenStack::Volume::Volume:0x8aad2e8 @id=90625, @display_name="marios volume", @display_description="attach test volume", @size=1, @volume_type=nil, @metadata={}, @availability_zone="nova", @snapshot_id="", @attachments=[{}], @created_at="2012-11-15 09:54:58">, #<OpenStack::Volume::Volume:0x8aad144 @id=90805, @display_name="marios volume", @display_description="some new volume bla", @size=1, @volume_type=nil, @metadata={}, @availability_zone="nova", @snapshot_id="", @attachments=[{}], @created_at="2012-11-15 13:51:23">]

# Get a particular volume - must specify ID:

>> vs.get_volume(90625)                #aliased as 'volume'
=> #<OpenStack::Volume::Volume:0x8abd4a4 @id=90625, @display_name="marios volume", @display_description="attach test volume", @size=1, @volume_type=nil, @metadata={}, @availability_zone="nova", @snapshot_id="", @attachments=[{}], @created_at="2012-11-15 09:54:58">

# Delete a volume:

>> vs.delete_volume(90805)
=> true

# Create a snapshot: - must specify display_name and volume_id parameters... optionally also :display_description
>> vs.create_snapshot({:volume_id=>"3b38b570-a4ff-4444-984a-3566cbdc8ab2", :display_name=>"marios_snapshot"})
=> #<OpenStack::Volume::Snapshot:0xa0cfea4 @id=223, @display_name="marios_snapshot", @display_description=nil, @volume_id="3b38b570-a4ff-4444-984a-3566cbdc8ab2", @status="creating", @size=1, @created_at="2013-01-23 14:25:02.654217">

# List snapshots: - aliased as just 'snapshots' ... vs.snapshots
>> vs.list_snapshots
=> [#<OpenStack::Volume::Snapshot:0x9a6e4fc @id="0e76dacb-2fcf-4565-84a3-aa3d7bd16224", @display_name="marios_snapshot", @display_description=nil, @volume_id="3b38b570-a4ff-4444-984a-3566cbdc8ab2", @status="creating", @size=1, @created_at="2013-01-23T14:18:13.000000">]

# Get a specific snapshot: - aliased as 'snapshot'
>> vs.get_snapshot("0e76dacb-2fcf-4565-84a3-aa3d7bd16224")
=> #<OpenStack::Volume::Snapshot:0x9a890b8 @id="0e76dacb-2fcf-4565-84a3-aa3d7bd16224", @display_name="marios_snapshot", @display_description=nil, @volume_id="3b38b570-a4ff-4444-984a-3566cbdc8ab2", @status="creating", @size=1, @created_at="2013-01-23T14:18:13.000000">

# Delete a snapshot:
>> vs.delete_snapshot("0e76dacb-2fcf-4565-84a3-aa3d7bd16224")
=> true

Examples for Object-Store:

os = OpenStack::Connection.create(:username => USERNAME, :api_key => API_KEY, :authtenant => TENANT, :auth_url => API_URL, :service_type => "object-store")

# Get info on container count and bytes:
>> os.get_info
=> {:count=>2, :bytes=>495041}

# Get list of containers under this account:
>> os.containers
=> ["another_containerfoo", "marios_test_container"]

# Get details of containers under this account:
>> os.containers_detail
=>=> {"another_containerfoo"=>{:count=>"3", :bytes=>"1994"}, "marios_test_container"=>{:count=>"2", :bytes=>"493047"}}

# Check if a container exists
>> os.container_exists?("no_such_thing")
=> false

# Create new container
>> os.create_container("foo")
=> => #<OpenStack::Swift::Container:0xb7275c38  ...... (rest of OpenStack::Swift::Container object)

# Delete container
>> os.delete_container("foo")
=> true

# Get a container (OpenStack::Swift::Container object):
>> cont = os.container("foo")
=> #<OpenStack::Swift::Container:0xb7262124 ...... (rest of OpenStack::Swift::Container object)

# Retrieve container metadata:
>> cont.container_metadata
=>{:count=>"2", :bytes=>"493047", :metadata=>{"foo"=>"bar", "author"=>"foobar", "jj"=>"foobar", "date"=>"today", "owner"=>"foo"}}

# Retrieve user defined metadata:
>> cont.metadata
=> {"foo"=>"bar", "author"=>"foobar", "jj"=>"foobar", "date"=>"today", "owner"=>"foo"}

# Set user defined metadata:
>> cont.set_metadata({"X-Container-Meta-Author"=> "msa", "version"=>"1.2", :date=>"today"})
=> true

# Get list of objects:
>> cont.objects
=> ["fosdem2012.pdf", "moved_object"]

# Get list of objects with details:
>> cont.objects_detail
=> {"fosdem2012.pdf"=>{:bytes=>"493009", :content_type=>"application/json", :hash=>"494e444f92a8082dabac80a74cdf2c3b", :last_modified=>"2012-04-26T09:22:51.611230"}, "moved_object"=>{:bytes=>"38", :content_type=>"application/json", :hash=>"a7942f97fe6bd34920a4f61fe5e604a5", :last_modified=>"2012-04-26T09:35:33.839920"}}

# Check if container is empty:
>> cont.empty?
=> false

# Check if object exists:
>> cont.object_exists?("foo")
=> false

# Create new object
>> new_obj = cont.create_object("foo", {:metadata=>{"herpy"=>"derp"}, :content_type=>"text/plain"}, "this is the data")  [can also supply File.open(/path/to/file) and the data]
=> #<OpenStack::Swift::StorageObject:0xb72fdac0  ... etc

# Delete object
>> cont.delete_object("foo")
=> true

# Get handle to an OpenStack::Swift::StorageObject Object
>> obj = cont.object("foo")
=> #<OpenStack::Swift::StorageObject:0xb72fdac0  ... etc

# Get object metadata
>> obj.object_metadata
=>

# Get user defined metadata pairs
>> obj.metadata
=>

# Get data (non streaming - returned as a String)
>> obj.data
=> "This is the text stored in the file"

# Get data (streaming - requires a block)
>> data = ""; object.data_stream do |chunk| data += chunk end
=> #<Net::HTTPOK 200 OK readbody=true>
>> data
=> "This is the text stored in the file"

# Set user defined metadata
>> obj.set_metadata({:foo=>"bar", "X-Object-Meta-herpa"=>"derp", "author"=>"me"})
=> true

# (Over)Write object data
>> object.write("This is new data")
=> true
>> object.data
=> "This is new data"

# Copy object:
>>copied = obj.copy('copied_object', "destination_container", {:content_type=>"text/plain", :metadata=>{:herp=>"derp", "X-Object-Meta-foo"=>"bar} } )
=> #<OpenStack::Swift::StorageObject:0xb728974c  ..... etc

# Move object: (copy and then delete original):
>> moved = obj.move('moved_object', "destination_container", {:content_type=>"text/plain", :metadata=>{:herp=>"derp", "X-Object-Meta-foo"=>"bar"} } )
=>  #<OpenStack::Swift::StorageObject:0xb7266bd4 ...
>> moved.metadata
=> {"foo"=>"bar", "herp"=>"derp", "herpy"=>"derp"}
>> obj.metadata
=> OpenStack::Exception::ItemNotFound: The resource could not be found

Authors

By Dan Prince <[email protected]>, Naveed Massjouni <[email protected]>, Marios Andreou <[email protected]>

Initial code checkin on May 23rd 2012 - code refactored from and based on the Rackspace Cloud Servers gem (github.com/rackspace/ruby-openstack-compute) and Rackspace Cloud Files gem (github.com/rackspace/ruby-cloudfiles).

License

See COPYING for license information.