En PostgreSQL la forma de asignar valores automaticamente a una columna (AUTOINCREMENT en MariaDB, por ejemplo) es mediante la asociacion de una secuencia de la que obtener los valores para la columna. Esto se hace automaticamente sin que nos tengamos que preocupar si el tipo elegido para la columna al crearla es SERIAL, que equivale a asignar a dicha columna la propiedad IDENTITY del estandar SQL [1].
Pero si la tabla ya esta creada, poblada y en uso, que hay que hacer?
Pues hay que crear la secuencia y asignarsela a la columna explicitamente [2]:
CREATE SEQUENCE public.table_id_seq
INCREMENT 1
START 1000;
ALTER TABLE public.table
ALTER COLUMN id SET DEFAULT nextval('table_id_seq')
ALTER SEQUENCE public.table_id_seq
OWNER TO table.id;
COMMIT;
Y si ya he creado la sequencia y la he asociado a la columna pero se me ha olvidado decirle a la secuencia quien es su propietario/
Pues se le dice despues, no pasa nada;
ALTER SEQUENCE public.table_id_seq
OWNED BY public.items.id;
Y ya que estamos, tengo otra columna para la que tambien quiero valores por defecto, pero esta vez quiero el momento de la insercion.
Esta es facil, basta con modificar la columna en cuestion:
ALTER TABLE public.items ALTER COLUMN create_date SET DEFAULT current_timestamp;
Varias veces había intentado utilizar sed para eliminar el doble salto de línea que aparece en algunos ficheros, normalmente en las copias de la configuración de algunos switches. Nunca lo había conseguido y no entendía muy bien por qué. Creía que sed no entendía la especificación del caracter salto de línea como \n, claro que tampoco funcionaba \\n ni \\\n.
Cuando ví esta solución para sustituir saltos de línea por espacios entendí creí entender, como explicaré más adelante, que el problema es que sed va analizando línea por línea, eliminando el fin de línea de cada línea antes de realizar la búsqueda de patrones, por lo que, naturalmente, nunca encuentra dos saltos de línea seguidos en la misma línea.
La solución ofrecida para reemplazar saltos de línea por espacios y la traducción de su explicación son las siguientes:
sed :a;N;$!ba;s/\n/ /g
:a crea una etiqueta, lo que nos permite a continuación añadir una nueva línea al espacio de reconocimiento de patrones con N. $!ba indica que no se aplique esta regla en la última línea, para dejar el salto de línea al final del fichero.
Al modificarlo para reemplazar un doble salto de línea por uno probé lo siguiente con éxito:
sed :a;N;$!ba;s/\n\n/\n/g
Algo no cuadraba, pues según leí antes, sed eliminaba el salto de línea al final de cada línea, pero supuse que N, además de añadir una línea al espacio de búsqueda de patrones, evitaba la eliminación del salto de línea.
Queriendo jugar un poco y aportar mi granito de arena a la solución, pensé que en este caso no era necesario hacer una excepción con la última línea, debería funcionar igualmente aunque sed intentase aplicar sin éxito la sustitución en la última línea y probé así:
sed :a;N;s/\n\n/\n/g
Pero así no hace nada de nada. ¿Cómo? ¿perdona?, ¿la última parte no era únicamente para no aplicar la regla en la última línea? Pues no. Así que tuve que continuar leyendo en busca de una respuesta mejor que la aceptada y más votada, y así llegué a una respuesta, que hacía referencia a las FAQ de sed. Sin embargo, el fragmento incluido tampoco aclaraba mis dudas.
Así que seguí leyendo y casi al final del hilo, donde casi nunca hay buenas respuestas, encontré esta solución:
sed -i "s/\\\\\n//g" filename
De la que se obtenía para mi caso la siguiente, que funcionaba like a charm.
sed 's/\\\\\n\\\\\n/\\\\\n/g'
Quedaba claro que era mentira que sed eliminara los saltos de línea, así que no quedaba más remedio que seguir profundizando un poco, no para resolver el problema que ya estaba resuelto, sino porque no aguantaba más mi incultura en lo relacionado con sed.
Explorando un poco en las FAQ de sed encontré una serie de scripts de una línea, entre los que había dos que hacían algo parecido a lo que quería:
# Delete all CONSECUTIVE blank lines from file except the first.
# This method also deletes all blank lines from top and end of file.
# (emulates "cat -s")
sed '/./,/^$/!d' file # this allows 0 blanks at top, 1 at EOF
sed '/^$/N;/\n$/D' file # this allows 1 blank at top, 0 at EOF
Otros scripts más que hacía lo mismo, pero no eran como los que había visto antes, pero ‘/./,/^$/.’, ¿esto que es?, no pararé hasta entender la sintaxis y semántica de todas estas soluciones, lo que finalmente, viendo algunos ejemplos más, logré.
'/./,/^$/!d'
La interpretación es la siguiente. /./,/^$/ especifica un rango sobre el que aplicar un comando, concretamente el rango entre ., es decir cualquier caracter, y ^$, es decir, una línea vacía. El ! a continuación niega la condición y la d elimina la coincidencia de la salida. Es decir, elimina los saltos de línea que no están precedidos por otro caracter.
Esto me llevó a esta sencilla y elegante solución con la que termino ya:
sed '/^$/d'
Es decir, borra las líneas vacías.
P.D. Reconozco que las diversas partes de este artículo son un poco incoherentes/inconexas/mejorables, pero tal como está ahora mismo sirve ya de ayuda para saber por donde tirar.
Cada paquete IP tiene en su cabecera un campo llamado TTL (Time To Live) destinado a evitar que el paquete quede eternamente circulando por la red en caso de algún problema de enrutamiento. Aunque el significado de sus siglas haga referencia al tiempo de vida, en realidad el valor del campo TTL representa el número máximo de saltos que puede dicho paquete realizar entre distintas redes.
Para que esto se cumpla, cada vez que el paquete es enrutado de una red a otra, se decrementa el valor TTL del paquete. Si al pasar de una red a otra el campo TTL alcanza el valor 0 entonces el paquete se descarta sin enviarse a la siguiente red IP.
Cada sistema operativo da un valor por defecto de TTL distinto a los paquetes unicast (con dirección IP de destino de un equipo concreto) que suele ser suficiente para alcanzar cualquier otro equipo del mundo a través de Internet (en Linux este valor es 64). Sin embargo, para los paquetes multicast (con dirección IP de destino de un grupo, no de ningún equipo concreto) el valor por defecto es 1, de modo que los paquetes multicast, por defecto, no puedan ser reenviados a otras redes distintas de aquella en la que se originaron.
Afortunadamente, las aplicaciones pueden indicar que desean aplicar un TTL distinto a los paquetes multicast enviados por cierto socket. Según la norma POSIX se hace de la siguiente forma:
También se puede establecer el TTL de forma genérica para todos los paquetes enviados utilizando la opción IP_TTL, en lugar de IP_MULTICAST_TTL.
Bueno vale, pero el título dice que los paquetes ya están enviados por la aplicación. Es decir, no controlo o no puedo modificar la aplicación, así que, ¿qué se puede hacer si no se puede modificar cierta aplicación y se necesita cambiar el TTL de los paquetes que envía?
socat
Una solución obvia y bastante sencilla consiste en desplegar una aplicación que reciba dichos paquetes y reenvíe su contenido estableciendo el TTL como se desee. Esto se puede hacer de forma bastante sencilla con la aplicación socat. El siguiente ejemplo muestra como utilizarla para recibir unos paquetes multicast y reenviarlos con TTL 16 a otra dirección multicast:
Esta orden abrirá un socket UDP de escucha en el puerto $ORIGINAL_DEST_PORT, asociado al interfaz (o interfaces) que correspondan a la dirección IP multicast $ORIGINAL_DESTINATION_IP y realizando la suscripción IGMP a la dirección de grupo multicast $ORIGINAL_DESTINATION_IP por el interfaz especificado por $INGRESS_INTERFACE. Los paquetes que reciba los enviará a la dirección IP $NEW_DESTINATION_IP y al puerto $NEW_DESTINATION_PORT con TTL 16 utilizando como dirección IP de origen $EGRESS_SOURCE_IP.
iptables
Otra solución mucho más versátil consiste en utilizar IPTABLES para modificar los paquetes mientras atraviesan la cadena de salida OUTPUT antes de que el sistema entregue el paquete al interfaz de red.
La modificación del TTL solo se puede realizar en la tabla mangle, por lo que el comando IPTABLES para realizar este cambio sería el siguiente:
Para poder utilizar un mismo código Python que tenía duplicado con distintas configuraciones en distintos directorios pensé en poner las variables que definen la configuración del trabajo a realizar en ficheros .py particulares y cargarlos como un módulo desde el ahora único fichero de código con lógica. Pensé en esta chapuza de ficheros de configuración para ahorrarme trabajo y no tener que analizar archivos JSON, YAML o cualquier otro formato. Al final el trabajo me lo ahorré gracias a un experto en Python que trabaja conmigo.
Para resumir digamos que tengo X ficheros Python del estilo de estos dos:
Lo que quería hacer es tener un fichero único con la lógica y un fichero particular por cada configuración importado desde el fichero de lógica. Así, los ficheros de configuración serían los siguientes:
Al fichero de lógica le proporcionaría la ruta del fichero de configuración a importar y listo. Pero en el fichero de lógica no se puede utilizar un simple import, ya que el nombre de los ficheros de configuración a importar es dinámico.
Buscando por ahí llegué hasta el punto en que teniendo la ruta del fichero de configuración preparada, lograba (más o menos) importar el fichero de configuración utilizando el módulo importlib. Aclaro que el entorno en el que trabajo es Python 2.7, aunque creo, que esto funciona igual en Python 3. Con la documentación y los artículos que había leído llegué hasta este código que requiere como primer parámetro la ruta del directorio donde buscar el módulo y como segundo el nombre del módulo (nombre del fichero eliminando el sufijo .py):
#!/usr/bin/python import os import sys # Obvio las comprobaciones de número de parámetros y existencia de rutas directorioConfiguracion = os.path.abspath(sys.argv[1]) nombreModulo = sys.argv[2]
# Aquí se le dice donde buscar el módulo sys.path.append(directorioConfiguracion)
# Aquí trato (sin éxito aún) de importarlo correctamente import importlib importlib.import_module(nombreModulo)
# Aquí se ve que el módulo se ha "cargado" con el nombre proporcionado como nombreModulo print sys.modules
El módulo era encontrado y cargado, pero no veía la forma de poder acceder a las variables que había en él. Y, (muy) probablemente, seguiría sin verla mientras escribo esto de no haber consultado al mencionado experto compañero. Él, echando un vistazo me dijo: «¿no tendrías que asignar el resultado del import_module a una variable?». Ahora me parece obvio que debía ser así.
Así, el código correcto resultó ser el siguiente:
#!/usr/bin/python import os import sys # Obvio las comprobaciones de número de parámetros y existencia de rutas directorioConfiguracion = os.path.abspath(sys.argv[1]) nombreModulo = sys.argv[2]
# Aquí se le dice donde buscar el módulo sys.path.append(directorioConfiguracion)
# Aquí trato (sin éxito aún) de importarlo correctamente import importlib moduloConfiguracion = importlib.import_module(nombreModulo)
Mi primera intención con esta entrada era simplemente anotar a modo de nota/receta/chuleta cómo crear un túnel SSH para poder iniciar sesión en una máquina (o copiar ficheros por SCP) a la que no se tiene acceso directo, es decir, utilizando otra máquina como paso intermedio. Pero al final me he liado.
En primer lugar hay que explicar en qué consiste el túnel o redirección de puertos SSH. Básicamente se trata de abrir un socket TCP en modo escucha, establecer otra conexión con otro socket TCP en modo escucha cuando se establezca una conexión con el primero, y enviar por cada una de estas dos conexiones todo lo que se reciba por la otra. Es decir, la primera conexión TCP es un extremo de un túnel y la segunda conexión TCP es el otro.
Dicho esto, hay que tener en cuenta que el establecimiento y mantenimiento de un túnel SSH está asociado a una sesión SSH. Así, uno de los dos extremos del túnel estará forzosamente en el cliente o en el servidor de dicha sesión SSH. Por lo tanto, hay que decidir si dicho puente se quiere abrir en el cliente o en el servidor de esa necesaria sesión SSH.
Si se desea establecer un puente desde la máquina local (cliente de la sesión SSH) se utilizará el parámetro -L (de Local tunnel, por ejemplo), mientras que si el puente se quiere establecer en la máquina remota (servidor de la sesión SSH) se utilizará -R (de Remote tunnel, para seguir con la misma lógica)
Puesto que una imagen suele valer más que mil palabras, aquí van dos para reflejar ejemplos de ambas situaciones.
Estas imágenes están atribuidas a Serypol [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)], from Wikimedia Commons.
Túneles desde servidor de la sesión SSH.Túneles desde cliente de la sesión SSH.
Seguiría extendiéndome porque el tema es interesante, pero ya me estoy cansando, así que voy a ir al grano de una vez, que es lo que originalmente pretendía.
En definitiva, para establecer el túnel hay que proporcionar cierta información que clasifico en cuatro grupos. El primero define la necesaria sesión SSH, donde se deberá al menos indicar la máquina con la que establecerla. El segundo determina si el túnel va a situar su entrada en el cliente de dicha sesión SSH (local) o en el servidor (remoto). En el tercero se define el socket TCP de entrada al túnel. Y en el cuarto y último el socket con el que se conecta la salida.
Para mostrar como se pone esto en práctica pondré dos ejemplos de túnel SSH que solucionan el problema que dio pie a esta entrada, uno en modo local y otro en modo remoto.
El problema
El problema a resolver consiste en obtener acceso a un servidor SSH con el que no tenía se tiene conectividad sino a través de otra servidor. La máquina desde la que se desea establecer la conexión se llamará local, la máquina desde la que se dará el salto (jump server se dice por ahí) se llamará proxima y la máquina destino final de la conexión se llamará ultima.
La solución con túnel local
Veamos como establecer el túnel en modo local definiendo los cuatro grupos de información que indiqué antes. El lugar donde colocar la entrada del túnel, como acabo de indicar, será en el lado local de la sesión SSH. La sesión SSH que soportará el túnel se establecerá entre las máquinas local y proxima, como no podía ser de otra forma. El socket TCP de entrada al túnel admitirá conexiones en todos los interfaces de local por el puerto 2222. Y el otro extremo del túnel establecerá conexión con el puerto 22 de ultima.
Para crear este túnel se ejecutaría el siguiente comando:
ssh -L *:2222:ultima:22 proxima -l usuario
En este comando se especifican los cuatro grupos de información tal como se explica a continuación por orden de aparición:
-L indica que se desea crear un túnel desde el extremo local de la sesión SSH a establecer.
*:2222 define la entrada al túnel.
* indica que el puerto de escucha se asociará a todos los interfaces de red (*) en local. Aquí se podría haber indicado una IP concreta (o IPv6) para admitir únicamente conexiones en el interfaz correspondiente a dicha IP.
2222 es el puerto de escucha en local.
ultima:22 define el destino del túnel.
ultima es la IP o nombre de equipo o de dominio de ultima, la máquina destino de la conexión desde la salida del túnel.
22 es el puerto en ultima hacia el que se canalizarán las conexiones que se establezcan con el puerto 2222 en proxima.
proxima -l usuario define la sesión SSH que abre y mantiene el túnel.
proxima es la IP o nombre de equipo o de dominio de proxima, la máquina intermedia o jump server.
-l usuario indica el nombre del usuario con el que se establecerá la sesión SSH a proxima.
Tras la ejecución de este comando se inicia una sesión SSH con proxima y, mientras dure dicha sesión, se mantendrá el túnel con entrada en el puerto 2222 de local y salida hacia el puerto 22 de ultima.
Sin embargo, con esta forma de establecer el túnel debemos mantener activa una sesión SSH que tal vez no necesitamos utilizar. Afortunadamente, se pueden añadir un par de opciones al comando anterior para crear una sesión SSH desligada de nuestra sesión local, de forma que mantengamos abierto el túnel sin requerir una sesión SSH interactiva.
Con el parámetro -f indicamos que la sesión SSH pase a segundo plano, desligándose de la sesión desde la que se lanza. Puesto que para pasar a segundo plano se debe indicar un comando a ejecutar en la sesión SSH, se añade también el parámetro -N que indica explícitamente que no se debe ejecutar ningún comando.
Por tanto el comando completo sería algo así:
ssh -L *:2222:ultima:22 proxima -l usuario -f -N
Ahora se puede iniciar una conexión con el puerto 22 de última para iniciar una sesión SSH desde cualquier máquina con acceso al puerto 2222 de local:
ssh -p 2222 local -l usuario
El túnel canalizará la conexión establecida con el puerto 2222 de local con una nueva conexión desde proxima al puerto 22 de ultima, donde deberá estar escuchando su servidor SSH.
La solución con túnel remoto
Por último, se puede crear la entrada del túnel en la máquina remota de la sesión SSH, es decir, en proxima. Para ello basta con utilizar la opción -R (remota) en lugar de -L (local).
ssh -R *:2222:ultima:22 proxima -l usuario -f -N
En este caso, puesto que la entrada del túnel la crea el servidor SSH (sshd) del lado remoto de la sesión SSH, este deberá tener la opción GatewayPorts habilitada en el fichero /etc/ssh/sshd_config, de lo contrario, solo aceptará conexiones al túnel en el interfaz de loopback, es decir solo se podrán conectar clientes que se ejecuten en la misma máquina que el servidor SSH.
Para permitir la conexión desde otras máquinas hay que comprobar que está la siguiente línea en el fichero /etc/ssh/sshd_config del servidor SSH (proxima) y reiniciar el servidor SSH si no lo estaba previamente:
¿Nunca has hecho una transacción en git y te has dado cuenta que deberías haberla puesto en otra rama? Yo lo hago todos los días.
Afortunadamente, es posible arreglar esto y dejarlo tal como si hubieras sido tan estrictamente riguroso en el control del árbol de cambios como se supone que se debe ser. La verdad es que yo sigo sin entender muchos detalles de cómo funciona git, pero parece que cualquier cosa que puedas imaginar o necesitar puede hacerse.
Originalmente reproduje aquí los pasos para mover una transacción ya hecha hacia una nueva rama, tal como se indica a continuación. A posteriori recurrí a este mismo recurso para mover una transacción a otra rama ya existente, cuyos pasos incluyo en el segundo ejemplo.
Los pasos (aparentemente más correctos) para mover la última transacción a una nueva rama son los siguientes:
Si no es solo una transacción sino varias seguidas se podría hacer cambiando el ~1 utilizado por el número de transacciones a mover.
¡Ojo!, si en el camino se han creado nuevas ramas o si se han subido (push) los cambios a un repositorio se pueden originar graves complicaciones, do it at your own risk.
La receta es simple, basta con devolver la rama erróneamente mezclada (merge) a su estado anterior y forzar la actualización en el repositorio (push).
Sin embargo, hay que tener en cuenta que hay unos requisitos previos para garantizar que esto salga bien.
Nadie, debe haber actualizado su espacio de trabajo con los cambios subidos al repositorio que queremos deshacer.
El merge debe haberse hecho con la opción –no-ff (no fast-forward).
No deben haberse realizado más commits después del del merge que queremos deshacer.
De estos tres requisitos el primero es el más importante, si deshacemos estos cambios habiendo actualizado otra persona su espacio de trabajo con el contenido del repositorio estaremos creando dos universos paralelos divergentes.
Los otros dos son más bien un «allá tú» si no los cumples. Es decir, puede que te funcione sin cumplirlos, pero si ese es tu caso, mejor mira en otros sitios donde se trate un caso más parecido al tuyo.
Así que si estás seguro de que cumples estos requisitos solo tienes que ejecutar este par de comandos:
El sha-commit-previo-a-merge será la firma del commit previo al merge en la rama que queremos restaurar. Para saber exactamente cuál es el commit previo en la rama en la que hemos realizado el merge ejecutamos este comando:
git log --first-parent <rama>
El commit previo al del merge que se quiere deshacer (el actual si no hemos seguido haciendo commits, tal como indica el tercer requisito) será el que debemos proporcionar para el git reset.
Y eso es todo. Las referencias que he consultado, entre otras, para atreverme a ejectuar esos comandos en mi repositorio son las siguientes:
Inicialmente encontré información para poder acceder desde la línea de comandos a páginas web a través del proxy de la empresa estableciendo unas variables de entorno:
Esto basta para que desde la línea de comandos se pueda hacer un wget hacia Internet utilizando el proxy de la empresa.
Sin embargo, esto no sirve para que yum pueda acceder a repositorios de paquetes en Internet. Para indicar a yum el proxy a utilizar hay que hacerlo en su propio fichero de configuración yum.conf, pero la autenticación no funciona.
Buscando en Google se encuentra rápidamente este hilo de los foros de CentOS, que da la solución exacta y detallada que copio vilmente a continuación para facilitarme la vida y sin ningún mérito por mi parte. Únicamente puedo aportar que las variables de entorno http_proxy y https_proxy deben establecerse ~/.bashrc, en lugar de en ~/.bash_profile, haciendo que estén disponibles en cualquier ventana de terminal y no solo en los inicios de sesión por SSH o en la terminal local.
En primer lugar se debe instalar el paquete cntlm que según la descripción es un rápido y eficiente proxy NTLM. Este paquete se encuentra en el repositorio EPEL, y se puede obtener desde aquí. Con este paquete se despliega un proxy local que puede redirigir las peticiones hacia el proxy del dominio Active Directory, delegando en este proxy local la correspondiente autenticación en el proxy del dominio.
Una vez instalado se proporcionan en el fichero /etc/cntlm.conf los datos del proxy del dominio y de autenticación del usuario y listo.
Desde luego, dejar la contraseña de acceso al dominio en claro en el fichero no es la mejor opción, por eso, se puede en su lugar poner los hashes que el propio cntlm puede generar:
$ cntlm -H -u usuario@DOMINIO
Password:
PassLM 0D82DC2C16C3CE23E4270BBC86ED9BC3
PassNT B07470DAF522DE3F7D8F61212417D5ED
PassNTLMv2 EBA9CA50AC426D4EF5ECC65C6591F198 # Only for user 'usuario', domain 'DOMINIO'
Si el proxy utiliza autenticación por el método NTLMv2 será necesario especificar correctamente el usuario y el dominio, teniendo en cuenta que el dominio será el nombre de dominio NetBIOS (DOMINIO), y no el nombre de dominio Active Directory (dominio.tld).
Una vez obtenidos los hashes se comenta la línea Password y se pegan estos en /etc/cntlm.conf.
Para ejecutar el servicio correspondiente basta con lanzar el ejecutable cntlm. Una vez disponible, podemos cambiar nuestro proxy en las variables de entorno para utilizar el ofrecido por cntlm.
Nótese que el proxy HTTPS sigue utilizando HTTP como protocolo, ya que, al menos por defecto, cntlm no soporta peticiones por HTTPS. Esto no es ningún problema para acceder a páginas por HTTPS, simplemente afecta a la conexión con el proxy local de cntlm.
$ wget https://uatek.com/
--2018-01-17 15:52:48-- https://uatek.com/
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:3128... failed: Connection refused.
Connecting to localhost (localhost)|127.0.0.1|:3128... connected.
Proxy request sent, awaiting response... 200 OK
Length: 209 [text/html]
Saving to: ‘index.html.2’
100%[====================================================================================================================================>] 209 --.-K/s in 0s
2018-01-17 15:52:49 (10.8 MB/s) - ‘index.html.2’ saved [209/209]
Viendo la salida de wget se observa que la resolución de localhost proporciona en primer lugar la dirección IPv6 ::1, en la que no está escuchando cntlm, por tanto, es mejor proporcionar directamente la dirección IP local 127.0.0.1 y evitar el intento fallido de acceso a la dirección ::1:3128.
export http_proxy=http://127.0.0.1:3128
El uso de cntlm evita además la exposición de la contraseña de usuario del dominio, ya que no es necesario establecerlas en las variables de entorno y están protegidas contra lecturas de usuarios que no sean root en el fichero /etc/cntlm.conf.
Con cntlm corriendo ya solo queda indicarle a yum que el proxy a utilizar es el proporcionado por cntlm en el fichero /etc/yum.conf.