rsyncによるディレクトリ同期を行う際、並列実行により高速化する手法


rsyncを高速化するために、分散して実行することにした。

全部を1つのスクリプトとしてもいいのだが、デバグがしやすいように分割して作業を行えるようにしている。

また、下記の記述はLinuxの/usrをコピーすることを想定している。環境に応じて書き換えること。

まず、/usr/xxx以下にあるファイルまでをコピーするために以下を実行する。

# rsync --archive -v --exclude="*/*/" /usr/ /mnt/vol/voltest

このexcludeオプションをつけている場合、「/usr/xxx/yyy」のファイルとシンボリックリンクはコピーされる。しかし「/usr/xxx/zzz/」のディレクトリはコピーされない。

次に、「/usr/xxx/zzz/」のディレクトリ一覧を取得する。

# find /usr -mindepth 2 -maxdepth 2 -type d -print

このディレクトリ一覧を下記のperlスクリプトに食わせる。(下記スクリプトは” find /usr -mindepth 2 -maxdepth 2 -type d -print > list.txt”で取得したlist.txtを使う想定)

#!/usr/bin/perl

use threads;
use Thread::Queue;

my $LOGDIR="/root/test";
my $MAXSESSION=5;

my $sourcepathbase="/usr";
my $destpathbase="/mnt/vol/voltest";

my $stream = Thread::Queue->new;

open(FILE,"list.txt");
while(my $tmp=<FILE>){
        $stream->enqueue("$tmp");

}
close(FILE);

sub SyncExecute{
        while(my $str = $stream->dequeue){
                # 改行削除
                $str =~ s/\n//ig;
                $str =~ s/\r//ig;
                # ログ出力用ファイル名
                my $filename=$str;
                $filename =~ s/\//-/ig;
                $filename =~ s/\.//ig;
                $filename =~ s/-$//ig;
                $filename =~ s/#//ig;
                $filename =~ s/^-//ig;
                $filename =~ s/ //ig;
                my $logfile="$LOGDIR/test-$filename.log";
                # rsync元と先の処理
                my $tmp,$st,$ed;
                my $sourcepath,$destpath;
                $tmp=substr($str,0,1);
                if($tmp eq "/"){
                        $sourcepath=$sourcepathbase.$str."/";
                        $destpath=$destpathbase.$str;
                }else{
                        $sourcepath=$sourcepathbase."/".$str."/";
                        $destpath=$destpathbase."/".$str;
                }
                `date >> $logfile`;
                print "rsync -v --archive $sourcepath $destpath >> $logfile 2>&amp;1 \n";
                `rsync -v --archive $sourcepath $destpath >> $logfile 2>&amp;1 `;
                `date >> $logfile`;
                #`sleep 5`;
        }
}


my @kids;
foreach(1..$MAXSESSION){
        my $kid = threads->new(\&amp;SyncExecute,$stream);
        push(@kids,$kid);
        $stream->enqueue(undef);
}


print "wait\n";

foreach(@kids){
        my ($return) =$_ -> join;
}

このスクリプトは、rsyncの同時実行数5で、並列にrsyncを実行していくものになっている。

実行したサーバの負荷状況に応じて「my $MAXSESSION=5;」で設定している 同時実行数 を調整する。あまり大きくしすぎるとサーバからの応答が遅くなりすぎるのでほどほどに・・・


2020/03/10追記

上記で実行するrsyncコマンドはハードリンクの処理を行わないものとなっている。

このため、ハードリンクされているファイルがある場合、コピー先のファイルが1つではなく複数別個のものとしてコピーされる。

ハードリンクをそのままコピーしたい場合は「–hard-links」オプションを追加する必要があるのだが、ハードリンク処理の効力範囲は同一プロセス内で処理すること、という条件があるため、今回のような分割処理して高速化する、という場合には不適切となっている。

このため、ハードリンクファイルがある場合は、初回同期は分割処理で行い、2回目はディレクトリ全体を–hard-linksオプションをつけて1プロセスで処理してハードリンク処理を行わせる、という手法をとる必要がある。

なお、ハードリンク処理が完了したあと、分割処理の対象となった場合、すでにファイルが存在しているので再コピーされる、ということは発生しない。

NetAppのperfstatデータ収集ツールで取得したファイルを分解する


