martes, 19 de diciembre de 2017

Polymer: Verificando shadow DOM

Podría ser algo seriamente trivial, pero decidí revisar que tanto significa lo de shadow DOM para Polymer. Así que aplique un estilo CSS tanto ridículo, tanto lo suficiente para verificar lo que quiero.

En src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html, agrego un ligero estilo y una pequeña línea, de tal forma que el fichero queda de la siguiente forma:
        
<link href="../../bower_components/polymer/polymer-element.html" rel="import"></link>
<link href="/src/componentes/entradas/vt-entrada.html" rel="import"></link>
<link href="/src/componentes/etiquetas/vt-etiqueta.html" rel="import"></link>

<dom-module id="polymer-muestra-inicial-app">
    <template>
        <style>
        :host {
            display: block;
        }
        p,label {
            background-color: aliceblue;
        }
        </style>

            <h2>
Hello [[prop1]]!</h2>
<vt-entrada contenido="{{contenido}}"></vt-entrada>
            <vt-etiqueta contenido="[[contenido]]"></vt-etiqueta>
            Este es texto desde el componente padre polymer-muestra-inicial-app<br />
        
    </template>

    <script>
    /**
     * @customElement
     * @polymer
     */
    class PolymerMuestraInicialApp extends Polymer.Element {
        static get is() { return 'polymer-muestra-inicial-app'; }
        static get properties() {
            return {
                prop1: {
                    type: String,
                    value: 'polymer-muestra-inicial-app'
                },
                contenido:{
                    type: String,
                    value: 'Desde componente host'
                }

            };
        }
    }
    
    window.customElements.define(PolymerMuestraInicialApp.is, PolymerMuestraInicialApp);
    </script>
</dom-module>
Luego, src/componentes/entradas/vt-entrada.html ha quedado de la siguiente forma: Y sí, sólo agregue un color de fondo y subrayado para el texto:
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<dom-module id="vt-entrada">
    <template>
        <style>
            :host {
                display: block;
            }
            p {
                background-color: beige;
                text-decoration: underline; 
            }
        </style>
        <input type="text" value="{{contenido::input}}">
        <p> Este es nuestro contenido escribiente: [[contenido]]</p>
    </template>
    <script>
        class VtEntrada extends Polymer.Element {
            static get is() {
                return 'vt-entrada';
            }
            static get properties() {
                return {
                    contenido: {
                        type: String,
                        notify: true,
                    }
                }
            }
        }

        window.customElements.define(VtEntrada.is, VtEntrada);
    </script>
</dom-module>
El resultado viene siendo elsiguiente:
Que básicamente viene a confirmar que:
  • Los estilos no suben desde un componente hijo a un componente padre. Lo que se esperaba pues. Esto se puede demotrar mejor si se elimina el estilo en src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html
  • Parece que los estilos no bajan desde el componente padre al componente hijo. Esto se puede demostrar si se elimina el estilo en src/componentes/entradas/vt-entrada.html
Esto último me parecía un poco preocupante, así que decidí maquetar un poco para ver como se comporta. Spoiler: Lo hace tal como se espera
Primer instalamos Pure mediante bower de la siguiente forma:
bower install pure --save
Luego, modificamos a src/index.html ara agregar los estilos de Pure:
 
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

        <title>polymer-muestra-inicial</title>
        <meta name="description" content="Breve descripción de mi persona">

        <!-- See https://goo.gl/OOhYW5 -->
        <link rel="manifest" href="/manifest.json">

        <script src="/bower_components/webcomponentsjs/webcomponents-loader.js"></script>

        <link rel="import" href="/src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html">
        <link rel="stylesheet" href="/bower_components/pure/base.css">
        <link rel="stylesheet" href="/bower_components/pure/grids.css">
        <link rel="stylesheet" href="/bower_components/pure/grids-responsive.css">
    </head>
    <body>
            <polymer-muestra-inicial-app></polymer-muestra-inicial-app>
    </body>
</html>
Y hacemos la maquetación dentro de polymer-muestra-inicial-app en src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html de la siguiente forma:
 
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="/src/componentes/entradas/vt-entrada.html">
<link rel="import" href="/src/componentes/etiquetas/vt-etiqueta.html">

<dom-module id="polymer-muestra-inicial-app">
    <template>
        <style>
        :host {
            display: block;
        }
        p,label {
            background-color: aliceblue;
        }
        </style>
        <div class="pure-g">
            <div class="pure-u-1 pure-u-md-2-5">
                <h2>Hello [[prop1]]!</h2>
            </div>
            <div class="pure-u-1 pure-u-md-3-5">
                    <vt-entrada contenido="{{contenido}}"></vt-entrada>
                    <vt-etiqueta contenido="[[contenido]]"></vt-etiqueta>
                    <p>Este es texto desde el componente padre polymer-muestra-inicial-app</p>        
            </div>
        </div>
    </template>

    <script>
    /**
     * @customElement
     * @polymer
     */
    class PolymerMuestraInicialApp extends Polymer.Element {
        static get is() { return 'polymer-muestra-inicial-app'; }
        static get properties() {
            return {
                prop1: {
                    type: String,
                    value: 'polymer-muestra-inicial-app'
                },
                contenido:{
                    type: String,
                    value: 'Desde componente host'
                }

            };
        }
    }
    
    window.customElements.define(PolymerMuestraInicialApp.is, PolymerMuestraInicialApp);
    </script>
</dom-module>
Que ahora se ve de la siguiente forma:
Así que supongo que maquetar bien, descender clases bien, pero sobre los detalles específicos y generales de como va funcionando esto tendrán que ir esperando un poco más

viernes, 15 de diciembre de 2017

Siguiendo con la Introducción a Polymer 2

Lo de los webcomponents es algo genial: Dividimos la complejidad en pequeños pequeños, cuyas soluciones pueden ser reutilizables. El siguiente paso es verificar no sólo como trabaja la relación padre-hijo, sino la de aquellos componentes que pudieran tener el mismo nivel jerárquico en un momento dado.

Sobre nuestro ejemplo anterior, haremos un nuevo componente vt-etiqueta
mkdir src/componentes/etiquetas/
El contenido del fichero src/componentes/etiquetas/vt-etiqueta.html es el siguiente:
<link href="../../../bower_components/polymer/polymer-element.html" rel="import"></link>
<dom-module id="vt-etiqueta">
    <template>
        <style>
        :host {
            display: block;
        }
        </style>
        <label>Etiqueta componente: [[contenido]] </label>
    </template>
    <script>
        class VtEtiqueta extends Polymer.Element {
            static get is() {
                return 'vt-etiqueta';
            }
            static get properties (){
                return {
                    contenido: {
                        type: String,
                    }
                }
            }
        }
        window.customElements.define(VtEtiqueta.is, VtEtiqueta);
    </script>
</dom-module>
Luego, hemos de añadirlo en el componente polymer-muestra-inicial-app, que tiene la mayor jerarquía en nuestra aplicación. Primero un import en forma de etiqueta <link> y luego una etiqueta propiamente dicha. El código de src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html queda ahora de la siguiente forma:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="/src/componentes/entradas/vt-entrada.html">
<link rel="import" href="/src/componentes/etiquetas/vt-etiqueta.html">

