Jekyll2022-08-19T22:30:47+02:00https://www.bgalati.fr/feed.xmlBenoit GalatiBlog #Linux #Dev #Tech ...and moreBenoit Galatibenoit.galati@gmail.comMonolog Sentry handler v2 is out!2022-08-19T16:42:49+02:002022-08-19T16:42:49+02:00https://www.bgalati.fr/blog/monolog-sentry-handler-v2<p>Some weeks ago, I released a <a href="https://twitter.com/B_Galati/status/1553016326834343943">new major version of Monolog Sentry handler</a>
and I thought it would be a good idea to share some insights about it; so, here we are đ.</p>
<p>The main goal of this release was to support the new major version (v3) of PHP Sentry SDK.
Details about it are in the <a href="https://github.com/B-Galati/monolog-sentry-handler/releases/tag/2.0.0">release note</a>
which contains pointers to the upgrade guide and more.</p>
<p>As a reminder this library is a Monolog Sentry Handler that displays Monolog logs as Sentry breadcrumbs.
Thus, with the right Monolog config you can end up with the screenshot below. Itâs practical
for debugging purposes to have all logs related to an error in your error tracking tool đ.</p>
<p><img src="/images/sentry-event-example.png" alt="Sentry event example screenshot" /></p>
<p>Migrating the code to work with the new Sentry SDK was quick. The most time-consuming tasks were:</p>
<ul>
<li>Testing manually SDK changes</li>
<li>Adding compatibility with the new version of Monolog (v3)</li>
<li>Updating the doc to be compatible with the new version of the library and Symfony</li>
<li>Migrating to GitHub actions: that was the 1st time for me</li>
</ul>
<p>If you want to quickly try the library, you can use
the new <a href="https://github.com/B-Galati/monolog-sentry-handler-example">example repository</a>.</p>
<p>Note that the library can be used with the <a href="https://github.com/getsentry/sentry-symfony">official Symfony bundle</a>.
It is a bit easier to set up with the bundle, and it comes with more features by default.</p>
<p>Thanks again to <a href="https://twitter.com/_Codito_">Grzegorz Korba</a> who initiated that work!</p>Benoit Galatibenoit.galati@gmail.comSome weeks ago, I released a new major version of Monolog Sentry handler and I thought it would be a good idea to share some insights about it; so, here we are đ.A docker compose caching strategy for CI2018-03-04T09:07:28+01:002018-03-04T09:07:28+01:00https://www.bgalati.fr/blog/docker-compose-caching-in-ci<p>Letâs say you have some great docker images for your dev/prod environment but
they are quite long to build, specially in a CI that you want as fast as possible.</p>
<p>If you have a docker registry available, you could avoid the pain of re-building them
completely every single time.</p>
<h1 id="docker-caching-mechanism">Docker caching mechanism</h1>
<p>The idea is to use the option <code class="language-plaintext highlighter-rouge">--cache-from</code> of <code class="language-plaintext highlighter-rouge">docker build</code> but with <code class="language-plaintext highlighter-rouge">docker-compose</code>
because I like managing as much things as possible with <code class="language-plaintext highlighter-rouge">docker-compose</code>.</p>
<p>The problem that needs to be solved first is : how can I have a proper docker tag to avoid
having to re-build the whole image when the CI ran ?</p>
<p>The solution is simple: generate a md5 hash of your docker image and dependencies
(E.g. config files, etc.).</p>
<p>Letâs dive into the example to see how we can do this.</p>
<h1 id="the-fictive-example">The fictive example</h1>
<p>Everything is simplified here to showcase the big picture in CircleCI. The
important parts are directly explained in the examples.</p>
<p>We start with <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file :</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3.2"</span>
<span class="na">app</span><span class="pi">:</span>
<span class="na">services</span><span class="pi">:</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">.:/app</span>
<span class="c1"># This is the image name that will be generated during the build</span>
<span class="c1"># DOCKER_IMAGE_MD5 is generated at runtime by the the CI</span>
<span class="c1"># It is used for pulling and pushing</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">${DOCKER_IMAGE}</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">context</span><span class="pi">:</span> <span class="s">docker/dev</span>
<span class="c1"># Two images are used for caching</span>
<span class="c1"># 1) A specific image hash to try to build the full image with the cache</span>
<span class="c1"># 2) The last CI build to try reusing as much cache as possible when</span>
<span class="c1"># a brand new image needs to be built</span>
<span class="na">cache_from</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">${DOCKER_IMAGE}</span>
<span class="pi">-</span> <span class="s">${DOCKER_IMAGE_CI}</span>
</code></pre></div></div>
<p>Here is the CircleCI config :</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="m">2</span>
<span class="na">references</span><span class="pi">:</span>
<span class="c1"># Use CircleCI machine executor in order to use volumes</span>
<span class="na">configure_base</span><span class="pi">:</span> <span class="nl">&configure_base</span>
<span class="na">machine</span><span class="pi">:</span>
<span class="na">enabled</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">circleci/classic:201711-01</span>
<span class="c1"># This is the task which generate a md5 hash of the content of the docker directory</span>
<span class="c1"># Basically it generates a md5 of all files in the "docker" directory, then put them</span>
<span class="c1"># in a file called "docker-ci.md5" and finally generates a md5 of this file.</span>
<span class="na">generate_docker_hashes</span><span class="pi">:</span> <span class="nl">&generate_docker_hashes</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">test -e docker-ci.md5 || find docker -type f -exec md5sum {} \; | sort -k 2 > docker-ci.md5</span>
<span class="s">echo 'export DOCKER_IMAGE_MD5=$(md5sum docker-ci.md5 | cut -f1 -d" ")' >> $BASH_ENV</span>
<span class="s">echo 'export DOCKER_IMAGE=example/docker-caching:$DOCKER_IMAGE_MD5' >> $BASH_ENV</span>
<span class="s">echo 'export DOCKER_IMAGE_CI=example/docker-caching:ci' >> $BASH_ENV</span>
<span class="na">authenticate_on_registry</span><span class="pi">:</span> <span class="nl">&authenticate_on_registry</span>
<span class="na">run</span><span class="pi">:</span> <span class="s">docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD</span>
<span class="na">jobs</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="s"><<</span><span class="pi">:</span> <span class="nv">*configure_base</span>
<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">checkout</span>
<span class="pi">-</span> <span class="nv">*authenticate_on_registry</span>
<span class="pi">-</span> <span class="nv">*generate_docker_hashes</span>
<span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">set -x</span>
<span class="c1"># Try to pull, then build a new image if it is needed</span>
<span class="s">docker pull $DOCKER_IMAGE_CI || </span><span class="no">true</span>
<span class="s">docker-compose pull --ignore-pull-failures --parallel</span>
<span class="s">docker-compose build</span>
<span class="s"># Build is done. We tag the (perhaps new) image with "ci" (ie. the last built version) and push.</span>
<span class="s">docker tag $DOCKER_IMAGE $DOCKER_IMAGE_CI</span>
<span class="s">docker-compose push</span>
<span class="na">unit-test</span><span class="pi">:</span>
<span class="s"><<</span><span class="pi">:</span> <span class="nv">*configure_base</span>
<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="nv">*generate_docker_hashes</span>
<span class="pi">-</span> <span class="nv">*authenticate_on_registry</span>
<span class="c1"># At this point, the pull must be successful because we just pushed that hash</span>
<span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">docker-compose pull --parallel</span>
<span class="c1"># Run tests now as you would do on your machine</span>
<span class="na">workflows</span><span class="pi">:</span>
<span class="na">version</span><span class="pi">:</span> <span class="m">2</span>
<span class="na">build_test_deploy</span><span class="pi">:</span>
<span class="na">jobs</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">build</span>
<span class="pi">-</span> <span class="na">tests</span><span class="pi">:</span>
<span class="na">requires</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">build</span>
</code></pre></div></div>
<h1 id="conclusion">Conclusion</h1>
<p>Thatâs it. We just see how to build docker images pragmatically in the CI for
your tests.</p>
<p>The major downside is that the CI would still need more time to execute than a
CI without docker, because yes, itâs an overhead.</p>
<p>The big benefit is that you can reuse any known environment (I.e. dev, prod, etc.)
in your CI for your tests or anything. So you would be almost sure that what is working
on your machine would work the same way in your CI.</p>
<p>I recently came across this problem. I was trying to add Selenium tests to the CI
and it was a real pain. Then I switch to this approach and everything was smooth, almost :-)</p>Benoit Galatibenoit.galati@gmail.comLetâs say you have some great docker images for your dev/prod environment but they are quite long to build, specially in a CI that you want as fast as possible.PHP Matcher with Behat to assert unpredictable Json2017-09-10T19:28:08+02:002017-09-10T19:28:08+02:00https://www.bgalati.fr/blog/php-matcher-with-behat-to-assert-unpredictable-json<p><a href="https://github.com/coduo/php-matcher">PHP Matcher</a> is an awesome library to do assertion against unpredictable data. Combined with <a href="http://behat.org/">Behat</a> and the <a href="https://github.com/Behatch/contexts">Behatch contexts</a> itâs the perfect tool to test for non-deterministic JSON payload.</p>
<p>Here is an example where I override a method of Behatch <code class="language-plaintext highlighter-rouge">JsonContext</code> to add PHP Matcher features:</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kn">use</span> <span class="nc">Behat\Gherkin\Node\PyStringNode</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Behat\Mink\Exception\ExpectationException</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Behatch\Context\JsonContext</span> <span class="k">as</span> <span class="nc">BehatchJsonContext</span><span class="p">;</span>
<span class="kn">use</span> <span class="nc">Coduo\PHPMatcher\PHPMatcher</span><span class="p">;</span>
<span class="kd">class</span> <span class="nc">JsonContext</span> <span class="kd">extends</span> <span class="nc">BehatchJsonContext</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">theJsonShouldBeEqualTo</span><span class="p">(</span><span class="kt">PyStringNode</span> <span class="nv">$content</span><span class="p">)</span>
<span class="p">{</span>
<span class="nv">$actual</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">getJson</span><span class="p">();</span>
<span class="c1">// Replace all useless whitespace</span>
<span class="c1">// You can use the \Behatch\Json\Json class to have the same behavior</span>
<span class="c1">// but it checks JSON syntax which could be problematic if you want to get rid</span>
<span class="c1">// of double quote in field value</span>
<span class="nv">$expected</span> <span class="o">=</span> <span class="nb">preg_replace</span><span class="p">(</span><span class="s1">'/\s(?=([^"]*"[^"]*")*[^"]*$)/'</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="nv">$content</span><span class="o">-></span><span class="nf">getRaw</span><span class="p">());</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nc">PHPMatcher</span><span class="o">::</span><span class="nf">match</span><span class="p">((</span><span class="n">string</span><span class="p">)</span> <span class="nv">$actual</span><span class="p">,</span> <span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="nv">$expected</span><span class="p">,</span> <span class="nv">$error</span><span class="p">))</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nc">ExpectationException</span><span class="p">(</span><span class="nv">$error</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">getSession</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then you need to use this context in your <code class="language-plaintext highlighter-rouge">behat.yml.dist</code> instead of the one provided by Behatch:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">default</span><span class="pi">:</span>
<span class="na">suites</span><span class="pi">:</span>
<span class="na">default</span><span class="pi">:</span>
<span class="na">contexts</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">JsonContext</span>
</code></pre></div></div>
<p>Now, all your feature files can use PHP Matcher features like the following:</p>
<pre><code class="language-feature">Feature:
Scenario: Client credentials authentication
When I send a "GET" request to "/foo"
Then the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the response status code should be 200
And the JSON should be equal to:
"""
{
"bar":"@uuid@"
}
"""
</code></pre>
<p>Let me know in the comment section below if you know any other alternative ;-)</p>Benoit Galatibenoit.galati@gmail.comPHP Matcher is an awesome library to do assertion against unpredictable data. Combined with Behat and the Behatch contexts itâs the perfect tool to test for non-deterministic JSON payload.Install Powerline on Debian2016-09-06T14:21:55+02:002016-09-06T14:21:55+02:00https://www.bgalati.fr/blog/install-powerline-debian<h1 id="install-powerline">Install Powerline</h1>
<p>Recently, I wanted to install Powerline as simply as possible on Debian Jessie. Here is a my feedback.</p>
<ul>
<li>Activate backports in <code class="language-plaintext highlighter-rouge">/etc/apt/sources.list</code></li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deb http://ftp.fr.debian.org/debian/ jessie-backports main contrib non-free
</code></pre></div></div>
<ul>
<li>Install Powerline</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update <span class="o">&&</span> <span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-t</span> jessie-backports powerline
</code></pre></div></div>
<p>Voila ! Powerline is installed (Yea, even my grand mother could have done it hahaha). Now we are going to see how you can use it with <code class="language-plaintext highlighter-rouge">zsh</code>, <code class="language-plaintext highlighter-rouge">tmux</code>, and <code class="language-plaintext highlighter-rouge">vim</code>.</p>
<h1 id="powerlines-settings-with-zsh-tmux-and-vim">Powerlineâs settings with zsh, tmux and vim</h1>
<p>The secret is the following : RTFM of Debian (<code class="language-plaintext highlighter-rouge">/usr/share/doc/powerline/README.Debian</code>).</p>
<p>On my part, I am using it like so (with some extra parameters compared to Debian doc to make sure everything is ok)Â :</p>
<ul>
<li>zsh</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ~/.zshrc</span>
<span class="nb">export </span><span class="nv">TERM</span><span class="o">=</span><span class="s2">"xterm-256color"</span>
<span class="nb">source</span> /usr/share/powerline/bindings/zsh/powerline.zsh
</code></pre></div></div>
<ul>
<li>tmux</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ~/.tmux.conf</span>
run-shell <span class="s2">"powerline-daemon -q"</span> <span class="c"># Powerline recommends it but it's normally useless</span>
<span class="nb">source</span> <span class="s2">"/usr/share/powerline/bindings/tmux/powerline.conf"</span>
<span class="nb">set</span> <span class="nt">-g</span> default-terminal <span class="s2">"screen-256color"</span>
</code></pre></div></div>
<ul>
<li>vim</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>" ~/.vimrc
python from powerline.vim import setup as powerline_setup
python powerline_setup()
python del powerline_setup
set laststatus=2 " Always show statusline
set t_Co=256
</code></pre></div></div>
<p>I am sure you have noticed that each time we must set the 256 colors mode otherwise it cannot work.</p>
<p>To finish, all my personal config is in my <a href="https://github.com/b-galati/dotfiles">dotfiles</a> repository. Questions / Suggestions are welcomed ;-).</p>Benoit Galatibenoit.galati@gmail.comInstall PowerlineConvert PGS subtitles (Blu-ray .sup) to VobSub (DVDÂ .idx/.sub)2015-12-13T21:48:07+01:002015-12-13T21:48:07+01:00https://www.bgalati.fr/blog/convert-bluray-subtitles<p>Following a problem with my TV which did not want to read Blu-ray subtitles, I had to find a way to convert them in an understandable format.
So I used the following tools:</p>
<ul>
<li><strong>mkvtoolnix</strong> (CLIÂ tool for MKV files)</li>
<li><strong>mkvtoolnix-gui</strong> (GUI for mkvtoolnix)</li>
<li><strong>BDSupToSub</strong> (to convert PGS subtitles to VobSub)</li>
</ul>
<h1 id="first-step-extract-subtitles">First step: extract subtitles</h1>
<ul>
<li>Identity track number with <code class="language-plaintext highlighter-rouge">mkvinfo</code> (here itâs 5):</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkvinfo <filename>
<span class="o">[</span>...]
| + One track
| + Track number : 6 <span class="o">(</span>track number <span class="k">for </span>mkvmerge & mkvextract would be 5<span class="o">)</span>
| + Track UID : 9788501119646790547
| + Type of track : subtitles
| + Default signal : 0
| + Forced signal : 1
| + Codec ID : S_HDMV/PGS
<span class="o">[</span>...]
</code></pre></div></div>
<ul>
<li>Extract identified track (example with number 5):</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkvextract tracks <input_filename> 5:<output_filename.sup>
</code></pre></div></div>
<h1 id="second-step-convert-track-from-sup-to-vobsub">Second step: convert track from SUP to VobSub</h1>
<p>We use BDSup2Sub for this (Download <a href="https://github.com/mjuhasz/BDSup2Sub/wiki/Download">here</a>):</p>
<ul>
<li>Start BDSup2Sub</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> BDSup2Sub.jar
</code></pre></div></div>
<ul>
<li>
<p>Load subtitles file to convert with <em>« File -> Load »</em></p>
</li>
<li>
<p>Let default options and start conversion with <em>« File -> Save/Export »</em></p>
</li>
</ul>
<h1 id="third-step-optional-multiplex-tracks">Third step: (optional) multiplex tracks</h1>
<p>On my end my TV only read default subtitle built-in the MKV file. Thatâs why I had to put the track back into the video. To do this, I used <strong>MKVToolNix GUI</strong>. Itâs very simple to use, here is example:</p>
<p><img src="/images/MKVToolNixGUI-exemple-multiplexage-mkv-vobsub.png" alt="Image Alt" /></p>
<p>Thatâs the end, let me know in the comment section below for anything !</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://github.com/mjuhasz/BDSup2Sub">GitHub de BDSup2Sub (Download on wiki page)</a></li>
<li><a href="https://subextractor.codeplex.com/">SubExtractor: for SRT subtitles</a></li>
<li><a href="http://en.flossmanuals.net/Avidemux/ExtractingDVDSubtitles/">Avidemux: same</a></li>
</ul>Benoit Galatibenoit.galati@gmail.comFollowing a problem with my TV which did not want to read Blu-ray subtitles, I had to find a way to convert them in an understandable format. So I used the following tools:Iptables cheat sheet2014-11-02T21:28:55+01:002014-11-02T21:28:55+01:00https://www.bgalati.fr/blog/iptables-cheat-sheet<h1 id="backuprestore">Backup/Restore</h1>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Save</span>
iptables-save <span class="o">></span> fichier_de_backup
<span class="c"># Restore</span>
iptables-restore < fichier_de_backup
</code></pre></div></div>
<h1 id="list-rules">List rules</h1>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Forwarding rules</span>
iptables <span class="nt">-t</span> nat <span class="nt">-L</span> <span class="nt">-n</span> <span class="nt">-v</span>
<span class="c"># All rules</span>
iptables <span class="nt">-L</span> <span class="nt">-n</span> <span class="nt">-v</span>
</code></pre></div></div>
<h1 id="create-rules">Create rules</h1>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Redirect all TCP traffict from port 25 to port 2525</span>
iptables <span class="nt">-t</span> nat <span class="nt">-A</span> PREROUTING <span class="nt">-i</span> eth0 <span class="nt">-p</span> tcp <span class="nt">--dport</span> 25 <span class="nt">-j</span> REDIRECT <span class="nt">--to-port</span> 2525
<span class="c"># Allow internet connection when you are connected through a VPN</span>
iptables <span class="nt">-t</span> nat <span class="nt">-A</span> POSTROUTING <span class="nt">-s</span> 192.168.0/24 <span class="nt">-o</span> eth0 <span class="nt">-j</span> MASQUERADE
</code></pre></div></div>
<h1 id="delete-all-rules">Delete all rules</h1>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables <span class="nt">-F</span>
iptables <span class="nt">-X</span>
iptables <span class="nt">-t</span> nat <span class="nt">-F</span>
iptables <span class="nt">-t</span> nat <span class="nt">-X</span>
iptables <span class="nt">-t</span> mangle <span class="nt">-F</span>
iptables <span class="nt">-t</span> mangle <span class="nt">-X</span>
iptables <span class="nt">-P</span> INPUT ACCEPT
iptables <span class="nt">-P</span> FORWARD ACCEPT
iptables <span class="nt">-P</span> OUTPUT ACCEPT
</code></pre></div></div>
<p>One could use <code class="language-plaintext highlighter-rouge">netfilter-persistent</code> to persist iptables rules :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netfilter-persistent flush
</code></pre></div></div>
<h1 id="list-opened-ports">List opened ports</h1>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># https://explainshell.com/explain?cmd=netstat+--inet+-nplae</span>
<span class="c"># --inet only displa type net connection (udp/tcp, etc.)</span>
netstat <span class="nt">--inet</span> <span class="nt">-nplae</span>
</code></pre></div></div>
<h1 id="references">References</h1>
<ul>
<li><a href="https://wiki.debian.org/iptables">Debianâs iptables</a></li>
</ul>Benoit Galatibenoit.galati@gmail.comBackup/Restore