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

Merge pull request #1168 from timuralp/ninefold

[Ninefold|Storage] Use Atmos in Ninefold storage.
This commit is contained in:
Timur Alperovich 2012-09-24 10:25:57 -07:00
commit 1f925cfc46
10 changed files with 14 additions and 493 deletions

View file

@ -1,48 +0,0 @@
require 'fog/core/collection'
require 'fog/ninefold/models/storage/directory'
module Fog
module Storage
class Ninefold
class Directories < Fog::Collection
attribute :directory
model Fog::Storage::Ninefold::Directory
def all
directory ? ns = directory.key : ns = ''
ns = ns + '/' unless ns =~ /\/$/
data = connection.get_namespace(ns).body[:DirectoryList]
data = {:DirectoryEntry => []} if data.kind_of? String
data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash
dirs = data[:DirectoryEntry].select {|de| de[:FileType] == 'directory'}
dirs.each do |d|
d[:Filename] = ns + d[:Filename] if directory
d[:Filename] += '/' unless d[:Filename] =~ /\/$/
end
load(dirs)
end
def get(key, options = {})
return nil if key == '' # Root dir shouldn't be retrieved like this.
key =~ /\/$/ ? ns = key : ns = key + '/'
res = connection.get_namespace ns
emc_meta = res.headers['x-emc-meta']
obj_id = emc_meta.scan(/objectid=(\w+),/).flatten[0]
new(:objectid => obj_id, :key => ns)
rescue Fog::Storage::Ninefold::NotFound
nil
end
def new(attributes ={})
attributes = {:directory => directory}.merge(attributes) if directory
super(attributes)
end
end
end
end
end

View file

@ -1,53 +0,0 @@
require 'fog/core/model'
module Fog
module Storage
class Ninefold
class Directory < Fog::Model
identity :key, :aliases => :Filename
attribute :objectid, :aliases => :ObjectID
def files
@files ||= begin
Fog::Storage::Ninefold::Files.new(
:directory => self,
:connection => connection
)
end
end
def directories
@directories ||= begin
Fog::Storage::Ninefold::Directories.new(
:directory => self,
:connection => connection
)
end
end
def save
self.key = attributes[:directory].key + key if attributes[:directory]
self.key = key + '/' unless key =~ /\/$/
res = connection.post_namespace key
reload
end
def destroy(opts={})
if opts[:recursive]
files.each {|f| f.destroy }
directories.each do |d|
d.files.each {|f| f.destroy }
d.destroy(opts)
end
end
connection.delete_namespace key
end
end
end
end
end

View file

@ -1,108 +0,0 @@
require 'fog/core/model'
module Fog
module Storage
class Ninefold
class File < Fog::Model
identity :key, :aliases => :Filename
attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer
attribute :content_type, :aliases => ['content_type', 'Content-Type']
attribute :objectid, :aliases => :ObjectID
def body
attributes[:body] ||= if objectid
collection.get(identity).body
else
''
end
end
def body=(new_body)
attributes[:body] = new_body
end
def directory
@directory
end
def copy(target_directory_key, target_file_key, options={})
target_directory = connection.directories.new(:key => target_directory_key)
target_directory.files.create(
:key => target_file_key,
:body => body
)
end
def destroy
requires :directory, :key
connection.delete_namespace([directory.key, key].join('/'))
true
end
# def owner=(new_owner)
# if new_owner
# attributes[:owner] = {
# :display_name => new_owner['DisplayName'],
# :id => new_owner['ID']
# }
# end
# end
def public=(new_public)
# NOOP - we don't need to flag files as public, getting the public URL for a file handles it.
end
# By default, expire in 5 years
def public_url(expires = (Time.now + 5 * 365 * 24 * 60 * 60))
requires :objectid
# TODO - more efficient method to get this?
storage = Fog::Storage.new(:provider => 'Ninefold')
uri = URI::HTTP.build(:scheme => Fog::Storage::Ninefold::STORAGE_SCHEME, :host => Fog::Storage::Ninefold::STORAGE_HOST, :port => Fog::Storage::Ninefold::STORAGE_PORT.to_i, :path => "/rest/objects/#{objectid}" )
Fog::Storage.new(:provider => 'Ninefold').uid
sb = "GET\n"
sb += uri.path.downcase + "\n"
sb += storage.uid + "\n"
sb += String(expires.to_i())
signature = storage.sign( sb )
uri.query = "uid=#{CGI::escape(storage.uid)}&expires=#{expires.to_i()}&signature=#{CGI::escape(signature)}"
uri.to_s
end
def save(options = {})
requires :body, :directory, :key
directory.kind_of?(Directory) ? ns = directory.key : ns = directory
ns += key
options[:headers] ||= {}
options[:headers]['Content-Type'] = content_type if content_type
options[:body] = body
begin
data = connection.post_namespace(ns, options)
self.objectid = data.headers['location'].split('/')[-1]
rescue => error
if error.message =~ /The resource you are trying to create already exists./
data = connection.put_namespace(ns, options)
else
raise error
end
end
# merge_attributes(data.headers)
true
end
private
def directory=(new_directory)
@directory = new_directory
end
end
end
end
end

