<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[pbrun.fr]]></title><description><![CDATA[Ma plateforme de connaissance.]]></description><link>https://pbrun.fr/</link><generator>Ghost 0.9</generator><lastBuildDate>Fri, 04 Jul 2025 13:33:21 GMT</lastBuildDate><atom:link href="https://pbrun.fr/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Redirect traffic from a server to localhost #ssh]]></title><description><![CDATA[<p>XXX.XXX.XXX.XX1 correspond à l'ip du serveur distant.</p>

<p>Créer un tunnel SSH :  </p>

<pre><code>sudo ssh -R 80:localhost:80 root@XXX.XXX.XXX.XX1  
</code></pre>

<p>Mettre en place l'iptables pour rediriger le traffic depuis l'interface public vers le localhost. Car la redirection n'écoute que sur 127.0.0.1 par</p>]]></description><link>https://pbrun.fr/redirect-traffic-from-a-server-to-localhost-ssh/</link><guid isPermaLink="false">8b1cffcd-a66e-49f1-93d7-6162b08f9543</guid><category><![CDATA[SSH]]></category><category><![CDATA[Tunnel]]></category><category><![CDATA[Forward]]></category><category><![CDATA[Ops]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Thu, 05 Apr 2018 10:25:03 GMT</pubDate><content:encoded><![CDATA[<p>XXX.XXX.XXX.XX1 correspond à l'ip du serveur distant.</p>

<p>Créer un tunnel SSH :  </p>

<pre><code>sudo ssh -R 80:localhost:80 root@XXX.XXX.XXX.XX1  
</code></pre>

<p>Mettre en place l'iptables pour rediriger le traffic depuis l'interface public vers le localhost. Car la redirection n'écoute que sur 127.0.0.1 par défaut.  </p>

<pre><code>sysctl -w net.ipv4.conf.eth0.route_localnet=1  
sysctl net.ipv4.ip_forward=1  
iptables -A FORWARD -p tcp -d XXX.XXX.XXX.XX1 --dport 80 -j ACCEPT  
iptables -t nat -A PREROUTING -p tcp -d XXX.XXX.XXX.XX1 --dport 80 -j DNAT --to 127.0.0.1:80  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Formation agile]]></title><description><![CDATA[<p>Depuis maintenant 4 ans, je donne dans une école privée une formation d’une journée sur la sensibilisation aux méthodes agiles et notamment SCRUM.</p>

<p>Cette journée se décompose en deux parties, l’une théorique avec la présentation du manifest et de SCRUM et l’autre avec des ateliers type serious</p>]]></description><link>https://pbrun.fr/formation-agile/</link><guid isPermaLink="false">7f5d5158-ac44-43e1-bd64-fee253a72964</guid><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Tue, 13 Feb 2018 10:36:26 GMT</pubDate><content:encoded><![CDATA[<p>Depuis maintenant 4 ans, je donne dans une école privée une formation d’une journée sur la sensibilisation aux méthodes agiles et notamment SCRUM.</p>

<p>Cette journée se décompose en deux parties, l’une théorique avec la présentation du manifest et de SCRUM et l’autre avec des ateliers type serious game permettant de comprendre l’intérêt des principes agiles.</p>

<p>Chaque année, la même question m’est posé : Pouvez-vous nous donner vos supports de cours ? <br>
Et chaque année, je réponds la même chose : “C’est pas évident, évident vu que c’est du web (reveals)".</p>

<p>Techniquement, c’est possible de générer un PDF depuis reveals, mais non seulement le résultat n’est pas génial, mais en plus cela est assez inutile. Je préfère que les personnes présentent à cette formation retienne les mots clés et retrouve rapidement les ressources nécessaire sur internet. Car si ce n’est mon expérience personnel (qui n’apparaît pas sur mes slides) mes slides sont comme 90% de celle disponible sur internet (slideshare ou autres).</p>

<h2 id="listedelienutilesurlagilit">Liste de lien utile sur l’agilité</h2>

<p>Mais histoire de pas être vache, voici une liste de lien intéressant que je donne pour compléter cette formation :</p>

<h3 id="lindispensable">L’indispensable</h3>

<p><a href="http://agilemanifesto.org/">http://agilemanifesto.org/</a></p>

<h3 id="unbonrsumdescrum">Un bon résumé de SCRUM</h3>

<p><a href="https://www.agiliste.fr/guide-de-demarrage-scrum/">https://www.agiliste.fr/guide-de-demarrage-scrum/</a></p>

<h3 id="quelquesboardsenligne">Quelques boards en ligne</h3>

<p>En gratuit : <br>
<a href="https://taiga.io/">https://taiga.io/</a> <br>
En facile : <br>
<a href="https://www.pivotaltracker.com/">https://www.pivotaltracker.com/</a> ou trello <br>
En complet mais un peu complexe : <a href="https://www.targetprocess.com/">https://www.targetprocess.com/</a></p>

<h3 id="encomplment">En complément</h3>

<p>MoSCoW : <a href="https://fr.wikipedia.org/wiki/M%C3%A9thode_MoSCoW">https://fr.wikipedia.org/wiki/M%C3%A9thode_MoSCoW</a></p>

<p>Retrospective : <br>
<a href="https://agilemouse.com/2016/12/02/a-chaque-sprint-sa-retrospective-rex/">https://agilemouse.com/2016/12/02/a-chaque-sprint-sa-retrospective-rex/</a></p>

<h3 id="sivousaimezlesateliers">Si vous aimez les ateliers</h3>

<p>Agile Game France : <br>
<a href="http://chrisdeniaud.com/wikiAGFr/index.php?title=Accueil">http://chrisdeniaud.com/wikiAGFr/index.php?title=Accueil</a></p>

<p>Agile Breizh Game (excellent et rennais) : <br>
<a href="https://jfallet.wordpress.com/2015/05/14/les-agile-games-breizh-cest-le-20-juin-a-rennes/">https://jfallet.wordpress.com/2015/05/14/les-agile-games-breizh-cest-le-20-juin-a-rennes/</a></p>

<p>Liste non exhaustive des jeux agiles : <br>
<a href="http://chrisdeniaud.com/wikiAGFr/index.php?title=Jeux">http://chrisdeniaud.com/wikiAGFr/index.php?title=Jeux</a></p>

<h3 id="lesamispersonnessuivre">Les amis / personnes à suivre</h3>

<p>Une excellente boite qui forme sur l’agilité à Rennes : <br>
<a href="https://www.kokan.fr/">https://www.kokan.fr/</a></p>

<p>Le blog de Jean Claude GROSJEAN, Coach Agile : <br>
<a href="http://www.qualitystreet.fr/2017/07/06/quest-ce-quun-scrummaster/">http://www.qualitystreet.fr/2017/07/06/quest-ce-quun-scrummaster/</a></p>

<p>Un article d'Aurélien Morvant qui prouve que l’agilité, elle peux s’appliquer partout : <br>
<a href="https://www.orange-business.com/fr/blogs/usages-dentreprise/management-de-projet/quand-ma-liste-au-pere-noel-devient-un-product-backlog">https://www.orange-business.com/fr/blogs/usages-dentreprise/management-de-projet/quand-ma-liste-au-pere-noel-devient-un-product-backlog</a></p>

<p>Si vous avez des questions, n’hésitez pas.</p>]]></content:encoded></item><item><title><![CDATA[Docker setup ? CoreOS : Ubuntu 14.04]]></title><description><![CDATA[<p>Lors du Breizhcamp, j'avais axé mes conférences sur Docker dans l'idée de le mettre en place chez mon employeur actuel (Mobizel).</p>

<p><img src="https://pbrun.fr/content/images/2015/Jun/breizhcamp_logo.png" alt="Breizhcamp"></p>

<p>J'ai donc assisté à plusieurs conférences techniques ou simplement à des retours d'expériences. Vous pouvez les voir (ou les revoir) ici :</p>

<p><a href="https://www.youtube.com/playlist?list=PLHWl6dPnEb4kHfPp5D8CiGNfA64HQs_Hb">https://www.youtube.com/playlist?list=PLHWl6dPnEb4kHfPp5D8CiGNfA64HQs_Hb</a></p>]]></description><link>https://pbrun.fr/docker-setup-coreos-ubuntu-14-04/</link><guid isPermaLink="false">84eb6255-2181-4dca-87bc-d05c9982c614</guid><category><![CDATA[Docker]]></category><category><![CDATA[REX]]></category><category><![CDATA[CoreOS]]></category><category><![CDATA[Ubuntu14.04]]></category><category><![CDATA[vsphere]]></category><category><![CDATA[ovh]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Wed, 24 Jun 2015 08:08:10 GMT</pubDate><content:encoded><![CDATA[<p>Lors du Breizhcamp, j'avais axé mes conférences sur Docker dans l'idée de le mettre en place chez mon employeur actuel (Mobizel).</p>

<p><img src="https://pbrun.fr/content/images/2015/Jun/breizhcamp_logo.png" alt="Breizhcamp"></p>

<p>J'ai donc assisté à plusieurs conférences techniques ou simplement à des retours d'expériences. Vous pouvez les voir (ou les revoir) ici :</p>

<p><a href="https://www.youtube.com/playlist?list=PLHWl6dPnEb4kHfPp5D8CiGNfA64HQs_Hb">https://www.youtube.com/playlist?list=PLHWl6dPnEb4kHfPp5D8CiGNfA64HQs_Hb</a> <br>
ou <br>
<a href="https://www.parleys.com/channel/breizh-jug">https://www.parleys.com/channel/breizh-jug</a></p>

<p>(Les conférences ne sont pas encore toutes uploadées)</p>

<p>Suite à de nombreuses conférences intéressantes sur le sujet, j'ai pu appréhender correctement les problèmes possibles et j'avais, sans mettre les mains dedans, une bonne idée de comment fonctionne Docker et comment l'utiliser. Je me suis mis à chercher le meilleur moyen de mettre une machine avec Docker sur une de nos VMs (serveur chez ovh avec vsphere, c'est important ;) )</p>

<h1 id="coreos">CoreOS</h1>

<p>Suite à une recherche sur le sujet, je me penche plus sur CoreOS. Une distribution comprenant l'ensemble des outils pour gérer des containeurs Docker. Parfait !</p>

<p><img src="https://pbrun.fr/content/images/2015/Jun/CoreOS_logo.png" alt="CoreOS"></p>

<p>J'upload donc l'ISO, je lance une nouvelle VM et je me tente une install</p>

<ul>
<li><strong>Fail 1 :</strong> Pas de connexion sans configuration ip et j'ai pas vraiment l'habitude de ArchLinux surtout avec un clavier qwerty</li>
<li><strong>Fail 2 :</strong> Je fais mon install sans problème, je relance la machine et là, demande de login / password. Je cherche dans la documentation et là, je lis la phrase suivante <code>but be sure to include user information, especially an SSH key, in a Cloud-Config file, or else you will not be able to log into your CoreOS instance</code> Ah ... bon, ben je supprime ma VM et je recommence car sans login, ça va être difficile.</li>
<li><strong>Fail 3 :</strong> Je configure ma ssh key, je crée mon fichier <code>cloud-config.yml</code>. (toujours avec un qwerty) et je relance... Bah oui, mais sans réseau c'est difficile de me connecter avec une clé et il ne me semble pas que la console vsphere permette une connexion par clé (si oui, n'hésitez pas à me le dire ;) )</li>
</ul>