<dom-module id="polymer-muestra-inicial-app">
    <template>
        <style>
        :host {
            display: block;
        }
        </style>

        <h2>Hello [[prop1]]!</h2>
        <vt-entrada contenido="{{contenido}}"></vt-entrada>
        <vt-etiqueta contenido="[[contenido]]"></vt-etiqueta>
    </template>

    <script>
    /**
     * @customElement
     * @polymer
     */
    class PolymerMuestraInicialApp extends Polymer.Element {
        static get is() { return 'polymer-muestra-inicial-app'; }
        static get properties() {
            return {
                prop1: {
                    type: String,
                    value: 'polymer-muestra-inicial-app'
                },
                contenido:{
                    type: String,
                    value: 'Desde componente host'
                }

            };
        }
    }
    
    window.customElements.define(PolymerMuestraInicialApp.is, PolymerMuestraInicialApp);
    </script>
</dom-module>

El contenido de vt-etiqueta, expuesto en la propiedad contenido será actualizado desde nuestro componente anterior, vt-entrada.
Para que eso suceda, modificamos la definición de este último en src/componentes/entradas/vt-entrada.html, configurando el atributo notify de dicha propiedad a true. Una pequeña línea no debería ser razón para volver a presentar todo el código, pero es gratis:
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<dom-module id="vt-entrada">
    <template>
        <style>
            :host {
                display: block;
            }
        </style>
        <input type="text" value="{{contenido::input}}">
        <p> Este es nuestro contenido escribiente: [[contenido]]</p>
    </template>
    <script>
        class VtEntrada extends Polymer.Element {
            static get is() {
                return 'vt-entrada';
            }
            static get properties() {
                return {
                    contenido: {
                        type: String,
                        notify: true,
                    }
                }
            }
        }

        window.customElements.define(VtEntrada.is, VtEntrada);
    </script>
</dom-module>
Al escribir en la caja de texto del componente vt-entrada, el contenido de vt-etiqueta se va modificando.

Para tener en mente según lo visto:

  • Los binding [[propiedad]] son unidireccionales. (Padre al hijo)
  • Los binding {{propiedad}} son bidireccionales. (Padre al hijo y viceversa)
  • Para que esto último funcione con propiedades que han de modificarse en un componente hijo, es preciso que la propiedad tenga configurada notify: true en dicho hijo.
Sí, los componente son bastante reutilizables
Para ejemplo, basta con agregar otro vt-entrada en src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html cuyo contenido haga binding bidireccional con prop1, que es la propiedad que muestra del Hola Mundo de nuestra aplicación. Es un pequeño cambio, pero como copiar y pegar es sencillo:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="/src/componentes/entradas/vt-entrada.html">
<link rel="import" href="/src/componentes/etiquetas/vt-etiqueta.html">

<dom-module id="polymer-muestra-inicial-app">
    <template>
        <style>
        :host {
            display: block;
        }
        </style>

        <h2>Hello [[prop1]]!</h2>
        <vt-entrada contenido="{{contenido}}"></vt-entrada>
        <vt-entrada contenido="{{prop1}}"></vt-entrada>
        <vt-etiqueta contenido="[[contenido]]"></vt-etiqueta>
    </template>

    <script>
    /**
     * @customElement
     * @polymer
     */
    class PolymerMuestraInicialApp extends Polymer.Element {
        static get is() { return 'polymer-muestra-inicial-app'; }
        static get properties() {
            return {
                prop1: {
                    type: String,
                    value: 'polymer-muestra-inicial-app'
                },
                contenido:{
                    type: String,
                    value: 'Desde componente host'
                }

            };
        }
    }
    
    window.customElements.define(PolymerMuestraInicialApp.is, PolymerMuestraInicialApp);
    </script>
</dom-module>
Desde el navegador, lo vemos de la siguiente forma:
Seguimos sin hacer que esto parezca algo mínimamente serio, pero al menos se esta volviendo cada vez más divertido.

jueves, 14 de diciembre de 2017

Especie de Introducción a Polymer 2

Este artículo debió llamarse "Especie de introducción a AngularJS". Es decir, estuve trabajando por mucho tiempo en una aplicación con AngularJS como framework: Fue una muy buena experiencia, y ha sido mi culpa empezar a aprender algo que años antes ya se sabía iba a quedar obsoleto por rediseño. Todos los cambios me superan: Claro que así descritos parecen una mejora, pero el sólo hecho de cambiar a TypeScript me hizo repensar desde cero toda la aplicación.

Así que reconsideré a Polymer: Esta basado en webcomponents, algo que ya había alcanzado a vislumbrar con AngularJS 1.5. Webcomponents aspira a ser el futuro de la web, bueno, así que veremos que tal se comporta en el presente.

Para instalarlo en Fedora, bastará con correr los siguiente comandos como root. Para npm, -g implica global, con lo que se supone que otros usuarios en el sistema podrían usar las herramientas así instaladas:
dnf install npm 
npm -g install bower
npm -g install polymer-cli
Luego seguimos las indicaciones que nos hace la gente de polymer. Desde consola, como cualquier usuario disponible en el sistema y ubicados desde cualquier parte que querramos (Preferentemente no "cualquier" parte), ejecutamos lo siguiente:
mkdir polymer-muestra-inicial
cd polymer-muestra-inicial/
polymer init --name polymer-2-application
Donde --name permite seleccionar la plantilla inicial para nuestro proyecto. polymer-2-application arranca con poco código, lo que es conveniente para aprender lo más posible.
polymer init --name polymer-2-application
info:    Running template polymer-init-polymer-2-application:app...
? Application name polymer-muestra-inicial
? Main element name polymer-muestra-inicial-app
? Brief description of the application Breve descripción de mi persona
A continuación, se instalan todos los paquetes necesarios para nuestra aplicación mediante bower, y es mediante él que debemos agregar nuevas dependencias.
En este momento, podemos probar lo que tenemos hasta ahora (Casi nada a decir verdad) ejecutando desde consola:
polymer serve --open
No subestimen este comando: Incluso permite servir contenido como HTTPS.

Ahora empezamos la introducción propiamente dicha: 

Aclaro que a este momento sigo sin pensar en una estructura de directorios para el código, así que voy a separarlo en componentes tanto como sea posible. 
mkdir -p src/componentes/entradas
cd src/componentes/entradas
Además, voy a trabajar modificando el componente que se ha creado originalmente para esta aplicación: polymer-muestra-inicial-app, no tanto por añorar mi época con AngularJS, sino por aprovechar todo el conocimiento previo de dicha época.

Así, creamos el fichero src/componentes/entradas/vt-entrada.html, que define un input casi sin modificación alguna, con el siguiente contenido
<link href="../../../bower_components/polymer/polymer-element.html" rel="import"></link>

<dom-module id="vt-entrada">
    <template>
        <style>
            :host {
                display: block;
            }
        </style>
        <input type="text" value="{{contenido}}" />
    </template>
    <script>
        class VtEntrada extends Polymer.Element {
            static get is() {
                return 'vt-entrada';
            }
            static get properties() {
                return {
                    contenido: {
                        type: String,
                    }
                }
            }
        }

        window.customElements.define(VtEntrada.is, VtEntrada);
    </script>
