我是一名学术医学物理学家。我做的实验会产生大量数据,而且运行起来很昂贵。我的大学有一个备份系统,它由一个废弃盐矿中的机器人磁带库组成,它使用 IBM 的Spectrum Protect(调用为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]
所以,上面的脚本去掉了每个发送文件的字节大小,然后简单地将它们相加。