<h1 id="leretourauxsourcesubuntu1404">Le retour aux sources Ubuntu 14.04</h1>

<p>Bon après <strong>3</strong> fails, je décide de changer de stratégie et de partir sur une Ubuntu 14.04, même si je sais que le package Docker de base n'est pas à jour...</p>

<p>Environ 5min après la configuration de base d'ubuntu, Docker est en place, je suis content :D.</p>

<p><img src="https://pbrun.fr/content/images/2015/Jun/docker_logo.png" alt=""></p>

<p>J'ajoute le TLS et les commandes à distance, pas de problème majeur, j'ai juste suivi la documentation (peut-être 2 ou 3 fois, quand même) : <br>
<a href="https://docs.docker.com/articles/https/">https://docs.docker.com/articles/https/</a></p>

<p>Puis je lance mon premier containeur avec un shipyard ;) <br>
<a href="https://registry.hub.docker.com/u/shipyard/shipyard/">https://registry.hub.docker.com/u/shipyard/shipyard/</a></p>

<p><img src="https://pbrun.fr/content/images/2015/Jun/shipyard_logo.png" alt=""></p>

<p>C'est un outil permettant de lancer des containeurs et de surveiller les ressources allouées à ceux-ci. Sympa mais loin d'être indispensable avec un seul serveur Docker, mais avec une flotte, pourquoi pas ;)</p>

<p>Cependant, il existe d'autres outils sur le marché qui me semblent intéressants, avec des fonctionnalités supplémentaires, du coup je vais continuer de creuser.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Je pense que CoreOS est une distribution intéressante, cependant, je n'ai pas trouvé comment la configurer correctement sur mon vsphere sans DHCP. Je compte bien :</p>

<ul>
<li>soit trouver une solution pour le mettre en place, car à terme je souhaite que Docker soit un moyen de <strong>production</strong></li>
<li>Ou me pencher sur une autre distribution type RancherOS qui me semble équivalente. </li>
</ul>

<p>L'Ubuntu 14.04 est une solution viable, cependant il faudra configurer l'ensemble des outils manuellement et donc prendre le risque de ne pas profiter des mises à jour du système en terme de sécurité aussi rapidement qu'avec une distribution dédiée et oublier la mise en place de certains composants.</p>

<p>Aujourd'hui, nous sommes (chez Mobizel) toujours en phase d'expérimentation de Docker avec notamment un serveur Icinga permettant la surveillance de nos serveurs, mais je vais bientôt vous en parler.</p>

<p>Pierre</p>]]></content:encoded></item><item><title><![CDATA[git squash]]></title><description><![CDATA[<p>Dans certain projet communautaire, il vous sera demandé de faire un git squash afin de réunir un ensemble de commit en un seul.</p>

<p>Voici la marche à suivre :</p>

<pre><code>git squash -i HEAD~[NOMBRE DE COMMIT A MODIFIER]
</code></pre>

<p>Ensuite la console doit vous afficher quelques chose du genre :</p>

<pre><code>$ git rebase -i</code></pre>]]></description><link>https://pbrun.fr/git-squash/</link><guid isPermaLink="false">f22311e0-2f71-4b1d-9eb7-e2dc0ea5c062</guid><category><![CDATA[git]]></category><category><![CDATA[squash]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Mon, 09 Mar 2015 15:52:25 GMT</pubDate><content:encoded><![CDATA[<p>Dans certain projet communautaire, il vous sera demandé de faire un git squash afin de réunir un ensemble de commit en un seul.</p>

<p>Voici la marche à suivre :</p>

<pre><code>git squash -i HEAD~[NOMBRE DE COMMIT A MODIFIER]
</code></pre>

<p>Ensuite la console doit vous afficher quelques chose du genre :</p>

<pre><code>$ git rebase -i HEAD~4
pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
</code></pre>

<p>Ici le commit <strong>01d1124</strong> est notre plus vieux commit et le <strong>60709da</strong> doit correspondre au commit précédent nos modifications. Si nous voulons réunir les 3 commits suivants le <strong>01d1124</strong> ainsi que lui-même dans un nouveau commit unique il faudra modifier cette affichage comme ceci :</p>

<pre><code>$ git rebase -i HEAD~4
pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
</code></pre>

<p>Une fois ces modifications effectuées vous devez enregistrer votre document et quitter (pour vi :w puis :q ou :wq)</p>

<p>La console va vous afficher la chose suivante :</p>

<pre><code># This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#    new file:   LICENSE
#    modified:   README.textile
#    modified:   Rakefile
#    modified:   bin/jekyll
#
</code></pre>

<p>Vous pouvez par exemple remanier le commentaire comme ceci :</p>

<pre><code># This is a combination of 4 commits.
# The first commit's message is:
Adding license
Moving license into its own file
Jekyll has become self-aware.
Changed the tagline in the binary, too.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#    new file:   LICENSE
#    modified:   README.textile
#    modified:   Rakefile
#    modified:   bin/jekyll
#
</code></pre>

<p>Voila notre squash est prêt à être utilisé ! <br>
Il ne manque plus qu'a le push sur notre branch:</p>

<pre><code>git push --force origin fix-test
</code></pre>

<p>Le force est obligatoire ;)</p>

<p>Merci à Laurent Baey pour son aide.</p>]]></content:encoded></item><item><title><![CDATA[Recap des News #3]]></title><description><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="http://github.hubspot.com/shepherd/docs/welcome/">Un guide pour l'utilisateur de votre site</a> : Petite librairie JS pour faire un tour guidé de votre site :).</p>

<p><a href="http://fr.slideshare.net/hhamon/silex-meets-soap-rest">Quand Silex rencontre REST ou SOAP</a> : Une slideshow vraiment sympathique,</p>]]></description><link>https://pbrun.fr/recap-des-news-3/</link><guid isPermaLink="false">a4c25975-a5b5-4d79-995c-d2e4f29f8b16</guid><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Tue, 20 May 2014 08:11:01 GMT</pubDate><content:encoded><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="http://github.hubspot.com/shepherd/docs/welcome/">Un guide pour l'utilisateur de votre site</a> : Petite librairie JS pour faire un tour guidé de votre site :).</p>

<p><a href="http://fr.slideshare.net/hhamon/silex-meets-soap-rest">Quand Silex rencontre REST ou SOAP</a> : Une slideshow vraiment sympathique, permettant de bien voir la mise en place rapide d'un webservice REST ou SOAP avec Silex</p>

<p><a href="http://www.blogdumoderateur.com/usage-reseaux-sociaux-diminue/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+captainjob+%28Le+blog+du+mod%C3%A9rateur%29"></a> : Des statistiques sur l'usage des reseaux sociaux... et surprise, ça diminue !</p>

<p><a href="http://www.commitstrip.com/">Blog BD</a> : Un blog BD avec quelques strip sympa sur l'informatique :) <a href="http://www.commitstrip.com/wp-content/uploads/2014/05/Strip-Roulette-russe-650-final.jpg">Celui-ci est énorme ^^</a></p>

<p><a href="http://kalasoo.github.io/NumberProgressBar/">Progress Bar</a> Un plugin pour faire des progress bar sympatoche :)</p>

