Есть ли хороший способ обнаружить устаревшее монтирование NFS - PullRequest
18 голосов
/ 29 октября 2009

У меня есть процедура, которую я хочу запустить только в случае успешного завершения нескольких тестов.

Один тест, который мне нужен, заключается в том, что все мои крепления 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. Необязательно (не в приведенном выше коде) разрыв первого монтированного монтирования.

Ответы [ 7 ]

7 голосов
/ 24 августа 2011

Мой коллега столкнулся с вашим сценарием. Это не избегает подхода "грубой силы", но, если можно, в 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 не использует стандартный вывод, потребуется (( $? > 128 )) проверка уровня ошибки - не стоит попадания в разборчивость, ИМО.
6 голосов
/ 04 сентября 2012

Мне потребовалось некоторое время, но вот что я нашел, что работает в 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!"

Примечания:

  1. кредит на ответ здесь .
  2. Вы также можете использовать альтернативную схему:

    os.fork() и os.stat()

проверьте, не закончена ли вилка, истекло ли время, и вы можете ее убить. Вам нужно будет работать с time.time() и так далее.

5 голосов
/ 14 февраля 2018

В дополнение к предыдущим ответам, которые зависают при некоторых обстоятельствах, этот фрагмент проверяет все подходящие монтирования, убивает с помощью сигнала KILL, а также тестируется с CIFS:

grep -v tracefs /proc/mounts | cut -d' ' -f2 | \
  while read m; do \
    timeout --signal=KILL 1 ls -d $m > /dev/null || echo "$m"; \
  done
4 голосов
/ 29 октября 2009

Вы можете написать программу на 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;
    }
}
3 голосов
/ 05 апреля 2016

Я написал https://github.com/acdha/mountstatus, который использует подход, аналогичный тому, который упоминал UndeadKernel, который я считаю наиболее надежным подходом: это демон, который периодически сканирует все смонтированные файловые системы путем разветвления дочернего процесса, который пытаетсяперечислите каталог верхнего уровня и SIGKILL, если он не отвечает в течение определенного времени ожидания, а в системный журнал записываются как успехи, так и ошибки.Это позволяет избежать проблем с определенными реализациями клиентов (например, в старых Linux), которые никогда не запускают таймауты для определенных классов ошибок, NFS-серверы, которые частично реагируют, но, например, не отвечают на реальные вызовы, такие как listdir и т. Д.Я не публикую их, но включенный Makefile использует fpm для сборки пакетов rpm и deb с помощью сценария Upstart.

3 голосов
/ 03 сентября 2013

Написание программы на 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);
  }
}
1 голос
/ 29 июля 2015

Другой способ, используя сценарий оболочки. Хорошо работает для меня:

#!/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
...