代码之家  ›  专栏  ›  技术社区  ›  Chen Levy

是否有一种检测过时NFS装载的好方法

  •  18
  • Chen Levy  · 技术社区  · 15 年前

    我需要的一个测试是,我的所有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 回复  |  直到 15 年前
        1
  •  5
  •   Teddy    12 年前

    您可以编写一个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
  •  8
  •   Chen Levy    12 年前

    我的一个同事偶然发现了你的剧本。这并不能避免“暴力”方法,但如果我可以在Bash中:

    while read _ _ mount _; do 
      read -t1 < <(stat -t "$mount") || echo "$mount timeout"; 
    done < <(mount -t nfs)
    

    mount 可以直接列出NFS装载。 read -t (shell内置)可以使命令超时。 stat -t (简洁的输出)仍然像 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 (( $? > 128 )) 对其进行错误级别检查是必要的-不值得进行易读性检查,IMO。
        3
  •  7
  •   Community Fabien Hure    4 年前

    我花了一些时间,但以下是我发现的在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. 归功于 answer here .

    2. 您也可以使用替代方案:

      os.fork() os.stat()

    time.time() 等等

        4
  •  6
  •   Chen Levy    7 年前

    除了之前的答案(在某些情况下会挂起)之外,此代码段还检查所有合适的挂载、使用信号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
    
        5
  •  3
  •   UndeadKernel    11 年前

    下面是一个小的概念验证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);
      }
    }
    
        6
  •  3
  •   Chris Adams    8 年前

    https://github.com/acdha/mountstatus 它使用了一种类似于UndeadKernel提到的方法,我发现这是最健壮的方法:它是一个守护进程,通过分叉一个子进程,尝试列出顶级目录和 SIGKILL 如果它在某个超时时间内没有响应,则会将成功和失败记录到syslog中。这避免了某些客户端实现(例如旧版Linux)的问题,这些客户端实现不会触发某些类错误的超时,NFS服务器部分响应,但不会响应实际调用,如 listdir

    我不发布它们,但包含的Makefile使用 fpm 使用Upstart脚本构建rpm和deb包。

        7
  •  1
  •   Birgit Ducarroz    9 年前

    另一种方法是使用shell脚本。对我有好处:

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