</dom-module> 
A ver, para explicaciones más detalladas las teoría. Desde aquí, sin embargo, podemos decir:
  • Esta es casi, casi la forma en que se define un webcomponent nativo al día de hoy: Dentro de <template></template> tenemos el HTML de toda la vida; dentro de <script></script> la definición de nuestro componente. Todo dentro de <dom-module>
  • properties define las propiedades de este componente, en resumen, las variables con las que nos relacionamos con otros componente y que controlamos mediante código. En realidad, extenderse en la teoría de este tema con la documentación oficial es un muy buen primer paso a tomar.
  • El estilo CSS definido dentro de <style> se circunscribe a este componente, mediante una técnica llamada DOM Shadow que hoy en día ya sirve para muchas otras cosas además de encapsular CSS (Igual nace por esta necesidad específica)
Ahora modificamos el componente que se venía de la plantilla, básicamente haciendo un import y usando nuestro componente. Al final se tiene que ver de la siguiente forma:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="/src/componentes/entradas/vt-entrada.html">

<dom-module id="polymer-muestra-inicial-app">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
    <h2>Hello [[prop1]]!</h2>
    <vt-entrada contenido="Configurado desde plantilla padre"></vt-entrada>
  </template>

  <script>
    /**
     * @customElement
     * @polymer
     */
    class PolymerMuestraInicialApp extends Polymer.Element {
      static get is() { return 'polymer-muestra-inicial-app'; }
      static get properties() {
        return {
          prop1: {
            type: String,
            value: 'polymer-muestra-inicial-app'
          }
        };
      }
    }

    window.customElements.define(PolymerMuestraInicialApp.is, PolymerMuestraInicialApp);
  </script>
</dom-module>
Al verlo en la navegador, tenemos algo como esto:

Ahora hacemos que esta introducción valga la pena:

Visto así,  polymer casi nos parece un sistema de plantillas estático, así que para despertar el interés haremos unos cambios en nuestro componente vt-entrada: Los cambios se realizan en la plantilla, agregando un <p></p> cuyo contenido se hace refencia a la propiedad contenido de nuestro componente.

Por otra parte, vamos a cambiar el data binding de nuestro <input> para que funcione en forma bidireccional: En lugar de contenido, ahora será contenido::input.

Lo último a resaltar es que tenemos dos forma de hacer los binding: En una vía mediante llaves y bidireccional mediante corchetes. Esto es otro tema bastante complicado en el que se debería extender con la documentación oficial. Al final, nuestro componente debe verse de la siguiente forma:
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">

<dom-module id="vt-entrada">
    <template>
        <style>
            :host {
                display: block;
            }
        </style>
        <input type="text" value="{{contenido::input}}">
        <p> Este es nuestro contenido escribiente: [[contenido]]</p>
    </template>
    <script>
        class VtEntrada extends Polymer.Element {
            static get is() {
                return 'vt-entrada';
            }
            static get properties() {
                return {
                    contenido: {
                        type: String,
                    }
                }
            }
        }

        window.customElements.define(VtEntrada.is, VtEntrada);
    </script>
</dom-module>
Lo vemos de la siguiente forma en el navegador:
Y sí, el contenido se actualiza conforme se escribe en la caja de texto. Así como que ya dan más ganas de trabajar con esto.

Supongo que para una breve introducción ya está.

viernes, 1 de diciembre de 2017

Otro intento para configurar QoS en Linux: Día 2

Hasta el momento y a grandes rasgos, hemos limitado el uso de ancho de banda a un límite especifíco. Luego, hemos modelado la cola para que esta reparta el uso de ancho de banda de manera casi equitativa entre todas las conexiones: Dicha política se verifica para todo el tráfico en nuestra red.

Lo siguiente será hacer una configuración que planteé precisamente el significado de QoS: Clasificaremos tráfico y vamos a aplicarle políticas específicas a cada tipo de tráfico. Puede pensarse un poco al revés: Estableceremos políticas y luego vamos a especificar a que tráfico le aplicamos cada una.

El primer intento de configuración va de la siguiente forma:
#!/bin/bash

## Borramos la configuración anterior. 
## Cuando se tenga la configuración por defecto, se lanza un mensaje de error "RTNETLINK answers: No such file or directory"
tc qdisc delete dev ens2 root

## Cambiamos qdisc de fast_fifo a htb. 
## Dentro del esquema de HTB, este es llamado ROOT
tc qdisc add dev ens2 root handle 1:0 htb default 10

## Agregamos la primera clase. 
## Dentro del esquema de HTB, este es llamado INNER. No hay shapping en este momento, pero es necesario configurarle para el funcionamiento de los leaf
tc class add dev ens2 parent 1:0 classid 1:1 htb rate 20480 ceil 20480

## Dentro del esquema de HTB, este es llamado LEAF. Este determina la política por defecto 
tc class add dev ens2 parent 1:1 classid 1:10 htb rate 2540 ceil 2540
### Creamos los nuevos LEAF que describen dos nuevas políticas para el tráfico. 
tc class add dev ens2 parent 1:1 classid 1:20 htb rate 7680 ceil 7680
tc class add dev ens2 parent 1:1 classid 1:22 htb rate 10240 ceil 10240

## Dentro de cada clase LEAF, agregamos una QDISC de tipo SFQ. 
## SFQ intentará repartir el ancho de banda de forma más o menos equitivativa
tc qdisc add dev ens2 parent 1:10 handle 130: sfq perturb 5
tc qdisc add dev ens2 parent 1:20 handle 140: sfq perturb 5
tc qdisc add dev ens2 parent 1:22 handle 150: sfq perturb 5

## Filtro en tc
## Se especifica mediante los filtros de tc que tráfico se envía a que política
## Usamos el filtro handle, es bastante simple y económico en términos de procesamiento
tc filter add dev ens2 parent 1:0 prio 0 protocol ip handle 20 fw flowid 1:20
tc filter add dev ens2 parent 1:0 prio 0 protocol ip handle 22 fw flowid 1:22

