2009-10-29 10 views
15

Tengo un procedimiento que deseo iniciar solo si varias pruebas se completan correctamente.¿Hay una buena manera de detectar un montaje obsoleto de NFS?

Una prueba que necesito es que todos mis soportes NFS estén vivos y en buen estado.

¿Puedo hacer algo mejor que el método de fuerza bruta:


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 

Aquí timeout es una utilidad que va a ejecutar el comando en el fondo, y matará a él después de un tiempo determinado, si no hay SIGCHLD fue capturado antes del límite de tiempo, regresando el éxito/fracaso de la manera obvia.


En Inglés: analizar la salida de mount, cheque (delimitada por un tiempo de espera) de cada punto de montaje NFS. Opcionalmente (no en el código anterior) rompiendo el primer montaje obsoleto.

Respuesta

3

Puede escribir un programa C y buscar 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; 
    } 
} 
+0

De 'man 3 errno': ESTALE Reservados . ¿Esto significa que debería buscar otra solución? –

+0

Supongo que depende de tu kernel. – Teddy

+0

Sí, tiene razón: en una versión posterior de mi distribución 'man 3 errno' decir:" 'ESTALE' Manejador de archivo añejo (POSIX.1)) Este error puede ocurrir para NFS y para otros sistemas de archivos". Y aunque fui con el enfoque de la fuerza bruta, descrito en mi pregunta, aceptaré esta respuesta. –

7

Un colega mío se encontró con el guión. Esto no evita un enfoque de "fuerza bruta", pero si me lo permite, en Bash:

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

mount puede enumerar NFS monta directamente. read -t (un shell incorporado) puede caducar un comando. stat -t (salida breve) todavía se cuelga como ls *. ls produce resultados innecesarios, arriesga falsos positivos en listas de directorios enormes/lentos y requiere permisos de acceso, lo que también activaría un falso positivo si no los tiene.

while read _ _ mount _; do 
    read -t1 < <(stat -t "$mount") || lsof -b 2>/dev/null|grep "$mount"; 
done < <(mount -t nfs) 

Estamos utilizando con lsof -b (sin bloqueo, por lo que no se colgará también) con el fin de determinar el origen de los bloqueos.

¡Gracias por el puntero!

  • test -d (una orden interna del shell) funcionaría en lugar de stat (un estándar externo) también, pero read -t rendimientos éxito sólo si no tiene tiempo de espera y lee una línea de entrada. Dado que test -d no utiliza stdout, es necesaria una comprobación de nivel de error de (($? > 128)) - no vale la pena el impacto de legibilidad, IMO.
+0

mientras que el último ejemplo permite el comando (sin colgar) en la estadística) el lsof -b 2 simplemente parece omitir todas las pruebas estadísticas y no devuelve nada. –

+0

Como ya sabe, '<(...)' se ejecuta en un subconjunto, y si 'stat (1)' cuelga debido a un NFS obsoleto, el subconjunto no terminará correctamente. Ver [check-nfs.sh] (https://gist.github.com/cinsk/840ed553905cb6e8f0ae) para la mejora de esto. – cinsk

5

Me llevó algún tiempo, pero aquí es lo que he encontrado que funciona en 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!" 

Observaciones:

  1. de crédito por el answer here.
  2. También es posible usar esquema alternativo:

    os.fork() y os.stat()

cheque si el tenedor terminado, si se ha agotado el tiempo se puede matar. Deberá trabajar con time.time(), etc.

3

Escribir un programa en C que compruebe si ESTALE es una buena opción si no le importa esperar a que termine el comando debido al sistema de archivos obsoleto. Si desea implementar una opción de "tiempo de espera excedido", la mejor forma que he encontrado para implementarlo (en un programa C) es dividir un proceso secundario que intente abrir el archivo. A continuación, verifica si el proceso hijo ha terminado de leer correctamente un archivo en el sistema de archivos dentro de un período de tiempo asignado.

Aquí es un pequeño programa de prueba de concepto C para hacer esto:

#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

Otra forma, utilizando script de shell. Funciona bien para mí:

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

escribí https://github.com/acdha/mountstatus que utiliza un enfoque similar a lo que UndeadKernel mencionado, que he encontrado para ser el enfoque más robusto: es un demonio que analiza periódicamente todos los sistemas de ficheros montados al bifurcar un niño proceso que intenta listar el directorio de nivel superior y SIGKILL si no responde en un tiempo de espera determinado, con éxitos y fallas registradas en syslog. Eso evita problemas con ciertas implementaciones de clientes (por ejemplo, Linux anterior) que nunca activan tiempos de espera para ciertas clases de errores, servidores NFS que son parcialmente receptivos, pero p. no responderá a llamadas reales como listdir, etc.

No las publico pero el Makefile incluido usa fpm para compilar paquetes rpm y deb con un script Upstart.

+0

Junto con portar el proyecto a Rust y agregar algunas otras características, ahora hay paquetes de deb y rpm: https://github.com/acdha/mountstatus/releases –

1

Además de las respuestas anteriores, que cuelga en algunas circunstancias, este fragmento comprueba todos los montajes adecuados, mata con la matanza de la señal, y se prueba con CIFS también:

grep -v tracefs /proc/mounts | cut -d' ' -f2 | \ 
    while read m; do \ 
    timeout --signal=KILL 1 ls -d $m > /dev/null || echo "$m"; \ 
    done 
Cuestiones relacionadas