<p><a href="http://tympanus.net/Tutorials/SamsungGrid/index2.html">Une grid Sympatoche</a> : Un grid sympatoche avec un chargement que je trouve bien classe :D</p>]]></content:encoded></item><item><title><![CDATA[Percona XtraDB Cluster - MariaDB maitre - maitre]]></title><description><![CDATA[<p>Percona XtraDB Cluster est une solution permettant de faire de la haute disponibilité et/ou de la redondance facilement avec MariaDB.</p>

<p>Il permet notamment :</p>

<ul>
<li>De faire de la réplication synchrone. </li>
<li>De faire du multi-maitre (vous pouvez écrire sur n'importe quel cluster).</li>
</ul>

<p>Voici une architecture typique avec un Fail Over en</p>]]></description><link>https://pbrun.fr/percona-xtradb-cluster-mariadb-maitre-maitre/</link><guid isPermaLink="false">d523eb6a-22f6-4a50-8e06-5dcaf1fc3f5d</guid><category><![CDATA[Percona]]></category><category><![CDATA[XtraBackup]]></category><category><![CDATA[Replication]]></category><category><![CDATA[Haute Disponibilités]]></category><category><![CDATA[Architecture]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Tue, 13 May 2014 09:11:13 GMT</pubDate><content:encoded><![CDATA[<p>Percona XtraDB Cluster est une solution permettant de faire de la haute disponibilité et/ou de la redondance facilement avec MariaDB.</p>

<p>Il permet notamment :</p>

<ul>
<li>De faire de la réplication synchrone. </li>
<li>De faire du multi-maitre (vous pouvez écrire sur n'importe quel cluster).</li>
</ul>

<p>Voici une architecture typique avec un Fail Over en place de Front1 vers Front2 :</p>

<p><img src="https://pbrun.fr/content/images/2014/May/Archi-Exemple.png" alt="Schema Architecture serveur avec Percona Cluster"></p>

<p>Ici chaque Front écrit directement sur son propre serveur Data. <br>
Front1 écrit sur Data1 et Front2 écrit sur Data2. </p>

<p>On peut imaginer rajouter un serveur d'API par exemple sur Data3 qui pourrait directement écrire sur Data3. Ou par exemple, mettre en place une IPFail Over : si Data2 tombe, le traffic serait redirigé vers Data3. Les possibilités sont nombreuses et dépendent de vos contraintes.</p>

<p>Il est conseillé d'avoir au moins 3 serveurs dans vos clusters afin d'éviter un problème connu sous le nom de <strong>split brain</strong>.</p>

<h3 id="lesplitbrain">Le split brain</h3>

<p>Je vais essayer de l'expliquer simplement. Dans notre schéma Data1 est le serveur "main". Il y a toujours un serveur "main" qui permet de définir les régles de synchronisation.</p>

<ol>
<li><p>Suite à un problème sur le switch devant le Data1, celui-ci va devenir inaccessible durant quelques secondes, suffisamment pour que Data2 n'arrive plus à communiquer avec lui.</p></li>
<li><p>S'il n'y avait que deux serveurs, Data2 deviendrait alors le "main" serveur.</p></li>
<li><p>Le problème sur le switch se règle tout seul, et Data1 revient se considèrant toujours comme "main" serveur.</p></li>
<li><p>Les deux n'arriveraient pas à communiquer se croyant tout deux maitres. C'est la situation du <strong>split brain</strong>. Il faut qu'il y ait un vote à majorité pour que la situation se débloque, comme ceci par exemple :</p></li>
</ol>

<p>Data1 vote Data1 <br>
Data2 vote Data2 <br>
Data3 vote Data1 => donc Data1 "main".</p>

<p>Il est possible d'avoir seulement deux serveurs MariaDB Maitre-Maitre avec l'ajout d'un serveur fake (ne servant que pour le vote) : regarder du coté de l'utilitaire <strong>garbd</strong> </p>

<p><a href="http://www.mysqlperformanceblog.com/2012/07/25/percona-xtradb-cluster-failure-scenarios-with-only-2-nodes/">Plus d'informations sur le Split Brain</a></p>

<p>Il est à priori aussi possible de ce servir de Garbd en tant que relai de communication. Je vous laisse voir la documentation : <a href="https://github.com/jayjanssen/percona-xtradb-cluster-tutorial/blob/master/instructions/olddoc/07-Arbitration%20Daemon.rst#replication-through-garbd">Garbd</a></p>

<h1 id="configurationdepercona">Configuration de Percona</h1>

<p>Cette documentation permet de créer <strong>une architecture en cluster du type maitre-maitre</strong> grâce à la suite Percona.</p>

<p>Afin d'utiliser la suite percona, nous avons besoin d'ajouter les repos de percona :</p>

<p>1 - Se connecter avec l'utilisateur root sur le serveur data.</p>

<p>2 - Lancer la ligne de commande suivante :</p>

<pre><code class=" bash">apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A  
</code></pre>

<p>2.1 - Ajouter Percona au fichier <code>source.list</code> :</p>

<p>2.2 - A la fin du fichier <code>/etc/apt/source.list</code> ajouter les lignes suivantes :</p>

<pre><code class=" bash">deb http://repo.percona.com/apt [VERSION] main  
deb-src http://repo.percona.com/apt [VERSION] main  
</code></pre>

<p>2.3 - Puis lancer la mise à jour de votre aptitude :</p>

<pre><code class=" bash">apt-get update  
apt-get install percona-server-server-5.6 percona-server-client-5.6  
apt-get install percona-xtradb-cluster-56  
service mysql stop  
</code></pre>

<p>3 - Mettre en place la configuration de base, éditer le fichier suivant <code>/etc/mysql/conf.d/galera.cnf</code> :</p>

<pre><code>[mysqld]

#mysql settings
binlog_format=ROW  
default-storage-engine=innodb  
innodb_autoinc_lock_mode=2  
query_cache_size=0  
query_cache_type=0  
bind-address=0.0.0.0  
</code></pre>

<ol>
<li>Editer ensuite le fichier suivant <code>/etc/mysql/conf.d/wsrep.cnf</code> :</li>
</ol>

<pre><code>[mysqld]
#galera settings

wsrep_provider=/usr/lib/libgalera_smm.so  
wsrep_cluster_name="mon_site_cluster"  
wsrep_cluster_address="gcomm://10.0.0.300,10.0.0.301,10.0.0.302"

wsrep_node_address='10.0.0.300'  
wsrep_node_name='node1'  
wsrep_sst_method=rsync  
# optional settings
wsrep_retry_autocommit=5  
</code></pre>

<p>Voici les variables importantes :</p>

<ul>
<li><code>wsrep_cluster_name</code> : Le nom de votre  ensemble de cluster, il doit être identique sur l'ensemble des clusters.</li>
<li><code>wsrep_cluster_address</code> : L'adresse ip de l'ensemble de vos cluster en commencant par <code>gcomm://</code> et séparer par des ,</li>
<li><code>wsrep_node_address</code> : L'adresse du serveur que vous êtes en train d'éditer</li>
<li><code>wsrep_node_name</code> : Le nom du serveur que vous êtes en train d'éditer.</li>
</ul>

<p>5 - Sur chaque cluster data que vous voulez mettre en place, il faut refaire les étapes 1, 2, 3 et 4.</p>

<h1 id="lancerleclusterperconaetvrifiersontat">Lancer le cluster percona et vérifier son état</h1>

<p>Pour lancer le cluster principal, il suffit de faire la commande suivante :</p>

<pre><code class=" bash">service mysql bootstrap-pxc  
</code></pre>

<p>Pour vérifier si le serveur est ok, vous pouvez lancer la commande suivante :  </p>

<pre><code class=" bash">mysql -u root -p -e 'SELECT VARIABLE_VALUE as "cluster size" FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME="wsrep_cluster_size"'  
</code></pre>

<p>Vous devez avoir normalement le résultat suivant :  </p>

<pre><code>+--------------+
| cluster size |
+--------------+
| 1            |
+--------------+
</code></pre>

<p>Ensuite sur chaque noeud, il suffit de lancer le serveur mariaDB comme d'habitude :</p>

<pre><code class=" bash">service mysql start  
</code></pre>

<p>Je vous conseille de vérifier après chaque lancement que tout est ok :  </p>

<pre><code class=" bash">mysql -u root -p -e 'SELECT VARIABLE_VALUE as "cluster size" FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME="wsrep_cluster_size"'  
</code></pre>

<p>Par exemple avec un cluster de 3 serveurs, voici le résultat attendu :  </p>

<pre><code>+--------------+
| cluster size |
+--------------+
| 3            |
+--------------+
</code></pre>

<h1 id="xtrabackupnepasbloquervotreclusterlorsdesasynchronisation">XtraBackup - ne pas bloquer votre cluster lors de sa synchronisation.</h1>

<p>Percona XtraBackup est un outil open-source permettant de faire un backup à chaud de votre base de données pour MySQL. Ceci permet de ne pas bloquer (en écriture) votre serveur durant cette étape. </p>

<h2 id="configurationduclusterperconaavecxtrabackup">Configuration du cluster Percona avec XtraBackup</h2>

<p>1 - Se connecter à vos serveurs data avec l'utilisateur root <br>
2 - Ajouter la clé afin de pouvoir installer xtrabackup, utiliser la commande suivante :</p>

<pre><code class=" bash">apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A  
</code></pre>

<p>3 - Créer le fichier suivant :  </p>

<pre><code class=" bash">vi /etc/apt/sources.list.d/percona.list  
</code></pre>

<p>6 - Il suffit ensuite d'installer avec aptitude xtrabackup.</p>

<pre><code class=" bash">root@debian7: apt-get install xtrabackup  
</code></pre>

<p>7 - Il suffit ensuite de simplement éditer le fichier de configuration de percona et changer la méthode de Synchronisation SST :</p>

<pre><code class=" bash">wsrep_sst_method = xtrabackup  
</code></pre>

<h2 id="effectuerunbackupavecxtrabackuppourpercona">Effectuer un backup avec Xtrabackup pour Percona</h2>

<p>Pour effectuer un backup avec xtrabackup, il suffit de lancer la commande suivante :</p>

<pre><code class=" bash">innobackupex --galera-info --user=xxxxx --password=xxxx &lt;backup_directory&gt;  
</code></pre>

<h2 id="effectuerunerestaurationdevosdonnespourpercona">Effectuer une restauration de vos données pour Percona</h2>

<pre><code class=" bash">cp -Rf &lt;backup_directory&gt; /var/lib/mysql/  
chown -Rf mysql. /var/lib/mysql/  
</code></pre>

<pre><code class=" bash">cat &lt;backup_directory&gt;/xtrabackup_galera_info  
cfa9b8f1-f37b-11e2-0800-b37f8ac5092c:1  
</code></pre>

<pre><code class=" bash">service mysql start --wsrep_cluster_address='gcomm://&lt;ip_node_1&gt;' --  
wsrep_start_position="cfa9b8f1-f37b-11e2-0800-b37f8ac5092c:1"  
</code></pre>

<h2 id="faireunemaintenancesurleserveurmain">Faire une maintenance sur le serveur "main"</h2>

<p>Sur chaque cluster hormis le serveur ciblé, il faut forcer le changement vers un autre cluster :</p>

<pre><code class=" bash">SET GLOBAL wsrep_cluster_address='gcomm://[AN OTHER IP CLUSTER]';  
</code></pre>

<p>Vérifier que tout est ok :</p>

<pre><code class=" bash">MariaDB [(none)]&gt; SHOW VARIABLES LIKE 'wsrep_cluster_address';  
</code></pre>

<h2 id="dfinirleserveurrelaisencasdchecdumainserveur">Définir le serveur relais en cas d'échec du main serveur</h2>

<p>Il est possible de définir quel cluster deviendra le prochain cluster "main". </p>

<p>Pour ceci, il faut mettre en place la variable wsrep-sst-donor avec la valeur du wsrep<em>node</em>name ciblé.</p>

<pre><code class=" bash">wsrep-sst-donor=[YOUR NEXT MAIN NODE]  
</code></pre>

<p><em>Merci à lbaey pour la relecture.</em></p>]]></content:encoded></item><item><title><![CDATA[Recap des News le Vendredi #2]]></title><description><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="https://www.youtube.com/watch?v=WWvZfby08jU&amp;feature=youtu.be">Usage des google glass dans l'aeronautique</a> J'ai un brevet de pilote et j'avoue qu'une partie des fonctionnalités proposées semblent très intéressantes. En tout cas un bel usage des</p>]]></description><link>https://pbrun.fr/recap-des-news-le-vendredi-2/</link><guid isPermaLink="false">91e0a71e-2309-4419-8fac-069c862b3b96</guid><category><![CDATA[Symfony2]]></category><category><![CDATA[Infographie]]></category><category><![CDATA[google glass]]></category><category><![CDATA[Bookmark]]></category><category><![CDATA[management]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Sat, 10 May 2014 16:01:52 GMT</pubDate><content:encoded><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="https://www.youtube.com/watch?v=WWvZfby08jU&amp;feature=youtu.be">Usage des google glass dans l'aeronautique</a> J'ai un brevet de pilote et j'avoue qu'une partie des fonctionnalités proposées semblent très intéressantes. En tout cas un bel usage des google glass.</p>

<p><a 2746086905="" href="https://pbrun.fr/recap-des-news-le-vendredi-2/" http:="" www.amazon.fr="" gp="" product="" ref="as_li_ss_tl?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=2746086905&amp;linkCode=as2&amp;tag=pbrunfr-21"">Livre : Symfony2 - Développez des sites web PHP structurés et performants</a> Un livre qui semblent intéressant sur Symfony2, je l'ai commandé, j'espère y trouver de belles informations.</p>

<p><a href="http://raindrop.io/">RainDrop</a> Un outil de bookmark intéressant, dommage qu'il ne soit pas open source et self hosted.</p>

<p><a href="https://github.com/carhartl/jquery-cookie/tree/v1.4.1">JQuery cookie</a> Une librairie permettant la gestion des cookies en JS. Simple et bien écrit.</p>

<p><a href="http://www.infoq.com/fr/articles/estimations">Le jeu de dupes "d'estimations"</a> Article intéressant sur les estimations dans le domaine informatique.</p>

<p>Autres que l'info :</p>

<p><a href="http://thedesigninspiration.com/popular/business-cards/all/">Les meilleures cartes de visite</a> De manifiques cartes de visite. Une belle source d'inspiration.</p>

<p><a href="http://www.indiemag.fr/articles/quand-limbo-rencontre-ghibli">Quand Limbo croise Miyazaki</a> De superbes fonds d'écran inspirés des deux univers.</p>]]></content:encoded></item><item><title><![CDATA[Recap des News le Vendredi #1]]></title><description><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="http://labs.spotify.com/2014/03/27/spotify-engineering-culture-part-1/">Video sur l'organisation de spotify</a> : Voici une vidéo sur la mise en place de l'organisation chez spotify, je vous la conseille fortement, AGILE, SCRUM et organisation inside ^^</p>

<p><a href="http://mariodelvalle.github.io/CaptainIconWeb/">Captain</a></p>]]></description><link>https://pbrun.fr/recap-des-news-le-vendredi-1/</link><guid isPermaLink="false">2af4ce42-cbff-47e2-8ecc-e8c25ef7b597</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Python]]></category><category><![CDATA[News]]></category><category><![CDATA[Infographie]]></category><category><![CDATA[Video]]></category><category><![CDATA[Stats]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Fri, 02 May 2014 09:50:00 GMT</pubDate><content:encoded><![CDATA[<p>Voici une liste de liens issus de ma veille quotidienne. <br>
On y trouvera de tout, formation, tutoriel, information, marketing, développement, agile, organisation, ...</p>

