pythonでGoogle Driveにリモートバックアップ

さくらのVPSのバックアップファイルをGoolge Driveに自動転送するスクリプトを作りました。容量オーバーにならないように古いファイルを削除する処理も組み込みました。

スクリプト

■ remoteBackup.py

#!/usr/bin/python
# coding: UTF-8
import os, time, re
import gDriveAccess

def backup_files(filename_pattern_str, from_days):
	today = time.localtime()
	today_epoch_delta = time.mktime(today)
	from_day_epoch_delta = today_epoch_delta - from_days*24*60*60

	src_dir = os.getcwd()
	files = os.listdir(src_dir)
	ret_files = []
	for file in files:
		file_upd_time = os.path.getmtime(file)
		filename = os.path.basename(file)
		if file_upd_time <= from_day_epoch_delta:
			continue
		if re.search(filename_pattern_str, filename):
			ret_files.append(filename)
	return ret_files

#------------------------------------------------------------------------------
# Connect to Google Drive
#------------------------------------------------------------------------------
drive_service = gDriveAccess.build_service()

#------------------------------------------------------------------------------
# Delete old backup files in Google Drive
#------------------------------------------------------------------------------
query = "title contains 'www'" #'contains' means 'start with'
query += " and mimeType = 'application/x-tar-gz'"
query += " and modifiedDate <'" + gDriveAccess.threshold_date_str(2) + "'"
files = gDriveAccess.retrieve_files(drive_service, query)
for item in files:
	print "delete file = %s, %s, %s" % (item["title"], item["id"], item["modifiedDate"])
	gDriveAccess.delete_file(drive_service, item["id"])

#------------------------------------------------------------------------------
# Transfer backup files to Google Drive
#------------------------------------------------------------------------------
files = backup_files("gz$", 1)
for filename in files:
	print "backup file = %s" % filename
	gDriveAccess.upload_file(drive_service, filename, 'application/x-tar-gz')

処理内容

  1. Google Driveに接続
  2. Google Driveにある古いバックアップファイルを削除
  3. 当日分のバックアップファイルをGoogle Driveに転送

■ gDriveAccess.py

Google Driveに簡単にアクセスするためのモジュールです。

#!/usr/bin/python
# coding: UTF-8

import httplib2
from oauth2client.client import OAuth2WebServerFlow
from apiclient.discovery import build
from apiclient.http import MediaFileUpload
from apiclient import errors
import gDriveCredential

import logging
logging.basicConfig()

CLIENT_ID = '********.apps.googleusercontent.com'
CLIENT_SECRET = '****************'
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'

def first_authorize():
	flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, REDIRECT_URI)
	authorize_url = flow.step1_get_authorize_url()
	print 'Go to the following link in your browser: ' + authorize_url
	code = raw_input('Enter verification code: ').strip()
	credentials = flow.step2_exchange(code)
	gDriveCredential.store_credentials(credentials)
	return credentials

def build_service():
	logging.getLogger().setLevel(getattr(logging, 'ERROR'))

	try:
		credentials = gDriveCredential.get_stored_credentials()
		if credentials is None or credentials.invalid:
			credentials = first_authorize()
	except Exception, e:
		credentials = first_authorize()

	# Connect to Google Drive
	http = httplib2.Http()
	http = credentials.authorize(http)
	drive_service = build('drive', 'v2', http=http)
	return drive_service

def upload_file(service, filename, file_mimetype):
	logging.getLogger().setLevel(getattr(logging, 'ERROR'))

	try:
		credentials = gDriveCredential.get_stored_credentials()
		if credentials is None or credentials.invalid:
			credentials = first_authorize()
	except Exception, e:
		credentials = first_authorize()

	# Upload a file
	media_body = MediaFileUpload(filename, mimetype=file_mimetype, resumable=True)
	body = {
		'title': filename,
		'description': 'sakuraVPS',
		'mimeType': file_mimetype
	}

	file = service.files().insert(body=body, media_body=media_body).execute()