## Pre-filtro en iptables
## Este es precisamente el filtraje del tráfico: Marcamos con iptables que tráfico es cada cosa
iptables -t mangle -F
iptables -t mangle -A POSTROUTING -p tcp -m multiport --sport 22 -j MARK --set-mark 20
iptables -t mangle -A POSTROUTING -p tcp -m multiport --dport 80,443 -j MARK --set-mark 22
Las clases por ahora se ven de la siguiente forma:
tc class show dev ens2
class htb 1:22 parent 1:1 leaf 150: prio 0 rate 10240bit ceil 10240bit burst 1600b cburst 1600b
class htb 1:1 root rate 20480bit ceil 20480bit burst 1600b cburst 1600b
class htb 1:10 parent 1:1 leaf 130: prio 0 rate 2536bit ceil 2536bit burst 1599b cburst 1599b
class htb 1:20 parent 1:1 leaf 140: prio 0 rate 7680bit ceil 7680bit burst 1599b cburst 1599b
La configuración consta de dos políticas implicítas y una por defecto. Limitan el ancho de banda: El tráfico SSH (Por ahora tendremos que servirnos de este) a 750kbps; tráfico web a 1 Mbps y el demás tráfico, todo aquello que no hallamos considerado, a 250kbps. Revisemos como es que funciona una configuración de este tipo
Un ping con alta latencia. Veamos que pasa al agregar un poco de tráfico HTTPS
La latencia del ping no cambia tanto como se espera, por otro lado, la descarga web parece ir bastante bien. Al agregar un poco de tráfico FTP, bastante conocido por saber acaparar el ancho de banda, tenemos lo siguiente
El uso de ancho de banda para FTP esta bastante contenido y apenas hizo mella en la descarga HTTP. Por ahora hemos verificado que los límites para el ancho de banda en realidad funciona. Hay un problema al que no pude sacarle captura de pantalla pero que se adivina en la alta latencia del ping: DNS puede llegar a dar timeout en estas condiciones. Esto es por la naturaleza misma del tráfico DNS. La solución a este problema es establecer prioridades para las políticas. Por defecto, todos tienen prioridad 0, así que en realidad lo que se hace es quitarle prioridad a ciertas reglas con la opción prio
...
## Dentro del esquema de HTB, este es llamado LEAF. Este determina la política por defecto 
tc class add dev ens2 parent 1:1 classid 1:10 htb rate 2540 ceil 2540 prio 1
### Creamos los nuevos LEAF que describen dos nuevas políticas para el tráfico. 
tc class add dev ens2 parent 1:1 classid 1:20 htb rate 7680 ceil 7680 prio 0
tc class add dev ens2 parent 1:1 classid 1:22 htb rate 10240 ceil 10240 prio 1
... 
Y marcar el tráfico ICMP de la siguiente forma
...
iptables -t mangle -A POSTROUTING -p icmp -j MARK --set-mark 20
...
Ahora, la tasa de transferencia para HTTP sigue con el mismo rendimiento: Es el tráfico ICMP el beneficiado, es posible ver como la latencia ha bajado considerablemente
En este punto, las políticas garantizan una máxima tasa de transferencia para el tráfico que así lo necesita, y la menor latencia posible para el tráfico que así lo requiera.

miércoles, 25 de octubre de 2017

Otro intento para configurar QoS en Linux: Día 1

Nota: Pues que creo que he tenido problemas con las capturas de pantalla y no se corresponden del todo con el trabajo en curso. Igual lo publico, así las cosas. Excepto ese pequeño detalle, todo lo demás es funcional

La idea es que modelemos el tráfico de nuestra red aplicando la siguiente configuración en nuestro firewall linux. O en nuestro IPS inline, que no es tan mala idea como en un principio lo parece. Que incluso puede hacerse una cajita específicamente para esto: Lo importante es que la configuración de este equipo pueda modelar todo el tráfico de nuestra red, al pasar toda nuestra red mediante él.

Por defecto, nuestra configuración tiene la siguiente forma:
tc qdisc show 
qdisc noqueue 0: dev lo root refcnt 2
qdisc pfifo_fast 0: dev ens2 root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc pfifo_fast 0: 10.dev ens3 root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc pfifo_fast 0: dev ens4 root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc pfifo_fast 0: dev ens9 root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
El usuario lo vería de la siguiente manera:

Empezamos con lo que podría considerarse una configuración mínima para hacer shaping:
#!/bin/bash

## Borramos la configuración anterior. 
## Cuando se tenga la configuración por defecto, se lanza un mensaje de error "RTNETLINK answers: No such file or directory"
tc qdisc delete dev ens2 root

## Cambiamos qdisc de fast_fifo a htb. 
## Dentro del esquema de HTB, este es llamado ROOT
tc qdisc add dev ens2 root handle 1:0 htb default 10

## Agregamos la primera clase. 
## Dentro del esquema de HTB, este es llamado INNER. No hay shapping en este momento, pero es necesario configurarle para el funcionamiento de los leaf
tc class add dev ens2 parent 1:0 classid 1:1 htb rate 20480 ceil 20480

## Dentro del esquema de HTB, este es llamado LEAF. Acá ocurre el shapping por primera vez
tc class add dev ens2 parent 1:1 classid 1:10 htb rate 20480 ceil 20480

## Dentro de la última (Y única por ahora) clase LEAF, agregamos una QDISC de tipo SFQ. 
## SFQ intentará repartir el ancho de banda de forma más o menos equitivativa
tc qdisc add dev ens2 parent 1:10 handle 130: sfq perturb 5


Ahora, estamos configurados de la siguiente forma:
tc qdisc show dev ens2
qdisc htb 1: root refcnt 2 r2q 10 default 10 direct_packets_stat 0 direct_qlen 1000

tc class show dev ens2
class htb 1:10 parent 1:1 leaf 130: prio 0 rate 20480bit ceil 20480bit burst 1600b cburst 1600b
class htb 1:1 root rate 20480bit ceil 20480bit burst 1600b cburst 1600b
El resultado es que ahora nos vamos limitar: 20480bit representa un enlace de 2 MB/s. Desde el punto de vista del cliente nos vemos de la siguiente forma: Al iniciar ambas peticiones casi al mismo tiempo, el uso de ancho de banda se reparte casi igual
Conforma pasa el tiempo, el tráfico para FTP-DATA consigue un mejor rendimiento. Luego, también podría ocurrir si se inicia la descarga de FTP primero:
En general, con pfifo_fast ocurrirá que algunas conexiones irán mejor que otras. Posiblemente las más nuevas (Esto es bien común de verificar). Algunos protocolos sbbre otros. Para tener mejor idea, descargo el mismo contenido usando HTTP en ambos clientes
El cliente a la derecha descarga con todo el ancho de banda disponible en ese momento

Cuando empieza la descarga en el cliente a la izquierda, este es claramente beneficiado de un mejor ancho de banda

Una solución a este problema (Que el ancho de banda se reparta mejor entre todas las conexiones), es usar una disciplina de cola en la clase leaf. SFQ es bastante sugerido para esta trabajo en específico. Agremos lo siguiente al final del script anterior

## Dentro de la última (Y única por ahora) clase LEAF, agregamos una QDISC de tipo SFQ.
## SFQ intentará repartir el ancho de banda de forma más o menos equitivativa
tc qdisc add dev ens2 parent 1:10 handle 130: sfq perturb 5
Una vez configurado así, no importa en que momento inicia la descarga el uno o el otro: Ambas conexiones tienen casi el mismo ancho de banda disponible. El "casi" es importante. SFQ "intenta" darles el mismo ancho de banda a ambas conexiones. Con todo lo que lleva en contra, parece que hace un trabajo bastante aceptable

martes, 24 de octubre de 2017

Otro intento para configurar QoS en Linux: Segunda introducción

Sobre como configurar ancho de banda en TC

Sobre este punto, tc puede ser complicado en tanto las nociones sobre unidades de transmisión suelen ser confusas para la mayoría de administradores de sistemas. Aún más, suelen confundirse las notaciones y usarse los términos indistintamente.

Al hablar de ancho de banda, la unidad de medida a usar es bites por segundo, que debería abreviarse como bps. Esta es la unidad de medida que suelen usar los proveedores de internet: Así, al decir megas, se refieren a Megabits por segundo; así por ejemplo, 2 Mbps en su forma abreviada.