View file

@ -1,73 +0,0 @@
require 'fog/core/collection'
require 'fog/ninefold/models/storage/file'
module Fog
module Storage
class Ninefold
class Files < Fog::Collection
attribute :directory
attribute :limit
attribute :marker
attribute :path
attribute :prefix
model Fog::Storage::Ninefold::File
def all(options = {})
requires :directory
directory ? ns = directory.key : ns = ''
ns = ns + '/' unless ns =~ /\/$/
data = connection.get_namespace(ns).body[:DirectoryList]
data = {:DirectoryEntry => []} if data.kind_of? String
data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash
files = data[:DirectoryEntry].select {|de| de[:FileType] == 'regular'}
files.each do |s|
s[:directory] = directory
end
# TODO - Load additional file meta?
load(files)
end
def get(key, &block)
requires :directory
data = connection.get_namespace(directory.key + key, :parse => false)#, &block)
file_data = data.headers.merge({
:body => data.body,
:key => key
})
new(file_data)
rescue Fog::Storage::Ninefold::NotFound
nil
end
def get_url(key)
requires :directory
if self.directory.public_url
"#{self.directory.public_url}/#{key}"
end
end
def head(key, options = {})
requires :directory
data = connection.head_namespace(directory.key + key, :parse => false)
file_data = data.headers.merge({
:body => data.body,
:key => key
})
new(file_data)
rescue Fog::Storage::Ninefold::NotFound
nil
end
def new(attributes = {})
requires :directory
super({ :directory => directory }.merge!(attributes))
end
end
end
end
end

View file

@ -1,19 +0,0 @@
module Fog
module Storage
class Ninefold
class Real
def delete_namespace(namespace = '', options = {})
options = options.reject {|key, value| value.nil?}
request({
:expects => 204,
:method => 'DELETE',
:path => "namespace/" + namespace,
:query => options
}.merge(options))
end
end
end
end
end

View file

@ -1,20 +0,0 @@
module Fog
module Storage
class Ninefold
class Real
def get_namespace(namespace = '', options = {})
options = options.reject {|key, value| value.nil?}
request({
:expects => 200,
:method => 'GET',
:path => "namespace/" + namespace,
:query => {},
:parse => true
}.merge(options))
end
end
end
end
end

View file

@ -1,20 +0,0 @@
module Fog
module Storage
class Ninefold
class Real
def head_namespace(namespace = '', options = {})
options = options.reject {|key, value| value.nil?}
request({
:expects => 200,
:method => 'HEAD',
:path => "namespace/" + namespace,
:query => {},
:parse => true
}.merge(options))
end
end
end
end
end

View file

@ -1,20 +0,0 @@
module Fog
module Storage
class Ninefold
class Real
def post_namespace(namespace = '', options = {})
options = options.reject {|key, value| value.nil?}
request({
:expects => 201,
:method => 'POST',
:path => "namespace/" + namespace,
:query => {},
:parse => true
}.merge(options))
end
end
end
end
end

View file

@ -1,20 +0,0 @@
module Fog
module Storage
class Ninefold
class Real
def put_namespace(namespace = '', options = {})
options = options.reject {|key, value| value.nil?}
request({
:expects => 200,
:method => 'PUT',
:path => "namespace/" + namespace,
:query => {},
:parse => true
}.merge(options))
end
end
end
end
end

View file

