Welcome to Kartones.Net Sign in

Stats



March 2008 - Posts

Caso: El inicio de sesión no funciona adecuadamente

Prólogo

El presente caso muestra algo que, dicho mal y pronto, es una verdadera perogrullada: una solución diseñada a medida no es directamente aplicable a otro entorno por mucho que se parezca al original.

Síntoma

Usuarios de una gran organización localizados en dos edificios distintos experimentan problemas con su inicio de sesión. En concreto hay unidades de red que siempre se les asignan a arrancar el sistema pero hay otras que, de forma aleatoria, no aparecen cuando sí deberían de estar disponibles. Sólo reiniciando el equipo varias veces consiguen que funcione correctamente.

Investigación

Aunque los síntomas iniciales ya apuntan a que el problema es algo externo a los equipos cliente, es conveniente buscar un patrón común entre las máquinas afectadas de todas formas. Esto puede aportar alguna pista adicional para la resolución del caso. Yo, como primer paso, tengo la costumbre de comprobar si los sistemas tienen o no un software o hardware similar.

Efectivamente, todos los equipos involucrados tienen instalados el mismo sistema operativo (parches incluidos) y el mismo conjunto de aplicaciones. Esta pista no es del todo concluyente ya que el usuario asegura que en en la sede principal unos equipos similares no experimentan problemas. Hay diferencias en el hardware entre los equipos, pero viendo que el problema aparece por igual en todos, no resulta útil usar esta idea como criterio de discriminación.

Avanzando un poco por la parte de la configuración de software, el usuario comenta que se tratan de una nueva plataforma de producción para un determinado departamento y que actualmente está desplegada sólo en las dos oficinas con problemas y en la sede principal. Esto ya centra más el caso. La sede principal se conecta con los Controladores de Dominio y a los servidores de ficheros a través de la red de área local mientras que las oficinas afectadas se conectan a través de conexiones remotas. Los servidores de ficheros de las unidades que siempre se conectan correctamente están disponibles en la red de área local de las oficinas.

Ya con las pistas de la configuración y velocidad de conexión, el siguiente paso es revisar el script de inicio de sesión.

El cliente comenta que el script de inicio de sesión en realidad esta compuesto por numerosos scripts los cuales se van invocando los unos a los otros. Este sistema depende de la configuración de la máquina y su pertenencia a uno u otro departamento. De forma casual sale un detalle que, como veremos después, resulta importante para el caso: la tecnología usada se basa originalmente en una solución que un grupo de consultores diseñó para otra organización y ahora han aplicado a esta. Tan sólo se realizaron una serie de modificaciones menores para ajustarlo al nuevo dominio. Avanzando más en la revisión de estos ficheros, se ve que es un script concreto el que aparentemente no llega a ejecutarse, el encargado de asignar las unidades de red. Esta información termina ya de centrar por completo el caso.

Analizando el código, se destacan las siguientes secciones.

En la función principal:

'Inicio del Sript On Error Resume Next ......

'Creación e inicio de entorno de informe de estado en pantalla Set objExplorer= Nothing Set objExplorer=CreateObject("InternetExplorer.Application") objExplorer.Navigate "about:blank" objExplorer.ToolBar = 0 objExplorer.StatusBar = 0 objExplorer.Width=500 objExplorer.Height = 200 objExplorer.Left = 0 objExplorer.Top = 0 Do While (objExplorer.Busy) Wscript.Sleep 100 Loop objExplorer.Visible = 1 strOutput = "<P><B>CONFIGURACIÓN DE ENTORNO</b></p>" objExplorer.Document.Body.InnerHTML = strOutput ......

'Creación e inicio del log en fichero Set objFilelog= CreateObject("Scripting.FileSystemObject") If (objFilelog.FileExists(strLogPath)) Then objFilelog.DeleteFile(strLogPath) End If Set filLog = objFilelog.CreateTextFile (strLogProcom, 1) filLog.WriteLine ("==========================") filLog.WriteLine ("INICIO DEL LOG") filLog.WriteLine ("Fecha ; Hora: " & date & " ; " & time) ......