<p><a href="http://labs.spotify.com/2014/03/27/spotify-engineering-culture-part-1/">Video sur l'organisation de spotify</a> : Voici une vidéo sur la mise en place de l'organisation chez spotify, je vous la conseille fortement, AGILE, SCRUM et organisation inside ^^</p>

<p><a href="http://mariodelvalle.github.io/CaptainIconWeb/">Captain Icon Web</a> : des ressources graphiques vectorielles pour mobile, il y en a 350 et c'est gratuit :)</p>

<p><a href="http://jolicode.github.io/best-bundle-conf/#/">Les Bundles que vous allez regretter de ne pas avoir connu plus tôt</a> : Une belle présentation des bundles les plus pratiques pour Symfony2, si vous faites du SF2, c'est un must have =]</p>

<p><a href="http://codecondo.com/learn-ruby-online-free/">Learn Ruby</a> : 10 manière gratuites d'apprendre le ruby. Je compte bien m'y mettre, donc j'ai bookmark ce lien ^^. J'espere vous en dire plus bientôt.</p>

<p><a href="http://trevorappleton.blogspot.co.uk/2014/04/writing-pong-using-python-and-pygame.html">Jeux video, faire un pong en python</a> un bel article mais assez classique sur la maniere d'écrire un jeu vidéo en Python avec PyGame.</p>

<p><a href="http://t.co/k8x1eaR3gv">Info Marketing</a> : Information Marketing sur les sites d'e-commerce</p>]]></content:encoded></item><item><title><![CDATA[RAML dans les détails]]></title><description><![CDATA[<p>Dans l'article suivant, je vais parler plus en détail des fonctionnalités offertes par le RAML.</p>

<p>Comment définir une API, comment définir les paramètres, comment définir les réponses, etc ...</p>

<h3 id="descriptiondudocument">Description du document</h3>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1  
</code></pre>

<p>Ici la balise <strong>RAML</strong> sert à décrire sur quelle version de</p>]]></description><link>https://pbrun.fr/raml-dans-les-details/</link><guid isPermaLink="false">44d093a1-865b-4952-8371-4e0a7db647ab</guid><category><![CDATA[API]]></category><category><![CDATA[RAML]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Tue, 29 Apr 2014 18:12:22 GMT</pubDate><content:encoded><![CDATA[<p>Dans l'article suivant, je vais parler plus en détail des fonctionnalités offertes par le RAML.</p>

<p>Comment définir une API, comment définir les paramètres, comment définir les réponses, etc ...</p>

<h3 id="descriptiondudocument">Description du document</h3>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1  
</code></pre>

<p>Ici la balise <strong>RAML</strong> sert à décrire sur quelle version de spécification RAML on se base.</p>

<p>Le <strong>titre</strong> est tout simplement le titre général de l'API, généralement le nom de votre site.</p>

<p>La <strong>version</strong> permet d'identifier clairement sur quelle version de description de notre API nous sommes.</p>

<h3 id="descriptiondunpointdentre">Description d'un point d'entrée</h3>

<p>Ensuite nous allons devoir définir un point d'entrée :</p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
</code></pre>

<p>Ici <strong>/api-action</strong> est l'url qui permettra d'accéder à notre api REST, par exemple : www.monsite.fr/api-action .Ensuite à nous de définir des méthodes GET / POST / PUT / DELETE / etc ... pour intéragir avec notre API.</p>

<p>Un exemple avec un <strong>GET</strong>. <br>
Un GET peut avoir des paramètres, mais pas de body (pourtant a priori rien ne l'interdit, mais la plupart des clients ne l'autorisent pas et API Designer non plus).</p>

<p>Voici la description d'un GET:</p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
    /list:
        get:
            ...
</code></pre>

<p>Ici <strong>get</strong> est le mot clé, il peut donc prendre les valeurs suivantes : connect, delete, post, patch, put, trace, ... en bref l'ensemble des actions REST possibles.</p>

<h3 id="ajoutdeparamtres">Ajout de paramètres</h3>

<p>Nous allons maintenant lui définir des paramètres.</p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
    /list:
        get:
          queryParameters:
            params1:
              displayName: Params 1
              type: integer
              description: filtre sur la date
              example: 1398792157
              required: false
</code></pre>

<p>Nous avons donc défini que notre requête pouvait prendre des paramètres grâce à <strong>queryParameters</strong>. Nous avons aussi défini un paramètre : <strong>params1</strong>.</p>

<p>Celui-ci est decrit, c'est un integer (grâce au <strong>type</strong>), il est non obligatoire (<strong>required: false</strong>). Et nous avons aussi d'autres informations pratiques <strong>description</strong> et <strong>displayName</strong> qui permet simplement d'avoir un nom affiché différent du paramètre en lui même.</p>

<p>Il est possible d'ajouter des paramètres, il suffit de répéter la chose :  </p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
    /list:
        get:
          queryParameters:
            params1:
              displayName: Params 1
              type: integer
              description: filtre sur la date
              example: 1398792157
              required: false
            params2:
              displayName: Params 2
              type: string
              description: filtre sur l'état
              example: done
              required: false
</code></pre>

<p>Et ceci autant de fois que nécessaire. <br>
Voici actuellement le rendu de notre API : <br>
<img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-6-.png" alt="Rendu API 1"></p>

<p>Mais il est possible aussi de définir un get directement avec un paramètre dans l'url, exemple :</p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
    /{id}/show:
        get:
          queryParameters:
            params1:
              displayName: Params 1
              type: integer
              description: filtre sur la date
              example: 1398792157
              required: false
</code></pre>

<p>Voici le résultat :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-9-.png" alt="Rendu API 2"></p>

<p>Voici l'ensemble des types acceptables par RAML :</p>

<ul>
<li>string</li>
<li>number     </li>
<li>integer    </li>
<li>date    </li>
<li>boolean    </li>
<li>file (uniquement en POST)</li>
</ul>

<p>Mais nous avons aussi plus d'options possibles :</p>

<ul>
<li>enum :  seulement pour le type string</li>
<li>pattern :  seulement pour le type string (type Perl 5)</li>
<li>minLength : seulement pour le type string</li>
<li>maxLength : seulement pour le type string</li>
<li>minimum : seulement pour le type number ou integer</li>
<li>maximum : seulement pour le type number ou integer</li>
<li>repeat : permet de passer plusieurs fois le même paramètre</li>
<li>default</li>
</ul>

<p>L'ensemble de ces informations est optionnel tout comme displayName, type, description, example, required.</p>

<p>Dans les cas autres qu'un get, il est possible de passer en plus un body. </p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
    /select:
        post:
            body:
              application/json:
                schema: |
                  {
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "minItems": 1,
                    "uniqueItems": true
                  }
                example: |
                  ["12315412131" ,"12315412145"]
</code></pre>

<p>Ici le body permet de nous définir que l'on attend forcément un body, celui-ci peut être ici uniquement de type application/json, cependant, il est tout à fait possible de le mettre en :</p>

<ul>
<li>application/x-www-form-urlencoded</li>
<li>application/xml</li>
<li>multipart/form-data</li>
</ul>

<p>Mais aussi d'en avoir plusieurs (accepter des body JSON et XML par exemple)</p>

<p>Nous avons aussi décrit un <strong>schéma de validation</strong> pour notre JSON (il est bien sûr aussi possible d'en faire un pour xml). Celui-ci permettra d'assurer vos échanges dans un bon format et de faire en sorte <strong>le bouchon (mock) vous retourne directement l'information si les paramètres</strong> sont mal formés.</p>

<h3 id="reponse">Reponse</h3>

<p>Nous allons maintenant nous intéresser à la réponse.</p>

<pre><code>#%RAML 0.8
title: API d'Exemple  
version: v1

/api-action:
  /list:
    get:
      description: |
        Exemple 1, Quid enim tam absurdum quam delectari multis inanimis rebus, ut honore, ut gloria, ut aedificio, ut vestitu cultuque corporis, animante virtute praedito
      queryParameters:
        params1:
          displayName: Params 1
          type: integer
          description: filtre sur la date
          example: 1398792157
          required: false
      responses:
        200:
            description: |
              Retourne un tableau JSON
            body:
              application/json:
                schema: |
                  {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "description" : "Information",
                      "properties": {
                        "reference": {
                          "description" : "la reference",
                          "type": "string",
                          "required":true
                        }
                   } } }
                example: |
                  [
                      { reference: '1234567890123' },
                      { reference: '1234567890125' }
                  ]
</code></pre>

<p>Voici une réponse complète. </p>

<ul>
<li><strong>200</strong> : représente le code HTTP.</li>
<li><strong>description</strong> : permet de donner rapidement une description de la réponse</li>
<li><strong>application/json</strong> : spécifie le type de réponse attendue (mais nous pouvons aussi avoir du xml)</li>
<li><strong>schema</strong> : précise le schéma de validation de la réponse</li>
<li><strong>exemple</strong> : donne simplement un exemple de réponse (celui rendu par le mock)</li>
</ul>

<p>Et en voici le rendu :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-11-.png" alt="alt"></p>

<p>Il est bien sûr possible d'ajouter des codes de réponse. Voici les plus utilisés :</p>

<ul>
<li>200 : OK</li>
<li>201 : Création effectuée</li>
<li>204 : Requête traitée mais pas de retour attendu</li>
<li>400 : Mauvaise requête</li>
<li>401 : Non autorisé à effectuer cette requête</li>
<li>403 : Interdiction d'effectuer cette requête</li>
<li>404 : Not Found </li>
<li>405 : Méthode non autorisée</li>
<li>409 : Conflit</li>
<li>500 : Erreur serveur</li>
</ul>

<p>Sinon, vous pouvez regarder la liste ici : <a href="http://www.restapitutorial.com/httpstatuscodes.html">Liste de code réponse</a></p>

<p>Attention, il est possible d'ajouter autant de codes réponses que souhaitez, cependant <strong>il est impossible d'ajouter deux exemples de réponses d'un code spécifique</strong> (ex: 500). C'est un peu dommage.</p>

<h3 id="tryitoulesmockpourvotreapi">Try it ou les mock pour votre API</h3>

<p>Lors de l'activation du mock service, il est possible d'effectuer directement en ligne de test sur votre api ou grâce à un url mulesoft spécifique directement dans votre application.</p>

