Edited at

ConohaちゃんのオブジェクトストレージのデータをPythonで出し挿れしたメモ

このはちゃん可愛い!!


ディスク拡張とオブジェクトストレージ

ConohaのVPSプランは色々あるんですが、どのプランでもディスク容量は50Gです。遊び用として使うには十分な容量なんですが、画像とか動画を配信したいな~って思うと全く足りませんよね。ConohaスケーラブルなVPSなので、あとから簡単に拡張出来ます。早速SSDを拡張しましょう

プラン
月額
1時間あたり

200G
2,500円
3.5円

500G
4,500円
6.3円

面倒くさいことが嫌いな方、上記の値段と容量に満足できる方、お金で解決することを厭わない方は素直にディスク拡張しておきましょう。単純にです。

さて、オブジェクトストレージのプランを見てみましょう...

プラン
月額
1時間あたり

100GB毎
450円
0.7円 


安い!

この値段と容量にあっさり食いついた私でしたが、めっちゃ大変だったので備忘録を残しておきます...w


オブジェクトストレージってなんなの?


早見表

名前
アクセス方法
お値段
難易度
FTP転送

ディスク拡張
ローカルアクセス
高い
簡単

オブジェクトストレージ
REST_API(OpenStack準拠)
安い
難しい
△(要専用ソフト)

AWS触ったことがある人なら、バケットと言えば分かると思います。

簡単に言ってしまうと凄く高機能なクラウドストレージですね。何をするにもAPIを使ってアクセスするので、言語、環境、場所を問わずにデータアクセスが出来ます。

正直SSDの拡張とは何から何まで全く違うので、無理っぽいな...って思ったら辞めておいたほうが良いです。以後はそれでもConohaちゃんのオブジェクトストレージとダンスしたい紳士のための内容になっています。


前提条件



  • Conohaオブジェクトストレージを契約していること


今回使う言語とかツールとか環境とか


  • CentOS7

  • python3.7

  • python-swiftclient


やること


  1. マニュアルを見ながらやろう

  2. python-swiftclientのインストール


    • pip install python-swiftclient

    • 環境変数の設定



  3. pythonでオブジェクトストレージを操作する


    • アクセストークンの取得

    • metadataの追加、削除


      • X-Container-Meta-Access-Control-Allow-Origin



    • コンテナの取得

    • オブジェクトの取得




マニュアルを見ながらやろう

conohaはドキュメントが少ないことで有名です

ドキュメントが用意されているので、それらを参照して作業を進めていきましょう。swiftコマンドのインストール、オブジェクトストレージへのアクセスまではドキュメントを見れば出来るはずです。

下手にググると古い情報が出てきて余計混乱する可能性があります(私のこと)。なるべく公式が用意しているものを参考にしましょう。OpenStackに準拠しているので、そちらのドキュメントも役に立ちます。


apiユーザ名、パスワード、エンドポイントなど

ログイン後、APIタブをクリック!


Conohaちゃん公式ドキュメント


OpenStack公式ドキュメント


MDN

ajaxとかCORSとかあやふやな方は読んでおくと良いです。preflightって美味いの?


python-swiftclientのインストール

pip install python-swiftclient

環境によってはpipだとpython2系の方にインストールされるかもしれません。その場合はpip3とかを試すと良いです。


環境変数の設定

オブジェクトストレージはオンライン上にあるので、当然認証情報が必要です。必要な情報はconohaにログイン後、APIの設定画面を開くと書いてあります。

以下の設定は~/.bashrcに追記して下さい。


# export OS_AUTH_URL='[Identity Service]'
export OS_AUTH_URL='https://identity.tyo2.conoha.io/v2.0'
# export OS_TENANT_NAME='[テナント名]'
export OS_TENANT_NAME='gnct12345678'
# export OS_USERNAME='[ユーザ名]'
export OS_USERNAME='gncu12345678'
# export OS_PASSWORD='[APIユーザー作成時に設定したパスワード]'
export OS_PASSWORD='conohachanpassword'

追記できたら保存して再ログインもしくは以下のコマンドで再読込します。

source ~/.bashrc


確認

[kiwi@edge ~]$ swift stat

Account: nc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Containers: 2
Objects: 6016
Bytes: 17443618515
Containers in policy "policy-0": 2
Objects in policy "policy-0": 6016
Bytes in policy "policy-0": 17443618515
Meta Quota-Bytes: 107374182400
X-Timestamp: 1530874927.30163
Content-Type: text/plain; charset=utf-8
Accept-Ranges: bytes
X-Trans-Id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

インストール、環境変数の設定が出来ていればswift statコマンドが実行出来るようになるはず...


pythonでオブジェクトストレージを操作する

以下のクラスは私が個人で使用するために書いたやつです。

