2013-12-12 12:16:38 -05:00
require 'fog/joyent'
require 'fog/joyent/errors'
2014-01-02 16:14:40 -05:00
require 'thread'
2013-12-12 12:16:38 -05:00
module Fog
module Joyent
class Analytics < Fog :: Service
requires :joyent_username
recognizes :joyent_password
recognizes :joyent_url
recognizes :joyent_keyname
recognizes :joyent_keyfile
recognizes :joyent_keydata
recognizes :joyent_keyphrase
recognizes :joyent_version
request_path 'fog/joyent/requests/analytics'
request :describe_analytics
2013-12-12 14:13:13 -05:00
request :list_instrumentations
2013-12-12 14:44:51 -05:00
request :get_instrumentation
request :create_instrumentation
2014-01-03 22:18:12 -05:00
request :delete_instrumentation
2013-12-17 13:54:25 -05:00
request :get_instrumentation_value
2013-12-12 12:16:38 -05:00
model_path 'fog/joyent/models/analytics'
collection :joyent_modules
model :joyent_module
2013-12-12 13:20:33 -05:00
collection :metrics
model :metric
2013-12-12 13:25:31 -05:00
collection :fields
model :field
2013-12-12 13:29:03 -05:00
collection :types
model :type
2013-12-12 13:32:56 -05:00
collection :transformations
model :transformation
2013-12-12 14:13:13 -05:00
collection :instrumentations
model :instrumentation
2013-12-17 13:54:25 -05:00
model :value
2013-12-12 12:16:38 -05:00
class Mock
def self . data
@data || = Hash . new do | hash , key |
2014-01-03 14:25:59 -05:00
hash [ key ] = case key
when :instrumentation
{ 'module' = > " cpu " ,
'stat' = > " usage " ,
'predicate' = > { } ,
'decomposition' = > [ " zonename " ] ,
'value-dimension' = > 2 ,
'value-arity' = > " discrete-decomposition " ,
'enabled' = > true ,
'retention-time' = > 86400 ,
'idle-max' = > 86400 ,
'transformations' = > [ ] ,
'nsources' = > 3 ,
'granularity' = > 30 ,
'persist-data' = > false ,
'crtime' = > 1388690982000 ,
'value-scope' = > " point " ,
'id' = > " 63 " ,
'uris' = >
[ { " uri " = > " / #{ @joyent_username } /analytics/instrumentations/63/value/raw " ,
" name " = > " value_raw " } ] }
2014-01-03 15:41:02 -05:00
when :values
{
2014-01-03 22:18:12 -05:00
'value' = > { 'zoneid' = > 0 } ,
'transformations' = > { } ,
'start_time' = > Time . now . utc - 600 ,
'duration' = > 30 ,
'end_time' = > Time . now . utc - 570 ,
'nsources' = > 1 ,
'minreporting' = > 1 ,
'requested_start_time' = > Time . now . utc - 600 ,
'requested_duration' = > 30 ,
'requested_end_time' = > Time . now . utc - 570
2014-01-03 15:41:02 -05:00
}
2014-01-03 21:34:17 -05:00
when :describe_analytics
2014-01-03 21:21:39 -05:00
{
'fields' = > {
'zonename' = > {
'label' = > 'zone name' ,
'type' = > 'string'
} ,
'pid' = > {
'label' = > 'process identifier' ,
'type' = > 'string'
}
2014-01-03 21:34:17 -05:00
} ,
'modules' = > {
'cpu' = > {
'label' = > 'CPU'
}
2014-01-03 22:18:12 -05:00
} ,
'transformations' = > {
'geolocate' = > {
'label' = > 'geolocate IP addresses' ,
'fields' = > [ 'raddr' ] }
} ,
'metrics' = > [ {
" module " = > " cpu " ,
" stat " = > " thread_executions " ,
" label " = > " thread executions " ,
" interval " = > " interval " ,
" fields " = > [ " hostname " , " zonename " , " runtime " ] ,
" unit " = > " operations "
} ] ,
'types' = > {
'string' = > {
'arity' = > " discrete " ,
'unit' = > " "
}
2014-01-03 21:21:39 -05:00
}
}
2014-01-03 14:25:59 -05:00
else
{ }
end
2013-12-12 12:16:38 -05:00
end
end
def data
self . class . data
end
def initialize ( options = { } )
2014-03-25 11:09:49 -04:00
@joyent_username = options [ :joyent_username ]
@joyent_password = options [ :joyent_password ]
2014-01-03 14:25:59 -05:00
@joyent_url = 'https://us-sw-1.api.joyentcloud.com'
@joyent_version = '~7'
2013-12-12 12:16:38 -05:00
end
def request ( opts )
raise " Not Implemented "
end
end # Mock
class Real
def initialize ( options = { } )
2014-01-02 16:18:20 -05:00
@mutex = Mutex . new
2013-12-12 12:16:38 -05:00
@connection_options = options [ :connection_options ] || { }
@persistent = options [ :persistent ] || false
@joyent_url = options [ :joyent_url ] || 'https://us-sw-1.api.joyentcloud.com'
@joyent_version = options [ :joyent_version ] || '~7'
@joyent_username = options [ :joyent_username ]
unless @joyent_username
raise ArgumentError , " options[:joyent_username] required "
end
if options [ :joyent_keyname ]
@joyent_keyname = options [ :joyent_keyname ]
@joyent_keyphrase = options [ :joyent_keyphrase ]
@key_manager = Net :: SSH :: Authentication :: KeyManager . new ( nil , {
:keys_only = > true ,
:passphrase = > @joyent_keyphrase
} )
@header_method = method ( :header_for_signature_auth )
if options [ :joyent_keyfile ]
if File . exists? ( options [ :joyent_keyfile ] )
@joyent_keyfile = options [ :joyent_keyfile ]
@key_manager . add ( @joyent_keyfile )
else
raise ArgumentError , " options[:joyent_keyfile] provided does not exist. "
end
elsif options [ :joyent_keydata ]
if options [ :joyent_keydata ] . to_s . empty?
raise ArgumentError , 'options[:joyent_keydata] must not be blank'
else
@joyent_keydata = options [ :joyent_keydata ]
@key_manager . add_key_data ( @joyent_keydata )
end
end
elsif options [ :joyent_password ]
@joyent_password = options [ :joyent_password ]
@header_method = method ( :header_for_basic_auth )
else
raise ArgumentError , " Must provide either a joyent_password or joyent_keyname and joyent_keyfile pair "
end
2014-02-26 19:50:35 -05:00
@connection = Fog :: XML :: Connection . new (
2013-12-12 12:16:38 -05:00
@joyent_url ,
@persistent ,
@connection_options
)
end
def request ( opts = { } )
opts [ :headers ] = {
" X-Api-Version " = > @joyent_version ,
" Content-Type " = > " application/json " ,
" Accept " = > " application/json "
} . merge ( opts [ :headers ] || { } ) . merge ( @header_method . call )
if opts [ :body ]
opts [ :body ] = Fog :: JSON . encode ( opts [ :body ] )
end
response = @connection . request ( opts )
if response . headers [ " Content-Type " ] == " application/json "
response . body = json_decode ( response . body )
end
response
rescue Excon :: Errors :: HTTPStatusError = > e
2013-12-12 14:44:51 -05:00
if e . response . headers [ " Content-Type " ] == " application/json "
e . response . body = json_decode ( e . response . body )
end
2013-12-12 12:16:38 -05:00
raise_if_error! ( e . request , e . response )
end
private
def json_decode ( body )
parsed = Fog :: JSON . decode ( body )
decode_time_attrs ( parsed )
end
def header_for_basic_auth
{
" Authorization " = > " Basic #{ Base64 . encode64 ( " #{ @joyent_username } : #{ @joyent_password } " ) . delete ( " \r \n " ) } "
}
end
def header_for_signature_auth
date = Time . now . utc . httpdate
# Force KeyManager to load the key(s)
2014-01-02 16:14:40 -05:00
@mutex . synchronize do
@key_manager . each_identity { }
end
2014-01-02 16:18:20 -05:00
2013-12-12 12:16:38 -05:00
key = @key_manager . known_identities . keys . first
sig = if key . kind_of? OpenSSL :: PKey :: RSA
@key_manager . sign ( key , date ) [ 15 .. - 1 ]
else
key = OpenSSL :: PKey :: DSA . new ( File . read ( @joyent_keyfile ) , @joyent_keyphrase )
key . sign ( 'sha1' , date )
end
key_id = " / #{ @joyent_username } /keys/ #{ @joyent_keyname } "
key_type = key . class . to_s . split ( '::' ) . last . downcase . to_sym
unless [ :rsa , :dsa ] . include? key_type
raise Joyent :: Errors :: Unauthorized . new ( 'Invalid key type -- only rsa or dsa key is supported' )
end
signature = Base64 . encode64 ( sig ) . delete ( " \r \n " )
{
" Date " = > date ,
" Authorization " = > " Signature keyId= \" #{ key_id } \" ,algorithm= \" #{ key_type } -sha1 \" #{ signature } "
}
rescue Net :: SSH :: Authentication :: KeyManagerError = > e
raise Joyent :: Errors :: Unauthorized . new ( 'SSH Signing Error: :#{e.message}' , e )
end
def decode_time_attrs ( obj )
if obj . kind_of? ( Hash )
obj [ " created " ] = Time . parse ( obj [ " created " ] ) unless obj [ " created " ] . nil? or obj [ " created " ] == ''
obj [ " updated " ] = Time . parse ( obj [ " updated " ] ) unless obj [ " updated " ] . nil? or obj [ " updated " ] == ''
elsif obj . kind_of? ( Array )
obj . map do | o |
decode_time_attrs ( o )
end
end
obj
end
def raise_if_error! ( request , response )
case response . status
when 401 then
raise Fog :: Compute :: Joyent :: Errors :: Unauthorized . new ( 'Invalid credentials were used' , request , response )
when 403 then
raise Fog :: Compute :: Joyent :: Errors :: Forbidden . new ( 'No permissions to the specified resource' , request , response )
when 404 then
raise Fog :: Compute :: Joyent :: Errors :: NotFound . new ( 'Requested resource was not found' , request , response )
when 405 then
raise Fog :: Compute :: Joyent :: Errors :: MethodNotAllowed . new ( 'Method not supported for the given resource' , request , response )
when 406 then
raise Fog :: Compute :: Joyent :: Errors :: NotAcceptable . new ( 'Try sending a different Accept header' , request , response )
when 409 then
raise Fog :: Compute :: Joyent :: Errors :: Conflict . new ( 'Most likely invalid or missing parameters' , request , response )
when 414 then
raise Fog :: Compute :: Joyent :: Errors :: RequestEntityTooLarge . new ( 'You sent too much data' , request , response )
when 415 then
raise Fog :: Compute :: Joyent :: Errors :: UnsupportedMediaType . new ( 'You encoded your request in a format we don\'t understand' , request , response )
when 420 then
raise Fog :: Compute :: Joyent :: Errors :: PolicyNotForfilled . new ( 'You are sending too many requests' , request , response )
when 449 then
raise Fog :: Compute :: Joyent :: Errors :: RetryWith . new ( 'Invalid API Version requested; try with a different API Version' , request , response )
when 503 then
raise Fog :: Compute :: Joyent :: Errors :: ServiceUnavailable . new ( 'Either there\'s no capacity in this datacenter, or we\'re in a maintenance window' , request , response )
end
end
end # Real
end
end
end