1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge pull request #347 from ninefold/ninefold

Ninefold
This commit is contained in:
Wesley Beary 2011-06-08 11:30:53 -07:00
commit 68101516b7
56 changed files with 1901 additions and 1 deletions

View file

@ -64,6 +64,7 @@ require 'fog/bin/google'
require 'fog/bin/linode'
require 'fog/bin/local'
require 'fog/bin/new_servers'
require 'fog/bin/ninefold'
require 'fog/bin/rackspace'
require 'fog/bin/slicehost'
require 'fog/bin/stormondemand'

30
lib/fog/bin/ninefold.rb Normal file
View file

@ -0,0 +1,30 @@
class Ninefold < Fog::Bin
class << self
def class_for(key)
case key
when :compute
Fog::Ninefold::Compute
else
raise ArgumentError, "Unsupported #{self} service: #{key}"
end
end
def [](service)
@@connections ||= Hash.new do |hash, key|
hash[key] = case key
when :compute
Fog::Compute.new(:provider => 'Ninefold')
else
raise ArgumentError, "Unrecognized service: #{service}"
end
end
@@connections[service]
end
def services
Fog::Ninefold.services
end
end
end

View file

@ -25,6 +25,9 @@ module Fog
when 'NewServers'
require 'fog/compute/new_servers'
Fog::NewServers::Compute.new(attributes)
when 'Ninefold'
require 'fog/compute/ninefold'
Fog::Ninefold::Compute.new(attributes)
when 'Rackspace'
require 'fog/compute/rackspace'
Fog::Rackspace::Compute.new(attributes)

View file

@ -0,0 +1,103 @@
require 'fog/core/model'
module Fog
module Ninefold
class Compute
class Address < Fog::Model
identity :id
attribute :account
attribute :allocated
attribute :associatednetworkid
attribute :domain
attribute :domainid
attribute :forvirtualnetwork
attribute :ipaddress
attribute :issourcenat
attribute :isstaticnat
attribute :jobid
attribute :jobstatus
attribute :networkid
attribute :state
attribute :virtualmachinedisplayname
attribute :virtualmachineid
attribute :virtualmachinename
attribute :vlanid
attribute :vlanname
attribute :zoneid
attribute :zonename
def initialize(attributes={})
super
end
def destroy
requires :identity
self.jobid = extract_job_id(connection.disassociate_ip_address(:id => identity))
true
end
def enable_static_nat(server)
server.kind_of?(Integer) ? serverid = server : serverid = server.identity
res = connection.enable_static_nat(:virtualmachineid => serverid, :ipaddressid => identity)
reload
to_boolean(res['success'])
end
def disable_static_nat()
self.jobid = extract_job_id(connection.disable_static_nat(:ipaddressid => identity))
true
end
def reload
self.virtualmachinedisplayname = nil
self.virtualmachineid = nil
self.virtualmachinename = nil
super
end
def ready?
if jobid && connection.query_async_job_result(:jobid => jobid)['jobstatus'] == 0
false
else # No running job, we are ready. Refresh data.
reload
true
end
end
def save
raise "Operation not supported" if self.identity
requires :zoneid
options = {
:zoneid => zoneid,
:networkid => networkid,
:account => account,
:domainid => domainid
}.delete_if {|k,v| v.nil? || v == "" }
data = connection.associate_ip_address(options)
merge_attributes(data)
true
end
private
def extract_job_id(job)
if job.kind_of? Integer
job
else
job['jobid'] || job['id']
end
end
# needed to hack around API inconsistencies
def to_boolean(val)
val && (val.to_s.match(/(true|t|yes|y|1)$/i) != nil)
end
end
end
end
end

View file

@ -0,0 +1,31 @@
require 'fog/core/collection'
require 'fog/compute/models/ninefold/address'
module Fog
module Ninefold
class Compute
class Addresses < Fog::Collection
model Fog::Ninefold::Compute::Address
def all
data = connection.list_public_ip_addresses
load(data)
end
def get(identifier)
return nil if identifier.nil? || identifier == ""
data = connection.list_public_ip_addresses(:id => identifier)
if data.empty?
nil
else
new(data[0])
end
end
end
end
end
end

View file

@ -0,0 +1,29 @@
require 'fog/core/model'
module Fog
module Ninefold
class Compute
class Flavor < Fog::Model
identity :id
attribute :cpunumber
attribute :cpuspeed
attribute :created, :type => :time
attribute :displaytext
attribute :domain
attribute :domainid
attribute :hosttags
attribute :memory
attribute :name
attribute :offerha
attribute :storagetype
attribute :tags
end
end
end
end

View file

@ -0,0 +1,30 @@
require 'fog/core/collection'
require 'fog/compute/models/ninefold/flavor'
module Fog
module Ninefold
class Compute
class Flavors < Fog::Collection
model Fog::Ninefold::Compute::Flavor
def all
data = connection.list_service_offerings
load(data)
end
def get(identifier)
data = connection.list_service_offerings(:id => identifier)
if data.empty?
nil
else
new(data[0])
end
end
end
end
end
end

View file

@ -0,0 +1,42 @@
require 'fog/core/model'
module Fog
module Ninefold
class Compute
class Image < Fog::Model
identity :id
attribute :account
attribute :accountid
attribute :bootable
attribute :created, :type => :time
attribute :crossZones
attribute :displaytext
attribute :domain
attribute :domainid
attribute :format
attribute :hypervisor
attribute :isextractable
attribute :isfeatured
attribute :ispublic
attribute :isready
attribute :jobid
attribute :jobstatus
attribute :name
attribute :ostypeid
attribute :ostypename
attribute :passwordenabled
attribute :removed
attribute :size
attribute :status
attribute :templatetype
attribute :zoneid
attribute :zonename
end
end
end
end