<p>Voici par exemple un try-it sur le list défini plus haut :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-13-.png" alt=""></p>

<p>On voit qu'il est possible de rentrer directement un parameter, ensuite il suffit de cliquer sur GET et voici le résultat :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-14--1.png" alt=""></p>

<p>Il est donc aussi possible d'appeler directement depuis votre application l'url que j'ai flouté : </p>

<p><a href="http://mocksvc.mulesoft.com/mocks/e116b3..62/api-action/list">http://mocksvc.mulesoft.com/mocks/e116b3..62/api-action/list</a></p>

<p>Et voici un exemple avec un POST qui est ok vis à vis du schéma :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-15-.png" alt=""></p>

<p>Et un POST en echec vis à vis du schéma :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-16-.png" alt=""></p>

<h3 id="autresinformations">Autres informations</h3>

<p>Pour information :</p>

<ul>
<li>Il est possible actuellement de mettre du code HTML dans la description (bien pratique :) )</li>
<li>Il est possible de faire des imports de fichier, comme ceci : <code>schema: !include job.xsd</code></li>
<li>Il existe une spécification pour la sécurité de l'API, cependant, je ne l'ai pas utilisé,donc voici le lien : <a href="https://github.com/raml-org/raml-spec/blob/master/08_security.md">https://github.com/raml-org/raml-spec/blob/master/08_security.md</a></li>
<li>Il est possible et assez facile d'héberger soit même sont propre API Console, voici le lien GITHUB : <a href="https://github.com/mulesoft/api-console">https://github.com/mulesoft/api-console</a></li>
<li>Pour activer les bouchons il faut cliquer sur Mocking Service en haut à droite dans l'API Designer de mulesoft
<img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-12-.png" alt=""></li>
</ul>

<p>Voilà, si vous avez des commentaires, des idées, n'hésitez pas :) <br>
Je pense qu'avec ceci, vous êtes prêts pour écrire vos API en RAML.</p>]]></content:encoded></item><item><title><![CDATA[Design Your API ? Use RAML]]></title><description><![CDATA[<p>Pour le travail, j'ai dû chercher un moyen de créer une documentation pour une API REST.</p>

<p>Je me suis donc intéressé aux outils existant sur le marché afin de designer simplement et rapidement une API voire même d'avoir une génération de bouchon via la documentation.</p>

<p>Voici les sites les plus</p>]]></description><link>https://pbrun.fr/design-your-api-use-raml/</link><guid isPermaLink="false">f570ce81-4970-4cba-b3c4-9d5bd7313b26</guid><category><![CDATA[API]]></category><category><![CDATA[RAML]]></category><category><![CDATA[REST]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Mon, 28 Apr 2014 22:04:39 GMT</pubDate><content:encoded><![CDATA[<p>Pour le travail, j'ai dû chercher un moyen de créer une documentation pour une API REST.</p>

<p>Je me suis donc intéressé aux outils existant sur le marché afin de designer simplement et rapidement une API voire même d'avoir une génération de bouchon via la documentation.</p>

<p>Voici les sites les plus intéréssants que j'ai trouvé sur le sujet : </p>

<ul>
<li><a href="https://helloreverb.com/developers/swagger">Swagger</a></li>
<li><a href="http://apiary.io/">http://apiary.io/</a></li>
<li><a href="http://www.mulesoft.com/platform/api/developer-portal">RAML by MuleSoft</a></li>
</ul>

<h2 id="apiaryio">apiary.io</h2>

<p>J'ai commencé par apiary.io <br>
Un design sympathique, une documentation simple, gratuit. <br>
Je me suis dit pourquoi pas :)</p>

<p>Ils utilisent un langage crée et défini par leur soin l'API Blueprint.</p>

<p>L'idée est la même pour l'ensemble de ces logiciels : c'est de créer une documentation en ligne, mais aussi un mock afin de tester vos API. (Un mock est un "bouchon"). </p>

<p>Voici un exemple de fichier de description pour APIARY : </p>

<pre><code>FORMAT: 1A  
HOST: http://www.google.com

# test
Notes API is a *short texts saving* service similar to its physical paper presence on your table.

# Group Notes
Notes related resources of the **Notes API**

## Notes Collection [/notes]
### List all Notes [GET]
+ Response 200 (application/json)
        [{
          "id": 1, "title": "Jogging in park"
        }, {
          "id": 2, "title": "Pick-up posters from post-office"
        }]

### Create a Note [POST]
+ Request (application/json)
        { "title": "Buy cheese and bread for breakfast." }

+ Response 201 (application/json)
        { "id": 3, "title": "Buy cheese and bread for breakfast." }

## Note [/notes/{id}]
A single Note object with all its details

+ Parameters
    + id (required, number, `1`) ... Numeric `id` of the Note to perform action with. Has example value.

### Retrieve a Note [GET]
+ Response 200 (application/json)
      + Header
          X-My-Header: The Value

      + Body
          { "id": 2, "title": "Pick-up posters from post-office" }
</code></pre>

<p>Voici le rendu de la documentation :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CropperCapture-5-.png" alt="Rendu HTML"></p>

<p>Cependant, j'ai eu quelques limitations dans ma description d'API. C'est seulement pour des cas particuliers, mais bloquants dans mon cas (validation, documentation sur les erreurs et sur les parameters autres que require ou texte ...) </p>

<h2 id="ramlapidesign">RAML API Design</h2>

<p>Je suis donc parti à la recherche d'un autre format de description d'API. Je suis donc tombé sur le RAML. </p>

<p>C'est un format intéressant, car il semble plus abouti que les autres formats. Il permet de définir des schemas de validation (xml ou JSON), plusieurs réponses, avoir une documentation HTML ou markdown ...</p>

<p>Le format n'est pas beaucoup différent de l'API Blueprint. <br>
Il s'agit en fait de YAML avec une structure particulière.</p>

<p>La description de l'API est ici : <a href="https://github.com/raml-org/raml-spec">https://github.com/raml-org/raml-spec</a></p>

<p>Elle est bien écrite et complète, ce qui est agréable.</p>

<p>J'ai utilisé la validation par schéma pour mes réponses JSON.</p>

<p>Je vous propose donc de jeter un oeil au lien suivant, permettant de valider votre json et votre schema facilement : <a href="http://jsonschemalint.com/">http://jsonschemalint.com/</a></p>

<p>Voici un fichier d'exemple : </p>

<pre><code>#%RAML 0.8
title: Mon API  
baseUri: http://mocksvc.mulesoft.com/mocks/1.../{version}  
version: v1