現用のNetAppの状況確認をするために「perfstatデータ収集ツール」というのが配布されている。

rsh/sshで対象のNetAppにログインして、いろんなコマンドを実行して、1つのテキストファイルとして出力をする。

この「1つのテキストファイル」というのがくせ者で、200MBぐらいのファイルができたりする。

これだとエディタで取り扱いづらいので細かく分割することにした。

中をみてみると「=-=-=-=-=-= CONFIG IPアドレス PRESTATS =-=-=-=-=-= aggr status -v」という感じで「 =-=-=-=-=-= 」区切りでファイルが出力されている。

それを元にファイルを分割したのが下記スクリプト。

$inputfile="x:\tmp\source\perfstat7_20191122_1400.txt"
$outdir="x:\tmp\output"

$filename=""

Get-Content $inputfile -Encoding UTF8 | ForEach-Object {
    $line=$_
    if( $line.Contains("=-=-=-=-=-=") ){
        $filename=$line
        $filename=$filename.Replace("=-=-=-=-=-= ","")
        $filename=$filename.Replace("/","-")
        $filename=$filename.Replace(":","")
        $filename=$outdir+"\"+$filename+".txt"
        New-Item $filename
    }
    if($filename -ne ""){
        $line | Add-Content $filename
    }
}

今回は1回しか実行しないので速度を気にする必要がないので簡単さを優先している。

もし、出力速度を気にするのであれば1行毎にAdd-Contentするのは非常に効率が悪いので工夫が必要となる。

参考例:「PowerShellで巨大なファイルをGet-Contentし、Export-Csvするのを省メモリで行う


2019/11/25 追記

上記のスクリプトだと遅すぎで、約200MBのファイル処理に5時間かかりました。

やはり改行のみ行が数万行ある、というのが悪かったようです。

また、同じヘッダ行が何回か登場するようで単純に「New-Item」としているとファイル名が重複しているというエラーがでてしまっていました。

さすがに5時間はないなーということで、高速化処理をしたのが下記となります。

実行にかかる時間は2分と大幅短縮となりました。

$inputfile="x:\tmp\source\perfstat7_20191122_1400.txt"
$outdir="x:\tmp\output2"

$filename=""

$results=@()
$linecount=0

Get-Content $inputfile -Encoding UTF8 | ForEach-Object {
    $line=$_
    if( $line.Contains("=-=-=-=-=-=") ){
        if($filename -ne ""){
            $results | Add-Content $filename
            $results=@()
            $linecount=0
        }
        $filename=$line
        $filename=$filename.Replace("=-=-=-=-=-= ","")
        $filename=$filename.Replace("/","-")
        $filename=$filename.Replace(":","")
        $filename=$outdir+"\"+$filename+".txt"
        if ( !(Test-Path $filename) ){
            New-Item $filename
        }
    }

    if($filename -ne ""){
        $results += $line
        $linecount++
        if(($linecount % 1000) -eq 0 ){
            $results | Add-Content $filename
            $results=@()
        }
    }
}

ユーザバックエンドがSQLのiredmailのSOGoでユーザがログインできない


qmail+vpopmailからパスワード暗号化文字列ごと移植したpostfix+dovecotベースの統合環境iredmail環境がある。

iredmailにはExchange互換サーバのSOGoもあるので、そちらでログインしようとしたらエラーになる。

roundcubeからだとログインができるし、dovecotを使うPOP3/IMAPアクセスでも問題無い。

/var/log/sogo/sogo.log へのエラーは下記の様になっていた。

Jul 04 13:15:04 sogod [9257]: SOGoRootPage Login from 'クライアントIPアドレス' for user 'ユーザ名@ドメイン名' might not have worked - password policy: 65535  grace: -1  expire: -1  bound: 0

iredmailのフォーラムに「Can’t configure password policy when using SOGo」といのがあり、sogo.confに「passwordPolicy = YES;」を追加すればいいじゃん?とあったのでやってみたが、変化はなし。

SOGo側のbug tracking system「 0003899: SQL authentication 」にてヒントを発見。

暗号化文字列の指定の問題のようだ。

