From 64e2cf069b5dc415d9497652503d46f4b2d1a2c5 Mon Sep 17 00:00:00 2001 From: gaolifeng Date: Fri, 14 Oct 2022 12:25:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5truenasseeker=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E5=92=8C=E5=90=AF=E5=8A=A8=E9=A1=BA=E5=BA=8F=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=AB=A0=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- ...truenasseeker.py脚本自动扫描nfs服务并挂载.md | 40 ++++++ .../设置docker-compose等待nfs挂载后再启动.md | 2 + files/truenasseeker.py | 120 ++++++++++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 details/使用truenasseeker.py脚本自动扫描nfs服务并挂载.md create mode 100644 files/truenasseeker.py diff --git a/README.md b/README.md index 8edbbd0..36116c8 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,8 @@ TrueNas: 服务主机ubuntu: - [使用setup_aquar.sh脚本进行环境初始化](./details/使用setup_aquar.sh脚本进行环境初始化) -- 设置docker-compose等待nfs挂载后再启动 +- [设置docker-compose等待nfs挂载后再启动](./details/设置docker-compose等待nfs挂载后再启动.md) +- [使用truenasseeker.py脚本自动扫描nfs服务并挂载](./details/使用truenasseeker.py脚本自动扫描nfs服务并挂载.md) Windows虚拟机: diff --git a/details/使用truenasseeker.py脚本自动扫描nfs服务并挂载.md b/details/使用truenasseeker.py脚本自动扫描nfs服务并挂载.md new file mode 100644 index 0000000..214722f --- /dev/null +++ b/details/使用truenasseeker.py脚本自动扫描nfs服务并挂载.md @@ -0,0 +1,40 @@ +如果你遇到了搬家或者更换路由器的情况,你网络中所有的设备都会失去原有的IP地址,这会导致Aquar中各个子系统间原本建立起来的关联会全部失效。 + +truenasseeker.py脚本就是用来解决这个问题的。它在每次开机时会检查系统是否有效地挂载了`/opt/aquar/storages/aquarpool`目录,如果这个目录没有正常挂载,则会载局域网中扫描truenas主机,并修改ubuntu的nfs配置为最最新地址,然后重新挂载这个这个地址。 + +如果你已经部署了truenasseeker.py但在网络变动后发现docker应用中的存储池数据仍然不见了,你可以在ubuntu控制台上以超级用户执行一次`aqserv restart`命令,或者干脆重启一次机器,再看存储池内容是否恢复了。 + +1.`sudo -i`切换超级用户。 + +2.安装依赖的软件包。 + +```shell +apt install -y nmap +pip install scapy python-nmap pyfunctional +``` + +3.将truenasseeker.py脚本拷贝至`/opt/aquar/src`路径下。 + +你可以尝试执行`python3 /opt/aquar/src/truenasseeker.py`命令运行一次看是否会出错,正常情况下它会输出"truenas seeker is no need to do anything." + +4.创建`/usr/lib/systemd/system/trunas-scan.service`配置文件,配置内容如下: + +``` +# [Unit] +# Description=scan local network for truenas +# After=docker.service opt-aquar-storages-aquarpool.mount +# +# [Service] +# Type=simple +# User=root +# ExecStart=python3 /opt/aquar/src/truenasseeker.py +# +# [Install] +# WantedBy=multi-user.target +``` + +5.执行`systemctl daemon-reload`重载系统服务。 + +6.执行`systemctl enable trunas-scan.service`将脚本设置为开机启动。 + +7.执行`systemctl status trunas-scan.service`查看服务状态是否为enabled。如果是,则代表脚本正常部署了。 \ No newline at end of file diff --git a/details/设置docker-compose等待nfs挂载后再启动.md b/details/设置docker-compose等待nfs挂载后再启动.md index 795d5ee..3fd1398 100644 --- a/details/设置docker-compose等待nfs挂载后再启动.md +++ b/details/设置docker-compose等待nfs挂载后再启动.md @@ -1,5 +1,7 @@ 设置docker-compose等待nfs挂载后再启动 +默认情况下docker-compose中的服务和nfs挂载动作的执行是不区分先后顺序的,这会导致开机以后docker服务已经启动但nfs挂载点未就绪,于是在docker应用中就无法正常的看到存储池中的文件了。所以这一步是必不可少的。 + 1.执行`sudo -i`进入管理员角色 2.执行`systemctl list-units | grep mount`寻找与fstab中配置对应的信息 diff --git a/files/truenasseeker.py b/files/truenasseeker.py new file mode 100644 index 0000000..4c6c7a6 --- /dev/null +++ b/files/truenasseeker.py @@ -0,0 +1,120 @@ +#! /usr/bin/env python +# vim: set fenc=utf8 ts=4 sw=4 et : +# apt install -y nmap +# pip install scapy python-nmap pyfunctional +# -------/usr/lib/systemd/system/trunas-scan.service-------- +# [Unit] +# Description=scan local network for truenas +# After=docker.service opt-aquar-storages-aquarpool.mount +# +# [Service] +# Type=simple +# User=root +# ExecStart=python3 /opt/aquar/src/truenasseeker.py +# +# [Install] +# WantedBy=multi-user.target + + +from __future__ import absolute_import, division, print_function +import logging +import scapy.config +import scapy.layers.l2 +import scapy.route +import math +import os +import getopt +import sys +import shutil +import re +import nmap +from functional import seq + +logging.basicConfig(format='%(asctime)s %(levelname)-5s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG) +logger = logging.getLogger(__name__) +nm = nmap.PortScanner() + +def long2net(arg): + if (arg <= 0 or arg >= 0xFFFFFFFF): + raise ValueError("illegal netmask value", hex(arg)) + return 32 - int(round(math.log(0xFFFFFFFF - arg, 2))) + + +def to_CIDR_notation(bytes_network, bytes_netmask): + network = scapy.utils.ltoa(bytes_network) + netmask = long2net(bytes_netmask) + net = "%s/%s" % (network, netmask) + if netmask < 16: + logger.warning("%s is too big. skipping" % net) + return None + + return net + + +def scan(): + if os.geteuid() != 0: + print('You need to be root to run this script', file=sys.stderr) + sys.exit(1) + + for network, netmask, _, interface, address, _ in scapy.config.conf.route.routes: + + # skip loopback network and default gw + if network == 0 or interface == 'lo' or address == '127.0.0.1' or address == '0.0.0.0': + continue + + if netmask <= 0 or netmask == 0xFFFFFFFF: + continue + + # skip docker interface + if interface.startswith('docker') or interface.startswith('br-') or interface.startswith('tun'): + logger.warning("Skipping interface '%s'" % interface) + continue + + net = to_CIDR_notation(network, netmask) + + nfsAddress = None + if net: + ipInfo = nm.scan(hosts=net, arguments='-p 22,80,111,139,443,445,8006,8008,8081,8384') + resList = seq(list(ipInfo['scan'].items())).map(lambda i: (i[0],seq(list(i[1]['tcp'].items())).filter(lambda p: p[1]['state'] == "open").map(lambda pi: pi[0] ))) + for res in resList: + logger.info("%s: %s" % (res[0],res[1])) + if 22 not in res[1] and 80 in res[1] and 111 in res[1] and 139 in res[1] and 443 in res[1] and 445 in res[1]: + nfsAddress = res[0] + + return nfsAddress + +def checknfs(): + return os.path.ismount('/opt/aquar/storages/aquarpool') + + +def updatefstab(nfsAdress): + shutil.copy('/etc/fstab', '/etc/fstab.bak') + fstabFile = open("/etc/fstab", "r+") + + configText = fstabFile.read() + prepart = re.split("##\[aquar config start\]##", configText)[0] + postpart = re.split("##\[aquar config end\]##", configText)[1] + nfsConfig = "\n##[aquar config start]##\n %s:/mnt/aquarpool /opt/aquar/storages/aquarpool nfs defaults,_netdev 0 0\n##[aquar config end]##\n" % nfsAdress + logger.info("new nfs config:") + logger.info(nfsConfig) + newConifg = prepart + nfsConfig+ postpart + logger.info("fstab now is:") + logger.info(newConifg) + + fstabFile.seek(0) + fstabFile.write(newConifg) + fstabFile.truncate() + fstabFile.close() + + +if __name__ == "__main__": + print('truenas seeker starting.') + if not checknfs(): + print('truenas seeker find nfs directory is broken, trying to find truenas in local network.') + nfsAddress = scan() + updatefstab(nfsAddress) + print('truenas seeker updated fstab succesfully.') + os.system("mount -a") + print('truenas seeker has remounted fstab.') + else: + print('truenas seeker is no need to do anything.') \ No newline at end of file