/test:
    /:
      post:
        body:
          application/json:
            schema: |
              {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "minItems": 1,
                "uniqueItems": true
              }
            example: |
              {
                ["12315412131" ,"12315412145"]
              }
        description: |
          test produits

          &lt;table&gt;
              &lt;tr&gt;
                &lt;td&gt;Parametre&lt;/td&gt;
                &lt;td&gt;exemple&lt;/td&gt;
                &lt;td&gt;description&lt;/td&gt;
                &lt;td&gt;obligatoire&lt;/td&gt;
                &lt;td&gt;format&lt;/td&gt;
              &lt;/tr&gt;
              &lt;tr&gt;
                &lt;td&gt;id&lt;/td&gt;
                &lt;td&gt;"12315412131"&lt;/td&gt;
                &lt;td&gt;id&lt;/td&gt;
                &lt;td&gt;true&lt;/td&gt;
                &lt;td&gt;string&lt;/td&gt;
              &lt;/tr&gt;
            &lt;/table&gt;
        responses:
          200:
            description: |
              Retourne un tableau JSON de produit
            body:
              application/json:
                schema: |
                  {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "description" : "test",
                      "properties": {
                        "reference": {
                          "description" : "id produit",
                          "type": "string",
                          "required":true
                        },
                        "nombre": {
                          "description" : "nombre",
                          "type": "integer",
                          "required":true
                        }
                      }
                    }
                  }
                example: |
                 {
                    [
                        { reference: '1234567890123', nombre: 134 },
                        { reference: '1234567890125', nombre:  20 },
                        { reference: '1234567890127', nombre:   0 },
                    ]
                 }
          400:
            description: |
              Erreur retourne par le serveur

              * CODE 101 : Format
            body:
              application/json:
                schema: |
                  {
                    "ERREUR": {
                      "description" : "Si un erreur est presente",
                      "type": "object",
                      "properties": {
                        "CODE" : {
                          "type": "integer"
                        },
                        "MSG" : {
                          "type": "string"
                        }
                      }
                    }
                  }
                example: |
                 {
                   ERREUR : { CODE: 101, MSG: 'Format Inconnu' }
                 }
          500:
            description: |
              Erreur retourne par le serveur

              * CODE 102 : Inconnu
              * CODE 103 : Existant, mais erreur
            body:
              application/json:
                schema: |
                  {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "description" : "tsdgfsdf qsf sd ",
                      "properties": {
                        "reference": {
                          "description" : "sdfgsdfg dfsg df g",
                          "type": "string",
                          "required":true
                        },
                        "nombre": {
                          "description" : "sdfg dfs gsdf g",
                          "type": "integer"
                        },
                        "ERREUR": {
                          "description" : "sdfgd gfsdfg sdfg",
                          "type": "object",
                          "properties": {
                            "CODE" : {
                              "type": "integer"
                            },
                            "MSG" : {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                example: |
                 {
                    [
                      { reference: '1234567890123', nombre: 134 },
                      { reference: '1234567890124', ERREUR: { CODE: 102, MSG: 'Inconnu' } }
                    ]
                 }
</code></pre>

<p>La documentation générée est sympathique aussi. Cependant avec le JS présent, il est impossible d'imprimer le document ou de sortir un PDF. C'est là le plus gros défaut que j'ai trouvé actuellement à cette méthode.</p>

<p>Je pense d'ailleurs développer une script Python afin de générer une documentation HTML en Standalone et surtout imprimable en PDF.</p>

<p>Je vais faire un autre article afin de décrire plus en détail l'utilisation du RAML.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Si votre client est d'accord pour avoir une documentation d'API en ligne non imprimable, je vous conseille le RAML. Excellent format assez rapide à écrire après avoir pris le coup de main. Le mock fonctionne bien, même s'il est dommage qu'il retourne toujours le 200 si la réponse est bien formée.</p>

<p>PS: Si vous cherchez un bon livre sur les API REST, je vous conseille celui-ci : <a href="http://www.amazon.fr/gp/product/B00F5BS966/ref=as_li_ss_tl?ie=UTF8&amp;camp=1642&amp;creative=19458&amp;creativeASIN=B00F5BS966&amp;linkCode=as2&amp;tag=pbrunfr-21">O'REILLY RESTful Web APIs </a></p>]]></content:encoded></item><item><title><![CDATA[NAS à quoi ça sert ? Synologie ?]]></title><description><![CDATA[<p>Bonjour,</p>

<p>Comme promis, voici mon retour bientôt 4 an d'utilisation de mon NAS Synology.</p>

<h2 id="questcequunnas">Qu'est ce qu'un NAS ?</h2>

<p>Un NAS ( Network Attached Storage ) permet simplement de mettre vos données à disposition sur l'ensemble de votre réseau.</p>

<p>Mais il va au-delà de ça car il permet de :</p>

<ul>
<li>Sécuriser</li>
<li>Sauvegarder</li>
<li>Partager</li>
<li>Conserver</li></ul>]]></description><link>https://pbrun.fr/nas-a-quoi-ca-sert-synologie/</link><guid isPermaLink="false">152ec5ac-761d-49cb-9c42-89d88d0d7f3a</guid><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Mon, 28 Apr 2014 18:39:22 GMT</pubDate><content:encoded><![CDATA[<p>Bonjour,</p>

<p>Comme promis, voici mon retour bientôt 4 an d'utilisation de mon NAS Synology.</p>

<h2 id="questcequunnas">Qu'est ce qu'un NAS ?</h2>

<p>Un NAS ( Network Attached Storage ) permet simplement de mettre vos données à disposition sur l'ensemble de votre réseau.</p>

<p>Mais il va au-delà de ça car il permet de :</p>

<ul>
<li>Sécuriser</li>
<li>Sauvegarder</li>
<li>Partager</li>
<li>Conserver</li>
</ul>

<h3 id="aquoiasert">A quoi ça sert ?</h3>

<p>Comme dit ci-dessus, le NAS met en oeuvre des technologies permettant d'avoir un espace de stockage de données accessible et fiable.</p>

<p>Mais le prix d'un NAS est souvent 5x supérieur au prix d'un HDD Externe, alors quels sont les différences ? Une petite explication ci-dessous.</p>

<p>Il existe un très bon article de materiel.net disponible ici : <br>
<a href="http://www.materiel.net/minisites/guide_nas/#guide_top">GUIDE Materiel.net</a></p>

<p>Il est possible qu'il y ait doublon ici, mais j'aborde les points d'un autre angle de vue.</p>

<h3 id="conserver">Conserver</h3>

<p>Sur une clé USB ou un disque dur externe, les données sont certes accessible, mais pas stockées de manière fiable. Combien de fois j'ai vu mon Disque Externe rendre l'âme de manière naturelle ou simplement à cause du port USB ou du transport. Il ne faut pas oublier que les mémoires flash de type USB possèdent aussi un nombre de lectures et d'écritures données (bien que je n'ai jamais atteint cette limite).</p>

<p>Dans les entreprises, il y a deux sortes de conservation des données. <br>
La première est sur un serveur NAS pour garder les données dans un espace sécurisé et en même temps accessible. <br>
La deuxième est l'archivage sur bande, un procédé qui peut paraître d'un autre temps et pourtant, il reste plus efficace sur le long terme et surtout moins coûteux mais il est extrêmement long d'accès.</p>

<h3 id="partager">Partager</h3>

<p>Un NAS facilite le partage des informations, car d'abord il est accessible depuis n'importe quel ordinateur connecté à ce même réseau et de manière simultanée (pas besoin comme un Disque dur externe de chercher votre port USB ;-))</p>

<p>Le NAS est aussi souvent utilisé dans un réseau Media Center, (en gros regarder des films ou séries sur votre télévision). Si votre télé possède le sigle DLNA, vous connectez votre télé et "tous" vos films sont accessibles.</p>

<p>De plus le NAS est souvent multi-systèmes, que vous ayez un Windows, un Linux ou un Mac pas de problème.</p>

<p>Il y a d'autres applications permettant d'augmenter le partage, j'en parlerais dans la section application tierce.</p>

<p>Attention, il y a un piège. Il ne faut pas confondre NDAS et NAS. Ce n'est pas la même chose. Avec le premier vous risquez de rencontrer de nombreux problèmes, installation d'un driver pour le fonctionnement, pour le deuxième, vous le branchez au réseau et voilà.</p>

<h3 id="sauvegarder">Sauvegarder</h3>

<p>Il est possible avec un NAS, d'utiliser un système de sauvegarde régulière de vos ordinateurs. Chez moi par exemple, j'ai 1 PC qui tourne en permanence, il est donc sauvegardé tout les deux jours sur mon NAS vers 04:00 du matin.</p>

<p>La mise en oeuvre de technologies RAID, permet suivant la technique utilisée une bonne garantie des données sauvegardées.</p>

<h3 id="scuriser">Sécuriser</h3>

<p>La majorité des NAS, possède soit leur propre système d'utilisateur, soit la possibilité de se connecté à un LDAP ou un autre annuaire. <br>
Il permet de géré les personnes récupérant les données. Certain NAS intègre même un antivirus ou un firewall ce qui est vraiment pratique.</p>

<h2 id="pourquoisynology">Pourquoi Synology ?</h2>

<p>C'est un choix comme un autre, mais l'avantage de Synology, c'est qu'il bénéficie d'une bonne réputation dans le domaine tout comme QNap. J'ai d’ailleurs longtemps hésité entre les deux marques.</p>

<p>De plus l'ensemble des applications tierce proposé me semblait plus approprié à mon besoin. <br>
Il existe une bonne communauté française active, et Synology met régulièrement à jour ces produits. je le conseil donc.</p>

<h3 id="raid015ouautre">RAID 0, 1, 5 ou autre ?</h3>

<p>Sur les principes de fonctionnement, je vous laisse l'article Wikipedia bien fait sur le sujet : <br>
<a href="http://fr.wikipedia.org/wiki/RAID_(informatique">http://fr.wikipedia.org/wiki/RAID_(informatique</a>)</p>

<p>En gros, il faut retenir ceci :</p>

<ul>
<li><p>RAID 0 : permet de faire de deux volumes, un gros volumes. Aucune sécurisation des données.</p></li>
<li><p>RAID 1 : duplique les données sur deux disque dur. Si l'un meurt, on le remplace et aucune perte de données.</p></li>
<li><p>RAID 5 : augmentation de la capacité, de la rapidité et un ou deux volume pour la sécurisation des données. C'est bien sur un des meilleurs RAID, mais il nécessite un minimum de 3 disque.</p></li>
</ul>

<p>N'étant pas riche, j'ai opté pour un NAS deux disques, et souhaitant une bonne sécurisation des données, j'ai opté donc naturellement pour le RAID 1.</p>

<h3 id="applicationtierce">Application Tierce.</h3>

<p>La majorité des NAS possède des applications tierce, sous ce terme légèrement barbare, se cache simplement des application non essentiel, mais complémentaire au produit.</p>

<p>Voici, une liste d'application tierce :</p>

<ul>
<li>Serveur FTP</li>
<li>Serveur DLNA</li>
<li>Serveur Messagerie</li>
<li>Serveur d'Impressions</li>
<li>Surveillance vidéo</li>
<li>Serveur de téléchargement</li>
<li>Serveur itunes</li>
<li>Serveur Cloud</li>
<li>Virtualisation</li>
<li>Serveur Streaming audio</li>
<li>Serveur Web</li>
<li>Antivirus</li>
<li>Domotique</li>
<li>...</li>
</ul>

<p>La liste est longue, mais le tout c'est de trouver pour vous vos applications qui vous servirons.</p>

<p>En tant que développeur, quasiment l'ensemble des applications me sont utile, mais voici un résumé des applications utile par domaine :</p>

<p>Maison</p>

<ul>
<li>Serveur DLNA</li>
<li>Serveur Cloud</li>
<li>Surveillance vidéo</li>
<li>Serveur Streaming audio</li>
<li>Téléchargement</li>
</ul>

<p>Imaginez, vous êtes dans votre canapé, et l'ensemble de votre musique, films et séries sont disponible sur votre télé ou votre mobile préféré !</p>

<p>Vous partez en vacances, mais vous n'avez pas les moyens d'avoir une vrai surveillance vidéo, pas de problème, acheter une caméra IP ( 50-80 euros les première prix actuellement ) et hop une caméra vidéo avec détection de mouvement.</p>

<p>Vous aimez votre spotify ou votre deezer, mais les pubs vous énerve et vous avez légalement toute la musique sur votre ordinateur chez vous, ben la pas de problème, écoutez en Streaming la musique de chez vous !</p>

<p>Voici pourquoi un NAS est Indispensable en plus de garder vos photos préféré de manière sécurisé.</p>

<h3 id="dveloppeurbidouilleur">Développeur / Bidouilleur</h3>

<ul>
<li>Serveur Web</li>
<li>Domotique</li>
<li>Serveur SVN</li>
<li>Intégration continue</li>
</ul>

<p>J'ai actuellement, un serveur web avec l'excellent rsslounge sur lequel je vais certainement faire un article dans pas longtemps, mais aussi mes projets personnel en intégration continue avec hudson !</p>

<p>Mes sources sont sur mon NAS en SVN, et je me suis essayé rapidement à la domotique, mais si j'ai mis cela dans le domaine développeur / bidouilleur, c'est que ce n'est pas encore prêt, mais ce sera certainement l'avenir !</p>

<h2 id="conclusion">Conclusion</h2>

<p>Actuellement, mon NAS me sers en permanence dans ma vie et dans celle de ma compagne n'étant pas du tout habituez à l'informatique. Elle regarde ces séries sur la télé, n'a plus besoin de sa clé USB, elle récupère simplement ses fichiers directement via l'interface web du NAS. Et pour moi, ben le pied, entre le téléchargement, mes projets perso, le SVN, la sauvegarde de mes données, l'écoute en streaming depuis mon boulot !</p>

<p>A vous de choisir votre NAS maintenant, prenait bien en compte l'usage futur de votre NAS, et si vous prenez Synology et que vous avez un budget plus gros que moi, n'hésitez pas à prendre les NAS +, pour deux raisons, l'extension de mémoire possible ( en servant à tout est n'importe quoi, il est vite noyé ) et l'extension du NAS avec les baies d’extension.</p>

<p>Pour un sécurité optimal de vos informations, je vous conseil deux rajouts :</p>

<p>Un compte distant sur amazon / dropbox / google pour les données sensible (car on n'est jamais à l'abri d'un bon incendie ou inondation ;-) )</p>

<p>Un onduleur, pour garantir le méchant coup de foudre, ou les tensions variable</p>

<h3 id="desexemplesdenas28042014">Des exemples de NAS (28/04/2014):</h3>

<p><a href="http://www.amazon.fr/gp/product/B00FWURI8K/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;camp=1642&amp;creative=6746&amp;creativeASIN=B00FWURI8K&amp;linkCode=as2&amp;tag=pbrunfr-21">Synology DS214se</a>
Entrée de Gamme, c'est l'equivalent actuel de mon NAS :) Il est largement suffisant pour les application évoqués. Cependant, il est possible que pour les bidouilleurs, il manque de mémoire.</p>

<p><a href="http://www.amazon.fr/gp/product/B00FWUQNDQ/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;camp=1642&amp;creative=6746&amp;creativeASIN=B00FWUQNDQ&amp;linkCode=as2&amp;tag=pbrunfr-21">Synology DS214play</a>
Excellent NAS pour faire de la diffusion vidéo / audio. Avec lui pas de probleme d'encodage et de lecture sur votre télé !</p>

