[Ninefold|Storage] Use Atmos in Ninefold storage.
Currently, code is duplicated between ninefold and atmos. We should remove the code duplication and extend the atmos module in Ninefold.
This commit is contained in:
parent
914c81c670
commit
eb1468c4ff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue