仕事で、バックアップファイルをAzure Blob Storageにアップロードさせるスクリプトを作成する必要が出てきた。
Azure Blob StorageはRest APIに対応しているので、それで上げれば良さそうだ。
というわけで、こちらの内容を参考にPythonに書き直してスクリプトを作成してみた。
運用時にアップロード・ダウンロード・削除と使い分けするのが面倒だったので、サブコマンドを指定する方式にしている。
azure_blob_backup.py
#!/bin/python
# -*- coding: utf-8 -*-
# +---------------------------------------------------------------------------------------+
# + [作成日] : 2016/02/02 +
# + [作成者] : Blacknon +
# + [概要] : +
# + AzureのBlobに対し、ファイルのアップロード・ダウンロード・削除処理を実施するスクリプト +
# +---------------------------------------------------------------------------------------+
import pycurl
import urllib
import datetime
import base64
import hmac,hashlib
import argparse
import os.path
import cStringIO
import xml.etree.ElementTree as ET
## --------------
# アカウント情報
## --------------
blob_account = 'ストレージアカウント'
blob_accesskey = 'アクセスキー'
def azure_blob_access():
# 変数の代入
blob_container = args.blob_container
if args.subcommand == 'upload':
file_path = args.file_path
file_name = os.path.basename(file_path)
http_request = 'PUT'
html_body = open(file_path).read()
html_size = str(len(html_body))
content_leng = "Content-Length:" + html_size
blob_path = "/" + blob_account + "/" + blob_container + "/" + file_name
blob_url = "https://" + blob_account +".blob.core.windows.net/" + blob_container + "/" + file_name
elif args.subcommand == 'download':
file_name = args.file_name
http_request = 'GET'
if os.path.isdir(args.output_path):
output_path = args.output_path + "/" + file_name
else:
output_path = args.output_path
print output_path
html_size = ''
content_leng = ''
blob_path = "/" + blob_account + "/" + blob_container + "/" + file_name
blob_url = "https://" + blob_account +".blob.core.windows.net/" + blob_container + "/" + file_name
elif args.subcommand == 'delete':
file_name = args.file_name
http_request = 'DELETE'
html_size = ''
content_leng = ''
blob_path = "/" + blob_account + "/" + blob_container + "/" + file_name
blob_url = "https://" + blob_account +".blob.core.windows.net/" + blob_container + "/" + file_name
elif args.subcommand == 'list':
http_request = 'GET'
html_size = ''
content_leng = ''
blob_path = "/" + blob_account + "/" + blob_container + '\ncomp:list\nrestype:container'
blob_url = "https://" + blob_account +".blob.core.windows.net/" + blob_container + '?restype=container&comp=list'
# HTTPリクエスト情報の作成
file_type = 'text/plain'
html_headers = [
'x-ms-blob-type:BlockBlob',
'x-ms-version:2014-02-14',
]
html_date = datetime.datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT")
stringToSign = [
# VERB
http_request,
# Content-Encoding
'',
# Content-Language
'',
# Content-Length
html_size,
# Content-MD5
'',
# Content-Type
file_type,
# Date
html_date,
# If-Modified-Since
'',
# If-Match
'',
# If-None-Match
'',
# If-Unmodified-Since
'',
# Range
'',
]
stringToSign = stringToSign + html_headers + [blob_path]
stringToSign = "\n".join(stringToSign)
signature = base64.encodestring(hmac.new(base64.decodestring(blob_accesskey),stringToSign,hashlib.sha256).digest())
signature = signature.rstrip("\n")
authorization = "SharedKey " + blob_account +":" + signature
html_headers = html_headers + [
"Authorization:" + authorization,
"Date:" + html_date,
"Content-Type:" + file_type,
content_leng,
]
c = pycurl.Curl()
c.setopt(pycurl.URL, blob_url.rstrip("\n"))
c.setopt(pycurl.HTTPHEADER, html_headers)
c.setopt(pycurl.CUSTOMREQUEST, http_request)
if args.subcommand == 'upload':
c.setopt(pycurl.POSTFIELDS, html_body)
elif args.subcommand == 'download':
op = open(output_path,'wb')
c.setopt(pycurl.WRITEDATA, op)
elif args.subcommand == 'list':
response = cStringIO.StringIO()
c.setopt(c.WRITEFUNCTION, response.write)
c.perform()
if args.subcommand == 'list':
text = response.getvalue()
root = ET.fromstring(text)
ufilelist= root.findall(".//Name")
for ufile in ufilelist:
print(ufile.text);
## ------------
# Paser設定
## ------------
parser = argparse.ArgumentParser(description='Azure Blob File Upload/Download/Delete. Only 1 File.')
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
# upload
parser_up = subparsers.add_parser('upload',help='upload file to Azure Blob.')
parser_up.add_argument('-c','--container',type=str,dest='blob_container',required=True,help='File upload destination of the Blob Container')
parser_up.add_argument('-f','--upload_file',type=str,dest='file_path',required=True,help='File to be uploaded to the Blob Container')
# download
parser_down = subparsers.add_parser('download',help='download file from Azure Blob.')
parser_down.add_argument('-c','--container',type=str,dest='blob_container',required=True,help='File download from This Blob Container')
parser_down.add_argument('-f','--download_file',type=str,dest='file_name',required=True,help='File to be downloaded to the Blob Container')
parser_down.add_argument('-o','--output_path',type=str,dest='output_path',required=True,help='File to out put path')
# delete
parser_del = subparsers.add_parser('delete',help='delete file Azure Blob.')
parser_del.add_argument('-c','--container',type=str,dest='blob_container',required=True,help='File delete from This Blob Container')
parser_del.add_argument('-f','--delete_file',type=str,dest='file_name',required=True,help='Delete to the Blob Container')
# list
parser_list = subparsers.add_parser('list',help='list up Azure Blob file.')
parser_list.add_argument('-c','--container',type=str,dest='blob_container',required=True,help='Get file list from This Blob Container')
# 変数代入
args=parser.parse_args()
azure_blob_access()
サブコマンドでlist、upload、download、deleteを使い、Azure Blob Storageに接続、操作できるようにしている。
なお、以下の問題点があるんだけど、とりあえず今は直してない。。。
- Azure Blob Storage上にないファイルをダウンロードすると、エラーにならないでアウトプットに指定したPATHにそのまま出力する
- Azure Blob Storageの仕様で64MBの制限があるのだが、その辺を考慮していない(上げたければ事前に分割しておく事)
ま、そのうち直す事にしようかな〜と…
今のところ、特に大きな影響ないし。