<p><a href="http://www.amazon.fr/gp/product/B009TPDDFG/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;camp=1642&amp;creative=6746&amp;creativeASIN=B009TPDDFG&amp;linkCode=as2&amp;tag=pbrunfr-21">Synology DS713+</a>
Il est cher, mais c'est le MUST. La différence de prix s'explique simplement car il permet de se connecter à des extensions comme celle-ci : <a href="http://www.amazon.fr/gp/product/B009ABM3TQ/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;camp=1642&amp;creative=6746&amp;creativeASIN=B009ABM3TQ&amp;linkCode=as2&amp;tag=pbrunfr-21">DX513</a> qui vous donne la possibilité d'avoir 5 disques supplémentaire !</p>

<p>à bientôt,
Pierre</p>]]></content:encoded></item><item><title><![CDATA[LDAP et PHP]]></title><description><![CDATA[<p>Effectuer une recherche dans LDAP.</p>

<pre><code>&lt;?php 

  // LDAP variables
  $ldaphost = "10.50.1.21";  // your ldap servers
  $ldapport = 389;           // your ldap server's port number

  // using ldap bind
  $ldaprdn  = 'cn=Directory Manager';     // ldap rdn or dn
  $ldappass = 'ipsweb06';  // associated password

  // Connecting to LDAP
  $ldapconn = ldap_connect($ldaphost, $ldapport)
            or die("Could</code></pre>]]></description><link>https://pbrun.fr/ldap-et-php/</link><guid isPermaLink="false">748f222a-8c95-4ec6-b8fb-f8f07a99befe</guid><category><![CDATA[PHP]]></category><category><![CDATA[LDAP]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Thu, 03 Apr 2014 23:07:39 GMT</pubDate><content:encoded><![CDATA[<p>Effectuer une recherche dans LDAP.</p>

<pre><code>&lt;?php 

  // LDAP variables
  $ldaphost = "10.50.1.21";  // your ldap servers
  $ldapport = 389;           // your ldap server's port number

  // using ldap bind
  $ldaprdn  = 'cn=Directory Manager';     // ldap rdn or dn
  $ldappass = 'ipsweb06';  // associated password

  // Connecting to LDAP
  $ldapconn = ldap_connect($ldaphost, $ldapport)
            or die("Could not connect to $ldaphost");

  if ($ldapconn) {
      // binding to ldap server
      $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);

      // verify binding
      if ($ldapbind) {
          echo "LDAP bind successful...";
          $dn = "ou=People, dc=transpac";

          $filter="sn=a*";

          $sr = ldap_search($ldapconn, $dn, $filter);

          $info = ldap_get_entries($ldapconn, $sr);
          echo $info["count"]." entries returned\n";

      } else {
          echo "LDAP bind failed...";
      }
  }else{
    echo "LDAP server not available ...";
  }
?&gt;
</code></pre>

<p>Pour cela on utilise ldap_search($ldapconn, $dn, $filter);</p>

<ul>
<li>$ldapconn : est la connection à ldap</li>
<li>$dn : l'endroit ou l'on recherche</li>
<li>$filter : la recherche en elle même</li>
</ul>

<p>Pour l'attribut $dn : <br>
Le DN représente le nom de l'entrée sous la forme du chemin d'accès à celle-ci depuis le sommet de l'arbre. On peut comparer le DN au path d'un fichier Unix. <br>
Exemple : <br>
People <br>
transpac    PM  FT  OBS People <br>
Oleane</p>

<p>ou=People,dc=OBS,dc=FT,dc=PM,dc=transpac</p>

<p>Permet d’accéder au "dossier" People de OBS. comme si on avait /transpac/PM/FT/OBS/People <br>
Il dépendra donc de l’arborescence du LDAP.</p>

<p>Pour l'attribut $filter : <br>
Le filtre de recherche s'exprime suivant une syntaxe spécifique dont la forme générale est : <br>
(&lt; operator(&lt; search operation)(&lt; search operation)...))</p>

<p>Ce filtre décrit une ou plusieurs conditions exprimées sous forme d'expressions régulières sensées désigner un ou plusieurs objets de l'annuaire, sur lesquels on veut appliquer l'opération voulue. Le tableau 6 récapitule les opérateurs de recherche disponibles : <br>
Filtre    Syntaxe Interprétation <br>
Approximation    (sn~=Mirtain)  nom dont l'orthographe est voisine de Mirtain <br>
Egalité    (sn=Mirtain)    vaut exactement Mirtain <br>
Comparaison    (sn>Mirtain) , &lt;= , >= , &lt;  noms situés alphabétiquement après Mirtain <br>
Présence    (sn=<em>)  toutes les entrées ayant un attribut sn <br>
Sous-chaîne    (sn=Mir</em>), (sn=<em>irtai</em>), (sn=Mirt<em>i</em>)   expressions régulières sur les chaînes <br>
ET    (&amp;(sn=Mirtain) (ou=Semir))  toutes les entrées dont le nom est Mirtain et du service Semir <br>
OU    (|(ou=Direction) (ou=Semir))    toutes les entrées dont le service est le Semir ou la Direction <br>
Négation    (!(tel=*))  toutes les entrées sans attribut téléphone</p>

<p>Pour plus d'informations</p>

<p>Exploiter le résultat : <br>
De nombreuses fonctions LDAP permettent d'exploiter les résultats renvoyés par la fonction ldap_search(). Ces fonctions ont un nom commençant généralement par <code>ldap_get_suivi</code> du nom de l'élément à récupérer :</p>

<pre><code>ldap_get_dn() permet de récupérer le DN de l'entrée
ldap_get_entries() permet de récupérer l'ensemble des entrées
ldap_get_option() permet de récupérer la valeur d'une option
ldap_get_values() permet de récupérer toutes les valeurs d'une  entrée
ldap_get_values_len() permet de récupérer les valeurs binaires d'une entrée
ldap_count_entries() permet de récupérer le nombre d'entrées retournées par la fonction de recherche
</code></pre>

<p>Exemple complet :  </p>

<pre><code>&lt;?php 

  // LDAP variables
  $ldaphost = "10.50.1.21";  // your ldap servers
  $ldapport = 389;           // your ldap server's port number

  // using ldap bind
  $ldaprdn  = 'cn=Directory Manager';     // ldap rdn or dn
  $ldappass = 'ipsweb06';  // associated password

  // Connecting to LDAP
  $ldapconn = ldap_connect($ldaphost, $ldapport)
            or die("Could not connect to $ldaphost");

  if ($ldapconn) {
      // binding to ldap server
      $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);

      // verify binding
      if ($ldapbind) {
          echo "LDAP bind successful...";

          // OU Organizational Unit
          // DC Organization
          $dn = "ou=People, dc=transpac";


          $filter="sn=a*";
          //$justthese = array("ou", "sn", "givenname", "mail");

          //$sr = ldap_search($ldapconn, $dn, $filter, $justthese);
          $sr = ldap_search($ldapconn, $dn, $filter);

          $info = ldap_get_entries($ldapconn, $sr);
          echo $info["count"]." entries returned\n";

          echo  "&lt;br /&gt;Récupération des entrées ...";
          $info = ldap_get_entries($ldapconn, $sr);

           echo  "Affichage des données des ".$info["count"]. " entrées trouvées :";

         for ($i=0; $i&lt;$info["count"]; $i++){
           echo  "&lt;p align='justify'&gt;";
           echo  "Le dn (Distinguished Name) est: ". $info[$i]["dn"] ."&lt;br&gt;";
           echo  "Nom (sn) : ". $info[$i]["sn"][0] . "&lt;br&gt;";
           echo  "Prénom (cn) : ". $info[$i]["cn"][0] . "&lt;br&gt;";
         }

      } else {
          echo "LDAP bind failed...";
      }
  }else{
    echo "LDAP server not available ...";
  }
?&gt;
</code></pre>]]></content:encoded></item><item><title><![CDATA[Fonctionnement de CAS]]></title><description><![CDATA[<p>Bonjour,</p>

<p>Voici le fonctionnement de CAS en mode Proxy :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CAS.png" alt=""></p>]]></description><link>https://pbrun.fr/fonctionnement-de-cas/</link><guid isPermaLink="false">f2f3ce33-504b-4267-9ea5-65456f82d7ba</guid><category><![CDATA[CAS]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Thu, 03 Apr 2014 22:56:28 GMT</pubDate><content:encoded><![CDATA[<p>Bonjour,</p>

<p>Voici le fonctionnement de CAS en mode Proxy :</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/CAS.png" alt=""></p>]]></content:encoded></item><item><title><![CDATA[CAS - Modification pour inclure les dépendances]]></title><description><![CDATA[<p>Pour inclure les dependances souhaité dans le war de cas, il vous faut.</p>

<ul>
<li>Une version de cas en svn.</li>
<li>Maven d'installé.</li>
<li>Modifier le fichier pom.xml</li>
<li>Lancer la ligne de commande de maven</li>
</ul>

<p>Pour récupéré la version de cas c'est simple, je vous laisse regarder sur le site officiel. <br>
Pour</p>]]></description><link>https://pbrun.fr/cas-modification-pour-inclure-les-dependances/</link><guid isPermaLink="false">f463ffbe-bf77-415d-8269-ee386fc014be</guid><category><![CDATA[CAS]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Thu, 03 Apr 2014 22:47:36 GMT</pubDate><content:encoded><![CDATA[<p>Pour inclure les dependances souhaité dans le war de cas, il vous faut.</p>

<ul>
<li>Une version de cas en svn.</li>
<li>Maven d'installé.</li>
<li>Modifier le fichier pom.xml</li>
<li>Lancer la ligne de commande de maven</li>
</ul>

<p>Pour récupéré la version de cas c'est simple, je vous laisse regarder sur le site officiel. <br>
Pour maven il s'agit de récupérer le zip du projet et de rajouter le répertoire dans le path de windows.</p>

<p>Voici un exemple avec l'intégration du support LDAP dans le pom.xml se trouvant dans cas-server-webapp :</p>

<pre><code>&lt;dependency&gt;  
        &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
        &lt;artifactId&gt;cas-server-support-generic&lt;/artifactId&gt;
        &lt;version&gt;${project.version}&lt;/version&gt;
        &lt;type&gt;jar&lt;/type&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
&lt;/dependency&gt;  
&lt;dependency&gt;  
        &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
        &lt;artifactId&gt;cas-server-support-ldap&lt;/artifactId&gt;
        &lt;version&gt;${project.version}&lt;/version&gt;
&lt;/dependency&gt;  
</code></pre>

<p>Puis aller dans le répertoire racine de cas et lancer la commande suivante :</p>

<pre><code>mvn clean package install -DskipTests=true
</code></pre>

<p>Le -DskipTests=true permet à cas de ne pas lancer les tests et donc de ne pas tombé en erreur lors dui build.</p>

<p>voici mon fichier pom.xml au complet :</p>

