[PYTHON] Script for backing up folders on the server to Google Drive

Overview

This is a Python script that zips a folder on the server and stores it in Google Drive. It is supposed to be executed regularly, and it also has a function to delete old backup files so that Google Drive does not fill up.

The GitHub repository is here.

I use this script to back up the save data of the Minecraft server, but I thought that it could be used for other purposes as well, so I wrote it in Qiita's article. By the way, I run this script on AWS EC2 (OS is Ubuntu).

To get it working, install the modules needed to use the Google Drive API with pip.

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

Whole code

The big picture of the code is below.

import pickle
import os.path
import datetime
import shutil
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.http import MediaFileUpload


# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive']
#Number of generations to complement backup
GENERATIONS = 3
#Backup data directory
DATA_DIR = '/home/minecraft/server/'
#The name of the folder you want to back up
DATA_NAME = 'newworld'
#Google Drive folder ID to save backup data(Will appear in the url)
PARENT_ID = 'xxxxxx'

def main():
    '''
Main function
    '''
    #Get credentials
    creds = get_creds()
    #Creating a service
    service = build('drive', 'v3', credentials=creds)
    #Checking files that have already been uploaded
    existing_files = get_existing_files(service)
    if len(existing_files) >= (GENERATIONS - 1):
        #If it is stored more than necessary, delete the old one
        delete_unnecessary_files(service,existing_files)
    #Zip the file to upload
    file_to_upload = create_zip_file()
    #Upload file
    upload_file(service)

def get_creds():
    '''
    1.Get credentials
    '''
    creds = None
    #Try to open the authentication file
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    #If you fail, make a new one
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds

def get_existing_files(service):
    '''
    2.Get an existing backup file
    '''
    # Call the Drive v3 API
    query = "'" + PARENT_ID + "' in parents"
    results = service.files().list(fields="nextPageToken, files(id, name, createdTime)", q=query).execute()
    items = results.get('files', [])

    if not items:
        return {}
    else:
        return items

def delete_unnecessary_files(service,existing_files):
    '''
    3.Delete backup files that you no longer need to keep
    '''
    #Get the creation time of each file and convert it to a datetime object
    for f in existing_files:
        f['datetime_createdTime'] = datetime.datetime.strptime(f['createdTime'], '%Y-%m-%dT%H:%M:%S.%fZ')
    #Sort by creation date
    sorted_files = sorted(existing_files, key=lambda x: x['datetime_createdTime'])          
    delete_len = len(sorted_files) - (GENERATIONS - 1)
    for i in range(delete_len):
        service.files().delete(fileId=sorted_files[i]['id']).execute()

def create_zip_file():
    '''
    4.Zip the folder you want to back up
    '''
    shutil.make_archive(DATA_DIR + DATA_NAME, 'zip', root_dir=DATA_DIR + DATA_NAME)

def upload_file(service):
    '''
    5.Upload file
    '''
    today_str = datetime.datetime.now().strftime("%D").replace("/","-")
    file_metadata = {'name': today_str + '.zip','parents':[PARENT_ID]}
    media = MediaFileUpload(DATA_DIR + DATA_NAME + '.zip', mimetype='application/zip')
    results = service.files().create(body=file_metadata,media_body=media,fields='id').execute()

if __name__ == '__main__':
    main()

Code description

1. Obtain authentication information

For the authentication information acquisition part, the thing of Google Drive API official sample is used as it is.

The first time you run it, your browser will open and ask for permission to authenticate. When the authentication flow is completed, the authentication information will be saved in token.pickle, so you will not need to authenticate with the browser from the next time.

2. Get an existing backup file

Fetch the file already saved as a backup in the folder of Google Drive. Official sample of Google Drive API I am trying to get the files under a specific folder by slightly modifying the thing. I will.

3. Delete backup files that you no longer need to keep

Delete unnecessary files so that Google Drive does not fill up. The file data on Google Drive acquired in the previous step is sorted in the order of upload time, and the oldest one is deleted.

4. Zip the backup folder

That's right. I am using shututil.

5. Upload the file

Upload the zip file. By tweaking the metadata of the file, I try to upload it under a specific folder.

By the way

The above script is automatically executed when the server starts

I have the above script set to run when the server starts. There are several ways to do this, but I used cron. Specifically, I wrote the following in crontab.

@reboot python3 /home/minecraft/tools/backup.py

By adding @ reboot, it will be executed at boot time.

Recommended Posts

Script for backing up folders on the server to Google Drive
Send a message from the server to your Chrome extension using Google Cloud Messaging for Chrome
To disable the browser cache on Python's simple HTTP server
Save images on the web to Drive with Python (Colab)
I tried changing the python script from 2.7.11 to 3.6.0 on windows10
ls -R on Google Drive
How to set cron for regular Python scraping on Sakura server.
A note on how to check the connection to the license server port
The day Chainer runs on GPU CentOS edition (up to CPU)
The most polite way to use the Google Maps SDK for iOS
Settings to be made when starting up the linux server centos7
Regularly upload files to Google Drive using the Google Drive API in Python
Python script for KiCad-Pre-place footprint almost according to the schematic layout
Host the network library Mirror for Unity on a Linux server
Download the images and videos included in the tweets you liked on Twitter and upload them to Google Drive
Notes on using matplotlib on the server
Connect to VPN with your smartphone and turn off / on the server
AtCoder writer I wrote a script to aggregate the contests for each writer
Verification of how to periodically execute a script on a Linux server on Windows
Implemented in Dataflow to copy the hierarchy from Google Drive to Google Cloud Storage