仕事で、バックアップファイルを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の制限があるのだが、その辺を考慮していない(上げたければ事前に分割しておく事)
ま、そのうち直す事にしようかな〜と…
今のところ、特に大きな影響ないし。
