2009-01-09 12 views
5

He envuelto el Net::SSH::Expect de Perl con un pequeño módulo para reducir el código repetitivo necesario para escribir una nueva secuencia de comandos de configuración para usar con nuestras tarjetas HP iLO. Mientras que, por un lado, quiero que este contenedor sea lo más delgado posible, para que colegas no programadores puedan usarlo, también quiero que esté lo mejor escrito posible.¿Cómo debo manejar los errores en los métodos de Perl y qué debo devolver de los métodos?

se usa de esta manera:

my $ilo = iLO->new(host => $host, password => $password); 
$ilo->login; 

$ilo->command("cd /system1"); 
$ilo->command("set oemhp_server_name=$system_name", 'status=0'); 

y esto es iLO::command():

sub command { 
    my ($self, $cmd, $response) = @_; 

    $response = 'hpiLO-> ' unless defined($response); 

    # $self->{ssh} is a Net::SSH::Expect object 
    croak "Not logged in!\n" unless ($self->{ssh}); 

    $self->{ssh}->send($cmd); 
    if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { 
     return { 
      before => $self->{ssh}->before(), 
      match => $self->{ssh}->match(), 
      after => $self->{ssh}->after(), 
     }; 
    } else { 
     carp "ERROR: '$cmd' response did not match /$response/:\n\n", 
      $self->{ssh}->before()), 
      "\n"; 
     return undef; 
    } 
} 

Tengo dos consultas relacionadas. Primero, ¿cómo debo tratar las respuestas que no coinciden con la respuesta esperada? Supongo que lo que estoy haciendo ahora es satisfactorio: al devolver undef señalo que algo se rompió y mi croak() dará como resultado un error (aunque con poca gracia). Pero se siente como un olor a código. Si Perl tuviera excepciones, plantearía una y dejaría que el código de llamada decidiera si ignorarlo o no/salir/imprimir una advertencia, pero no (bueno, en 5.8). Tal vez debería devolver algún otro objeto (iLO::response, o algo así) que contenga un mensaje de error y el contenido de $ilo->before() (que es solo Net :: SSH :: Expect's before())? Pero si hago eso, y tengo que ajustar cada $ilo->command en una prueba para atraparlo, mis scripts van a estar llenos de texto repetitivo.

En segundo lugar, ¿qué debo devolver para tener éxito? Una vez más, mi hash que contiene más o menos la respuesta de Net :: SSH :: Expect hace el trabajo, pero no se siente "bien" de alguna manera. Aunque este ejemplo en Perl mi código en otros idiomas emite el mismo olor familiar: nunca estoy seguro de cómo o qué devolver de un método. ¿Que me puedes decir?

+0

Nunca debe 'devolver undef()' ya que eso crea una lista de un elemento en el contexto de la lista. Simplemente 'return;' hará lo correcto en cualquier contexto. :) –

Respuesta

5

Si está familiarizado con las excepciones de lenguajes como Java, y luego pensar en die como throw y eval como try y catch. En lugar de devolver undef, se podría hacer algo como esto:

if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { 
    return { 
     before => $self->{ssh}->before(), 
     match => $self->{ssh}->match(), 
     after => $self->{ssh}->after(), 
    }; 
} 

die "ERROR: '$cmd' response did not match /$response/:\n\n" 
. $self->{ssh}->before(); 

Luego, en su Código de llamada:

eval { 
    $ilo->command("set oemhp_server_name=$system_name", 'status=0'); 
}; 

if (my $error = [email protected]) { 
    # handle $error here 
} 

Al igual excepciones en otros idiomas, lo que le permite la libertad bajo fianza de un Submethod en cualquier punto sin tener que preocuparse por la propagación de valores de retorno en la pila de llamadas. Serán capturados por el primer bloque eval que los encuentre. Además, puede die nuevamente para volver a lanzar una excepción que no puede tratar con una copia de seguridad de la pila.

Mejor aún, puede usar die para lanzar un objeto, que su manejador de excepciones puede interrogar para obtener información útil y mensajes de error. Me gusta usar Exception::Class para este propósito. El módulo Error también proporciona algo de azúcar sintáctico para hacer bloques try/catch similares a Java.

5

La forma habitual de generar excepciones en Perl es con die. La forma habitual de atraparlos es usar eval con un bloque como argumento y probar $ @ después de que finalice la evaluación.

0

Además de usar "muere", como excepción, también se puede añadir otro método:

if (!$ilo->commandSucceeded("set oemhp_server_name=$system_name", 'status=0')) { 
    #recover here 
} 

Por supuesto, la implementación interna de mando() se convierte en

die ... if !commandSucceeded; 
4

Encontrará mucha discusión de este tipo de cosas en Google. La mejor práctica, sin importar lo que decida, es no sobrecargar ninguno de los valores, por lo que el valor de retorno significa cosas diferentes. Siempre debe ser un código de error, o nunca debe ser el código de error. La gente no debería tener que mirar el valor real para decidir si es un código de error o no.

Consulte los populares módulos de Perl en CPAN (o los que ya usa) para ver lo que hacen. Incluso hablo de esto un poco en Mastering Perl, pero no me da una respuesta en blanco y negro. Al igual que con todos los códigos reales, la respuesta real es "depende".

Hay muchas maneras diferentes de hacerlo. Desafortunadamente, eso significa que las personas lo hacen de todas las maneras. Como ese es el caso, invoco consistencia como la regla principal. ¿Qué hace la mayoría del código ya (sin contar el camino equivocado)? Si tengo que encajar en una base de código existente, trato de utilizar el mismo tipo de interfaz que la mayoría del código ya usa.

Si no hay un ganador claro, escriba casos de uso usando un par de estilos diferentes. ¿Cuál de ellos se ajusta mejor al problema o más naturalmente expresa los pasos que la mayoría de los usuarios tomará? Eso no siempre es solo para die y eval. Escriba scripts de ejemplo usando sus interfaces no implementadas. ¿Qué estilo vas a querer usar? Descubrí que escribir el guión antes de implementar la interfaz me muestra mucho más de lo que pensaba. Si estoy escribiendo cosas para que las usen otras personas, les muestro las secuencias de comandos en diferentes estilos y les pregunto cuál les gusta más.

Y, si todo eso falla, alcance el 2d6. :)

Cuestiones relacionadas