@ -1,34 +1,28 @@
require 'fog/ninefold'
require 'fog/storage'
require 'fog/atmos'
module Fog
module Storage
class Ninefold < Fog::Service
class Ninefold < Fog::Storage::Atmos
STORAGE_HOST = "onlinestorage.ninefold.com" #"api.ninefold.com"
STORAGE_PATH = "" #"/storage/v1.0"
STORAGE_PORT = "80" # "443"
STORAGE_SCHEME = "http" # "https"
requires :ninefold_storage_token, :ninefold_storage_secret
recognizes :persistent
model_path 'fog/ninefold/models/storage'
model_path 'fog/atmos/models/storage'
model :directory
collection :directories
model :file
collection :files
request_path 'fog/ninefold/requests/storage'
# request :delete_container
request :get_namespace
request :head_namespace
request :post_namespace
request :put_namespace
request :delete_namespace
module Utils
end
class Mock
class Mock < Fog::Storage::Atmos::Mock
include Utils
def initialize(options={})
@ -43,111 +37,19 @@ module Fog
end
class Real
class Real < Fog::Storage::Atmos::Real
include Utils
def initialize(options={})
require 'mime/types'
@ninefold_storage_token = options[:ninefold_storage_token]
@ninefold_storage_secret = options[:ninefold_storage_secret]
@ninefold_storage_secret_decoded = Base64.decode64( @ninefold_storage_secret )
@connection_options = options[:connection_options] || {}
@hmac = Fog::HMAC.new('sha1', @ninefold_storage_secret_decoded)
@persistent = options.fetch(:persistent, true)
@connection = Fog::Connection.new("#{Fog::Storage::Ninefold::STORAGE_SCHEME}://#{Fog::Storage::Ninefold::STORAGE_HOST}:#{Fog::Storage::Ninefold::STORAGE_PORT}", @persistent, @connection_options)
endpoint = "#{STORAGE_SCHEME}://"\
"#{STORAGE_HOST}:"\
"#{STORAGE_PORT}"\
"#{STORAGE_PATH}"
options[:atmos_storage_endpoint] = endpoint
options[:atmos_storage_token] = options[:ninefold_storage_token]
options[:atmos_storage_secret] = options[:ninefold_storage_secret]
super(options)
end
def uid
@ninefold_storage_token#.split('/')[-1]
end
def sign(string)
value = @hmac.sign(string)
Base64.encode64( value ).chomp()
end
def reload
@connection.reset
end
def request(params, &block)
req_path = params[:path]
# Force set host and port
params.merge!({
:host => Fog::Storage::Ninefold::STORAGE_HOST,
:path => "#{Fog::Storage::Ninefold::STORAGE_PATH}/rest/#{params[:path]}",
})
# Set default method and headers
params = {:method => 'GET', :headers => {}}.merge params
params[:headers]["Content-Type"] ||= "application/octet-stream"
# Add request date
params[:headers]["date"] = Time.now().httpdate()
params[:headers]["x-emc-uid"] = @ninefold_storage_token
# Build signature string
signstring = ""
signstring += params[:method]
signstring += "\n"
signstring += params[:headers]["Content-Type"]
signstring += "\n"
if( params[:headers]["range"] )
signstring += params[:headers]["range"]
end
signstring += "\n"
signstring += params[:headers]["date"]
signstring += "\n"
signstring += "/rest/" + URI.unescape( req_path ).downcase
query_str = params[:query].map{|k,v| "#{k}=#{v}"}.join('&')
signstring += '?' + query_str unless query_str.empty?
signstring += "\n"
customheaders = {}
params[:headers].each { |key,value|
case key
when 'x-emc-date', 'x-emc-signature'
#skip
when /^x-emc-/
customheaders[ key.downcase ] = value
end
}
header_arr = customheaders.sort()
header_arr.each { |key,value|
# Values are lowercase and whitespace-normalized
signstring += key + ":" + value.strip.chomp.squeeze( " " ) + "\n"
}
digest = @hmac.sign(signstring.chomp())
signature = Base64.encode64( digest ).chomp()
params[:headers]["x-emc-signature"] = signature
begin
response = @connection.request(params, &block)
rescue Excon::Errors::HTTPStatusError => error
raise case error
when Excon::Errors::NotFound
Fog::Storage::Ninefold::NotFound.slurp(error)
else
error
end
end
unless response.body.empty?
if params[:parse]
document = Fog::ToHashDocument.new
parser = Nokogiri::XML::SAX::PushParser.new(document)
parser << response.body
parser.finish
response.body = document.body
end
end
response
end
end
end
end