外部ライブラリを利用しているので、使用する場合は以下のコマンドでrequestsをインストールして下さい。

pip install requests


storage.py

from requests import post

from requests import get
from json import loads
from os.path import join
from os import chmod
from subprocess import Popen, PIPE

class RestApi(object):
def __init__(self):
self.auth: dict = {
'tenant_name': 'gnct12345678',
'tenant_id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'auth_url': 'https://identity.tyo2.conoha.io/v2.0/',
'endpoint_url': 'https://object-storage.tyo2.conoha.io/v1/nc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/',
'password_credentials': {
'username': 'gncu12345678',
'password': 'conohachanpassword',
},
'tempurl_key': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
}

def fetch_token(self) -> str:
"""
apiアクセスにはtoken取得が必要なので、事前にこの関数を実行して返り値を貰う必要があります。
"""

url: str = join(self.auth['auth_url'], 'tokens')
data: dict = {
"auth": {
'passwordCredentials': self.auth['password_credentials'],
'tenantId': self.auth['tenant_id'],
}
}
r = post(url=url, json=data)
if r.status_code == 200:
return loads(r.text, encoding='utf-8')['access']['token']['id']
else:
raise AuthorizedError()

def fetch(self, container: str = '', obj: str = ''):
"""
container, objが共に空なら、オブジェクトストレージ全体の情報を返します。
objのみが空なら対象オブジェクト内に存在するファイルをdictで返します。
container, objに文字列が渡されていれば、対象objectファイルのバイナリを返却します。
"""

url: str = join(self.auth['endpoint_url'], container, obj)
payload: dict = {
'X-Auth-Token': self.fetch_token(),
'Accept': 'application/json',
}

r = get(url=url, headers=payload)
if r.status_code == 200:
if obj:
return r.content
else:
return loads(r.text, encoding='utf-8')
else:
raise AuthorizedError()

def fetch_container(self) -> iter:
r = self.fetch()
return (i['name'] for i in r)

def create_tempurl(self, container: str, obj: str) -> str:
method: str = 'GET'
expires: str = "300"
path: str = join(self.auth['endpoint_url'], container, obj)
key: str = self.auth['tempurl_key']
command: list = f'swift tempurl {method} {expires} {path} {key}'.split(
' ')
std = Popen(command, shell=False, encoding='utf-8',
stdout=PIPE, stderr=PIPE)
outs, _ = std.communicate()
return outs

def register_for_metadata(self, payload: dict, container: str = "", obj: str = ""):
url: str = join(self.auth['endpoint_url'], container, obj)
r = post(url=url, headers=payload)
return r.status_code, r.headers, r.text

def remove_for_metadata(self, metaname: str, container: str = "", obj: str = ""):
metaname = metaname.title()
if metaname[:2] == 'X-':
metaname = metaname[2:]
url: str = join(self.auth['endpoint_url'], container, obj)
meta = 'X-Remove-' + metaname
payload: dict = {
'X-Auth-Token': self.fetch_token(),
meta: '',
}
r = post(url=url, headers=payload)
return r.status_code, r.headers, r.text

def write_token(self,
path: str = "./token.txt",
mode: str = 'wt',
encoding: str = 'utf-8'):
token: str = self.fetch_token()
try:
with open(file=path, mode=mode, encoding=encoding) as fp:
fp.write(token)
except (IOError, OSError, TypeError):
raise IOError
else:
chmod(path, 0o704)

class AuthorizedError(Exception):
pass


以後、上のクラスを利用してオブジェクトストレージを操作します。認証情報などは各々RestApi.authを書き換えて下さい。


storage.pyの使い方


アクセストークンを取得する


test.py

from storage import RestApi

api = RestApi()
print(api.fetch_token())



アクセストークンを./token.txtに保存する


test.py

from storage import RestApi

api = RestApi()
api.write_token(path='./token.txt')



imageコンテナにmetadataを追加する


test.py

from storage import RestApi

api = RestApi()
origin: str = "https://www.kiwi-bird.xyz"
payload: dict = {
'X-Auth-Token': api.fetch_token(),
'Accept': 'application/json',
'X-Container-Meta-Access-Control-Allow-Origin': origin, # CORS対策
}
api.register_for_metadata(payload=payload, container="image")



imageコンテナのmetanameX-Container-Readを削除する


test.py

from storage import RestApi

api = RestApi()
metaname: str = 'X-Container-Read'
api.remove_for_metadata(metaname=metaname, container="image")



imageコンテナのtest.jpgオブジェクトを取得する


test.py

from storage import RestApi

api = RestApi()
print(api.fetch(container='image', obj='test.jpg'))



終わり

以上!すぐに忘れるので備忘録として残しました。ConohaちゃんLOVE