Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

main_conoha_type3.png

このはちゃん可愛い!!

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

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

kiwi-bird
ゲーム制作、サイト制作が好きです。お手製のウェブアプリ「Helium」を日々改造しています。 ※R-18なので公共の場からはアクセスしないようにね♡
https://kiwing.ddns.net/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away