我是一名学术医学物理学家。我做的实验会产生大量数据,但运行起来很昂贵。我的大学有一个备份系统,由位于废弃盐矿中的机器人磁带库组成,该系统使用 IBM 的频谱保护(调用为dsmc
)我用于异地备份。虽然我可以发送到盐矿的总大小没有限制,但是是每天传输限制为 200 GB。据我所知,没有办法让 Spectrum Protect 客户端遵守此限制,并在达到传输限制后停止。
如果突破了这个限制,服务器就会锁定该节点,我必须向某人发送一封充满歉意的电子邮件,要求他们解锁它。他们因为我使用了太多带宽而责备我,并且大约 24-48 小时后,解锁节点。
为了解决我以离散块(在实验日)创建数据并且每月或每周都远低于带宽限制的事实,我编写了一个简单的包装器脚本来解析dsmc
和终止的输出如果传输太大。
解析是通过dsmc
使用简单的 python 脚本将 bash 中的输出视为此处文档来完成的:
#!/bin/bash
# A silly wrapper script to halt TSM backups
#
# Usage: sudo /path/to/script /path/to/backup/location
#
# Requires python3 accessible as python3, and the regex / os modules.
# Tested on MacOS and Linux
BYTES_SENT=0;
#MAX_SIZE_TO_SEND=150 #Bytes, for testing
MAX_SIZE_TO_SEND=$[185*(2**30)]
args=("$@")
sudo rm -f /tmp/dsmc-script.PID
function outputParser() {
python3 <<'EOF'
import os, re
rex=re.compile(r"Normal File\-\-\>\s*?([,0-9]*,?)\s*?\/")
valueToParse=os.environ.get('line');
match=rex.match(valueToParse);
try:
stringToReturn = str(match.group(1));
stringToReturn =stringToReturn.replace(',','');
except AttributeError:
stringToReturn = "";
#Check for failed transfers
failedResults = re.findall(r"\*\* Unsuccessful \*\*", valueToParse);
nFailedResults = len(failedResults);
if (nFailedResults >0):
stringToReturn = "";
print(stringToReturn);
EOF
} #I am sure that the above is a one-liner in sed or awk. I just don't know what the one line is.
function trapCaught() {
#Do cleanup, not shown
echo ", quitting."
}
trap trapCaught sigint
killCount=0
startTime=$SECONDS
while read -r line; do
echo "$line"
export line;
X=$(export line=$line; outputParser)
if [[ ! -z "$X" ]]; then
BYTES_SENT=$[$BYTES_SENT + $X]
echo "Sent $X bytes, $BYTES_SENT in total"
fi
if (( BYTES_SENT > MAX_SIZE_TO_SEND )); then
if (( killCount < 1)); then
echo "STOPPED BACKUP BECAUSE $BYTES_SENT is GREATER THAN THE PERMITTED MAXIMUM OF $MAX_SIZE_TO_SEND";
killStartTime=$(( SECONDS - startTime ))
pid=$(cat /tmp/dsmc-script.PID)
echo "PID is $pid"
echo $pid | sudo xargs kill
fi
killCount=$[$killCount + 1];
timeKillNow=$(( SECONDS - killStartTime ))
rm -f /tmp/dsmc-script.PID
if (( killCount > 100 || timeKillNow > 30 )); then
echo "Taking too long to die; retrying"
echo $pid | sudo xargs kill -9;
sleep 0.1;
sudo kill -9 0;
fi
fi
done < <( sudo dsmc incr ${args[0]} & echo $! > /tmp/dsmc-script.PID )
这有效,并且适合我的目的。然而,性能很差,接近可怕,我认为这是因为循环的每次迭代while
都会产生 python 解释器/脚本组合的另一个实例。
鉴于我无法更改限制或二进制编译的 blob 的行为dsmc
,我有三个相关问题:
(a)这是解决这个问题的明智方法,还是有我缺少的更简单的方法,例如高级巫术netstat
?
(b) 鉴于 python实际上确实如此在循环中的每次迭代中本质上是完全相同的,有没有一种方法可以缓存解释器对代码的翻译,从而极大地加快整个过程?
(c) 如果我用等效的sed
或awk
构造替换 python 脚本,我怀疑整个事情会快得多。为什么?是否可以轻松地进行这种类型的算术,或者这是另一个转移注意力的事情?
编辑:对于那些不熟悉的人来说,示例输出dsmc
如下 - 仅当“普通文件”出现在字符串中时才会发送文件,后跟其大小(以字节为单位)。因此,在下面,文件spclicert.kdb
被发送,但既不发送TSM.PWD
,也不发送目录CaptiveNetworkSupport
:
# dsmc incr /
< header message containing personal information>
Incremental backup of volume '/'
ANS1898I ***** Processed 79,000 files *****
Directory--> 0 /Library/Preferences/SystemConfiguration/CaptiveNetworkSupport [Sent]
Normal File--> 5,080 /Library/Preferences/Tivoli Storage Manager/Nodes/SHUG2765-MACBOOKPRO-PHYSICS/spclicert.kdb [Sent]
Updating--> 224 /Library/Preferences/Tivoli Storage Manager/BrokenOrOld/TSM.PWD (original) [Sent]
因此,上面的脚本去掉了发送的每个文件的大小(以字节为单位),然后简单地将它们相加。
答案1
假设连接是可靠的,一个简单的办法就是使用用户空间流量整形器。只需将其设置为每天使用的带宽不超过最大即可。
l=$(( (200*10**6)/(24*60**2) ))
trickle -d $l scp foo username@remotehost:~/
并且trickle
会减慢传输速度2314K每秒,最高速度不超过199,929,600,000每天字节。文件传输程序不必是scp
,它可以是任何东西(甚至是网络浏览器),(或dsmc
),只是从命令行启动。
此方法的优点是无需分解文件富如果大于每日限额。当然发送需要一段时间富结束,(如果富如果是 1TB,则需要 5 天),但无论如何也需要那么长时间。
trickle
有一个名为 的守护程序版本trickled
,它控制“trickle”的每次后续运行。例子:
l=$(( (200*10**6)/(24*60**2) ))
trickled -d $l
trickle scp foo username@remotehost:~/ &
trickle scp bar username@remotehost:~/ &
trickle scp baz username@remotehost:~/ &
假设每个文件富,酒吧, 和巴兹是1TB在规模上,trickled
仍将转移保持在200GB/天限制。
答案2
您的输入可以完全在bash
.这是一个示例:
max=$[185*(2**30)]
export total=0
while read first second third rest; do
[[ "$first" == "Normal" && "$second" == "File-->" ]] && {
size=${third//,/}
echo "file: $size"
total=$(( total + size ))
(( total > max )) && kill something
}
done < ~/tmp/your-input
如果您确实受到生成子进程所需时间的限制,那么这甚至可以避免调用awk
或的开销sed
。