<pre><code>&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 &lt;a href="http://maven.apache.org/maven-v4_0_0.xsd"&gt;&lt;br /&gt;  
" title="http://maven.apache.org/maven-v4_0_0.xsd"&gt;&lt;br /&gt;  
"&gt;http://maven.apache.org/maven-v4_0_0.xsd"&gt;&lt;br /&gt;  
&lt;/a&gt;    &lt;parent&gt;  
        &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
        &lt;artifactId&gt;cas-server&lt;/artifactId&gt;
        &lt;version&gt;3.4.7&lt;/version&gt;
    &lt;/parent&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
    &lt;artifactId&gt;cas-server-webapp&lt;/artifactId&gt;
    &lt;packaging&gt;war&lt;/packaging&gt;
    &lt;name&gt;JA-SIG CAS Web Application&lt;/name&gt;
    &lt;dependencies&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.mockito&lt;/groupId&gt;
            &lt;artifactId&gt;mockito-all&lt;/artifactId&gt;
            &lt;version&gt;${mockito.version}&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
            &lt;type&gt;jar&lt;/type&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.github.inspektr&lt;/groupId&gt;
            &lt;artifactId&gt;inspektr-support-spring&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-cas-client&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-config&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
            &lt;artifactId&gt;cas-server-core&lt;/artifactId&gt;
            &lt;version&gt;${project.version}&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
                        &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                        &lt;artifactId&gt;cas-server-support-generic&lt;/artifactId&gt;
                        &lt;version&gt;${project.version}&lt;/version&gt;
                        &lt;type&gt;jar&lt;/type&gt;
                        &lt;scope&gt;runtime&lt;/scope&gt;
                &lt;/dependency&gt;

                &lt;dependency&gt;
                     &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                     &lt;artifactId&gt;cas-server-support-radius&lt;/artifactId&gt;
                     &lt;version&gt;${project.version}&lt;/version&gt;
                &lt;/dependency&gt;

                &lt;dependency&gt;
                     &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                     &lt;artifactId&gt;cas-server-support-x509&lt;/artifactId&gt;
                     &lt;version&gt;${project.version}&lt;/version&gt;
                &lt;/dependency&gt;

                &lt;dependency&gt;
                     &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                     &lt;artifactId&gt;cas-server-support-spnego&lt;/artifactId&gt;
                     &lt;version&gt;${project.version}&lt;/version&gt;
                &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-context-support&lt;/artifactId&gt;
            &lt;scope&gt;compile&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.opensymphony.quartz&lt;/groupId&gt;
            &lt;artifactId&gt;quartz&lt;/artifactId&gt;
            &lt;version&gt;1.6.1&lt;/version&gt;
            &lt;type&gt;jar&lt;/type&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;jstl&lt;/artifactId&gt;
            &lt;version&gt;1.1.2&lt;/version&gt;
            &lt;type&gt;jar&lt;/type&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;taglibs&lt;/groupId&gt;
            &lt;artifactId&gt;standard&lt;/artifactId&gt;
            &lt;version&gt;1.1.2&lt;/version&gt;
            &lt;type&gt;jar&lt;/type&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;ognl&lt;/groupId&gt;
            &lt;artifactId&gt;ognl&lt;/artifactId&gt;
            &lt;version&gt;2.7.3&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
            &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
                &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                &lt;artifactId&gt;cas-server-support-jdbc&lt;/artifactId&gt;
                        &lt;version&gt;${project.version}&lt;/version&gt;     
                &lt;/dependency&gt;

                &lt;dependency&gt;
          &lt;groupId&gt;commons-dbcp&lt;/groupId&gt;
          &lt;artifactId&gt;commons-dbcp&lt;/artifactId&gt;
          &lt;version&gt;1.4&lt;/version&gt;
         &lt;scope&gt;runtime&lt;/scope&gt;
                 &lt;/dependency&gt;

        &lt;dependency&gt;
          &lt;groupId&gt;mysql&lt;/groupId&gt;
          &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
          &lt;version&gt;5.1.13&lt;/version&gt;
          &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
          &lt;groupId&gt;concurrent&lt;/groupId&gt;
          &lt;artifactId&gt;concurrent&lt;/artifactId&gt;
          &lt;version&gt;1.3.4&lt;/version&gt;
          &lt;scope&gt;runtime&lt;/scope&gt;
                 &lt;/dependency&gt;

        &lt;dependency&gt;
                     &lt;groupId&gt;org.jasig.cas&lt;/groupId&gt;
                     &lt;artifactId&gt;cas-server-support-ldap&lt;/artifactId&gt;
                     &lt;version&gt;${project.version}&lt;/version&gt;
                &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;warName&gt;cas&lt;/warName&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Choix d'un VPS]]></title><description><![CDATA[<p>J'étais à la recherche d'un nouveau VPS afin d'héberger tout mon environnement (nodejs, plusieurs projets PHP, ...).</p>

<p>J'ai donc essayé de trouver une liste exhaustive des serveurs et surtout des comparatifs me permettant de faire facilement une choix parmi la liste (longue, vraiment trop longue ...) des fournisseurs.</p>

<p>L'interet d'un VPS pour</p>]]></description><link>https://pbrun.fr/performance-des-vps/</link><guid isPermaLink="false">4bcba52f-f178-4966-982b-649a2cc181c4</guid><category><![CDATA[VPS]]></category><category><![CDATA[OVH]]></category><category><![CDATA[FirstHeberg]]></category><category><![CDATA[Be1Host]]></category><dc:creator><![CDATA[PIerre Brun]]></dc:creator><pubDate>Thu, 03 Apr 2014 22:45:00 GMT</pubDate><content:encoded><![CDATA[<p>J'étais à la recherche d'un nouveau VPS afin d'héberger tout mon environnement (nodejs, plusieurs projets PHP, ...).</p>

<p>J'ai donc essayé de trouver une liste exhaustive des serveurs et surtout des comparatifs me permettant de faire facilement une choix parmi la liste (longue, vraiment trop longue ...) des fournisseurs.</p>

<p>L'interet d'un VPS pour moi est de ne pas regretter l'achat d'un dédié car des offres moins cher et plus aléchante seront disponible pour le même prix. Pouvoir faire évoluer mon besoin à la demande et profiter de la sécurité d'un VPS.</p>

<p>Après une longue recherche, je me suis décidé pour un serveur chez Be1Host. </p>

<p>J'ai choisi l'offre <a href="http://www.be1host.com/vps-lowcost.php" title="Offre Be1Host">LC 4</a> qui lors de l'écriture de cette article propose ceci :</p>

<pre><code>Processeur : 2 coeurs (2x 3.0 GHz)
Mémoire vive : 4 Go ECC
Disque dur : 150 Go SAS
IPv4 dédiée
Localisé en France ou Canada
Disponibilité 99.9%
Connexion 400 Mbps - Best effort
API
KVM
Protection anti-ddos
By OpenVZ
</code></pre>

<p>Bref ceci correspond quasi exactement à mon besoin, et cela pour <strong>23 euros/mois</strong>. Donc dans mes tarifs.</p>

<p>J'ai donc sauté sur l'occasion et j'ai commandé un serveur. <br>
Cependant, je ne m'y suis pas totalement retrouvé, ceci pour une principale raison : il y a une limite de Bandwith impossible à trouver lors de la commande de l'offre. Seule une petite phrase permet de se douter de la chose, mais bon. Après il faut modéré ce critère car il permet tout de même d'avoir une Bandwith de 15To.</p>

<p>Je n'ai pas aimer aussi l'absence de snapshot, la connexion de 400Mps qui n'est pas garenti, une interface de gestion pas vraiment sympathique. Cependant, c'est une offre qui est vraiment intéréssante.</p>

<p>Je vous les conseils si les points ci dessus ne sont pas bloquant pour vous.</p>

<p>Je me suis donc remis à la recherche d'un autre VPS dans les même charactéristique.</p>

<p>J'ai eu deux choix : les offres cloud d'OVH et les offres de firstheberg.</p>

<p>Parlons en premier du dernier. <br>
Chez FirstHeberg, j'ai craqué pour cette offre : <a href="http://www.firstheberg.com/virtual-private-server">HQ2</a>. <br>
Cette offre propose ceci :</p>

<pre><code>Processeur : 4 vcore
Mémoire vive : 8 Go
Disque dur  250 Go
IPv4 dédiée
Localisé en France
Bande Passante  100 Mbps - best effort
50 Go de backup offert !
Pas de SLA 
Protection anti-ddos
By QEMU
</code></pre>

<p>Tout cela pour le prix de <strong>24 euros</strong></p>

<p>Cependant, j'ai tout de suite eu quelques malheur ... <br>
Lors du transfert de mon backup (~40Go) par STP, je n'avais qu'un débit de 1mo/s (j'ai tout de même de la fibre optique et un upload de 8mo/s (re-tester durant le transfert)). </p>

<p>Aussi, on m'a promis un backup de 50Go, mais impossible de le trouver dans l'interface, après un ticket client j'ai eu la réponse suivante :</p>

<blockquote>
  <p>Il faut en faire la demande par ticket pour que nous vous activions l'accès FTP.</p>
</blockquote>

<p>Non mais vraiment, si je commande un serveur avec un backup, je m'attend à l'avoir de suite à disposition... pas devoir faire un ticket pour l'obtenir ... surtout que j'attends encore (3j actuellement)</p>

<p>Aussi actuellement, il est impossible de faire l'upgrade de son serveur vers une autre offre... Fonctionalité à venir très rapidement d'après FirstHeberg. Tout comme les snapshots qui sera a priori une option payante.</p>

<p>Leur Backoffice est sympathique sans plus, et j'ai bien aimer le bloc INFORMATIONS sur l'état des serveurs et plus généralement des services firstheberg. Et qui m'a permi d'apprendre qu'il effectuer bien la mise à jour de leur ancienne offre gratuitement !</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/Interface_FirstHeberg.png" alt=""></p>

<p>Cependant pas totalement satisfait, j'ai donc commander un <a href="http://www.ovh.com/fr/vps/vps-cloud.xml">VPS Cloud 2</a> chez OVH. Celui-ci se présente comme ceci :</p>

<pre><code>4 vCores
RAM garantie    4 Go
Disque dur  50 Go Raid 10
IPv4 dédiée
Localisé en France
100 Mbps Garenti !
SLA 99,99%
Protection anti-ddos
By VMWare
</code></pre>

<p>A oui, mais pas au même tarif, comme ceci, il vous faudra ~ 20 euros. Mais j'ai ajouter l'option BackupFTP 200Go et l'option snapshot qui me semblait très intéréssante, mais qui monte le prix à <strong>31 euros</strong> (ouch !)</p>

<p>Bon après sur ma prestation pas de problème, c'est du OVH. <br>
5 mo/s pour le transfert de mon backup. J'ai monté le FTP comme un point de montage et j'ai 10mo/s en écriture. Pas mal ! L'interface admin est top !</p>

<p><img src="https://pbrun.fr/content/images/2014/Apr/Interface_OVH.png" alt=""></p>

<p><strong>En bref :</strong></p>

<p>Je vous conseil les 3 ! <br>
Be1Host à des offres intéréssantes, même si je prefere tout de même celle de FirstHeberg. Cependant ces deux fournisseurs n'ont pas les même garenties et fonctionnalitées qu'OVH (SLA, débit garenti, snapshot !) il faut seulement faire un sacrifice sur l'espace disque ...</p>]]></content:encoded></item></channel></rss>