WordPressのサイトはバックアッププ系ラグイン「BackWPup」で定期的にバックアップを取っています。
一時期、自動更新で更新してたらサイトが表示されないバグがあったので使っていませんでしたが気が付いたらバージョンアップされバグも修正されまた使えるようになったで、このプラグインでバックアップ作成するのを再開しました。
ただ、BackWPupで取ったバックアップファイル世代管理していても30世代分や60世代分となるとサーバーの使用容量が増えるので以前Raspberry PIを使ってバックアップファイルをローカルに保存するプログラム(スクリプト)を作っていました。
改修したスクリプトは、FTP接続情報やダウンロードするバックアップファイルのダウンロード先ディレクトリなどの設定を外部の構成ファイルに変更、FTPの他にSFTPにも対応させました。
あとは気になった部分の細かい修正。
数年ぶりにプログラムに触った。
使ってない頭を使いながらの作業でした。基本、ネットで調べて出てきたプログラムを組み合わせてるだけなので、具体的にどの様な処理をしているか説明出来るレベルでないことをご了承ください。
自分用のメモでソースを書き残しているだけです。特に解説はありません。また、動作も自分用で2023年9月現在契約してるさくらインターネットのさくらのレンタルサーバーのみ確認してるので、自分用にFTP接続情報など外部の構成ファイルで管理出来るようにしているだけで、自分以外の人が使える汎用的な作りにはなってない点もご了承ください。
プログラムの変数名やコメントも自分が分かるように書いてあるので、英単語の意味が違ってたりコメントに書いてある用語も実際の意味とは少し違っていたりするかもしれませんが、あくまで自分用のプログラムです。
動作環境
- さくらインターネットのさくらのレンタルサーバー スタンダードプラン
- RaspberryPI2
- Raspberry Pi OS 32bit バージョン11
- Python3.9
2023年9月現在。
Raspberry Pi OS旧バージョン(多分バージョン10?)だとPythonのSFTP通信に使うモジュール「paramiko」のインストールがうまくできなかったので、OS再インストールしたらPython3.9が標準に変わり、すんなりとparamikoのインストール出来るようになってます。
少しOS再インストールした経緯
以前インストールされてたOSだと、標準でPython2.7、Python3で実行するとPython3.7で動作、pipもpip3でparamikoインストールしようにも「libffi」が古いとかのエラーで新しくインストールしようにも404エラーでダウンロードができない。
ソースからPython3.9インストールしたけどparamikoもlibffiもインストールできず、Python3.9をアンインストールしようにもアンインストール仕方が分からずOS再インストールしました。
そのおかげで標準でPython3.9になりparamikoのインストールもpipで簡単に出来ましたが。
プログラム動作の、ダウンロード先ディレクトリ作成で既にディレクトリがあるのに作成しようとしてエラーになる問題を修正しました。ダウンロード先ディレクトリの存在チェック追加(2023/10/1)
スクリプト改修するまでの経緯
2023年9月現在このブログのサーバーはさくらインターネットのさくらのレンタル「スタンダードプラン」を使用しています。去年だったかに新サーバーに移行してます。
上記のBackWPupで作られたバックアップファイルをダウンロードするスクリプト、以前さくらのレンタルサーバーでFTP接続してバックアップファイルをダウンロードしてる途中で通信が遮断されてダウンロード失敗することがあり現在このスクリプトを使っていませんでした。
さくらのレンタルサーバースタンダードプランのディスク容量は300GBあるので、バックアップファイルはサーバーだけに保管されてる状態ですが、契約してるドメイン代がまた値上げされたのでドメインは数年運用してて他の運用してるサイト含めてリンクも貰ってるのでドメインを変えるのも中々難しく、サーバー移転を考えてます。
以前使ってたリトルサーバーをまた契約しようかと考えていますが、サーバー容量が足りない。
一番容量が食ってるのが、BackWPupで作られたバックアップファイル。1日1回バックアップを取るとして数日分だと少ない、30日分は世代管理したいのでサーバーでの世代管理を少なくしてローカルドライブで世代管理する必要があります。
そこで、以前作ったBackWPupで作られたバックアップファイルをローカルドライブにダウンロードするスクリプト、今回はSFTP接続にも対応させたいのでプログラムを改修します。
以前スクリプトを動かしていた時だと、ダウンロード中の通信遮断はさくらだけで起きていて、以前契約していたリトルサーバーだと起きてませんでした。ファイルダウンロード中に無駄な通信をして通信が途切れないようにする方法もあるらしいですが、方法が分からないのとリトルサーバーで起きなかったので、FTP接続周りのプログラムは多少書き方を変えていても以前のままです。
サーバーの話で余談
リトルサーバーを契約していた当時、レンタルサーバーは「さくら」と「リトルサーバー」の掛け持ちでした。
リトルサーバーは2年ほど契約していたと思いますが、WordPressのfunction.php編集など自分のミスでサイトが表示されなくなった以外は、トラブルも不満もなくリトルサーバーを使っていましたが、さくらインターネットは昔契約していたメール専用プランを含めると長い付き合いになるので、費用を抑えるのが理由でレンタルサーバーを「さくら」1本にしました。
今回はドメイン代の値上げで、その費用を抑える理由でメールは「さくら」、サーバーは「リトルサーバー」に考えてます。リトルサーバーの契約で初期費用除くと1年契約で今より1500円軽減される予定。
ドメインは数年運用していて同じドメインで運用してる他のサイト含めてリンクも貰ってるようなので、ドメインを変えるのはそう簡単にはいかないです。
まあ、さくらのレンタルサーバーの更新は来年5月なのでサーバー移転は来年3月ごろを予定していますが。
スクリプトファイルの構成
- ./
- wpbk_dl.py ※メインのスクリプトファイル
- config.ini 構成ファイル
- url_list.txt ※WordPressサイトのリスト(構成ファイルのパラメーターに依存)
- log ※エラーログファイルのディレクトリ
- error_202309.log ※エラーログ(ファイル名年月で管理)
- ./hoge/
- WPBK/ ※バックアップファイルのダウンロード先(構成ファイルのパラメーターに依存)
- example.jp
- 2023-09-27_10-00-07_hogehoge.zip
- 2023-09-28_10-00-07_hogehoge.zip
- sub.example.jp
- 2023-09-27_10-00-07_hogehoge.tar.gz
- 2023-09-28_10-00-07_hogehoge.tar.gz
- example.jp
- WPBK/ ※バックアップファイルのダウンロード先(構成ファイルのパラメーターに依存)
こんな感じです。同一サーバー内で複数サイト運用していても対応できるようにしてます。バックアップファイルも[.zip][.gz]共に対応、混在していてもダウンロード出来ます。
構成ファイルに書かれてるパラメーターで、ダウンロードするバックアップファイルのディレクトリ先とurl_list.txtのファイル名を任意に変えられます。
ファイルの「wpbk_dl.py」「config.ini」「url_list.txt」は同じディレクトリに入れる必要があります。
url_list.txtは、バックアップファイルはサイト毎で管理するのでローカルに「ダウンロード先」とサーバーにあるバックアップファイルの絶対パスが書かれています。
url_list.txt
example.com.jp,/home/ユーザー/www/example.com.jp/wp-content/uploads/backwpup-******-backups
sub.example.com,/home/ユーザー/www/sub.example.com.jp/wp-content/uploads/backwpup-******-backups
[ダウンロード先パス],[ダウンロード元パス]
「ダウンロード先パス」の文字は何でもいいのですが、サーバーがサイトのドメイン名のディレクトリになってるのでそのままです。
「ダウンロード元パス」は、BackWPupでバックアップファイルを保存しているディレクトリ、絶対パスです。さくらの階層はこんな感じです。
プログラムはダウンロード元ディレクトリ内のファイルを参照して、「.zip」と「.gz」のファイルをダウンロードするように動作してます。バックアップファイル以外にも同じ拡張子のファイルがあればダウンロードされてしまいます。
もちろん、サイトのURLやサーバーのユーザーディレクトリは架空のものです。
BacnkWPupで作られたバックアップファイルのファイル名に日時が入ってるので、ローカルドライブでの世代管理はこの日時で管理してます。
そのため、WordPress管理画面のBacnkWPup管理ページの作成したジョブの「バックアップ作成」項目にあるアーカイブ名は日時が入ったファイルする必要です。
構成ファイルconfig.ini
FTP接続情報やダウンロード先パスなどの情報が入ってる構成ファイルです。
config.ini
;※公開鍵認証での接続は実装していません
[FTP_CONNECT]
;パスワードと公開鍵認証(RSA)ともに値が入っている場合は公開鍵認証優先で接続します。
;また、公開鍵認証で接続する場合はSFTPになります。
HOST = ホスト名
USER = ユーザー名
PASS = パスワード
RSA_PATH =
;[FTP]接続か[SFTP]接続か設定。※ポート番号に注意
MODE = SFTP
;FTPはポート21、SFTPはポート22 ※さくらのレンタルサーバーの場合
PORT = 22
;FTP再接続の回数を指定
RECNT = 3
[LOCAL_DIR_PATH]
;サーバーからダウンロードするバックアップファイルの保存先。
;※絶対パス
DIR_PATH = /home/user/hoge
[FILE_GEN]
;ローカルで保存するバックアップファイルの世代管理。各サイトで設定した世代数で管理します。
FILE_GEN = 10
[URL_LIST_PATH]
;バックアップファイルが保存されてるサーバーの絶対パスとサイトのURLが保存されてるテキストファイルを入力します。サイトURLはローカルの保存先になります。
;※wpbk_dl.pyと同じディレクトリに保存
URL_LIST_PATH = url_list.txt
[FTP_CONNECT]にはFTP接続に必要なホスト名やユーザー名、パスワードにポート番号を入力。FTP接続の時は「MODE = FTP」、SFTPで接続する場合は「MODE = SFTP」と入力します。
SFTP接続はパスワードと公開鍵認証の秘密鍵にも対応できるように両方用意していますが、さくらのレンタルサーバースタンダードプランはパスワード認証しかないので、公開鍵認証はプログラムに実装されてないので秘密鍵のパスを入力しても接続エラーになります。
リトルサーバーが公開鍵認証のみの認証らしいので(昔の事なので忘れました)、契約したら対応する予定。
SFTPの公開鍵認証は以下のサイトを参考にすると、秘密鍵のファイルからキーを取得して、パスワードの代わりにキーで認証すれば接続出来そうです。
さくらで、ダウンロード中に通信が遮断することがたまに起こるので、「RECNT = 3」で再接続する回数決めます。デフォルトは3回まで再接続してます。
[LOCAL_DIR_PATH]にダウンロード先ディレクトリの絶対パスを入力。絶対パスなので外部ストレージにも対応してます。
USB外付けケースに入れたSSDをラズパイでマウントして、改修前のプログラムで動作確認してます。外部ストレージのマウント方法は以下のサイトを参考。
[FILE_GEN]は世代管理。[10]と入力すれば、サイト毎にバックアップファイル10個分まで保存され、11個以上はファイル名の日付が古いのから削除される。
[URL_LIST_PATH]は先ほど説明したディレクトリの[ダウンロード先][ダウンロード元]が書かれたテキストファイル。ファイル名「url_list.txt」は任意ですが、スクリプト「wpbk_dl.py」と同じディレクトリ内に入れる必要があります。
「./text/url_list.txt」のようなディレクトリ含めた書き方は動作未検証。自分が使うからいいのです。
プログラムコード
説明なしただの貼り付けです。作ったプログラム削除してしまった時のバックアップということで。
wpbk.dl.py
#!/usr/bin/env /usr/bin/python
# -*- coding: utf-8 -*-
import re
import os
import glob
import ftplib
import time
import paramiko #SFTP接続
import configparser
from datetime import datetime
#ファイルチェックのフラグ。=1ならNG
start_flg = 0
#実行中ファイルのディレクトリ取得してカレントディレクトリ移動
SCRIPT_DIR_PATH = os.path.dirname(__file__)
#関数
#エラーログ作成関数
def errorlog_create():
month_now = datetime.now().strftime("%Y%m")
with open(SCRIPT_DIR_PATH + '/log/error_' + month_now + '.log', mode='a') as f:
f.write('')
#保存ファイルのパーミッション変更
os.chmod(SCRIPT_DIR_PATH + '/log/error_' + month_now + '.log', 0o755)
#エラーログ書き込み
def errorlog_write(error_var, error_code = ''):
month_now = datetime.now().strftime("%Y%m")
with open(SCRIPT_DIR_PATH + '/log/error_' + month_now + '.log', mode='a') as f:
f.write('エラー:' + datetime.now().strftime("%Y/%m/%d %H:%M:%S") + ',' + error_var + "\r\n")
if(error_code):
f.write('>' + error_code + "\r\n")
#保存ファイルのパーミッション変更
os.chmod(SCRIPT_DIR_PATH + '/log/error_' + month_now + '.log', 0o755)
#FTP接続でファイル名取得関数
def ftp_cwd_list(dir_var):
with ftplib.FTP() as ftp:
ftp.timeout = timeout
try:
#FTP接続
ftp.connect(FTP_HOST, FTP_PORT)
ftp.login(FTP_USER, FTP_PASS)
ftp.set_pasv("True")
except ftplib.all_errors as e:
print('サーバーに接続できません.')
errorlog_write('[FTP]サーバーに接続できません.', str(e))
exit()
try:
#FTPサーバーからファイル名取得
ftp.cwd(dir_var)
file_list_var = []
file_list_var += ftp.nlst()
return file_list_var
except ftplib.all_errors as e:
#ftp.close()
print('サーバーの指定したディレクトリにアクセスできません。サーバーディレクトリパスに誤りがあります')
errorlog_write('[FTP]サーバーの指定したディレクトリにアクセスできません.', str(e))
time.sleep(2) #時間待ち。この処理は不要かも
return 0
#FTP接続でファイルダウンロード関数
def ftp_file_dl(local_dir_var, ftp_dir_var, file_name):
#ファイルDL失敗したら再接続。上限回数=retry_n
for cnt in range(FTP_RECNT):
with ftplib.FTP() as ftp:
ftp.timeout = timeout
try:
print('FTP接続(' + str(cnt+1) + ')回')
print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
# 接続先サーバーのホスト名
ftp.connect(FTP_HOST, FTP_PORT)
ftp.login(FTP_USER, FTP_PASS)
ftp.set_pasv("True")
# ユーザ名とパスワードを指定しログイン
ftp.cwd(ftp_dir_var) #ディレクトリ移動
with open(local_dir_var + '/' + file_name, 'wb') as f:
ftp.retrbinary('RETR %s' % './' + file_name, f.write)
print('ダウンロード完了')
#保存ファイルのパーミッション変更
#os.chmod(local_dir_var + '/' + file_name,0o775)
break #ダウンロード完了したらFTP再接続ループから脱出
except Exception as e:
if os.path.exists(local_dir_var + '/' + file_name):
os.remove(local_dir_var + '/' + file_name)
print('FTP_ファイルDL失敗')
if cnt + 1 >= FTP_RECNT : #FTP上限回数超えて切断したらログを記録する
print(e)
errorlog_write('[FTP]ダウンロードが途中で中断されました' + ', FTP,' + local_dir_var + '/' + file_name + '.', str(e))
time.sleep(2) #時間待ち。この処理は不要かも
#SFTP接続でファイル名取得関数
def sftp_cwd_list(dir_var):
try:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
client.connect(FTP_HOST, port=FTP_PORT, username=FTP_USER, password=FTP_PASS, timeout=timeout)
sftp_connection = client.open_sftp() #SFTP接続
except Exception as e:
client.close()
print('サーバーに接続できません。ホスト、ユーザーアカウント、パスワード、ポート番号に誤りがあります')
errorlog_write('[SFTP]サーバーに接続できません.', str(e))
exit()
try:
sftp_connection.chdir(dir_var) #SFTP ディレクトリ移動
file_list_var = []
#サーバーディレクトリにあるファイル名全取得
file_list_var += sftp_connection.listdir()
time.sleep(2) #時間待ち。この処理は不要かも
return file_list_var
except Exception as e:
client.close()
print('サーバーの指定したディレクトリにアクセスできません。サーバーディレクトリパスに誤りがあります')
errorlog_write('[SFTP]サーバーの指定したディレクトリにアクセスできません.', str(e))
return 0
#SFTP接続でファイルダウンロード関数
def sftp_file_dl(local_dir_var, ftp_dir_var, file_name):
#ファイルDL失敗したら再接続。上限回数=FTP_RECNT
for cnt in range(FTP_RECNT):
try:
print('SFTP接続(' + str(cnt+1) + ')回')
print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
#SFTP接続
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
client.connect(FTP_HOST, port=FTP_PORT, username=FTP_USER, password=FTP_PASS, timeout=timeout)
sftp_connection = client.open_sftp() #SFTP接続
sftp_connection.chdir(ftp_dir_var) #SFTP ディレクトリ移動
sftp_connection.get(file_name, local_dir_var + '/' + file_name) #ダウンロード
client.close() #SFTP切断
print('ダウンロード完了')
#保存ファイルのパーミッション変更
#os.chmod(local_dir_var + '/' + file_name,0o775)
break #ダウンロード完了したらFTP再接続ループから脱出
except Exception as e:
print(e.__class__.__name__)
if os.path.exists(local_dir_var + '/' + file_name):
os.remove(local_dir_var + '/' + file_name)
print('SFTP_ファイルDL失敗')
if cnt + 1 >= FTP_RECNT : #FTP上限回数超えて切断したらログを記録する
client.close() #SFTP切断
print(e)
errorlog_write('[SFTP]ダウンロードが途中で中断されました' + ', FTP,' + local_dir_var + '/' + file_name + '.', str(e))
time.sleep(2) #時間待ち。この処理は不要かも
#ファイル名の拡張子.zip、.gzのみ出力する関数
def filename_zipgz_sorting(file_name_list):
#空リスト
file_sorting_name = []
for file_name in file_name_list:
#拡張子.zip判定
if(file_name.endswith('.zip')):
file_sorting_name.append(file_name)
#拡張子.gz判定
if(file_name.endswith('.gz')):
file_sorting_name.append(file_name)
return file_sorting_name
#// 関数 //
#処理開始
#ログ保存先ディレクトリ作成
if not(os.path.exists(SCRIPT_DIR_PATH + '/log')):
os.mkdir(SCRIPT_DIR_PATH + '/log')
#パーミッション変更
os.chmod(SCRIPT_DIR_PATH + '/log', 0o775)
#エラーログ作成
errorlog_create()
#config.iniファイルチェック
if not(os.path.exists(SCRIPT_DIR_PATH + '/' + 'config.ini')):
print('config.iniが見つかりません。保存先の階層やファイル名に間違いがないか確認してください')
errorlog_write('config.iniファイルが見つかりません')
exit()
#構成ファイル読み込み
config_ini = configparser.ConfigParser()
config_ini.read(SCRIPT_DIR_PATH + '/' + 'config.ini', encoding='utf-8')
FTP_HOST = config_ini['FTP_CONNECT']['HOST']
FTP_USER = config_ini['FTP_CONNECT']['USER']
FTP_PASS = config_ini['FTP_CONNECT']['PASS']
FTP_RSA_PATH = config_ini['FTP_CONNECT']['RSA_PATH']
FTP_PORT = config_ini['FTP_CONNECT']['PORT']
FTP_MODE = config_ini['FTP_CONNECT']['MODE']
FTP_RECNT = config_ini['FTP_CONNECT']['RECNT']
BK_DIR_PATH = config_ini['LOCAL_DIR_PATH']['DIR_PATH']
FILE_GEN = config_ini['FILE_GEN']['FILE_GEN']
URL_LIST_PATH = config_ini['URL_LIST_PATH']['URL_LIST_PATH']
#FTP接続のタイムアウト
timeout = 15
#構成ファイルのチェック
if not(FTP_HOST):
print('接続情報にホスト名がありません')
errorlog_write('接続情報のホスト名がありません')
start_flg = 1
if not(FTP_USER):
print('接続情報にユーザー名がありません')
errorlog_write('接続情報のユーザー名がありません')
start_flg = 1
if(FTP_PASS or FTP_RSA_PATH):
if not(FTP_PASS):
if(FTP_RSA_PATH):
if not os.path.exists(FTP_RSA_PAT):
print('RSAキーのファイルが見つかりません')
errorlog_write('接続情報のユーザー名がありません')
start_flg = 1
else:
print('接続情報にパスワードがありません')
errorlog_write('接続情報のパスワードがありません')
try:
FTP_PORT = int(FTP_PORT)
except Exception as e:
print('接続情報のポート番号に誤りがあります')
errorlog_write('接続情報のポート番号に誤りがあります.', str(e))
start_flg = 1
if not((FTP_MODE == 'FTP') or (FTP_MODE == 'SFTP')): #FTP_MODEの値がFTPとSFTP以外
print('FTPモードに誤りがあります')
errorlog_write('FTPモードに間違いがあります')
start_flg = 1
try:
FTP_RECNT = int(FTP_RECNT)
except Exception as e:
print('FTP再接続回数の数値に問題があります')
errorlog_write('FTP再接続の回数の数値に誤りがあります.', str(e))
start_flg = 1
try:
FILE_GEN = int(FILE_GEN)
except Exception as e:
print('世代管理数の数字に誤りがあります')
errorlog_write('世代管理数の数値に誤りがあります.', str(e))
start_flg = 1
if not(os.path.exists(SCRIPT_DIR_PATH + '/' + URL_LIST_PATH)):
print('サイトのURLリストが見つかりません')
errorlog_write(SCRIPT_DIR_PATH + '/' + URL_LIST_PATH + 'が見つかりません')
#バックアップファイル ダウンロード先ディレクトリ作成
if(BK_DIR_PATH):
try:
if not(os.path.exists(BK_DIR_PATH)):
os.mkdir(BK_DIR_PATH)
#パーミッション変更
os.chmod(BK_DIR_PATH, 0o775)
except Exception as e:
print('ダウンロード先ディレクトリを作成できません')
errorlog_write('ダウンロード先ディレクトリを作成できません.', str(e))
start_flg = 1
else:
print('ダウンロード先ディレクトリパスが空欄です')
errorlog_write('ダウンロード先ディレクトリパスが空欄です')
start_flg = 1
if(start_flg):
print('config.iniのパラメーターにエラー:詳細はerror.logを確認してください')
print('処理を中断します')
exit()
os.chdir(BK_DIR_PATH) #ディレクトリ移動
#サイトリストを取得(外部ファイル)
with open(SCRIPT_DIR_PATH + '/' + URL_LIST_PATH , 'r') as f:
url_list = f.read()
url_list = re.split('[\n,]', url_list)
url_cnt = len(url_list)
#サイト毎に処理開始
for i in range(0,url_cnt,2): #1つ飛び出ループ
print(url_list[i])
#サイト毎にディレクトリ作成
if not os.path.isdir(url_list[i]):
os.mkdir(url_list[i])
os.chmod(url_list[i],0o775)
#FTP接続でファイル名取得
if(FTP_MODE == 'FTP'):
ftp_file_name = ftp_cwd_list(url_list[i+1])
elif(FTP_MODE == 'SFTP'):
ftp_file_name = sftp_cwd_list(url_list[i+1])
if(ftp_file_name):
ftp_file_name = filename_zipgz_sorting(ftp_file_name) #拡張子.zipと.gzのファイル名を取得
local_dir_list = os.listdir(url_list[i])
local_dir_list.sort()
local_name_set = set(local_dir_list)
ftp_name_set = set(ftp_file_name)
new_file_name = list(ftp_name_set -local_name_set) #サーバにある新しいファイル取得
#リストを昇順にソート
new_file_name.sort()
if len(new_file_name) != 0:
for new_file_name in new_file_name:
print('ダウンロード:' + new_file_name)
if(FTP_MODE == 'FTP'): #FTP接続でファイルダウンロード
ftp_file_dl(url_list[i], url_list[i+1], new_file_name)
elif(FTP_MODE == 'SFTP'): #SFTP接続でファイルダウンロード
sftp_file_dl(url_list[i], url_list[i+1], new_file_name)
else: #新しくダウンロードするファイル無し
print('新規BKファイル無し')
#ここからBKファイルの世代管理
local_dir_list = os.listdir(url_list[i])
local_dir_list.sort()
print(len(local_dir_list) > FILE_GEN)
if len(local_dir_list) > FILE_GEN:
file_cnt = len(local_dir_list) - FILE_GEN
for n in range(file_cnt):
print('ファイル削除:' + local_dir_list[n])
os.remove(url_list[i] + '/' + local_dir_list[n])
else:
#ローカルドライブに保存されたフィル数カウントのためファイル名リスト取得
local_name_set =os.listdir(url_list[i])
print('削除ファイルなし 世代:' + str(len(local_name_set)))
ソース丸々、print関数もそのまま貼り付けてます。
pipでparamikoモジュールをインストール。それ以外のモジュールは標準のを使用。
最初に「os.path.dirname(__file__)」で実行ファイル「wpbk_dl.py」のディレクトリを取得してます。
crontabから実行するとカレントディレクトリがホームディレクトリになるので実行ファイルのディレクトリを取得する必要がありました。
crontabでカレントディレクトリ移動してからスクリプト実行「cd ディレクトリ; python wpbk_dl.py」と書く方法も出てきたけど何か好きじゃない。
構成ファイルの入力パラメーターチェックして問題なければFTP(SFTP)接続して接続も成功ならファイルダウンロード、ダウンロード先ディレクトリのファイル数を確認して世代管理する流れです。
ダウンロード先ディレクトリにバックアップファイル以外のファイルが含まれると、それもカウントして世代管理するので他のファイル保存されてない状態を前提として動いてます。
エラーログは、構成ファイルのパラメーター入力チェックの他にFTP接続失敗やダウンロード先ディレクトリが存在しない場合も出力されます。
エラーメッセージのログは日時と内容な感じです。
構成ファイルのパラメーターが空欄の様な単純なミスなら簡単なメッセージを出力しますが、FTP接続失敗やダウンロード先ディレクトリが存在しないなど入力したパラメーター見ただけでミスだと気づきにくいミスはシステムのエラーメッセージを加えて出力してます。
>実際にプログラムのミスで既に存在してるディレクトリを作成しようとして「[Errno 17] File exists:」のエラーが出たので、プログラムを修正するのに役に立った(2023/10/1)
こんな感じでしょうか。他の環境で動作するかは分かりませんが、自分環境で動作するからヨシっということで。
最後にcronで実行して動作確認。実行中のスクリプトファイルのディレクトリパスを取得しているので、config.iniを読み込むことが出来てダウンロード先ディレクトリにバックアップファイルも保存されてます。
第三者が見ると色々ダメ出しされそうなプログラムですが、自分も一つ気になる点があるとすれば、ローカルのダウンロード先に保存されてるファイル以外の.zipや.gzファイルを全てダウンロードするところでしょうか。
世代管理の数は例えばサーバー[3]世代、ローカル[10]世代と「サーバー<ローカル」になるように運用する予定なので問題ならないですが、これがサーバー[5],ローカル[3]のように「サーバー>ローカル」になると、ローカルで削除した古いバックアップファイルをダウンロードしてまた削除する処理になりその分時間がかかるんですよね。
だから自分のメモ用になるんですけど。
運用しながら動作確認
ファイルダウンロード中の通信遮断はたまに起こっていたので、まだ分かりません。SFTPだと上手くいかないかなと思ってはいるんですけど。
この記事書きながら思い出しつつあるけど、cronで実行中の時に起きるのかな。その辺も忘れてるので運用しながら動作確認です。
cronでスクリプト実行中。
回線は1Gbps光ですがRaspberry PI2でSFTPは負荷が高いようで1GBのファイルダウンロードするのに4,5分掛かります。処理中に別のコンソールでlsコマンド打つだけでも重い。
計測はしてないですが体感でFTPの方がかなり速いし軽いです。
PythonでSFTPの参考になったサイト