tag:blogger.com,1999:blog-14579764484764385612024-03-12T19:47:43.869-06:00Apuntes TuxianosUn colección de apuntes con los que espero guardar de loa más interesante que haga con GNU/LinuxVTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.comBlogger64125tag:blogger.com,1999:blog-1457976448476438561.post-21701244781914337352018-09-18T10:26:00.001-06:002018-09-18T10:26:19.547-06:00Resolviendo problemas con nginx en zimbra 8.0.6Yo entiendo que usar una versión tan vieja tendrá sus problemas, pero como no tengo tiempo para dedicar a revisar una actualización, pierdo mi tiempo en este tipo de cosas:<br />
<br />
Los usuarios se quejan (Como usuarios, es su única tarea) de que al intentar descargar [correos con] archivos [adjuntos] grandes hay problemas.<br />
Así que configuro Thunderbird, dispuesto a esperar que baje todos los correos que ahora están en mi cuenta; pero resulta que no pasa nada. Ha bajado unos cuantos nada más; al hacer click en "Recibir mensajes" vuelve a bajar un par más. Incluso a veces, al hacer click, no baja ninguno.<br />
<br />
Después de revisar los logs, logró hallar un único problema en /opt/zimbra/log/nginx.log:
<br />
<pre class="brush:bash">2018/09/18 08:48:07 [info] 10103#0: *258 client logged in, client: 192.168.2.20 using starttls, server: 0.0.0.0:110, login: "alortiz", upstream: 10.10.20.2:7995 (192.168.2.20:45070-10.10.20.2:110) <=> (10.10.20.2:51452-10.10.20.2:7995)
2018/09/18 08:48:31 [error] 10103#0: *258 SSL_read() failed (SSL: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac) while proxying and reading from upstream, client: 192.168.2.20 using starttls, server: 0.0.0.0:110, login: "alortiz", upstream: 10.10.20.2:7995 (192.168.2.20:45070-10.10.20.2:110) <=> (10.10.20.2:51452-10.10.20.2:7995)
2018/09/18 08:48:31 [info] 10103#0: *258 proxied session done, client: 192.168.2.20 using starttls, server: 0.0.0.0:110, login: "alortiz", upstream: 10.10.20.2:7995 (192.168.2.20:45070-10.10.20.2:110) <=> (10.10.20.2:51452-10.10.20.2:7995)</pre>
La segunda línea es la que cuenta. Mi primera sospecha, acorde a <a href="https://forums.zimbra.org/viewtopic.php?t=13422#p154193">ZCS 8.0.2 Proxy Issues with downloading large mails via pop</a>, es la configuración de SSLv3. <br />
Después de revisar <a href="https://wiki.zimbra.com/wiki/How_to_disable_SSLv3">la guía que seguí para deshabilitarlo</a>, he encontrado que el problema se debe a una configuración no contemplada: La comunicación entre el Nginx (Proxy) con POP3. La recomendación más usual es deshabilitar SSL en esa comunicación, lo que no es tan grave considerando que es una instalación en un sólo servidor, así que nada, el hecho que haya datos en texto claro no es tan grave (Que lo es).<br />
Así que todo esto se ha solucionado al correr como usuario zimbra:
<br />
<pre class="brush:bash">zmprov ms mail.salud.gob.sv zimbraReverseProxySSLToUpstreamEnabled FALSE
</pre>
Una vez configurado, posiblemente haya que reiniciar el proxy
<br />
<pre class="brush:bash">zmproxyctl restart
</pre>
Fuentes: (La idea es bien recurrente)<br />
<a href="https://forums.zimbra.org/viewtopic.php?t=13422&start=20#p154191">ZCS 8.0.2 Proxy Issues with downloading large mails via pop</a><br />
<a href="https://forums.zimbra.org/viewtopic.php?t=56912&start=10#p261494">zimbra server delivering email to pop3 clients timeout</a> <br />
<br />VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-31339424189430295482018-03-14T09:01:00.000-06:002018-03-14T09:03:49.023-06:00FTP Pasivo con IptablesQue sí, es penoso que a estas alturas del siglo se siga configurando FTP como una especie de solución para almacenamiento. <br />
<br />
El objetivo de este post es resolver cierto problema con los permisos que se configuran en nuestro firewall con iptables para el FTP pasivo. Que básicamente no funcionan.<br />
<br />
El FTP pasivo se beneficia de las reglas para tráfico "de respuesta" que identificamos mediante el módulo conntrack
<br />
<pre class="brush:bash">-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
</pre>
En nuestra cadena FORWARD de FILTER al hablar de nuestro firewall; esto, una vez que hayamos cargado el módulo ip_conntrack_ftp y, hasta donde entiendo, nf_nat_ftp.
<br />
<pre class="brush:bash">modprobe nf_nat_ftp
modprobe ip_conntrack_ftp
</pre>
Para hacerlo permanente:
<br />
<pre class="brush:bash">sed -i '$a ip_conntrack_ftp\nnf_nat_ftp ' /etc/modules
</pre>
Aún con eso sigue sin funcionar. La solución la encontré en <a href="https://unix.stackexchange.com/a/308357/196138">Iptables to allow incoming FTP</a>: Debe configurarse el kernel con net.netfilter.nf_conntrack_helper=1. Yo soy más de la idea de ponerlo directamente en /etc/sysctl.conf, pero en realidad puede ir al gusto:
<br />
<pre class="brush:bash">sed -i '$a net.netfilter.nf_conntrack_helper=1' /etc/sysctl.conf
</pre>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-46554235936001122752017-12-19T11:53:00.001-06:002017-12-19T11:53:57.220-06:00Polymer: Verificando shadow DOMPodrí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.<br />
<br />
En <b>src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html</b>, agrego un ligero estilo y una pequeña línea, de tal forma que el fichero queda de la siguiente forma:
<br />
<pre class="brush:js">
<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>
</pre>
Luego,
<b>src/componentes/entradas/vt-entrada.html</b> ha quedado de la siguiente forma: Y sí, sólo agregue un color de fondo y subrayado para el texto:<br />
<pre class="brush:js"><link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<dom-module id="vt-entrada">
<template>
<style>
:host {
display: block;
}
p {
background-color: beige;</pre>
<pre class="brush:js"> text-decoration: underline; </pre>
<pre class="brush:js"> }
</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>
</pre>
El resultado viene siendo elsiguiente:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTiQNwbLcCjugvkdx3eBaLZSUBICF8AWQx5xY7qvmyqEEDblpnBubRNLdM2LCitcQ-9O74gJV_sYG8EAL49POYDoUKwzS8XGikuo2ni2DXw1cYV6cyprnj8E0v_1k7M9BzJcwQWDEUigk/s1600/Screenshot-2017-12-19+polymer-muestra-inicial%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="221" data-original-width="1196" height="118" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTiQNwbLcCjugvkdx3eBaLZSUBICF8AWQx5xY7qvmyqEEDblpnBubRNLdM2LCitcQ-9O74gJV_sYG8EAL49POYDoUKwzS8XGikuo2ni2DXw1cYV6cyprnj8E0v_1k7M9BzJcwQWDEUigk/s640/Screenshot-2017-12-19+polymer-muestra-inicial%25281%2529.png" width="640" /></a></div>
Que básicamente viene a confirmar que:<br />
<ul>
<li>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 <b>src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html</b></li>
<li>Parece que los estilos no bajan desde el componente padre al componente hijo. Esto se puede demostrar si se elimina el estilo en <b>src/componentes/entradas/vt-entrada.html</b></li>
</ul>
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<br />
Primer instalamos <b>Pure</b> mediante bower de la siguiente forma:
<br />
<pre class="brush:bash">bower install pure --save
</pre>
Luego, modificamos a <b>src/index.html</b> ara agregar los estilos de <b>Pure:</b>
<br />
<pre class="brush:js">
<!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>
</pre>
Y hacemos la maquetación dentro de <b>polymer-muestra-inicial-app</b> en <b>src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html </b> de la siguiente forma:
<br />
<pre class="brush:js">
<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>
</pre>
Que ahora se ve de la siguiente forma:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitxp12sVAij-PcfFSAbXKLFGNHre_s9DclXw-6piYdaFNyX7zH2wQu8jpNnf_EtyGhR9cSshMpjBoa74PpVL7-OGFSSupRJvCeB2nTLAVZ4MfvBQEmSoAQ1vSn2qQAktUOH_uMfjWzDKw/s1600/Screenshot-2017-12-19+polymer-muestra-inicial%25282%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="153" data-original-width="958" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitxp12sVAij-PcfFSAbXKLFGNHre_s9DclXw-6piYdaFNyX7zH2wQu8jpNnf_EtyGhR9cSshMpjBoa74PpVL7-OGFSSupRJvCeB2nTLAVZ4MfvBQEmSoAQ1vSn2qQAktUOH_uMfjWzDKw/s640/Screenshot-2017-12-19+polymer-muestra-inicial%25282%2529.png" width="640" /></a></div>
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 VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-78473010635912690462017-12-15T09:35:00.000-06:002017-12-15T09:35:11.079-06:00Siguiendo con la Introducción a Polymer 2Lo 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. <br />
<br />
Sobre nuestro <a href="http://apuntestuxianos.blogspot.com/2017/12/especie-de-introduccion-polymer-2.html">ejemplo anterior</a>, haremos un nuevo componente <b>vt-etiqueta</b>
<br />
<pre class="brush:bash">mkdir src/componentes/etiquetas/
</pre>
El contenido del fichero <b>src/componentes/etiquetas/vt-etiqueta.html</b> es el siguiente:
<br />
<pre class="brush:js"><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>
</pre>
Luego, hemos de añadirlo en el componente <b>polymer-muestra-inicial-app</b>, 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 <b>src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html</b> queda ahora de la siguiente forma:
<br />
<pre class="brush:js"><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>
</pre>
El contenido de <b>vt-etiqueta</b>, expuesto en la propiedad <i><b>contenido</b></i> será actualizado desde nuestro componente anterior, <b>vt-entrada</b>.<br />
Para que eso suceda, modificamos la definición de este último en <b>src/componentes/entradas/vt-entrada.html</b>, configurando el atributo <i><b>notify</b></i> de dicha propiedad a <i><b>true. </b></i>Una pequeña línea no debería ser razón para volver a presentar todo el código, pero es gratis:
<br />
<pre class="brush:js"><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>
</pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgknPo7mqQxFAqm-pbD58PkB5K2zNfAG7p4wV642CVcf2Un-5nAIWX5ILfSuZhwGMJL4Yw68tbCkQDIqDLEZbD5CtU-7deF3X1r3I-jt1WHXUPvs18PBvoi-ANTMwPjGwyYSjQ4YOdksG4/s1600/Screenshot-2017-12-15+polymer-muestra-inicial.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="178" data-original-width="958" height="118" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgknPo7mqQxFAqm-pbD58PkB5K2zNfAG7p4wV642CVcf2Un-5nAIWX5ILfSuZhwGMJL4Yw68tbCkQDIqDLEZbD5CtU-7deF3X1r3I-jt1WHXUPvs18PBvoi-ANTMwPjGwyYSjQ4YOdksG4/s640/Screenshot-2017-12-15+polymer-muestra-inicial.png" width="640" /></a></div>
Al escribir en la caja de texto del componente vt-entrada, el contenido de vt-etiqueta se va modificando.<br />
<h4>
Para tener en mente según lo visto:</h4>
<ul>
<li>Los binding <b>[[propiedad]]</b> son unidireccionales. (Padre al hijo)</li>
<li>Los binding <b>{{propiedad}}</b> son bidireccionales. (Padre al hijo y viceversa) </li>
<li>Para que esto último funcione con propiedades que han de modificarse en un componente hijo, es preciso que la propiedad tenga configurada <b>notify: true</b> en dicho hijo.</li>
</ul>
<b>Sí, los componente son bastante reutilizables</b><br />
Para ejemplo, basta con agregar otro <b>vt-entrada</b> en <b>src/polymer-muestra-inicial-app/polymer-muestra-inicial-app.html</b> 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:
<br />
<pre class="brush:js"><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>
</pre>
Desde el navegador, lo vemos de la siguiente forma:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGleMnGNPC7E7CCr0uuI_SSj52kHgFWgwNXWMxzEOu8WseTXWgb-Fnd5jDDw1V08weHnKrCs3hNXXuH-d_sPSISA5At834wWaVqP5mSDnoVF1n-Vonha-Pd2cX5h4lwA_6aPCSOL9w5WA/s1600/Screenshot-2017-12-15+polymer-muestra-inicial%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="261" data-original-width="958" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGleMnGNPC7E7CCr0uuI_SSj52kHgFWgwNXWMxzEOu8WseTXWgb-Fnd5jDDw1V08weHnKrCs3hNXXuH-d_sPSISA5At834wWaVqP5mSDnoVF1n-Vonha-Pd2cX5h4lwA_6aPCSOL9w5WA/s640/Screenshot-2017-12-15+polymer-muestra-inicial%25281%2529.png" width="640" /></a></div>
Seguimos sin hacer que esto parezca algo mínimamente serio, pero al menos se esta volviendo cada vez más divertido. VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-13743898072295480632017-12-14T12:53:00.000-06:002017-12-15T09:35:34.418-06:00Especie de Introducción a Polymer 2Este artículo debió llamarse "Especie de introducción a AngularJS". Es decir, estuve <a href="https://github.com/VTacius/justine-web">trabajando por mucho tiempo en una aplicación</a> 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 <a href="http://angularjs.blogspot.com/2014/03/angular-20.html">iba a quedar obsoleto</a> por rediseño. Todos <a href="https://medium.com/@angularminds/comparison-between-angular-1-vs-angular-2-vs-angular-4-62fe79c379e3">los cambios</a> 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.<br />
<br />
Así que reconsideré a Polymer: Esta basado en <a href="https://webcomponents.github.io/">webcomponents</a>, 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.<br />
<br />
Para instalarlo en Fedora, bastará con correr los siguiente comandos como root. Para npm, <i><b>-g</b></i> implica global, con lo que se supone que otros usuarios en el sistema podrían usar las herramientas así instaladas: <br />
<pre class="brush:bash">dnf install npm
npm -g install bower
npm -g install polymer-cli
</pre>
Luego seguimos las <a href="https://www.polymer-project.org/2.0/start/toolbox/set-up">indicaciones que nos hace la gente de polymer.</a> Desde consola, como cualquier usuario disponible en el sistema y ubicados desde cualquier parte que querramos (Preferentemente no "cualquier" parte), ejecutamos lo siguiente:
<br />
<pre class="brush:bash">mkdir polymer-muestra-inicial
cd polymer-muestra-inicial/
polymer init --name polymer-2-application
</pre>
Donde <b><i>--name</i></b> permite seleccionar la plantilla inicial para nuestro proyecto. <b>polymer-2-application</b> arranca con poco código, lo que es conveniente para aprender lo más posible.
<br />
<pre class="brush:bash">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
</pre>
A continuación, se instalan todos los paquetes necesarios para nuestra aplicación mediante <b>bower</b>, y es mediante él que debemos agregar nuevas dependencias.<br />
En este momento, podemos probar lo que tenemos hasta ahora (Casi nada a decir verdad) ejecutando desde consola:
<br />
<pre class="brush:bash">polymer serve --open
</pre>
No subestimen este comando: Incluso permite servir contenido como HTTPS.<br />
<h4>
Ahora empezamos la introducción propiamente dicha: </h4>
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.
<br />
<pre class="brush:bash">mkdir -p src/componentes/entradas
cd src/componentes/entradas
</pre>
Además, voy a trabajar modificando el componente que se ha creado originalmente para esta aplicación: <b>polymer-muestra-inicial-app</b>, no tanto por añorar mi época con AngularJS, sino por aprovechar todo el conocimiento previo de dicha época. <br />
<br />
Así, creamos el fichero <b>src/componentes/entradas/vt-entrada.html</b>, que define un input casi sin modificación alguna, con el siguiente contenido
<br />
<pre class="brush:js"><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></pre>
<pre class="brush:js"></dom-module> </pre>
A ver, para explicaciones más detalladas las teoría. Desde aquí, sin embargo, podemos decir:<br />
<ul>
<li>Esta es casi, casi la forma en que se define un webcomponent nativo al día de hoy: Dentro de <b><template></template></b> tenemos el HTML de toda la vida; dentro de <b><script></script></b> la definición de nuestro componente. Todo dentro de <b><dom-module></b>. </li>
<li><b>properties</b> 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 <a href="https://www.polymer-project.org/2.0/docs/devguide/properties">la documentación oficial </a>es un muy buen primer paso a tomar.</li>
<li>El estilo CSS definido dentro de <style> se circunscribe a este componente, mediante una técnica llamada <a href="https://www.polymer-project.org/2.0/docs/devguide/shadow-dom"><b>DOM Shadow</b></a> que hoy en día ya sirve para muchas otras cosas además de encapsular CSS (Igual nace por esta necesidad específica)</li>
</ul>
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:
<br />
<pre class="brush:js"><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>
</pre>
Al verlo en la navegador, tenemos algo como esto:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM3nl5NmYvX4MEAJF-QQO_ZAxgdkOQERbFJaGCainqv_9W8JVIhiCZrtTFb0XXldaBo5BF-sSyyPCfgxC5G-3_MrbBQoOeULu1fn8fR8vpmw8zf8tXgzBSdcHuXh4nLXGji_-3dO7Kv70/s1600/Screenshot-2017-12-14+polymer-muestra-inicial%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="108" data-original-width="958" height="72" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM3nl5NmYvX4MEAJF-QQO_ZAxgdkOQERbFJaGCainqv_9W8JVIhiCZrtTFb0XXldaBo5BF-sSyyPCfgxC5G-3_MrbBQoOeULu1fn8fR8vpmw8zf8tXgzBSdcHuXh4nLXGji_-3dO7Kv70/s640/Screenshot-2017-12-14+polymer-muestra-inicial%25281%2529.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<h4>
Ahora hacemos que esta introducción valga la pena:</h4>
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 <b>vt-entrada</b>: Los cambios se realizan en la plantilla, agregando un <b><p></p></b> cuyo contenido se hace refencia a la propiedad <b>contenido</b> de nuestro componente.<br />
<br />
Por otra parte, vamos a cambiar el data binding de nuestro <input> para que funcione en forma bidireccional: En lugar de <b>contenido</b>, ahora será <b>contenido::input</b>.<br />
<br />
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 <a href="https://www.polymer-project.org/2.0/docs/devguide/data-binding">la documentación oficial</a>. Al final, nuestro componente debe verse de la siguiente forma:
<br />
<pre class="brush:js"><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>
</pre>
Lo vemos de la siguiente forma en el navegador: <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZV7WVOIoGd6zuTge8xF4Klf9xP6sNupUBQ6loI_aTO6h4iZ2Hemw9aa2zzfNjQmIxQ5TRlW2uBKzQ-yNoFixtD8MT2sekGJkvJSmRhePvIA7wyZ_23vp_uo2uzV1aUAxmJSur_UOAHgs/s1600/Screenshot-2017-12-14+polymer-muestra-inicial%25282%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="151" data-original-width="958" height="99" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZV7WVOIoGd6zuTge8xF4Klf9xP6sNupUBQ6loI_aTO6h4iZ2Hemw9aa2zzfNjQmIxQ5TRlW2uBKzQ-yNoFixtD8MT2sekGJkvJSmRhePvIA7wyZ_23vp_uo2uzV1aUAxmJSur_UOAHgs/s640/Screenshot-2017-12-14+polymer-muestra-inicial%25282%2529.png" width="640" /></a></div>
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.<br />
<br />
Supongo que para una breve introducción ya está. VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-40071584364209557912017-12-01T13:34:00.001-06:002017-12-01T13:34:48.509-06:00Otro intento para configurar QoS en Linux: Día 2Hasta 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.<br />
<br />
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. <br />
<br />
El primer intento de configuración va de la siguiente forma:<br />
<pre class="brush:bash">#!/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
</pre>
Las clases por ahora se ven de la siguiente forma:<br />
<pre class="brush:bash">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
</pre>
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
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8UtKEWwinnAHw0nYxTDTRyoRZhLoA4_C2FJafkqwvVLRPh0wuyQ9jIEWR7z0EwyJ9siQYh0i-s4J7PnFaDfbJvVsfKc_MZZSjxm18KXH5S8W5iyL4Ij-hPWrMl5loUF0i3z_hCkPAOwE/s1600/Selecci%25C3%25B3n_054.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="879" data-original-width="1600" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8UtKEWwinnAHw0nYxTDTRyoRZhLoA4_C2FJafkqwvVLRPh0wuyQ9jIEWR7z0EwyJ9siQYh0i-s4J7PnFaDfbJvVsfKc_MZZSjxm18KXH5S8W5iyL4Ij-hPWrMl5loUF0i3z_hCkPAOwE/s640/Selecci%25C3%25B3n_054.png" width="640" /></a></div>
Un ping con alta latencia. Veamos que pasa al agregar un poco de tráfico HTTPS
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR1ddFGIQ5qsopl30AzyxY1w8BcaqH-_ohNN7QOCSwc5kXyGcejUmn5ExaShCbGkDBt-zmM0nnckH_ZVFeTl25XNSWRy9vbg9MVSuZ-tiDGaiBIClCh15dz_m-LppsEX1geMKB3x4wBwU/s1600/Selecci%25C3%25B3n_055.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="875" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR1ddFGIQ5qsopl30AzyxY1w8BcaqH-_ohNN7QOCSwc5kXyGcejUmn5ExaShCbGkDBt-zmM0nnckH_ZVFeTl25XNSWRy9vbg9MVSuZ-tiDGaiBIClCh15dz_m-LppsEX1geMKB3x4wBwU/s640/Selecci%25C3%25B3n_055.png" width="640" /></a></div>
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
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ACQHs7SfqUe-twPTW72YXwgfT4uSfD5igJPHA-mOjgqXf_5NP9IxBfaMKMvAch_gsYATbm6-42hwZdkaAIYBjZmFv8oXdYG6-3jaYDkUBgAMGwKnU2U5b6fKM-yjQjfRYLnXmB8enPs/s1600/Selecci%25C3%25B3n_056.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1600" height="351" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ACQHs7SfqUe-twPTW72YXwgfT4uSfD5igJPHA-mOjgqXf_5NP9IxBfaMKMvAch_gsYATbm6-42hwZdkaAIYBjZmFv8oXdYG6-3jaYDkUBgAMGwKnU2U5b6fKM-yjQjfRYLnXmB8enPs/s640/Selecci%25C3%25B3n_056.png" width="640" /></a></div>
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 <b>prio
</b><br />
<pre class="brush:bash">...
## 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
... </pre>
Y marcar el tráfico ICMP de la siguiente forma
<br />
<pre class="brush:bash">...
iptables -t mangle -A POSTROUTING -p icmp -j MARK --set-mark 20
...
</pre>
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
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpvWkfO1-fjEXDgQNLYApu1I_TugMSSolDt2aKmMToemIFBgTV7ubT8T6Fhb-KQsQwwD9F0VJN9j7f4ILxyzk0jOPTBVLb1b_TKYBAv1fPHIQwlR486d29emdTV5yr95y0UUy-xm7x8tM/s1600/Selecci%25C3%25B3n_062.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="876" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpvWkfO1-fjEXDgQNLYApu1I_TugMSSolDt2aKmMToemIFBgTV7ubT8T6Fhb-KQsQwwD9F0VJN9j7f4ILxyzk0jOPTBVLb1b_TKYBAv1fPHIQwlR486d29emdTV5yr95y0UUy-xm7x8tM/s640/Selecci%25C3%25B3n_062.png" width="640" /></a></div>
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.
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-67032909977127297722017-10-25T09:01:00.000-06:002017-11-30T10:04:28.051-06:00Otro intento para configurar QoS en Linux: Día 1<b>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. </b>Excepto ese pequeño detalle, todo lo demás es funcional<br />
<br />
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.<br />
<br />
Por defecto, nuestra configuración tiene la siguiente forma: <br />
<pre class="brush:bash">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
</pre>
El usuario lo vería de la siguiente manera:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiShHEXIuZ49hvXZO4BNczBUeLGsfGzHRmeJG7vyFqH3LXbPhlxyxnzyep7sh0lMBCFjFvJiTeeJitXSfLVe9KgT91J-RBSrz6H-3iaC4pQFsJyTMbA6efgyRiZN84qc7T58T7yL-peW2Q/s1600/Selecci%25C3%25B3n_034.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="872" data-original-width="1600" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiShHEXIuZ49hvXZO4BNczBUeLGsfGzHRmeJG7vyFqH3LXbPhlxyxnzyep7sh0lMBCFjFvJiTeeJitXSfLVe9KgT91J-RBSrz6H-3iaC4pQFsJyTMbA6efgyRiZN84qc7T58T7yL-peW2Q/s640/Selecci%25C3%25B3n_034.png" width="640" /></a></div>
<br />
Empezamos con lo que podría considerarse una configuración mínima para hacer shaping:<br />
<pre class="brush:bash">#!/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
</pre>
<br />
Ahora, estamos configurados de la siguiente forma:
<br />
<pre class="brush:bash">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
</pre>
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
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5ooWtiRv3FVecUUz_1FGYGkuVPsMHwwwNab2pavWMVNB-H4RSnw-iPhzirux4UxQa50woaxguHWPiW6hkwg_LywPQJujxT4J6VBN6OQZv-RN_mRk1zlwdO73J5IVtqDbtwurnfyZ5J0I/s1600/Selecci%25C3%25B3n_036.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="872" data-original-width="1600" height="349" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5ooWtiRv3FVecUUz_1FGYGkuVPsMHwwwNab2pavWMVNB-H4RSnw-iPhzirux4UxQa50woaxguHWPiW6hkwg_LywPQJujxT4J6VBN6OQZv-RN_mRk1zlwdO73J5IVtqDbtwurnfyZ5J0I/s640/Selecci%25C3%25B3n_036.png" width="640" /></a></div>
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:
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6SzGQebCPmZ0DWAt_xCsZojXK8LPg0RtmdVGshyqlv52mtx9Rj4rM222GgAyQl6-CC8bkB81U4Mu9bBFyR9pVHWdIoOQ2ub51GFR6op1YEx_lYzOiweTDfxptiUBFjjC6vir0B0HbeU/s1600/Selecci%25C3%25B3n_037.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="872" data-original-width="1600" height="349" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6SzGQebCPmZ0DWAt_xCsZojXK8LPg0RtmdVGshyqlv52mtx9Rj4rM222GgAyQl6-CC8bkB81U4Mu9bBFyR9pVHWdIoOQ2ub51GFR6op1YEx_lYzOiweTDfxptiUBFjjC6vir0B0HbeU/s640/Selecci%25C3%25B3n_037.png" width="640" /></a></div>
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<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigm5SJ0BRhvHDeeFHdUNoITYhPOh2wFFk5zY3mUZLhSGj-U89lssMzmxLKGtmLDNo2rJRk_-7slHbY6ZrbgvU3__8dCNkgmrBfyyy5e_vdYRCtfVKyyHq2Aijm2a9H90j89EjzrwXMKYk/s1600/Selecci%25C3%25B3n_042.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="875" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigm5SJ0BRhvHDeeFHdUNoITYhPOh2wFFk5zY3mUZLhSGj-U89lssMzmxLKGtmLDNo2rJRk_-7slHbY6ZrbgvU3__8dCNkgmrBfyyy5e_vdYRCtfVKyyHq2Aijm2a9H90j89EjzrwXMKYk/s640/Selecci%25C3%25B3n_042.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">El cliente a la derecha descarga con todo el ancho de banda disponible en ese momento</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLZTIGVDqa_Y8pr9M_ytIaOHpmijbwoP-fehyhJQ8Xncro3GC0q_rzOisBXg1JBMkl6b41nDTQdLgrZT0HlrtVb4cyOcehxEDGA8qJRTv96jw5yhwNPDJs9i6Eu_DqSCTNYvEZVcTBOwE/s1600/Selecci%25C3%25B3n_043.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="875" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLZTIGVDqa_Y8pr9M_ytIaOHpmijbwoP-fehyhJQ8Xncro3GC0q_rzOisBXg1JBMkl6b41nDTQdLgrZT0HlrtVb4cyOcehxEDGA8qJRTv96jw5yhwNPDJs9i6Eu_DqSCTNYvEZVcTBOwE/s640/Selecci%25C3%25B3n_043.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cuando empieza la descarga en el cliente a la izquierda, este es claramente beneficiado de un mejor ancho de banda</td></tr>
</tbody></table>
<br />
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<br />
<br />
<pre class="brush:bash">## 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
</pre>
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<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-b2C5grCXtwqD5peuKUJH3AoSnx48NoYh_g9iQGIadPilD6QXQODG20p9bd6LadxtirFJUYkgCoczbHAjKeTFtwrs4HdwN1KYYO-6V1TVwSkxPgh1ZImNMd402ta_LR2spFbEiw-5M1w/s1600/Selecci%25C3%25B3n_045.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="877" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-b2C5grCXtwqD5peuKUJH3AoSnx48NoYh_g9iQGIadPilD6QXQODG20p9bd6LadxtirFJUYkgCoczbHAjKeTFtwrs4HdwN1KYYO-6V1TVwSkxPgh1ZImNMd402ta_LR2spFbEiw-5M1w/s640/Selecci%25C3%25B3n_045.png" width="640" /></a></div>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-18496500489445463812017-10-24T07:40:00.000-06:002017-11-29T10:41:06.323-06:00Otro intento para configurar QoS en Linux: Segunda introducción<h4>
Sobre como configurar ancho de banda en TC</h4>
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. <br />
<br />
Al hablar de ancho de banda, la unidad de medida a usar es <i>bites por segundo, </i>que debería abreviarse como <i>bps. </i>Esta es la unidad de medida que suelen usar los proveedores de internet: Así, al decir <i>megas</i>, se refieren a Megabits por segundo; así por ejemplo, 2 Mbps en su forma abreviada.<br />
<br />
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<br />
<br />
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 <i>kilos: KB/s </i>y <i>kbps</i>, la relación sigue siendo la misma. <i> </i><br />
(El truco más barato para saber que unidad de medida esta usando, es que si es muy grande, posiblemente se refiera a <i>bps</i>)<br />
<br />
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:<br />
<ul>
<li><b>kbit se refiere a kilobits por segundo</b> (Para este no hay problema). Esta también puede escribirse como <b>Kbit</b></li>
<li><b>kbps se refiere a kilobytes por segundo</b> (Acá se usa la notación que se espera para la otra unidad de medida)</li>
</ul>
<h4>
Si ya domina la confusión sobre unidades de transferencia, puede venir a entender a tc</h4>
tc usa bits por segundo como unidad por defecto<br />
<br />
<b>Sobre como clasificar tráfico para tc </b><br />
Nuestra primera opción es la de usar el componente <b>filter</b> de tc. En este caso, se considera que el <a href="http://lartc.org/howto/lartc.adv-filter.html#LARTC.ADV-FILTER.U32">clasificador u32</a> es el máximo exponente. Pero dado que puede convertirse en algo complicado, tenemos la opción de seguir usando a <b>filter</b> con algo más sencillo: Buscar "marcas" hechas con iptables mediante <a href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/x4389.html">MARK target</a>. También es posible usar a <a href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/x4758.html">TOS target</a> (Nos limitaría un poco) y a <a href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/x4193.html">DSCP target</a> (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.<br />
<br />
Mi mejor apuesta es a usar el <a href="https://www.frozentux.net/iptables-tutorial/chunkyhtml/x4002.html">CLASSIFY target</a>, que como se verá en las pruebas que realicemos, nos ahorra, al menos a nivel de configuración, al componente <b>filter</b> de <b>tc</b>, casi nada.VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-87553356241625371242017-10-23T12:54:00.000-06:002017-11-29T10:40:28.414-06:00Otro intento para configurar QoS en Linux: IntroducciónAunque no tengo algo como esto en producción, quería empezar a registrar algunas ideas al respecto:<br />
<h4>
Es posible que lo que entiendas por QoS sea una especie de cajón de sastre: </h4>
Como muchos otros conceptos en informática, dicho sea de paso.<br />
En este caso, no ayuda que en realidad haya muchos conceptos involucrados:<br />
<ul>
<li>La definición misma del <b>Protocolo de Internet</b> (<a href="https://www.ietf.org/rfc/rfc791.txt">RFC791</a>) describe en sus cabeceras al campo <b>TOS</b>, 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. </li>
<li>Para ser más preciso, quizá casi a manera de curiosidad, parece haber un <a href="https://tools.ietf.org/html/rfc1349">RFC específico para TOS</a>, pero esta marcado como obsoleto debido a RFC2474</li>
<li>Precisamente, una ampliación de TOS se realiza en <b>Differentiated Services</b> (<a href="https://www.ietf.org/rfc/rfc2474.txt">RFC2474</a>), que básicamente define más técnicas para mejorar la calidad de la red.</li>
<li>Dentro del campo <b>DS</b> (Differentiated Service), de los 8 bits que lo componen, 6 bits se corresponden con <b>DSCP</b> (<b>Differentiated Service Code Point</b>). 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</li>
</ul>
En GNU/Linux, la <a href="http://www.lartc.org/lartc.html">Linux Advanced Routing & Traffic Control</a> 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 <a href="http://www.lartc.org/lartc.html#LARTC.QDISC">Chapter 9. Queueing Disciplines for Bandwidth Management</a>. Es una lectura poco extensa pero que como introducción cumple bastante bien su cometido.<br />
Recomendable leer <a href="http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm">HTB Linux queuing discipline manual - user guide</a>, 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<br />
<h4>
Qos no es el mesías hecho software</h4>
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.<br />
<h4>
Algunos conceptos a tener en cuenta</h4>
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 <br />
<ul>
<li><b>Shaping:</b> Se refiere a la administración del ancho de banda. Dentro de tc, se realiza con las <b>class</b></li>
<li><b>Queueing</b>: 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 <b>qdisc </b>(Disciplina de colas)</li>
<li><b>Filter:</b> 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</li>
</ul>
Pero estos activadades no se corresponden con todos sus componentes. Debería leerse <a href="http://tldp.org/HOWTO/Traffic-Control-HOWTO/elements.html">Traditional Elements of Traffic Control</a>, que de todo lo disponible, es la lectura teórica más importante que podría hacer de todo este tema.<br />
Aprovechando, a <a href="http://tldp.org/HOWTO/Traffic-Control-HOWTO/components.html">Components of Linux Traffic Control</a> prácticamente debe memorizarlo<br />
<ul>
</ul>
<h4>
Fuentes y lecturas complementarias</h4>
<ul>
<li><a href="https://www.cisco.com/c/en/us/support/docs/quality-of-service-qos/qos-packet-marking/10103-dscpvalues.html">Implementing Quality of Service Policies with DSCP</a></li>
<li><a href="https://blogs.manageengine.com/network/netflowanalyzer/2012/04/24/understanding-ip-precedence-tos-dscp.html">Understanding IP Precedence, TOS & DSCP</a> </li>
<li><a href="http://www.dlink.com/mk/mk/support/faq/routers/mydlink-routers/dir-605l/how-do-i-configure-qos-quality-of-service-traffic-control-settings-on-my-router">How do I configure QoS (Quality of Service)/ Traffic Control settings on my router?</a></li>
</ul>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-85596840314600978532017-10-12T13:39:00.003-06:002017-10-12T13:41:50.884-06:00Resolviendo problemas con Nvidia GLX en Fedora 26Aunque Fedora 26 ya tenía mucho andando en mi equipo, no había resentido el problema de GLX excepto en algunas cuestiones puntuales:<br />
<ul>
<li>Al intentar ejecutar <b><i>glxinfo</i> </b>desde consola, aparecía un mensaje de error:<b> BadWindow (invalid Window parameter). </b>También aparecía cuando intentaba acceder a la pestaña <br /><b></b></li>
<li>También aparecía al intentar acceder a <b> X Screen 0 > OpenGL/GLX Information </b>en <b>Nvidia X Server Settings</b></li>
<li>Al estar usando LXQT como entorno gráfico, <b>SDDM</b>, 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.</li>
</ul>
Al revisar los log de SDDM, encontré algo parecido a <b>Unrecognized OpenGL Version. </b>Supuse que GLX no cargo y decidí buscar en los log de Xorg en <b>/var/log/Xorg.0.log</b>:<br />
<pre class="brush:bash">[ 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.
</pre>
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
<br />
<pre class="brush:bash">(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
</pre>
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
<br />
<pre class="brush:bash">cd /usr/lib64/xorg/modules/extensions/
mv libglx.so libglx.xorg.so
</pre>
Y al reiniciarlo, todo bien, ningún problema<br />
<br />
Fuentes: (<a href="https://forums.geforce.com/default/topic/369385/geforce-drivers/failed-to-initialize-the-glx-module-can-39-t-init-glx-driver-on-fedoracore-6/">Lo más cercano que tuve a un consejo</a>) VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-22171699423190396402017-08-30T10:44:00.001-06:002017-09-19T11:45:44.492-06:00Configurando Squid como proxy transparente HTTPS: Configuración de SslBump en SquidEn 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 (<a href="https://github.com/jpelias/squid3-ssl-bump/blob/master/Install%20Squid%203.4%20with%20ssl%20bump%20on%20Debian%208%20(Jessie)#L40">1</a>, <a href="http://debian-base.blogspot.com/2015/05/install-squid-34-with-ssl-bump-on.html">2</a>); CentOS 7 (<a href="http://www.bitbull.ch/wiki/index.php/CentOS7_Squid_%2B_SquidGuard_%2B_blacklist">1</a> la cual a su modo es muy completa) y CentOS 6 (<a href="http://roberts.bplaced.net/index.php/linux-guides/centos-6-guides/proxy-server/squid-transparent-proxy-http-https">1</a>) entre otros.<br />
<br />
Fue esta guía <a href="https://smoothnet.org/squid-v3-5-proxy-with-ssl-bump/">(Squid (v3.5+) proxy with SSL Bump</a> la que señala la forma correcta de configurar <a href="http://www.squid-cache.org/Doc/config/ssl_bump/">ssl_bump</a> en squid v3.5+; básandome en ella, hago una mejora mínima con la cuestión del certificado.<br />
Crear un certificado puede ser algo tan sencillo como se recoge en <a href="https://wiki.squid-cache.org/ConfigExamples/Intercept/SslBumpExplicit:">Intercept HTTPS CONNECT messages with SSL-Bump</a>, 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:
<br />
<pre class="brush:bash"> $ openssl genrsa -out myCA.key 4096 [0/1508]
Generating RSA private key, 4096 bit long modulus
..........++
.....................................................................++
e is 65537 (0x010001)
</pre>
Y luego la clave pública así:
<br />
<pre class="brush:bash">$ 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
</pre>
Cambiamos un poco el esquema de seguridad para los ficheros de la siguiente forma. Incluso las podría haber mejores
<br />
<pre class="brush:bash">$ 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/
</pre>
Ahora configuramos precisamente a squid. No se necesita más que asegur tener las siguientes líneas:
<br />
<pre class="brush:bash">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
...
</pre>
La configuración de <b>ssl_bump</b> aún sigue siendo un poco complicada, aún cuando existe <a href="https://wiki.squid-cache.org/Features/SslPeekAndSplice#Actions">bastante documentación al respecto</a>, hasta ahora entiendo que va de esta forma:<br />
<ul>
<li><b>ssl_bump stare all</b>: 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.</li>
<li><b>ssl_bumps splice all</b>: En esta parte hacemos precisamente la conexión. Usar <b>splice</b> en lugar de <b>bump </b>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</li>
</ul>
En las guías anteriores, se recomiendan configuraciones como
<br />
<pre class="brush:bash">ssl_bump server first all
sslproxy_cert_error deny all
sslproxy_flags DONT_VERIFY_PEER
</pre>
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:
<br />
<pre class="brush:bash">sslcrtd_program /usr/lib/squid/ssl_crtd -s /var/lib/squid_ssl/ -M 4MB
</pre>
Ya que los valores por defecto no están del todo bien:
<br />
<pre class="brush:bash">sslcrtd_program /usr/local/squid/libexec/ssl_crtd -s /var/lib/ssl_db -M 4MB
</pre>
En ambos casos, es necesario inicializar el directorio mencionado:
<br />
<pre class="brush:bash">/usr/lib/squid/ssl_crtd -c -s /var/lib/squid_ssl/
Initialization SSL db...
Done
</pre>
TODO: Pueden hacerse bastante mejoras aún con el certificadoVTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-72623038940861234572017-08-29T10:03:00.000-06:002017-08-30T07:49:24.294-06:00Configurando Squid como proxy transparente HTTPS: Squid con soporte SSL Build errors with Squid 3.5.24 under Debian <br />
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.<br />
<br />
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)<br />
<br />
Por tanto, los pasos a realizar serán básicamente los siguientes:<br />
<pre class="brush:bash">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
</pre>
Respecto al siguiente paso, hay varias versiones de lo que podríamos hacer. El parche podría trabajarse de la siguiente forma
<br />
<pre class="brush:bash">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
</pre>
Pero es un poco innecesario, creo que realizando la modificación directamente en el fichero correpondiente, <code>debian/rules</code> no debería haber mayores problemas: Modificamos <code>DEB_CONFIGURE_EXTRA_FLAGS</code> agregando estas opciones:
<br />
<pre class="brush:bash">vim debian/rules
+ --with-openssl \
+ --enable-ssl-crtd \
</pre>
Sea como sea, una vez modificadas las reglas de configuración previas, podemos contruir el paquete.
<br />
<pre class="brush:bash">debuild -b -uc -us
</pre>
Pues deberían haberse construido los paquetes y podemos probar a instalar en el mismo equipo
<br />
<pre class="brush:bash">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
</pre>
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:
<br />
<pre class="brush:bash">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
</pre>
# A partir de este punto, todo paquete que dependa de squid se dará por satisfecho. Por ejemplo
<pre class="brush:bash">
apt-get install squidguard{,-doc} sarg
</pre>
Escojo pinning como método para evitar que el paquete sea actualizado
<br />
<pre class="brush:bash">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
</pre>
Fuentes: <a href="http://Build errors with Squid 3.5.24 under Debian ">1</a>, <a href="https://wiki.squid-cache.org/SquidFaq/CompilingSquid#Debian.2C_Ubuntu">2</a>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-46564080659805796972017-08-28T11:09:00.002-06:002017-08-28T13:17:58.593-06:00Configurando Squid como proxy transparente HTTPS: IntroducciónLa 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.<br />
<br />
Descrito pronto, el proyecto usa a squid como proxy caché, y este, usa a squidGuard mediante <b>url_rewrite_program</b> para filtrar la navegación de los usuarios mediante las listas negras de <span id="goog_305287742"></span><a href="https://www.blogger.com/">shallalist</a><span id="goog_305287743"></span> .<br />
<br />
Esta configuración mejora el proyecto en dos cosas básicamente:<br />
<ul>
<li>Los usuarios no necesitan configurar proxy para navegar por HTTPS (Aunque si necesitarían instalar un certificado)</li>
<li>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</li>
</ul>
En este momento, me preocupan dos cosas que también deberían preocuparle a todos los que quieran seguir esta guía:<br />
<ul>
<li>El rendimiento por parte de squid para servir las páginas</li>
<li>¿Tendrá algún sitio demasiados problemas para funcionar con esta configuración? </li>
</ul>
Al momento de escribir esta entrada, esta configuración no ha entrado en producción, so... <br />
<br />
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.<br />
<br />
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.<br />
<br />VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-57601150724920673132017-06-22T13:47:00.002-06:002017-06-22T14:08:54.922-06:00Apuntes sobre el error de Squid / SquidGuard en Debian: SolucionadoLa nueva versión de Debian ha salido el sábado pasado. Y me aventuré a probar que se hubiera solucionado <a href="http://apuntestuxianos.blogspot.com/2016/06/apuntes-sobre-el-error-de-squid3.html">el problema con <b>redirector protocol </b></a>que detuvo tan mal a mi proyecto de firewall.<br />
<br />
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).<br />
<br />
Lo quiero resumir de esta forma: <b>El problema no era tal.</b><br />
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<br />
<br />
El truco está en la configuración del redirector en Squid. Por defecto, yo tenía<br />
<pre class="brush:bash">url_rewrite_children 15 startup=0 idle=1 concurrency=3
</pre>
Pero según la documentación en <a href="http://www.squid-cache.org/Doc/config/url_rewrite_children/">Squid configuration directive url_rewrite_children:</a><br />
<pre>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.
<b> 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.</b></pre>
<br />
La cuestión es que al programa configurado como redirector le debe llegar una petición por parte de squid de la siguiente forma:<br />
<pre class="brush:bash">"http://www.google.com.sv/ 10.168.3.5/10.168.3.5 - GET myip=10.168.3.1 myport=3128";</pre>
Pero configurado <b>concurrency</b>, tal como lo ha dicho la documentación, se le antepone un identificador, de modo que la consulta queda de la siguiente forma.
<br />
<pre class="brush:bash">"0 http://www.google.com.sv/ 10.168.3.5/10.168.3.5 - GET myip=10.168.3.1 myport=3128";</pre>
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... <br />
<br />
La solución es algo tan sencillo como retirar concurrency:
<br />
<pre class="brush:bash">url_rewrite_children 15 startup=0 idle=1 concurrency=0 </pre>
<br />
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 <a href="http://e2guardian.org/cms/index.php">e2guardian</a>.<br />
Yo mismo he intentando <a href="http://wiki.salud.gob.sv/wiki/Creando_parches_para_paquetes_Debian">parchear el paquete en Debian</a>, intentando <a href="https://gist.github.com/VTacius/aa287ddac46037c6cacf34ce9dca82a2">quitar el ID de la petición</a> 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.VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-3481621962096505072017-05-31T10:03:00.000-06:002017-06-01T08:19:04.748-06:00Integrando (Un poco) pyramid en Eclipse: Test AutomatizadosEsto 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.<br />
<br />
Por otra parte, acá hay algo raro que será mejor verificar: Desde una consola de nuestro entorno virtual, hay que instalar <b>nose</b>, y en Fedora, al menos para mí, webtest, así, en minúsculas
<br />
<pre class="brush: bash">virtualenv proyecto-ambiente
pip install nose
pip search webtest
</pre>
Buscamos a <b>proyecto </b>en <b>Project Explorer. </b>Accedemos a propiedades con <b>Alt + Enter</b> o desde el menú desplegable al hacer click derecho.<br />
<br />
Buscamos la pestaña <b>Run/Debug Settings</b>. Hay dos perfiles configurados. En general, el que nos importa es aquel que se llama <b><nombre del proyecto>_test</b>. En nuestro caso que nuestro proyecto se llama <b>proyecto:</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Sk4WZImMr0pUjApEcs6tg3bPcRryMWEbJ-bwkSzv8KI55Qll73lBnugZEMA4NP197tqxGiPMyZbElgEHhuZWBdb65B-MqQXkaj-QtFmnjqs_HhRZMQEUuLnofS-g_WYJY1A3JqG_RWU/s1600/Properties+for+proyecto+_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="562" data-original-width="728" height="494" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Sk4WZImMr0pUjApEcs6tg3bPcRryMWEbJ-bwkSzv8KI55Qll73lBnugZEMA4NP197tqxGiPMyZbElgEHhuZWBdb65B-MqQXkaj-QtFmnjqs_HhRZMQEUuLnofS-g_WYJY1A3JqG_RWU/s640/Properties+for+proyecto+_001.png" width="640" /></a></div>
<br />
Lo seleccionamos en la lista, y hacemos click en <b>Edit </b>y aparece un cuadro de diálogo con las configuración de dichos perfiles<br />
<br />
En la pestaña <b>Main</b>, debe estar configurado de la siguiente manera. Si se configuro tal como en el post anterior, ya debería estar así<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkANDCcBWgtziIzJAe8NpN7n_-iM8XCfMMqMsihp7HHnQ9JLSFnNJsAo69x-9RjOJCtjOWhErozZTqPsvgHUtjMvle0bDG7rbUy45Hft53HxX87v7VceKcJPhXOPpLMtRCOdDNf0mJT4k/s1600/Edit+Configuration+_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1020" data-original-width="997" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkANDCcBWgtziIzJAe8NpN7n_-iM8XCfMMqMsihp7HHnQ9JLSFnNJsAo69x-9RjOJCtjOWhErozZTqPsvgHUtjMvle0bDG7rbUy45Hft53HxX87v7VceKcJPhXOPpLMtRCOdDNf0mJT4k/s640/Edit+Configuration+_002.png" width="624" /></a></div>
<b> </b><br />
En <b>Arguments</b>, debe estar checado <b>Override PyUnit preferences for this launch?</b>, de esta forma podremos configurar Nose test runner al elegirlo desde la lista desplegable<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpIl9uJQQo0ylkyMH3Ydy50RJd8rxP81MPgOprK1lbUYrU9kjG75I7EgJcfAAegZnPwJYmK_PRnsBIKMkSUjQya8hln103c5eCaUOgmQ2PA-YieTK0ouDS0sWc55Y-9GtO_ktyLC-NYrg/s1600/Edit+Configuration+_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1020" data-original-width="997" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpIl9uJQQo0ylkyMH3Ydy50RJd8rxP81MPgOprK1lbUYrU9kjG75I7EgJcfAAegZnPwJYmK_PRnsBIKMkSUjQya8hln103c5eCaUOgmQ2PA-YieTK0ouDS0sWc55Y-9GtO_ktyLC-NYrg/s640/Edit+Configuration+_003.png" width="624" /></a></div>
<br />
En la pestaña <b>Interpreter</b>, pues si, debería configurarse <b>python_proyecto</b>, 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<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBoekaZXcAIOitVUGRQ9vZ9MLfCJr-31FUTfWnmP2OdC_VasPeHvuW73VcIAC-JSXChE5xrwtajs4SsFU_eBa7C22TB7FWv2fuPo7kkG8Iz9v09KUM5a73aCAjjwgOB1Y5OEEzcZD0Caw/s1600/Edit+Configuration+_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1020" data-original-width="997" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBoekaZXcAIOitVUGRQ9vZ9MLfCJr-31FUTfWnmP2OdC_VasPeHvuW73VcIAC-JSXChE5xrwtajs4SsFU_eBa7C22TB7FWv2fuPo7kkG8Iz9v09KUM5a73aCAjjwgOB1Y5OEEzcZD0Caw/s640/Edit+Configuration+_004.png" width="624" /></a></div>
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:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_bAfSbSMl9PbMVc9D_sYOt1rBE-Rwa0OyLsg-9gD_1yrvDuZ3brtxqoI7R6tbd6dr_r8ydBAVe6smuKgrs6Iy_7Cfjp6trlzSAGNu6uf90PkCHKzrNgSgJVLlV2fAo63LBc4k97Q6ctQ/s1600/P_blico+-+Resource+-+proyecto-proyecto-tests.py+-+Eclipse+Platform+_005.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1017" data-original-width="1517" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_bAfSbSMl9PbMVc9D_sYOt1rBE-Rwa0OyLsg-9gD_1yrvDuZ3brtxqoI7R6tbd6dr_r8ydBAVe6smuKgrs6Iy_7Cfjp6trlzSAGNu6uf90PkCHKzrNgSgJVLlV2fAo63LBc4k97Q6ctQ/s640/P_blico+-+Resource+-+proyecto-proyecto-tests.py+-+Eclipse+Platform+_005.png" width="640" /></a></div>
Vemos que, en hacia el fondo de la aplicación, hallamos la ventana Console con el resultado de la operación:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcDBb5URzgJOWEo6x00f9lykNwEzD750r23iVROS4JPK5saS0Xy0VYrnpvyyC_HiAdzzBmqrY51rAX3vTxhTPQaMgojc3Vgp8QpyxGKhc7jTEffrVAbbj0YOtN0kt5_3sikD6GyGkw8p4/s1600/P_blico+-+Resource+-+proyecto-proyecto-tests.py+-+Eclipse+Platform+_006.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1017" data-original-width="1517" height="429" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcDBb5URzgJOWEo6x00f9lykNwEzD750r23iVROS4JPK5saS0Xy0VYrnpvyyC_HiAdzzBmqrY51rAX3vTxhTPQaMgojc3Vgp8QpyxGKhc7jTEffrVAbbj0YOtN0kt5_3sikD6GyGkw8p4/s640/P_blico+-+Resource+-+proyecto-proyecto-tests.py+-+Eclipse+Platform+_006.png" width="640" /></a></div>
<br />
<b> </b>Dos cosas por si se me olvida registrarlas después:<br />
<ul>
<li>Para hacer más test, lo mejor será borrar el fichero <b>test.py</b> y luego crear una carpeta <b>test/</b>. Asimismo, el nombre de cada fichero debe comenzar con <b>test</b></li>
<li><b> </b></li>
</ul>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-40783667327424169722017-05-30T10:17:00.000-06:002017-05-30T10:17:03.401-06:00Integrando (Un poco) pyramid en EclipseUna vez hayamos creado un proyecto (Uno sencillo, como <a href="http://apuntestuxianos.blogspot.com/2017/05/integrando-un-poco-pyramid-en-eclipse.html">este</a>), 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.<br />
<br />
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é<br />
<br />
Abrimos el menú para abrir proyectos en <b>File > Open Project from File System</b>. Aparece el siguiente cuadro de diálogo<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7HoswLaIwH1zUT9qLIiAzmw2Q9k7WhEDPPctJsud4i_wgqQsyf0ZFXIRuFfkbEoF6SdmSMV9pxr18qC8VpJKUQhwFcPNytntQoJNG3cD7yuz5ua5z7e3tcqzuaFHOqbaLicH-hnH2d2g/s1600/Import+Projects+from+File+System+or+Archive+_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="880" height="419" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7HoswLaIwH1zUT9qLIiAzmw2Q9k7WhEDPPctJsud4i_wgqQsyf0ZFXIRuFfkbEoF6SdmSMV9pxr18qC8VpJKUQhwFcPNytntQoJNG3cD7yuz5ua5z7e3tcqzuaFHOqbaLicH-hnH2d2g/s640/Import+Projects+from+File+System+or+Archive+_004.png" width="640" /></a></div>
<span id="goog_1588059999"></span><span id="goog_1588060000"></span><br />
Haciendo click en <b>Directory</b>, nos aparece un cuadro de dialogo del sistema para buscar el directorio donde se encuentra nuestro proyecto.<br />
Yo escogí <b>proyecto-ambiente/proyecto</b> (Para coincidir con el punto donde configuro el repositorio git, gusto mío), pero habrá quién prefiera <b>proyecto-ambiente/proyecto/proyecto</b>, que es donde se encuentra precisamente nuestro código.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHpApBYEXOn2F06GS5V6ZdB9UY3r69q-ZaQb8uef3QoC1VQIg7tB96mPQDAA2GmsLeBp4AuGXSYyNx-mjAoKfKBZiRSs9oiKOFYB0C0NN6Ue5F5xPvR9y8_7TdiQIvP5gRLwX7-H-SWXs/s1600/Import+Projects+from+File+System+or+Archive+_005.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="880" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHpApBYEXOn2F06GS5V6ZdB9UY3r69q-ZaQb8uef3QoC1VQIg7tB96mPQDAA2GmsLeBp4AuGXSYyNx-mjAoKfKBZiRSs9oiKOFYB0C0NN6Ue5F5xPvR9y8_7TdiQIvP5gRLwX7-H-SWXs/s640/Import+Projects+from+File+System+or+Archive+_005.png" width="640" /></a></div>
<br />
Luego, se verá de la siguiente manera una vez configurado. Haciendo click en <b>Finish, </b>habremos terminado de configurar nuestro proyecto.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijwdqvF2UyFfVadVR2QGnROEweEgNLpLhb-wTdATPOOREJlEW8hxW6v9YKYire1K7iRIy-ThMmlcP7hs6F7TisyJP1f_FyIP66zh4N6QqZxwoso0yu_4Im3L7_flpCADg94zno_tdS83E/s1600/Import+Projects+from+File+System+or+Archive+_006.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="880" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijwdqvF2UyFfVadVR2QGnROEweEgNLpLhb-wTdATPOOREJlEW8hxW6v9YKYire1K7iRIy-ThMmlcP7hs6F7TisyJP1f_FyIP66zh4N6QqZxwoso0yu_4Im3L7_flpCADg94zno_tdS83E/s640/Import+Projects+from+File+System+or+Archive+_006.png" width="640" /></a></div>
<br />
A continuación, necesitamos que Eclipse reconozca que el proyecto es python para que active ciertas funciones.<br />
En el <b>Project Explorer</b>, usualmente a la izquierda, hacemos click derecho en nuestro proyecto; en el menú desplegable que aparece nos dirigimos a<b> PyDev > Set as PyDev Project.</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKObwQSjT3gqE0WwefN9-Oy9E8B8SzqbgugnmQelfMEAtHd4_KQ3ypH1cg-9wMY1iZcI-JbSr507ZW1BDJzqpBR-GWmAWwWYYUO9yGNSXma2xb4lY47BP-mAnm0k3IvqzTQH9MejaI0E/s1600/P_blico+-+Resource+-+Eclipse+Platform+_007.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1017" data-original-width="1517" height="429" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKObwQSjT3gqE0WwefN9-Oy9E8B8SzqbgugnmQelfMEAtHd4_KQ3ypH1cg-9wMY1iZcI-JbSr507ZW1BDJzqpBR-GWmAWwWYYUO9yGNSXma2xb4lY47BP-mAnm0k3IvqzTQH9MejaI0E/s640/P_blico+-+Resource+-+Eclipse+Platform+_007.png" width="640" /></a></div>
<br />
<br />
También<b> </b>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.<br />
<br />
Ahora, en el anterior menú desplegable del proyecto buscamos <b>Properties </b>o hacemos <b>Alt + Enter</b> para acceder al siguiente cuadro de diálogo.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2WounOxoTeAyYNU1XIJ7F3kr-f7sKvnrkO15Eih9wYEoEQpGaVAW-fhK21GeCHFeOuOzLApnwPfvVR5DCr8OG0KczJIJdI1t1JkdGwUfb7k_CwJiFDsvjWg0I9fSHUmC0poMfgJkk3c0/s1600/Properties+for+proyecto+_008.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="556" data-original-width="1040" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2WounOxoTeAyYNU1XIJ7F3kr-f7sKvnrkO15Eih9wYEoEQpGaVAW-fhK21GeCHFeOuOzLApnwPfvVR5DCr8OG0KczJIJdI1t1JkdGwUfb7k_CwJiFDsvjWg0I9fSHUmC0poMfgJkk3c0/s640/Properties+for+proyecto+_008.png" width="640" /></a></div>
<br />
<br />
Nuestra primera pestaña a configurar será <b>PyDev - Interpreter/Grammar</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBexCsDc12Q-zgUKyadn5wcTc7YYaOjx_dYYVmbEh6comIKFA57xWpuP-vt7IZW1ALXnu_OA5gqxRrPuF-R2oRqoJc7XCBGF_zfcnEGgYaOpJziYRBsRUVBIuH1AWcLt-i2vX25YWuwPw/s1600/Properties+for+proyecto+_009.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="556" data-original-width="1040" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBexCsDc12Q-zgUKyadn5wcTc7YYaOjx_dYYVmbEh6comIKFA57xWpuP-vt7IZW1ALXnu_OA5gqxRrPuF-R2oRqoJc7XCBGF_zfcnEGgYaOpJziYRBsRUVBIuH1AWcLt-i2vX25YWuwPw/s640/Properties+for+proyecto+_009.png" width="640" /></a></div>
<br />
La idea es configurar en <b>Interpreter</b> al que se encuentra instalado en nuestro entorno virtual <b>proyecto-ambiente</b>, y para ellos habrá que configurarlo haciendo click en <b>Click here to configure an interpreter not listed</b><br />
Aparece la siguiente ventana<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVE0B7hEoOeeVPKXmoexXPVa6ClTDK_iRtLoMccwkT8mtna52uTH27nEn0iXpM_ZKPazEJS04fXyyIhSAJdrdTFMwXM2fJ-DnVtW3dsoPVpj3jcxn-DC9A8aT4S8VT_wOE5QtdZiI18Rw/s1600/Preferences+_010.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="708" data-original-width="1055" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVE0B7hEoOeeVPKXmoexXPVa6ClTDK_iRtLoMccwkT8mtna52uTH27nEn0iXpM_ZKPazEJS04fXyyIhSAJdrdTFMwXM2fJ-DnVtW3dsoPVpj3jcxn-DC9A8aT4S8VT_wOE5QtdZiI18Rw/s640/Preferences+_010.png" width="640" /></a></div>
Haciendo click en <b>New,</b> nos aparece el siguiente Cuadro de diálogo.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg04do9vJGuWXQaoW_6jAUjZrg6enFcKwZjiZuPVcSndQnS9v8Jot55nf9lnBv7pIeZPUvYfCXS-gDGJGbBt3bw3lDyeBAi6AnhL8GJP2uMpeIVUDCrQapy3KGwSgQLHm83w92GUQ1TrJo/s1600/Select+interpreter+_011.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="242" data-original-width="648" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg04do9vJGuWXQaoW_6jAUjZrg6enFcKwZjiZuPVcSndQnS9v8Jot55nf9lnBv7pIeZPUvYfCXS-gDGJGbBt3bw3lDyeBAi6AnhL8GJP2uMpeIVUDCrQapy3KGwSgQLHm83w92GUQ1TrJo/s640/Select+interpreter+_011.png" width="640" /></a></div>
<br />
En <b>Interpreter Name</b>, configuramos un nombre cualquiera vagamente descriptivo, tanto más si vamos a trabajar en varios proyectos a la vez.<b> </b> <br />
<br />
Para <b>Interpreter Executable</b>, hacemos click en <b>Browse</b>, y si, aparece un cuadro de diálogo de tipo fichero. Buscamos dentro de nuestro entorno virtual proyecto-ambiente el directorio de ejectubles <b>bin/</b>, y dentro de este, a <b>python</b>. El cuadro de diálogo queda de la siguiente forma:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU_ZDsRWw3PZodToBY5wm3bPIOcKgWIH46bWACbSigfYbOz1mvNBP0i22ZQX3PP3Zb5ZHA9fUJTshyf1RYrFXli1hrUYFJZpNiZ9C3Mr2PFjbHpjs-0nZkw-EhHTkBbAOWbUFqfKo628I/s1600/Select+interpreter+_012.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="242" data-original-width="648" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU_ZDsRWw3PZodToBY5wm3bPIOcKgWIH46bWACbSigfYbOz1mvNBP0i22ZQX3PP3Zb5ZHA9fUJTshyf1RYrFXli1hrUYFJZpNiZ9C3Mr2PFjbHpjs-0nZkw-EhHTkBbAOWbUFqfKo628I/s640/Select+interpreter+_012.png" width="640" /></a></div>
<br />
Al hacer click en Ok, nos aparece un cuadro de diálogo <b>Selection needed</b>, donde se nos pide agregar las librerías que se corresponden con ese interprete<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmz7hcegOnayX0pcwJUQV-s-8RSNjhw7Rj5iAll_O52LZTQl1pOpYbryrWGmC72sFfhNFayUZ8kLXtayhsTFaMZHezWxFZx2Vy63MxJYB4JFrAB_IA2Pn76wQKBpznq3VHE8rM6n6vfMU/s1600/Selection+needed+_013.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="587" data-original-width="732" height="513" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmz7hcegOnayX0pcwJUQV-s-8RSNjhw7Rj5iAll_O52LZTQl1pOpYbryrWGmC72sFfhNFayUZ8kLXtayhsTFaMZHezWxFZx2Vy63MxJYB4JFrAB_IA2Pn76wQKBpznq3VHE8rM6n6vfMU/s640/Selection+needed+_013.png" width="640" /></a></div>
<br />
Ahora, se puede ver este nuevo intérprete configurado<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV5s61X1HloblzGtUjeH9SAUOFjKl2EfjNj4DMlchQvKVSxDL2EjNTt6qKHoYjKkJ6o5c0h9Hr074m_5QbGEaybC-XbOucqGS0WaP7SNUjrKzOIznkY5PhRP0QAqoX9xgfFv6aeUr7xsE/s1600/Preferences+_014.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="708" data-original-width="1055" height="429" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV5s61X1HloblzGtUjeH9SAUOFjKl2EfjNj4DMlchQvKVSxDL2EjNTt6qKHoYjKkJ6o5c0h9Hr074m_5QbGEaybC-XbOucqGS0WaP7SNUjrKzOIznkY5PhRP0QAqoX9xgfFv6aeUr7xsE/s640/Preferences+_014.png" width="640" /></a></div>
<br />
En este punto, podemos configurarlo como intérprete de nuestro proyecto en la lista desplegable <b>Interpreter.</b> <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoKJ0yU7uYqUKkz7ZuXKruMx-KUASM9Jv09FXJ0N0IP8iE0qtrXe4akkDXwwYH-9xHO0AKJCh9CJBLsqDGMisL6YOWjX3YYdkRSjXO9VHpdJt9GME_xCXlzv2IVeXqJRlxMwZfSATjC94/s1600/Properties+for+proyecto+_015.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="556" data-original-width="1040" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoKJ0yU7uYqUKkz7ZuXKruMx-KUASM9Jv09FXJ0N0IP8iE0qtrXe4akkDXwwYH-9xHO0AKJCh9CJBLsqDGMisL6YOWjX3YYdkRSjXO9VHpdJt9GME_xCXlzv2IVeXqJRlxMwZfSATjC94/s640/Properties+for+proyecto+_015.png" width="640" /></a></div>
<br />
Hacemos click en <b>Apply</b> para seguir configurando. O en <b>Ok, </b>que esto sigue en otro post.<br />
<br />
Mi objetivo se cumple: El autocompletado se rebusca en las librerías del entorno virtual<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvelTJL32iG6qpqNtPLqLCVnore8b_dMK0fOCNcKzcWTu2JP3PNqBYZ386vH0Mq1qxh_75h0DuRxD8k5vELxyqlkA4XSvDI0uv8Y4XxnxsYty83G-IbMDJunYpyYPKZr9u3KLtADyYcow/s1600/P_blico+-+Resource+-+proyecto-proyecto-views.py+-+Eclipse+Platform+_007.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1017" data-original-width="1517" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvelTJL32iG6qpqNtPLqLCVnore8b_dMK0fOCNcKzcWTu2JP3PNqBYZ386vH0Mq1qxh_75h0DuRxD8k5vELxyqlkA4XSvDI0uv8Y4XxnxsYty83G-IbMDJunYpyYPKZr9u3KLtADyYcow/s640/P_blico+-+Resource+-+proyecto-proyecto-views.py+-+Eclipse+Platform+_007.png" width="640" /></a></div>
<br />
<br />VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-83284285290238386972017-05-29T10:01:00.000-06:002017-05-30T13:37:44.752-06:00Integrando (Un poco) pyramid en Eclipse: Creando el proyectoEste artículo muestra la forma más sencilla de empezar un proyecto con Pyramid, ya que a diferencia de <a href="http://apuntestuxianos.blogspot.com/2016/05/primeros-pasos-con-pyramid.html">Primero pasos con Pyramid</a> 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<br />
<br />
Creamos un entorno virtual para el proyecto de nombre <b>proyecto</b> y entramos al directorio creado
<br />
<pre class="brush: bash">virtualenv proyecto-ambiente
cd proyecto-ambiente
</pre>
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.
<br />
<pre class="brush: bash">source bin/activate
</pre>
En este momento, el entorno se ve más o menos de esta forma:
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj51_fRNe57sjcT86y1cD-CWLCNlWKAp4MB_lFogsXXXqmAKDf3tGMgQuLC0lOpPh6zdFe0JVin4VE_g1359bJ8mFJAV4bx0LXLIA0HX-IrH9_m8d6tLO7zKkzzPimR_hUjeoT6wN_AXAA/s1600/alortiz%2540ilaria%253A%257E-proyecto-ambiente_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="416" data-original-width="1190" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj51_fRNe57sjcT86y1cD-CWLCNlWKAp4MB_lFogsXXXqmAKDf3tGMgQuLC0lOpPh6zdFe0JVin4VE_g1359bJ8mFJAV4bx0LXLIA0HX-IrH9_m8d6tLO7zKkzzPimR_hUjeoT6wN_AXAA/s640/alortiz%2540ilaria%253A%257E-proyecto-ambiente_001.png" width="640" /></a></div>
Instalamos pyramid
<br />
<pre class="brush: bash">pip install pyramid
</pre>
Creamos un proyecto de nombre <b>proyecto</b> con la plantilla starter
<br />
<pre class="brush: bash">pcreate -s starter proyecto
</pre>
El pcreate ha creado un directorio proyecto:
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy4cj-yLHHgUCjH8LaZ7etX9S6i38Yu8VRxKD7WlwX8pxyuP7jbv6mhjRqSgGiaTQ69a2-gAnFgsgOaB12neXCekRqtT4AuDrwRrTvYE61EFfOrNo0q6r6_fjIxeToWVt_IJZ6MyuTm2Q/s1600/alortiz%2540ilaria%253A%257E-proyecto-ambiente_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="686" data-original-width="1190" height="369" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy4cj-yLHHgUCjH8LaZ7etX9S6i38Yu8VRxKD7WlwX8pxyuP7jbv6mhjRqSgGiaTQ69a2-gAnFgsgOaB12neXCekRqtT4AuDrwRrTvYE61EFfOrNo0q6r6_fjIxeToWVt_IJZ6MyuTm2Q/s640/alortiz%2540ilaria%253A%257E-proyecto-ambiente_002.png" width="640" /></a></div>
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
<br />
<pre class="brush: bash">python proyecto/setup.py develop
</pre>
Y nuestro proyecto se ve de esta forma
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUcQKg31gRfGpByZxWuBDabUH3R2t3j_eNIDTizKmqhNoDm3ElgXaNTFjGStK0Q4nDLdmHla9v4uVpCwhVfsPakIDsHTXwiQOP-TU5zb8yxNYekLXn3WhPKsxuGUe_NbVmH-QXu-9w9cA/s1600/alortiz%2540ilaria%253A%257E-proyecto-ambiente_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="866" data-original-width="1190" height="466" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUcQKg31gRfGpByZxWuBDabUH3R2t3j_eNIDTizKmqhNoDm3ElgXaNTFjGStK0Q4nDLdmHla9v4uVpCwhVfsPakIDsHTXwiQOP-TU5zb8yxNYekLXn3WhPKsxuGUe_NbVmH-QXu-9w9cA/s640/alortiz%2540ilaria%253A%257E-proyecto-ambiente_003.png" width="640" /></a></div>
Y eso es todo.VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-57246566308416155132017-03-13T13:50:00.000-06:002017-03-13T14:41:55.166-06:00Usando snapshot para equipos virtualizados con XEN en DebianPese al título, esta entrada aún necesita llevar un subtítulo: <b>"Usando qcow en máquinas virtuales en Debian Jessie"</b>, y ya, no se necesitaría mayor introducción al respecto.<br />
<br />
Cuando se sigue una <a href="http://wiki.salud.gob.sv/wiki/Instalar_Xen_en_Debian_con_LVM_y_bridges_a_varias_interfaces_de_red">guía de virtualización en Debian,</a> 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. <br />
<br />
Tampoco fuí capaz de encontrar una opción en <a href="https://xen-tools.org/software/xen-tools/">xen-create-image</a> que permita crear las imágenes con formato qcow. Crear la imagen con <i><b>qemu-img create</b></i> y luego apuntarla en <b>xen-create-image</b> con <b>--image-dev</b> y <b>--swap-dev</b> no funciona porque sin importar qué, xen-create-image convierte las imágenes a raw.<br />
<br />
Con todo esto, es necesario complicar el proceso de instalación de la siguiente manera:
<br />
<ul>
<li>Creamos la máquina virtual con un disco raw, que es el formato por defecto:
</li>
</ul>
<pre class="brush:bash">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/
</pre>
La instalación del sistema base empezará creando dos imágenes: <b>disk.img</b> y <b>swap.img </b>, dentro de <b>/var/lib/xen/domains/pdc.salud.gob.sv/ </b>en este caso según el hostname dado.<br />
<br />
<ul>
<li>Lo convertimos a qcow (Respecto a <b>preallocation</b>: 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)
</li>
</ul>
<pre class="brush:bash">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
</pre>
<ul>
<li>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:
</li>
</ul>
<pre class="brush:bash">#
# 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',
]
</pre>
Y el gran cambio será <b><i>file:</i></b> por <i><b>tap:qcow2:</b></i> y la extensión: <i>.<b>img</b></i> por <b><i>.qcow2</i> </b>en donde se apunte al disco que cambiamos
<br />
<pre class="brush:bash">#
# 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',
]
</pre>
Fuentes:<br />
<a href="https://gist.github.com/rubenerd/ab425d2ea388bea91780">Using qcow2 images in Xen 4.1 on Debian</a>
<br />
<br />VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-83537483404482409002016-06-30T08:24:00.003-06:002016-06-30T08:24:44.716-06:00Actualizando owncloud 8.1.8 a 9.0.0Aunque la relación de Owncloud con Debian no pasa por sus mejores momentos (<a href="http://lists.alioth.debian.org/pipermail/pkg-owncloud-maintainers/2016-March/002899.html">[Pkg-owncloud-maintainers] About no more ownCloud in Debian)</a> en realidad es posible seguir instalando al primero sobre el segundo.<br />
<br />
Siempre es recomendable mantener un ritmo de actualizaciones sano, pero si no es así, parece que es posible actualizar gradualmente (Esto es de suma importancia) de la siguiente forma:<br />
<br />
7.0.3-debian >> <a href="https://owncloud.org/changelog/#latest7" target="_blank">7.0.14</a> >> <a href="https://owncloud.org/changelog/#latest8.0" target="_blank">8.0.12</a> >> <a href="https://owncloud.org/changelog/#latest8.1" target="_blank">8.1.7</a>>> <a href="https://owncloud.org/changelog/#latest8.2" target="_blank">8.2.4</a> >> 9.0.2<br />
<ul>
</ul>
Según se recoge en <a href="https://owncloud.org/blog/upgrading-owncloud-on-debian-stable-to-official-packages/">Upgrading ownCloud on Debian Stable to official packages,</a> Esta forma gradual de hacer las actualizaciones pueden ser molestas, pero lo contrario (7.0.14 a 8.1.7, por ejemplo) es un proceso temerario que no estará automatizado por las herramientas de owncloud.<br />
<br />
Actualizé la instalación en producción desde el punto 8.1.7 (En este contexto, se refieren al <i>release</i>. Mi versión era la 8.1.8) al 8.2.4.<br />
En esta versión hubo un molesto problema con la app Documentos, cuya solución descrita en <a href="https://github.com/owncloud/documents/issues/639">ASSERTION FAILED: tried to unsubscribe unknown callback from event "input/compositionstart"</a> era actualizar a 9.0.2. Comprobé que con actualizar a 9.0.0 era suficiente.<br />
<br />
Los cambios en <i><b>/etc/apt/sources.list.d/owncloud.list</b></i> describen básicamente el proceso de actualización:<br />
<pre class="brush:bash">#deb http://download.opensuse.org/repositories/isv:ownCloud:community/Debian_7.0/ /
# Dejaron de usar el servicio de opensuse
#deb http://download.owncloud.org/download/repositories/8.2/Debian_7.0/ /
# Esta no sirve del todo. Hay un con el repositorio según parece
#deb http://download.owncloud.org/download/repositories/stable/Debian_7.0/ /
## Esta es totalmente estable, aunque no es precisamente la última versión pero si la primera del relase 9.0.0, me pareció buena idea llegar a este punto
deb http://download.owncloud.org/download/repositories/9.0.0/Debian_7.0/ /
</pre>
<br />
Por último, queda señalar que el paquete para la versión 9.0 se ha cambiado a <b>owncloud-files</b>, y que el paquete <b>owncloud-deps </b>no existe para esta versión de Debian. Cuando se instale owncloud-files, apache, amavis y php van a irse por un rato (Se supone que todos serían reemplazados por owncloud-deps). Una vez instalado se instalan todos de nuevo y todo funciona de maravilla.<br />
<pre class="brush:bash">aptitude install owncloud-files
Se instalarán los siguiente paquetes NUEVOS:
owncloud-files{b}
0 paquetes actualizados, 1 nuevos instalados, 0 para eliminar y 0 sin actualizar.
Necesito descargar 32,7 MB de ficheros. Después de desempaquetar se usarán 93,6 MB.
No se satisfacen las dependencias de los siguientes paquetes:
owncloud-files : Entra en conflicto: owncloud (<= 8.99.99) pero está instalado 8.2.5-1.1.
Entra en conflicto: owncloud-config-apache (<= 8.99.99) pero está instalado 8.2.5-1.1.
Entra en conflicto: owncloud-server (<= 8.99.99) pero está instalado 8.2.5-1.1.
Las acciones siguientes resolverán estas dependencias
Eliminar los paquetes siguientes:
1) owncloud
2) owncloud-config-apache
3) owncloud-server
¿Acepta esta solución? [Y/n/q/?]y
Se instalarán los siguiente paquetes NUEVOS:
owncloud-files
Se ELIMINARÁN los siguientes paquetes:
apache2{u} clamav{u} clamav-base{u} clamav-freshclam{u} libclamav7{u} libllvm3.0{u} libmcrypt4{u} libpq5{u} owncloud{a} owncloud-config-apache{a} owncloud-server{a} php-xml-parser{u} php5{u} php5-curl{u} php5-intl{u} php5-mcrypt{u} php5-mysql{u} php5-pgsql{u} php5-sqlite{u}
0 paquetes actualizados, 1 nuevos instalados, 19 para eliminar y 0 sin actualizar.
Necesito descargar 32,7 MB de ficheros. Después de desempaquetar se liberarán 17,9 MB.
¿Quiere continuar? [Y/n/?] y
</pre>
Y claro, backup. Hacer el backup de un sistema de backup es un poco gracioso pero así las cosasVTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-50190724509874815732016-06-23T10:30:00.000-06:002016-06-24T08:17:41.874-06:00Sistema de voceo con ElastixLos sistemas de voceo más comunes suelen estar basados en hardware y ser soluciones específicas, pero es posible configurar Asterisk (El que ya esta configurado por Elastix) para tener uno sustentado en nuestro servicio de VoIP.<br />
Con esto, una tarjeta de sonido para el servidor es el único costo en hardware que vamos a tener.<br />
<br />
Lo primero es configurar en <i><b>/etc/asterisk/modules.conf</b></i> la carga de los módulos que vamos a necesitar para que Asterisk sea capaz de usar el sistema de sonido del sistema. Buscamos las siguientes líneas para que queden de la siguiente forma:
<br />
<pre name="brush:bash">;
; Load either OSS or ALSA, not both
; By default, load no console driver
;
noload => chan_alsa.so
load => chan_oss.so
</pre>
Luego, habrá que configurar el fichero <i><b>/etc/asterisk/oss.conf</b></i><br />
con algunas configuraciones propias del módulo. Hay que revisar algo en el sistema si es que existe el dispositivo <i><b>/dev/dsp</b></i> o por el contrario será <i><b>/dev/dsp1</b></i>. El primero esta configurado por defecto, para el segundo (Y para otros, supongo) usamos la opción <b>device</b>
<br />
<pre name="brush:bash">[general]
autoanswer=yes
context=from-internal
overridecontext=yes
extension=s
language=en
playbackonly=yes
device = /dev/dsp1
</pre>
Por último, configuramos la extensión a usar en el fichero <i><b>/etc/asterisk/extensions_custom.conf</b></i>, que por lo demás es el lugar donde se configurar extensiones de este tipo
<br />
<pre name="brush:bash">[voceo-neomano]
; Primero hay que ver como funciona sin el
exten => 1030,1,Dial(console/dsp,20,A(beep))
exten => 1030,1,Set(PITCH_SHIFT(both)=.15)
exten => 1030,n,Hangup()
</pre>
A voceo-neomano será necesario agregarlo bajo [from-internal-custom] con include. Como ejemplo, esa sección queda de la siguiente forma:
<br />
<pre name="brush:bash">[from-internal-custom]
exten => 1234,1,Playback(demo-congrats) ; extensions can dial 1234
exten => 1234,2,Hangup()
exten => h,1,Hangup()
include => agentlogin
include => conferences
include => calendar-event
include => weather-wakeup
include => voceo-neomano
</pre>
<br />
Fuentes:<br />
<a href="http://www.neomano.com/2011/09/sistema-de-voceo-anti-feedback-de-bajo-costo-para-elastix/">Sistema de voceo anti-feedback de bajo costo para Elastix</a><br />
<a href="http://forums.asterisk.org/viewtopic.php?f=1&t=87068">Unable to re-open DSP device /dev/dsp</a> VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-29147819795488828782016-06-15T11:13:00.004-06:002017-06-22T14:04:28.742-06:00Apuntes sobre el error de Squid3 / SquidGuard en Debian JessieComo la lectura no es tan amena como quisiera, recomiendo que siga a la respuesta a este problema, que publiqué un tiempo después:<br />
<a href="http://apuntestuxianos.blogspot.com/2017/06/apuntes-sobre-el-error-de-squid.html">Apuntes sobre el error de Squid / SquidGuard en Debian: Solucionado </a><br />
<br />
La configuración mínima necesaria para Squid3 parece ir de la siguiente forma:
<br />
<pre class="brush:bash">acl usuarios src 10.40.20.0/24
acl usuarios src 10.20.20.0/24
acl Safe_ports port 80 443 8080 20 21
## Según https://forums.gentoo.org/viewtopic-t-952948-start-0.html
## Hay que comentar esto en Squid 3.4 porque ya esta configurado por defecto
# acl manager proto cache_object
acl CONNECT method CONNECT
acl NONE method NONE
ftp_passive off
host_verify_strict on
http_access deny NONE
http_access deny !Safe_ports
http_access allow usuarios
http_access deny all
## Es necesario que hay al menos uno sin intercept
http_port 10.20.20.1:3128
## TODO: ¿Funcionará al descomentar lo siguiente?
# http_port 10.20.20.1:3128 intercept
http_port 10.40.20.1:3128 intercept
cache_mem 469 MB
## TODO: Ni siquiera recuerdo el origen de estas líneas, pero su funcionamiento no tiene
## implicaciones sobre nuestro problema
#cache_dir aufs /var/spool/squid3 500 16 256
#cache_dir aufs /var/spool/squid3-${process_number} 500 16 256 min-size=322560
#debug_options 84,1
#debug_options 85,2
debug_options ALL,2
coredump_dir /var/spool/squid3/dump
url_rewrite_program /usr/bin/squidGuard -c /etc/squidguard/squidGuard.conf -d
url_rewrite_children 5 startup=0 idle=1 concurrency=3
url_rewrite_host_header off
refresh_pattern . 0 20% 4320
relaxed_header_parser warn
connect_timeout 20 seconds
shutdown_lifetime 3 seconds
cache_mgr fws@salud.gob.sv
httpd_suppress_version_string on
visible_hostname firewall.dominio.com
error_default_language es-sv
prefer_direct on
check_hostnames on
dns_retransmit_interval 2 seconds
dns_timeout 1 minutes
dns_nameservers 10.10.20.20 10.10.20.21
dns_v4_first on
</pre>
La configuración mínima necesaria en squidGuard, y estoy hablando que esto es apenas un ejemplo para nada funcional de como va a trabajar realmente:
<br />
<pre class="brush:bash">#
# CONFIG FILE FOR SQUIDGUARD
#
# Caution: do NOT use comments inside { }
#
dbhome /var/lib/squidguard/db
logdir /var/log/squidguard
#
# TIME RULES:
# abbrev for weekdays:
# s = sun, m = mon, t =tue, w = wed, h = thu, f = fri, a = sat
time laboral {
weekly * 00:15 - 12:29
weekly * 13:15 - 23:55
}
#
# SOURCE ADDRESSES:
#
src usuarios {
ip 10.20.20.0/24
}
#
# DESTINATION CLASSES:
#
# [see also in file dest-snippet.txt]
dest deportes {
domainlist BL/recreation/sports/domains
log deportes.log
}
dest webtv {
domainlist BL/webtv/domains
log ocio.log
}
#dest adult {
# domainlist BL/adult/domains
# urllist BL/adult/urls
# expressionlist BL/adult/expressions
# redirect http://admin.foo.bar.de/cgi-bin/blocked.cgi?clientaddr=%a&clientname=%n&clientuser=%i&clientgroup=%s&targetgroup=%t&url=%u
#}
#
# ACL RULES:
#
acl {
usuarios {
pass !in-addr !deportes !webtv any
}
default {
pass none
redirect http://admin.foo.bar.de/cgi-bin/blocked.cgi?clientaddr=%a&clientname=%n&clientuser=%i&clientgroup=%s&targetgroup=%t&url=%u
}
}
</pre>
Esto funcionaba en Debian Squeezy/Wheezy perfectamente. Tiene la ventaja de ser presumiblemente rápido a la hora de filtrar tráfico, la configuración se realiza por medio de ACL que son bastante fáciles de entender y tiene altas posibilidades con las listas
En Debian Jessie hay un problema con las versiones presentes en los respositorios, precisamente en la forma en que squid3 le comunica los datos de la petición HTTP al redirector SquidGuard.
Cuando se hace una prueba a redireccionar desde squidGuard
<br />
<pre class="brush:bash">$ echo "http://anontv.com 10.20.20.11 - - GET" | squidGuard -c /etc/squidguard/squidGuard.conf
OK rewrite-url="http://admin.foo.bar.de/cgi-bin/blocked.cgi?clientaddr=10.20.20.11&clientname=&clientuser=&clientgroup=usuarios&targetgroup=webtv&url=http://anontv.com"
</pre>
Mientras que <b>/var/log/squid3/cache.log<i></i></b> es posible ver
<br />
<pre class="brush:bash">2016/06/14 19:22:20.711 kid1| client_side_reply.cc(1969) processReplyAccessResult: The reply for GET http://admin.foo.bar.de/cgi-bin/blocked.cgi?clientaddr=http:&clientname=/www.anontv.com/&clientuser=10.20.20.1/firewall.salud.gob.sv&clientgroup=default&targetgroup=none&url=0 is ALLOWED, because it matched 'usuarios'
</pre>
cuando se hace una petición a squid3 desde un equipo cliente. Usando un navegador, telnet o algo como wget. Por lo pronto no encuentro la solución a este problema, ahora mismo estoy bajando el DVD de Debian Testing, quizá en Strech los paquetes disponibles ya no tengan ese problema.
Puede verse como el <b>clientaddr</b> que devuelve SquidGuard es diferente en cada caso. Así que debemos suponer que ese es el valor que squidGuard toma como IP a la hora de relacionarlo con las ACL.<br />
<br />
Fuentes:<br />
<ul>
<li><a href="http://vtacius.github.io/firewall/">Firewall para pequeñas organizaciones</a></li>
<li><a href="http://www.squidguard.org/Doc/extended.html">Extended Configuration of SquidGuard</a></li>
<li><a href="http://metadata.ftp-master.debian.org/changelogs/main/s/squidguard/squidguard_1.5-4_squidguard.README.Debian">squidguard for Debian</a></li>
<li><a href="http://wiki.squid-cache.org/Features/AddonHelpers">Feature: Add-On Helpers for Request Manipulation</a></li>
<li><a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=773983">Debian Bug report logs - #773983</a>
</li>
</ul>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-32241233089514066012016-06-11T10:30:00.000-06:002016-06-22T14:42:01.638-06:00Colander: O de como validar un JSON en PyramidConfieso que he dejado alguna aplicación sin que, una vez validados los datos en el cliente, los valide en el servidor.<br />
Pero esta vez quiero hacer las cosas bien, así que encontré de suerte a Colander (Con una documentación bastante útil hasta ahora) que es capaz de realizar algo como validar los datos JSON una vez llegan a nuestra aplicación. Y se integra bien con Pyramid. Y no hay ningún problema con los test, excepto escribirlos.<br />
Añadimos colander como dependencia de nuestro proyecto en <b>requires</b> de <i><b>./ambiente/aplicacion/setup.py</b></i>
<br />
<pre class="brush:python">(...)
requires = [
'pyramid',
'pyramid_debugtoolbar',
'waitress',
'nose',
'webtest',
'coverage',
'colander'
]
(...)
</pre>
Lo instalamos
<br />
<pre class="brush:python"># Este sí, desde el directorio ./ambiente/aplicacion es la mejor idea
python setup.py develop
</pre>
Creamos un fichero <i><b>./ambiente/aplicacion/aplicacion/schemas/usuario.py</b></i> el que creamos una clase que, descendiendo de algún tipo específico de Colander, sea capaz de definir la estructura de nuestro objeto JSON, aunque en realidad podemos hacer validaciones de cadenas simples y todo bien.
<br />
<pre class="brush:python">mkdir ./ambiente/aplicacion/aplicacion/schemas
touch ./ambiente/aplicacion/aplicacion/schemas/__init__.py
</pre>
Y creamos el fichero <i><b>./ambiente/aplicacion/aplicacion/schemas/usuario.py</b></i> con el siguiente contenido:
<br />
<pre class="brush:python"># coding: utf-8
import colander
class Palabra(colander.SequenceSchema):
# Palabra es un string utf-8 de al menos dos caracteres
palabra = colander.SchemaNode(colander.String('utf-8'), validator=colander.Length(min=2))
class UsuarioEsquema(colander.MappingSchema):
# nombre es un string utf-8
nombre = colander.SchemaNode(colander.String('utf-8'))
# apellido es un string utf-8
apellido = colander.SchemaNode(colander.String('utf-8'))
# palabras es una lista de palabras
palabras = Palabra(validator=colander.Length(min=1))
if __name__ == '__main__':
esquema = Data()
data = {'nombre': 'Alexander', 'apellido': 'Ortíz', 'palabras': ['usuario', 'formidable']}
data = {'nombre': 389, 'palabras': ['usuario', 'formidable']}
</pre>
Y en lo que ya parece una costumbre, actualizamos nuestra vista <i><b>./ambiente/aplicacion/aplicacion/views/actividades.py</b></i>
<br />
<pre class="brush:python"># coding: utf-8
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPBadRequest
from colander import Invalid
from ..fichero_app.ficheros import Usuarios
from ..schemas.usuario import UsuarioEsquema
# Empieza el trabajo con la autenticación
from pyramid.security import Allow, Deny, Everyone, NO_PERMISSION_REQUIRED, Authenticated
# Dejamos por acá un esquema listo para usarse
esquema = UsuarioEsquema()
@view_config(route_name='ficheros_listado', renderer='json', permission='listar')
def ficheros_listado(request):
"""Cuando request_method se configura acà, el mensaje es diferente porque
la operación alcanzada es diferente
The resource could not be found.
predicate mismatch for view get_listar_ficheros (request_method = GET,HEAD)
Por tanto lo mejor es configurarlo allá en __init__
"""
ficheros = Usuarios()
listado = ficheros.listado()
return {'respuesta': listado}
@view_config(route_name='ficheros_detalle', renderer='json', permission='detallar')
def ficheros_detalle(request):
usuario = request.matchdict['usuario']
ficheros = Usuarios()
detalle = ficheros.detalle(usuario)
return {'respuesta': detalle}
@view_config(route_name='ficheros_creacion', renderer='json', permission='creacion')
def ficheros_creacion(request):
try:
usuario = request.json_body['usuario']
data = esquema.deserialize(request.json_body['data'])
except Invalid as e:
return HTTPBadRequest(json_body=e.asdict())
except Exception as e:
return HTTPBadRequest()
ficheros = Usuarios()
creacion = ficheros.creacion(usuario, data)
return {'respuesta': creacion}
@view_config(route_name='ficheros_modificacion', renderer='json', permission='modificacion')
def ficheros_modificacion(request):
usuario = request.matchdict['usuario']
try:
data = esquema.deserialize(request.json_body['data'])
except Invalid as e:
return HTTPBadRequest(json_body=e.asdict())
except Exception as e:
return HTTPBadRequest()
ficheros = Usuarios()
modificacion = ficheros.modificacion(usuario, data)
return {'respuesta': modificacion}
@view_config(route_name='ficheros_borrado', renderer='json', permission='borrado')
def ficheros_borrado(request):
usuario = request.matchdict['usuario']
ficheros = Usuarios()
borrado = ficheros.borrado(usuario)
return {'respuesta': borrado}
</pre>
Y para mantener las buenas costumbres, agregamos a <b>Creacion</b> un método <b>test_ficheros_creacion_malformed</b> en <b>./ambiente/aplicacion/aplicacion/tests/testFuncionales.py</b> de la siguiente forma:
<br />
<pre class="brush:python"> def test_ficheros_creacion_malformed(self):
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
# Dañamos el nombre, así nos aseguramos que sea ese contenido el equivocado
datos['data']['nombre'] = 389
respuesta = self.testapp.post_json('/ficheros', status=400, params=datos)
self.assertRegexpMatches(respuesta.json_body['nombre'], '389 is not a string')
</pre>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-28515139264418992722016-06-09T10:30:00.000-06:002016-06-09T10:30:03.734-06:00Test funcionales en Pyramid para vistas que requieren autenticaciónLos test funcionales no están funcionando en este momento.
<br />
<pre class="brush: bash">======================================================================
ERROR: test_ficheros_borrado (aplicacion.tests.testFuncionales.Borrado)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/var/www/ambiente/aplicacion/aplicacion/tests/testFuncionales.py", line 78, in setUp
self.testapp.post_json('/ficheros', status=200, params=datos)
File "/var/www/ambiente/local/lib/python2.7/site-packages/WebTest-2.0.21-py2.7.egg/webtest/utils.py", line 36, in wrapper
return self._gen_request(method, url, **kw)
File "/var/www/ambiente/local/lib/python2.7/site-packages/WebTest-2.0.21-py2.7.egg/webtest/app.py", line 740, in _gen_request
expect_errors=expect_errors)
File "/var/www/ambiente/local/lib/python2.7/site-packages/WebTest-2.0.21-py2.7.egg/webtest/app.py", line 636, in do_request
self._check_status(status, res)
File "/var/www/ambiente/local/lib/python2.7/site-packages/WebTest-2.0.21-py2.7.egg/webtest/app.py", line 671, in _check_status
"Bad response: %s (not %s)", res_status, status)
AppError: Bad response: 403 Forbidden (not 200)
(...)
</pre>
Como mencioné, estos ya suceden en un nivel bastante arriba de la aplicación, casi que son las peticiones tan normales como las que hacemos con el navegador.<br />
<br />
Los test unitarios siguen testando partes específicas del código sin importarle en realidad la actitud HTTP, incluso aunque en unos de ellos usemos un <b>pyramid.request.Request</b> para la petición. Que es real, pero no tan real como se esperaría, y vemos que eso es un poco bueno.<br />
<br />
Por otra parte, <b>webtest.TestApp </b>es tan real que de hecho acepta cookies. Ahora veremos lo conveniente de automatizar los test: Nos seguimos ahorrando hacer un intento de formulario con una simple petición post
<br />
<pre class="brush: bash">self.testapp.post('/login', status=302, params={'username':'vtacius'})
</pre>
Y algo tan simple en el <b>setUp</b> de cada clase nos permitirá autenticarnos en el sistema y realizar todas las operaciones que queramos comprobar<br />
Así que autenticamos los test de la siguiente forma en el fichero correspondiente <b><i>./ambiente/aplicacion/aplicacion/tests/testFuncionales.py</i></b>:
<br />
<pre class="brush: python">#coding:utf-8
from unittest import TestCase
class Listado(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
def test_ficheros_listado(self):
respuesta = self.testapp.get('/ficheros', status=200, xhr=True)
self.assertEqual(respuesta.content_type, 'application/json')
self.assertItemsEqual(respuesta.json_body['respuesta'], ['alortiz', 'kpenate', 'opineda'])
class Detalle(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
def test_unauth_detalle(self):
respuesta = self.testapp.get('/ficheros/' + 'alortiz', status=403, xhr=True)
self.assertRegexpMatches(respuesta.body, 'Access was denied to this resource')
def test_ficheros_detalle(self):
# Habrá que loguear en cada test si no se hace en setUp()
self.testapp.post('/login', status=302, params={'username':'vtacius'})
respuesta = self.testapp.get('/ficheros/' + 'alortiz', status=200, xhr=True)
self.assertItemsEqual(respuesta.json_body['respuesta']['palabras'], ['ambiente', 'publico'])
def test_ficheros_detalle_inexistente(self):
# Habrá que loguear en cada test si no se hace en setUp()
self.testapp.post('/login', status=302, params={'username':'vtacius'})
respuesta = self.testapp.get('/ficheros/' + 'fitzcarraldo', status=200, xhr=True)
self.assertEqual(respuesta.json_body['respuesta']['error'], 'No such file or directory')
class Creacion(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
# Loguemos en la aplicación con datos reales
self.testapp.post('/login', status=302, params={'username':'vtacius'})
def tearDown(self):
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
def test_ficheros_creacion(self):
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
respuesta = self.testapp.post_json('/ficheros', status=200, params=datos)
self.assertDictEqual(respuesta.json_body['respuesta'], datos['data'])
class Modificacion(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
# Loguemos en la aplicación con datos reales
self.testapp.post('/login', status=302, params={'username':'vtacius'})
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
self.testapp.post_json('/ficheros', status=200, params=datos)
def tearDown(self):
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
def test_ficheros_modificacion(self):
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['espejismo', 'olvido']}}
respuesta = self.testapp.put_json('/ficheros/' + 'fcornejo', status=200, params=datos)
self.assertDictEqual(respuesta.json_body['respuesta'], datos['data'])
class Borrado(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
# Loguemos en la aplicación con datos reales
self.testapp.post('/login', status=302, params={'username':'vtacius'})
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
self.testapp.post_json('/ficheros', status=200, params=datos)
def test_ficheros_borrado(self):
self.testapp.post('/login', status=302, params={'username':'vtacius'})
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
self.assertEqual(respuesta.json_body['respuesta'], 'fcornejo')
</pre>
Con un poco de verbosidad, la salida se verá ahora de la siguiente manera:
<br />
<pre class="brush: bash">nosetests -v
test_ficheros_borrado (aplicacion.tests.testFuncionales.Borrado) ... ok
test_ficheros_creacion (aplicacion.tests.testFuncionales.Creacion) ... ok
test_ficheros_detalle (aplicacion.tests.testFuncionales.Detalle) ... ok
test_ficheros_detalle_inexistente (aplicacion.tests.testFuncionales.Detalle) ... ok
test_unauth_detalle (aplicacion.tests.testFuncionales.Detalle) ... ok
test_ficheros_listado (aplicacion.tests.testFuncionales.Listado) ... ok
test_ficheros_modificacion (aplicacion.tests.testFuncionales.Modificacion) ... ok
test_ficheros_borrado (aplicacion.tests.testUnitarios.Borrado) ... ok
test_ficheros_borrado_inexistente (aplicacion.tests.testUnitarios.Borrado) ... ok
test_ficheros_creacion (aplicacion.tests.testUnitarios.Creacion) ... ok
test_ficheros_creacion_json_malformed (aplicacion.tests.testUnitarios.Creacion) ... ok
test_ficheros_detalle (aplicacion.tests.testUnitarios.Detalle) ... ok
test_ficheros_detalle_noexistente (aplicacion.tests.testUnitarios.Detalle) ... ok
test_ficheros_listado (aplicacion.tests.testUnitarios.Listado) ... ok
test_ficheros_modificacion (aplicacion.tests.testUnitarios.Modificacion) ... ok
Name Stmts Miss Cover Missing
-------------------------------------------------------------------
aplicacion.py 19 0 100%
aplicacion/fichero_app.py 0 0 100%
aplicacion/fichero_app/ficheros.py 46 11 76% 21-22, 36, 52-55, 69-72
aplicacion/resources.py 5 0 100%
aplicacion/security.py 5 0 100%
aplicacion/tests.py 0 0 100%
aplicacion/tests/testFuncionales.py 69 0 100%
aplicacion/tests/testUnitarios.py 99 0 100%
aplicacion/views.py 0 0 100%
aplicacion/views/actividades.py 36 2 94% 48-49
aplicacion/views/autenticacion.py 7 0 100%
-------------------------------------------------------------------
TOTAL 286 13 95%
----------------------------------------------------------------------
Ran 15 tests in 1.546s
OK
</pre>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-14491914114153571392016-06-07T10:30:00.000-06:002016-06-07T18:20:13.072-06:00Un vistazo a la autenticación y autorización con PyramidAcostumbrado a que las Universidades nos hagan ver ambas cosas como una misma, siempre es un poco complicado entrar a este tema en cualquier framework, aunque después que has abierto los ojos todos es coser y cantar. Según la <a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/tutorials/wiki/authorization.html">documentación de Pyramid al respecto</a>, la forma más sencilla de empezar a configurar la seguridad en esta wea es la siguiente:
Creamos el fichero<i> <b>./ambiente/aplicacion/aplicacion/security.py</b></i> con el siguiente contenido:
<br />
<pre class="brush:python">USERS = {'vtacius':'editor',
'viewer':'viewer'}
GROUPS = {'vtacius':['group:editors','group:admins']}
def groupfinder(userid, request):
if userid in USERS:
return GROUPS.get(userid, [])
</pre>
Lo que necesitamos en realidad es a <b>groupfinder</b>. Los diccionarios USERS y GROUPS son, por decirlo de una forma, nuestra forma de simular nuestra base de datos. De hecho, <b>esta función no es del todo obligatoria</b>. Sólo la usamos si queremos agregar un <a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/glossary.html#term-principal">principals</a> a nuestro usuario logueado. Sea cual sea la forma que usemos para esta función (Seguramente habrá una consulta a una base de datos), lo importante es que debe devolver una lista de <b>principals</b> (Que a esta altura se antoja entenderlos como "roles") en la forma mostrada:
<br />
<pre class="brush:python">['groups:editors','groups:publishers']
</pre>
<br />
Luego creamos el ficheros <b><i>./ambiente/aplicacion/aplicacion/resources.py</i></b> con el siguiente contenido:
<br />
<pre class="brush:python">from pyramid.security import Allow, Everyone, Authenticated
class Root(object):
__acl__ = [
(Allow, Everyone, 'listar'),
(Allow, Authenticated, 'detallar'),
(Allow, 'groups:admins', 'creacion'),
(Allow, 'groups:editors', 'modificacion'),
(Allow, 'groups:admins', 'borrado')
]
def __init__(self, request):
pass
</pre>
Que es básicamente la ACL de la aplicación. La ACL en cuestión de compone de una lista de tuplas (Llamadas <a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/glossary.html#term-ace">ACE</a>). que tienen la siguiente forma:
<br />
<pre class="brush:python">({{Acción a tomar}}, {{<b>Principals</b> necesario}}, {{Nombre de ACE}})
</pre>
Para unir todo esto, necesitamos modificar el <b><i>./ambiente/aplicacion/aplicacion/__init__.py</i></b> de nuestra aplicación para configurar la autenticación y autorización:
<br />
<pre class="brush:python"># coding: utf-8
from pyramid.config import Configurator
# Empieza el trabajo con la autenticación de la wea esta
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.security import Authenticated
from .security import groupfinder
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
# Empieza el trabajo con la autenticacion: Creo las politicas, así nomás y sin gracia
# Por cierto, 'c3cre3t0 debería cambiarse a algo más personal
# como callback, aparece nuestro amigo groupfinder, pero esto podría obviarse
authn_policy = AuthTktAuthenticationPolicy('c3cr3t0', hashalg='sha512', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
# Y por acá agregamos a resources.py
config = Configurator(settings=settings, root_factory='.resources.Root')
# Empieza el trabajo con la autenticacion: Configuro las politicas en la aplicación
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
# Donde sucede la magia del login
config.add_route(name='login', pattern='/login', request_method='POST')
# Cuando request_method se encuentra acá, es devuelto un error como
# "The resource could not be found."
# Así que es como mejor configurar desde acá a request_method
config.add_route('ficheros_listado', '/ficheros', request_method='GET')
config.add_route('ficheros_detalle', '/ficheros/{usuario}', request_method='GET')
config.add_route('ficheros_creacion', '/ficheros', request_method='POST')
config.add_route('ficheros_modificacion', '/ficheros/{usuario}', request_method='PUT')
config.add_route('ficheros_borrado', '/ficheros/{usuario}', request_method='DELETE')
config.scan()
return config.make_wsgi_app()
</pre>
Y añadimos los permisos en nuestras vistas. Pues parece que sólo funcionan de esa manera. Así que el contenido de <b>./ambiente/aplicacion/aplicacion/views/actividades.py<i></i></b> queda de la siguiente forma
<br />
<pre class="brush:python"># coding: utf-8
from pyramid.view import view_config
from ..fichero_app.ficheros import Usuarios
from pyramid import httpexceptions as exception
# Podrías usar los siguientes en lugar de los permisos personalizados que tienes en este momento, pero no
# La importación de hecho no es necesaria para nada
from pyramid.security import Everyone, Authenticated
@view_config(route_name='ficheros_listado', renderer='json', permission='listar')
def ficheros_listado(request):
"""Cuando request_method se configura acà, el mensaje es diferente porque
la operación alcanzada es diferente
The resource could not be found.
predicate mismatch for view get_listar_ficheros (request_method = GET,HEAD)
Por tanto lo mejor es configurarlo allá en __init__
"""
ficheros = Usuarios()
listado = ficheros.listado()
return {'respuesta': listado}
@view_config(route_name='ficheros_detalle', renderer='json', permission='detallar')
def ficheros_detalle(request):
usuario = request.matchdict['usuario']
ficheros = Usuarios()
detalle = ficheros.detalle(usuario)
return {'respuesta': detalle}
@view_config(route_name='ficheros_creacion', renderer='json', permission='creacion')
def ficheros_creacion(request):
try:
usuario = request.json_body['usuario']
data = request.json_body['data']
except Exception as e:
return exception.HTTPBadRequest()
ficheros = Usuarios()
creacion = ficheros.creacion(usuario, data)
return {'respuesta': creacion}
@view_config(route_name='ficheros_modificacion', renderer='json', permission='modificacion')
def ficheros_modificacion(request):
usuario = request.matchdict['usuario']
try:
data = request.json_body['data']
except Exception as e:
return exception.HTTPBadRequest()
ficheros = Usuarios()
modificacion = ficheros.modificacion(usuario, data)
return {'respuesta': modificacion}
@view_config(route_name='ficheros_borrado', renderer='json', permission='borrado')
def ficheros_borrado(request):
usuario = request.matchdict['usuario']
ficheros = Usuarios()
borrado = ficheros.borrado(usuario)
return {'respuesta': borrado}
</pre>
Y solo faltaría agregar la pequeña vista que se encarga del login <b>./ambiente/aplicacion/aplicacion/views/login.py<i></i></b>:
<br />
<pre class="brush: python"># coding: utf-8
from pyramid.view import view_config
from pyramid.security import remember
from pyramid.httpexceptions import HTTPFound
@view_config(route_name='login', request_method='POST')
def login(request):
usuario = request.POST.get('username')
cabeceras = remember(request, usuario)
return HTTPFound(headers=cabeceras)
# De la siguiente forma, no hay HTML devuelto, pero todo funciona bien
#response = request.response
#response.headerlist.extend(cabeceras)
#return response
</pre>
Así procedemos a probar nuestra aplicación desde consola:
<br />
<pre class="brush: bash">$ curl -w '\n' -X GET http://localhost:6543/ficheros
{"respuesta": ["alortiz", "kpenate", "opineda"]}
</pre>
Atentos a este. <b>-i</b> agregará las cabeceras que recibimos de respuesta para que quede totalmente claro lo que pasa:
<br />
<pre class="brush: bash">$ curl -i -w '\n' -X GET http://localhost:6543/ficheros/alortiz
HTTP/1.1 403 Forbidden
Content-Length: 1085
Content-Type: text/html; charset=UTF-8
Date: Tue, 07 Jun 2016 02:36:22 GMT
Server: waitress
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<h1>403 Forbidden</h1>
Access was denied to this resource.<br/><br/>
debug_authorization of url http://localhost:6543/ficheros/alortiz (view name u'' against context &lt;aplicacion.resources.Root object at 0x7f501c490e90&gt;): ACLDenied permission 'detallar' via ACE '&lt;default deny&gt;' in ACL [('Allow', 'system.Everyone', 'listar'), ('Allow', 'system.Authenticated', 'detallar'), ('Allow', 'groups:admins', 'creacion'), ('Allow', 'groups:editors', 'modificar'), ('Allow', 'groups:admins', 'eliminar')] on context &lt;aplicacion.resources.Root object at 0x7f501c490e90&gt; for principals ['system.Everyone']
<link rel="stylesheet" type="text/css" href="http://localhost:6543/_debug_toolbar/static/toolbar/toolbar_button.css">
<div id="pDebug">
<div id="pDebugToolbarHandle">
<a title="Show Toolbar" id="pShowToolBarButton"
href="http://localhost:6543/_debug_toolbar/313339393832303438363539353336" target="pDebugToolbar">&#171; FIXME: Debug Toolbar</a>
</div>
</div>
</body>
</html>
</pre>
<a href="https://es.wikipedia.org/wiki/HTTP_403">HTTP/1.1 403 Forbidden</a>. Que ha funcionado. Así que ahora probamos la autenticación mediante curl desde la siguiente forma:
<br />
<pre class="brush: html">$ curl -i -w "\n" -X POST http://localhost:6543/login -d "username=vtacius" </pre>
<pre class="brush: html">HTTP/1.1 302 Found
Content-Length: 556
Content-Type: text/html; charset=UTF-8
Date: Tue, 07 Jun 2016 02:43:25 GMT
Location: http://localhost:6543/login
Server: waitress
Set-Cookie: auth_tkt=95c36928020725f40edd54266285117cfddd4d05b0cc42f969b9fee13a3ff52f4e5f98491c426f6c6bc5242d1ace926e264865a8cc1a30f127d1b1a6ec3de457575634cddnRhY2l1cw%3D%3D!userid_type:b64unicode; Path=/
Set-Cookie: auth_tkt=95c36928020725f40edd54266285117cfddd4d05b0cc42f969b9fee13a3ff52f4e5f98491c426f6c6bc5242d1ace926e264865a8cc1a30f127d1b1a6ec3de457575634cddnRhY2l1cw%3D%3D!userid_type:b64unicode; Domain=localhost; Path=/
Set-Cookie: auth_tkt=95c36928020725f40edd54266285117cfddd4d05b0cc42f969b9fee13a3ff52f4e5f98491c426f6c6bc5242d1ace926e264865a8cc1a30f127d1b1a6ec3de457575634cddnRhY2l1cw%3D%3D!userid_type:b64unicode; Domain=.localhost; Path=/
<html>
<head>
<title>302 Found</title>
</head>
<body>
<h1>302 Found</h1>
The resource was found at ; you should be redirected automatically.
<link rel="stylesheet" type="text/css" href="http://localhost:6543/_debug_toolbar/static/toolbar/toolbar_button.css">
<div id="pDebug">
<div id="pDebugToolbarHandle">
<a title="Show Toolbar" id="pShowToolBarButton"
href="http://localhost:6543/_debug_toolbar/313339393832303434383337353834" target="pDebugToolbar">&#171; FIXME: Debug Toolbar</a>
</div>
</div>
</body>
</html>
</pre>
(Para correr este última petición podría ser necesario reiniciar nuestro servidor web de prueba)<br />
Y otra vez nos hemos ahorrado escribir un formulario a la carrera con la opción <b>-d</b> de curl, con la que enviamos los datos que la aplicación requiere.<br />
<br />
Lo que necesitamos de ahora en adelante es usar la opción <b>-H</b> para configurar a curl que use la cookie <b>auth_tkt</b> que nos envío como respuesta la aplicación en cada petición que necesite autenticación. Cuidado con usar comillas dobles para limitar el contenido de la cookie, desde consola existe el inconveniente de:
<br />
<pre>$ curl -w '\n' -X GET http://localhost:6543/ficheros/alortiz -H 'Cookie: auth_tkt=95c36928020725f40edd54266285117cfddd4d05b0cc42f969b9fee13a3ff52f4e5f98491c426f6c6bc5242d1ace926e264865a8cc1a30f127d1b1a6ec3de457575634cddnRhY2l1cw%3D%3D!userid_type:b64unicode'
{"respuesta": {"palabras": ["ambiente", "publico"], "nombre": "Alexander", "apellido": "Ort\u00edz"}}
</pre>
<br />
Fuentes:<br />
<a href="http://lovelysystems.github.io/lovely.microblog/be_authentication.html">How to create high scalable web-backends for ios developers 0.0.0 documentation </a>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0tag:blogger.com,1999:blog-1457976448476438561.post-4863847718170986522016-06-05T11:30:00.000-06:002016-06-06T21:51:45.894-06:00Automatizando las pruebas para Pyramid con Test FuncionalesLas pruebas funcionales son otra onda. La cuestión es que se encuentran un poco más arriba de la aplicación, casi que casi es como hacer las pruebas de toda la vida en el navegador, así que hay poco control de la aplicación en sí, lo que de hecho no es tan malo después de todo, ya que tampoco el usuario final tendrá tanto control de nuestra aplicación.<br />
<br />
Para nuestro proyecto, bastará con que crear el fichero<i><b> ./ambiente/aplicacion/aplicacion/tests/testFuncionales.py</b></i> con el siguiente contenido. <br />
<pre class="brush: python">#coding:utf-8
from unittest import TestCase
class Listado(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
def test_ficheros_listado(self):
respuesta = self.testapp.get('/ficheros', status=200, xhr=True)
self.assertEqual(respuesta.content_type, 'application/json')
self.assertItemsEqual(respuesta.json_body['respuesta'], ['alortiz', 'kpenate', 'opineda'])
class Detalle(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
def test_ficheros_detalle(self):
respuesta = self.testapp.get('/ficheros/' + 'alortiz', status=200, xhr=True)
self.assertItemsEqual(respuesta.json_body['respuesta']['palabras'], ['ambiente', 'publico'])
def test_ficheros_detalle_inexistente(self):
respuesta = self.testapp.get('/ficheros/' + 'fitzcarraldo', status=200, xhr=True)
self.assertEqual(respuesta.json_body['respuesta']['error'], 'No such file or directory')
class Creacion(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
def tearDown(self):
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
def test_ficheros_creacion(self):
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
respuesta = self.testapp.post_json('/ficheros', status=200, params=datos)
self.assertDictEqual(respuesta.json_body['respuesta'], datos['data'])
class Modificacion(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
self.testapp.post_json('/ficheros', status=200, params=datos)
def tearDown(self):
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
def test_ficheros_modificacion(self):
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['espejismo', 'olvido']}}
respuesta = self.testapp.put_json('/ficheros/' + 'fcornejo', status=200, params=datos)
self.assertDictEqual(respuesta.json_body['respuesta'], datos['data'])
class Borrado(TestCase):
def setUp(self):
from aplicacion import main
from webtest import TestApp
app = main({})
self.testapp = TestApp(app)
datos = {'usuario': 'fcornejo', 'data': {'nombre': 'Flor', 'apellido':'Cornejo', 'palabras': ['ente', 'obvio']}}
self.testapp.post_json('/ficheros', status=200, params=datos)
def test_ficheros_borrado(self):
respuesta = self.testapp.delete('/ficheros/' + 'fcornejo', status=200)
self.assertEqual(respuesta.json_body['respuesta'], 'fcornejo')
</pre>
Al correr nosetest con un poco de verbosidad (Un <b>-v</b> más no añade nada importante, y el próximo es casi preocupante), la salida debería ser la siguiente:
<br />
<pre class="brush:bash">$ nosetests -v
test_ficheros_borrado (aplicacion.tests.testFuncionales.Borrado) ... ok
test_ficheros_creacion (aplicacion.tests.testFuncionales.Creacion) ... ok
test_ficheros_detalle (aplicacion.tests.testFuncionales.Detalle) ... ok
test_ficheros_detalle_inexistente (aplicacion.tests.testFuncionales.Detalle) ... ok
test_ficheros_listado (aplicacion.tests.testFuncionales.Listado) ... ok
test_ficheros_modificacion (aplicacion.tests.testFuncionales.Modificacion) ... ok
test_ficheros_borrado (aplicacion.tests.testUnitarios.Borrado) ... ok
test_ficheros_borrado_inexistente (aplicacion.tests.testUnitarios.Borrado) ... ok
test_ficheros_creacion (aplicacion.tests.testUnitarios.Creacion) ... ok
test_ficheros_creacion_json_malformed (aplicacion.tests.testUnitarios.Creacion) ... ok
test_ficheros_detalle (aplicacion.tests.testUnitarios.Detalle) ... ok
test_ficheros_detalle_noexistente (aplicacion.tests.testUnitarios.Detalle) ... ok
test_ficheros_listado (aplicacion.tests.testUnitarios.Listado) ... ok
test_ficheros_modificacion (aplicacion.tests.testUnitarios.Modificacion) ... ok
Name Stmts Miss Cover Missing
-------------------------------------------------------------------
aplicacion.py 10 0 100%
aplicacion/fichero_app.py 0 0 100%
aplicacion/fichero_app/ficheros.py 46 11 76% 21-22, 36, 52-55, 69-72
aplicacion/tests.py 0 0 100%
aplicacion/tests/testFuncionales.py 60 0 100%
aplicacion/tests/testUnitarios.py 99 0 100%
aplicacion/views.py 0 0 100%
aplicacion/views/actividades.py 35 2 94% 43-44
-------------------------------------------------------------------
TOTAL 250 13 95%
----------------------------------------------------------------------
Ran 14 tests in 1.231s
OK
</pre>
VTaciushttp://www.blogger.com/profile/15486571140800642727noreply@blogger.com0