View file

@ -0,0 +1,30 @@
require 'fog/core/collection'
require 'fog/compute/models/ninefold/image'
module Fog
module Ninefold
class Compute
class Images < Fog::Collection
model Fog::Ninefold::Compute::Image
def all(offering = 'executable')
data = connection.list_templates(:templatefilter => offering)
load(data)
end
def get(identifier, offering = 'executable')
data = connection.list_templates(:templatefilter => offering, :id => identifier)
if data.empty?
nil
else
new(data[0])
end
end
end
end
end
end

View file

@ -0,0 +1,79 @@
require 'fog/core/model'
module Fog
module Ninefold
class Compute
class IpForwardingRule < Fog::Model
identity :id
attribute :protocol
attribute :virtualmachineid
attribute :virtualmachinename
attribute :ipaddressid
attribute :ipaddress
attribute :startport
attribute :endport
attribute :state
attribute :jobid
def initialize(attributes={})
super
end
def destroy
requires :identity
self.jobid = extract_job_id(connection.delete_ip_forwarding_rule(:id => identity))
true
end
def ready?
if jobid && connection.query_async_job_result(:jobid => jobid)['jobstatus'] == 0
false
else # No running job, we are ready. Refresh data.
reload
true
end
end
def address
Ninefold.address.get(ipaddressid)
end
def address=(addr)
self.ipaddressid = addr.identity
end
def save
raise "Operation not supported" if self.identity
requires :ipaddressid
requires :protocol
requires :startport
options = {
:ipaddressid => ipaddressid,
:protocol => protocol,
:startport => startport,
:endport => endport
}.delete_if {|k,v| v.nil? || v == "" }
data = connection.create_ip_forwarding_rule(options)
merge_attributes(data)
true
end
private
def extract_job_id(job)
if job.kind_of? Integer
job
else
job['jobid'] || job['id']
end
end
end
end
end
end

View file

@ -0,0 +1,31 @@
require 'fog/core/collection'
require 'fog/compute/models/ninefold/ip_forwarding_rule'
module Fog
module Ninefold
class Compute
class IpForwardingRules < Fog::Collection
model Fog::Ninefold::Compute::IpForwardingRule
def all
data = connection.list_ip_forwarding_rules
load(data)
end
def get(identifier)
return nil if identifier.nil? || identifier == ""
data = connection.list_ip_forwarding_rules(:id => identifier)
if data.empty?
nil
else
new(data[0])
end
end
end
end
end
end

View file

@ -0,0 +1,180 @@
require 'fog/core/model'
module Fog
module Ninefold
class Compute
class Server < Fog::Model
identity :id
attribute :account
attribute :cpunumber
attribute :cpuspeed
attribute :cpuused
attribute :created, :type => :time
attribute :displayname
attribute :domain
attribute :domainid
attribute :forvirtualnetwork
attribute :group
attribute :groupid
attribute :guestosid
attribute :haenable
attribute :hostid
attribute :hostname
attribute :hypervisor
#attribute :ipaddress
attribute :isodisplaytext
attribute :isoid
attribute :isoname
attribute :jobid
attribute :jobstatus
attribute :memory
attribute :name
attribute :networkkbsread
attribute :networkkbswrite
attribute :password
attribute :passwordenabled
attribute :rootdeviceid
attribute :rootdevicetype
attribute :serviceofferingid
attribute :serviceofferingname
attribute :state
attribute :templatedisplaytext
attribute :templateid
attribute :templatename
attribute :zoneid
attribute :zonename
attribute :nic
attribute :securitygroup
# used for creation only.
attribute :networkids
attribute :diskofferingid
attribute :keypair
attribute :securitygroupids
attribute :size
attribute :userdata
#attribute :account_id, :aliases => "account", :squash => "id"
#attribute :image_id, :aliases => "image", :squash => "id"
#attribute :flavor_id, :aliases => "server_type", :squash => "id"
#attribute :zone_id, :aliases => "zone", :squash => "id"
def initialize(attributes={})
super
end
# This is temporary - we need to model nics.
def ipaddress
nic[0] ? nic[0]['ipaddress'] : nil
end
def reboot
requires :identity
self.jobid = extract_job_id(connection.reboot_virtual_machine(:id => identity))
puts "jobid: " + jobid.to_s
true
end
def start
requires :identity
self.jobid = extract_job_id(connection.start_virtual_machine(:id => identity))
true
end
def stop
requires :identity
self.jobid = extract_job_id(connection.stop_virtual_machine(:id => identity))
true
end
def destroy
requires :identity
self.jobid = extract_job_id(connection.destroy_virtual_machine(:id => identity))
true
end
def flavor
requires :serviceofferingid
connection.flavors.get(serviceofferingid)
end
def image
requires :templateid
connection.images.get(templateid)
end
def ready?
if jobid
# we do this by polling the last job id status.
res = connection.query_async_job_result(:jobid => jobid)
if res['jobstatus'] == 0
false
else
# update with new values.
merge_attributes(res['jobresult']['virtualmachine'])
true
end
else # No running job, we are ready. Refresh data.
reload
true
end
end
def save
raise "Operation not supported" if self.identity
requires :serviceofferingid
requires :templateid
requires :zoneid
unless networkids
# No network specified, use first in this zone.
networks = connection.list_networks(:zoneid => zoneid)
if networks.empty?
raise "No networks. Please create one, or specify a network ID"
else
# use the network with the lowest ID - the safe default
self.networkids = networks.sort {|x,y| x['id'] <=> y['id']}[0]['id']
end
end
options = {
:serviceofferingid => serviceofferingid,
:templateid => templateid,
:name => name,
:zoneid => zoneid,
:networkids => networkids,
:account => account,
:diskofferingid => diskofferingid,
:displayname => displayname,
:domainid => domainid,
:group => group,
:hypervisor => hypervisor,
:keypair => keypair,
:securitygroupids => securitygroupids,
:size => size,
:userdata => userdata
}.delete_if {|k,v| v.nil? || v == "" }
data = connection.deploy_virtual_machine(options)
merge_attributes(data)
true
end
private
def extract_job_id(job)
if job.kind_of? Integer
job
else
job['jobid'] || job['id']
end
end
end
end
end
end