Por otra parte, la mayoría de aplicaciones suelen medir la cantidad de bytes (Entendido como unidad de almacenamiento) que recibimos en una unidad de tiempo. Esta es la que se debería escribir como bytes/s

Para entender que estamos midiendo, pues considere dos cosas: Primero, la relación entre bytes/s y bps y  es de 1 a 8: 1 bytes/s = 8bps.  Luego, que como son unidades muy pequeñas, es común que ambas se usen como el prefijo kilos: KB/s y kbps, la relación sigue siendo la misma. 
(El truco más barato para saber que unidad de medida esta usando, es que si es muy grande, posiblemente se refiera a bps)

Luego esta tc. Se supone que tc acepta ambas unidades de medidas al agregar la unidad como sufijo. Hay que prestar atención para entender la confusión:
  • kbit se refiere a kilobits por segundo (Para este no hay problema). Esta también puede escribirse como Kbit
  • kbps se refiere a kilobytes por segundo (Acá se usa la notación que se espera para la otra unidad de medida)

Si ya domina la confusión sobre unidades de transferencia, puede venir a entender a tc

tc usa bits por segundo como unidad por defecto

Sobre como clasificar tráfico para tc
Nuestra primera opción es la de usar el componente filter de tc. En este caso, se considera que el clasificador u32 es el máximo exponente. Pero dado que puede convertirse en algo complicado, tenemos la opción de seguir usando a filter con algo más sencillo: Buscar "marcas" hechas con iptables mediante MARK target. También es posible usar a TOS target (Nos limitaría un poco) y a DSCP target (Con más opciones) para buscar valores en ambas cabeceras, pero tienen el inconveniente que podría tener efectos inesperados con algunos paquetes que ya tienen marca de acuerdo a sus programas de origen.

Mi mejor apuesta es a usar el CLASSIFY target, que como se verá en las pruebas que realicemos, nos ahorra, al menos a nivel de configuración, al componente filter de tc, casi nada.

lunes, 23 de octubre de 2017

Otro intento para configurar QoS en Linux: Introducción

Aunque no tengo algo como esto en producción, quería empezar a registrar algunas ideas al respecto:

Es posible que lo que entiendas por QoS sea una especie de cajón de sastre: 

Como muchos otros conceptos en informática, dicho sea de paso.
En este caso, no ayuda que en realidad haya muchos conceptos involucrados:
  • La definición misma del Protocolo de Internet (RFC791) describe en sus cabeceras al campo TOS, que permitiría elegir la calidad de servicio, es decir, los parámetros requeridos a la red por la que el paquete va a atravesar. 
  • Para ser más preciso, quizá casi a manera de curiosidad, parece haber un RFC específico para TOS, pero esta marcado como obsoleto debido a RFC2474
  • Precisamente, una ampliación de TOS se realiza en Differentiated Services (RFC2474), que básicamente define más técnicas para mejorar la calidad de la red.
  • Dentro del campo DS (Differentiated Service), de los 8 bits que lo componen, 6 bits se corresponden con DSCP (Differentiated Service Code Point). Pues sí, DSCP es parte de DiffServ, algo que en la práctica no puede tener mayor repercusión, pero que parece confundir a muchos
En GNU/Linux, la Linux Advanced Routing & Traffic Control debe considerarse La Guía de Referencia sobre QoS en este ambiente. En realidad, trata sobre casi todas las cosas más avanzadas que es posible configurar en la red de un sistema GNU/Linux, así que después después de los primeros capítulos, introductorios, nos interesa Chapter 9. Queueing Disciplines for Bandwidth Management. Es una lectura poco extensa pero que como introducción cumple bastante bien su cometido.
Recomendable leer HTB Linux queuing discipline manual - user guide, manual sobre la disciplina de encolado que vamos a usar, y que representa el núcleo de toda la configuración. De hecho, todo lo demás que puedan ofrecer esta guía proviene de allí, así que igual podría irse a las fuentes originales

Qos no es el mesías hecho software

No va a aumentar el ancho de banda mágicamente. Su función es resolver problemas bien específicos, entre los cuales no está el acelerar la navegación web. Es decir, puede hacerse algo, pero no tanto: Parece que en general, podrían haber cambios de percepción de cara al usuario, pero al final hablamos de un par de milisegundos ganados.

Algunos conceptos a tener en cuenta

A grandes rasgos, tres cosas se realizan dentro de las reglas de QoS: Restringir ancho de banda, modelar colas y clasificar el tráfico para establecer precisamente que tipo de tráfico recibirá que reglas
  • Shaping:  Se refiere a la administración del ancho de banda. Dentro de tc, se realiza con las class
  • Queueing: Se refiere a la forma en modelamos las colas. De hecho, la mayoría de documentación se vertebran respecto a este aspecto. Dentro de tc, se realiza con qdisc (Disciplina de colas)
  • Filter: Clasificar el tráfico es realmente el paso fundamental en QoS. Por diseño, el tráfico en TCP/IP no lo está, clasificarlo es el primer paso de cara a establecer un comportamiento de parte de la red hacia ellos
Pero estos activadades no se corresponden con todos sus componentes. Debería leerse Traditional Elements of Traffic Control, que de todo lo disponible, es la lectura teórica más importante que podría hacer de todo este tema.
Aprovechando, a Components of Linux Traffic Control prácticamente debe memorizarlo

Fuentes y lecturas complementarias

jueves, 12 de octubre de 2017

Resolviendo problemas con Nvidia GLX en Fedora 26

Aunque Fedora 26 ya tenía mucho andando en mi equipo, no había resentido el problema de GLX excepto en algunas cuestiones puntuales:
  • Al intentar ejecutar glxinfo desde consola, aparecía un mensaje de error: BadWindow (invalid Window parameter). También aparecía cuando intentaba acceder a la pestaña
  • También aparecía al intentar acceder a  X Screen 0 > OpenGL/GLX Information en Nvidia X Server Settings
  • Al estar usando LXQT como entorno gráfico, SDDM, el gestor gráfico (No me había dado cuenta de como llamo a estas cosas) no se mostraba en pantalla. Básicamente, SDDM cargaba como pantalla negra (Pero cargaba: Bastaba escribir la contraseña, hacer Enter y cargaba mi sesión) . Este era precisamente lo único que se me antojaba como un error.
Al revisar los log de SDDM, encontré algo parecido a Unrecognized OpenGL Version. Supuse que GLX no cargo y decidí buscar en los log de Xorg en /var/log/Xorg.0.log:
[    35.236] (EE) NVIDIA(0): Failed to initialize the GLX module; please check in your X
[    35.236] (EE) NVIDIA(0):     log file that the GLX module has been loaded in your X
[    35.236] (EE) NVIDIA(0):     server, and that the module is the NVIDIA GLX module.  If
[    35.236] (EE) NVIDIA(0):     you continue to encounter problems, Please try
[    35.236] (EE) NVIDIA(0):     reinstalling the NVIDIA driver.
Revisé con más atención el fichero y me doy cuenta que la carga del módulo ocurre, pero lo hace con el módulo de Xorg
(II) LoadModule: "glx"
(II) Loading /usr/lib/xorg/modules/extensions/libglx.so
(II) Module glx: vendor="X.Org Foundation"
compiled for 7.1.1, module version = 1.0.0
ABI class: X.Org Server Extension, version 0.3
De todas las soluciones posibles (Borrarlo, enlace simbólico, etc), me pareció que la mejor opción era cambiar el nombre del módulo GLX de Xorg para que al no hallarlo cargara el de NVIDIA
cd /usr/lib64/xorg/modules/extensions/
mv libglx.so libglx.xorg.so
Y al reiniciarlo, todo bien, ningún problema

