Есть ли хороший способ обнаружить устаревшее монтирование NFS
У меня есть процедура, которую я хочу инициировать, только если несколько тестов успешно завершены.
Один тест, который мне нужен, это то, что все мои монстры NFS живы и хорошо.
Могу ли я сделать лучше, чем метод грубой силы:
mount | sed -n "s/^.* on \(.*\) type nfs .*$/\1/p" |
while read mount_point ; do
timeout 10 ls $mount_point >& /dev/null || echo "stale $mount_point" ;
done
Здесь timeout
- это утилита, которая будет запускать команду в фоновом режиме и будет убивать ее по истечении заданного времени, если не было обнаружено SIGCHLD
до истечения срока, что приведет к успешному завершению/неудаче.
На английском языке: проанализируйте вывод mount
, проверьте (ограниченное таймаутом) каждую точку монтирования NFS. Необязательно (не в коде выше), разбивающемся на первом устаревшем держателе.
Ответы
Ответ 1
Вы можете написать программу на C и проверить ESTALE
.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iso646.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
struct stat st;
int ret;
ret = stat("/mnt/some_stale", &st);
if(ret == -1 and errno == ESTALE){
printf("/mnt/some_stale is stale\n");
return EXIT_SUCCESS;
} else {
return EXIT_FAILURE;
}
}
Ответ 2
Мой коллега столкнулся с вашим script. Это не исключает подход "грубой силы", но если я могу в Bash:
while read _ _ mount _; do
read -t1 < <(stat -t "$mount") || echo "$mount timeout";
done < <(mount -t nfs)
mount
может напрямую указывать монтировки NFS. read -t
(встроенная оболочка) может отключить команду. stat -t
(точный вывод) по-прежнему висит как ls
*. ls
дает ненужный вывод, подвергает риску ложные срабатывания в списках огромных/медленных каталогов и требует разрешений на доступ - что также вызывает ложное срабатывание, если оно не имеет их.
while read _ _ mount _; do
read -t1 < <(stat -t "$mount") || lsof -b 2>/dev/null|grep "$mount";
done < <(mount -t nfs)
Мы используем его с lsof -b
(неблокирующий, поэтому он тоже не будет висеть), чтобы определить источник зависаний.
Спасибо за указатель!
-
test -d
(встроенная оболочка) будет работать вместо stat
(стандартный внешний), но read -t
возвращает успех только в том случае, если он не тайм-аут и не читает строку ввода. Поскольку test -d
не использует stdout, для него потребуется проверка уровня ошибок (( $? > 128 ))
, это не стоит того, чтобы попасть в текст, IMO.
Ответ 3
Пришло время, но вот что я нашел, что работает в Python:
import signal, os, subprocess
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
pathToNFSMount = '/mnt/server1/' # or you can implement some function
# to find all the mounts...
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(3) # 3 seconds
try:
proc = subprocess.call('stat '+pathToNFSMount, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
stdoutdata, stderrdata = proc.communicate()
signal.alarm(0) # reset the alarm
except Alarm:
print "Oops, taking too long!"
Примечания:
проверьте, закончилась ли вилка, если она истекло, вы можете ее убить. Вам нужно будет работать с time.time()
и так далее.
Ответ 4
Написание C-программы, которая проверяет ESTALE, является хорошим вариантом, если вы не возражаете ждать завершения команды из-за устаревшей файловой системы. Если вы хотите реализовать параметр "тайм-аут", лучшим способом, который я нашел для его реализации (в программе на C), является разветвление дочернего процесса, который пытается открыть файл. Затем вы проверяете, завершил ли дочерний процесс успешное чтение файла в файловой системе за выделенный промежуток времени.
Вот небольшое доказательство программы C для этого:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
void readFile();
void waitForChild(int pid);
int main(int argc, char *argv[])
{
int pid;
pid = fork();
if(pid == 0) {
// Child process.
readFile();
}
else if(pid > 0) {
// Parent process.
waitForChild(pid);
}
else {
// Error
perror("Fork");
exit(1);
}
return 0;
}
void waitForChild(int child_pid)
{
int timeout = 2; // 2 seconds timeout.
int status;
int pid;
while(timeout != 0) {
pid = waitpid(child_pid, &status, WNOHANG);
if(pid == 0) {
// Still waiting for a child.
sleep(1);
timeout--;
}
else if(pid == -1) {
// Error
perror("waitpid()");
exit(1);
}
else {
// The child exited.
if(WIFEXITED(status)) {
// Child was able to call exit().
if(WEXITSTATUS(status) == 0) {
printf("File read successfully!\n");
return;
}
}
printf("File NOT read successfully.\n");
return;
}
}
// The child did not finish and the timeout was hit.
kill(child_pid, 9);
printf("Timeout reading the file!\n");
}
void readFile()
{
int fd;
fd = open("/path/to/a/file", O_RDWR);
if(fd == -1) {
// Error
perror("open()");
exit(1);
}
else {
close(fd);
exit(0);
}
}
Ответ 5
Другой способ, используя оболочку script. Работает хорошо для меня:
#!/bin/bash
# Purpose:
# Detect Stale File handle and remove it
# Script created: July 29, 2015 by Birgit Ducarroz
# Last modification: --
#
# Detect Stale file handle and write output into a variable and then into a file
mounts=`df 2>&1 | grep 'Stale file handle' |awk '{print ""$2"" }' > NFS_stales.txt`
# Remove : ‘ and ’ characters from the output
sed -r -i 's/://' NFS_stales.txt && sed -r -i 's/‘//' NFS_stales.txt && sed -r -i 's/’//' NFS_stales.txt
# Not used: replace space by a new line
# stales=`cat NFS_stales.txt && sed -r -i ':a;N;$!ba;s/ /\n /g' NFS_stales.txt`
# read NFS_stales.txt output file line by line then unmount stale by stale.
# IFS='' (or IFS=) prevents leading/trailing whitespace from being trimmed.
# -r prevents backslash escapes from being interpreted.
# || [[ -n $line ]] prevents the last line from being ignored if it doesn't end with a \n (since read returns a non-zero exit code when it encounters EOF).
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Unmounting due to NFS Stale file handle: $line"
umount -fl $line
done < "NFS_stales.txt"
#EOF
Ответ 6
Я написал https://github.com/acdha/mountstatus, в котором используется подход, похожий на то, что упоминалось в UndeadKernel, которое я нашел наиболее надежным подходом: it демон, который периодически сканирует все смонтированные файловые системы, разворачивая дочерний процесс, который пытается перечислить каталог верхнего уровня и SIGKILL
, если он не отвечает в определенный тайм-аут, причем как успехи, так и неудачи записываются в syslog. Это позволяет избежать проблем с некоторыми реализациями клиента (например, старая Linux), которая никогда не запускает тайм-ауты для определенных классов ошибок, серверы NFS, которые частично реагируют, но, например, не будет отвечать на фактические вызовы, такие как listdir
и т.д.
Я не публикую их, но включенный Makefile использует fpm
для сборки rpm и deb пакетов с помощью Upstart script.