def retrieve_files(service, query):
	"""Retrieve a list of File resources.
	Args:
		service: Drive API service instance.
	Returns:
		List of File resources.
	"""
	result = []
	page_token = None
	while True:
		try:
			param = {}
			if page_token:
				param['pageToken'] = page_token
			param['q'] = query
			# print "param = ", param
			files = service.files().list(**param).execute()

			result.extend(files['items'])
			page_token = files.get('nextPageToken')
			if not page_token:
				break
		except errors.HttpError, error:
			print 'An error occurred: %s' % error
			break
	return result

def delete_file(service, file_id):
	"""Permanently delete a file, skipping the trash.
	Args:
		service: Drive API service instance.
		file_id: ID of the file to delete.
	"""
  	try:
  		service.files().delete(fileId=file_id).execute()
	except errors.HttpError, error:
		print 'An error occurred: %s' % error

def threshold_date_str(days_before):
	import time
	today = time.localtime()
	today_epoch_delta = time.mktime(today)
	return_day_epoch_delta = today_epoch_delta - days_before*24*60*60
	return time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(return_day_epoch_delta))

事前設定

次の変数に、https://code.google.com/apis/consoleで作成したOAuth 2.0 client の値を設定して使用します。

  • CLIENT_ID
  • CLIENT_SECRET

■ gDriveCredential.py

認証情報を保存して再利用するためのモジュールです。

import httplib2
import oauth2client.client

CREDENTIAL_FILE = 'jsonCredential.txt'

def storeJsonCredential(jsonStr):
  f = open(CREDENTIAL_FILE, 'w')
  f.write(jsonStr)
  f.close()

def readJsonCredential():
  f = open(CREDENTIAL_FILE)
  json_credential = f.read()
  f.close()
  return json_credential

def get_stored_credentials():
  """Retrieved stored credentials for the provided user ID.

  Args:
    user_id: User's ID.
  Returns:
    Stored oauth2client.client.OAuth2Credentials if found, None otherwise.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  json_credential = readJsonCredential()
  return oauth2client.client.Credentials.new_from_json(json_credential)


def store_credentials(credentials):
  """Store OAuth 2.0 credentials in the application's database.

  This function stores the provided OAuth 2.0 credentials using the user ID as
  key.

  Args:
    user_id: User's ID.
    credentials: OAuth 2.0 credentials to store.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  json_credential = credentials.to_json()
  storeJsonCredential(json_credential)

使い方

上記のスクリプトをバックアップファイルのあるディレクトリに保存して、remoteBackup.pyを実行します。

前提条件

バックアップファイルは、毎日次のようなファイル名で保存されることを想定しています。

-rwxr-xr-x 1 root root 1172599580  1月 24 03:11 2013 wwwbackup-2013-01-24.tar.gz
-rwxr-xr-x 1 root root 1172592143  1月 25 03:11 2013 wwwbackup-2013-01-25.tar.gz
-rwxr-xr-x 1 root root 1173426517  1月 26 03:11 2013 wwwbackup-2013-01-26.tar.gz
-rwxr-xr-x 1 root root 1177605759  1月 27 03:11 2013 wwwbackup-2013-01-27.tar.gz
-rwxr-xr-x 1 root root 1178057670  1月 28 03:11 2013 wwwbackup-2013-01-28.tar.gz
-rwxr-xr-x 1 root root 1178319271  1月 29 03:11 2013 wwwbackup-2013-01-29.tar.gz
-rwxr-xr-x 1 root root 1178883881  1月 30 03:12 2013 wwwbackup-2013-01-30.tar.gz

注意事項

1回目の実行時は手動でアクセス認証する必要があります。(cf. pythonでGoogle Driveにファイル転送

その時、認証情報が次のファイルに保存されます。

  • jsonCredential.txt

2回目以降の実行は、保存された認証情報を使用するので自動でファイル転送・削除処理が行われます。

Google Drive内のファイルへのアクセス

Googleのドキュメント

ファイル検索

remoteBackup.pyで、削除するファイルを選択するために検索フィルターを設定しています。

 query = "title contains 'www'"

“Search for  Files”にファイル検索時に使えるキーワードが記述されています。しかし、曖昧検索に関する詳細な記述がなく、containsキーワードがそれっぽいですが、試してみたところ、containsキーワードは先頭一致のようです。


関連記事