View file

@ -0,0 +1,31 @@
require 'fog/core/collection'
require 'fog/compute/models/ninefold/server'
module Fog
module Ninefold
class Compute
class Servers < Fog::Collection
model Fog::Ninefold::Compute::Server
def all
data = connection.list_virtual_machines
load(data)
end
def get(identifier)
return nil if identifier.nil? || identifier == ""
data = connection.list_virtual_machines(:id => identifier)
if data.empty?
nil
else
new(data[0])
end
end
end
end
end
end

154
lib/fog/compute/ninefold.rb Normal file
View file

@ -0,0 +1,154 @@
module Fog
module Ninefold
class Compute < Fog::Service
API_URL = "http://api.ninefold.com/compute/v1.0/"
requires :ninefold_compute_key, :ninefold_compute_secret
recognizes :provider # remove post deprecation
model_path 'fog/compute/models/ninefold'
model :server
collection :servers
model :flavor
collection :flavors
model :image
collection :images
model :address
collection :addresses
model :ip_forwarding_rule
collection :ip_forwarding_rules
request_path 'fog/compute/requests/ninefold'
# General list-only stuff
request :list_accounts
request :list_events
request :list_service_offerings
request :list_disk_offerings
request :list_capabilities
request :list_hypervisors
request :list_zones
request :list_network_offerings
request :list_resource_limits
# Templates
request :list_templates
# Virtual Machines
request :deploy_virtual_machine
request :destroy_virtual_machine
request :list_virtual_machines
request :reboot_virtual_machine
request :stop_virtual_machine
request :start_virtual_machine
request :change_service_for_virtual_machine
request :reset_password_for_virtual_machine
request :update_virtual_machine
# Jobs
request :list_async_jobs
request :query_async_job_result
# Networks
request :list_networks
# Addresses
request :associate_ip_address
request :list_public_ip_addresses
request :disassociate_ip_address
# NAT
request :enable_static_nat
request :disable_static_nat
request :create_ip_forwarding_rule
request :delete_ip_forwarding_rule
request :list_ip_forwarding_rules
class Mock
def initialize(options)
unless options.delete(:provider)
location = caller.first
warning = "[yellow][WARN] Fog::Ninefold::Compute.new is deprecated, use Fog::Compute.new(:provider => 'Ninefold') instead[/]"
warning << " [light_black](" << location << ")[/] "
Formatador.display_line(warning)
end
require "json"
@api_url = options[:ninefold_api_url] || Fog.credentials[:ninefold_api_url] || API_URL
@ninefold_compute_key = options[:ninefold_compute_key] || Fog.credentials[:ninefold_compute_key]
@ninefold_compute_secret = options[:ninefold_compute_secret] || Fog.credentials[:ninefold_compute_secret]
@connection = Fog::Connection.new(@api_url)
end
def request(options)
raise "Not implemented"
end
end
class Real
def initialize(options)
unless options.delete(:provider)
location = caller.first
warning = "[yellow][WARN] Fog::Ninefold::Compute.new is deprecated, use Fog::Compute.new(:provider => 'Ninefold') instead[/]"
warning << " [light_black](" << location << ")[/] "
Formatador.display_line(warning)
end
require "json"
@api_url = options[:ninefold_api_url] || Fog.credentials[:ninefold_api_url] || API_URL
@ninefold_compute_key = options[:ninefold_compute_key] || Fog.credentials[:ninefold_compute_key]
@ninefold_compute_secret = options[:ninefold_compute_secret] || Fog.credentials[:ninefold_compute_secret]
@connection = Fog::Connection.new(@api_url)
end
def request(command, params, options)
params['response'] = "json"
req = "apiKey=#{@ninefold_compute_key}&command=#{command}&"
# convert params to strings for sort
req += URI.escape(params.sort_by{|k,v| k.to_s }.collect{|e| "#{e[0].to_s}=#{e[1].to_s}"}.join('&'))
encoded_signature = url_escape(encode_signature(req))
options = {
:expects => 200,
:method => 'GET',
:query => "#{req}&signature=#{encoded_signature}"
}.merge(options)
begin
response = @connection.request(options)
end
unless response.body.empty?
# Because the response is some weird xml-json thing, we need to try and mung
# the values out with a prefix, and if there is an empty data entry return an
# empty version of the expected type (if provided)
response = JSON.parse(response.body)
if options.has_key? :response_prefix
keys = options[:response_prefix].split('/')
keys.each do |k|
if response[k]
response = response[k]
elsif options[:response_type]
response = options[:response_type].new
break
else
end
end
response
else
response
end
end
end
private
def url_escape(string)
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end.tr(' ', '+')
end
def encode_signature(data)
Base64.encode64(OpenSSL::HMAC.digest('sha1', @ninefold_compute_secret, URI.encode(data.downcase).gsub('+', '%20'))).chomp
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def associate_ip_address(options = {})
request('associateIpAddress', options, :expects => [200],
:response_prefix => 'associateipaddressresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def change_service_for_virtual_machine(options = {})
request('changeServiceForVirtualMachine', options, :expects => [200],
:response_prefix => 'changeserviceforvirtualmachineresponse/virtualmachine', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def create_ip_forwarding_rule(options = {})
request('createIpForwardingRule', options, :expects => [200],
:response_prefix => 'createipforwardingruleresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def delete_ip_forwarding_rule(options = {})
request('deleteIpForwardingRule', options, :expects => [200],
:response_prefix => 'deleteipforwardingruleresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def deploy_virtual_machine(options = {})
request('deployVirtualMachine', options, :expects => [200],
:response_prefix => 'deployvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def destroy_virtual_machine(options = {})
request('destroyVirtualMachine', options, :expects => [200],
:response_prefix => 'destroyvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def disable_static_nat(options = {})
request('disableStaticNat', options, :expects => [200],
:response_prefix => 'disablestaticnatresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def disassociate_ip_address(options = {})
request('disassociateIpAddress', options, :expects => [200],
:response_prefix => 'disassociateipaddressresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def enable_static_nat(options = {})
request('enableStaticNat', options, :expects => [200],
:response_prefix => 'enablestaticnatresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_accounts(options = {})
request('listAccounts', options, :expects => [200],
:response_prefix => 'listaccountsresponse/account', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Ninefold
class Compute
class Real
def list_async_jobs(options = {})
puts "about to perf request.."
request('listAsyncJobs', options, :expects => [200],
:response_prefix => 'listasyncjobsresponse/asyncjobs', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_capabilities(options = {})
request('listCapabilities', options, :expects => [200],
:response_prefix => 'listcapabilitiesresponse/capability', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_disk_offerings(options = {})
request('listDiskOfferings', options, :expects => [200],
:response_prefix => 'listdiskofferingsresponse/diskoffering', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_events(options = {})
request('listEvents', options, :expects => [200],
:response_prefix => 'listeventsresponse/event', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_hypervisors(options = {})
request('listHypervisors', options, :expects => [200],
:response_prefix => 'listhypervisorsresponse/hypervisor', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_ip_forwarding_rules(options = {})
request('listIpForwardingRules', options, :expects => [200],
:response_prefix => 'listipforwardingrulesresponse/ipforwardingrule', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_network_offerings(options = {})
request('listNetworkOfferings', options, :expects => [200],
:response_prefix => 'listnetworkofferingsresponse/networkoffering', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_networks(options = {})
request('listNetworks', options, :expects => [200],
:response_prefix => 'listnetworksresponse/network', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_public_ip_addresses(options = {})
request('listPublicIpAddresses', options, :expects => [200],
:response_prefix => 'listpublicipaddressesresponse/publicipaddress', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_resource_limits(options = {})
request('listResourceLimits', options, :expects => [200],
:response_prefix => 'listresourcelimitsresponse/resourcelimit', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_service_offerings(options = {})
request('listServiceOfferings', options, :expects => [200],
:response_prefix => 'listserviceofferingsresponse/serviceoffering', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_templates(options = {})
request('listTemplates', options, :expects => [200],
:response_prefix => 'listtemplatesresponse/template', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_virtual_machines(options = {})
request('listVirtualMachines', options, :expects => [200],
:response_prefix => 'listvirtualmachinesresponse/virtualmachine', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def list_zones(options = {})
request('listZones', options, :expects => [200],
:response_prefix => 'listzonesresponse/zone', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def query_async_job_result(options = {})
request('queryAsyncJobResult', options, :expects => [200],
:response_prefix => 'queryasyncjobresultresponse', :response_type => Array)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def reboot_virtual_machine(options = {})
request('rebootVirtualMachine', options, :expects => [200],
:response_prefix => 'rebootvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def reset_password_for_virtual_machine(options = {})
request('resetPasswordForVirtualMachine', options, :expects => [200],
:response_prefix => 'resetpasswordforvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def start_virtual_machine(options = {})
request('startVirtualMachine', options, :expects => [200],
:response_prefix => 'startvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def stop_virtual_machine(options = {})
request('stopVirtualMachine', options, :expects => [200],
:response_prefix => 'stopvirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Ninefold
class Compute
class Real
def update_virtual_machine(options = {})
request('updateVirtualMachine', options, :expects => [200],
:response_prefix => 'updatevirtualmachineresponse', :response_type => Hash)
end
end
end
end
end

View file

@ -17,6 +17,7 @@ require 'fog/providers/google'
require 'fog/providers/linode'
require 'fog/providers/local'
require 'fog/providers/new_servers'
require 'fog/providers/ninefold'
require 'fog/providers/rackspace'
require 'fog/providers/slicehost'
require 'fog/providers/storm_on_demand'

View file

@ -0,0 +1,11 @@
require 'fog/core'
module Fog
module Ninefold
extend Fog::Provider
service(:compute, 'compute/ninefold')
end
end

View file

@ -17,6 +17,9 @@ def compute_providers
},
:mocked => false
},
Ninefold => {
:mocked => false
},
Rackspace => {
:server_attributes => {
:image_id => 49, # image 49 = Ubuntu 10.04 LTS (lucid)

View file

@ -0,0 +1,43 @@
Shindo.tests('Ninefold::Compute | server requests', ['ninefold']) do
tests('success') do
tests("#associate_ip_address()").formats(Ninefold::Compute::Formats::Addresses::ADDRESS) do
pending if Fog.mocking?
job = newaddress = Ninefold[:compute].associate_ip_address(:zoneid => Ninefold::Compute::TestSupport::ZONE_ID)
while Ninefold[:compute].query_async_job_result(:jobid => job['jobid'])['jobstatus'] == 0
sleep 1
end
result = Ninefold[:compute].query_async_job_result(:jobid => job['jobid'])['jobresult']['ipaddress']
@newaddressid = result['id']
Ninefold::Compute::Formats::Addresses::fill_address_data(result)
end
tests("#list_public_ip_addresses()").formats(Ninefold::Compute::Formats::Addresses::ADDRESSES) do
pending if Fog.mocking?
result = Ninefold[:compute].list_public_ip_addresses
Ninefold::Compute::Formats::Addresses::fill_address_data(result)
end
tests("#disassociate_ip_address()").formats(Ninefold::Compute::Formats::Addresses::DISASSOC_ADDRESS) do
pending if Fog.mocking?
job = Ninefold[:compute].disassociate_ip_address(:id => @newaddressid)
while Ninefold[:compute].query_async_job_result(:jobid => job['jobid'])['jobstatus'] == 0
sleep 1
end
job
end
end
tests('failure') do
tests("#associate_ip_address()").raises(Excon::Errors::HTTPStatusError) do
pending if Fog.mocking?
Ninefold[:compute].associate_ip_address
end
end
end

View file

@ -0,0 +1,33 @@
# This will fail until there are jobs in the system.
Shindo.tests('Ninfold::Compute | server requests', ['ninefold']) do
tests('success') do
tests("#list_async_jobs()").formats(Ninefold::Compute::Formats::Jobs::JOBS) do
pending if Fog.mocking?
jobs = Ninefold[:compute].list_async_jobs()
unless jobs[0]
raise "No async jobs in system yet - create a VM through web UI to create"
end
@jobid = jobs[0]['jobid']
jobs
end
tests("#query_async_job_result()").formats(Ninefold::Compute::Formats::Jobs::JOB_QUERY) do
pending if Fog.mocking?
Ninefold[:compute].query_async_job_result(:jobid => @jobid)
end
end
tests('failure') do
#tests("#deploy_virtual_machine()").raises(Excon::Errors::HTTPStatusError) do
# pending if Fog.mocking?
# Ninefold[:compute].deploy_virtual_machine
#end
end
end

View file

@ -0,0 +1,346 @@
class Ninefold
module Compute
module TestSupport
# 1CPU, 1.7GB RAM, 160GB Storage
SERVICE_OFFERING = 67
# alternate for testing -
ALT_SERVICE_OFFERING = 68
# XEN Basic Ubuntu 10.04 Server x64 PV r2.0
TEMPLATE_ID = 421
# Sydney
ZONE_ID = 1
# Max time to wait for job completion (2 mins)
MAXWAIT = 2 * 60
## Waits for a job, returning the completed jobs payload.
## Accepts an integer jobid, or a hash containing a jobid or id.
def wait_for_job(job)
job = job['jobid'] || job['id'] unless job.kind_of? Integer
while Ninefold[:compute].query_async_job_result(:jobid => job)['jobstatus'] == 0
sleep 1
end
Ninefold[:compute].query_async_job_result(:jobid => job)
end
module_function :wait_for_job
end
module Formats
module Lists
SERVICE_OFFERING = {
"id" => Integer,
"name" => String,
"displaytext" => String,
"cpunumber" => Integer,
"cpuspeed" => Integer,
"memory" => Integer,
"created" => String,
"storagetype" => String,
"offerha" => Fog::Boolean,
"domainid" => Integer,
"domain" => String
}
#SERVICE_OFFERINGS = [Ninefold::Compute::Formats::Lists::SERVICE_OFFERING]
ACCOUNTS = [{
"id"=>Integer,
"name"=>String,
"accounttype"=>Integer,
"domainid"=>Integer,
"domain"=>String,
"receivedbytes"=>Integer,
"sentbytes"=>Integer,
"vmlimit"=>String,
"vmtotal"=>Integer,
"vmavailable"=>String,
"iplimit"=>String,
"iptotal"=>Integer,
"ipavailable"=>String,
"volumelimit"=>String,
"volumetotal"=>Integer,
"volumeavailable"=>String,
"snapshotlimit"=>String,
"snapshottotal"=>Integer,
"snapshotavailable"=>String,
"templatelimit"=>String,
"templatetotal"=>Integer,
"templateavailable"=>String,
"vmstopped"=>Integer,
"vmrunning"=>Integer,
"state"=>String,
"user"=> [{
"id"=>Integer,
"username"=>String,
"firstname"=>String,
"lastname"=>String,
"email"=>String,
"created"=>String,
"state"=>String,
"account"=>String,
"accounttype"=>Integer,
"domainid"=>Integer,
"domain"=>String,
"apikey"=>String,
"secretkey"=>String
}]
}]
EVENTS = [{
"id"=>Integer,
"username"=>String,
"type"=>String,
"level"=>String,
"description"=>String,
"account"=>String,
"domainid"=>Integer,
"domain"=>String,
"created"=>String,
"state"=>String,
"parentid"=>Integer
}]
SERVICE_OFFERINGS = [{
"id"=>Integer,
"name"=>String,
"displaytext"=>String,
"cpunumber"=>Integer,
"cpuspeed"=>Integer,
"memory"=>Integer,
"created"=>String,
"storagetype"=>String,
"offerha"=>Fog::Boolean,
"domainid"=>Integer,
"domain"=>String
}]
DISK_OFFERINGS = [{
"id"=>Integer,
"domainid"=>Integer,
"domain"=>String,
"name"=>String,
"displaytext"=>String,
"disksize"=>Integer,
"created"=>String,
"iscustomized"=>Fog::Boolean,
"tags"=>String
}]
CAPABILITIES = {
"securitygroupsenabled" => Fog::Boolean,
"cloudstackversion" => String,
"userpublictemplateenabled" => Fog::Boolean
}
HYPERVISORS = [{
"name"=>String
}]
ZONES = [{
"id"=>Integer,
"name"=>String,
"networktype"=>String,
"securitygroupsenabled"=>Fog::Boolean
}]
NETWORK_OFFERINGS = [{
"id"=>Integer,
"name"=>String,
"displaytext"=>String,
"traffictype"=>String,
"isdefault"=>Fog::Boolean,
"specifyvlan"=>Fog::Boolean,
"availability"=>String,
"guestiptype"=>String,
"networkrate"=>Integer
}]
RESOURCE_LIMITS = [{
"account"=>String,
"domainid"=>Integer,
"domain"=>String,
"resourcetype"=>String,
"max"=>Integer
}]
end
module VirtualMachines
# Sometimes a few fields are missing from VM data - this method
# will fill them in if they don't exist, to ensure the format passes
def fill_virtual_machine_data(vms)
if vms.kind_of? Hash
vms['cpuused'] ||= ''
vms['networkkbsread'] ||= 0
vms['networkkbswrite'] ||= 0
elsif vms.kind_of? Array
vms.each {|vm| fill_virtual_machine_data(vm) }
end
vms
end
module_function :fill_virtual_machine_data
VIRTUAL_MACHINE = {
"id"=>Integer,
"name"=>String,
"displayname"=>String,
"account"=>String,
"domainid"=>Integer,
"domain"=>String,
"created"=>String,
"state"=>String,
"haenable"=>Fog::Boolean,
"zoneid"=>Integer,
"zonename"=>String,
"templateid"=>Integer,
"templatename"=>String,
"templatedisplaytext"=>String,
"passwordenabled"=>Fog::Boolean,
"serviceofferingid"=>Integer,
"serviceofferingname"=>String,
"cpunumber"=>Integer,
"cpuspeed"=>Integer,
"memory"=>Integer,
"guestosid"=>Integer,
"rootdeviceid"=>Integer,
"rootdevicetype"=>String,
"securitygroup"=>Array,
"nic"=>[{
"id"=>Integer,
"networkid"=>Integer,
"netmask"=>String,
"gateway"=>String,
"ipaddress"=>String,
"traffictype"=>String,
"type"=>String,
"isdefault"=>Fog::Boolean,
}],
"hypervisor"=>String,
"cpuused"=>String,
"networkkbsread"=>Integer,
"networkkbswrite"=>Integer
}
VIRTUAL_MACHINES = [VIRTUAL_MACHINE]
end
module Templates
TEMPLATES = [{
"id"=>Integer,
"name"=>String,
"displaytext"=>String,
"ispublic"=>Fog::Boolean,
"created"=>String,
"isready"=>Fog::Boolean,
"passwordenabled"=>Fog::Boolean,
"format"=>String,
"isfeatured"=>Fog::Boolean,
"crossZones"=>Fog::Boolean,
"ostypeid"=>Integer,
"ostypename"=>String,
"account"=>String,
"zoneid"=>Integer,
"zonename"=>String,
"size"=>Integer,
"templatetype"=>String,
"hypervisor"=>String,
"domain"=>String,
"domainid"=>Integer,
"isextractable"=>Fog::Boolean,
}]
end
module Jobs
JOB = {
"jobid"=>Integer,
"accountid"=>Integer,
"userid"=>Integer,
"cmd"=>String,
"jobstatus"=>Integer,
"jobprocstatus"=>Integer,
"jobresultcode"=>Integer,
"jobresult"=>Hash,
"created"=>String
}
JOBS = [JOB]
JOB_QUERY = {
"jobid"=>Integer,
"jobstatus"=>Integer,
"jobprocstatus"=>Integer,
"jobresultcode"=>Integer,
"jobresulttype"=>String,
"jobresult"=>Hash
}
end
module Networks
NETWORKS=[{"id"=>Integer,
"name"=>String,
"displaytext"=>String,
"broadcastdomaintype"=>String,
"traffictype"=>String,
"zoneid"=>Integer,
"networkofferingid"=>Integer,
"networkofferingname"=>String,
"networkofferingdisplaytext"=>String,
"networkofferingavailability"=>String,
"isshared"=>Fog::Boolean,
"issystem"=>Fog::Boolean,
"state"=>String,
"related"=>Integer,
"broadcasturi"=>String,
"dns1"=>String,
"dns2"=>String,
"type"=>String,
"account"=>String,
"domainid"=>Integer,
"domain"=>String,
"isdefault"=>Fog::Boolean,
"service"=>Array,
"networkdomain"=>String,
"securitygroupenabled"=>Fog::Boolean
}]
end
module Addresses
def fill_address_data(data)
if data.kind_of? Hash
data['virtualmachineid'] ||= 0
data['virtualmachinename'] ||= ''
elsif data.kind_of? Array
data.each {|d| fill_address_data(d) }
end
data
end
module_function :fill_address_data
ADDRESS = {
"id"=>Integer,
"ipaddress"=>String,
"allocated"=>String,
"zoneid"=>Integer,
"zonename"=>String,
"issourcenat"=>Fog::Boolean,
"account"=>String,
"domainid"=>Integer,
"domain"=>String,
"forvirtualnetwork"=>Fog::Boolean,
"isstaticnat"=>Fog::Boolean,
"associatednetworkid"=>Integer,
"networkid"=>Integer,
"state"=>String,
"virtualmachineid"=>Integer,
"virtualmachinename"=>String
}
ADDRESSES = [ADDRESS]
DISASSOC_ADDRESS = {"jobid"=>Integer}
end
module Nat
ENABLE_NAT_RESPONSE = {
'success' => String
}
DISABLE_NAT_RESPONSE = {
'success' => Fog::Boolean
}
DELETE_RULE_RESPONSE = {
'success' => Fog::Boolean
}
FORWARDING_RULE = {
"id"=>Integer,
"protocol"=>String,
"virtualmachineid"=>Integer,
"virtualmachinename"=>String,
"ipaddressid"=>Integer,
"ipaddress"=>String,
"startport"=>Integer,
"endport"=>Integer,
"state"=>String
}
FORWARDING_RULES = [FORWARDING_RULE]
end
end
end
end

View file

@ -0,0 +1,56 @@
Shindo.tests('Ninfold::Compute | list only requests', ['ninefold']) do
tests('success') do
tests("#list_accounts()").formats(Ninefold::Compute::Formats::Lists::ACCOUNTS) do
pending if Fog.mocking?
Ninefold[:compute].list_accounts
end
tests("#list_events()").formats(Ninefold::Compute::Formats::Lists::EVENTS) do
pending if Fog.mocking?
Ninefold[:compute].list_events
end
tests("#list_service_offerings()").formats(Ninefold::Compute::Formats::Lists::SERVICE_OFFERINGS) do
pending if Fog.mocking?
Ninefold[:compute].list_service_offerings
end
tests("#list_disk_offerings()").formats(Ninefold::Compute::Formats::Lists::DISK_OFFERINGS) do
pending if Fog.mocking?
Ninefold[:compute].list_disk_offerings
end
tests("#list_capabilities()").formats(Ninefold::Compute::Formats::Lists::CAPABILITIES) do
pending if Fog.mocking?
Ninefold[:compute].list_capabilities
end
tests("#list_hypervisors()").formats(Ninefold::Compute::Formats::Lists::HYPERVISORS) do
pending if Fog.mocking?
Ninefold[:compute].list_hypervisors
end
tests("#list_zones()").formats(Ninefold::Compute::Formats::Lists::ZONES) do
pending if Fog.mocking?
Ninefold[:compute].list_zones
end
tests("#list_network_offerings()").formats(Ninefold::Compute::Formats::Lists::NETWORK_OFFERINGS) do
pending if Fog.mocking?
Ninefold[:compute].list_network_offerings
end
tests("#list_resource_limits()").formats(Ninefold::Compute::Formats::Lists::RESOURCE_LIMITS) do
pending if Fog.mocking?
Ninefold[:compute].list_resource_limits
end
end
tests('failure') do
end
end

View file

@ -0,0 +1,92 @@
## Note:
# If needed, these tests will create a new VM and public IP address. Because this is expensive, you
# can optionally specify VM_ID and IP_ID as environment variables, and we will use those. Note:
# The IP must not already have static nat enabled or any port mappings.
Shindo.tests('Ninefold::Compute | server requests', ['ninefold']) do
if ENV['VM_ID'] && ENV['IP_ID']
@ipid, @vmid = ENV['IP_ID'], ENV['VM_ID']
else
begin
# Create a VM to work with
networks = Ninefold[:compute].list_networks
vm_job = Ninefold[:compute].deploy_virtual_machine(:serviceofferingid => Ninefold::Compute::TestSupport::SERVICE_OFFERING,
:templateid => Ninefold::Compute::TestSupport::TEMPLATE_ID,
:zoneid => Ninefold::Compute::TestSupport::ZONE_ID,
:networkids => networks[0]['id'])
@vm = Ninefold::Compute::TestSupport.wait_for_job(vm_job)['jobresult']['virtualmachine']
@vmid = @vm['id']
# Allocate a public IP to work with
ip_job = Ninefold[:compute].associate_ip_address(:zoneid => Ninefold::Compute::TestSupport::ZONE_ID)
@ip = Ninefold::Compute::TestSupport.wait_for_job(ip_job)['jobresult']['ipaddress']
@ipid = @ip['id']
rescue => e
puts "*** CREATING VM OR IP FAILED - PLEASE TEST AND CORRECT THIS FIRST"
raise e
end
end
tests('success') do
tests("#enable_static_nat()").formats(Ninefold::Compute::Formats::Nat::ENABLE_NAT_RESPONSE) do
pending if Fog.mocking?
Ninefold[:compute].enable_static_nat(:ipaddressid => @ipid, :virtualmachineid => @vmid)
end
tests("#create_ip_forwarding_rule()").formats(Ninefold::Compute::Formats::Nat::FORWARDING_RULE) do
pending if Fog.mocking?
job = Ninefold[:compute].create_ip_forwarding_rule(:ipaddressid => @ipid,
:protocol => 'TCP',
:startport => 22)
result = Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']['ipforwardingrule']
@fwd_rule_id = result['id']
result
end
tests("#list_ip_forwarding_rules()").formats(Ninefold::Compute::Formats::Nat::FORWARDING_RULES) do
pending if Fog.mocking?
Ninefold[:compute].list_ip_forwarding_rules
end
tests("#delete_ip_forwarding_rule()").formats(Ninefold::Compute::Formats::Nat::DELETE_RULE_RESPONSE) do
pending if Fog.mocking?
job = Ninefold[:compute].delete_ip_forwarding_rule(:id => @fwd_rule_id)
Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']
end
tests("#disable_static_nat()").formats(Ninefold::Compute::Formats::Nat::DISABLE_NAT_RESPONSE) do
pending if Fog.mocking?
job = Ninefold[:compute].disable_static_nat(:ipaddressid => @ipid)
Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']
end
end
tests('failure') do
tests("#associate_ip_address()").raises(Excon::Errors::HTTPStatusError) do
pending if Fog.mocking?
Ninefold[:compute].associate_ip_address
end
end
unless ENV['VM_ID'] && ENV['IP_ID']
begin
# Kill test VM
vm_job = Ninefold[:compute].destroy_virtual_machine(:id => @vmid)
Ninefold::Compute::TestSupport.wait_for_job(vm_job)
# Disassociate public IP
ip_job = Ninefold[:compute].disassociate_ip_address(:id => @ipid)
Ninefold::Compute::TestSupport.wait_for_job(ip_job)
rescue => e
puts "*** DESTROYING VM OR IP FAILED - PLEASE TEST AND CORRECT THIS FIRST"
raise e
end
end
end

View file

@ -0,0 +1,21 @@
Shindo.tests('Ninfold::Compute | network requests', ['ninefold']) do
tests('success') do
tests("#list_networks()").formats(Ninefold::Compute::Formats::Networks::NETWORKS) do
pending if Fog.mocking?
Ninefold[:compute].list_networks()
end
end
tests('failure') do
#tests("#deploy_virtual_machine()").raises(Excon::Errors::HTTPStatusError) do
# pending if Fog.mocking?
# Ninefold[:compute].deploy_virtual_machine
#end
end
end

View file

@ -0,0 +1,21 @@
Shindo.tests('Ninfold::Compute | server requests', ['ninefold']) do
tests('success') do
tests("#list_templates()").formats(Ninefold::Compute::Formats::Templates::TEMPLATES) do
pending if Fog.mocking?
Ninefold[:compute].list_templates(:templatefilter => 'executable')
end
end
tests('failure') do
#tests("#deploy_virtual_machine()").raises(Excon::Errors::HTTPStatusError) do
# pending if Fog.mocking?
# Ninefold[:compute].deploy_virtual_machine
#end
end
end

View file

@ -0,0 +1,78 @@
Shindo.tests('Ninefold::Compute | server requests', ['ninefold']) do
tests('success') do
tests("#deploy_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
networks = Ninefold[:compute].list_networks
unless networks[0]
raise "No networks, ensure a network has been created by deploying a VM from the web UI and verify list_networks test"
end
newvm = Ninefold[:compute].deploy_virtual_machine(:serviceofferingid => Ninefold::Compute::TestSupport::SERVICE_OFFERING,
:templateid => Ninefold::Compute::TestSupport::TEMPLATE_ID,
:zoneid => Ninefold::Compute::TestSupport::ZONE_ID,
:networkids => networks[0]['id'])
# wait for deployment, stash the job id.
@newvmid = newvm['id']
result = Ninefold::Compute::TestSupport.wait_for_job(newvm['jobid'])['jobresult']['virtualmachine']
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(result)
end
tests("#list_virtual_machines()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINES) do
pending if Fog.mocking?
vms = Ninefold[:compute].list_virtual_machines
# This is a hack to work around the changing format - these fields may or may not exist.
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(vms)
end
tests("#reboot_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
job = Ninefold[:compute].reboot_virtual_machine(:id => @newvmid)
result = Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']['virtualmachine']
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(result)
end
tests("#stop_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
job = Ninefold[:compute].stop_virtual_machine(:id => @newvmid)
result = Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']['virtualmachine']
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(result)
end
tests("#change_service_for_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
vms = Ninefold[:compute].change_service_for_virtual_machine(:id => @newvmid,
:serviceofferingid => Ninefold::Compute::TestSupport::ALT_SERVICE_OFFERING)
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(vms)
end
tests("#start_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
job = Ninefold[:compute].start_virtual_machine(:id => @newvmid)
result = Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']['virtualmachine']
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(result)
end
tests("#destroy_virtual_machine()").formats(Ninefold::Compute::Formats::VirtualMachines::VIRTUAL_MACHINE) do
pending if Fog.mocking?
job = Ninefold[:compute].destroy_virtual_machine(:id => @newvmid)
result = Ninefold::Compute::TestSupport.wait_for_job(job)['jobresult']['virtualmachine']
Ninefold::Compute::Formats::VirtualMachines::fill_virtual_machine_data(result)
end
end
tests('failure') do
tests("#deploy_virtual_machine()").raises(Excon::Errors::HTTPStatusError) do
pending if Fog.mocking?
Ninefold[:compute].deploy_virtual_machine
end
end
end

View file

@ -8,7 +8,7 @@ def lorem_file
end
# check to see which credentials are available and add others to the skipped tags list
all_providers = ['aws', 'bluebox', 'brightbox', 'dnsimple', 'dnsmadeeasy', 'ecloud', 'gogrid', 'google', 'linode', 'local', 'newservers', 'rackspace', 'slicehost', 'stormondemand', 'voxel', 'zerigo']
all_providers = ['aws', 'bluebox', 'brightbox', 'dnsimple', 'dnsmadeeasy', 'ecloud', 'gogrid', 'google', 'linode', 'local', 'ninefold', 'newservers', 'rackspace', 'slicehost', 'stormondemand', 'voxel', 'zerigo']
available_providers = Fog.available_providers.map {|provider| provider.downcase}
for provider in (all_providers - available_providers)
Formatador.display_line("[yellow]Skipping tests for [bold]#{provider}[/] [yellow]due to lacking credentials (add some to '~/.fog' to run them)[/]")