Fuentes: (Lo más cercano que tuve a un consejo)

miércoles, 30 de agosto de 2017

Configurando Squid como proxy transparente HTTPS: Configuración de SslBump en Squid

En este punto, la terminología empieza a ser un poco confusa. Como ya lo he dicho, muchas otras guías consiguen lo mismo en Debian Jessie usando Squid 3.4 (1, 2); CentOS 7 (1 la cual a su modo es muy completa) y CentOS 6 (1) entre otros.

Fue esta guía (Squid (v3.5+) proxy with SSL Bump la que señala la forma correcta de configurar ssl_bump en squid v3.5+; básandome en ella, hago una mejora mínima con la cuestión del certificado.
Crear un certificado puede ser algo tan sencillo como se recoge en Intercept HTTPS CONNECT messages with SSL-Bump, pero ese procedimiento tiene el inconveniente de enviar al cliente la clave pública y privada con la que funciona el servidor. La separamos creando primero la clave privada de la siguiente forma:
 $ openssl genrsa -out myCA.key 4096                                                                                                                                                                          [0/1508]
Generating RSA private key, 4096 bit long modulus
..........++
.....................................................................++
e is 65537 (0x010001)
Y luego la clave pública así:
$ openssl req -sha256 -new -x509 -days 1826 -key myCA.key -out myCA.crt
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:SV
State or Province Name (full name) [Some-State]:San Salvador
Locality Name (eg, city) []:San Salvador
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Dirección Central
Organizational Unit Name (eg, section) []:Establecimiento
Common Name (e.g. server FQDN or YOUR name) []:Establecimiento dependiente
Email Address []:vtacius@gmail.com
Cambiamos un poco el esquema de seguridad para los ficheros de la siguiente forma. Incluso las podría haber mejores
$ usermod -G ssl-cert proxy
$ chmod 770 /etc/ssl/private/
$ chmod 640 myCA.*
$ chown root:proxy myCA.*
$ cp -a myCA.key /etc/ssl/private/
$ cp -a myCA.crt /etc/ssl/certs/
Ahora configuramos precisamente a squid. No se necesita más que asegur tener las siguientes líneas:
http_port 10.168.4.1:3128
http_port 10.168.4.1:3129 transparent
https_port 10.168.4.1:3130 ssl-bump intercept generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/etc/ssl/certs/myCA.crt key=/etc/ssl/private/myCA.key

...
ssl_bump stare all
ssl_bump splice all
...
La configuración de ssl_bump aún sigue siendo un poco complicada, aún cuando existe bastante documentación al respecto, hasta ahora entiendo que va de esta forma:
  • ssl_bump stare all:  Básicamente, permite una primera conexión con el servidor remoto. Por este paso es que se requiere que los clientes que usen proxy transparente tenga bien configurado la resolución DNS. Y no, no encuentro una mejor forma de cambiar este paso, si no se especifica, pues que la conexión no realiza bien.
  • ssl_bumps splice all: En esta parte hacemos precisamente la conexión. Usar splice en lugar de bump permite al tráfico salir con menos trabajo por parte del servidor. En este punto, ni siquiera entiendo para iría a necesitar tanto trabajo por parte del proxy
En las guías anteriores, se recomiendan configuraciones como
ssl_bump server first all  
sslproxy_cert_error deny all  
sslproxy_flags DONT_VERIFY_PEER 
Pero ninguna de estas deberían ser necesarias para esta versión de squid. La otra opción que valdría la pena configurar es:
sslcrtd_program /usr/lib/squid/ssl_crtd -s /var/lib/squid_ssl/ -M 4MB
Ya que los valores por defecto no están del todo bien:
sslcrtd_program /usr/local/squid/libexec/ssl_crtd -s /var/lib/ssl_db -M 4MB
En ambos casos, es necesario inicializar el directorio mencionado:
/usr/lib/squid/ssl_crtd -c -s /var/lib/squid_ssl/
Initialization SSL db...
Done
TODO: Pueden hacerse bastante mejoras aún con el certificado

martes, 29 de agosto de 2017

Configurando Squid como proxy transparente HTTPS: Squid con soporte SSL

 Build errors with Squid 3.5.24 under Debian
La mejor forma de modificar los parámetros de compilación en Debian es mediante sus herramientas de compilación, casi, casi como si fuéramos a trabajar como empaquetadores.

Que bien podría bajarse el source desde la página oficial, pero había que configurar más cosas a manos y olvidarnos del soporte de Debian; podríamos bajar una versión o rama más novedosa (Para squid, v4 esta en Beta, aunque la gente de Fedora ya la considera lo suficientemente estable como para incluirla como versión por defecto)

Por tanto, los pasos a realizar serán básicamente los siguientes:
apt-get install dpkg-dev devscripts quilt libcrypto++-dev libssl1.0-dev

mkdir /root/paquetes
chown _apt -R paquetes/
cd paquetes/ 
apt-get source squid
apt-get build-dep squid

# Podríamos verificar el caso improbable que el proceso de compilación del paquete tenga problemas
cd squid3-3.5.23/ 
debuild -b -uc -us
Respecto al siguiente paso, hay varias versiones de lo que podríamos hacer. El parche podría trabajarse de la siguiente forma
quilt push -a
quilt new 0032-add_ssl_crtd.patch
quilt add debian/rules
## Hacemos la modificación en este punto
quilt refresh
quilt pop -a -f
Pero es un poco innecesario, creo que realizando la modificación directamente en el fichero correpondiente, debian/rules no debería haber mayores problemas: Modificamos DEB_CONFIGURE_EXTRA_FLAGS agregando estas opciones:
vim debian/rules
+       --with-openssl \
+       --enable-ssl-crtd \
Sea como sea, una vez modificadas las reglas de configuración previas, podemos contruir el paquete.
debuild -b -uc -us
Pues deberían haberse construido los paquetes y podemos probar a instalar en el mismo equipo
apt-get install squid-langpack libdbi-perl libcap2-bin libecap3
dpkg -i ../squid-common_3.5.23-5_all.deb ../squid_3.5.23-5_amd64.deb ../squid3_3.5.23-5_all.deb
Ahora, la mejor recomendación que puedo hacer es que se construyan los paquetes en un equipo diferente al firewall. Una máquina virtual incluso debería funcionar Una vez contruidos, los enviamos al firewall y lo instalamos de la siguiente forma:
apt-get install squid-langpack libdbi-perl libcap2-bin libecap3 libssl1.1
dpkg -i squid_3.5.23-5_amd64.deb squid-common_3.5.23-5_all.deb squidclient_3.5.23-5_amd64.deb squid3_3.5.23-5_all.deb
# A partir de este punto, todo paquete que dependa de squid se dará por satisfecho. Por ejemplo
apt-get install squidguard{,-doc} sarg
Escojo pinning como método para evitar que el paquete sea actualizado
cat <<MAFI >/etc/apt/preferences.d/squid
Package: squid 
Pin: release n=stretch
Pin-Priority: -1

Package: squid3
Pin: release n=stretch
Pin-Priority: -1

Package: squid-common
Pin: release n=stretch
Pin-Priority: -1

Package: squidclient
Pin: release n=stretch
Pin-Priority: -1
MAFI
Fuentes: 1, 2

lunes, 28 de agosto de 2017

Configurando Squid como proxy transparente HTTPS: Introducción

La comunidad ha esperado mucho para llegar a este punto. Los manuales sobre la configuración aún siguen siendo bastante confusos al respecto, pero al fin logré una configuración funcional a integrar en mi proyecto ya existente.

Descrito pronto, el proyecto usa a squid como proxy caché, y este, usa a squidGuard mediante  url_rewrite_program para filtrar la navegación de los usuarios mediante las listas negras de shallalist .

Esta configuración mejora el proyecto en dos cosas básicamente:
  • Los usuarios no necesitan configurar proxy para navegar por HTTPS (Aunque si necesitarían instalar un certificado)
  • Los usuarios podrán ver un mensaje de error cuando navegan a un sitio bloqueado que usa HTTPS, aunque parezca poca cosa, se considera en realidad un gran avance de cara a los usuarios
 En este momento, me preocupan dos cosas que también deberían preocuparle a todos los que quieran seguir esta guía:
  • El rendimiento por parte de squid para servir las páginas
  • ¿Tendrá algún sitio demasiados problemas para funcionar con esta configuración?
Al momento de escribir esta entrada, esta configuración no ha entrado en producción, so...

Tome en cuenta que como debo instalar en varios firewall, la idea será compilar en un equipo independiente de lo que sea el firewall. De hecho, aún cuando se compile para un sólo equipo, debería considerar trabajar de esta forma.

Voy a repetir lo que ya se ha dicho hasta el cansancio: Hay cuestiones éticas muy importantes en esta configuración. Básicamente, lo mejor es explicar al usuario lo que se esta haciendo.

jueves, 22 de junio de 2017

Apuntes sobre el error de Squid / SquidGuard en Debian: Solucionado

La nueva versión de Debian ha salido el sábado pasado. Y me aventuré a probar que se hubiera solucionado el problema con redirector protocol que detuvo tan mal a mi proyecto de firewall.

Pues resulta que no, el problema seguía siendo tal, pero esta vez tenía un poco más de tiempo disponible que la última vez (Y un problema más serio, al estar dos lanzamientos atrasados con el sistema base).

Lo quiero resumir de esta forma: El problema no era tal.
Básicamente porque es un problema que es posible resolver con un cambio en la configuración, no se debía (No del todo, al menos) a un falta de actualización en la forma en que squidGuard entendía el redirector protocol, como yo pensé en mi primer post

El truco está en la configuración del redirector en Squid. Por defecto, yo tenía
url_rewrite_children 15 startup=0 idle=1 concurrency=3
Pero según la documentación en Squid configuration directive url_rewrite_children:
concurrency=

 The number of requests each redirector helper can handle in
 parallel. Defaults to 0 which indicates the redirector
 is a old-style single threaded redirector.

 When this directive is set to a value >= 1 then the protocol
 used to communicate with the helper is modified to include
 an ID in front of the request/response. The ID from the request
 must be echoed back with the response to that request.
 
La cuestión es que al programa configurado como redirector le debe llegar una petición por parte de squid de la siguiente forma:
"http://www.google.com.sv/ 10.168.3.5/10.168.3.5 - GET myip=10.168.3.1 myport=3128";
Pero configurado concurrency, tal como lo ha dicho la documentación, se le antepone un identificador, de modo que la consulta queda de la siguiente forma.
"0 http://www.google.com.sv/ 10.168.3.5/10.168.3.5 - GET myip=10.168.3.1 myport=3128";
Lo que pasa es que lo interpreta mal: Para el, la URL que se solicita es 0 y el identificador del cliente es la URL; de hecho, toma a http: como la dirección IP del cliente, es decir, la primera parte de ip_cliente/fqdn...

La solución es algo tan sencillo como retirar concurrency:
url_rewrite_children 15 startup=0 idle=1 concurrency=0 

Si bien squidGuard debería ser capaz de manejar este problema, pues no, no voy a reclamarle tanto a la única opción viable frente a cosas como e2guardian.
Yo mismo he intentando parchear el paquete en Debian, intentando quitar el ID de la petición antes de que squidGuard lo siguiera parseando, pero tampoco, es que aunque ha funcionado parece que todo va mal, las respuestas hacia el cliente ralentizan la navegación. Que ni siquiera revisé los log de Squid con mayor detenimiento, es que tampoco tengo todo el tiempo del mundo.

miércoles, 31 de mayo de 2017

Integrando (Un poco) pyramid en Eclipse: Test Automatizados

Esto será breve. Es más bien una revisión, porque con la forma en que lo hemos configurado esto se ha hecho automáticamente, y bien.

Por otra parte, acá hay algo raro que será mejor verificar: Desde una consola de nuestro entorno virtual, hay que instalar nose, y en Fedora, al menos para mí, webtest, así, en minúsculas
virtualenv proyecto-ambiente
pip install nose
pip search webtest
Buscamos a proyecto en Project Explorer. Accedemos a propiedades con Alt + Enter o desde el menú desplegable al hacer click derecho.

Buscamos la pestaña Run/Debug Settings. Hay dos perfiles configurados. En general, el que nos importa es aquel que se llama <nombre del proyecto>_test. En nuestro caso que nuestro proyecto se llama proyecto:

Lo seleccionamos en la lista, y hacemos click en Edit y aparece un cuadro de diálogo con las configuración de dichos perfiles

En la pestaña Main, debe estar configurado de la siguiente manera. Si se configuro tal como en el post anterior, ya debería estar así

En Arguments, debe estar checado Override PyUnit preferences for this launch?, de esta forma podremos configurar Nose test runner al elegirlo desde la lista desplegable

En la pestaña Interpreter, pues si, debería configurarse python_proyecto, el intepréte personalizado que configuramos en el post anterior pero parece haber un bug en esto, aunque se elija y configure tal, no se queda configurado
Por último, la verificación es de lo más sencilla. Corremos los test que vienen por defecto en la plantilla de proyecto starter de pyramid:
Vemos que, en hacia el fondo de la aplicación, hallamos la ventana Console con el resultado de la operación:

Dos cosas por si se me olvida registrarlas después:
  • Para hacer más test, lo mejor será borrar el fichero test.py y luego crear una carpeta test/. Asimismo, el nombre de cada fichero debe comenzar con test
  •  

martes, 30 de mayo de 2017

Integrando (Un poco) pyramid en Eclipse

Una vez hayamos creado un proyecto (Uno sencillo, como este), configuramos a Eclipse para trabajar de forma más o menos integrada con pyramid. No, no va estar del todo integrado, apenas lo suficiente para asegurarnos de que va a autocompletar con las librerías de pyramid y para realizar los test automatizados sin mayores inconvenientes. Es que, básicamente, esto va sobre configurar el entorno virtual que creamos para este proyecto.

Y antes de otra cosa, es necesario tener instalado PyDev para que esto funcione. Y como hay guías más especializadas allá fuera, no repetiré

Abrimos el menú para abrir proyectos en File > Open Project from File System. Aparece el siguiente cuadro de diálogo

Haciendo click en Directory, nos aparece un cuadro de dialogo del sistema para buscar el directorio donde se encuentra nuestro proyecto.
Yo escogí proyecto-ambiente/proyecto (Para coincidir con el punto donde configuro el repositorio git, gusto mío), pero habrá quién prefiera proyecto-ambiente/proyecto/proyecto, que es donde se encuentra precisamente nuestro código.

Luego, se verá de la siguiente manera una vez configurado. Haciendo click en Finish, habremos terminado de configurar nuestro proyecto.

A continuación, necesitamos que Eclipse reconozca que el proyecto es python para que active ciertas funciones.
En el Project Explorer, usualmente a la izquierda, hacemos click derecho en nuestro proyecto; en el menú desplegable que aparece nos dirigimos a PyDev > Set as PyDev Project.


También podría bastar con abrir una fichero python, pero no es que la primera sea la "forma más correcta", sino que habría que pelearse más con la configuración.

Ahora, en el anterior menú desplegable del proyecto buscamos Properties o hacemos Alt + Enter para acceder al siguiente cuadro de diálogo.



Nuestra primera pestaña a configurar será PyDev - Interpreter/Grammar

La idea es configurar en Interpreter al que se encuentra instalado en nuestro entorno virtual proyecto-ambiente, y para ellos habrá que configurarlo haciendo click en Click here to configure an interpreter not listed
Aparece la siguiente ventana
Haciendo click en New, nos aparece el siguiente Cuadro de diálogo.

En Interpreter Name, configuramos un nombre cualquiera vagamente descriptivo, tanto más si vamos a trabajar en varios proyectos a la vez. 

Para Interpreter Executable, hacemos click en Browse, y si, aparece un cuadro de diálogo de tipo fichero. Buscamos dentro de nuestro entorno virtual proyecto-ambiente el directorio de ejectubles bin/, y dentro de este, a python. El cuadro de diálogo queda de la siguiente forma:

Al hacer click en Ok, nos aparece un cuadro de diálogo Selection needed, donde se nos pide agregar las librerías que se corresponden con ese interprete

Ahora, se puede ver este nuevo intérprete configurado

En este punto, podemos configurarlo como intérprete de nuestro proyecto en la lista desplegable Interpreter.

Hacemos click en Apply para seguir configurando. O en Ok, que esto sigue en otro post.

Mi objetivo se cumple: El autocompletado se rebusca en las librerías del entorno virtual


lunes, 29 de mayo de 2017

Integrando (Un poco) pyramid en Eclipse: Creando el proyecto

Este artículo muestra la forma más sencilla de empezar un proyecto con Pyramid, ya que a diferencia de Primero pasos con Pyramid no se modifica nada en orden de iniciar un gran proyecto. Además, esta vez hice algunas capturas de pantalla y creo que esa es la principal justificación

Creamos un entorno virtual para el proyecto de nombre proyecto y entramos al directorio creado
virtualenv proyecto-ambiente
cd proyecto-ambiente
Activamos el entorno virtual. Esto lo hacemos siempre que vayamos a usarlo. Así es como usamos las librerías y ejecutables que instalemos en él y no los del sistema.
source bin/activate
En este momento, el entorno se ve más o menos de esta forma:
Instalamos pyramid
pip install pyramid
Creamos un proyecto de nombre proyecto con la plantilla starter
pcreate -s starter proyecto
El pcreate ha creado un directorio proyecto:
Instalamos los paquetes python que este proyecto en específico necesita. Se configuran en proyecto/setup.py, por ahora lo dejamos tal como se configuro por defecto
python proyecto/setup.py develop
Y nuestro proyecto se ve de esta forma
Y eso es todo.

lunes, 13 de marzo de 2017

Usando snapshot para equipos virtualizados con XEN en Debian

Pese al título, esta entrada aún necesita llevar un subtítulo: "Usando qcow en máquinas virtuales en Debian Jessie", y ya, no se necesitaría mayor introducción al respecto.

Cuando se sigue una guía de virtualización en Debian, se cae por defecto en la virtualización con XEN. No hay problema, pero parece que hasta Jessie, las herramientas para administración por defecto siguen sin manejar la cuestión de snapshot para las máquinas virtuales.

Tampoco fuí capaz de encontrar una opción en xen-create-image que permita crear las imágenes con formato qcow. Crear la imagen con qemu-img create y luego apuntarla en xen-create-image con --image-dev y --swap-dev  no funciona porque sin importar qué, xen-create-image convierte las imágenes a raw.

Con todo esto, es necesario complicar el proceso de instalación de la siguiente manera:
  • Creamos la máquina virtual con un disco raw, que es el formato por defecto:
xen-create-image  --hostname=pdc.salud.gob.sv --vcpus=2 --size=20Gb --memory=896Mb --ip=10.20.20.10 --gateway=10.20.20.1 --bridge=xenbr0 --arch=amd64 --role=udev --dir=/var/lib/xen/
La instalación del sistema base empezará creando dos imágenes: disk.img y swap.img , dentro de /var/lib/xen/domains/pdc.salud.gob.sv/ en este caso según el hostname dado.

  • Lo convertimos a qcow (Respecto a preallocation: Resulta que en Jessie no es posible usar full, además, no soy capaz de decir si para el caso de conversión esta opción tiene algún tipo de efecto)
qemu-img convert -O qcow2 -o preallocation=metadata /var/lib/xen/domains/pdc.salud.gob.sv/disk.img /var/lib/xen/domains/pdc.salud.gob.sv/disk.qcow2
  • Ahora, modificamos el fichero que define a la máquina virtual para que apunte al nuevo disco: Por xen-create-image, el fichero había quedado de esta forma:
#
#  Disk device(s).
#
root        = '/dev/xvda2 ro'
disk        = [
                  'file:/var/lib/xen/images/domains/pdc.salud.gob.sv/disk.img,xvda2,w',
                  'file:/var/lib/xen/images/domains/pdc.salud.gob.sv/swap.img,xvda1,w',
              ]
Y el gran cambio será file: por tap:qcow2: y la extensión: .img por .qcow2 en donde se apunte al disco que cambiamos
#
#  Disk device(s).
#
root        = '/dev/xvda2 ro'
disk        = [
                  'tap:qcow2:/var/lib/xen/images/domains/pdc.salud.gob.sv/disk.qcow2,xvda2,w',
                  'file:/var/lib/xen/images/domains/pdc.salud.gob.sv/swap.img,xvda1,w',
              ]
Fuentes:
Using qcow2 images in Xen 4.1 on Debian 

Otros apuntes interesantes

Otros apuntes interesantes