Permalink
Fetching contributors…
Cannot retrieve contributors at this time
617 lines (560 sloc) 32.4 KB
module OpenStack
module Compute
class Connection
attr_accessor :connection
attr_accessor :extensions
def initialize(connection)
@extensions = nil
@connection = connection
OpenStack::Authentication.init(@connection)
end
# Returns true if the authentication was successful and returns false otherwise.
#
# cs.authok?
# => true
def authok?
@connection.authok
end
# Returns the OpenStack::Compute::Server object identified by the given id.
#
# >> server = cs.get_server(110917)
# => #<OpenStack::Compute::Server:0x101407ae8 ...>
# >> server.name
# => "MyServer"
def get_server(id)
OpenStack::Compute::Server.new(self,id)
end
alias :server :get_server
# Returns an array of hashes, one for each server that exists under this account. The hash keys are :name and :id.
#
# You can also provide :limit and :offset and :"changes-since" and :image and :flavor and :name and :status
# and :host and :limit and :marker parameters to handle pagination.
# http://developer.openstack.org/api-ref-compute-v2.1.html
#
# >> cs.list_servers
# => [{:name=>"MyServer", :id=>110917}]
#
# >> cs.list_servers(:limit => 2, :offset => 3, :name => "MyServer", :status => "ACTIVE")
# => [{:name=>"demo-standingcloud-lts", :id=>168867},
# {:name=>"demo-aicache1", :id=>187853}]
def list_servers(options = {})
anti_cache_param="cacheid=#{Time.now.to_i}"
path = options.empty? ? "#{@connection.service_path}/servers?#{anti_cache_param}" : "#{@connection.service_path}/servers?#{options.to_query}&#{anti_cache_param}"
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
OpenStack.symbolize_keys(JSON.parse(response.body)["servers"])
end
alias :servers :list_servers
# Returns an array of hashes with more details about each server that exists under this account. Additional information
# includes public and private IP addresses, status, hostID, and more. All hash keys are symbols except for the metadata
# hash, which are verbatim strings.
#
# You can also provide :limit and :offset and :"changes-since" and :image and :flavor and :name and :status and :host
# and :limit and :marker parameters to handle pagination.
# http://developer.openstack.org/api-ref-compute-v2.1.html
# >> cs.list_servers_detail
# => [{: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"}]
#
# >> cs.list_servers_detail(:limit => 2, :offset => 3, :name => "MyServer", :flavor => 1, :status => "ACTIVE")
# => [{: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"},
# {: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"}]
def list_servers_detail(options = {})
path = options.empty? ? "#{@connection.service_path}/servers/detail" : "#{@connection.service_path}/servers/detail?#{options.to_query}"
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
json_server_list = JSON.parse(response.body)["servers"]
json_server_list.each do |server|
server["addresses"] = OpenStack::Compute::Address.fix_labels(server["addresses"])
end
OpenStack.symbolize_keys(json_server_list)
end
alias :servers_detail :list_servers_detail
# Creates a new server instance on OpenStack Compute
#
# The argument is a hash of options. The keys :name, :flavorRef,
# and :imageRef are required; :metadata, :security_groups,
# :key_name and :personality are optional.
#
# :flavorRef and :imageRef are href strings identifying a particular
# server flavor and image to use when building the server. The :imageRef
# can either be a stock image, or one of your own created with the
# server.create_image method.
#
# The :metadata argument should be a hash of key/value pairs. This
# metadata will be applied to the server at the OpenStack Compute API level.
#
# The "Personality" option allows you to include up to five files, # of
# 10Kb or less in size, that will be placed on the created server.
# For :personality, pass a hash of the form {'local_path' => 'server_path'}.
# The file located at local_path will be base64-encoded and placed at the
# location identified by server_path on the new server.
#
# Returns a OpenStack::Compute::Server object. The root password is
# available in the adminPass instance method.
#
# >> server = cs.create_server(
# :name => 'NewServer',
# :imageRef => '3',
# :flavorRef => '1',
# :metadata => {'Racker' => 'Fanatical'},
# :personality => {'/home/bob/wedding.jpg' => '/root/wedding.jpg'},
# :key_name => "mykey",
# :security_groups => [ "devel", "test"])
# => #<OpenStack::Compute::Server:0x101229eb0 ...>
# >> server.name
# => "NewServer"
# >> server.status
# => "BUILD"
# >> server.adminPass
# => "NewServerSHMGpvI"
def create_server(options)
raise OpenStack::Exception::MissingArgument, "Server name, flavorRef, and imageRef, must be supplied" unless (options[:name] && options[:flavorRef] && options[:imageRef])
if options[:personality]
options[:personality] = Personalities.get_personality(options[:personality])
end
options[:security_groups] = (options[:security_groups] || []).inject([]){|res, c| res << {"name"=>c} ;res}
data = JSON.generate(:server => options)
response = @connection.csreq("POST",@connection.service_host,"#{@connection.service_path}/servers",@connection.service_port,@connection.service_scheme,{'content-type' => 'application/json'},data)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
server_info = JSON.parse(response.body)['server']
server = OpenStack::Compute::Server.new(self,server_info['id'])
server.adminPass = server_info['adminPass'] if server_info['adminPass']
server
end
# Returns an array of hashes listing available server images that you have access too,
# including stock OpenStack Compute images and any that you have created. The "id" key
# in the hash can be used where imageRef is required. You can also provide :limit and
# :offset and :"changes-since" and :server and :name and :status :minDisk and :minRam
# and :type and :limit and :marker parameters to handle pagination.
#
# >> cs.list_images
# => [{:name=>"CentOS 5.2", :id=>2, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},
# {:name=>"Gentoo 2008.0", :id=>3, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},...
#
# >> cs.list_images(:limit => 3, :offset => 2, :status => "ACTIVE")
# => [{:status=>"ACTIVE", :name=>"Fedora 11 (Leonidas)", :updated=>"2009-12-08T13:50:45-06:00", :id=>13},
# {:status=>"ACTIVE", :name=>"CentOS 5.3", :updated=>"2009-08-26T14:59:52-05:00", :id=>7},
# {:status=>"ACTIVE", :name=>"CentOS 5.4", :updated=>"2009-12-16T01:02:17-06:00", :id=>187811}]
def list_images(options = {})
path = options.empty? ? "#{@connection.service_path}/images/detail" : "#{@connection.service_path}/images/detail?#{options.to_query}"
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
OpenStack.symbolize_keys(JSON.parse(response.body)['images'])
end
alias :images :list_images
# Returns a OpenStack::Compute::Image object for the image identified by the provided id.
#
# >> image = cs.get_image(8)
# => #<OpenStack::Compute::Image:0x101659698 ...>
def get_image(id)
OpenStack::Compute::Image.new(self,id)
end
alias :image :get_image
# Returns an array of hashes listing all available server flavors. The :id key in the hash can be used when flavorRef is required.
#
# You can also provide :limit and :offset parameters to handle pagination.
# http://developer.openstack.org/api-ref-compute-v2.1.html
#
# >> cs.list_flavors
# => [{:name=>"256 server", :id=>1, :ram=>256, :disk=>10},
# {:name=>"512 server", :id=>2, :ram=>512, :disk=>20},...
#
# >> cs.list_flavors(:limit => 3, :offset => 2)
# => [{:ram=>1024, :disk=>40, :name=>"1GB server", :id=>3},
# {:ram=>2048, :disk=>80, :name=>"2GB server", :id=>4},
# {:ram=>4096, :disk=>160, :name=>"4GB server", :id=>5}]
def list_flavors(options = {})
path = options.empty? ? "#{@connection.service_path}/flavors/detail" : "#{@connection.service_path}/flavors/detail?#{options.to_query}"
response = @connection.csreq("GET",@connection.service_host,path,@connection.service_port,@connection.service_scheme)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
OpenStack.symbolize_keys(JSON.parse(response.body)['flavors'])
end
alias :flavors :list_flavors
# Returns a OpenStack::Compute::Flavor object for the flavor identified by the provided ID.
#
# >> flavor = cs.flavor(1)
# => #<OpenStack::Compute::Flavor:0x10156dcc0 @name="256 server", @disk=10, @id=1, @ram=256>
def get_flavor(id)
response = @connection.req('GET', "/flavors/#{id}")
flavor_info = JSON.parse(response.body)['flavor']
OpenStack::Compute::Flavor.new(flavor_info)
end
alias :flavor :get_flavor
# nova.create_flavor({name: 'small', vcpus: 2, ram: 1024, disk: 1}, true)
# :name - must be unique, :ram - MB, :disk - GB
# => #<OpenStack::Compute::Flavor:0x007ff95333e268 @id="0c0c393b-3acd-4569-baae-7a7afbe398f6", @name="small", @ram=1024, @disk=1, @vcpus=2>
def create_flavor(options, public = false)
raise OpenStack::Exception::MissingArgument, 'Flavor name, vcpus, ram and disk, must be supplied' unless (options[:name] && options[:vcpus] && options[:ram] && options[:disk])
data = JSON.generate(:flavor => options.merge!({'os-flavor-access:is_public' => public}))
response = @connection.req('POST', '/flavors', {data: data})
flavor_info = JSON.parse(response.body)['flavor']
OpenStack::Compute::Flavor.new(flavor_info)
end
def delete_flavor(id)
@connection.req('DELETE', "/flavors/#{id}")
true
end
def add_tenant_to_flavor(flavor_id, tenant_id)
data = JSON.generate({'addTenantAccess' => {'tenant' => tenant_id}})
response = @connection.req('POST', "/flavors/#{flavor_id}/action", {data: data})
JSON.parse(response.body)
end
def delete_tenant_from_flavor(flavor_id, tenant_id)
data = JSON.generate({'removeTenantAccess' => {'tenant' => tenant_id}})
response = @connection.req('POST', "/flavors/#{flavor_id}/action", {data: data})
JSON.parse(response.body)
end
# Lists, creates, deletes, and updates the extra-specs or keys for a flavor.
# Creates extra specs for a flavor, by ID
#
# create_flavor_extra_specs('999aca87-5c8a-4862-92fe-deb8bb024b6b', { 'quota:disk_total_iops_sec' => 1000 })
# => { 'quota:disk_total_iops_sec' => 1000 }
def create_flavor_extra_specs(flavor_id, options)
data = JSON.generate(extra_specs: options)
response = @connection.req('POST', "/flavors/#{flavor_id}/os-extra_specs", data: data)
JSON.parse(response.body)['extra_specs']
end
# Lists all extra specs for a flavor, by ID.
#
# flavor_extra_specs('999aca87-5c8a-4862-92fe-deb8bb024b6b')
# => { 'quota:disk_total_iops_sec' => 1000 }
def flavor_extra_specs(flavor_id)
response = @connection.req('GET', "/flavors/#{flavor_id}/os-extra_specs")
JSON.parse(response.body)['extra_specs']
end
# Shows an extra spec, by key, for a flavor, by ID.
#
# flavor_extra_spec('999aca87-5c8a-4862-92fe-deb8bb024b6b', 'quota:disk_total_iops_sec')
# => { 'quota:disk_total_iops_sec' => 1000 }
def flavor_extra_spec(flavor_id, flavor_extra_spec_key)
response = @connection.req('GET', "/flavors/#{flavor_id}/os-extra_specs/#{flavor_extra_spec_key}")
JSON.parse(response.body)
end
# Updates an extra spec, by key, for a flavor, by ID.
#
# update_flavor_extra_spec('999aca87-5c8a-4862-92fe-deb8bb024b6b', 'quota:disk_total_iops_sec', 2000)
# => { 'quota:disk_total_iops_sec' => 2000 }
def update_flavor_extra_spec(flavor_id, flavor_extra_spec_key, new_value)
data = JSON.generate(flavor_extra_spec_key => new_value)
response = @connection.req('PUT', "/flavors/#{flavor_id}/os-extra_specs/#{flavor_extra_spec_key}", data: data)
JSON.parse(response.body)
end
# Deletes an extra spec, by key, for a flavor, by ID.
#
# delete_flavor_extra_spec('999aca87-5c8a-4862-92fe-deb8bb024b6b', 'quota:disk_total_iops_sec')
# => true
def delete_flavor_extra_spec(flavor_id, flavor_extra_spec_key)
@connection.req('DELETE', "/flavors/#{flavor_id}/os-extra_specs/#{flavor_extra_spec_key}")
true
end
# Returns the current state of the programatic API limits. Each account has certain limits on the number of resources
# allowed in the account, and a rate of API operations.
#
# The operation returns a hash. The :absolute hash key reveals the account resource limits, including the maxmimum
# amount of total RAM that can be allocated (combined among all servers), the maximum members of an IP group, and the
# maximum number of IP groups that can be created.
#
# The :rate hash key returns an array of hashes indicating the limits on the number of operations that can be performed in a
# given amount of time. An entry in this array looks like:
#
# {:regex=>"^/servers", :value=>50, :verb=>"POST", :remaining=>50, :unit=>"DAY", :resetTime=>1272399820, :URI=>"/servers*"}
#
# This indicates that you can only run 50 POST operations against URLs in the /servers URI space per day, we have not run
# any operations today (50 remaining), and gives the Unix time that the limits reset.
#
# Another example is:
#
# {:regex=>".*", :value=>10, :verb=>"PUT", :remaining=>10, :unit=>"MINUTE", :resetTime=>1272399820, :URI=>"*"}
#
# This says that you can run 10 PUT operations on all possible URLs per minute, and also gives the number remaining and the
# time that the limit resets.
#
# Use this information as you're building your applications to put in relevant pauses if you approach your API limitations.
def limits
response = @connection.csreq("GET",@connection.service_host,"#{@connection.service_path}/limits",@connection.service_port,@connection.service_scheme)
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
OpenStack.symbolize_keys(JSON.parse(response.body)['limits'])
end
# ==============================
# API EXTENSIONS
#
# http://nova.openstack.org/api_ext/index.html
# http://api.openstack.org/ (grep 'Compute API Extensions')
#
#query the openstack provider for any implemented extensions to the compute API
#returns a hash with openstack service provider's returned details
#about the implemented extensions, e.g.:
#
# { :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"},
# :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"}
# }
#
def api_extensions
if @extensions.nil?
response = @connection.req("GET", "/extensions")
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
res = OpenStack.symbolize_keys(JSON.parse(response.body))
@extensions = res[:extensions].inject({}){|result, c| result[c[:alias].to_sym] = c ; result}
end
@extensions
end
# Retrieve a list of key pairs associated with the current authenticated account
# Will return a hash:
# { :key_one => { :fingerprint => "3f:12:4d:d1:54:f1:f4:3f:fe:a8:12:ec:1a:fb:35:b2",
# :public_key => "ssh-rsa AAAAB3Nza923kJU123AADAQABAAAAg928JUwydszi029kIJudfOzQ7o160Ll1ItybDzYYcCAJ/N02loIKJU17264520bmXZFSsaZf2ErX3nSBNI3K+2zQzu832jkhkfdsa7GHH5hvNOxO7u800894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@nv-zz2232-api0002\n",
# :name => "key_one"},
#
# :key_two => { :fingerprint => "6b:32:dd:d2:51:c1:f2:3a:fb:a2:52:3a:1a:bb:25:1b",
# :public_key => "ssh-rsa AKIJUuw71645kJU123AADAQABAAAAg928019oiUJY12765IJudfOzQ7o160Ll1ItybDzYYcCAJ/N80438012480321jhkhKJlfdsazu832jkhkfdsa7GHH5fdasfdsajlj2999789799987989894312JKLJLHP/R91fdsajHKKJADSAgQ== nova@bv-fdsa32-api0002\n",
# :name => "key_two"}
# }
#
# Raises OpenStack::Exception::NotImplemented if the current provider doesn't
# offer the os-keypairs extension
#
def keypairs
begin
response = @connection.req("GET", "/os-keypairs")
res = OpenStack.symbolize_keys(JSON.parse(response.body))
res[:keypairs].inject({}){|result, c| result[c[:keypair][:name].to_sym] = c[:keypair] ; result }
rescue OpenStack::Exception::ItemNotFound => not_found
msg = "The os-keypairs extension is not implemented for the provider you are talking to "+
"- #{@connection.http.keys.first}"
raise OpenStack::Exception::NotImplemented.new(msg, 501, "#{not_found.message}")
end
end
# Create a new keypair for use with launching servers. Raises
# a OpenStack::Exception::NotImplemented if os-keypairs extension
# is not implemented (or not advertised) by the OpenStack provider.
#
# The 'name' parameter MUST be supplied, otherwise a
# OpenStack::Exception::MissingArgument will be raised.
#
# Optionally requests can specify a 'public_key' parameter,
# with the full public ssh key (String) to be used to create the keypair
# (i.e. import a existing key).
#
# Returns a hash with details of the new key; the :private_key attribute
# must be saved must be saved by caller (not retrievable thereafter).
# NOTE: when the optional :public_key parameter is given, the response
# will obviously NOT contain the :private_key attribute.
#
# >> 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 AAAAB3NzaC1yc2EAAAADAQABAAAAgQDgGhDH3z9uMAvPV8ziE9BCEjHCPXGufy5bOgY5mY5jOSfdmKspdbl0z/LimHVKRDNX6HoL5qRg5V/tGH/NYP5sX2zF/XRKz16lfBxiUL1EONXA9fsTEBR3FGp8NcA7hW2+YiUxWafms4If3NFqttTQ11XqTU8JCMvms4D81lhbiQ== nova@use03147k5-eth0\n",
# :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"
# }
#
# Will raise an OpenStack::Exception::BadRequest if an invalid public_key is provided:
# >> os.create_keypair({:name=>"marios_keypair_test_invalid", :public_key=>"derp"})
# => OpenStack::Exception::BadRequest: Unexpected error while running command.
# Stdout: '/tmp/tmp4kI12a/import.pub is not a public key file.\n'
#
def create_keypair(options)
raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
raise OpenStack::Exception::MissingArgument, "Keypair name must be supplied" unless (options[:name])
data = JSON.generate(:keypair => options)
response = @connection.req("POST", "/os-keypairs", {:data=>data})
res = OpenStack.symbolize_keys(JSON.parse(response.body))
res[:keypair]
end
# Delete an existing keypair. Raises OpenStack::Exception::NotImplemented
# if os-keypairs extension is not implemented (or not advertised) by the OpenStack provider.
#
# Returns true if succesful.
# >> os.delete_keypair("marios_keypair")
# => true
#
# Will raise OpenStack::Exception::ItemNotFound if specified keypair doesn't exist
#
def delete_keypair(keypair_name)
raise OpenStack::Exception::NotImplemented.new("os-keypairs not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-keypairs"]
@connection.req("DELETE", "/os-keypairs/#{keypair_name}")
true
end
#Security Groups:
#Returns a hash with the security group IDs as keys:
#=> { "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},
# {:from_port=>80, :group=>{}, :ip_protocol=>"tcp", :to_port=>80,
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4903},
# {:from_port=>443, :group=>{}, :ip_protocol=>"tcp", :to_port=>443,
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4904},
# {:from_port=>-1, :group=>{}, :ip_protocol=>"icmp", :to_port=>-1,
# :parent_group_id=>1381, :ip_range=>{:cidr=>"0.0.0.0/0"}, :id=>4905}],
# ]
# },
# "1234" => { ... } }
#
def security_groups
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]
response = @connection.req("GET", "/os-security-groups")
res = OpenStack.symbolize_keys(JSON.parse(response.body))
res[:security_groups].inject({}){|result, c| result[c[:id].to_s] = c ; result }
end
def security_group(id)
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]
response = @connection.req("GET", "/os-security-groups/#{id}")
res = OpenStack.symbolize_keys(JSON.parse(response.body))
{res[:security_group][:id].to_s => res[:security_group]}
end
def create_security_group(name, description)
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]
data = JSON.generate(:security_group => { "name" => name, "description" => description})
response = @connection.req("POST", "/os-security-groups", {:data => data})
res = OpenStack.symbolize_keys(JSON.parse(response.body))
{res[:security_group][:id].to_s => res[:security_group]}
end
def update_security_group(id, name, description)
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]
data = JSON.generate(:security_group => { "name" => name, "description" => description})
response = @connection.req("PUT", "/os-security-groups/#{id}", {:data => data})
res = OpenStack.symbolize_keys(JSON.parse(response.body))
{res[:security_group][:id].to_s => res[:security_group]}
end
def delete_security_group(id)
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]
@connection.req("DELETE", "/os-security-groups/#{id}")
true
end
#params: { :ip_protocol=>"tcp", :from_port=>"123", :to_port=>"123", :cidr=>"192.168.0.1/16", :group_id:="123" }
#observed behaviour against Openstack@HP cloud - can specify either cidr OR group_id as source, but not both
#if both specified, the group is used and the cidr ignored.
def create_security_group_rule(security_group_id, params)
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]
params.merge!({:parent_group_id=>security_group_id.to_s})
data = JSON.generate(:security_group_rule => params)
response = @connection.req("POST", "/os-security-group-rules", {:data => data})
res = OpenStack.symbolize_keys(JSON.parse(response.body))
{res[:security_group_rule][:id].to_s => res[:security_group_rule]}
end
def delete_security_group_rule(id)
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]
@connection.req("DELETE", "/os-security-group-rules/#{id}")
true
end
#VOLUMES - attach detach
def attach_volume(server_id, volume_id, device_id)
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
data = JSON.generate(:volumeAttachment => {"volumeId" => volume_id, "device"=> device_id})
@connection.req("POST", "/servers/#{server_id}/os-volume_attachments", {:data=>data})
true
end
def list_attachments(server_id)
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
response = @connection.req("GET", "/servers/#{server_id}/os-volume_attachments")
OpenStack.symbolize_keys(JSON.parse(response.body))
end
def detach_volume(server_id, attachment_id)
raise OpenStack::Exception::NotImplemented.new("os-volumes not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[:"os-volumes"]
@connection.req("DELETE", "/servers/#{server_id}/os-volume_attachments/#{attachment_id}")
true
end
#FLOATING IPs:
#list all float ips associated with tennant or account
def get_floating_ips
check_extension("os-floating-ips")
response = @connection.req("GET", "/os-floating-ips")
res = JSON.parse(response.body)["floating_ips"]
res.inject([]){|result, c| result<< OpenStack::Compute::FloatingIPAddress.new(c) ; result }
end
alias :floating_ips :get_floating_ips
#get details of a specific floating_ip by its id
def get_floating_ip(id)
check_extension("os-floating-ips")
response = @connection.req("GET", "/os-floating-ips/#{id}")
res = JSON.parse(response.body)["floating_ip"]
OpenStack::Compute::FloatingIPAddress.new(res)
end
alias :floating_ip :get_floating_ip
#can optionally pass the :pool parameter
def create_floating_ip(opts={})
check_extension("os-floating-ips")
data = opts[:pool] ? JSON.generate(opts) : JSON.generate({:pool=>nil})
response = @connection.req("POST", "/os-floating-ips",{:data=>data} )
res = JSON.parse(response.body)["floating_ip"]
OpenStack::Compute::FloatingIPAddress.new(res)
end
alias :allocate_floating_ip :create_floating_ip
#delete or deallocate a floating IP using its id
def delete_floating_ip(id)
check_extension("os-floating-ips")
@connection.req("DELETE", "/os-floating-ips/#{id}")
true
end
#add or attach a floating IP to a runnin g server
def attach_floating_ip(opts={:server_id=>"", :ip_id => ""})
check_extension("os-floating-ips")
#first get the address:
addr = get_floating_ip(opts[:ip_id]).ip
data = JSON.generate({:addFloatingIp=>{:address=>addr}})
@connection.req("POST", "/servers/#{opts[:server_id]}/action", {:data=>data})
true
end
def detach_floating_ip(opts={:server_id=>"", :ip_id => ""})
check_extension("os-floating-ips")
#first get the address:
addr = get_floating_ip(opts[:ip_id]).ip
data = JSON.generate({:removeFloatingIp=>{:address=>addr}})
@connection.req("POST", "/servers/#{opts[:server_id]}/action", {:data=>data})
true
end
def get_floating_ip_pools
check_extension 'os-floating-ip-pools'
response = @connection.req('GET', '/os-floating-ip-pools')
JSON.parse(response.body)['floating_ip_pools']
end
#deprecated - please do not use this typo method :)
def get_floating_ip_polls
puts "get_floating_ip_polls() is DEPRECATED: Please use get_floating_ip_pools() without typo!"
self.get_floating_ip_pools
end
def get_floating_ips_bulk
check_extension 'os-floating-ips-bulk'
response = @connection.req('GET', '/os-floating-ips-bulk')
res = JSON.parse(response.body)['floating_ip_info']
res.inject([]){|result, c| result << OpenStack::Compute::FloatingIPInfo.new(c); result}
end
def create_floating_ips_bulk(opts = {})
raise ArgumentError, 'Should not be empty' if opts.empty?
data = JSON.generate({:floating_ips_bulk_create => opts})
check_extension 'os-floating-ips-bulk'
response = @connection.req('POST', '/os-floating-ips-bulk', {:data => data})
JSON.parse(response.body)['floating_ips_bulk_create']
end
# Not working
# Nova does not supported deletion via API
def delete_floating_ips_bulk(opts = {})
raise ArgumentError, 'Should not be empty' if opts.empty?
data = JSON.generate(opts)
check_extension 'os-floating-ips-bulk'
response = @connection.req('POST', '/os-floating-ips-bulk/delete', {:data => data})
JSON.generate(response)
end
# Shows summary statistics for all hypervisors over all compute nodes
def get_hypervisor_stats
response = @connection.req('GET', '/os-hypervisors/statistics')
OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
JSON.parse(response.body)['hypervisor_statistics']
end
private
def check_extension(name)
raise OpenStack::Exception::NotImplemented.new("#{name} not implemented by #{@connection.http.keys.first}", 501, "NOT IMPLEMENTED") unless api_extensions[name.to_sym]
true
end
end
end
end