前回、PHP FUSEでG-Storageをマウント出来るようにしてみた - yoshida_eth0の日記で作った奴だと、書き込みが出来なかった。
だから今回はPythonでやってみた。
実行するには、easy_installでoauthとtlsliteを入れる必要あり。
インスタンス変数をチェックする所のこの部分、もうちょっとどうにか出来ないかなぁ。
if eval('self.%s' % key) is None:
メソッド内にクラスを定義出来るのに感動して、意味もなくメソッド内に定義してみた。
FUSE PYTHONインストール
FUSEインストール
yum -y install fuse fuse-devel dkms-fuse modprobe fuse yum -y install pkgconfig echo "export PKG_CONFIG_PATH=/usr/lib/pkgconfig" > /etc/profile.d/pkgconfig.sh source /etc/profile.d/pkgconfig.sh
easy_install fuse-python
その他ライブラリ
easy_install oauth easy_install tlslite
GStorageFS.py
ソース
#!/usr/bin/env python import sys, os, time, re, base64, hashlib, random import urllib, urllib2 import errno, stat, fuse from oauth import oauth from tlslite.utils import keyfactory fuse.fuse_python_api = (0, 2) """ debug """ def debug(txt, eol=True): f = open('/tmp/pyfuse.log', 'a') f.write(txt) if eol: f.write("\n") f.close() """ Exception """ class Usage(Exception): def __str__(self): return """Usage: python GStorageFS.py mount_dir parameters : method : Authentication method. (WSSE_a, WSSE_b, OAuth_HMAC_SHA1 or OAuth_RSA_SHA1) username : Username. You should specify this parameter. password : Password. If authentication method is wsse_a, wsse_b or oauth_hmac_sha1, you should specify this parameter. privatekey : Privatekey. If authentication method is oauth_rsa_sha1, you should specify this parameter.""" """ OAuthSignatureMethod """ class OAuthSignatureMethod_RSA_SHA1(oauth.OAuthSignatureMethod): def get_name(self): return 'RSA-SHA1' def fetch_private_cert(self, oauth_request): abstruct def build_signature_base_string(self, oauth_request, consumer, token): sig = ( oauth.escape(oauth_request.get_normalized_http_method()), oauth.escape(oauth_request.get_normalized_http_url()), oauth.escape(oauth_request.get_normalized_parameters()), ) raw = '&'.join(sig) return raw def build_signature(self, oauth_request, consumer, token): raw = self.build_signature_base_string(oauth_request, consumer, token) privatekey = keyfactory.parsePEMKey(self.fetch_private_cert(oauth_request), private=True) signature = privatekey.hashAndSign(raw) return base64.b64encode(signature) """ GStorage IO """ class GStorageIO(object): def __init__(self): self.ssl = False self.host = None self.basepath = '' self.username = None self.password = None self.privatekey = None def request(self, method, path, data=None): scheme = 'http' if self.ssl: scheme = 'https' url = '%s://%s%s%s' % (scheme, self.host, self.basepath, path) params = { 'format' : 'bin', 'method' : method, } if isinstance(data, dict): params.update(data) data = None try: req = self.getRequest(url, params, data) response = urllib2.urlopen(req) except urllib2.HTTPError, e: raise Exception('%s %s' % (e.code, e.read())) except Exception, e: raise e return response def init(self): abstract def getRequest(self, url, params, data): abstract class GStorageIO_WSSE_a(GStorageIO): def init(self): for key in ['host', 'username', 'password']: if eval('self.%s' % key) is None: raise Usage() def getRequest(self, url, params, data): nonce = base64.b64encode(hashlib.sha1(str(time.time() + random.random())).digest()) created = time.strftime('%Y-%m-%dT%H:%M:%SZ') digest = base64.b64encode(hashlib.sha1(nonce + created + self.password).digest()) wsse = 'UsernameToken Username="%(u)s", PasswordDigest="%(p)s", Nonce="%(n)s", Created="%(c)s"' value = dict(u=self.username, p=digest, n=nonce, c=created) wsse = wsse % value url = '%s?%s' % (url, urllib.urlencode(params)) return urllib2.Request(url, data, {'X-WSSE' : wsse, 'Content-Type' : 'application/octet-stream'}) class GStorageIO_WSSE_b(GStorageIO): def init(self): for key in ['host', 'username', 'password']: if eval('self.%s' % key) is None: raise Usage() def getRequest(self, url, params, data): nonce = hashlib.sha1((str(time.time() + random.random())).digest()) created = time.strftime('%Y-%m-%dT%H:%M:%SZ') digest = base64.b64encode(hashlib.sha1(nonce + created + self.password).digest()) wsse = 'UsernameToken Username="%(u)s", PasswordDigest="%(p)s", Nonce="%(n)s", Created="%(c)s"' value = dict(u=self.username, p=digest, n=base64.b64encode(nonce), c=created) wsse = wsse % value url = '%s?%s' % (url, urllib.urlencode(params)) return urllib2.Request(url, data, {'X-WSSE' : wsse, 'Content-Type' : 'application/octet-stream'}) class GStorageIO_OAuth_HMAC_SHA1(GStorageIO): def __init__(self): GStorageIO.__init__(self) self.consumer = None self.signature_method = None def init(self): for key in ['host', 'username', 'password']: if eval('self.%s' % key) is None: raise Usage() self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self.consumer = oauth.OAuthConsumer(self.username, self.password) def getRequest(self, url, params, data): method = 'GET' if data is not None: method = 'POST' request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_method=method, http_url=url, parameters=params) request.sign_request(self.signature_method, self.consumer, None) url = request.to_url() return urllib2.Request(url, data, {'Content-Type' : 'application/octet-stream'}) class GStorageIO_OAuth_RSA_SHA1(GStorageIO_OAuth_HMAC_SHA1): def __init__(self): GStorageIO_OAuth_HMAC_SHA1.__init__(self) def init(self): for key in ['host', 'username', 'privatekey']: if eval('self.%s' % key) is None: raise Usage() class OAuthSignatureMethod_RSA_SHA1_Wrapper(OAuthSignatureMethod_RSA_SHA1): def __init__(self, privatekey): self.privatekey = privatekey def fetch_private_cert(self, oauth_request): return self.privatekey self.signature_method = OAuthSignatureMethod_RSA_SHA1_Wrapper(self.privatekey) self.consumer = oauth.OAuthConsumer(self.username, None) """ FUSE Stat """ class GStorageStat(fuse.Stat): def __init__(self): self.st_mode = 0 self.st_ino = 0 self.st_dev = 0 self.st_nlink = 0 self.st_uid = 0 self.st_gid = 0 self.st_size = 0 self.st_atime = 0 self.st_mtime = 0 self.st_ctime = 0 """ FUSE FS """ class GStorageFS(fuse.Fuse): def __init__(self, io, *args, **kw): fuse.Fuse.__init__(self, *args, **kw) self.__io = io self.__cache = {} self.__buffer = {} def getattr(self, path): debug("getattr %s" % path) try: item = {} if self.__cache.get(path) and time.time()-3.0<self.__cache[path]['time']: item = self.__cache.get(path) elif path!='/': debug(' ', False) for a in self.readdir(re.sub('[^/]+$', '', path), 0): pass if self.__cache.get(path): item = self.__cache.get(path) st = GStorageStat() if path=='/' or item.get('model')=='dir': st.st_mode = stat.S_IFDIR | 0755 st.st_nlink = 2 elif item.get('model')=='file': st.st_mode = stat.S_IFREG | 0644 st.st_nlink = 1 st.st_size = item.get('size', 0) else: return -errno.ENOENT return st except Exception, e: debug(str(e)) return -errno.ENOENT def readdir(self, path, offset): debug("readdir %s %s" % (path, offset)) try: res = self.__io.request('ls', path).read().strip() list = ['.', '..'] # parse #self.__cache = {} for line in res.split('\n'): item = line.split('\t') if len(item)==3: model = item[0].strip() name = item[1] size = int(item[2].strip()) list.append(item[1]) self.__cache[os.path.join(path, name)] = { 'model' : model, 'size' : size, 'time' : time.time(), } # return for item in list: yield fuse.Direntry(item) except Exception, e: debug(str(e)) def open(self, path, flags): debug("open %s %s" % (path, flags)) def read(self, path, size, offset): debug("read %s %s %s" % (path, size, offset)) try: return self.__io.request('cat', path).read() except Exception, e: return -errno.ENOENT def mknod(self, path, mode, dev): debug('mknod %s %s %s' % (path, oct(mode), dev)) self.__cache[path] = { 'model' : 'file', 'time' : time.time(), } return 0 def write(self, path, buf, offset): debug("write %s %s %s" % (path, len(buf), offset)) try: self.__buffer[path] = self.__buffer.get(path, '') + buf return len(buf) except Exception, e: debug(str(e)) return -errno.EIO def release(self, path, flags): debug('release %s %s' % (path, flags)) try: buf = self.__buffer.pop(path, None) if buf is not None: res = self.__io.request('post', path, buf) except Exception, e: debug(str(e)) return -errno.EIO return 0 def truncate(self, path, size): debug('truncate %s %s' % (path, size)) return 0 def mkdir(self, path, mode): debug('mkdir %s %s' % (path, oct(mode))) try: self.__io.request('mkdir', path) except Exception, e: debug(str(e)) return -errno.EIO return 0 def rmdir(self, path): debug('rmdir %s' % path) try: self.__io.request('rm', path) except Exception, e: debug(str(e)) return -errno.EIO return 0 def unlink(self, path): debug('unlink %s' % path) try: self.__io.request('rm', path) except Exception, e: debug(str(e)) return -errno.EIO return 0 def rename(self, from_path, to_path): debug('rename %s %s' % (from_path, to_path)) try: to_path = self.__io.basepath + to_path self.__io.request('mv', from_path, {'to':to_path}) except Exception, e: debug(str(e)) return -errno.EIO return 0 # not implement def getdir(self, path): """ return: [[('file1', 0), ('file2', 0), ... ]] """ debug('*** getdir %s' % path) return -errno.ENOSYS def mythread(self): debug('*** mythread') return -errno.ENOSYS def chmod(self, path, mode): debug('*** chmod %s %s', path, oct(mode)) return -errno.ENOSYS def chown(self, path, uid, gid): debug('*** chown %s %s %s' % (path, uid, gid)) return -errno.ENOSYS def fsync(self, path, isFsyncFile): debug('*** fsync %s %s' % (path, isFsyncFile)) return -errno.ENOSYS def link(self, targetPath, linkPath): debug('*** link %s %s' % (targetPath, linkPath)) return -errno.ENOSYS def readlink(self, path): debug('*** readlink %s' % path) return -errno.ENOSYS def statfs(self): debug('*** statfs') return -errno.ENOSYS def symlink(self, targetPath, linkPath): debug('*** symlink %s %s' % (targetPath, linkPath)) return -errno.ENOSYS def utime(self, path, times): debug('*** utime %s %s' % (path, times)) return -errno.ENOSYS """ Application start """ def main(): try: io = GStorageIO_WSSE_a() #io = GStorageIO_WSSE_b() #io = GStorageIO_OAuth_HMAC_SHA1() #io = GStorageIO_OAuth_RSA_SHA1() io.ssl = False io.host = 'g-storage.appspot.com' io.basepath = '/storage' io.username = 'testuser' io.password = 'testpass' io.privatekey = None io.init() # check io.request('info', '/') # mount fs = GStorageFS(io) fs.parse(errex=1) fs.main() except Exception, e: print str(e) if __name__=='__main__': main()
使い方
# python GStorageFS.py /mnt
参考URL
ここいらでfuseを一区切り - KoshigoeBLOG
http://blog.koshigoe.jp/archives/2007/04/fuse.html
とりあえずFUSE-pythonを使ってみる - 自称すーじー。
http://d.hatena.ne.jp/suu-g/20080604/1212557931
pythonでfuse - lolloo-htnの日記
http://d.hatena.ne.jp/lolloo-htn/20091014/1255540299
fuse-python で遊んだ - zyxwvの日記
http://d.hatena.ne.jp/zyxwv/20090108/1231433753
PythonでRSA-SHA1方式のOAuth認証をするクライアント側のライブラリ
easy_install oauthで入れられるライブラリはRSA-SHA1に対応してなかった。
とりあえずそれを継承してRSA-SHA1に対応させた。
使い方は、OAuthSignatureMethod_RSA_SHA1を継承して、fetch_private_certをオーバーライドするだけ。
import base64 from oauth.oauth import * from tlslite.utils import keyfactory class OAuthSignatureMethod_RSA_SHA1(OAuthSignatureMethod): def get_name(self): return 'RSA-SHA1' def fetch_private_cert(self, oauth_request): abstruct def build_signature_base_string(self, oauth_request, consumer, token): sig = ( escape(oauth_request.get_normalized_http_method()), escape(oauth_request.get_normalized_http_url()), escape(oauth_request.get_normalized_parameters()), ) raw = '&'.join(sig) return raw def build_signature(self, oauth_request, consumer, token): raw = self.build_signature_base_string(oauth_request, consumer, token) privatekey = keyfactory.parsePEMKey(self.fetch_private_cert(oauth_request), private=True) signature = privatekey.hashAndSign(raw) return base64.b64encode(signature)