今回、sogo.confは下記の様に「userPasswordAlgorithm = ssha512」となっており、パスワード暗号化文字列がssha512フォーマットである、ということになっている。

    SOGoUserSources = (
        {
            type = sql;
            id = users;
            viewURL = "mysql://sogo:~@127.0.0.1:3306/sogo/users";
            canAuthenticate = YES;

            // The algorithm used for password encryption when changing
            // passwords without Password Policies enabled.
            // Possible values are: plain, crypt, md5-crypt, ssha, ssha512.
            userPasswordAlgorithm = ssha512;
            prependPasswordScheme = YES;

            // Use `vmail.mailbox` as per-domain address book.
            isAddressBook = YES;
            displayName = "Domain Address Book";
            SOGoEnableDomainBasedUID = YES;
            DomainFieldName = "domain";
        },

しかし、今回、vpopmail時代の文字列「$1$5ulpxxxx$VS0xHxxKxMPBSIPQlXDXC/」という書式、つまりはmd5-cryptフォーマットを流用しているので認証できなかった、ということになる。

メインとなるiredmail側は新しくパスワードを設定した場合はssha512、移植したものはmd5-cryptという運用にしている。

ただ、postfix,dovecot,roundcubeの運用に関しては、ssha512でもmd5-cryptでも問題無くログインできている。

それに対してSOGo側は「SOGO Installation and Configuration Guide」を見ると userPasswordAlgorithm には1つの値しか指定はできないようだ。

セキュリティを考えると全体をmd5-cryptに下げる、という選択肢はとれないので、SOGoを使いたい場合は、パスワードを再設定する、ということになる。

パスワードを再設定すると以下のログとなる。

Jul 04 13:50:43 sogod [1094]: SOGoRootPage successful login from 'クライアントIPアドレス' for user 'ユーザ名@ドメイン名' - expire = -1  grace = -1

Arduino/ESP32/TTGO T-WatchのシステムクロックとRTC周りのメモ 2019/07/02


TTGO T-watchの腕時計の時刻表示がおかしいので調べていったことのメモ

・T-Watchでは画面オフ時にシステムクロックが止まる

T-watchでは左側の端っこボタンを押すと画面がオフになる。

この処理は、power_handle関数内の「LVGL_POWER_IRQ」イベントの「axp.isPEKShortPressIRQ()」( 左側の端っこボタン =PEKキー)で行われている。

オフ時に「rtc_clk_cpu_freq_set(RTC_CPU_FREQ_2M)」を実行して、CPU周波数を落としている(240M→2M)

これによりOS内のシステムクロックの進みも劇的に遅くなって、ほぼ止まっているように見える。

このことがあるので、T-Watchのソフトウェアでは、システムクロックからではなく、RTC上の時計から時刻を取得しているようだった。

・システムクロックの設定方法はsettimeofday(UNIX)

Arduino/ESP32環境でシステムクロックを簡単に設定する手法はNTPからの時刻を取得して、システムクロックを適用する、というもの。

    struct tm timeinfo;
    bool ret = false;
    int retry = 0;
    configTzTime("JST-9", "pool.ntp.org");
    do {
        ret = getLocalTime(&amp;timeinfo);
        if (!ret) {
            Serial.printf("get ntp fail,retry : %d \n", retry++);
        }
    } while (!ret &amp;&amp; retry < 3);

また、システムクロックをRTCに反映するのも簡単。(以下は、RTCモジュールがPCF8563の場合に使うPCF8563_Libraryの場合)

        rtc.setDateTime(timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

しかし、逆にRTCからシステムクロックに対して時刻を反映させる手法がよく分からない。

RTCモジュールがDS1307RTCの場合の事例があるので Michael MargolisさんによるTimeモジュールを使ってみたが、時刻設定はできなかった。

void syncSystemTimeByRtc()
{
    Serial.print("Read RTC :");
    Serial.println(rtc.formatDateTime(PCF_TIMEFORMAT_YYYY_MM_DD_H_M_S));
    struct tm dt;
    getLocalTime(&amp;dt);
    Serial.printf("getLocalTime is %d:%d:%d\n",dt.tm_hour,dt.tm_min,dt.tm_sec);
    RTC_Date d = rtc.getDateTime();
    Serial.printf("  %d,%d,%d,%d,%d,%d\n",d.hour,d.minute,d.second,d.day,d.month,d.year);
    setTime(d.hour,d.minute,d.second,d.day,d.month,d.year);
    Serial.println(timeStatus());
    getLocalTime(&amp;dt);
    Serial.printf("getLocalTime override RTC clock, %d:%d:%d\n",dt.tm_hour,dt.tm_min,dt.tm_sec);
   
}

上記のデバグコードをいれて、画面オフ→オンイベントを起こしてみると、

10:16:18.034 -> LVGL_POWER_IRQ event
10:16:18.034 -> 
10:16:18.034 -> PEKShortPressIRQ to off
10:31:15.803 -> PEKShortPressIRQ to on
10:31:15.803 -> bma423_disable:0
10:31:15.803 -> Read RTC :2019-7-2/10:31:15
10:31:15.835 -> getLocalTime is 10:16:9
10:31:15.835 ->   10,31,15,2,7,2019
10:31:15.835 -> 2
10:31:15.835 -> getLocalTime override RTC clock, 10:16:9
10:31:24.715 -> RTC time is 2019-7-2/10:31:24
10:31:24.715 -> getLocalTime is 10:16:17

時刻が設定されていない・・・

UNIXだとどうやってシステムクロック設定できたかな?と調べて見たらsettimeofdayでunixtimeを指定する、ということが判明

#include <time.h>    // requried for settimeofday 
#include <sys/time.h>// requried for timeval

上記を冒頭に追加した上で、以下を書いた。

void syncSystemTimeByRtc()
{
    Serial.print("Read RTC :");
    Serial.println(rtc.formatDateTime(PCF_TIMEFORMAT_YYYY_MM_DD_H_M_S));
    struct tm dt;
    getLocalTime(&amp;dt);
    Serial.printf("getLocalTime is %d:%d:%d\n",dt.tm_hour,dt.tm_min,dt.tm_sec);
    RTC_Date d = rtc.getDateTime();
    Serial.printf("  %d,%d,%d,%d,%d,%d\n",d.hour,d.minute,d.second,d.day,d.month,d.year);
    dt.tm_hour = d.hour;
    dt.tm_min  = d.minute;
    dt.tm_sec  = d.second;
    dt.tm_mday  = d.day;
    dt.tm_mon = d.month-1;
    dt.tm_year = d.year-1900; 
    time_t timertc = mktime(&amp;dt);
    Serial.print("RTC unixtime is ");
    Serial.print(timertc);
    Serial.print(" ,system unixtime is ");
    time_t timesys = time(NULL);
    Serial.println(timesys);
    struct timeval tv ={
      .tv_sec = timertc
    };
    settimeofday(&amp;tv,NULL);
    
    getLocalTime(&amp;dt);
    Serial.printf("getLocalTime override RTC clock, %d:%d:%d\n",dt.tm_hour,dt.tm_min,dt.tm_sec);
   
}

これで期待通りにRTCの時刻をシステムクロックに反映することができるようになった。

13:23:48.103 -> PEKShortPressIRQ to on
13:23:48.103 -> bma423_disable:0
13:23:48.103 -> Read RTC :2019-7-2/13:23:47
13:23:48.103 -> getLocalTime is 13:17:30
13:23:48.103 ->   13,23,47,2,7,2019
13:23:48.103 -> RTC unixtime is 1562041427 ,system unixtime is 1562041050
13:23:48.103 -> getLocalTime override RTC clock, 13:23:47
13:23:57.121 -> RTC time is 2019-7-2/13:23:56
13:23:57.121 -> getLocalTime is 13:23:56

・rtc_cpu_freq_setは非推奨

コンパイル中に以下の警告が・・・

C:\Users\osakanataro\Documents\Arduino\TTGO-T-Watch-mod\TTGO-T-Watch-mod.ino:328:17: warning: 'void rtc_clk_cpu_freq_set(rtc_cpu_freq_t)' is deprecated [-Wdeprecated-declarations]

                 rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);

https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/rtc.h 」を確認したところ、「 rtc_clk_cpu_freq_config_set 」に置き換わった、とある。

・・・あるんだけど、 rtc_clk_cpu_freq_config_set に関する資料がでてこないってどういうこと???

grepしても定義無いし、試しに置き換えてみても定義されてない、というエラーになった。

ESP32腕時計TTGO T-Watchが届いた


ESP32を搭載した腕時計風のTTGO T-Watchを約49ドルで購入しました。

約2週間で届いたので早速遊んでみました。

資料系

メーカページ(LILYGO)
公式英語マニュアル
公式中国語マニュアル
サンプルソフトウェア

開梱

かわいらしい感じの梱包ですね。

内容物はこちら

T-Watch本体と、予備のボード?、T-Watchの横にあるコネクタから拡張するためのケーブル、Type-Cケーブル、腕時計用バンド、裏蓋取り外し用ネジ回しと予備のネジ

説明書には中を開けた時にどういう感じになっているのか書かれています。

とりあえずこちらも開けてみました。

元に戻して電源ON

最初はタッチパネルの検査プログラムが起動しタッチした座標を報告します、Type-Cコネクタの隣のボタンを押すと、今度は衝撃検知プログラムが起動しstepCountとして振り回した回数を表示します。

とりあえず、パソコンにつなげるとドライバがあれば「Silicon Labs CP210x USB to UART Bridge (COM?)」として認識します。

ドライバがインストールされていない場合は下記の様に「CP2104 USB to UART Bridge Controller」として認識されます。ドライバのインストールについては後述します。

デモ用のプログラムがgithubで公開されていますのでArduio IDEをセットアップします。

ただし、普通にインストールしただけでは認識せず、追加手順が必要となります。

必要なもの

Arduino IDE
git for Windows

T-watch ソフトウェア書き込み手順

TTGO公式の設定手順を参照しながらやりました。

その1:git for Windowsをインストール

以前に別の件でインストールしていたのでそのまま使っています。

その2:Arduino IDEをインストール

今回はArduino IDE ver 1.8.9をインストールしました。

その3: Arduino IDEのインストール先のhardwareディレクトリ内にespressifディレクトリを作る

標準インストールだとC:\Program Files (x86)\Arduino\hardware ディレクトリ内にespressifディレクトリを作る。

その4:作成したespressifディレクトリ内でコマンドプロンプトを開く

作成したC:\Program Files (x86)\Arduino\hardware\espressifディレクトリででコマンドプロンプトを開きます。

その5:gitでespressifのarduino-esp32レポジトリを取得する

espressifディレクトリで「git clone –recursive https://github.com/espressif/arduino-esp32.git esp32」(wordpress編集画面だと「-」が2個なのに1個だけになる・・・)を実行します。

そうすると、 C:\Program Files (x86)\Arduino\hardware\espressif\esp32 ディレクトリが作成されファイルが置かれていきます。

その6:esp32\toolsディレクトリにあるgetコマンドを実行し追加ファイルを取得

C:\Program Files (x86)\Arduino\hardware\espressif\esp32\tools\ ディレクトリ内にある「get.exe」を実行し、追加ファイルを取得します。

その7:Silicon Labs CP210xをインストール/アップデート

手順には明記されていないのですが、古いドライバ Ver6.7.4.261で書き込もうとするとエラーになりました。「T-WatchマニュアルのGetting started」に掲載されているCP2104-Win10(Ver10.1.8.2466)だと正常に動きますので、アップデートします。
(CP2104の製造メーカ公式ドライバダウンロードページ)

なお、書き込み時のエラーは下記の「A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header」です。

その8:T-Watchをパソコンに接続

その9:Arduino IDEを起動し、TTGO T-Watchを選択

Arduino IDEを起動し、「ツール」-「ボード」の一覧から「TTGO T-Watch」を選択します。

その10:T-Watchサンプルソフトを取得

git for Windowsを使って、 https://github.com/Xinyuan-LilyGO/TTGO-T-Watch で公開されているサンプルソフトウェアを取得します。

「git clone –recursive https://github.com/Xinyuan-LilyGO/TTGO-T-Watch」

取得完了後、「library」にあるディレクトリを、マイドキュメントの中にある「Arduino\library」内にコピーします。

その11:書き込み

Arduino IDEからサンプルソフトウェアを書き込みます。


サンプルソフトウェアを書き込んだのですが、うまいこと自宅のWiFiに接続できず、時計合わせに失敗しています。

また、タッチパネルの動作が若干微妙な感じもあります・・・