'Llamada a la función de asignación de unidad MapDrive Set objNetwork = CreateObject("WScript.Network") Call MapDrive (strUnidad , "\\" & strSrv & "\" & strRecurso objNetwork, 3) ......

'Cierre de los medios de registro strStream.WriteLine("================================================") strStream.Close WScript.Sleep(2000) objExplorer.Quit

Y la función MampDrive completa:

1 Function Mapdrive(ByVal strUnidad, ByVal strRecurso , ByVal objNetwork, ByVal intReintento) 2 3 On Error Resume Next 4 5 Dim objDrives, i 6 Do 7 Err.Clear 8 Set objDrives = objNetwork.EnumNetworkDrives 9 If objDrives.Count > 0 Then 10 For i = 0 To objDrives.Count-1 11 If UCase(objDrives.Item(i)) = UCase(strUnidad) Then 12 objNetwork.RemoveNetworkDrive strUnidad, True, True 13 i=objDrives.Count-1 14 End If 15 Next 16 End If 17 strOutput = strOutput&"<P>Conectando la letra "& strUnidad &" a la ruta "& strRecurso &"</p>" 18 objExplorer.Document.Body.InnerHTML = strOutput 19 strStream.WriteLine ("Conectando la letra "& strUnidad &" a la ruta " & strRecurso ) 20 objNetwork.MapNetworkDrive strUnidad, strRecurso 21 ierr=Err.Number 22 intReintento= intReintento - 1 23 Loop Until Err.Number = 0 or intReintento = 0 24 25 Select Case ierr 26 Case ierr=0 27 objExplorer.Document.Body.InnerHTML = "<p>Unidad conectada</p>" 28 strStream.WriteLine ("Unidad conectada " & strRecurso) 29 Case ierr=-2147024829 30 objExplorer.Document.Body.InnerHTML = "<p>No existe el recurso</p>" 31 strStream.WriteLine ("Error: No existe el recurso " & strRecurso) 32 Case ierr=-2147024811 33 objExplorer.Document.Body.InnerHTML = "<p>No se pudo liberar la unidad</p>" 34 strStream.WriteLine ("Error: No se pudo liberar la unidad "& strUnidad) 35 Case Else 36 objExplorer.Document.Body.InnerHTML = "<p>Error no controlado</p>" 37 strStream.WriteLine ("Error: " & CStr(ierr) & " - " & Err.Description) 38 End Select 39 On Error Goto 0 40 End Function

Analizando la salida por pantalla y el fichero de registro se comprueban dos cosas:

  1. Por un lado las ventanas de Internet Explorer cierran muy rápido o que incluso no aparecen.
  2. Tanto si funciona el script como si no , el fichero txt de registro se corta siempre después de la línea 19 del código: "Conectando al recurso \\Servidor\Z"

¿Qué hacer llegado a este punto? Muy sencillo: ya que pese a tener un control de errores y un sistema de registro no aparece ningún mensaje claro... ¡Desactivemos el control de errores! :-). Para ello se hace una copia del script original, se comenta todas las instrucciones "On Error" y se le asigna a un usuario de los del grupo afectado.

Cual es la sorpresa cuando el usuario reporta que, tras numerosas pruebas, le aparece un mensaje de error en tiempo de ejecución en una línea de la función principal, la correspondiente a la instrucción que crea el objeto Internet Explorer para el informe en pantalla:

Set objExplorer=CreateObject("InternetExplorer.Application")

El error en tiempo de ejecución (del que lamentablemente no pude capturar pantalla) detalla que, pese a haber abierto otras ventanas de explorador, existe un bloqueo de recursos que impide abrir una más. Este bloqueo, por lo que se pudo ver después, ocurría en todos las oficinas. Se pudo ver también que el fallo sólo ocurría si el usuario iniciaba sesión nada más encender el equipo.

Con el control de errores activado y ocurría este problema, la función principal continuaba ejecutando las siguientes instrucciones, eso sí, sin la pantalla del explorador. De hecho llamaba a la función MapDrive y llegaba a la siguiente instrucción:

strOutput = strOutput&"<P>Conectando la letra "& strUnidad &" a la ruta "& strRecurso &"</p>" objExplorer.Document.Body.InnerHTML = strOutput

Aquí, al no existir el objeto objExplorer, el sistema registraba un fallo más. Esto en teoría no sería problema, ya que la función también posee un control de errores. El problema surge cuando al ejecutar la asignación de unidad esta también falla: al producirse dos errores seguidos, el control de errores se desactiva y la ejecución sale de la función. Si la ventana del explorador estuviera funcionando este problema nunca ocurriría, ya que los errores de asignación de red se descartan en el siguiente reintento gracias a la instrucción:

Err.Clear

Por último se comprueba que la sentencia select de la función estaba mal escrita, lo que impedía que salieran las entradas del fichero txt de registro que se echaban en falta.

Select Case ierr Case ierr=0 ......

Solución

Como solución al caso se opta por eliminar por completo los mensajes por pantalla a través de Internet Explorer. Es "vistoso" y da la sensación de "algo funcionando por debajo" al usuario pero en grandes compañías, con un gran número de equipos y servidores conectados a redes de distintas velocidades, lo que prima es la eficiencia.

Una mención especial merece la instrucción "on error resume next". La verdad es que el uso de este comando en entornos como inicios de sesión o tareas administrativas tendría que reducirse al mínimo. Yo personalmente intento hacer lo posible para no tener que utilizarlo. El uso de "on error resume next" debería de ser sólo utilizado en partes del código donde se realicen llamadas a recursos no controlados y exista una posibilidad común de fallo y deseamos evaluarlo para reaccionar de forma adecuada. Un ejemplo, basándonos en el código anterior sería el siguiente:

1 Function MapeaUnidad(ByVal strUnidad, ByVal strRecurso , ByVal objNetwork) 2 On Error GoTo 0 3 4 Dim objDrives, i 5 Set objDrives = objNetwork.EnumNetworkDrives 6 strOutput = strOutput&"<P>Conectando la letra "& strUnidad &" a la ruta "& strRecurso &"</p>" 7 objExplorer.Document.Body.InnerHTML = strOutput 8 strStream.WriteLine ("Conectando la letra "& strUnidad &" a la ruta " & strRecurso ) 9 10 On Error Resume Next 11 12 objNetwork.MapNetworkDrive strUnidad, strRecurso 13 14 On Error GoTo 0 15 16 ierr=Err.Number 17 18 Select Case ierr 19 Case 0 20 objExplorer.Document.Body.InnerHTML = "<p>Unidad conectada</p>" 21 strStream.WriteLine ("Unidad conectada " & strRecurso) 22 Case Else 23 objExplorer.Document.Body.InnerHTML = "<p>Error no controlado</p>" 24 strStream.WriteLine ("Error: " & CStr(ierr) & " - " & Err.Description) 25 End Select 26 End Function

Conclusiones

Cada organización es diferente y requiere que ciertas soluciones sean ajustadas a sus necesidades. En otros sitios, los mismos ajustes no serían necesarios o incluso provocarían problemas imprevistos. Esto no invalida que se pueda utilizar en distintas ocasiones una buena idea, tampoco hay que reinventar el plato sopero, vamos, pero sí es conveniente prestar mayor atención cuanto más particular sea el entorno donde nos movemos.  Sobre todo no hay que sorprenderse si algo que ha funcionado en miles de despliegues ahora deja de hacerlo.

Ser metódico en la resolución de los problemas e incluir siempre una búsqueda de patrones comunes entre los elementos afectados como se ha intentado mostrar en este artículo, en cambio son medidas que siempre darán buenos resultados independientemente del tamaño o tipo de organización donde se aplique.

Un saludo a todos y gracias por leerme.

Posted: Mar 23 2008, 11:12 AM by TheSpike | with 1 comment(s)
Filed under: , ,